Some more robustness, correctness and completeness fixes for
javax.swing.text Views.

This corrects a misinterpretation in modelToView. We have been throwing
BadLocationExceptions when the position is outside of the view's
bounds. However, this should really only be thrown if the position is
outside the document's bounds and for all other cases return something
reasonable (like the left or right edge of that view, like implemented
in CompositeView).

I also added some missing methods in BoxView. This includes some methods
that should (according to the specs) be overridden but were not. I added
these methods too and tagged them with a FIXME, so that it is more
obvious that there might be a problem.

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

        * javax/swing/text/BoxView.java
        (getAxis): Added @since tag.
        (setAxis): Added @since tag.
        (layoutChanged): Added @since tag.
        (isLayoutValid): Added @since tag.
        (paint): Don't call setSize here. This is done in RootView already.
        (getMaximumSpan): Reimplemented to return the requirements'
        maximum size. Added API docs.
        (getMinimumSpan): New method.
        (layout): Fixed layout order.
        (modelToView): Call layout instead of setSize here.
        (getResizeWeight): New method.
        (getChildAllocation): New method.
        (forwardUpdate): New method.
        (viewToModel): New method.
        (flipEastEndWestEnds): New method.
        * javax/swing/text/CompositeView.java
        (modelToView): Made this method more robust by returning a default
        location if it's not possible to calculate one via the children.
        This default location returns the left or right edge of this
        view.
        (createDefaultLocation): New helper method.
        * javax/swing/text/IconView.java
        (modelToView): Don't throw BadLocationException. This should
        really only be thrown if the position is outside the document
        model, not if it's outside the view's boundary.

/Roman
Index: javax/swing/text/BoxView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/BoxView.java,v
retrieving revision 1.13
diff -u -r1.13 BoxView.java
--- javax/swing/text/BoxView.java	9 Feb 2006 14:28:49 -0000	1.13
+++ javax/swing/text/BoxView.java	9 Feb 2006 23:11:49 -0000
@@ -43,6 +43,7 @@
 import java.awt.Shape;
 
 import javax.swing.SizeRequirements;
