Here come a couple of fixes for javax.swing.text that have been
necessary to get the new HTML stuff to work properly.

2006-11-06  Roman Kennke  <[EMAIL PROTECTED]>

        * javax/swing/text/ComponentView.java
        (setParent): Lock the document and repaint the hosting
        container.
        * javax/swing/text/FlowView.java
        (FlowStrategy.createView): Removed comment.
        (FlowView): Initialize span with Short.MAX_VALUE.
        (getFlowStart): Return 0 unconditionally.
        (layout): Moved code around to make it more readable.
        (loadChildren): Always set the parent.
        * javax/swing/text/GlyphView.java
        (DefaultGlyphPainter.fontMetrics): New field.
        (DefaultGlyphPainter.getAscent): Use new helper method to
        synchronize the font metrics.
        (DefaultGlyphPainter.getBoundedPosition): Use new helper method
        to synchronize the font metrics.
        (DefaultGlyphPainter.getDescent): Use new helper method to
        synchronize the font metrics.
        (DefaultGlyphPainter.getHeight): Use new helper method to
        synchronize the font metrics.
        (DefaultGlyphPainter.getSpan): Use new helper method to
        synchronize the font metrics.
        (DefaultGlyphPainter.modelToView): Use new helper method to
        synchronize the font metrics.
        (DefaultGlyphPainter.updateFontMetrics): New helper method for
        font metrics caching.
        (DefaultGlyphPainter.viewToModel): Use new helper method to
        synchronize the font metrics. Fixed view to model mapping.
        * javax/swing/text/View.java
        (removeAll): Pass null to replace().
        (setParent): Only reparent children that have this view as parent.

/Roman

