Hi,
I am surprised myself but I really got this working. :)

What I fixed here are mainly drawing problems. The underlying representation of
the highlighted area was mostly correct. There are some small glitches in the
behavior which I am going to fix next.

Please comment the patch.

The ChangeLog:

2006-02-21  Robert Schuster  <[EMAIL PROTECTED]>

        * javax/swing/plaf/basic/BasicTextUI.java:
        (paint): Remove unneccessary part of the if-expression.
        (damageRange): Added case where the range spans multiple lines.
        * javax/swing/text/DefaultCaret.java:
        (clearHighlight): New method.
        (handleHighlight): Removed unneccessary part of the if-expression.
        (setDot): Use clearHighlight method.
        * javax/swing/text/DefaultHighlighter.java: Use ArrayList instead
        of Vector.
        (paint): Prevented calling size() on every loop iteration, fixed
        calculation of allocation area bounds.
        (getHighlights): Implemented.
        (removeHighlight): Mark damaged area in textcomponent.
        (addHighlight): Mark damaged area in textcomponent.
        (changeHighlight): Mark damaged area in textcomponent.
        (DefaultHighlighter.HighlightEntry): Made it a real
        Highlighter.Highlight implementation.
        (DefaultHighlighter.DefaultHighlightPainter.paint): Fixed
        calculations.

cya
Robert

Index: javax/swing/text/DefaultCaret.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/DefaultCaret.java,v
retrieving revision 1.30
diff -u -r1.30 DefaultCaret.java
--- javax/swing/text/DefaultCaret.java	9 Feb 2006 17:15:33 -0000	1.30
+++ javax/swing/text/DefaultCaret.java	20 Feb 2006 23:28:47 -0000
@@ -577,7 +577,39 @@
   {
     return mark;
   }
-
+  
+  private void clearHighlight()
+  {
+    Highlighter highlighter = textComponent.getHighlighter();
+    
+    if (highlighter == null)
+      return;
+    
+    if (selectionVisible)
+      {
+    try
+      {
+        if (highlightEntry == null)
+          highlightEntry = highlighter.addHighlight(0, 0, getSelectionPainter());
+        else
+          highlighter.changeHighlight(highlightEntry, 0, 0);
+      }
+    catch (BadLocationException e)
+      {
+        // This should never happen.
+        throw new InternalError();
+      }
+      }
+    else
+      {
+    if (highlightEntry != null)
+      {
+        highlighter.removeHighlight(highlightEntry);
+        highlightEntry = null;
+      }
+      }
+  }
+  
   private void handleHighlight()
   {
     Highlighter highlighter = textComponent.getHighlighter();
@@ -588,7 +620,7 @@
     int p0 = Math.min(dot, mark);
     int p1 = Math.max(dot, mark);
     
-    if (selectionVisible && p0 != p1)
+    if (selectionVisible)
       {
 	try
 	  {
@@ -818,6 +850,7 @@
         if (doc != null)
           this.dot = Math.min(dot, doc.getLength());
         this.dot = Math.max(this.dot, 0);
+        
         handleHighlight();
         adjustVisibility(this);
         appear();
@@ -842,7 +875,8 @@
           this.dot = Math.min(dot, doc.getLength());
         this.dot = Math.max(this.dot, 0);
         this.mark = this.dot;
-        handleHighlight();
+        
+        clearHighlight();
         adjustVisibility(this);
         appear();
       }
Index: javax/swing/text/DefaultHighlighter.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/DefaultHighlighter.java,v
retrieving revision 1.6
diff -u -r1.6 DefaultHighlighter.java
--- javax/swing/text/DefaultHighlighter.java	19 Oct 2005 14:57:30 -0000	1.6
+++ javax/swing/text/DefaultHighlighter.java	20 Feb 2006 23:28:47 -0000
@@ -40,9 +40,10 @@
 
 import java.awt.Color;
 import java.awt.Graphics;
+import java.awt.Insets;
 import java.awt.Rectangle;
 import java.awt.Shape;
-import java.util.Vector;
+import java.util.ArrayList;
 
 public class DefaultHighlighter extends LayeredHighlighter
 {
@@ -84,7 +85,7 @@
 	  // This should never occur.
           return;
 	}
-
+      
       if (r0 == null || r1 == null)
 	return;
 
@@ -100,7 +101,7 @@
 	  paintHighlight(g, r0);
 	  return;
 	}
-
+      
       // First line, from p0 to end-of-line.
       r0.width = rect.x + rect.width - r0.x;
       paintHighlight(g, r0);
@@ -109,15 +110,17 @@
       // have the same height -- not a good assumption with JEditorPane/JTextPane).
       r0.y += r0.height;
       r0.x = rect.x;
-
+      r0.width = rect.width;
+      
       while (r0.y < r1.y)
 	{
 	  paintHighlight(g, r0);
 	  r0.y += r0.height;
 	}
 
-      // Last line, from beginnin-of-line to p1.
-      paintHighlight(g, r1);
+      // Last line, from beginning-of-line to p1.
+      r0.width = r1.x + r1.width;
+      paintHighlight(g, r0);
     }
 
     public Shape paintLayer(Graphics g, int p0, int p1, Shape bounds,
@@ -127,7 +130,7 @@
     }
   }
   
