Here comes another update on javax.swing.text. Basically this resolves
the problem with removing characters in styled text components (observed
in BeanShell).
2006-08-05 Roman Kennke <[EMAIL PROTECTED]>
* javax/swing/plaf/basic/BasicTextUI.java
(modelToView): Read-lock the document. Set size of the
root view before fetching the model-to-view mapping.
(getViewIndex): Check of the position is inside the range and
return -1 if this is not the case.
(getViewAtPosition(int,Rectangle): Update child allocation for valid
view index.
(getViewIndexAtPosition(int)): Delegate the index search to
the element since we have a 1:1 mapping between elements and
views here.
* javax/swing/text/DefaultCaret.java
(appear): Ignore BadLocationException.
(paint): Ignore BadLocationException.
* javax/swing/text/FlowView.java
(changedUpdate): Also notify the layoutPool view.
(removeUpdate): Also notify the layoutPool view.
* javax/swing/text/ParagraphView.java
(Row.getViewIndexAtPosition): Overridden to search linearily
through the view instead of relying on a 1:1 model to view
mapping.
* javax/swing/text/View.java
(removeUpdate): Clear ElementChange object if updateChildren
returns false.
(forwardUpdate): Special handle some boundary cases.
/Roman
Index: javax/swing/plaf/basic/BasicTextUI.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/plaf/basic/BasicTextUI.java,v
retrieving revision 1.88
diff -u -1 -2 -r1.88 BasicTextUI.java
--- javax/swing/plaf/basic/BasicTextUI.java 5 Aug 2006 12:13:19 -0000 1.88
+++ javax/swing/plaf/basic/BasicTextUI.java 7 Aug 2006 11:15:15 -0000
@@ -1178,28 +1178,47 @@
* <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 Rectangle modelToView(JTextComponent t, int pos, Position.Bias bias)
throws BadLocationException
{
- Rectangle r = getVisibleEditorRect();
-
- return (r != null) ? rootView.modelToView(pos, r, bias).getBounds()
- : null;
+ // We need to read-lock here because we depend on the document
+ // structure not beeing changed in between.
+ Document doc = textComponent.getDocument();
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ Rectangle rect = null;
+ try
+ {
+ Rectangle r = getVisibleEditorRect();
+ if (r != null)
+ {
+ rootView.setSize(r.width, r.height);
+ Shape s = rootView.modelToView(pos, r, bias);
+ if (s != null)
+ rect = s.getBounds();
+ }
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ return rect;
}
/**
* Maps a point in the <code>View</code> coordinate space to a position
* inside a document model.
*
* @param t the text component
* @param pt the point to be mapped
*
* @return the position inside the document model that corresponds to
* <code>pt</code>
*/
Index: javax/swing/text/CompositeView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/CompositeView.java,v
retrieving revision 1.19
diff -u -1 -2 -r1.19 CompositeView.java
--- javax/swing/text/CompositeView.java 5 Aug 2006 12:13:21 -0000 1.19
+++ javax/swing/text/CompositeView.java 7 Aug 2006 11:15:16 -0000
@@ -212,25 +212,25 @@
*
* @throws BadLocationException if <code>pos</code> is invalid
* @throws IllegalArgumentException if b is not one of the above listed
* valid values
*/
public Shape modelToView(int pos, Shape a, Position.Bias bias)
throws BadLocationException
{
boolean backward = bias == Position.Bias.Backward;
int testpos = backward ? Math.max(0, pos - 1) : pos;
Shape ret = null;
- if (!backward || testpos >= getStartOffset())
+ if (! backward || testpos >= getStartOffset())
{
int childIndex = getViewIndexAtPosition(testpos);
if (childIndex != -1 && childIndex < getViewCount())
{
View child = getView(childIndex);
if (child != null && testpos >= child.getStartOffset()
&& testpos < child.getEndOffset())
{
Shape childAlloc = getChildAllocation(childIndex, a);
if (childAlloc != null)
{
ret = child.modelToView(pos, childAlloc, bias);
@@ -387,25 +387,28 @@
* model location.
*
* @param pos the model location for which to determine the child view index
* @param b the bias to be applied to <code>pos</code>
*
* @return the index of the child view that represents the specified
* model location
*/
public int getViewIndex(int pos, Position.Bias b)
{
if (b == Position.Bias.Backward && pos != 0)
pos -= 1;
- return getViewIndexAtPosition(pos);
+ int i = -1;
+ if (pos >= getStartOffset() && pos < getEndOffset())
+ i = getViewIndexAtPosition(pos);
+ return i;
}
/**
* Returns <code>true</code> if the specified point lies before the
* given <code>Rectangle</code>, <code>false</code> otherwise.
*
* "Before" is typically defined as being to the left or above.
*
* @param x the X coordinate of the point
* @param y the Y coordinate of the point
* @param r the rectangle to test the point against
*
@@ -455,53 +458,50 @@
/**
* Returns the child <code>View</code> that contains the given model
* position. The given <code>Rectangle</code> gives the parent's allocation
* and is changed to the child's allocation on exit.
*
* @param pos the model position to query the child <code>View</code> for
* @param a the parent allocation on entry and the child allocation on exit
*
* @return the child view at the given model position
*/
protected View getViewAtPosition(int pos, Rectangle a)
{
+ View view = null;
int i = getViewIndexAtPosition(pos);
- View view = children[i];
- childAllocation(i, a);
+ if (i >= 0 && i < getViewCount() && a != null)
+ {
+ view = getView(i);
+ childAllocation(i, a);
+ }
return view;
}
/**
* Returns the index of the child <code>View</code> for the given model
* position.
*
* @param pos the model position for whicht the child <code>View</code> is
* queried
*
* @return the index of the child <code>View</code> for the given model
* position
*/
protected int getViewIndexAtPosition(int pos)
{
- int index = -1;
- for (int i = 0; i < children.length; i++)
- {
- if (children[i].getStartOffset() <= pos
- && children[i].getEndOffset() > pos)
- {
- index = i;
- break;
- }
- }
- return index;
+ // We have a 1:1 mapping of elements to views here, so we forward
+ // this to the element.
+ Element el = getElement();
+ return el.getElementIndex(pos);
}
/**
* Returns the allocation that is given to this <code>CompositeView</code>
* minus this <code>CompositeView</code>'s insets.
*
* Also this translates from an immutable allocation to a mutable allocation
* that is typically reused and further narrowed, like in
* [EMAIL PROTECTED] #childAllocation}.
*
* @param a the allocation given to this <code>CompositeView</code>
*
Index: javax/swing/text/DefaultCaret.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/DefaultCaret.java,v
retrieving revision 1.44
diff -u -1 -2 -r1.44 DefaultCaret.java
--- javax/swing/text/DefaultCaret.java 26 Jul 2006 19:23:01 -0000 1.44
+++ javax/swing/text/DefaultCaret.java 7 Aug 2006 11:15:17 -0000
@@ -902,28 +902,28 @@
// Make sure the dot has a sane position.
dot = Math.min(dot, textComponent.getDocument().getLength());
dot = Math.max(dot, 0);
Rectangle rect = null;
try
{
rect = textComponent.modelToView(dot);
}
catch (BadLocationException e)
{
- AssertionError ae;
- ae = new AssertionError("Unexpected bad caret location: " + dot);
- ae.initCause(e);
- throw ae;
+ // Let's ignore that. This shouldn't really occur. But if it
+ // does (it seems that this happens when the model is mutating),
+ // it causes no real damage. Uncomment this for debugging.
+ // e.printStackTrace();
}
if (rect == null)
return;
// Check if paint has possibly been called directly, without a previous
// call to damage(). In this case we need to do some cleanup first.
if ((x != rect.x) || (y != rect.y))
{
repaint(); // Erase previous location of caret.
x = rect.x;
y = rect.y;
@@ -1139,28 +1139,28 @@
// Draw the caret in the new position.
visible = true;
Rectangle area = null;
int dot = getDot();
try
{
area = getComponent().modelToView(dot);
}
catch (BadLocationException e)
{
- AssertionError ae;
- ae = new AssertionError("Unexpected bad caret location: " + dot);
- ae.initCause(e);
- throw ae;
+ // Let's ignore that. This shouldn't really occur. But if it
+ // does (it seems that this happens when the model is mutating),
+ // it causes no real damage. Uncomment this for debugging.
+ // e.printStackTrace();
}
if (area != null)
damage(area);
}
repaint();
}
/**
* Returns <code>true</code> if this <code>Caret</code> is blinking,
* and <code>false</code> if not. The returned value is independent of
* the visiblity of this <code>Caret</code> as returned by [EMAIL PROTECTED] #isVisible()}.
*
Index: javax/swing/text/FlowView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/FlowView.java,v
retrieving revision 1.14
diff -u -1 -2 -r1.14 FlowView.java
--- javax/swing/text/FlowView.java 5 Aug 2006 12:13:21 -0000 1.14
+++ javax/swing/text/FlowView.java 7 Aug 2006 11:15:17 -0000
@@ -515,39 +515,41 @@
/**
* Receice notification that some content has been removed from the region
* that this view is responsible for. This calls
* [EMAIL PROTECTED] FlowStrategy#removeUpdate}.
*
* @param changes the document event describing the changes
* @param a the current allocation of the view
* @param vf the view factory that is used for creating new child views
*/
public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory vf)
{
+ layoutPool.removeUpdate(changes, a, vf);
strategy.removeUpdate(this, changes, getInsideAllocation(a));
layoutDirty = true;
}
/**
* Receice notification that some attributes changed in the region
* that this view is responsible for. This calls
* [EMAIL PROTECTED] FlowStrategy#changedUpdate}.
*
* @param changes the document event describing the changes
* @param a the current allocation of the view
* @param vf the view factory that is used for creating new child views
*/
public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory vf)
{
+ layoutPool.changedUpdate(changes, a, vf);
strategy.changedUpdate(this, changes, getInsideAllocation(a));
layoutDirty = true;
}
/**
* Returns the index of the child <code>View</code> for the given model
* position.
*
* This is implemented to iterate over the children of this
* view (the rows) and return the index of the first view that contains
* the given position.
*
Index: javax/swing/text/ParagraphView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/ParagraphView.java,v
retrieving revision 1.8
diff -u -1 -2 -r1.8 ParagraphView.java
--- javax/swing/text/ParagraphView.java 5 Aug 2006 12:13:21 -0000 1.8
+++ javax/swing/text/ParagraphView.java 7 Aug 2006 11:15:17 -0000
@@ -78,24 +78,44 @@
* Allows rows to span the whole parent view.
*/
public float getMaximumSpan(int axis)
{
float max;
if (axis == X_AXIS)
max = Float.MAX_VALUE;
else
max = super.getMaximumSpan(axis);
return max;
}
+ /**
+ * Overridden because child views are not necessarily laid out in model
+ * order.
+ */
+ protected int getViewIndexAtPosition(int pos)
+ {
+ int index = -1;
+ if (pos >= getStartOffset() && pos < getEndOffset())
+ {
+ int nviews = getViewCount();
+ for (int i = 0; i < nviews && index == -1; i++)
+ {
+ View child = getView(i);
+ if (pos >= child.getStartOffset() && pos < child.getEndOffset())
+ index = i;
+ }
+ }
+ return index;
+ }
+
protected void loadChildren(ViewFactory vf)
{
// Do nothing here. The children are added while layouting.
}
}
/**
* The indentation of the first line of the paragraph.
*/
protected int firstLineIndent;
/**
Index: javax/swing/text/View.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/View.java,v
retrieving revision 1.33
diff -u -1 -2 -r1.33 View.java
--- javax/swing/text/View.java 5 Aug 2006 14:14:11 -0000 1.33
+++ javax/swing/text/View.java 7 Aug 2006 11:15:19 -0000
@@ -392,25 +392,28 @@
* repair its layout, reschedule layout or do nothing at all.</li>
* </ul>
*
* @param ev the DocumentEvent that describes the change
* @param shape the shape of the view
* @param vf the ViewFactory for creating child views
*/
public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
{
Element el = getElement();
DocumentEvent.ElementChange ec = ev.getChange(el);
if (ec != null)
- updateChildren(ec, ev, vf);
+ {
+ if (! updateChildren(ec, ev, vf))
+ ec = null;
+ }
forwardUpdate(ec, ev, shape, vf);
updateLayout(ec, ev, shape);
}
/**
* Receive notification about a change update to the text model.
*
* The default implementation of this method does the following:
* <ul>
* <li>Call [EMAIL PROTECTED] #updateChildren} if the element that this view is
* responsible for has changed. This makes sure that the children can
* correctly represent the model.<li>
@@ -484,45 +487,84 @@
* @param ev the DocumentEvent describing the changes to the model
* @param shape the current allocation of the view
* @param vf the ViewFactory used to create new Views
*
* @since 1.3
*/
protected void forwardUpdate(DocumentEvent.ElementChange ec,
DocumentEvent ev, Shape shape, ViewFactory vf)
{
int count = getViewCount();
if (count > 0)
{
+ // Determine start index.
int startOffset = ev.getOffset();
- int endOffset = startOffset + ev.getLength();
int startIndex = getViewIndex(startOffset, Position.Bias.Backward);
- int endIndex = getViewIndex(endOffset, Position.Bias.Forward);
- int index = -1;
- int addLength = -1;
- if (ec != null)
+
+ // For REMOVE events we have to forward the event to the last element,
+ // for the case that an Element has been removed that represente
+ // the offset.
+ if (startIndex == -1 && ev.getType() == DocumentEvent.EventType.REMOVE
+ && startOffset >= getEndOffset())
+ {
+ startIndex = getViewCount() - 1;
+ }
+
+ // When startIndex is on a view boundary, forward event to the
+ // previous view too.
+ if (startIndex >= 0)
+ {
+ View v = getView(startIndex);
+ if (v != null)
+ {
+ if (v.getStartOffset() == startOffset && startOffset > 0)
+ startIndex = Math.max(0, startIndex - 1);
+ }
+ }
+ startIndex = Math.max(0, startIndex);
+
+ // Determine end index.
+ int endIndex = startIndex;
+ if (ev.getType() != DocumentEvent.EventType.REMOVE)
+ {
+ endIndex = getViewIndex(startOffset + ev.getLength(),
+ Position.Bias.Forward);
+ if (endIndex < 0)
+ endIndex = getViewCount() - 1;
+ }
+
+ // Determine hole that comes from added elements (we don't forward
+ // the event to newly added views.
+ int startAdded = endIndex + 1;
+ int endAdded = startAdded;
+ Element[] added = (ec != null) ? ec.getChildrenAdded() : null;
+ if (added != null && added.length > 0)
{
- index = ec.getIndex();
- addLength = ec.getChildrenAdded().length;
+ startAdded = ec.getIndex();
+ endAdded = startAdded + added.length - 1;
}
- if (startIndex >= 0 && endIndex >= 0)
+ // Forward event to all views between startIndex and endIndex,
+ // and leave out all views in the hole.
+ for (int i = startIndex; i <= endIndex; i++)
{
- for (int i = startIndex; i <= endIndex; i++)
+ // Skip newly added child views.
+ if (! (i >= startAdded && i <= endAdded))
{
- // Skip newly added child views.
- if (index >= 0 && i >= index && i < (index+addLength))
- continue;
View child = getView(i);
- forwardUpdateToView(child, ev, shape, vf);
+ if (child != null)
+ {
+ Shape childAlloc = getChildAllocation(i, shape);
+ forwardUpdateToView(child, ev, childAlloc, vf);
+ }
}
}
}
}
/**
* Forwards an update event to the given child view. This calls
* [EMAIL PROTECTED] #insertUpdate}, [EMAIL PROTECTED] #removeUpdate} or [EMAIL PROTECTED] #changedUpdate},
* depending on the type of document event.
*
* @param view the View to forward the event to
* @param ev the DocumentEvent to forward