This fixes PlainView's tab support and viewToModel/modelToView mapping according to Intel's testsuite.

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

        * javax/swing/text/PlainView.java
        (tabBase): New field.
        (tabSize): New field.
        (updateMetrics): Update tabSize.
        (lineToRect): Only allocate when really necessary.
        (modelToView): Use tabBase for offset calculations.
        (paint): Only allocate when really necessary. Update tabBase.
        (nextTabStop): Fixed tab calculation.
        (viewToModel): Correctly handle multiline text and locations
        outside the view's bounds. Set bias.
        (getLineLength): Use tabBase.
        * javax/swing/text/Utilities.java
        (drawTabbedText): Don't special case newlines. The views
        must take care of this.

/Roman
Index: javax/swing/text/PlainView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/PlainView.java,v
retrieving revision 1.45
diff -u -1 -2 -r1.45 PlainView.java
--- javax/swing/text/PlainView.java	8 Aug 2006 10:36:34 -0000	1.45
+++ javax/swing/text/PlainView.java	11 Aug 2006 12:06:18 -0000
@@ -78,76 +78,88 @@
   float maxLineLength = -1;
   
   /** The longest line in the Document **/
   Element longestLine = null;
   
   protected FontMetrics metrics;
 
   /**
    * The instance returned by [EMAIL PROTECTED] #getLineBuffer()}.
    */
   private transient Segment lineBuffer;
 
