summaryrefslogtreecommitdiff
path: root/libjava/classpath/javax/swing/text/FlowView.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/javax/swing/text/FlowView.java')
-rw-r--r--libjava/classpath/javax/swing/text/FlowView.java394
1 files changed, 178 insertions, 216 deletions
diff --git a/libjava/classpath/javax/swing/text/FlowView.java b/libjava/classpath/javax/swing/text/FlowView.java
index 6d4b9cd3174..8be8f41e939 100644
--- a/libjava/classpath/javax/swing/text/FlowView.java
+++ b/libjava/classpath/javax/swing/text/FlowView.java
@@ -38,14 +38,10 @@ exception statement from your version. */
package javax.swing.text;
-import java.awt.Container;
-import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;
-import java.util.Iterator;
-import java.util.Vector;
-import javax.swing.SwingConstants;
+import javax.swing.SizeRequirements;
import javax.swing.event.DocumentEvent;
/**
@@ -89,7 +85,7 @@ public abstract class FlowView extends BoxView
*/
public void insertUpdate(FlowView fv, DocumentEvent e, Rectangle alloc)
{
- layout(fv);
+ // The default implementation does nothing.
}
/**
@@ -105,7 +101,7 @@ public abstract class FlowView extends BoxView
*/
public void removeUpdate(FlowView fv, DocumentEvent e, Rectangle alloc)
{
- layout(fv);
+ // The default implementation does nothing.
}
/**
@@ -121,7 +117,7 @@ public abstract class FlowView extends BoxView
*/
public void changedUpdate(FlowView fv, DocumentEvent e, Rectangle alloc)
{
- layout(fv);
+ // The default implementation does nothing.
}
/**
@@ -131,7 +127,7 @@ public abstract class FlowView extends BoxView
*
* @return the logical view of the managed <code>FlowView</code>
*/
- public View getLogicalView(FlowView fv)
+ protected View getLogicalView(FlowView fv)
{
return fv.layoutPool;
}
@@ -166,43 +162,60 @@ public abstract class FlowView extends BoxView
* Lays out one row of the flow view. This is called by {@link #layout}
* to fill one row with child views until the available span is exhausted.
*
+ * The default implementation fills the row by calling
+ * {@link #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,
+ * {@link #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());
}
- return offset;
+ if (span < 0)
+ {
+ int flowStart = fv.getFlowStart(axis);
+ int flowSpan = fv.getFlowSpan(axis);
+ adjustRow(fv, rowIndex, flowSpan, flowStart);
+ int rowViewCount = row.getViewCount();
+ if (rowViewCount > 0)
+ offset = row.getView(rowViewCount - 1).getEndOffset();
+ else
+ offset = -1;
+ }
+ return offset != pos ? offset : -1;
}
/**
@@ -212,189 +225,106 @@ public abstract class FlowView extends BoxView
* 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
+ * {@link 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);
-
- 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;
- }
- }
-
- /**
- * This special subclass of <code>View</code> is used to represent
- * the logical representation of this view. It does not support any
- * visual representation, this is handled by the physical view implemented
- * in the <code>FlowView</code>.
- */
- class LogicalView extends View
- {
- /**
- * The child views of this logical view.
- */
- Vector children;
-
- /**
- * Creates a new LogicalView instance.
- */
- LogicalView(Element el)
- {
- super(el);
- children = new Vector();
- }
-
- /**
- * Returns the container that holds this view. The logical view returns
- * the enclosing FlowView's container here.
- *
- * @return the container that holds this view
- */
- public Container getContainer()
- {
- return FlowView.this.getContainer();
- }
-
- /**
- * Returns the number of child views of this logical view.
- *
- * @return the number of child views of this logical view
- */
- public int getViewCount()
- {
- return children.size();
- }
-
- /**
- * Returns the child view at the specified index.
- *
- * @param index the index
- *
- * @return the child view at the specified index
- */
- public View getView(int index)
- {
- return (View) children.get(index);
- }
-
- /**
- * Replaces some child views with other child views.
- *
- * @param offset the offset at which to replace child views
- * @param length the number of children to remove
- * @param views the views to be inserted
- */
- public void replace(int offset, int length, View[] views)
- {
- if (length > 0)
- {
- for (int count = 0; count < length; ++count)
- children.remove(offset);
- }
-
- int endOffset = offset + views.length;
- for (int i = offset; i < endOffset; ++i)
- {
- children.add(i, views[i - offset]);
- // Set the parent of the child views to the flow view itself so
- // it has something to resolve.
- views[i - offset].setParent(FlowView.this);
- }
+ 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;
}
/**
- * Returns the index of the child view that contains the specified
- * position in the document model.
+ * 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 >
+ * {@link View#BadBreakWeight}, this view is broken using the
+ * {@link 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 pos the position for which we are searching the child view
- * @param b the bias
- *
- * @return the index of the child view that contains the specified
- * position in the document model
+ * @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
*/
- public int getViewIndex(int pos, Position.Bias b)
- {
- int index = -1;
- int i = 0;
- for (Iterator it = children.iterator(); it.hasNext(); i++)
+ 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 child = (View) it.next();
- if (child.getStartOffset() >= pos
- && child.getEndOffset() < pos)
+ View view = row.getView(i);
+ int weight = view.getBreakWeight(axis, currentX, currentSpan);
+ if (weight >= maxBreakWeight)
{
- index = i;
- break;
+ breakIndex = i;
+ breakX = currentX;
+ breakSpan = currentSpan;
+ maxBreakWeight = weight;
}
+ int size = (int) view.getPreferredSpan(axis);
+ currentX += size;
+ currentSpan -= size;
}
- return index;
- }
- /**
- * Throws an AssertionError because it must never be called. LogicalView
- * only serves as a holder for child views and has no visual
- * representation.
- */
- public float getPreferredSpan(int axis)
- {
- throw new AssertionError("This method must not be called in "
- + "LogicalView.");
- }
-
- /**
- * Throws an AssertionError because it must never be called. LogicalView
- * only serves as a holder for child views and has no visual
- * representation.
- */
- public Shape modelToView(int pos, Shape a, Position.Bias b)
- throws BadLocationException
- {
- throw new AssertionError("This method must not be called in "
- + "LogicalView.");
- }
-
- /**
- * Throws an AssertionError because it must never be called. LogicalView
- * only serves as a holder for child views and has no visual
- * representation.
- */
- public void paint(Graphics g, Shape s)
- {
- throw new AssertionError("This method must not be called in "
- + "LogicalView.");
+ // 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});
+ }
}
+ }
+ /**
+ * This special subclass of <code>View</code> is used to represent
+ * the logical representation of this view. It does not support any
+ * visual representation, this is handled by the physical view implemented
+ * in the <code>FlowView</code>.
+ */
+ class LogicalView extends BoxView
+ {
/**
- * Throws an AssertionError because it must never be called. LogicalView
- * only serves as a holder for child views and has no visual
- * representation.
+ * Creates a new LogicalView instance.
*/
- public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
+ LogicalView(Element el, int axis)
{
- throw new AssertionError("This method must not be called in "
- + "LogicalView.");
+ super(el, axis);
}
}
@@ -424,6 +354,11 @@ public abstract class FlowView extends BoxView
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 +371,7 @@ public abstract class FlowView extends BoxView
{
super(element, axis);
strategy = sharedStrategy;
+ layoutDirty = true;
}
/**
@@ -510,16 +446,8 @@ public abstract class FlowView extends BoxView
{
if (layoutPool == null)
{
- layoutPool = new LogicalView(getElement());
-
- Element el = getElement();
- int count = el.getElementCount();
- for (int i = 0; i < count; ++i)
- {
- Element childEl = el.getElement(i);
- View childView = vf.create(childEl);
- layoutPool.append(childView);
- }
+ layoutPool = new LogicalView(getElement(), getAxis());
+ layoutPool.setParent(this);
}
}
@@ -534,27 +462,32 @@ public abstract class FlowView extends BoxView
*/
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 +507,7 @@ public abstract class FlowView extends BoxView
// be updated accordingly.
layoutPool.insertUpdate(changes, a, vf);
strategy.insertUpdate(this, changes, getInsideAllocation(a));
+ layoutDirty = true;
}
/**
@@ -588,6 +522,7 @@ public abstract class FlowView extends BoxView
public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory vf)
{
strategy.removeUpdate(this, changes, getInsideAllocation(a));
+ layoutDirty = true;
}
/**
@@ -602,6 +537,7 @@ public abstract class FlowView extends BoxView
public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory vf)
{
strategy.changedUpdate(this, changes, getInsideAllocation(a));
+ layoutDirty = true;
}
/**
@@ -640,4 +576,30 @@ public abstract class FlowView extends BoxView
}
return result;
}
+
+ /**
+ * Calculates the size requirements of this <code>BoxView</code> along
+ * its minor axis, that is the axis opposite to the axis specified in the
+ * constructor.
+ *
+ * This is overridden and forwards the request to the logical view.
+ *
+ * @param axis the axis that is examined
+ * @param r the <code>SizeRequirements</code> object to hold the result,
+ * if <code>null</code>, a new one is created
+ *
+ * @return the size requirements for this <code>BoxView</code> along
+ * the specified axis
+ */
+ protected SizeRequirements calculateMinorAxisRequirements(int axis,
+ SizeRequirements r)
+ {
+ // We need to call super here so that the alignment is properly
+ // calculated.
+ SizeRequirements res = super.calculateMinorAxisRequirements(axis, r);
+ res.minimum = (int) layoutPool.getMinimumSpan(axis);
+ res.preferred = (int) layoutPool.getPreferredSpan(axis);
+ res.maximum = (int) layoutPool.getMaximumSpan(axis);
+ return res;
+ }
}