-  private class HighlightEntry
+  private class HighlightEntry implements Highlighter.Highlight
   {
     int p0;
     int p1;
@@ -140,12 +143,12 @@
       this.painter = painter;
     }
 
-    public int getStartPosition()
+    public int getStartOffset()
     {
       return p0;
     }
 
-    public int getEndPosition()
+    public int getEndOffset()
     {
       return p1;
     }
@@ -163,7 +166,7 @@
     new DefaultHighlightPainter(null);
   
   private JTextComponent textComponent;
-  private Vector highlights = new Vector();
+  private ArrayList highlights = new ArrayList();
   private boolean drawsLayeredHighlights = true;
   
   public DefaultHighlighter()
@@ -208,12 +211,20 @@
     checkPositions(p0, p1);
     HighlightEntry entry = new HighlightEntry(p0, p1, painter);
     highlights.add(entry);
+    
+    textComponent.getUI().damageRange(textComponent, p0, p1);
+    
     return entry;
   }
 
   public void removeHighlight(Object tag)
   {
     highlights.remove(tag);
+
+    HighlightEntry entry = (HighlightEntry) tag;
+    textComponent.getUI().damageRange(textComponent,
+                                      entry.p0,
+                                      entry.p1);
   }
 
   public void removeAllHighlights()
