From: <ls...@us...> - 2006-12-09 18:47:16
|
Revision: 2882 http://jnode.svn.sourceforge.net/jnode/?rev=2882&view=rev Author: lsantha Date: 2006-12-09 10:47:15 -0800 (Sat, 09 Dec 2006) Log Message: ----------- Classpath patches. Modified Paths: -------------- trunk/core/src/classpath/javax/javax/swing/text/AbstractDocument.java trunk/core/src/classpath/javax/javax/swing/text/BoxView.java trunk/core/src/classpath/javax/javax/swing/text/ComponentView.java trunk/core/src/classpath/javax/javax/swing/text/CompositeView.java trunk/core/src/classpath/javax/javax/swing/text/DefaultCaret.java trunk/core/src/classpath/javax/javax/swing/text/DefaultEditorKit.java trunk/core/src/classpath/javax/javax/swing/text/DefaultFormatter.java trunk/core/src/classpath/javax/javax/swing/text/DefaultStyledDocument.java trunk/core/src/classpath/javax/javax/swing/text/ElementIterator.java trunk/core/src/classpath/javax/javax/swing/text/FieldView.java trunk/core/src/classpath/javax/javax/swing/text/FlowView.java trunk/core/src/classpath/javax/javax/swing/text/GapContent.java trunk/core/src/classpath/javax/javax/swing/text/GlyphView.java trunk/core/src/classpath/javax/javax/swing/text/InternationalFormatter.java trunk/core/src/classpath/javax/javax/swing/text/MaskFormatter.java trunk/core/src/classpath/javax/javax/swing/text/ParagraphView.java trunk/core/src/classpath/javax/javax/swing/text/Position.java trunk/core/src/classpath/javax/javax/swing/text/StringContent.java trunk/core/src/classpath/javax/javax/swing/text/StyleConstants.java trunk/core/src/classpath/javax/javax/swing/text/StyleContext.java trunk/core/src/classpath/javax/javax/swing/text/TextAction.java trunk/core/src/classpath/javax/javax/swing/text/Utilities.java trunk/core/src/classpath/javax/javax/swing/text/View.java trunk/core/src/classpath/javax/javax/swing/text/WrappedPlainView.java Modified: trunk/core/src/classpath/javax/javax/swing/text/AbstractDocument.java =================================================================== --- trunk/core/src/classpath/javax/javax/swing/text/AbstractDocument.java 2006-12-09 18:44:58 UTC (rev 2881) +++ trunk/core/src/classpath/javax/javax/swing/text/AbstractDocument.java 2006-12-09 18:47:15 UTC (rev 2882) @@ -46,6 +46,7 @@ import java.util.Dictionary; import java.util.Enumeration; import java.util.EventListener; +import java.util.HashMap; import java.util.Hashtable; import java.util.Vector; @@ -158,15 +159,11 @@ private int numReaders = 0; /** - * Tells if there are one or more writers waiting. + * The number of current writers. If this is > 1 then the same thread entered + * the write lock more than once. */ - private int numWritersWaiting = 0; + private int numWriters = 0; - /** - * A condition variable that readers and writers wait on. - */ - private Object documentCV = new Object(); - /** An instance of a DocumentFilter.FilterBypass which allows calling * the insert, remove and replace method without checking for an installed * document filter. @@ -179,6 +176,12 @@ private BidiRootElement bidiRoot; /** + * True when we are currently notifying any listeners. This is used + * to detect illegal situations in writeLock(). + */ + private transient boolean notifyListeners; + + /** * Creates a new <code>AbstractDocument</code> with the specified * {@link Content} model. * @@ -315,7 +318,8 @@ * @throws BadLocationException if <code>offset</code> is not a valid * location in the documents content model */ - public Position createPosition(final int offset) throws BadLocationException + public synchronized Position createPosition(final int offset) + throws BadLocationException { return content.createPosition(offset); } @@ -327,11 +331,18 @@ */ protected void fireChangedUpdate(DocumentEvent event) { + notifyListeners = true; + try + { DocumentListener[] listeners = getDocumentListeners(); - for (int index = 0; index < listeners.length; ++index) listeners[index].changedUpdate(event); } + finally + { + notifyListeners = false; + } + } /** * Notifies all registered listeners when content is inserted in the document @@ -341,11 +352,18 @@ */ protected void fireInsertUpdate(DocumentEvent event) { + notifyListeners = true; + try + { DocumentListener[] listeners = getDocumentListeners(); - for (int index = 0; index < listeners.length; ++index) listeners[index].insertUpdate(event); } + finally + { + notifyListeners = false; + } + } /** * Notifies all registered listeners when content is removed from the @@ -355,11 +373,18 @@ */ protected void fireRemoveUpdate(DocumentEvent event) { + notifyListeners = true; + try + { DocumentListener[] listeners = getDocumentListeners(); - for (int index = 0; index < listeners.length; ++index) listeners[index].removeUpdate(event); } + finally + { + notifyListeners = false; + } + } /** * Notifies all registered listeners when an <code>UndoableEdit</code> has @@ -432,7 +457,7 @@ * @return the thread that currently modifies this <code>Document</code> * if there is one, otherwise <code>null</code> */ - protected final Thread getCurrentWriter() + protected final synchronized Thread getCurrentWriter() { return currentWriter; } @@ -1022,33 +1047,29 @@ * Blocks until a read lock can be obtained. Must block if there is * currently a writer modifying the <code>Document</code>. */ - public final void readLock() + public final synchronized void readLock() { - if (currentWriter != null && currentWriter.equals(Thread.currentThread())) - return; - synchronized (documentCV) - { - while (currentWriter != null || numWritersWaiting > 0) - { - try { - documentCV.wait(); - } - catch (InterruptedException ie) + while (currentWriter != null) { - throw new Error("interrupted trying to get a readLock"); - } + if (currentWriter == Thread.currentThread()) + return; + wait(); } numReaders++; } + catch (InterruptedException ex) + { + throw new Error("Interrupted during grab read lock"); + } } /** * Releases the read lock. If this was the only reader on this * <code>Document</code>, writing may begin now. */ - public final void readUnlock() + public final synchronized void readUnlock() { // Note we could have a problem here if readUnlock was called without a // prior call to readLock but the specs simply warn users to ensure that @@ -1075,21 +1096,14 @@ // FIXME: the reference implementation throws a // javax.swing.text.StateInvariantError here - if (numReaders == 0) + if (numReaders <= 0) throw new IllegalStateException("document lock failure"); - synchronized (documentCV) - { // If currentWriter is not null, the application code probably had a // writeLock and then tried to obtain a readLock, in which case // numReaders wasn't incremented - if (currentWriter == null) - { - numReaders --; - if (numReaders == 0 && numWritersWaiting != 0) - documentCV.notify(); - } - } + numReaders--; + notify(); } /** @@ -1113,10 +1127,19 @@ */ public void remove(int offset, int length) throws BadLocationException { - if (documentFilter == null) + writeLock(); + try + { + DocumentFilter f = getDocumentFilter(); + if (f == null) removeImpl(offset, length); else - documentFilter.remove(getBypass(), offset, length); + f.remove(getBypass(), offset, length); + } + finally + { + writeUnlock(); + } } void removeImpl(int offset, int length) throws BadLocationException @@ -1135,10 +1158,6 @@ new DefaultDocumentEvent(offset, length, DocumentEvent.EventType.REMOVE); - try - { - writeLock(); - // The order of the operations below is critical! removeUpdate(event); UndoableEdit temp = content.remove(offset, length); @@ -1146,12 +1165,7 @@ postRemoveUpdate(event); fireRemoveUpdate(event); } - finally - { - writeUnlock(); - } } - } /** * Replaces a piece of content in this <code>Document</code> with @@ -1343,42 +1357,41 @@ * Blocks until a write lock can be obtained. Must wait if there are * readers currently reading or another thread is currently writing. */ - protected final void writeLock() - { - if (currentWriter != null && currentWriter.equals(Thread.currentThread())) - return; - synchronized (documentCV) - { - numWritersWaiting++; - while (numReaders > 0) + protected synchronized final void writeLock() { try { - documentCV.wait(); - } - catch (InterruptedException ie) + while (numReaders > 0 || currentWriter != null) + { + if (Thread.currentThread() == currentWriter) { - throw new Error("interruped while trying to obtain write lock"); + if (notifyListeners) + throw new IllegalStateException("Mutation during notify"); + numWriters++; + return; } + wait(); } - numWritersWaiting --; currentWriter = Thread.currentThread(); + numWriters = 1; } + catch (InterruptedException ex) + { + throw new Error("Interupted during grab write lock"); + } } /** * Releases the write lock. This allows waiting readers or writers to * obtain the lock. */ - protected final void writeUnlock() + protected final synchronized void writeUnlock() { - synchronized (documentCV) - { - if (Thread.currentThread().equals(currentWriter)) + if (--numWriters <= 0) { + numWriters = 0; currentWriter = null; - documentCV.notifyAll(); - } + notifyAll(); } } @@ -2384,6 +2397,11 @@ /** The serialization UID (compatible with JDK1.5). */ private static final long serialVersionUID = 5230037221564563284L; + /** + * The threshold that indicates when we switch to using a Hashtable. + */ + private static final int THRESHOLD = 10; + /** The starting offset of the change. */ private int offset; @@ -2394,15 +2412,18 @@ private DocumentEvent.EventType type; /** - * Maps <code>Element</code> to their change records. + * Maps <code>Element</code> to their change records. This is only + * used when the changes array gets too big. We can use an + * (unsync'ed) HashMap here, since changes to this are (should) always + * be performed inside a write lock. */ - Hashtable changes; + private HashMap changes; /** * Indicates if this event has been modified or not. This is used to * determine if this event is thrown. */ - boolean modified; + private boolean modified; /** * Creates a new <code>DefaultDocumentEvent</code>. @@ -2417,7 +2438,6 @@ this.offset = offset; this.length = length; this.type = type; - changes = new Hashtable(); modified = false; } @@ -2431,9 +2451,27 @@ public boolean addEdit(UndoableEdit edit) { // XXX - Fully qualify ElementChange to work around gcj bug #2499. - if (edit instanceof DocumentEvent.ElementChange) + + // Start using Hashtable when we pass a certain threshold. This + // gives a good memory/performance compromise. + if (changes == null && edits.size() > THRESHOLD) { - modified = true; + changes = new HashMap(); + int count = edits.size(); + for (int i = 0; i < count; i++) + { + Object o = edits.elementAt(i); + if (o instanceof DocumentEvent.ElementChange) + { + DocumentEvent.ElementChange ec = + (DocumentEvent.ElementChange) o; + changes.put(ec.getElement(), ec); + } + } + } + + if (changes != null && edit instanceof DocumentEvent.ElementChange) + { DocumentEvent.ElementChange elEdit = (DocumentEvent.ElementChange) edit; changes.put(elEdit.getElement(), elEdit); @@ -2492,7 +2530,27 @@ public DocumentEvent.ElementChange getChange(Element elem) { // XXX - Fully qualify ElementChange to work around gcj bug #2499. - return (DocumentEvent.ElementChange) changes.get(elem); + DocumentEvent.ElementChange change = null; + if (changes != null) + { + change = (DocumentEvent.ElementChange) changes.get(elem); + } + else + { + int count = edits.size(); + for (int i = 0; i < count && change == null; i++) + { + Object o = edits.get(i); + if (o instanceof DocumentEvent.ElementChange) + { + DocumentEvent.ElementChange ec = + (DocumentEvent.ElementChange) o; + if (elem.equals(ec.getElement())) + change = ec; + } + } + } + return change; } /** Modified: trunk/core/src/classpath/javax/javax/swing/text/BoxView.java =================================================================== --- trunk/core/src/classpath/javax/javax/swing/text/BoxView.java 2006-12-09 18:44:58 UTC (rev 2881) +++ trunk/core/src/classpath/javax/javax/swing/text/BoxView.java 2006-12-09 18:47:15 UTC (rev 2882) @@ -38,6 +38,7 @@ package javax.swing.text; +import java.awt.Container; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.Shape; @@ -105,6 +106,8 @@ myAxis = axis; layoutValid[0] = false; layoutValid[1] = false; + requirementsValid[X_AXIS] = false; + requirementsValid[Y_AXIS] = false; span[0] = 0; span[1] = 0; requirements[0] = new SizeRequirements(); @@ -141,7 +144,10 @@ */ public void setAxis(int axis) { + boolean changed = axis != myAxis; myAxis = axis; + if (changed) + preferenceChanged(null, true, true); } /** @@ -222,55 +228,48 @@ */ public void replace(int offset, int length, View[] views) { - int numViews = 0; - if (views != null) - numViews = views.length; + // Actually perform the replace. + super.replace(offset, length, views); // Resize and copy data for cache arrays. - // The spansX cache. - int oldSize = getViewCount(); + int newItems = views != null ? views.length : 0; + int minor = 1 - myAxis; + offsets[myAxis] = replaceLayoutArray(offsets[myAxis], offset, newItems); + spans[myAxis] = replaceLayoutArray(spans[myAxis], offset, newItems); + layoutValid[myAxis] = false; + requirementsValid[myAxis] = false; + offsets[minor] = replaceLayoutArray(offsets[minor], offset, newItems); + spans[minor] = replaceLayoutArray(spans[minor], offset, newItems); + layoutValid[minor] = false; + requirementsValid[minor] = false; + } - 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)); - spans[X_AXIS] = newSpansX; + /** + * Helper method. This updates the layout cache arrays in response + * to a call to {@link #replace(int, int, View[])}. + * + * @param oldArray the old array + * + * @return the replaced array + */ + private int[] replaceLayoutArray(int[] oldArray, int offset, int newItems) - // The spansY cache. - 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)); - spans[Y_AXIS] = newSpansY; + { + int num = getViewCount(); + int[] newArray = new int[num]; + System.arraycopy(oldArray, 0, newArray, 0, offset); + System.arraycopy(oldArray, offset, newArray, offset + newItems, + num - newItems - offset); + return newArray; + } - // The offsetsX cache. - 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)); - offsets[X_AXIS] = newOffsetsX; + /** + * A Rectangle instance to be reused in the paint() method below. + */ + private final Rectangle tmpRect = new Rectangle(); - // The offsetsY cache. - 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)); - offsets[Y_AXIS] = newOffsetsY; + private Rectangle clipRect = new Rectangle(); - // Actually perform the replace. - super.replace(offset, length, views); - - // Invalidate layout information. - layoutValid[X_AXIS] = false; - requirementsValid[X_AXIS] = false; - layoutValid[Y_AXIS] = false; - requirementsValid[Y_AXIS] = false; - } - /** * Renders the <code>Element</code> that is associated with this * <code>View</code>. @@ -280,26 +279,20 @@ */ public void paint(Graphics g, Shape a) { - Rectangle alloc; - if (a instanceof Rectangle) - alloc = (Rectangle) a; - else - alloc = a.getBounds(); + // Try to avoid allocation if possible (almost all cases). + Rectangle alloc = a instanceof Rectangle ? (Rectangle) a : a.getBounds(); - int x = alloc.x + getLeftInset(); - int y = alloc.y + getTopInset(); + // This returns a cached instance. + alloc = getInsideAllocation(alloc); - Rectangle clip = g.getClipBounds(); - Rectangle tmp = new Rectangle(); int count = getViewCount(); - for (int i = 0; i < count; ++i) + for (int i = 0; i < count; i++) { - tmp.x = x + getOffset(X_AXIS, i); - tmp.y = y + getOffset(Y_AXIS, i); - tmp.width = getSpan(X_AXIS, i); - tmp.height = getSpan(Y_AXIS, i); - if (tmp.intersects(clip)) - paintChild(g, tmp, i); + View child = getView(i); + tmpRect.setBounds(alloc); + childAllocation(i, tmpRect); + if (g.hitClip(tmpRect.x, tmpRect.y, tmpRect.width, tmpRect.height)) + paintChild(g, tmpRect, i); } } @@ -571,7 +564,7 @@ res.minimum = 0; res.preferred = 0; - res.maximum = 0; + res.maximum = Integer.MAX_VALUE; res.alignment = 0.5F; int n = getViewCount(); for (int i = 0; i < n; i++) @@ -651,24 +644,54 @@ { View result = null; int count = getViewCount(); - Rectangle copy = new Rectangle(r); - - for (int i = 0; i < count; ++i) + if (myAxis == X_AXIS) { - copy.setBounds(r); - // The next call modifies copy. - childAllocation(i, copy); - if (copy.contains(x, y)) + // Border case. Requested point is left from the box. + if (x < r.x + offsets[X_AXIS][0]) { - // Modify r on success. - r.setBounds(copy); - result = getView(i); - break; + childAllocation(0, r); + result = getView(0); } + else + { + // Search views inside box. + for (int i = 0; i < count && result == null; i++) + { + if (x < r.x + offsets[X_AXIS][i]) + { + childAllocation(i - 1, r); + result = getView(i - 1); + } } - - if (result == null && count > 0) - return getView(count - 1); + } + } + else // Same algorithm for Y_AXIS. + { + // Border case. Requested point is above the box. + if (y < r.y + offsets[Y_AXIS][0]) + { + childAllocation(0, r); + result = getView(0); + } + else + { + // Search views inside box. + for (int i = 0; i < count && result == null; i++) + { + if (y < r.y + offsets[Y_AXIS][i]) + { + childAllocation(i - 1, r); + result = getView(i - 1); + } + } + } + } + // Not found, other border case: point is right from or below the box. + if (result == null) + { + childAllocation(count - 1, r); + result = getView(count - 1); + } return result; } @@ -702,49 +725,32 @@ */ protected void layout(int width, int height) { - int[] newSpan = new int[]{ width, height }; - int count = getViewCount(); - - // Update minor axis as appropriate. We need to first update the minor - // axis layout because that might affect the children's preferences along - // the major axis. - int minorAxis = myAxis == X_AXIS ? Y_AXIS : X_AXIS; - if ((! isLayoutValid(minorAxis)) || newSpan[minorAxis] != span[minorAxis]) - { - layoutValid[minorAxis] = false; - span[minorAxis] = newSpan[minorAxis]; - layoutMinorAxis(span[minorAxis], minorAxis, offsets[minorAxis], - spans[minorAxis]); - - // Update the child view's sizes. - for (int i = 0; i < count; ++i) - { - getView(i).setSize(spans[X_AXIS][i], spans[Y_AXIS][i]); + layoutAxis(X_AXIS, width); + layoutAxis(Y_AXIS, height); } - layoutValid[minorAxis] = true; - } - - // Update major axis as appropriate. - if ((! isLayoutValid(myAxis)) || newSpan[myAxis] != span[myAxis]) + private void layoutAxis(int axis, int s) { - layoutValid[myAxis] = false; - span[myAxis] = newSpan[myAxis]; - layoutMajorAxis(span[myAxis], myAxis, offsets[myAxis], - spans[myAxis]); + if (span[axis] != s) + layoutValid[axis] = false; + if (! layoutValid[axis]) + { + span[axis] = s; + updateRequirements(axis); + if (axis == myAxis) + layoutMajorAxis(span[axis], axis, offsets[axis], spans[axis]); + else + layoutMinorAxis(span[axis], axis, offsets[axis], spans[axis]); + layoutValid[axis] = true; - // Update the child view's sizes. - for (int i = 0; i < count; ++i) + // Push out child layout. + int viewCount = getViewCount(); + for (int i = 0; i < viewCount; i++) { - getView(i).setSize(spans[X_AXIS][i], spans[Y_AXIS][i]); + View v = getView(i); + v.setSize(spans[X_AXIS][i], spans[Y_AXIS][i]); } - layoutValid[myAxis] = true; } - - if (layoutValid[myAxis] == false) - System.err.println("WARNING: Major axis layout must be valid after layout"); - if (layoutValid[minorAxis] == false) - System.err.println("Minor axis layout must be valid after layout"); } /** @@ -767,7 +773,7 @@ { View child = getView(i); spans[i] = (int) child.getPreferredSpan(axis); - sumPref = spans[i]; + sumPref += spans[i]; } // Try to adjust the spans so that we fill the targetSpan. @@ -870,7 +876,9 @@ */ public int getWidth() { - return span[X_AXIS] + getLeftInset() - getRightInset(); + // The RI returns the following here, however, I'd think that is a bug. + // return span[X_AXIS] + getLeftInset() - getRightInset(); + return span[X_AXIS] + getLeftInset() + getRightInset(); } /** @@ -880,7 +888,9 @@ */ public int getHeight() { - return span[Y_AXIS] + getTopInset() - getBottomInset(); + // The RI returns the following here, however, I'd think that is a bug. + // return span[Y_AXIS] + getTopInset() - getBottomInset(); + return span[Y_AXIS] + getTopInset() + getBottomInset(); } /** @@ -1004,9 +1014,11 @@ { if (axis != X_AXIS && axis != Y_AXIS) throw new IllegalArgumentException("Illegal axis argument"); - int weight = 1; - if (axis == myAxis) - weight = 0; + updateRequirements(axis); + int weight = 0; + if ((requirements[axis].preferred != requirements[axis].minimum) + || (requirements[axis].preferred != requirements[axis].maximum)) + weight = 1; return weight; } @@ -1033,13 +1045,39 @@ protected void forwardUpdate(DocumentEvent.ElementChange ec, DocumentEvent e, Shape a, ViewFactory vf) { - // FIXME: What to do here? + boolean wasValid = isLayoutValid(myAxis); super.forwardUpdate(ec, e, a, vf); + // Trigger repaint when one of the children changed the major axis. + if (wasValid && ! isLayoutValid(myAxis)) + { + Container c = getContainer(); + if (a != null && c != null) + { + int pos = e.getOffset(); + int index = getViewIndexAtPosition(pos); + Rectangle r = getInsideAllocation(a); + if (myAxis == X_AXIS) + { + r.x += offsets[myAxis][index]; + r.width -= offsets[myAxis][index]; + } + else + { + r.y += offsets[myAxis][index]; + r.height -= offsets[myAxis][index]; + } + c.repaint(r.x, r.y, r.width, r.height); + } + } } public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) { - // FIXME: What to do here? + if (! isAllocationValid()) + { + Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds(); + setSize(r.width, r.height); + } return super.viewToModel(x, y, a, bias); } Modified: trunk/core/src/classpath/javax/javax/swing/text/ComponentView.java =================================================================== --- trunk/core/src/classpath/javax/javax/swing/text/ComponentView.java 2006-12-09 18:44:58 UTC (rev 2881) +++ trunk/core/src/classpath/javax/javax/swing/text/ComponentView.java 2006-12-09 18:47:15 UTC (rev 2882) @@ -397,7 +397,24 @@ { public void run() { + 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(); + } } }); } Modified: trunk/core/src/classpath/javax/javax/swing/text/CompositeView.java =================================================================== --- trunk/core/src/classpath/javax/javax/swing/text/CompositeView.java 2006-12-09 18:44:58 UTC (rev 2881) +++ trunk/core/src/classpath/javax/javax/swing/text/CompositeView.java 2006-12-09 18:47:15 UTC (rev 2882) @@ -56,14 +56,19 @@ /** * The child views of this <code>CompositeView</code>. */ - View[] children; + private View[] children; /** + * The number of child views. + */ + private int numChildren; + + /** * The allocation of this <code>View</code> minus its insets. This is * initialized in {@link #getInsideAllocation} and reused and modified in * {@link #childAllocation(int, Rectangle)}. */ - Rectangle insideAllocation; + private final Rectangle insideAllocation = new Rectangle(); /** * The insets of this <code>CompositeView</code>. This is initialized @@ -101,6 +106,8 @@ */ protected void loadChildren(ViewFactory f) { + if (f != null) + { Element el = getElement(); int count = el.getElementCount(); View[] newChildren = new View[count]; @@ -115,6 +122,7 @@ // Harmony's tests this is not what the RI does. replace(0, 0, newChildren); } + } /** * Sets the parent of this <code>View</code>. @@ -126,7 +134,7 @@ public void setParent(View parent) { super.setParent(parent); - if (parent != null && ((children == null) || children.length == 0)) + if (parent != null && numChildren == 0) loadChildren(getViewFactory()); } @@ -137,7 +145,7 @@ */ public int getViewCount() { - return children.length; + return numChildren; } /** @@ -169,27 +177,37 @@ if (views == null) views = new View[0]; - // Check for null views to add. - for (int i = 0; i < views.length; ++i) - if (views[i] == null) - throw new NullPointerException("Added views must not be null"); - + // First we set the parent of the removed children to null. int endOffset = offset + length; - - // First we set the parent of the removed children to null. for (int i = offset; i < endOffset; ++i) { if (children[i].getParent() == this) children[i].setParent(null); + children[i] = null; } - View[] newChildren = new View[children.length - length + views.length]; + // Update the children array. + int delta = views.length - length; + int src = offset + length; + int numMove = numChildren - src; + int dst = src + delta; + if (numChildren + delta > children.length) + { + // Grow array. + int newLength = Math.max(2 * children.length, numChildren + delta); + View[] newChildren = new View[newLength]; System.arraycopy(children, 0, newChildren, 0, offset); System.arraycopy(views, 0, newChildren, offset, views.length); - System.arraycopy(children, offset + length, newChildren, - offset + views.length, - children.length - (offset + length)); + System.arraycopy(children, src, newChildren, dst, numMove); children = newChildren; + } + else + { + // Patch existing array. + System.arraycopy(children, src, children, dst, numMove); + System.arraycopy(views, 0, children, offset, views.length); + } + numChildren += delta; // Finally we set the parent of the added children to this. for (int i = 0; i < views.length; ++i) @@ -264,12 +282,12 @@ } } } - else - { + } + + if (ret == null) throw new BadLocationException("Position " + pos + " is not represented by view.", pos); - } - } + return ret; } @@ -509,24 +527,17 @@ if (a == null) return null; - Rectangle alloc = a.getBounds(); + // Try to avoid allocation of Rectangle here. + Rectangle alloc = a instanceof Rectangle ? (Rectangle) a : a.getBounds(); + // Initialize the inside allocation rectangle. This is done inside // a synchronized block in order to avoid multiple threads creating // this instance simultanously. - Rectangle inside; - synchronized(this) - { - inside = insideAllocation; - if (inside == null) - { - inside = new Rectangle(); - insideAllocation = inside; - } - } - inside.x = alloc.x + left; - inside.y = alloc.y + top; - inside.width = alloc.width - left - right; - inside.height = alloc.height - top - bottom; + Rectangle inside = insideAllocation; + inside.x = alloc.x + getLeftInset(); + inside.y = alloc.y + getTopInset(); + inside.width = alloc.width - getLeftInset() - getRightInset(); + inside.height = alloc.height - getTopInset() - getBottomInset(); return inside; } Modified: trunk/core/src/classpath/javax/javax/swing/text/DefaultCaret.java =================================================================== --- trunk/core/src/classpath/javax/javax/swing/text/DefaultCaret.java 2006-12-09 18:44:58 UTC (rev 2881) +++ trunk/core/src/classpath/javax/javax/swing/text/DefaultCaret.java 2006-12-09 18:47:15 UTC (rev 2882) @@ -1075,8 +1075,6 @@ handleHighlight(); appear(); - - adjustVisibility(this); } } @@ -1114,8 +1112,6 @@ clearHighlight(); appear(); - - adjustVisibility(this); } } @@ -1154,8 +1150,13 @@ // e.printStackTrace(); } if (area != null) + { + adjustVisibility(area); + if (getMagicCaretPosition() == null) + setMagicCaretPosition(new Point(area.x, area.y)); damage(area); } + } repaint(); } Modified: trunk/core/src/classpath/javax/javax/swing/text/DefaultEditorKit.java =================================================================== --- trunk/core/src/classpath/javax/javax/swing/text/DefaultEditorKit.java 2006-12-09 18:44:58 UTC (rev 2881) +++ trunk/core/src/classpath/javax/javax/swing/text/DefaultEditorKit.java 2006-12-09 18:47:15 UTC (rev 2882) @@ -311,11 +311,12 @@ public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); + if (t != null) + { int offs = t.getDocument().getLength(); Caret c = t.getCaret(); c.setDot(0); c.moveDot(offs); - try { c.setMagicCaretPosition(t.modelToView(offs).getLocation()); @@ -326,6 +327,7 @@ } } } + } static class SelectionBeginAction extends TextAction @@ -338,6 +340,8 @@ public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); + if (t != null) + { Caret c = t.getCaret(); c.moveDot(0); try @@ -350,6 +354,7 @@ } } } + } static class SelectionEndAction extends TextAction @@ -362,6 +367,8 @@ public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); + if (t != null) + { int offs = t.getDocument().getLength(); Caret c = t.getCaret(); c.moveDot(offs); @@ -375,6 +382,7 @@ } } } + } static class SelectionBeginLineAction extends TextAction @@ -388,6 +396,8 @@ public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); + if (t != null) + { Caret c = t.getCaret(); try { @@ -398,7 +408,7 @@ { // Can't happen. } - + } } } @@ -413,6 +423,8 @@ public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); + if (t != null) + { Caret c = t.getCaret(); try { @@ -423,7 +435,7 @@ { // Can't happen. } - + } } } @@ -437,15 +449,15 @@ public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); + if (t != null) + { Caret c = t.getCaret(); try { int offs1 = Utilities.getRowStart(t, c.getDot()); int offs2 = Utilities.getRowEnd(t, c.getDot()); - c.setDot(offs2); c.moveDot(offs1); - c.setMagicCaretPosition(t.modelToView(offs2).getLocation()); } catch(BadLocationException ble) @@ -454,6 +466,7 @@ } } } + } static class SelectWordAction extends TextAction { @@ -465,9 +478,10 @@ public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); + if (t != null) + { Caret c = t.getCaret(); int dot = c.getDot(); - try { int wordStart = Utilities.getWordStart(t, dot); @@ -505,7 +519,6 @@ // as well. if (c.getDot() != dot) c.setMagicCaretPosition(t.modelToView(c.getDot()).getLocation()); - } catch(BadLocationException ble) { @@ -513,6 +526,7 @@ } } } + } static class SelectionDownAction extends TextAction.VerticalMovementAction @@ -714,10 +728,11 @@ public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); + if (t != null) + { try { int offs = Utilities.getRowEnd(t, t.getCaretPosition()); - if (offs > -1) { Caret c = t.getCaret(); @@ -731,6 +746,7 @@ } } } + } static class BeginLineAction extends TextAction @@ -743,10 +759,11 @@ public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); + if (t != null) + { try { int offs = Utilities.getRowStart(t, t.getCaretPosition()); - if (offs > -1) { Caret c = t.getCaret(); @@ -760,6 +777,7 @@ } } } + } static class BeginAction extends TextAction { @@ -772,6 +790,8 @@ public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); + if (t != null) + { Caret c = t.getCaret(); c.setDot(0); try @@ -784,6 +804,7 @@ } } } + } static class EndAction extends TextAction { @@ -796,6 +817,8 @@ public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); + if (t != null) + { int offs = t.getDocument().getLength(); Caret c = t.getCaret(); c.setDot(offs); @@ -809,6 +832,7 @@ } } } + } /** * Creates a beep on the PC speaker. @@ -861,7 +885,9 @@ */ public void actionPerformed(ActionEvent event) { - getTextComponent(event).copy(); + JTextComponent target = getTextComponent(event); + if (target != null) + target.copy(); } } @@ -892,7 +918,9 @@ */ public void actionPerformed(ActionEvent event) { - getTextComponent(event).cut(); + JTextComponent target = getTextComponent(event); + if (target != null) + target.cut(); } } @@ -921,7 +949,9 @@ */ public void actionPerformed(ActionEvent event) { - getTextComponent(event).paste(); + JTextComponent target = getTextComponent(event); + if (target != null) + target.paste(); } } @@ -1003,6 +1033,7 @@ public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); + if (t != null) t.replaceSelection("\n"); } } @@ -1058,6 +1089,7 @@ public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); + if (t != null) t.replaceSelection("\t"); } } Modified: trunk/core/src/classpath/javax/javax/swing/text/DefaultFormatter.java =================================================================== --- trunk/core/src/classpath/javax/javax/swing/text/DefaultFormatter.java 2006-12-09 18:44:58 UTC (rev 2881) +++ trunk/core/src/classpath/javax/javax/swing/text/DefaultFormatter.java 2006-12-09 18:47:15 UTC (rev 2882) @@ -216,7 +216,7 @@ */ public DefaultFormatter() { - commitsOnValidEdit = true; + commitsOnValidEdit = false; overwriteMode = true; allowsInvalid = true; } Modified: trunk/core/src/classpath/javax/javax/swing/text/DefaultStyledDocument.java =================================================================== --- trunk/core/src/classpath/javax/javax/swing/text/DefaultStyledDocument.java 2006-12-09 18:44:58 UTC (rev 2881) +++ trunk/core/src/classpath/javax/javax/swing/text/DefaultStyledDocument.java 2006-12-09 18:47:15 UTC (rev 2882) @@ -683,6 +683,58 @@ return ret; } + /** + * Creates a document in response to a call to + * {@link DefaultStyledDocument#create(ElementSpec[])}. + * + * @param len the length of the inserted text + * @param data the specs for the elements + * @param ev the document event + */ + void create(int len, ElementSpec[] data, DefaultDocumentEvent ev) + { + prepareEdit(offset, len); + Element el = root; + int index = el.getElementIndex(0); + while (! el.isLeaf()) + { + Element child = el.getElement(index); + Edit edit = new Edit(el, index, false); + elementStack.push(edit); + el = child; + index = el.getElementIndex(0); + } + Edit ed = (Edit) elementStack.peek(); + Element child = ed.e.getElement(ed.index); + ed.added.add(createLeafElement(ed.e, child.getAttributes(), getLength(), + child.getEndOffset())); + ed.removed.add(child); + while (elementStack.size() > 1) + pop(); + int n = data.length; + + // Reset root element's attributes. + AttributeSet newAtts = null; + if (n > 0 && data[0].getType() == ElementSpec.StartTagType) + newAtts = data[0].getAttributes(); + if (newAtts == null) + newAtts = SimpleAttributeSet.EMPTY; + MutableAttributeSet mAtts = (MutableAttributeSet) root.getAttributes(); + ev.addEdit(new AttributeUndoableEdit(root, newAtts, true)); + mAtts.removeAttributes(mAtts); + mAtts.addAttributes(newAtts); + + // Insert the specified elements. + for (int i = 1; i < n; i++) + insertElement(data[i]); + + // Pop remaining stack. + while (elementStack.size() > 0) + pop(); + + finishEdit(ev); + } + private boolean canJoin(Element e0, Element e1) { boolean ret = false; @@ -987,6 +1039,8 @@ ElementEdit ee = new ElementEdit(parent, index, removed, added); ev.addEdit(ee); } + edits.clear(); + elementStack.clear(); } /** @@ -1069,16 +1123,15 @@ if (offset == 0 && fracturedParent != null && data[0].getType() == ElementSpec.EndTagType) { - for (int p = 0; + int p; + for (p = 0; p < data.length && data[p].getType() == ElementSpec.EndTagType; - p++) - { + p++); Edit edit = insertPath[insertPath.length - p - 1]; edit.index--; edit.removed.add(0, edit.e.getElement(edit.index)); } } - } private void pop() { @@ -2379,18 +2432,24 @@ if (length == 0) return; - UndoableEdit edit = content.insertString(offset, + Content c = getContent(); + UndoableEdit edit = c.insertString(offset, contentBuffer.toString()); // Create the DocumentEvent with the ElementEdit added DefaultDocumentEvent ev = new DefaultDocumentEvent(offset, length, DocumentEvent.EventType.INSERT); + ev.addEdit(edit); // Finally we must update the document structure and fire the insert // update event. buffer.insert(offset, length, data, ev); + + super.insertUpdate(ev, null); + + ev.end(); fireInsertUpdate(ev); fireUndoableEditUpdate(new UndoableEditEvent(this, ev)); } @@ -2410,14 +2469,16 @@ */ protected void create(ElementSpec[] data) { - writeLock(); try { + // Clear content if there is some. int len = getLength(); if (len > 0) remove(0, len); + writeLock(); + // Now we insert the content. StringBuilder b = new StringBuilder(); for (int i = 0; i < data.length; ++i) @@ -2429,38 +2490,18 @@ Content content = getContent(); UndoableEdit cEdit = content.insertString(0, b.toString()); + len = b.length(); DefaultDocumentEvent ev = new DefaultDocumentEvent(0, b.length(), DocumentEvent.EventType.INSERT); ev.addEdit(cEdit); - // We do a little trick here to get the new structure: We instantiate - // a new ElementBuffer with a new root element, insert into that root - // and then reparent the newly created elements to the old root - // element. - BranchElement createRoot = - (BranchElement) createBranchElement(null, null); - Element dummyLeaf = createLeafElement(createRoot, null, 0, 1); - createRoot.replace(0, 0, new Element[]{ dummyLeaf }); - ElementBuffer createBuffer = new ElementBuffer(createRoot); - createBuffer.insert(0, b.length(), data, new DefaultDocumentEvent(0, b.length(), DocumentEvent.EventType.INSERT)); - // Now the new root is the first child of the createRoot. - Element newRoot = createRoot.getElement(0); - BranchElement root = (BranchElement) getDefaultRootElement(); - Element[] added = new Element[newRoot.getElementCount()]; - for (int i = 0; i < added.length; ++i) - { - added[i] = newRoot.getElement(i); - ((AbstractElement) added[i]).element_parent = root; - } - Element[] removed = new Element[root.getElementCount()]; - for (int i = 0; i < removed.length; ++i) - removed[i] = root.getElement(i); + buffer.create(len, data, ev); - // Replace the old elements in root with the new and update the event. - root.replace(0, removed.length, added); - ev.addEdit(new ElementEdit(root, 0, removed, added)); + // For the bidi update. + super.insertUpdate(ev, null); + ev.end(); fireInsertUpdate(ev); fireUndoableEditUpdate(new UndoableEditEvent(this, ev)); } Modified: trunk/core/src/classpath/javax/javax/swing/text/ElementIterator.java =================================================================== --- trunk/core/src/classpath/javax/javax/swing/text/ElementIterator.java 2006-12-09 18:44:58 UTC (rev 2881) +++ trunk/core/src/classpath/javax/javax/swing/text/ElementIterator.java 2006-12-09 18:47:15 UTC (rev 2882) @@ -37,6 +37,8 @@ package javax.swing.text; +import java.util.Stack; + /** * This class can be used to iterate over the {@link Element} tree of * a {@link Document} or an {@link Element}. This iterator performs @@ -46,30 +48,49 @@ */ public class ElementIterator implements Cloneable { + /** + * Uses to track the iteration on the stack. + */ + private class ElementRef + { + /** + * The element. + */ + Element element; + + /** + * The child index. -1 means the element itself. >= 0 values mean the + * n-th child of the element. + */ + int index; + + /** + * Creates a new ElementRef. + * + * @param el the element + */ + ElementRef(Element el) + { + element = el; + index = -1; + } + } + // The root element. private Element root; - // The current element. - private Element currentElement; - // The depth to which we have descended in the tree. - private int currentDepth; - // This is at least as big as the current depth, and at index N - // contains the index of the child element we're currently - // examining. - private int[] state; + /** + * Holds ElementRefs. + */ + private Stack stack; - // The previous item. - private Element previousItem; - /** * Create a new ElementIterator to iterate over the given document. * @param document the Document over which we iterate */ public ElementIterator(Document document) { - this.root = document.getDefaultRootElement(); - this.currentElement = root; - this.state = new int[5]; + root = document.getDefaultRootElement(); } /** @@ -79,8 +100,6 @@ public ElementIterator(Element root) { this.root = root; - this.currentElement = root; - this.state = new int[5]; } /** @@ -105,7 +124,24 @@ */ public Element current() { - return currentElement; + Element current; + if (stack == null) + current = first(); + else + { + current = null; + if (! stack.isEmpty()) + { + ElementRef ref = (ElementRef) stack.peek(); + Element el = ref.element; + int index = ref.index; + if (index == -1) + current = el; + else + current = el.getElement(index); + } + } + return current; } /** @@ -113,7 +149,10 @@ */ public int depth() { - return currentDepth; + int depth = 0; + if (stack != null) + depth = stack.size(); + return depth; } /** @@ -121,11 +160,15 @@ */ public Element first() { - // Reset the iterator. - currentElement = root; - currentDepth = 0; - previousItem = null; - return root; + Element first = null; + if (root != null) + { + stack = new Stack(); + if (root.getElementCount() > 0) + stack.push(new ElementRef(root)); + first = root; + } + return first; } /** @@ -134,39 +177,41 @@ */ public Element next() { - previousItem = currentElement; - if (currentElement == null) - return null; - if (! currentElement.isLeaf()) + Element next; + if (stack == null) + next = first(); + else { - ++currentDepth; - if (currentDepth > state.length) + next = null; + if (! stack.isEmpty()) { - int[] newState = new int[state.length * 2]; - System.arraycopy(state, 0, newState, 0, state.length); - state = newState; - } - state[currentDepth] = 0; - currentElement = currentElement.getElement(0); - return currentElement; + ElementRef ref = (ElementRef) stack.peek(); + Element el = ref.element; + int index = ref.index; + if (el.getElementCount() > index + 1) + { + Element child = el.getElement(index + 1); + if (child.isLeaf()) + ref.index++; + else + stack.push(new ElementRef(child)); + next = child; + next = child; } - - while (currentDepth > 0) + else { - // At a leaf, or done with a non-leaf's children, so go up a - // level. - --currentDepth; - currentElement = currentElement.getParentElement(); - ++state[currentDepth]; - if (state[currentDepth] < currentElement.getElementCount()) + stack.pop(); + if (! stack.isEmpty()) { - currentElement = currentElement.getElement(state[currentDepth]); - return currentElement; + ElementRef top = (ElementRef) stack.peek(); + top.index++; + next = next(); } } - - currentElement = null; - return currentElement; + } + // else return null. + } + return next; } /** @@ -174,8 +219,54 @@ */ public Element previous() { - if (currentElement == null || currentElement == root) - return null; - return previousItem; + Element previous = null; + int stackSize; + if (stack != null && (stackSize = stack.size()) > 0) + { + ElementRef ref = (ElementRef) stack.peek(); + Element el = ref.element; + int index = ref.index; + if (index > 0) + { + previous = deepestLeaf(el.getElement(--index)); + } + else if (index == 0) + { + previous = el; + } + else if (index == -1) + { + ElementRef top = (ElementRef) stack.pop(); + ElementRef item = (ElementRef) stack.peek(); + stack.push(top); + index = item.index; + el = item.element; + previous = index == -1 ? el : deepestLeaf(el.getElement(index)); + } + } + return previous; } + + /** + * Determines and returns the deepest leaf of the element <code>el</code>. + * + * @param el the base element + * + * @returnthe deepest leaf of the element <code>el</code> + */ + private Element deepestLeaf(Element el) + { + Element leaf; + if (el.isLeaf()) + leaf = el; + else + { + int count = el.getElementCount(); + if (count == 0) + leaf = el; + else + leaf = deepestLeaf(el.getElement(count - 1)); + } + return leaf; + } } Modified: trunk/core/src/classpath/javax/javax/swing/text/FieldView.java =================================================================== --- trunk/core/src/classpath/javax/javax/swing/text/FieldView.java 2006-12-09 18:44:58 UTC (rev 2881) +++ trunk/core/src/classpath/javax/javax/swing/text/FieldView.java 2006-12-09 18:47:15 UTC (rev 2882) @@ -45,8 +45,6 @@ import java.awt.Insets; import java.awt.Rectangle; import java.awt.Shape; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import javax.swing.BoundedRangeModel; import javax.swing.JTextField; @@ -225,7 +223,7 @@ public int getResizeWeight(int axis) { - return axis = axis == X_AXIS ? 1 : 0; + return axis == X_AXIS ? 1 : 0; } public Shape modelToView(int pos, Shape a, Position.Bias bias) Modified: trunk/core/src/classpath/javax/javax/swing/text/FlowView.java =================================================================== --- trunk/core/src/classpath/javax/javax/swing/text/FlowView.java 2006-12-09 18:44:58 UTC (rev 2881) +++ trunk/core/src/classpath/javax/javax/swing/text/FlowView.java 2006-12-09 18:47:15 UTC (rev 2882) @@ -38,6 +38,8 @@ package javax.swing.text; +import java.awt.Component; +import java.awt.Graphics; import java.awt.Rectangle; import java.awt.Shape; @@ -85,7 +87,17 @@ */ public void insertUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) { - // The default implementation does nothing. + if (alloc == null) + { + fv.layoutChanged(X_AXIS); + fv.layoutChanged(Y_AXIS); + } + else + { + Component host = fv.getContainer(); + if (host != null) + host.repaint(alloc.x, alloc.y, alloc.width, alloc.height); + } } /** @@ -101,7 +113,17 @@ */ public void removeUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) { - // The default implementation does nothing. + if (alloc == null) + { + fv.layoutChanged(X_AXIS); + fv.layoutChanged(Y_AXIS); + } + else + { + Component host = fv.getContainer(); + if (host != null) + host.repaint(alloc.x, alloc.y, alloc.width, alloc.height); + } } /** @@ -117,7 +139,17 @@ */ public void changedUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) { - // The default implementation does nothing. + if (alloc == null) + { + fv.layoutChanged(X_AXIS); + fv.layoutChanged(Y_AXIS); + } + else + { + Component host = fv.getContainer(); + if (host != null) + host.repaint(alloc.x, alloc.y, alloc.width, alloc.height); + } } /** @@ -143,18 +175,35 @@ */ public void layout(FlowView fv) { + 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(); - Element el = fv.getElement(); - int rowStart = el.getStartOffset(); - int end = el.getEndOffset(); - int rowIndex = 0; - while (rowStart >= 0 && rowStart < end) + for (int rowIndex = 0; start < end; rowIndex++) { View row = fv.createRow(); fv.append(row); - rowStart = layoutRow(fv, rowIndex, rowStart); - rowIndex++; + 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"; } } @@ -179,46 +228,69 @@ 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; + int end = fv.getEndOffset(); - Row: while (span > 0) + // Needed for adjusting indentation in adjustRow(). + int preX = x; + int availableSpan = span; + + TabExpander tabExp = fv instanceof TabExpander ? (TabExpander) fv : null; + + boolean forcedBreak = false; + while (pos < end && span >= 0) { - if (logicalView.getViewIndex(offset, Position.Bias.Forward) == - 1) + View view = createView(fv, pos, span, rowIndex); + if (view == null + || (span == 0 && view.getPreferredSpan(axis) > 0)) break; - View view = createView(fv, offset, span, rowIndex); - if (view == null) - break; - int viewSpan = (int) view.getPreferredSpan(axis); - int breakWeight = view.getBreakWeight(axis, x, span); + int viewSpan; + if (axis == X_AXIS && view instanceof TabableView) + viewSpan = (int) ((TabableView) view).getTabbedSpan(x, tabExp); + else + viewSpan = (int) view.getPreferredSpan(axis); - row.append(view); - offset += (view.getEndOffset() - view.getStartOffset()); - x += viewSpan; - span -= viewSpan; - // Break if the line if the view does not fit in this row or the // line just must be broken. - if (span < 0 || breakWeight >= View.ForcedBreakWeight) + int breakWeight = view.getBreakWeight(axis, pos, span); + if (breakWeight >= ForcedBreakWeight) { - 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(); + { + view = view.breakView(axis, pos, x, span); + if (view != null) + { + if (axis == X_AXIS && view instanceof TabableView) + viewSpan = + (int) ((TabableView) view).getTabbedSpan(x, tabExp); else - offset = - 1; - break Row; + viewSpan = (int) view.getPreferredSpan(axis); } + else + ... [truncated message content] |