The attached patch fixes a lot of issues with styled text in Swing and
make it more usable with respect to performance and rendering. Try
BeanShell for example.
2006-02-09 Roman Kennke <[EMAIL PROTECTED]>
* javax/swing/text/BoxView.java
(myAxis): Made field private.
(xLayoutValid): Replaced by layoutValid array.
(yLayoutValid): Replaced by layoutValid array.
(layoutValid): New field.
(spansX): Replaced by spans array.
(spansY): Replaced by spans array.
(spans): New field.
(offsetsX): Replaced by offsets array.
(offsetsY): Replaced by offsets array.
(offsets): New field.
(requirements): New field.
(BoxView): Initialize new fields.
(layoutChanged): Rewritten to use the layoutValid array.
(isLayoutValid): Rewritten to use the layoutValid array.
(replace): Use the new arrays.
(getPreferredSpan): Rewritten to call calculateXXXRequirements
instead of baselineRequirements.
(baselineRequirements): Rewritten to calculate baseline requirements.
(baselineLayout): Rewritten to calculate baseline layout.
(childAllocation): Use new arrays.
(layout): Rewritten. Only update the layout if necessary.
(layoutMajorAxis): Directly set layoutValid.
(layoutMinorAxis): Directly set layoutValid. Use cached size
requirements.
(getWidth): Use new span array.
(getHeight): Likewise.
(setSize): Rewritten to simply call layout().
(validateLayout): Removed unneeded method.
(getSpan): Use new arrays.
(getOffset): Use new arrays.
(getAlignment): Use cached requirements if possible.
(preferenceChanged): Use new arrays.
* javax/swing/text/FlowView.java
(FlowStrategy.insertUpdate): Do nothing here.
(FlowStrategy.removeUpdate): Do nothing here.
(FlowStrategy.changedUpdate): Do nothing here.
(FlowStrategy.layoutRow): Rewritten.
(FlowStrategy.createView): Rewritten.
(FlowStrategy.adjustRow): New method.
(LogicalView.getViewIndex): Fixed condition for finding child
view.
(layoutDirty): New field indicating the state of the layout.
(FlowView): Initialize new field.
(loadChildren): Set parent on logical view so that preferenceChanges
get propagated upwards.
(layout): Rewritten to match the specs.
(insertUpdate): Set layout to dirty.
(removeUpdate): Set layout to dirty.
(changedUpdate): Set layout to dirty.
* javax/swing/text/GlyphView.java
(getBreakWeight): Rewritten to use the Utilities class. Commented
out though because that is broken.
(insertUpdate): Call preferenceChanged on this object instead of
parent.
* javax/swing/text/ParagraphView.java
(Row.loadChildren): Overridden to be a noop to prevent initial
creation of child views. This is carried out by the flow layout.
* javax/swing/text/View.java
(getPreferredSpan): Added API docs.
(getResizeWeight): Added API docs.
(getMaximumSpan): Added API docs. Rewritten to only have one exit
point.
(getMinimumSpan): Added API docs. Rewritten to return 0 when
resizable instead of Integer.MAX_VALUE.
(getAlignment): Added API docs.
(replace): Added API docs.
(forwardUpdate): Rewritten to only notify child views that need to
be notified.
/Roman
Index: javax/swing/text/BoxView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/BoxView.java,v
retrieving revision 1.12
diff -u -r1.12 BoxView.java
--- javax/swing/text/BoxView.java 17 Nov 2005 20:56:38 -0000 1.12
+++ javax/swing/text/BoxView.java 9 Feb 2006 14:27:58 -0000
@@ -58,49 +58,32 @@
/**
* The axis along which this <code>BoxView</code> is laid out.
*/
- int myAxis;
+ private int myAxis;
/**
- * Indicates wether the layout in X_AXIS is valid.
+ * Indicates if the layout is valid along X_AXIS or Y_AXIS.
*/
- boolean xLayoutValid;
+ private boolean[] layoutValid = new boolean[2];
/**
- * Indicates whether the layout in Y_AXIS is valid.
+ * The spans along the X_AXIS and Y_AXIS.
*/
- boolean yLayoutValid;
+ private int[][] spans = new int[2][];
/**
- * The spans in X direction of the children.
+ * The offsets of the children along the X_AXIS and Y_AXIS.
*/
- int[] spansX;
+ private int[][] offsets = new int[2][];
/**
- * The spans in Y direction of the children.
+ * The size requirements along the X_AXIS and Y_AXIS.
*/
- int[] spansY;
+ private SizeRequirements[] requirements = new SizeRequirements[2];
/**
- * The offsets of the children in X direction relative to this BoxView's
- * inner bounds.
+ * The current span along X_AXIS or Y_AXIS.
*/
- int[] offsetsX;
-
- /**
- * The offsets of the children in Y direction relative to this BoxView's
- * inner bounds.
- */
- int[] offsetsY;
-
- /**
- * The current width.
- */
- int width;
-
- /**
- * The current height.
- */
- int height;
+ private int[] span = new int[2];
/**
* Creates a new <code>BoxView</code> for the given
@@ -114,17 +97,18 @@
{
super(element);
myAxis = axis;
- xLayoutValid = false;
- yLayoutValid = false;
+ layoutValid[0] = false;
+ layoutValid[1] = false;
+ span[0] = 0;
+ span[1] = 0;
+ requirements[0] = new SizeRequirements();
+ requirements[1] = new SizeRequirements();
// Initialize the cache arrays.
- spansX = new int[0];
- spansY = new int[0];
- offsetsX = new int[0];
- offsetsY = new int[0];
-
- width = 0;
- height = 0;
+ spans[0] = new int[0];
+ spans[1] = new int[0];
+ offsets[0] = new int[0];
+ offsets[1] = new int[0];
}
/**
@@ -166,17 +150,9 @@
*/
public void layoutChanged(int axis)
{
- switch (axis)
- {
- case X_AXIS:
- xLayoutValid = false;
- break;
- case Y_AXIS:
- yLayoutValid = false;
- break;
- default:
- throw new IllegalArgumentException("Invalid axis parameter.");
- }
+ if (axis != X_AXIS && axis != Y_AXIS)
+ throw new IllegalArgumentException("Invalid axis parameter.");
+ layoutValid[axis] = false;
}
/**
@@ -193,19 +169,9 @@
*/
protected boolean isLayoutValid(int axis)
{
- boolean valid = false;
- switch (axis)
- {
- case X_AXIS:
- valid = xLayoutValid;
- break;
- case Y_AXIS:
- valid = yLayoutValid;
- break;
- default:
- throw new IllegalArgumentException("Invalid axis parameter.");
- }
- return valid;
+ if (axis != X_AXIS && axis != Y_AXIS)
+ throw new IllegalArgumentException("Invalid axis parameter.");
+ return layoutValid[axis];
}
/**
@@ -242,40 +208,44 @@
*/
public void replace(int offset, int length, View[] views)
{
+ int numViews = 0;
+ if (views != null)
+ numViews = views.length;
+
// Resize and copy data for cache arrays.
// The spansX cache.
int oldSize = getViewCount();
- int[] newSpansX = new int[oldSize - length + views.length];
- System.arraycopy(spansX, 0, newSpansX, 0, offset);
- System.arraycopy(spansX, offset + length, newSpansX,
- offset + views.length,
+ int[] newSpansX = new int[oldSize - length + numViews];
+ System.arraycopy(spans[X_AXIS], 0, newSpansX, 0, offset);
+ System.arraycopy(spans[X_AXIS], offset + length, newSpansX,
+ offset + numViews,
oldSize - (offset + length));
- spansX = newSpansX;
+ spans[X_AXIS] = newSpansX;
// The spansY cache.
- int[] newSpansY = new int[oldSize - length + views.length];
- System.arraycopy(spansY, 0, newSpansY, 0, offset);
- System.arraycopy(spansY, offset + length, newSpansY,
- offset + views.length,
+ int[] newSpansY = new int[oldSize - length + numViews];
+ System.arraycopy(spans[Y_AXIS], 0, newSpansY, 0, offset);
+ System.arraycopy(spans[Y_AXIS], offset + length, newSpansY,
+ offset + numViews,
oldSize - (offset + length));
- spansY = newSpansY;
+ spans[Y_AXIS] = newSpansY;
// The offsetsX cache.
- int[] newOffsetsX = new int[oldSize - length + views.length];
- System.arraycopy(offsetsX, 0, newOffsetsX, 0, offset);
- System.arraycopy(offsetsX, offset + length, newOffsetsX,
- offset + views.length,
+ int[] newOffsetsX = new int[oldSize - length + numViews];
+ System.arraycopy(offsets[X_AXIS], 0, newOffsetsX, 0, offset);
+ System.arraycopy(offsets[X_AXIS], offset + length, newOffsetsX,
+ offset + numViews,
oldSize - (offset + length));
- offsetsX = newOffsetsX;
+ offsets[X_AXIS] = newOffsetsX;
// The offsetsY cache.
- int[] newOffsetsY = new int[oldSize - length + views.length];
- System.arraycopy(offsetsY, 0, newOffsetsY, 0, offset);
- System.arraycopy(offsetsY, offset + length, newOffsetsY,
- offset + views.length,
+ int[] newOffsetsY = new int[oldSize - length + numViews];
+ System.arraycopy(offsets[Y_AXIS], 0, newOffsetsY, 0, offset);
+ System.arraycopy(offsets[Y_AXIS], offset + length, newOffsetsY,
+ offset + numViews,
oldSize - (offset + length));
- offsetsY = newOffsetsY;
+ offsets[Y_AXIS] = newOffsetsY;
// Actually perform the replace.
super.replace(offset, length, views);
@@ -323,9 +293,16 @@
*/
public float getPreferredSpan(int axis)
{
- SizeRequirements sr = new SizeRequirements();
- int pref = baselineRequirements(axis, sr).preferred;
- return (float) pref;
+ if (!isLayoutValid(axis))
+ {
+ if (axis == myAxis)
+ requirements[axis] = calculateMajorAxisRequirements(axis,
+ requirements[axis]);
+ else
+ requirements[axis] = calculateMinorAxisRequirements(axis,
+ requirements[axis]);
+ }
+ return requirements[axis].preferred;
}
public float getMaximumSpan(int axis)
@@ -337,8 +314,9 @@
}
/**
- * Calculates the size requirements for this <code>BoxView</code> along
- * the specified axis.
+ * This method is obsolete and no longer in use. It is replaced by
+ * [EMAIL PROTECTED] #calculateMajorAxisRequirements(int, SizeRequirements)} and
+ * [EMAIL PROTECTED] #calculateMinorAxisRequirements(int, SizeRequirements)}.
*
* @param axis the axis that is examined
* @param sr the <code>SizeRequirements</code> object to hold the result,
@@ -350,12 +328,8 @@
protected SizeRequirements baselineRequirements(int axis,
SizeRequirements sr)
{
- SizeRequirements result;
- if (axis == myAxis)
- result = calculateMajorAxisRequirements(axis, sr);
- else
- result = calculateMinorAxisRequirements(axis, sr);
- return result;
+ SizeRequirements[] childReqs = getChildRequirements(axis);
+ return SizeRequirements.getAlignedSizeRequirements(childReqs);
}
/**
@@ -370,10 +344,12 @@
protected void baselineLayout(int span, int axis, int[] offsets,
int[] spans)
{
- if (axis == myAxis)
- layoutMajorAxis(span, axis, offsets, spans);
- else
- layoutMinorAxis(span, axis, offsets, spans);
+ SizeRequirements[] childReqs = getChildRequirements(axis);
+ // Calculate the spans and offsets using the SizeRequirements uility
+ // methods.
+ SizeRequirements.calculateAlignedPositions(span, requirements[axis],
+ childReqs, offsets, spans);
+ layoutValid[axis] = true;
}
/**
@@ -412,6 +388,7 @@
SizeRequirements[] childReqs = getChildRequirements(axis);
return SizeRequirements.getAlignedSizeRequirements(childReqs);
}
+
/**
* Returns <code>true</code> if the specified point lies before the
@@ -511,10 +488,10 @@
if (! isAllocationValid())
layout(a.width, a.height);
- a.x += offsetsX[index];
- a.y += offsetsY[index];
- a.width = spansX[index];
- a.height = spansY[index];
+ a.x += offsets[X_AXIS][index];
+ a.y += offsets[Y_AXIS][index];
+ a.width = spans[X_AXIS][index];
+ a.height = spans[Y_AXIS][index];
}
/**
@@ -528,8 +505,35 @@
*/
protected void layout(int width, int height)
{
- baselineLayout(width, X_AXIS, offsetsX, spansX);
- baselineLayout(height, Y_AXIS, offsetsY, spansY);
+ int[] newSpan = new int[]{ width, height };
+
+ // Update major axis as appropriate.
+ if (! isLayoutValid(myAxis) || newSpan[myAxis] != span[myAxis])
+ {
+ requirements[myAxis] = calculateMajorAxisRequirements(myAxis,
+ requirements[myAxis]);
+ layoutMajorAxis(newSpan[myAxis], myAxis, offsets[myAxis],
+ spans[myAxis]);
+ span[myAxis] = newSpan[myAxis];
+ }
+
+ // Update minor axis as appropriate.
+ int minorAxis = myAxis == X_AXIS ? Y_AXIS : X_AXIS;
+ if (! isLayoutValid(minorAxis))
+ {
+ requirements[minorAxis] = calculateMinorAxisRequirements(minorAxis,
+ requirements[minorAxis]);
+ layoutMinorAxis(newSpan[minorAxis], minorAxis, offsets[minorAxis],
+ spans[minorAxis]);
+ span[myAxis] = newSpan[myAxis];
+ }
+
+ // Update the child view's sizes.
+ int count = getViewCount();
+ for (int i = 0; i < count; ++i)
+ {
+ getView(i).setSize(spans[X_AXIS][i], spans[Y_AXIS][i]);
+ }
}
/**
@@ -549,7 +553,7 @@
// methods.
SizeRequirements.calculateTiledPositions(targetSpan, null, childReqs,
offsets, spans);
- validateLayout(axis);
+ layoutValid[axis] = true;
}
/**
@@ -567,15 +571,9 @@
SizeRequirements[] childReqs = getChildRequirements(axis);
// Calculate the spans and offsets using the SizeRequirements uility
// methods.
- // TODO: This might be an opportunity for performance optimization. Here
- // we could use a cached instance of SizeRequirements instead of passing
- // null to baselineRequirements. However, this would involve rewriting
- // the baselineRequirements() method to not use the SizeRequirements
- // utility method, since they cannot reuse a cached instance.
- SizeRequirements total = baselineRequirements(axis, null);
- SizeRequirements.calculateAlignedPositions(targetSpan, total, childReqs,
- offsets, spans);
- validateLayout(axis);
+ SizeRequirements.calculateAlignedPositions(targetSpan, requirements[axis],
+ childReqs, offsets, spans);
+ layoutValid[axis] = true;
}
/**
@@ -597,7 +595,7 @@
*/
public int getWidth()
{
- return width;
+ return span[X_AXIS];
}
/**
@@ -607,7 +605,7 @@
*/
public int getHeight()
{
- return height;
+ return span[Y_AXIS];
}
/**
@@ -619,31 +617,7 @@
*/
public void setSize(float width, float height)
{
- if (this.width != (int) width)
- layoutChanged(X_AXIS);
- if (this.height != (int) height)
- layoutChanged(Y_AXIS);
-
- this.width = (int) width;
- this.height = (int) height;
-
- Rectangle outside = new Rectangle(0, 0, this.width, this.height);
- Rectangle inside = getInsideAllocation(outside);
- if (!isAllocationValid())
- layout(inside.width, inside.height);
- }
-
- /**
- * Sets the layout to valid for a specific axis.
- *
- * @param axis the axis for which to validate the layout
- */
- void validateLayout(int axis)
- {
- if (axis == X_AXIS)
- xLayoutValid = true;
- if (axis == Y_AXIS)
- yLayoutValid = true;
+ layout((int) width, (int) height);
}
/**
@@ -653,7 +627,7 @@
* @return the size requirements of this view's children for the major
* axis
*/
- SizeRequirements[] getChildRequirements(int axis)
+ private SizeRequirements[] getChildRequirements(int axis)
{
// Allocate SizeRequirements for each child view.
int count = getViewCount();
@@ -682,10 +656,9 @@
*/
protected int getSpan(int axis, int childIndex)
{
- if (axis == X_AXIS)
- return spansX[childIndex];
- else
- return spansY[childIndex];
+ if (axis != X_AXIS && axis != Y_AXIS)
+ throw new IllegalArgumentException("Illegal axis argument");
+ return spans[axis][childIndex];
}
/**
@@ -701,10 +674,9 @@
*/
protected int getOffset(int axis, int childIndex)
{
- if (axis == X_AXIS)
- return offsetsX[childIndex];
- else
- return offsetsY[childIndex];
+ if (axis != X_AXIS && axis != Y_AXIS)
+ throw new IllegalArgumentException("Illegal axis argument");
+ return offsets[axis][childIndex];
}
/**
@@ -719,10 +691,17 @@
*/
public float getAlignment(int axis)
{
+ float align;
if (axis == myAxis)
- return 0.5F;
+ align = 0.5F;
else
- return baselineRequirements(axis, null).alignment;
+ {
+ if (! isLayoutValid(axis))
+ requirements[axis] = calculateMinorAxisRequirements(axis,
+ requirements[axis]);
+ align = requirements[axis].alignment;
+ }
+ return align;
}
/**
@@ -735,9 +714,9 @@
public void preferenceChanged (View child, boolean width, boolean height)
{
if (width)
- xLayoutValid = false;
+ layoutValid[X_AXIS] = false;
if (height)
- yLayoutValid = false;
+ layoutValid[Y_AXIS] = false;
super.preferenceChanged(child, width, height);
}
Index: javax/swing/text/FlowView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/FlowView.java,v
retrieving revision 1.7
diff -u -r1.7 FlowView.java
--- javax/swing/text/FlowView.java 23 Nov 2005 11:59:30 -0000 1.7
+++ javax/swing/text/FlowView.java 9 Feb 2006 14:27:58 -0000
@@ -45,7 +45,6 @@
import java.util.Iterator;
import java.util.Vector;
-import javax.swing.SwingConstants;
import javax.swing.event.DocumentEvent;
/**
@@ -89,7 +88,7 @@
*/
public void insertUpdate(FlowView fv, DocumentEvent e, Rectangle alloc)
{
- layout(fv);
+ // The default implementation does nothing.
}
/**
@@ -105,7 +104,7 @@
*/
public void removeUpdate(FlowView fv, DocumentEvent e, Rectangle alloc)
{
- layout(fv);
+ // The default implementation does nothing.
}
/**
@@ -121,7 +120,7 @@
*/
public void changedUpdate(FlowView fv, DocumentEvent e, Rectangle alloc)
{
- layout(fv);
+ // The default implementation does nothing.
}
/**
@@ -166,41 +165,55 @@
* Lays out one row of the flow view. This is called by [EMAIL PROTECTED] #layout}
* to fill one row with child views until the available span is exhausted.
*
+ * The default implementation fills the row by calling
+ * [EMAIL PROTECTED] #createView(FlowView, int, int, int)} until the available space
+ * is exhausted, a forced break is encountered or there are no more views
+ * in the logical view. If the available space is exhausted,
+ * [EMAIL PROTECTED] #adjustRow(FlowView, int, int, int)} is called to fit the row
+ * into the available span.
+ *
* @param fv the flow view for which we perform the layout
* @param rowIndex the index of the row
- * @param pos the start position for the row
+ * @param pos the model position for the beginning of the row
*
* @return the start position of the next row
*/
protected int layoutRow(FlowView fv, int rowIndex, int pos)
{
- int spanLeft = fv.getFlowSpan(rowIndex);
- if (spanLeft <= 0)
- return -1;
-
- int offset = pos;
View row = fv.getView(rowIndex);
- int flowAxis = fv.getFlowAxis();
+ int axis = fv.getFlowAxis();
+ int span = fv.getFlowSpan(rowIndex);
+ int x = fv.getFlowStart(rowIndex);
+ int offset = pos;
+ View logicalView = getLogicalView(fv);
+ // Special case when span == 0. We need to layout the row as if it had
+ // a span of Integer.MAX_VALUE.
+ if (span == 0)
+ span = Integer.MAX_VALUE;
- while (spanLeft > 0)
+ while (span > 0)
{
- View child = createView(fv, offset, spanLeft, rowIndex);
- if (child == null)
- {
- offset = -1;
- break;
- }
-
- int span = (int) child.getPreferredSpan(flowAxis);
- if (span > spanLeft)
- {
- offset = -1;
- break;
- }
-
- row.append(child);
- spanLeft -= span;
- offset = child.getEndOffset();
+ if (logicalView.getViewIndex(offset, Position.Bias.Forward) == -1)
+ break;
+ View view = createView(fv, offset, span, rowIndex);
+ if (view == null)
+ break;
+ int viewSpan = (int) view.getPreferredSpan(axis);
+ row.append(view);
+ int breakWeight = view.getBreakWeight(axis, x, span);
+ if (breakWeight >= View.ForcedBreakWeight)
+ break;
+ x += viewSpan;
+ span -= viewSpan;
+ offset += (view.getEndOffset() - view.getStartOffset());
+ }
+ if (span < 0)
+ {
+ int flowStart = fv.getFlowStart(axis);
+ int flowSpan = fv.getFlowSpan(axis);
+ adjustRow(fv, rowIndex, flowSpan, flowStart);
+ int rowViewCount = row.getViewCount();
+ offset = row.getView(rowViewCount - 1).getEndOffset();
}
return offset;
}
@@ -212,36 +225,89 @@
* available span and can be broken down) or <code>null</code> (if it does
* not fit in the available span and also cannot be broken down).
*
+ * The default implementation fetches the logical view at the specified
+ * <code>startOffset</code>. If that view has a different startOffset than
+ * specified in the argument, a fragment is created using
+ * [EMAIL PROTECTED] View#createFragment(int, int)} that has the correct startOffset
+ * and the logical view's endOffset.
+ *
* @param fv the flow view
- * @param offset the start offset for the view to be created
+ * @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 offset, int spanLeft,
+ protected View createView(FlowView fv, int startOffset, int spanLeft,
int rowIndex)
{
- // Find the logical element for the given offset.
- View logicalView = getLogicalView(fv);
+ View logicalView = getLogicalView(fv);
+ // FIXME: Handle the bias thing correctly.
+ int index = logicalView.getViewIndex(startOffset, Position.Bias.Forward);
+ View retVal = null;
+ if (index >= 0)
+ {
+ retVal = logicalView.getView(index);
+ if (retVal.getStartOffset() != startOffset)
+ retVal = retVal.createFragment(startOffset, retVal.getEndOffset());
+ }
+ return retVal;
+ }
- int viewIndex = logicalView.getViewIndex(offset, Position.Bias.Forward);
- if (viewIndex == -1)
- return null;
-
- View child = logicalView.getView(viewIndex);
- int flowAxis = fv.getFlowAxis();
- int span = (int) child.getPreferredSpan(flowAxis);
-
- if (span <= spanLeft)
- return child;
- else if (child.getBreakWeight(flowAxis, offset, spanLeft)
- > BadBreakWeight)
- // FIXME: What to do with the pos parameter here?
- return child.breakView(flowAxis, offset, 0, spanLeft);
- else
- return null;
+ /**
+ * 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.
+ *
+ * @param fv the flow view
+ * @param rowIndex the index of the row to be adjusted
+ * @param desiredSpan the layout span
+ * @param x the X location at which the row starts
+ */
+ protected void adjustRow(FlowView fv, int rowIndex, int desiredSpan, int x) {
+ // Determine the last view that has the highest break weight.
+ int axis = fv.getFlowAxis();
+ View row = fv.getView(rowIndex);
+ int count = row.getViewCount();
+ int breakIndex = -1;
+ int maxBreakWeight = View.BadBreakWeight;
+ int breakX = x;
+ int breakSpan = desiredSpan;
+ int currentX = x;
+ int currentSpan = desiredSpan;
+ for (int i = 0; i < count; ++i)
+ {
+ View view = row.getView(i);
+ int weight = view.getBreakWeight(axis, currentX, currentSpan);
+ if (weight >= maxBreakWeight)
+ {
+ breakIndex = i;
+ breakX = currentX;
+ breakSpan = currentSpan;
+ maxBreakWeight = weight;
+ }
+ int size = (int) view.getPreferredSpan(axis);
+ currentX += size;
+ currentSpan -= size;
+ }
+
+ // If there is a potential break location found, break the row at
+ // this location.
+ if (breakIndex > -1)
+ {
+ View toBeBroken = row.getView(breakIndex);
+ View brokenView = toBeBroken.breakView(axis,
+ toBeBroken.getStartOffset(),
+ breakX, breakSpan);
+ row.replace(breakIndex, count - breakIndex,
+ new View[]{brokenView});
+ }
}
}
@@ -342,8 +408,7 @@
for (Iterator it = children.iterator(); it.hasNext(); i++)
{
View child = (View) it.next();
- if (child.getStartOffset() >= pos
- && child.getEndOffset() < pos)
+ if (pos >= child.getStartOffset() && pos < child.getEndOffset())
{
index = i;
break;
@@ -424,6 +489,11 @@
protected FlowStrategy strategy;
/**
+ * Indicates if the flow should be rebuild during the next layout.
+ */
+ private boolean layoutDirty;
+
+ /**
* Creates a new <code>FlowView</code> for the given
* <code>Element</code> and <code>axis</code>.
*
@@ -436,6 +506,7 @@
{
super(element, axis);
strategy = sharedStrategy;
+ layoutDirty = true;
}
/**
@@ -511,7 +582,7 @@
if (layoutPool == null)
{
layoutPool = new LogicalView(getElement());
-
+ layoutPool.setParent(this);
Element el = getElement();
int count = el.getElementCount();
for (int i = 0; i < count; ++i)
@@ -534,27 +605,32 @@
*/
protected void layout(int width, int height)
{
- boolean rebuild = false;
-
int flowAxis = getFlowAxis();
if (flowAxis == X_AXIS)
{
- rebuild = !(width == layoutSpan);
- layoutSpan = width;
+ if (layoutSpan != width)
+ {
+ layoutChanged(Y_AXIS);
+ layoutSpan = width;
+ }
}
else
{
- rebuild = !(height == layoutSpan);
- layoutSpan = height;
+ if (layoutSpan != height)
+ {
+ layoutChanged(X_AXIS);
+ layoutSpan = height;
+ }
}
- if (rebuild)
- strategy.layout(this);
+ if (layoutDirty)
+ {
+ strategy.layout(this);
+ layoutDirty = false;
+ }
- // TODO: If the span along the box axis has changed in the process of
- // relayouting the rows (that is, if rows have been added or removed),
- // call preferenceChanged in order to throw away cached layout information
- // of the surrounding BoxView.
+ if (getPreferredSpan(getAxis()) != height)
+ preferenceChanged(this, false, true);
super.layout(width, height);
}
@@ -574,6 +650,7 @@
// be updated accordingly.
layoutPool.insertUpdate(changes, a, vf);
strategy.insertUpdate(this, changes, getInsideAllocation(a));
+ layoutDirty = true;
}
/**
@@ -588,6 +665,7 @@
public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory vf)
{
strategy.removeUpdate(this, changes, getInsideAllocation(a));
+ layoutDirty = true;
}
/**
@@ -602,6 +680,7 @@
public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory vf)
{
strategy.changedUpdate(this, changes, getInsideAllocation(a));
+ layoutDirty = true;
}
/**
Index: javax/swing/text/GlyphView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/GlyphView.java,v
retrieving revision 1.15
diff -u -r1.15 GlyphView.java
--- javax/swing/text/GlyphView.java 7 Feb 2006 15:54:03 -0000 1.15
+++ javax/swing/text/GlyphView.java 9 Feb 2006 14:27:58 -0000
@@ -567,7 +567,7 @@
tabEx, 0.F);
}
else
- span = painter.getHeight(this);
+ span = painter.getHeight(this);
return span;
}
@@ -936,23 +936,24 @@
weight = super.getBreakWeight(axis, pos, len);
else
{
- // Determine the model locations at pos and pos + len.
- int spanX = (int) getPreferredSpan(X_AXIS);
- int spanY = (int) getPreferredSpan(Y_AXIS);
- Rectangle dummyAlloc = new Rectangle(0, 0, spanX, spanY);
- Position.Bias[] biasRet = new Position.Bias[1];
- int offset1 = viewToModel(pos, spanY / 2, dummyAlloc, biasRet);
- int offset2 = viewToModel(pos, spanY / 2, dummyAlloc, biasRet);
- Segment txt = getText(offset1, offset2);
- BreakIterator lineBreaker = BreakIterator.getLineInstance();
- lineBreaker.setText(txt);
- int breakLoc = lineBreaker.previous();
- if (breakLoc == offset1)
- weight = View.BadBreakWeight;
- else if(breakLoc == BreakIterator.DONE)
- weight = View.GoodBreakWeight;
- else
- weight = View.ExcellentBreakWeight;
+ // FIXME: Commented out because the Utilities.getBreakLocation method
+ // is still buggy. The GoodBreakWeight is a reasonable workaround for
+ // now.
+// int startOffset = getStartOffset();
+// int endOffset = getEndOffset() - 1;
+// Segment s = getText(startOffset, endOffset);
+// Container c = getContainer();
+// FontMetrics fm = c.getFontMetrics(c.getFont());
+// int x0 = (int) pos;
+// int x = (int) (pos + len);
+// int breakLoc = Utilities.getBreakLocation(s, fm, x0, x,
+// getTabExpander(),
+// startOffset);
+// if (breakLoc == startOffset || breakLoc == endOffset)
+// weight = GoodBreakWeight;
+// else
+// weight = ExcellentBreakWeight;
+ weight = GoodBreakWeight;
}
return weight;
}
@@ -975,8 +976,8 @@
/**
* Receives notification that some text has been inserted within the
* text fragment that this view is responsible for. This calls
- * [EMAIL PROTECTED] View#preferenceChanged(View, boolean, boolean)} on the parent for
- * width.
+ * [EMAIL PROTECTED] View#preferenceChanged(View, boolean, boolean)} for the
+ * direction in which the glyphs are rendered.
*
* @param e the document event describing the change; not used here
* @param a the view allocation on screen; not used here
@@ -984,7 +985,7 @@
*/
public void insertUpdate(DocumentEvent e, Shape a, ViewFactory vf)
{
- getParent().preferenceChanged(this, true, false);
+ preferenceChanged(this, true, false);
}
/**
Index: javax/swing/text/ParagraphView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/ParagraphView.java,v
retrieving revision 1.4
diff -u -r1.4 ParagraphView.java
--- javax/swing/text/ParagraphView.java 17 Nov 2005 20:31:51 -0000 1.4
+++ javax/swing/text/ParagraphView.java 9 Feb 2006 14:27:58 -0000
@@ -63,11 +63,16 @@
{
super(el, X_AXIS);
}
+
public float getAlignment(int axis)
{
// FIXME: This is very likely not 100% correct. Work this out.
return 0.0F;
}
+ protected void loadChildren(ViewFactory vf)
+ {
+ // Do nothing here. The children are added while layouting.
+ }
}
/**
Index: javax/swing/text/View.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/View.java,v
retrieving revision 1.26
diff -u -r1.26 View.java
--- javax/swing/text/View.java 7 Feb 2006 13:54:48 -0000 1.26
+++ javax/swing/text/View.java 9 Feb 2006 14:27:58 -0000
@@ -100,27 +100,65 @@
return elt;
}
+ /**
+ * Returns the preferred span along the specified axis. Normally the view is
+ * rendered with the span returned here if that is possible.
+ *
+ * @param axis the axis
+ *
+ * @return the preferred span along the specified axis
+ */
public abstract float getPreferredSpan(int axis);
+ /**
+ * Returns the resize weight of this view. A value of <code>0</code> or less
+ * means this view is not resizeable. Positive values make the view
+ * resizeable. The default implementation returns <code>0</code>
+ * unconditionally.
+ *
+ * @param axis the axis
+ *
+ * @return the resizability of this view along the specified axis
+ */
public int getResizeWeight(int axis)
{
return 0;
}
+ /**
+ * Returns the maximum span along the specified axis. The default
+ * implementation will forward to
+ * [EMAIL PROTECTED] #getPreferredSpan(int)} unless [EMAIL PROTECTED] #getResizeWeight(int)}
+ * returns a value > 0, in which case this returns [EMAIL PROTECTED] Integer#MIN_VALUE}.
+ *
+ * @param axis the axis
+ *
+ * @return the maximum span along the specified axis
+ */
public float getMaximumSpan(int axis)
{
+ float max = Integer.MAX_VALUE;
if (getResizeWeight(axis) <= 0)
- return getPreferredSpan(axis);
-
- return Integer.MAX_VALUE;
+ max = getPreferredSpan(axis);
+ return max;
}
+ /**
+ * Returns the minimum span along the specified axis. The default
+ * implementation will forward to
+ * [EMAIL PROTECTED] #getPreferredSpan(int)} unless [EMAIL PROTECTED] #getResizeWeight(int)}
+ * returns a value > 0, in which case this returns <code>0</code>.
+ *
+ * @param axis the axis
+ *
+ * @return the minimum span along the specified axis
+ */
public float getMinimumSpan(int axis)
{
+ float min = 0;
if (getResizeWeight(axis) <= 0)
- return getPreferredSpan(axis);
-
- return Integer.MAX_VALUE;
+ min = getPreferredSpan(axis);
+ return min;
}
public void setSize(float width, float height)
@@ -128,6 +166,20 @@
// The default implementation does nothing.
}
+ /**
+ * Returns the alignment of this view along the baseline of the parent view.
+ * An alignment of <code>0.0</code> will align this view with the left edge
+ * along the baseline, an alignment of <code>0.5</code> will align it
+ * centered to the baseline, an alignment of <code>1.0</code> will align
+ * the right edge along the baseline.
+ *
+ * The default implementation returns 0.5 unconditionally.
+ *
+ * @param axis the axis
+ *
+ * @return the alignment of this view along the parents baseline for the
+ * specified axis
+ */
public float getAlignment(int axis)
{
return 0.5f;
@@ -159,6 +211,15 @@
return parent != null ? parent.getViewFactory() : null;
}
+ /**
+ * Replaces a couple of child views with new child views. If
+ * <code>length == 0</code> then this is a simple insertion, if
+ * <code>views == null</code> this only removes some child views.
+ *
+ * @param offset the offset at which to replace
+ * @param length the number of child views to be removed
+ * @param views the new views to be inserted, may be <code>null</code>
+ */
public void replace(int offset, int length, View[] views)
{
// Default implementation does nothing.
@@ -407,25 +468,32 @@
DocumentEvent ev, Shape shape, ViewFactory vf)
{
int count = getViewCount();
- int index = -1;
- int addLength = -1;
- if (ec != null)
+ if (count > 0)
{
- index = ec.getIndex();
- addLength = ec.getChildrenAdded().length;
- }
+ int startOffset = ev.getOffset();
+ int endOffset = startOffset + ev.getLength();
+ // FIXME: What about this bias stuff?
+ int startIndex = getViewIndex(startOffset, Position.Bias.Forward);
+ int endIndex = getViewIndex(endOffset, Position.Bias.Forward);
+ int index = -1;
+ int addLength = -1;
+ if (ec != null)
+ {
+ index = ec.getIndex();
+ addLength = ec.getChildrenAdded().length;
+ }
- for (int i = 0; i < count; i++)
- {
- // Skip newly added child views.
- if (index >= 0 && index == i && addLength > 0)
+ if (startIndex >= 0 && endIndex >= 0)
{
- // We add addLengt - 1 here because the for loop does add 1 more.
- i += addLength - 1;
- continue;
+ for (int i = startIndex; i <= endIndex; i++)
+ {
+ // Skip newly added child views.
+ if (index >= 0 && i >= index && i < (index+addLength))
+ continue;
+ View child = getView(i);
+ forwardUpdateToView(child, ev, shape, vf);
+ }
}
- View child = getView(i);
- forwardUpdateToView(child, ev, shape, vf);
}
}