This fixes a couple of layout issues with the HTML renderer, effectively
improving the ability to wrap paragraphs correctly:
http://kennke.org/~roman/planet4.png
http://kennke.org/~roman/japi3.png
It also contains performance fixes in Utilities, so rendering should be
a little snappier now.
2006-11-16 Roman Kennke <[EMAIL PROTECTED]>
* javax/swing/text/FlowView.java
(LogicalView.getPreferredSpan): Calculate maximum correctly.
* javax/swing/text/GlyphView.java
(tabExpander): New field.
(tabX): New field.
(breakView): Set tabX on broken view.
(getPartialSpan): Let the painter fetch the span.
(getTabbedSpan): Update the tab expander field. Maybe trigger
relayout.
(getTabExpander): Simply return the stored expander.
* javax/swing/text/Utilities.java
(getTabbedTextOffset): Made algoritm a little smarter and more
efficient.
(getTabbedTextWidth): Don't add single char widths, instead add
chunks of characters.
* javax/swing/text/html/ParagraphView.java
(calculateMinorAxisRequirements): Adjust margin only when the
CSS span is not fixed.
/Roman
Index: javax/swing/text/FlowView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/FlowView.java,v
retrieving revision 1.19
diff -u -1 -5 -r1.19 FlowView.java
--- javax/swing/text/FlowView.java 6 Nov 2006 16:02:54 -0000 1.19
+++ javax/swing/text/FlowView.java 16 Nov 2006 12:59:46 -0000
@@ -476,31 +476,31 @@
return false;
}
public float getPreferredSpan(int axis)
{
float max = 0;
float pref = 0;
int n = getViewCount();
for (int i = 0; i < n; i++)
{
View v = getView(i);
pref += v.getPreferredSpan(axis);
if (v.getBreakWeight(axis, 0, Integer.MAX_VALUE)
>= ForcedBreakWeight)
{
- max = Math.max(pref, pref);
+ max = Math.max(max, pref);
pref = 0;
}
}
max = Math.max(max, pref);
return max;
}
public float getMinimumSpan(int axis)
{
float max = 0;
float min = 0;
boolean wrap = true;
int n = getViewCount();
for (int i = 0; i < n; i++)
{
Index: javax/swing/text/GlyphView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/GlyphView.java,v
retrieving revision 1.24
diff -u -1 -5 -r1.24 GlyphView.java
--- javax/swing/text/GlyphView.java 6 Nov 2006 16:02:54 -0000 1.24
+++ javax/swing/text/GlyphView.java 16 Nov 2006 12:59:47 -0000
@@ -485,30 +485,40 @@
* 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.
*/
private int length;
/**
+ * The x location against which the tab expansion is done.
+ */
+ private float tabX;
+
+ /**
+ * The tab expander that is used in this view.
+ */
+ private TabExpander tabExpander;
+
+ /**
* Creates a new <code>GlyphView</code> for the given <code>Element</code>.
*
* @param element the element that is rendered by this GlyphView
*/
public GlyphView(Element element)
{
super(element);
offset = 0;
length = 0;
}
/**
* Returns the <code>GlyphPainter</code> that is used by this
* <code>GlyphView</code>. If no <code>GlyphPainer</code> has been installed
* <code>null</code> is returned.
@@ -646,82 +656,69 @@
*/
public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
{
checkPainter();
GlyphPainter painter = getGlyphPainter();
return painter.viewToModel(this, x, y, a, b);
}
/**
* Return the [EMAIL PROTECTED] TabExpander} to use.
*
* @return the [EMAIL PROTECTED] TabExpander} to use
*/
public TabExpander getTabExpander()
{
- TabExpander te = null;
- View parent = getParent();
-
- if (parent instanceof TabExpander)
- te = (TabExpander) parent;
-
- return te;
+ return tabExpander;
}
/**
* Returns the preferred span of this view for tab expansion.
*
* @param x the location of the view
* @param te the tab expander to use
*
* @return the preferred span of this view for tab expansion
*/
public float getTabbedSpan(float x, TabExpander te)
{
checkPainter();
+ TabExpander old = tabExpander;
+ tabExpander = te;
+ if (tabExpander != old)
+ {
+ // Changing the tab expander will lead to a relayout in the X_AXIS.
+ preferenceChanged(null, true, false);
+ }
+ tabX = x;
return getGlyphPainter().getSpan(this, getStartOffset(),
- getEndOffset(), te, x);
+ getEndOffset(), tabExpander, x);
}
/**
* Returns the span of a portion of the view. This is used in TAB expansion
* for fragments that don't contain TABs.
*
* @param p0 the start index
* @param p1 the end index
*
* @return the span of the specified portion of the view
*/
public float getPartialSpan(int p0, int p1)
{
- Element el = getElement();
- Document doc = el.getDocument();
- Segment seg = new Segment();
- try
- {
- doc.getText(p0, p1 - p0, seg);
- }
- catch (BadLocationException ex)
- {
- AssertionError ae;
- ae = new AssertionError("BadLocationException must not be thrown "
- + "here");
- ae.initCause(ex);
- throw ae;
- }
- FontMetrics fm = null; // Fetch font metrics somewhere.
- return Utilities.getTabbedTextWidth(seg, fm, 0, null, p0);
+ checkPainter();
+ return glyphPainter.getSpan(this, p0, p1, tabExpander, tabX);
}
/**
* Returns the start offset in the document model of the portion
* of text that this view is responsible for.
*
* @return the start offset in the document model of the portion
* of text that this view is responsible for
*/
public int getStartOffset()
{
Element el = getElement();
int offs = el.getStartOffset();
if (length > 0)
offs += offset;
@@ -926,30 +923,32 @@
* possible
*/
public View breakView(int axis, int p0, float pos, float len)
{
View brokenView = this;
if (axis == X_AXIS)
{
checkPainter();
int end = glyphPainter.getBoundedPosition(this, p0, pos, len);
int breakLoc = getBreakLocation(p0, end);
if (breakLoc != -1)
end = breakLoc;
if (p0 != getStartOffset() || end != getEndOffset())
{
brokenView = createFragment(p0, end);
+ if (brokenView instanceof GlyphView)
+ ((GlyphView) brokenView).tabX = pos;
}
}
return brokenView;
}
/**
* Determines how well the specified view location is suitable for inserting
* a line break. If <code>axis</code> is <code>View.Y_AXIS</code>, then
* this method forwards to the superclass, if <code>axis</code> is
* <code>View.X_AXIS</code> then this method returns
* [EMAIL PROTECTED] View#ExcellentBreakWeight} if there is a suitable break location
* (usually whitespace) within the specified view span, or
* [EMAIL PROTECTED] View#GoodBreakWeight} if not.
*
* @param axis the axis along which the break weight is requested
Index: javax/swing/text/Utilities.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/Utilities.java,v
retrieving revision 1.39
diff -u -1 -5 -r1.39 Utilities.java
--- javax/swing/text/Utilities.java 2 Nov 2006 11:20:21 -0000 1.39
+++ javax/swing/text/Utilities.java 16 Nov 2006 12:59:47 -0000
@@ -151,61 +151,60 @@
* into account.
*/
public static final int getTabbedTextWidth(Segment s, FontMetrics metrics,
int x, TabExpander e,
int startOffset)
{
// This buffers the chars to be drawn.
char[] buffer = s.array;
// The current x coordinate.
int pixelX = x;
// The current maximum width.
int maxWidth = 0;
- for (int offset = s.offset; offset < (s.offset + s.count); ++offset)
+ int end = s.offset + s.count;
+ int count = 0;
+ for (int offset = s.offset; offset < end; offset++)
{
switch (buffer[offset])
{
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 'm'.
if (e != null)
pixelX = (int) e.nextTabStop(pixelX,
startOffset + offset - s.offset);
else
pixelX += metrics.charWidth(' ');
break;
case '\n':
// In case we have a newline, we must 'draw'
// the buffer and jump on the next line.
- pixelX += metrics.charWidth(buffer[offset]);
- maxWidth = Math.max(maxWidth, pixelX - x);
- pixelX = x;
- break;
- default:
- // Here we draw the char.
- pixelX += metrics.charWidth(buffer[offset]);
- break;
- }
+ pixelX += metrics.charsWidth(buffer, offset - count, count);
+ count = 0;
+ break;
+ default:
+ count++;
+ }
}
// Take the last line into account.
- maxWidth = Math.max(maxWidth, pixelX - x);
+ pixelX += metrics.charsWidth(buffer, end - count, count);
- return maxWidth;
+ return pixelX - x;
}
/**
* Provides a facility to map screen coordinates into a model location. For a
* given text fragment and start location within this fragment, this method
* determines the model location so that the resulting fragment fits best
* into the span <code>[x0, x]</code>.
*
* The parameter <code>round</code> controls which model location is returned
* if the view coordinates are on a character: If <code>round</code> is
* <code>true</code>, then the result is rounded up to the next character, so
* that the resulting fragment is the smallest fragment that is larger than
* the specified span. If <code>round</code> is <code>false</code>, then the
* resulting fragment is the largest fragment that is smaller than the
* specified span.
@@ -216,67 +215,65 @@
* @param x the target screen location at which the requested fragment should
* end
* @param te the tab expander to use; if this is <code>null</code>, TABs are
* expanded to one space character
* @param p0 the starting model location
* @param round if <code>true</code> round up to the next location, otherwise
* round down to the current location
*
* @return the model location, so that the resulting fragment fits within the
* specified span
*/
public static final int getTabbedTextOffset(Segment s, FontMetrics fm, int x0,
int x, TabExpander te, int p0,
boolean round)
{
- // At the end of the for loop, this holds the requested model location
- int pos;
+ int found = s.count;
int currentX = x0;
- int width = 0;
+ int nextX = currentX;
- for (pos = 0; pos < s.count; pos++)
+ int end = s.offset + s.count;
+ for (int pos = s.offset; pos < end && found == s.count; pos++)
{
- char nextChar = s.array[s.offset+pos];
-
- if (nextChar == 0)
- break;
+ char nextChar = s.array[pos];
if (nextChar != '\t')
- width = fm.charWidth(nextChar);
+ nextX += fm.charWidth(nextChar);
else
{
if (te == null)
- width = fm.charWidth(' ');
+ nextX += fm.charWidth(' ');
else
- width = ((int) te.nextTabStop(currentX, pos)) - currentX;
+ nextX += ((int) te.nextTabStop(nextX, p0 + pos - s.offset));
}
- if (round)
+ if (x >= currentX && x < nextX)
{
- if (currentX + (width>>1) > x)
- break;
- }
- else
- {
- if (currentX + width > x)
- break;
+ // Found position.
+ if ((! round) || ((x - currentX) < (nextX - x)))
+ {
+ found = pos - s.offset;
+ }
+ else
+ {
+ found = pos + 1 - s.offset;
+ }
}
-
- currentX += width;
+ currentX = nextX;
}
- return pos;
+ return found;
}
/**
* Provides a facility to map screen coordinates into a model location. For a
* given text fragment and start location within this fragment, this method
* determines the model location so that the resulting fragment fits best
* into the span <code>[x0, x]</code>.
*
* This method rounds up to the next location, so that the resulting fragment
* will be the smallest fragment of the text, that is greater than the
* specified span.
*
* @param s the text segment
* @param fm the font metrics to use
* @param x0 the starting screen location
Index: javax/swing/text/html/ParagraphView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/html/ParagraphView.java,v
retrieving revision 1.4
diff -u -1 -5 -r1.4 ParagraphView.java
--- javax/swing/text/html/ParagraphView.java 5 Nov 2006 20:23:12 -0000 1.4
+++ javax/swing/text/html/ParagraphView.java 16 Nov 2006 12:59:47 -0000
@@ -175,52 +175,38 @@
/**
* Calculates the minor axis requirements of this view. This is implemented
* to return the super class'es requirements and modifies the minimumSpan
* slightly so that it is not smaller than the length of the longest word.
*
* @param axis the axis
* @param r the SizeRequirements object to be used as return parameter;
* if <code>null</code> a new one will be created
*
* @return the requirements along the minor layout axis
*/
protected SizeRequirements calculateMinorAxisRequirements(int axis,
SizeRequirements r)
{
r = super.calculateMinorAxisRequirements(axis, r);
- if (setCSSSpan(r, axis))
+ if (! setCSSSpan(r, axis))
{
- // If we have set the span from CSS, then we need to adjust
- // the margins.
- SizeRequirements parent = super.calculateMinorAxisRequirements(axis,
- null);
int margin = axis == X_AXIS ? getLeftInset() + getRightInset()
: getTopInset() + getBottomInset();
r.minimum -= margin;
r.preferred -= margin;
r.maximum -= margin;
}
- else
- {
- float min = 0;
- int n = getLayoutViewCount();
- for (int i = 0; i < n; i++)
- min = Math.max(getLayoutView(i).getMinimumSpan(axis), min);
- r.minimum = (int) min;
- r.preferred = Math.max(r.preferred, r.minimum);
- r.maximum = Math.max(r.maximum, r.preferred);
- }
return r;
}
/**
* Sets the span on the SizeRequirements object according to the
* according CSS span value, when it is set.
*
* @param r the size requirements
* @param axis the axis
*
* @return <code>true</code> when the CSS span has been set,
* <code>false</code> otherwise
*/
private boolean setCSSSpan(SizeRequirements r, int axis)
{