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()
{