Index: javax/swing/text/ComponentView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/ComponentView.java,v
retrieving revision 1.16
diff -u -1 -5 -r1.16 ComponentView.java
--- javax/swing/text/ComponentView.java	25 Aug 2006 20:44:50 -0000	1.16
+++ javax/swing/text/ComponentView.java	6 Nov 2006 16:02:14 -0000
@@ -385,31 +385,48 @@
    *
    * @param p the parent view or <code>null</code> if this view is removed
    *        from it's parent
    */
   public void setParent(final View p)
   {
     super.setParent(p);
     if (SwingUtilities.isEventDispatchThread())
       setParentImpl();
     else
       SwingUtilities.invokeLater
       (new Runnable()
        {
          public void run()
          {
-           setParentImpl();
+           Document doc = getDocument();
+           try
+             {
+               if (doc instanceof AbstractDocument)
+                 ((AbstractDocument) doc).readLock();
+               setParentImpl();
+               Container host = getContainer();
+               if (host != null)
+                 {
+                   preferenceChanged(null, true, true);
+                   host.repaint();
+                 }
+             }
+           finally
+             {
+               if (doc instanceof AbstractDocument)
+                 ((AbstractDocument) doc).readUnlock();
+             }
          }
        });
   }
 
   /**
    * The implementation of [EMAIL PROTECTED] #setParent}. This is package private to
    * avoid a synthetic accessor method.
    */
   void setParentImpl()
   {
     View p = getParent();
     if (p != null)
       {
         Container c = getContainer();
         if (c != null)
Index: javax/swing/text/FlowView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/FlowView.java,v
retrieving revision 1.18
diff -u -1 -5 -r1.18 FlowView.java
--- javax/swing/text/FlowView.java	2 Nov 2006 11:20:21 -0000	1.18
+++ javax/swing/text/FlowView.java	6 Nov 2006 16:02:14 -0000
@@ -177,31 +177,31 @@
     {
       int start = fv.getStartOffset();
       int end = fv.getEndOffset();
 
       // Preserve the views from the logical view from beeing removed.
       View lv = getLogicalView(fv);
       int viewCount = lv.getViewCount();
       for (int i = 0; i < viewCount; i++)
         {
           View v = lv.getView(i);
           v.setParent(lv);
         }
 
       // Then remove all views from the flow view.
       fv.removeAll();
- 
+
       for (int rowIndex = 0; start < end; rowIndex++)
         {
           View row = fv.createRow();
           fv.append(row);
           int next = layoutRow(fv, rowIndex, start);
           if (row.getViewCount() == 0)
             {
               row.append(createView(fv, start, Integer.MAX_VALUE, rowIndex));
               next = row.getEndOffset();
             }
           if (start < next)
             start = next;
           else
             assert false: "May not happen";
         }
@@ -306,32 +306,32 @@
      * [EMAIL PROTECTED] View#createFragment(int, int)} that has the correct startOffset
      * and the logical view's endOffset.
      *
      * @param fv the flow view
      * @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 startOffset, int spanLeft,
                               int rowIndex)
     {
        View logicalView = getLogicalView(fv);
-       // FIXME: Handle the bias thing correctly.
-       int index = logicalView.getViewIndex(startOffset, Position.Bias.Forward);
+       int index = logicalView.getViewIndex(startOffset,
+                                            Position.Bias.Forward);
        View retVal = logicalView.getView(index);
        if (retVal.getStartOffset() != startOffset)
          retVal = retVal.createFragment(startOffset, retVal.getEndOffset());
        return retVal;
     }
 
     /**
      * 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.
@@ -591,30 +591,31 @@
   protected FlowStrategy strategy;
 
   /**
    * Creates a new <code>FlowView</code> for the given
    * <code>Element</code> and <code>axis</code>.
    *
    * @param element the element that is rendered by this FlowView
    * @param axis the axis along which the view is tiled, either
    *        <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>, the flow
    *        axis is orthogonal to this one
    */
   public FlowView(Element element, int axis)
   {
     super(element, axis);
     strategy = sharedStrategy;
+    layoutSpan = Short.MAX_VALUE;
   }
 
   /**
    * Returns the axis along which the view should be flowed. This is
    * orthogonal to the axis along which the boxes are tiled.
    *
    * @return the axis along which the view should be flowed
    */
   public int getFlowAxis()
   {
     int axis = getAxis();
     int flowAxis;
  
     if (axis == X_AXIS)
       flowAxis = Y_AXIS;
@@ -639,90 +640,86 @@
   {
     return layoutSpan;
   }
 
   /**
    * Returns the location along the flow axis where the flow span starts
    * given a child view index. The flow can be shaped by providing
    * different values here.
    *
    * @param index the index of the child for which to return the flow location
    *
    * @return the location along the flow axis where the flow span starts
    */
   public int getFlowStart(int index)
   {
-    return getLeftInset(); // TODO: Is this correct?
+    return 0;
   }
 
   /**
    * Creates a new view that represents a row within a flow.
    *
    * @return a view for a new row
    */
   protected abstract View createRow();
 
   /**
    * Loads the children of this view. The <code>FlowView</code> does not
    * directly load its children. Instead it creates a logical view
    * ([EMAIL PROTECTED] #layoutPool}) which is filled by the logical child views.
    * The real children are created at layout time and each represent one
    * row.
    *
    * This method is called by [EMAIL PROTECTED] View#setParent} in order to initialize
    * the view.
    *
    * @param vf the view factory to use for creating the child views
    */
   protected void loadChildren(ViewFactory vf)
   {
     if (layoutPool == null)
       {
         layoutPool = new LogicalView(getElement());
-        layoutPool.setParent(this);
       }
+    layoutPool.setParent(this);
     // Initialize the flow strategy.
     strategy.insertUpdate(this, null, null);
   }
 
   /**
    * Performs the layout of this view. If the span along the flow axis changed,
    * this first calls [EMAIL PROTECTED] FlowStrategy#layout} in order to rebuild the
    * rows of this view. Then the superclass's behaviour is called to arrange
    * the rows within the box.
    *
    * @param width the width of the view
    * @param height the height of the view
    */
   protected void layout(int width, int height)
   {
     int flowAxis = getFlowAxis();
+    int span; 
     if (flowAxis == X_AXIS)
-      {
-        if (layoutSpan != width)
-          {
-            layoutChanged(Y_AXIS);
-            layoutSpan = width;
-          }
-      }
+      span = (int) width;
     else
+      span = (int) height;
+
+    if (layoutSpan != span)
       {
-        if (layoutSpan != height)
-          {
-            layoutChanged(X_AXIS);
-            layoutSpan = height;
-          }
+        layoutChanged(flowAxis);
+        layoutChanged(getAxis());
+        layoutSpan = span;
       }
 
     if (! isLayoutValid(flowAxis))
       {
         int axis = getAxis();
         int oldSpan = axis == X_AXIS ? getWidth() : getHeight();
         strategy.layout(this);
         int newSpan = (int) getPreferredSpan(axis);
         if (oldSpan != newSpan)
           {
             View parent = getParent();
             if (parent != null)
               parent.preferenceChanged(this, axis == X_AXIS, axis == Y_AXIS);
           }
       }
Index: javax/swing/text/GlyphView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/GlyphView.java,v
retrieving revision 1.23
diff -u -1 -5 -r1.23 GlyphView.java
--- javax/swing/text/GlyphView.java	2 Nov 2006 11:20:21 -0000	1.23
+++ javax/swing/text/GlyphView.java	6 Nov 2006 16:02:14 -0000
@@ -240,40 +240,41 @@
      * @param p1 the end offset of the rendered area
      *
      * @return a painter that can be used to render the specified glyph view
      */
     public GlyphPainter getPainter(GlyphView v, int p0, int p1)
     {
       return this;
     }
   }
 
   /**
    * The default <code>GlyphPainter</code> used in <code>GlyphView</code>.
    */
   static class DefaultGlyphPainter extends GlyphPainter
   {
+    FontMetrics fontMetrics;
+
     /**
      * Returns the full height of the rendered text.
      *
      * @return the full height of the rendered text
      */
     public float getHeight(GlyphView view)
     {
-      Font font = view.getFont();
-      FontMetrics metrics = Toolkit.getDefaultToolkit().getFontMetrics(font);
-      float height = metrics.getHeight();
+      updateFontMetrics(view);
+      float height = fontMetrics.getHeight();
       return height;
     }
     
     /**
      * Paints the glyphs.
      *
      * @param view the glyph view to paint
      * @param g the graphics context to use for painting
      * @param a the allocation of the glyph view
      * @param p0 the start position (in the model) from which to paint
      * @param p1 the end position (in the model) to which to paint
      */
     public void paint(GlyphView view, Graphics g, Shape a, int p0,
                       int p1)
     {
@@ -329,150 +330,166 @@
      *        [EMAIL PROTECTED] Position.Bias#Backward} depending on the preferred
      *        direction bias. If <code>null</code> this defaults to
      *        <code>Position.Bias.Forward</code>
      *
      * @return a rectangle that gives the location of the document position
      *         inside the view coordinate space
      *
      * @throws BadLocationException if <code>pos</code> is invalid
      * @throws IllegalArgumentException if b is not one of the above listed
      *         valid values
      */
     public Shape modelToView(GlyphView view, int pos, Position.Bias b,
                              Shape a)
       throws BadLocationException
     {
+      updateFontMetrics(view);
       Element el = view.getElement();
-      Font font = view.getFont();
-      FontMetrics fm = view.getContainer().getFontMetrics(font);
       Segment txt = view.getText(el.getStartOffset(), pos);
       Rectangle bounds = a instanceof Rectangle ? (Rectangle) a
                                                 : a.getBounds();
       TabExpander expander = view.getTabExpander();
-      int width = Utilities.getTabbedTextWidth(txt, fm, bounds.x, expander,
+      int width = Utilities.getTabbedTextWidth(txt, fontMetrics, bounds.x,
+                                               expander,
                                                view.getStartOffset());
-      int height = fm.getHeight();
+      int height = fontMetrics.getHeight();
       Rectangle result = new Rectangle(bounds.x + width, bounds.y,
                                        0, height);
       return result;
     }
 
     /**
      * Determine the span of the glyphs from location <code>p0</code> to
      * location <code>p1</code>. If <code>te</code> is not <code>null</code>,
      * then TABs are expanded using this <code>TabExpander</code>.
      * The parameter <code>x</code> is the location at which the view is
      * located (this is important when using TAB expansion).
      *
      * @param view the glyph view
      * @param p0 the starting location in the document model
      * @param p1 the end location in the document model
      * @param te the tab expander to use
      * @param x the location at which the view is located
      *
      * @return the span of the glyphs from location <code>p0</code> to
      *         location <code>p1</code>, possibly using TAB expansion
      */
     public float getSpan(GlyphView view, int p0, int p1,
                          TabExpander te, float x)
     {
-      Font font = view.getFont();
-      FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
+      updateFontMetrics(view);
       Segment txt = view.getText(p0, p1);
-      int span = Utilities.getTabbedTextWidth(txt, fm, (int) x, te, p0);
+      int span = Utilities.getTabbedTextWidth(txt, fontMetrics, (int) x, te,
+                                              p0);
       return span;
     }
 
     /**
      * Returns the ascent of the text run that is rendered by this
      * <code>GlyphPainter</code>.
      *
      * @param v the glyph view
      *
      * @return the ascent of the text run that is rendered by this
      *         <code>GlyphPainter</code>
      *
      * @see FontMetrics#getAscent()
      */
     public float getAscent(GlyphView v)
     {
-      Font font = v.getFont();
-      FontMetrics fm = v.getContainer().getFontMetrics(font);
-      return fm.getAscent();
+      updateFontMetrics(v);
+      return fontMetrics.getAscent();
     }
 
     /**
      * Returns the descent of the text run that is rendered by this
      * <code>GlyphPainter</code>.
      *
      * @param v the glyph view
      *
      * @return the descent of the text run that is rendered by this
      *         <code>GlyphPainter</code>
      *
      * @see FontMetrics#getDescent()
      */
     public float getDescent(GlyphView v)
     {
-      Font font = v.getFont();
-      FontMetrics fm = v.getContainer().getFontMetrics(font);
-      return fm.getDescent();
+      updateFontMetrics(v);
+      return fontMetrics.getDescent();
     }
 
     /**
      * Determines the model offset, so that the text between <code>p0</code>
      * and this offset fits within the span starting at <code>x</code> with
      * the length of <code>len</code>. 
      *
      * @param v the glyph view
      * @param p0 the starting offset in the model
      * @param x the start location in the view
      * @param len the length of the span in the view
      */
     public int getBoundedPosition(GlyphView v, int p0, float x, float len)
     {
+      updateFontMetrics(v);
       TabExpander te = v.getTabExpander();
       Segment txt = v.getText(p0, v.getEndOffset());
-      Font font = v.getFont();
-      Container c = v.getContainer();
-      FontMetrics fm;
-      if (c != null)
-        fm = c.getFontMetrics(font);
-      else
-        fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
-      int pos = Utilities.getTabbedTextOffset(txt, fm, (int) x,
+      int pos = Utilities.getTabbedTextOffset(txt, fontMetrics, (int) x,
                                               (int) (x + len), te, p0, false);
       return pos + p0;
     }
 
     /**
      * Maps a visual position into a document location.
      *
      * @param v the glyph view
      * @param x the X coordinate of the visual position
      * @param y the Y coordinate of the visual position
      * @param a the allocated region
      * @param biasRet filled with the bias of the model location on method exit
      *
      * @return the model location that represents the specified view location
      */
     public int viewToModel(GlyphView v, float x, float y, Shape a,
                            Bias[] biasRet)
     {
-      Rectangle b = a.getBounds();
-      int pos = getBoundedPosition(v, v.getStartOffset(), b.x, x - b.x);
-      return pos + v.getStartOffset();
+      Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+      int p0 = v.getStartOffset();
+      int p1 = v.getEndOffset();
+      TabExpander te = v.getTabExpander();
+      Segment s = v.getText(p0, p1);
+      int offset = Utilities.getTabbedTextOffset(s, fontMetrics, r.x, (int) x,
+                                                 te, p0);
+      int ret = p0 + offset;
+      if (ret == p1)
+        ret--;
+      biasRet[0] = Position.Bias.Forward;
+      return ret;
+    }
+
+    private void updateFontMetrics(GlyphView v)
+    {
+      Font font = v.getFont();
+      if (fontMetrics == null || ! font.equals(fontMetrics.getFont()))
+        {
+          Container c = v.getContainer();
+          FontMetrics fm;
+          if (c != null)
+            fm = c.getFontMetrics(font);
+          else
+            fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
+          fontMetrics = fm;
+        }
     }
   }
 
   /**
    * The GlyphPainer used for painting the glyphs.
    */
   GlyphPainter glyphPainter;
 
   /**
    * The start offset within the document for this view.
    */
   private int offset;
 
   /**
    * The end offset within the document for this view.
Index: javax/swing/text/View.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/View.java,v
retrieving revision 1.36
diff -u -1 -5 -r1.36 View.java
--- javax/swing/text/View.java	28 Aug 2006 15:52:05 -0000	1.36
+++ javax/swing/text/View.java	6 Nov 2006 16:02:14 -0000
@@ -80,31 +80,38 @@
    *
    * If <code>parent</code> is <code>null</code>, a call to this method also
    * calls <code>setParent</code> on the children, thus disconnecting them from
    * the view hierarchy. That means that super must be called when this method
    * is overridden.
    *
    * @param parent the parent to set, <code>null</code> when this view is
    *        beeing disconnected from the view hierarchy
    */
   public void setParent(View parent)
   {
     if (parent == null)
       {
         int numChildren = getViewCount();
         for (int i = 0; i < numChildren; i++)
-          getView(i).setParent(null);
+          {
+            View child = getView(i);
+            // It is important that we only reset the parent on views that
+            // actually belong to us. In FlowView the child may already be
+            // reparented.
+            if (child.getParent() == this)
+              child.setParent(null);
+          }
       }
 
     this.parent = parent;
   }
     
   public View getParent()
   {
     return parent;
   }
     
   public Container getContainer()
   {
     View parent = getParent();
     if (parent == null)
       return null;
@@ -250,31 +257,31 @@
   public void insert(int offset, View view)
   {
     View[] array = { view };
     replace(offset, 1, array);
   }
 
   public void append(View view)
   {
     View[] array = { view };
     int offset = getViewCount();
     replace(offset, 0, array);
   }
 
   public void removeAll()
   {
-    replace(0, getViewCount(), new View[0]); 
+    replace(0, getViewCount(), null);
   }
 
   public void remove(int index)
   {
     replace(index, 1, null); 
   }
 
   public View createFragment(int p0, int p1)
   {
     // The default implementation doesn't support fragmentation.
     return this;
   }
 
   public int getStartOffset()
   {

Reply via email to