+import javax.swing.event.DocumentEvent;
 
 /**
  * An implementation of [EMAIL PROTECTED] CompositeView} that arranges its children in
@@ -115,6 +116,8 @@
    * Returns the axis along which this <code>BoxView</code> is laid out.
    *
    * @return the axis along which this <code>BoxView</code> is laid out
+   *
+   * @since 1.3
    */
   public int getAxis()
   {
@@ -128,6 +131,8 @@
    * [EMAIL PROTECTED] View#Y_AXIS}.
    *
    * @param axis the axis along which this <code>BoxView</code> is laid out
+   *
+   * @since 1.3
    */
   public void setAxis(int axis)
   {
@@ -147,6 +152,8 @@
    * [EMAIL PROTECTED] View#Y_AXIS}.
    *
    * @param axis an <code>int</code> value
+   *
+   * @since 1.3
    */
   public void layoutChanged(int axis)
   {
@@ -166,6 +173,8 @@
    *
    * @return <code>true</code> if the layout along the specified
    *         <code>axis</code> is valid, <code>false</code> otherwise
+   *
+   * @since 1.4
    */
   protected boolean isLayoutValid(int axis)
   {
@@ -264,12 +273,6 @@
    */
   public void paint(Graphics g, Shape a)
   {
-    // Adjust size if the size is changed.
-    Rectangle bounds = a.getBounds();
-
-    if (bounds.width != getWidth() || bounds.height != getHeight())
-      setSize(bounds.width, bounds.height);
-
     Rectangle inside = getInsideAllocation(a);
     Rectangle copy = new Rectangle(inside);
     int count = getViewCount();
@@ -305,12 +308,54 @@
     return requirements[axis].preferred;
   }
 
+  /**
+   * Returns the maximum span of this view along the specified axis.
+   * This calculates the maximum span using
+   * [EMAIL PROTECTED] #calculateMajorAxisRequirements} or
+   * [EMAIL PROTECTED] #calculateMinorAxisRequirements} (depending on the axis) and
+   * returns the resulting maximum span.
+   *
+   * @param axis the axis
+   *
+   * @return the maximum span of this view along the specified axis
+   */
   public float getMaximumSpan(int axis)
   {
-    if (axis == getAxis())
-      return getPreferredSpan(axis);
-    else
-      return Integer.MAX_VALUE;
+    if (!isLayoutValid(axis))
+      {
+        if (axis == myAxis)
+          requirements[axis] = calculateMajorAxisRequirements(axis,
+                                                           requirements[axis]);
+        else
+          requirements[axis] = calculateMinorAxisRequirements(axis,
+                                                           requirements[axis]);
+      }
+    return requirements[axis].maximum;
+  }
+
+  /**
+   * Returns the minimum span of this view along the specified axis.
+   * This calculates the minimum span using
+   * [EMAIL PROTECTED] #calculateMajorAxisRequirements} or
+   * [EMAIL PROTECTED] #calculateMinorAxisRequirements} (depending on the axis) and
+   * returns the resulting minimum span.
+   *
+   * @param axis the axis
+   *
+   * @return the minimum span of this view along the specified axis
+   */
+  public float getMinimumSpan(int axis)
+  {
+    if (!isLayoutValid(axis))
+      {
+        if (axis == myAxis)
+          requirements[axis] = calculateMajorAxisRequirements(axis,
+                                                           requirements[axis]);
+        else
+          requirements[axis] = calculateMinorAxisRequirements(axis,
+                                                           requirements[axis]);
+      }
+    return requirements[axis].minimum;
   }
 
   /**
@@ -506,34 +551,51 @@
   protected void layout(int width, int height)
   {
     int[] newSpan = new int[]{ width, height };
+    int count = getViewCount();
 
-    // 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.
+    // Update minor axis as appropriate. We need to first update the minor
+    // axis layout because that might affect the children's preferences along
+    // the major axis.
     int minorAxis = myAxis == X_AXIS ? Y_AXIS : X_AXIS;
-    if (! isLayoutValid(minorAxis))
+    if ((! isLayoutValid(minorAxis)) || newSpan[minorAxis] != span[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.
+        for (int i = 0; i < count; ++i)
+          {
+            getView(i).setSize(spans[X_AXIS][i], spans[Y_AXIS][i]);
+          }
+        layoutValid[minorAxis] = true;
       }
 
-    // Update the child view's sizes.
-    int count = getViewCount();
-    for (int i = 0; i < count; ++i)
+
+    // Update major axis as appropriate.
+    if ((! isLayoutValid(myAxis)) || newSpan[myAxis] != span[myAxis])
       {
-        getView(i).setSize(spans[X_AXIS][i], spans[Y_AXIS][i]);
+        requirements[myAxis] = calculateMajorAxisRequirements(myAxis,
+                                                         requirements[myAxis]);
+        layoutMajorAxis(newSpan[myAxis], myAxis, offsets[myAxis],
+                        spans[myAxis]);
+        span[myAxis] = newSpan[myAxis];
+
+        // Update the child view's sizes.
+        for (int i = 0; i < count; ++i)
+          {
+            getView(i).setSize(spans[X_AXIS][i], spans[Y_AXIS][i]);
+          }
+        layoutValid[myAxis] = true;
       }
+
+
+    assert layoutValid[myAxis] == true
+           : "Major axis layout must be valid after layout";
+    assert layoutValid[minorAxis] == true
+    : "Minor axis layout must be valid after layout";
   }
 
   /**
@@ -730,11 +792,72 @@
       throws BadLocationException
   {
     // Make sure everything is allocated properly and then call super
-    if (!isAllocationValid())
+    if (! isAllocationValid())
       {
         Rectangle bounds = a.getBounds();
-        setSize(bounds.width, bounds.height);
+        layout(bounds.width, bounds.height);
       }
     return super.modelToView(pos, a, bias);
   }
+
+  /**
+   * 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. This implementation returns <code>0</code> for the major
+   * axis and <code>1</code> for the minor axis of this box view.
+   *
+   * @param axis the axis
+   *
+   * @return the resizability of this view along the specified axis
+   *
+   * @throws IllegalArgumentException if <code>axis</code> is invalid
+   */
+  public int getResizeWeight(int axis)
+  {
+    if (axis != X_AXIS && axis != Y_AXIS)
+      throw new IllegalArgumentException("Illegal axis argument");
+    int weight = 1;
+    if (axis == myAxis)
+      weight = 0;
+    return weight;
+  }
+
+  /**
+   * Returns the child allocation for the child view with the specified
+   * <code>index</code>. If the layout is invalid, this returns
+   * <code>null</code>.
+   *
+   * @param index the child view index
+   * @param a the allocation to this view
+   *
+   * @return the child allocation for the child view with the specified
+   *         <code>index</code> or <code>null</code> if the layout is invalid
+   *         or <code>a</code> is null
+   */
+  public Shape getChildAllocation(int index, Shape a)
+  {
+    Shape ret = null;
+    if (isAllocationValid() && a != null)
+      ret = super.getChildAllocation(index, a);
+    return ret;
+  }
+
+  protected void forwardUpdate(DocumentEvent.ElementChange ec, DocumentEvent e,
+                               Shape a, ViewFactory vf)
+  {
+    // FIXME: What to do here?
+    super.forwardUpdate(ec, e, a, vf);
+  }
+
+  public int viewToModel(float x, float y, Shape a, Position.Bias[] bias)
+  {
+    // FIXME: What to do here?
+    return super.viewToModel(x, y, a, bias);
+  }
+
+  protected boolean flipEastAndWestEnds(int position, Position.Bias bias)
+  {
+    // FIXME: What to do here?
+    return super.flipEastAndWestAtEnds(position, bias);
+  }
 }
Index: javax/swing/text/CompositeView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/CompositeView.java,v
retrieving revision 1.13
diff -u -r1.13 CompositeView.java
--- javax/swing/text/CompositeView.java	23 Nov 2005 14:23:14 -0000	1.13
+++ javax/swing/text/CompositeView.java	9 Feb 2006 23:11:49 -0000
@@ -218,20 +218,43 @@
     throws BadLocationException
   {
     int childIndex = getViewIndex(pos, bias);
+    Shape ret = null;
     if (childIndex != -1)
       {
         View child = getView(childIndex);
-        Rectangle r = a.getBounds();
-        childAllocation(childIndex, r);
-        Shape result = child.modelToView(pos, r, bias);
-        if (result == null)
-          throw new AssertionError("" + child.getClass().getName()
-                                   + ".modelToView() must not return null");
-        return result;
+        Shape childAlloc = getChildAllocation(childIndex, a);
+        if (childAlloc == null)
+          ret = createDefaultLocation(a, bias);
+        Shape result = child.modelToView(pos, childAlloc, bias);
+        if (result != null)
+          ret = result;
+        else
+          ret =  createDefaultLocation(a, bias);
       }
     else
-      throw new BadLocationException("No child view for the specified location",
-                                     pos);
+      ret = createDefaultLocation(a, bias);
+    return ret;
+  }
+
+  /**
+   * A helper method for [EMAIL PROTECTED] #modelToView(int, Position.Bias, int,
+   * Position.Bias, Shape)}. This creates a default location when there is
+   * no child view that can take responsibility for mapping the position to
+   * view coordinates. Depending on the specified bias this will be the
+   * left or right edge of this view's allocation.
+   *
+   * @param a the allocation for this view
+   * @param bias the bias
+   *
+   * @return a default location
+   */
+  private Shape createDefaultLocation(Shape a, Position.Bias bias)
+  {
+    Rectangle alloc = a.getBounds();
+    Rectangle location = new Rectangle(alloc.x, alloc.y, 1, alloc.height);
+    if (bias == Position.Bias.Forward)
+      location.x = alloc.x + alloc.width;
+    return location;
   }
 
   /**
Index: javax/swing/text/IconView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/IconView.java,v
retrieving revision 1.7
diff -u -r1.7 IconView.java
--- javax/swing/text/IconView.java	23 Nov 2005 11:59:30 -0000	1.7
+++ javax/swing/text/IconView.java	9 Feb 2006 23:11:49 -0000
@@ -130,8 +130,6 @@
     throws BadLocationException
   {
     Element el = getElement();
-    if (pos < el.getStartOffset() || pos >= el.getEndOffset())
-      throw new BadLocationException("Illegal offset for this view", pos);
     Rectangle r = a.getBounds();
     Icon icon = StyleConstants.getIcon(el.getAttributes());
     return new Rectangle(r.x, r.y, icon.getIconWidth(), icon.getIconHeight());

Reply via email to