@@ -223,16 +234,30 @@
 
   public Highlighter.Highlight[] getHighlights()
   {
-    return null;
+    return (Highlighter.Highlight[]) 
+      highlights.toArray(new Highlighter.Highlight[highlights.size()]);
   }
 
   public void changeHighlight(Object tag, int p0, int p1)
     throws BadLocationException
   {
+    int o0, o1;
+    
     checkPositions(p0, p1);
     HighlightEntry entry = (HighlightEntry) tag;
+    o0 = entry.p0;
+    o1 = entry.p1;
+    
+    // Prevent useless write & repaint operations.
+    if (o0 == o1 && o1 == p1)
+      return;
+    
     entry.p0 = p0;
     entry.p1 = p1;
+    
+    textComponent.getUI().damageRange(textComponent,
+                                      Math.min(p0, o0),
+                                      Math.max(p1, o1));
   }
 
   public void paintLayeredHighlights(Graphics g, int p0, int p1,
@@ -244,13 +269,21 @@
 
   public void paint(Graphics g)
   {
+    int size = highlights.size();
+    
     // Check if there are any highlights.
-    if (highlights.size() == 0)
+    if (size == 0)
       return;
+
+    // Prepares the rectangle of the inner drawing area.
+    Insets insets = textComponent.getInsets();
+    Shape bounds =
+      new Rectangle(insets.left,
+                    insets.top,
+                    textComponent.getWidth() - insets.left - insets.right,
+                    textComponent.getHeight() - insets.top - insets.bottom);
     
-    Shape bounds = textComponent.getBounds();
-    
-    for (int index = 0; index < highlights.size(); ++index)
+    for (int index = 0; index < size; ++index)
       {
 	HighlightEntry entry = (HighlightEntry) highlights.get(index);
 	entry.painter.paint(g, entry.p0, entry.p1, bounds, textComponent);
Index: javax/swing/plaf/basic/BasicTextUI.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/plaf/basic/BasicTextUI.java,v
retrieving revision 1.69
diff -u -r1.69 BasicTextUI.java
--- javax/swing/plaf/basic/BasicTextUI.java	20 Feb 2006 12:40:42 -0000	1.69
+++ javax/swing/plaf/basic/BasicTextUI.java	20 Feb 2006 23:28:47 -0000
@@ -79,6 +79,7 @@
 import javax.swing.text.JTextComponent;
 import javax.swing.text.Keymap;
 import javax.swing.text.Position;
+import javax.swing.text.Utilities;
 import javax.swing.text.View;
 import javax.swing.text.ViewFactory;
 
@@ -875,9 +876,19 @@
     if (textComponent.isOpaque())
       paintBackground(g);
 
-    if (highlighter != null
-        && textComponent.getSelectionStart() != textComponent.getSelectionEnd())
-      highlighter.paint(g);
+    // Try painting with the highlighter without checking whether there
+    // is a selection because a highlighter can be used to do more than
+    // marking selected text.
+    if (highlighter != null)
+      {
+        // Handle restoring of the color here to prevent
+        // drawing problems when the Highlighter implementor
+        // forgets to restore it.
+        Color oldColor = g.getColor();
+        highlighter.paint(g);
+        g.setColor(oldColor);
+      }
+      
 
     rootView.paint(g, getVisibleEditorRect());
 
@@ -945,9 +956,75 @@
   {
     try
       {
+        // Limit p0 and p1 to sane values to prevent unfriendly
+        // BadLocationExceptions. This makes it possible for the highlighter
+        // to send us illegal values which can happen when a large number
+        // of selected characters are removed (eg. by pressing delete
+        // or backspace).
+        // The reference implementation does not throw an exception, too.
+        p0 = Math.min(p0, t.getDocument().getLength());
+        p1 = Math.min(p1, t.getDocument().getLength());
+        
         Rectangle l1 = modelToView(t, p0, firstBias);
         Rectangle l2 = modelToView(t, p1, secondBias);
-        t.repaint(l1.union(l2));
+        if (l1.y == l2.y)
+          t.repaint(l1.union(l2));
+        else
+          {
+            // The two rectangles lie on different lines and we need a
+            // different algorithm to calculate the damaged area:
+            // 1. The line of p0 is damaged from the position of p0
+            // to the right border.
+            // 2. All lines between the ones where p0 and p1 lie on
+            // are completely damaged. Use the allocation area to find
+            // out the bounds.
+            // 3. The final line is damaged from the left bound to the
+            // position of p1.
+            Insets insets = t.getInsets();
+
+            // Damage first line until the end.
+            l1.width = insets.right + t.getWidth() - l1.x;
+            t.repaint(l1);
+            
+            // Note: Utilities.getPositionBelow() may return the offset
+            // that was put in. In that case there is no next line and
+            // we should stop searching for one.
+            
+            int posBelow = Utilities.getPositionBelow(t, p0, l1.x);
+            if (posBelow < p1 && posBelow != -1 && posBelow != p0)
+              {
+                // Take the rectangle of the offset we just found and grow it
+                // to the maximum width. Retain y because this is our start
+                // height.
+                Rectangle grow = modelToView(t, posBelow);
+                grow.x = insets.left;
+                grow.width = t.getWidth() + insets.right;
+                
+                // Find further lines which have to be damaged completely.
+                int nextPosBelow = posBelow;
+                while (nextPosBelow < p1 && nextPosBelow != -1 && posBelow != nextPosBelow)
+                  {
+                    posBelow = nextPosBelow;
+                    nextPosBelow = Utilities.getPositionBelow(t, posBelow, l1.x);
+                  }
+                // Now posBelow is an offset on the last line which has to be damaged
+                // completely. (newPosBelow is on the same line as p1)
+                 
+                // Retrieve the rectangle of posBelow and use its y and height
+                // value to calculate the final height of the multiple line
+                // spanning rectangle.
+                Rectangle end = modelToView(t, posBelow);
+                grow.height = end.y + end.height - grow.y;
+                
+                // Mark that area as damage.
+                t.repaint(grow);
+              }
+            
+            // Damage last line from its beginning to the position of p1.
+            l2.width += l2.x;
+            l2.x = insets.left;
+            t.repaint(l2);
+          }
       }
     catch (BadLocationException ex)
       {

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to