+  /**
+   * The base offset for tab calculations.
+   */
+  private int tabBase;
+
+  /**
+   * The tab size.
+   */
+  private int tabSize;
+
   public PlainView(Element elem)
   {
     super(elem);
   }
 
   /**
    * @since 1.4
    */
   protected void updateMetrics()
   {
     Component component = getContainer();
     Font font = component.getFont();
 
     if (this.font != font)
       {
 	this.font = font;
 	metrics = component.getFontMetrics(font);
+        tabSize = getTabSize() * metrics.charWidth('m');
       }
   }
   
   /**
    * @since 1.4
    */
   protected Rectangle lineToRect(Shape a, int line)
   {
     // Ensure metrics are up-to-date.
     updateMetrics();
     
-    Rectangle rect = a.getBounds();
+    Rectangle rect = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
     int fontHeight = metrics.getHeight();
     return new Rectangle(rect.x, rect.y + (line * fontHeight),
 			 rect.width, fontHeight);
   }
 
   public Shape modelToView(int position, Shape a, Position.Bias b)
     throws BadLocationException
   {
     // Ensure metrics are up-to-date.
     updateMetrics();
     
     Document document = getDocument();
 
     // Get rectangle of the line containing position.
     int lineIndex = getElement().getElementIndex(position);
     Rectangle rect = lineToRect(a, lineIndex);
+    tabBase = rect.x;
 
     // Get the rectangle for position.
     Element line = getElement().getElement(lineIndex);
     int lineStart = line.getStartOffset();
     Segment segment = getLineBuffer();
     document.getText(lineStart, position - lineStart, segment);
-    int xoffset = Utilities.getTabbedTextWidth(segment, metrics, rect.x,
+    int xoffset = Utilities.getTabbedTextWidth(segment, metrics, tabBase,
 					       this, lineStart);
 
     // Calc the real rectangle.
     rect.x += xoffset;
     rect.width = 1;
     rect.height = metrics.getHeight();
 
     return rect;
   }
   
   /**
    * Draws a line of text. The X and Y coordinates specify the start of
@@ -253,25 +265,26 @@
   {
     // Ensure metrics are up-to-date.
     updateMetrics();
     
     JTextComponent textComponent = (JTextComponent) getContainer();
 
     selectedColor = textComponent.getSelectedTextColor();
     unselectedColor = textComponent.getForeground();
     disabledColor = textComponent.getDisabledTextColor();
     selectionStart = textComponent.getSelectionStart();
     selectionEnd = textComponent.getSelectionEnd();
 
-    Rectangle rect = s.getBounds();
+    Rectangle rect = s instanceof Rectangle ? (Rectangle) s : s.getBounds();
+    tabBase = rect.x;
 
     // FIXME: Text may be scrolled.
     Document document = textComponent.getDocument();
     Element root = getElement();
     int y = rect.y + metrics.getAscent();
     int height = metrics.getHeight();
 
     // For layered highlighters we need to paint the layered highlights
     // before painting any text.
     LayeredHighlighter hl = null;
     Highlighter h = textComponent.getHighlighter();
     if (h instanceof LayeredHighlighter)
@@ -315,26 +328,31 @@
   }
 
   /**
    * Returns the next tab stop position after a given reference position.
    *
    * This implementation ignores the <code>tabStop</code> argument.
    * 
    * @param x the current x position in pixels
    * @param tabStop the position within the text stream that the tab occured at
    */
   public float nextTabStop(float x, int tabStop)
   {
-    float tabSizePixels = getTabSize() * metrics.charWidth('m');
-    return (float) (Math.floor(x / tabSizePixels) + 1) * tabSizePixels;
+    float next = x;
+    if (tabSize != 0)
+      {
+        int numTabs = (((int) x) - tabBase) / tabSize;
+        next = tabBase + (numTabs + 1) * tabSize;
+      }
+    return next; 
   }
 
   /**
    * Returns the length of the longest line, used for getting the span
    * @return the length of the longest line
    */
   float determineMaxLineLength()
   {
     // if the longest line is cached, return the cached value
     if (maxLineLength != -1)
       return maxLineLength;
     
@@ -402,59 +420,76 @@
    * in the document model.
    *
    * @param x the x coordinate in the view space
    * @param y the y coordinate in the view space
    * @param a the allocation of this <code>View</code>
    * @param b the bias to use
    *
    * @return the position in the document that corresponds to the screen
    *         coordinates <code>x, y</code>
    */
   public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
   {
-    Rectangle rec = a.getBounds();
-    Document doc = getDocument();
-    Element root = doc.getDefaultRootElement();
-    
-    // PlainView doesn't support line-wrapping so we can find out which
-    // Element was clicked on just by the y-position.    
-    // Since the coordinates may be outside of the coordinate space
-    // of the allocation area (e.g. user dragged mouse outside
-    // the component) we have to limit the values.
-    // This has the nice effect that the user can drag the
-    // mouse above or below the component and it will still
-    // react to the x values (e.g. when selecting).
-    int lineClicked
-      = Math.min(Math.max((int) (y - rec.y) / metrics.getHeight(), 0),
-                          root.getElementCount() - 1);
-    
-    Element line = root.getElement(lineClicked);
-    
-    Segment s = getLineBuffer();
-    int start = line.getStartOffset();
-    // We don't want the \n at the end of the line.
-    int end = line.getEndOffset() - 1;
-    try
-      {
-        doc.getText(start, end - start, s);
-      }
-    catch (BadLocationException ble)
+    Rectangle rec = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+    tabBase = rec.x;
+
+    int pos;
+    if ((int) y < rec.y)
+      // Above our area vertically. Return start offset.
+      pos = getStartOffset();
+    else if ((int) y > rec.y + rec.height)
+      // Below our area vertically. Return end offset.
+      pos = getEndOffset() - 1;
+    else
       {
-        AssertionError ae = new AssertionError("Unexpected bad location");
-        ae.initCause(ble);
-        throw ae;
+        // Inside the allocation vertically. Determine line and X offset.
+        Document doc = getDocument();
+        Element root = doc.getDefaultRootElement();
+        int line = Math.abs(((int) y - rec.y) / metrics.getHeight());
+        if (line >= root.getElementCount())
+          pos = getEndOffset() - 1;
+        else
+          {
+            Element lineEl = root.getElement(line);
+            if (x < rec.x)
+              // To the left of the allocation area.
+              pos = lineEl.getStartOffset();
+            else if (x > rec.x + rec.width)
+              // To the right of the allocation area.
+              pos = lineEl.getEndOffset() - 1;
+            else
+              {
+                try
+                  {
+                    int p0 = lineEl.getStartOffset();
+                    int p1 = lineEl.getEndOffset();
+                    Segment s = new Segment();
+                    doc.getText(p0, p1 - p0, s);
+                    tabBase = rec.x;
+                    pos = p0 + Utilities.getTabbedTextOffset(s, metrics,
+                                                             tabBase, (int) x,
+                                                             this, p0);
+                  }
+                catch (BadLocationException ex)
+                  {
+                    // Should not happen.
+                    pos = -1;
+                  }
+              }
+            
+          }
       }
-    
-    int pos = Utilities.getTabbedTextOffset(s, metrics, rec.x, (int)x, this, start);
-    return Math.max (0, pos);
+    // Bias is always forward.
+    b[0] = Position.Bias.Forward;
+    return pos;
   }     
   
   /**
    * Since insertUpdate and removeUpdate each deal with children
    * Elements being both added and removed, they both have to perform
    * the same checks.  So they both simply call this method.
    * @param changes the DocumentEvent for the changes to the Document.
    * @param a the allocation of the View.
    * @param f the ViewFactory to use for rebuilding.
    */
   protected void updateDamage(DocumentEvent changes, Shape a, ViewFactory f)
   {
@@ -666,17 +701,17 @@
         Document doc = getDocument();
         doc.getText(lineEl.getStartOffset(),
                     lineEl.getEndOffset() - lineEl.getStartOffset() - 1,
                     buffer);
       }
     catch (BadLocationException ex)
       {
         AssertionError err = new AssertionError("Unexpected bad location");
         err.initCause(ex);
         throw err;
       }
 
-    return Utilities.getTabbedTextWidth(buffer, metrics, 0, this,
+    return Utilities.getTabbedTextWidth(buffer, metrics, tabBase, this,
                                         lineEl.getStartOffset());
   }
 }
 
Index: javax/swing/text/Utilities.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/Utilities.java,v
retrieving revision 1.33
diff -u -1 -2 -r1.33 Utilities.java
--- javax/swing/text/Utilities.java	13 May 2006 11:30:05 -0000	1.33
+++ javax/swing/text/Utilities.java	11 Aug 2006 12:06:18 -0000
@@ -34,25 +34,24 @@
 this exception to your version of the library, but you are not
 obligated to do so.  If you do not wish to do so, delete this
 exception statement from your version. */
 
 
 package javax.swing.text;
 
 import java.awt.FontMetrics;
 import java.awt.Graphics;
 import java.awt.Point;
 import java.text.BreakIterator;
 
-import javax.swing.SwingConstants;
 import javax.swing.text.Position.Bias;
 
 /**
  * A set of utilities to deal with text. This is used by several other classes
  * inside this package.
  *
  * @author Roman Kennke ([EMAIL PROTECTED])
  * @author Robert Schuster ([EMAIL PROTECTED])
  */
 public class Utilities
 {
   /**
@@ -100,51 +99,46 @@
     int pixelX = x;
     int pixelY = y - ascent;
 
     int pixelWidth = 0;
     int pos = s.offset;
     int len = 0;
     
     int end = s.offset + s.count;
 
     for (int offset = s.offset; offset < end; ++offset)
       {
         char c = buffer[offset];
-        if (c == '\t' || c == '\n')
+        if (c == '\t')
           {
             if (len > 0) {
               g.drawChars(buffer, pos, len, pixelX, pixelY + ascent);            
               pixelX += pixelWidth;
               pixelWidth = 0;
             }
             pos = offset+1;
             len = 0;
           }
         
 	switch (c)
 	  {
 	  case '\t':
 	    // In case we have a tab, we just 'jump' over the tab.
 	    // When we have no tab expander we just use the width of ' '.
 	    if (e != null)
 	      pixelX = (int) e.nextTabStop((float) pixelX,
 					   startOffset + offset - s.offset);
 	    else
 	      pixelX += metrics.charWidth(' ');
 	    break;
-	  case '\n':
-	    // In case we have a newline, we must jump to the next line.
-	    pixelY += metrics.getHeight();
-	    pixelX = x;
-	    break;
 	  default:
             ++len;
 	    pixelWidth += metrics.charWidth(buffer[offset]);
 	    break;
 	  }
       }
 
     if (len > 0)
       g.drawChars(buffer, pos, len, pixelX, pixelY + ascent);            
     
     return pixelX + pixelWidth;
   }

Reply via email to