From 61cab9b7c774229aa09a35cd9cfcf8d736c982b4 Mon Sep 17 00:00:00 2001 From: Mark Wielaard Date: Tue, 8 Aug 2006 20:40:49 +0000 Subject: 2006-08-08 Roman Kennke * javax/swing/plaf/basic/BasicTextUI.java (damageRange(JTextComponent,int,int)): Call damageRange() with correct biases, rather than null. (damageRange(JTextComponent,int,int,Bias,Bias)): Rewritten to use simpler modelToView() approach without much special casing. This seems not worth the effort and actually caused problems. Added locking of the document. * javax/swing/text/BoxView.java (requirementsValid): New field. (calculateMajorAxisRequirements): Rewritten without using SizeRequirements. The SizeRequirements algorithms are slightly different and too inefficient. (calculateMinorAxisRequirements): Rewritten without using SizeRequirements. The SizeRequirements algorithms are slightly different and too inefficient. (getAlignment): Simply return the alignment of the cached requirements. (getMaximumSpan): Add insets. (getMinimumSpan): Add insets. (getPreferredSpan): Add insets. (layoutMajorAxis): Rewritten without using SizeRequirements. The SizeRequirements algorithms are slightly different and too inefficient. (layoutMinorAxis): Rewritten without using SizeRequirements. The SizeRequirements algorithms are slightly different and too inefficient. (modelToView): Call setSize() rather than layout(). (paint): Check clip for more efficient painting. (preferenceChanged): Invalidate requirements here. (replace): Invalidate requirements here. (updateRequirements): Update requirements only when requirements are marked invalid. * javax/swing/text/CompositeView.java (modelToView): Added some more checks and handling of corner cases. * javax/swing/text/FlowView.java (calculateMinorAxisRequirements): Set aligment to 0.5 and maximum span to Integer.MAX_VALUE. Limit preferredSize to minimumSize. * javax/swing/text/IconView.java (getAlignment): Implemented to return 1.0 for vertical alignment. * javax/swing/text/ParagraphView.java (Row.getMaximumSpan): Implemented to let Rows span the whole ParagraphView. (getAlignment): Fixed horizontal alignment and vertical alignment for empty paragraphs to be 0.5. 2006-08-08 Roman Kennke * javax/swing/text/View.java (modelToView): Added special handling for corner case at the end of the view and for multiline views. 2006-08-08 Roman Kennke * javax/swing/plaf/basic/BasicTextUI.java (modelToView): Read-lock the document. Set size of the root view before fetching the model-to-view mapping. (getViewIndex): Check of the position is inside the range and return -1 if this is not the case. (getViewAtPosition(int,Rectangle): Update child allocation for valid view index. (getViewIndexAtPosition(int)): Delegate the index search to the element since we have a 1:1 mapping between elements and views here. * javax/swing/text/DefaultCaret.java (appear): Ignore BadLocationException. (paint): Ignore BadLocationException. * javax/swing/text/FlowView.java (changedUpdate): Also notify the layoutPool view. (removeUpdate): Also notify the layoutPool view. * javax/swing/text/ParagraphView.java (Row.getViewIndexAtPosition): Overridden to search linearily through the view instead of relying on a 1:1 model to view mapping. * javax/swing/text/View.java (removeUpdate): Clear ElementChange object if updateChildren returns false. (forwardUpdate): Special handle some boundary cases. --- ChangeLog | 80 ++++++++++ javax/swing/plaf/basic/BasicTextUI.java | 144 ++++++----------- javax/swing/text/BoxView.java | 264 ++++++++++++++++++++------------ javax/swing/text/CompositeView.java | 82 ++++++---- javax/swing/text/DefaultCaret.java | 16 +- javax/swing/text/FlowView.java | 14 +- javax/swing/text/IconView.java | 18 ++- javax/swing/text/ParagraphView.java | 37 ++++- javax/swing/text/View.java | 113 ++++++++++++-- 9 files changed, 504 insertions(+), 264 deletions(-) diff --git a/ChangeLog b/ChangeLog index 54728531e..341e8a5cf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,83 @@ +2006-08-08 Roman Kennke + + * javax/swing/plaf/basic/BasicTextUI.java + (damageRange(JTextComponent,int,int)): Call damageRange() with + correct biases, rather than null. + (damageRange(JTextComponent,int,int,Bias,Bias)): Rewritten + to use simpler modelToView() approach without much special + casing. This seems not worth the effort and actually + caused problems. Added locking of the document. + * javax/swing/text/BoxView.java + (requirementsValid): New field. + (calculateMajorAxisRequirements): Rewritten without using + SizeRequirements. The SizeRequirements algorithms are slightly + different and too inefficient. + (calculateMinorAxisRequirements): Rewritten without using + SizeRequirements. The SizeRequirements algorithms are slightly + different and too inefficient. + (getAlignment): Simply return the alignment of the cached + requirements. + (getMaximumSpan): Add insets. + (getMinimumSpan): Add insets. + (getPreferredSpan): Add insets. + (layoutMajorAxis): Rewritten without using + SizeRequirements. The SizeRequirements algorithms are slightly + different and too inefficient. + (layoutMinorAxis): Rewritten without using + SizeRequirements. The SizeRequirements algorithms are slightly + different and too inefficient. + (modelToView): Call setSize() rather than layout(). + (paint): Check clip for more efficient painting. + (preferenceChanged): Invalidate requirements here. + (replace): Invalidate requirements here. + (updateRequirements): Update requirements only when requirements + are marked invalid. + * javax/swing/text/CompositeView.java + (modelToView): Added some more checks and handling of corner cases. + * javax/swing/text/FlowView.java + (calculateMinorAxisRequirements): Set aligment to 0.5 and maximum + span to Integer.MAX_VALUE. Limit preferredSize to minimumSize. + * javax/swing/text/IconView.java + (getAlignment): Implemented to return 1.0 for vertical alignment. + * javax/swing/text/ParagraphView.java + (Row.getMaximumSpan): Implemented to let Rows span the whole + ParagraphView. + (getAlignment): Fixed horizontal alignment and vertical alignment + for empty paragraphs to be 0.5. + +2006-08-08 Roman Kennke + + * javax/swing/text/View.java + (modelToView): Added special handling for corner case at the end + of the view and for multiline views. + +2006-08-08 Roman Kennke + + * javax/swing/plaf/basic/BasicTextUI.java + (modelToView): Read-lock the document. Set size of the + root view before fetching the model-to-view mapping. + (getViewIndex): Check of the position is inside the range and + return -1 if this is not the case. + (getViewAtPosition(int,Rectangle): Update child allocation for valid + view index. + (getViewIndexAtPosition(int)): Delegate the index search to + the element since we have a 1:1 mapping between elements and + views here. + * javax/swing/text/DefaultCaret.java + (appear): Ignore BadLocationException. + (paint): Ignore BadLocationException. + * javax/swing/text/FlowView.java + (changedUpdate): Also notify the layoutPool view. + (removeUpdate): Also notify the layoutPool view. + * javax/swing/text/ParagraphView.java + (Row.getViewIndexAtPosition): Overridden to search linearily + through the view instead of relying on a 1:1 model to view + mapping. + * javax/swing/text/View.java + (removeUpdate): Clear ElementChange object if updateChildren + returns false. + (forwardUpdate): Special handle some boundary cases. + 2006-08-07 Raif S. Naffah * gnu/java/security/key/dss/DSSKey.java: Updated documentation. diff --git a/javax/swing/plaf/basic/BasicTextUI.java b/javax/swing/plaf/basic/BasicTextUI.java index 5febe141d..8e9c8c949 100644 --- a/javax/swing/plaf/basic/BasicTextUI.java +++ b/javax/swing/plaf/basic/BasicTextUI.java @@ -83,7 +83,6 @@ import javax.swing.text.Highlighter; import javax.swing.text.JTextComponent; import javax.swing.text.Keymap; import javax.swing.text.Position; -import javax.swing.text.Utilities; import javax.swing.text.View; import javax.swing.text.ViewFactory; @@ -1042,7 +1041,7 @@ public abstract class BasicTextUI extends TextUI */ public void damageRange(JTextComponent t, int p0, int p1) { - damageRange(t, p0, p1, null, null); + damageRange(t, p0, p1, Position.Bias.Forward, Position.Bias.Backward); } /** @@ -1062,106 +1061,36 @@ public abstract class BasicTextUI extends TextUI public void damageRange(JTextComponent t, int p0, int p1, Position.Bias firstBias, Position.Bias secondBias) { - // Do nothing if the component cannot be properly displayed. - if (t.getWidth() == 0 || t.getHeight() == 0) - return; - - try + Rectangle alloc = getVisibleEditorRect(); + if (alloc != null) { - // Limit p0 and p1 to sane values to prevent unfriendly - // BadLocationExceptions. This makes it possible for the highlighter - // to send us illegal values which can happen when a large number - // of selected characters are removed (eg. by pressing delete - // or backspace). - // The reference implementation does not throw an exception, too. - p0 = Math.min(p0, t.getDocument().getLength()); - p1 = Math.min(p1, t.getDocument().getLength()); - - Rectangle l1 = modelToView(t, p0, firstBias); - Rectangle l2 = modelToView(t, p1, secondBias); - if (l1 == null || l2 == null) + Document doc = t.getDocument(); + + // Acquire lock here to avoid structural changes in between. + if (doc instanceof AbstractDocument) + ((AbstractDocument) doc).readLock(); + try { - // Unable to determine the start or end of the selection. - t.repaint(); + rootView.setSize(alloc.width, alloc.height); + Shape damage = rootView.modelToView(p0, firstBias, p1, secondBias, + alloc); + Rectangle r = damage instanceof Rectangle ? (Rectangle) damage + : damage.getBounds(); + textComponent.repaint(r.x, r.y, r.width, r.height); } - else if (l1.y == l2.y) + catch (BadLocationException ex) { - SwingUtilities.computeUnion(l2.x, l2.y, l2.width, l2.height, l1); - t.repaint(l1); + // Lets ignore this as it causes no serious problems. + // For debugging, comment this out. + // ex.printStackTrace(); } - else + finally { - // The two rectangles lie on different lines and we need a - // different algorithm to calculate the damaged area: - // 1. The line of p0 is damaged from the position of p0 - // to the right border. - // 2. All lines between the ones where p0 and p1 lie on - // are completely damaged. Use the allocation area to find - // out the bounds. - // 3. The final line is damaged from the left bound to the - // position of p1. - Insets insets = t.getInsets(); - - // Damage first line until the end. - l1.width = insets.right + t.getWidth() - l1.x; - t.repaint(l1); - - // Note: Utilities.getPositionBelow() may return the offset - // that was put in. In that case there is no next line and - // we should stop searching for one. - - int posBelow = Utilities.getPositionBelow(t, p0, l1.x); - int p1RowStart = Utilities.getRowStart(t, p1); - - if (posBelow != -1 - && posBelow != p0 - && Utilities.getRowStart(t, posBelow) != p1RowStart) - { - // Take the rectangle of the offset we just found and grow it - // to the maximum width. Retain y because this is our start - // height. - Rectangle grow = modelToView(t, posBelow); - grow.x = insets.left; - grow.width = t.getWidth() + insets.right; - - // Find further lines which have to be damaged completely. - int nextPosBelow = posBelow; - while (nextPosBelow != -1 - && posBelow != nextPosBelow - && Utilities.getRowStart(t, nextPosBelow) != p1RowStart) - { - posBelow = nextPosBelow; - nextPosBelow = Utilities.getPositionBelow(t, posBelow, - l1.x); - - if (posBelow == nextPosBelow) - break; - } - // Now posBelow is an offset on the last line which has to be - // damaged completely. (newPosBelow is on the same line as p1) - - // Retrieve the rectangle of posBelow and use its y and height - // value to calculate the final height of the multiple line - // spanning rectangle. - Rectangle end = modelToView(t, posBelow); - grow.height = end.y + end.height - grow.y; - - // Mark that area as damage. - t.repaint(grow); - } - - // Damage last line from its beginning to the position of p1. - l2.width += l2.x; - l2.x = insets.left; - t.repaint(l2); + // Release lock. + if (doc instanceof AbstractDocument) + ((AbstractDocument) doc).readUnlock(); } } - catch (BadLocationException ex) - { - AssertionError err = new AssertionError("Unexpected bad location"); - err.initCause(ex); - throw err; - } } /** @@ -1258,10 +1187,29 @@ public abstract class BasicTextUI extends TextUI public Rectangle modelToView(JTextComponent t, int pos, Position.Bias bias) throws BadLocationException { - Rectangle r = getVisibleEditorRect(); - - return (r != null) ? rootView.modelToView(pos, r, bias).getBounds() - : null; + // We need to read-lock here because we depend on the document + // structure not beeing changed in between. + Document doc = textComponent.getDocument(); + if (doc instanceof AbstractDocument) + ((AbstractDocument) doc).readLock(); + Rectangle rect = null; + try + { + Rectangle r = getVisibleEditorRect(); + if (r != null) + { + rootView.setSize(r.width, r.height); + Shape s = rootView.modelToView(pos, r, bias); + if (s != null) + rect = s.getBounds(); + } + } + finally + { + if (doc instanceof AbstractDocument) + ((AbstractDocument) doc).readUnlock(); + } + return rect; } /** diff --git a/javax/swing/text/BoxView.java b/javax/swing/text/BoxView.java index a184a8131..27e3c0f9a 100644 --- a/javax/swing/text/BoxView.java +++ b/javax/swing/text/BoxView.java @@ -66,6 +66,11 @@ public class BoxView */ private boolean[] layoutValid = new boolean[2]; + /** + * Indicates if the requirements for an axis are valid. + */ + private boolean[] requirementsValid = new boolean[2]; + /** * The spans along the X_AXIS and Y_AXIS. */ @@ -265,8 +270,10 @@ public class BoxView super.replace(offset, length, views); // Invalidate layout information. - layoutChanged(X_AXIS); - layoutChanged(Y_AXIS); + layoutValid[X_AXIS] = false; + requirementsValid[X_AXIS] = false; + layoutValid[Y_AXIS] = false; + requirementsValid[Y_AXIS] = false; } /** @@ -278,19 +285,26 @@ public class BoxView */ public void paint(Graphics g, Shape a) { - Rectangle inside = getInsideAllocation(a); - // TODO: Used for debugging. - //g.drawRect(inside.x, inside.y, inside.width, inside.height); + Rectangle alloc; + if (a instanceof Rectangle) + alloc = (Rectangle) a; + else + alloc = a.getBounds(); + + int x = alloc.x + getLeftInset(); + int y = alloc.y + getTopInset(); - Rectangle copy = new Rectangle(inside); + Rectangle clip = g.getClipBounds(); + Rectangle tmp = new Rectangle(); int count = getViewCount(); for (int i = 0; i < count; ++i) { - copy.setBounds(inside); - childAllocation(i, copy); - if (!copy.isEmpty() - && g.hitClip(copy.x, copy.y, copy.width, copy.height)) - paintChild(g, copy, 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); } } @@ -305,7 +319,13 @@ public class BoxView public float getPreferredSpan(int axis) { updateRequirements(axis); - return requirements[axis].preferred; + // Add margin. + float margin; + if (axis == X_AXIS) + margin = getLeftInset() + getRightInset(); + else + margin = getTopInset() + getBottomInset(); + return requirements[axis].preferred + margin; } /** @@ -319,12 +339,14 @@ public class BoxView */ public float getMaximumSpan(int axis) { - float max; - if (axis == myAxis) - max = getPreferredSpan(axis); + updateRequirements(axis); + // Add margin. + float margin; + if (axis == X_AXIS) + margin = getLeftInset() + getRightInset(); else - max = Integer.MAX_VALUE; - return max; + margin = getTopInset() + getBottomInset(); + return requirements[axis].maximum + margin; } /** @@ -341,7 +363,13 @@ public class BoxView public float getMinimumSpan(int axis) { updateRequirements(axis); - return requirements[axis].minimum; + // Add margin. + float margin; + if (axis == X_AXIS) + margin = getLeftInset() + getRightInset(); + else + margin = getTopInset() + getBottomInset(); + return requirements[axis].minimum + margin; } /** @@ -435,34 +463,29 @@ public class BoxView protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements sr) { - updateChildRequirements(axis); + SizeRequirements res = sr; + if (res == null) + res = new SizeRequirements(); - SizeRequirements result = sr; - if (result == null) - result = new SizeRequirements(); + float min = 0; + float pref = 0; + float max = 0; - long minimum = 0; - long preferred = 0; - long maximum = 0; - for (int i = 0; i < children.length; i++) + int n = getViewCount(); + for (int i = 0; i < n; i++) { - minimum += childReqs[axis][i].minimum; - preferred += childReqs[axis][i].preferred; - maximum += childReqs[axis][i].maximum; + View child = getView(i); + min += child.getMinimumSpan(axis); + pref = child.getPreferredSpan(axis); + max = child.getMaximumSpan(axis); } - // Overflow check. - if (minimum > Integer.MAX_VALUE) - minimum = Integer.MAX_VALUE; - if (preferred > Integer.MAX_VALUE) - preferred = Integer.MAX_VALUE; - if (maximum > Integer.MAX_VALUE) - maximum = Integer.MAX_VALUE; - - result.minimum = (int) minimum; - result.preferred = (int) preferred; - result.maximum = (int) maximum; - result.alignment = 0.5F; - return result; + + res.minimum = (int) min; + res.preferred = (int) pref; + res.maximum = (int) max; + res.alignment = 0.5F; + + return res; } /** @@ -480,44 +503,24 @@ public class BoxView protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements sr) { - updateChildRequirements(axis); - SizeRequirements res = sr; if (res == null) res = new SizeRequirements(); - float minLeft = 0; - float minRight = 0; - float prefLeft = 0; - float prefRight = 0; - float maxLeft = 0; - float maxRight = 0; - for (int i = 0; i < childReqs[axis].length; i++) + res.minimum = 0; + res.preferred = 0; + res.maximum = 0; + res.alignment = 0.5F; + int n = getViewCount(); + for (int i = 0; i < n; i++) { - float myMinLeft = childReqs[axis][i].minimum * childReqs[axis][i].alignment; - float myMinRight = childReqs[axis][i].minimum - myMinLeft; - minLeft = Math.max(myMinLeft, minLeft); - minRight = Math.max(myMinRight, minRight); - float myPrefLeft = childReqs[axis][i].preferred * childReqs[axis][i].alignment; - float myPrefRight = childReqs[axis][i].preferred - myPrefLeft; - prefLeft = Math.max(myPrefLeft, prefLeft); - prefRight = Math.max(myPrefRight, prefRight); - float myMaxLeft = childReqs[axis][i].maximum * childReqs[axis][i].alignment; - float myMaxRight = childReqs[axis][i].maximum - myMaxLeft; - maxLeft = Math.max(myMaxLeft, maxLeft); - maxRight = Math.max(myMaxRight, maxRight); + View child = getView(i); + res.minimum = Math.max((int) child.getMinimumSpan(axis), res.minimum); + res.preferred = Math.max((int) child.getPreferredSpan(axis), + res.preferred); + res.maximum = Math.max((int) child.getMaximumSpan(axis), res.maximum); } - int minSize = (int) (minLeft + minRight); - int prefSize = (int) (prefLeft + prefRight); - int maxSize = (int) (maxLeft + maxRight); - float align = prefLeft / (prefRight + prefLeft); - if (Float.isNaN(align)) - align = 0; - res.alignment = align; - res.maximum = maxSize; - res.preferred = prefSize; - res.minimum = minSize; return res; } @@ -697,15 +700,62 @@ public class BoxView protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) { - updateChildRequirements(axis); - updateRequirements(axis); + // Set the spans to the preferred sizes. Determine the space + // that we have to adjust the sizes afterwards. + long sumPref = 0; + int n = getViewCount(); + for (int i = 0; i < n; i++) + { + View child = getView(i); + spans[i] = (int) child.getPreferredSpan(axis); + sumPref = spans[i]; + } - // Calculate the spans and offsets using the SizeRequirements uility - // methods. - SizeRequirements.calculateTiledPositions(targetSpan, requirements[axis], - childReqs[axis], - offsets, spans); + // Try to adjust the spans so that we fill the targetSpan. + long diff = targetSpan - sumPref; + float factor = 0.0F; + int[] diffs = null; + if (diff != 0) + { + long total = 0; + diffs = new int[n]; + for (int i = 0; i < n; i++) + { + View child = getView(i); + int span; + if (diff < 0) + { + span = (int) child.getMinimumSpan(axis); + diffs[i] = spans[i] - span; + } + else + { + span = (int) child.getMaximumSpan(axis); + diffs[i] = span - spans[i]; + } + total += span; + } + float maxAdjust = Math.abs(total - sumPref); + factor = diff / maxAdjust; + factor = Math.min(factor, 1.0F); + factor = Math.max(factor, -1.0F); + } + + // Actually perform adjustments. + int totalOffs = 0; + for (int i = 0; i < n; i++) + { + offsets[i] = totalOffs; + if (diff != 0) + { + float adjust = factor * diffs[i]; + spans[i] += Math.round(adjust); + } + // Avoid overflow here. + totalOffs = (int) Math.min((long) totalOffs + (long) spans[i], + Integer.MAX_VALUE); + } } /** @@ -720,14 +770,26 @@ public class BoxView protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) { - updateChildRequirements(axis); - updateRequirements(axis); - - // Calculate the spans and offsets using the SizeRequirements uility - // methods. - SizeRequirements.calculateAlignedPositions(targetSpan, requirements[axis], - childReqs[axis], offsets, - spans); + int count = getViewCount(); + for (int i = 0; i < count; i++) + { + View child = getView(i); + int max = (int) child.getMaximumSpan(axis); + if (max < targetSpan) + {System.err.println("align: " + child); + // Align child when it can't be made as wide as the target span. + float align = child.getAlignment(axis); + offsets[i] = (int) ((targetSpan - max) * align); + spans[i] = max; + } + else + { + // Expand child to target width if possible. + int min = (int) child.getMinimumSpan(axis); + offsets[i] = 0; + spans[i] = Math.max(min, targetSpan); + } + } } /** @@ -822,15 +884,8 @@ public class BoxView */ public float getAlignment(int axis) { - float align; - if (axis == myAxis) - align = 0.5F; - else - { - updateRequirements(axis); - align = requirements[axis].alignment; - } - return align; + updateRequirements(axis); + return requirements[axis].alignment; } /** @@ -843,9 +898,15 @@ public class BoxView public void preferenceChanged(View child, boolean width, boolean height) { if (width) - layoutValid[X_AXIS] = false; + { + layoutValid[X_AXIS] = false; + requirementsValid[X_AXIS] = false; + } if (height) - layoutValid[Y_AXIS] = false; + { + layoutValid[Y_AXIS] = false; + requirementsValid[Y_AXIS] = false; + } super.preferenceChanged(child, width, height); } @@ -862,7 +923,7 @@ public class BoxView if (! isAllocationValid()) { Rectangle bounds = a.getBounds(); - layout(bounds.width, bounds.height); + setSize(bounds.width, bounds.height); } return super.modelToView(pos, a, bias); } @@ -963,7 +1024,7 @@ public class BoxView */ private void updateRequirements(int axis) { - if (! layoutValid[axis]) + if (! requirementsValid[axis]) { if (axis == myAxis) requirements[axis] = calculateMajorAxisRequirements(axis, @@ -971,6 +1032,7 @@ public class BoxView else requirements[axis] = calculateMinorAxisRequirements(axis, requirements[axis]); + requirementsValid[axis] = true; } } } diff --git a/javax/swing/text/CompositeView.java b/javax/swing/text/CompositeView.java index 17f13dbed..6f487b898 100644 --- a/javax/swing/text/CompositeView.java +++ b/javax/swing/text/CompositeView.java @@ -217,25 +217,43 @@ public abstract class CompositeView public Shape modelToView(int pos, Shape a, Position.Bias bias) throws BadLocationException { - int childIndex = getViewIndex(pos, bias); - if (childIndex == -1) - throw new BadLocationException("Position " + pos + " is not represented by view.", pos); - - Shape ret = null; - - View child = getView(childIndex); - Shape childAlloc = getChildAllocation(childIndex, a); - - if (childAlloc == null) - ret = createDefaultLocation(a, bias); - - Shape result = child.modelToView(pos, childAlloc, bias); - - if (result != null) - ret = result; - else - ret = createDefaultLocation(a, bias); + boolean backward = bias == Position.Bias.Backward; + int testpos = backward ? Math.max(0, pos - 1) : pos; + Shape ret = null; + if (! backward || testpos >= getStartOffset()) + { + int childIndex = getViewIndexAtPosition(testpos); + if (childIndex != -1 && childIndex < getViewCount()) + { + View child = getView(childIndex); + if (child != null && testpos >= child.getStartOffset() + && testpos < child.getEndOffset()) + { + Shape childAlloc = getChildAllocation(childIndex, a); + if (childAlloc != null) + { + ret = child.modelToView(pos, childAlloc, bias); + // Handle corner case. + if (ret == null && child.getEndOffset() == pos) + { + childIndex++; + if (childIndex < getViewCount()) + { + child = getView(childIndex); + childAlloc = getChildAllocation(childIndex, a); + ret = child.modelToView(pos, childAlloc, bias); + } + } + } + } + } + else + { + throw new BadLocationException("Position " + pos + + " is not represented by view.", pos); + } + } return ret; } @@ -378,7 +396,10 @@ public abstract class CompositeView { if (b == Position.Bias.Backward && pos != 0) pos -= 1; - return getViewIndexAtPosition(pos); + int i = -1; + if (pos >= getStartOffset() && pos < getEndOffset()) + i = getViewIndexAtPosition(pos); + return i; } /** @@ -446,9 +467,13 @@ public abstract class CompositeView */ protected View getViewAtPosition(int pos, Rectangle a) { + View view = null; int i = getViewIndexAtPosition(pos); - View view = children[i]; - childAllocation(i, a); + if (i >= 0 && i < getViewCount() && a != null) + { + view = getView(i); + childAllocation(i, a); + } return view; } @@ -464,17 +489,10 @@ public abstract class CompositeView */ protected int getViewIndexAtPosition(int pos) { - int index = -1; - for (int i = 0; i < children.length; i++) - { - if (children[i].getStartOffset() <= pos - && children[i].getEndOffset() > pos) - { - index = i; - break; - } - } - return index; + // We have a 1:1 mapping of elements to views here, so we forward + // this to the element. + Element el = getElement(); + return el.getElementIndex(pos); } /** diff --git a/javax/swing/text/DefaultCaret.java b/javax/swing/text/DefaultCaret.java index ffce9a1b7..8557e63c3 100644 --- a/javax/swing/text/DefaultCaret.java +++ b/javax/swing/text/DefaultCaret.java @@ -911,10 +911,10 @@ public class DefaultCaret extends Rectangle } catch (BadLocationException e) { - AssertionError ae; - ae = new AssertionError("Unexpected bad caret location: " + dot); - ae.initCause(e); - throw ae; + // Let's ignore that. This shouldn't really occur. But if it + // does (it seems that this happens when the model is mutating), + // it causes no real damage. Uncomment this for debugging. + // e.printStackTrace(); } if (rect == null) @@ -1148,10 +1148,10 @@ public class DefaultCaret extends Rectangle } catch (BadLocationException e) { - AssertionError ae; - ae = new AssertionError("Unexpected bad caret location: " + dot); - ae.initCause(e); - throw ae; + // Let's ignore that. This shouldn't really occur. But if it + // does (it seems that this happens when the model is mutating), + // it causes no real damage. Uncomment this for debugging. + // e.printStackTrace(); } if (area != null) damage(area); diff --git a/javax/swing/text/FlowView.java b/javax/swing/text/FlowView.java index 89fcc6fcd..3de95ed7f 100644 --- a/javax/swing/text/FlowView.java +++ b/javax/swing/text/FlowView.java @@ -524,6 +524,7 @@ public abstract class FlowView extends BoxView */ public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory vf) { + layoutPool.removeUpdate(changes, a, vf); strategy.removeUpdate(this, changes, getInsideAllocation(a)); layoutDirty = true; } @@ -539,6 +540,7 @@ public abstract class FlowView extends BoxView */ public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory vf) { + layoutPool.changedUpdate(changes, a, vf); strategy.changedUpdate(this, changes, getInsideAllocation(a)); layoutDirty = true; } @@ -597,12 +599,14 @@ public abstract class FlowView extends BoxView 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); + SizeRequirements res = r; + if (res == null) + res = new SizeRequirements(); res.minimum = (int) layoutPool.getMinimumSpan(axis); - res.preferred = (int) layoutPool.getPreferredSpan(axis); - res.maximum = (int) layoutPool.getMaximumSpan(axis); + res.preferred = Math.max(res.minimum, + (int) layoutPool.getMinimumSpan(axis)); + res.maximum = Integer.MAX_VALUE; + res.alignment = 0.5F; return res; } } diff --git a/javax/swing/text/IconView.java b/javax/swing/text/IconView.java index 699cda90e..7bb7635b4 100644 --- a/javax/swing/text/IconView.java +++ b/javax/swing/text/IconView.java @@ -44,7 +44,6 @@ import java.awt.Shape; import javax.swing.Icon; import javax.swing.JTextPane; -import javax.swing.SwingConstants; /** * A View that can render an icon. This view is created by the @@ -156,4 +155,21 @@ public class IconView return el.getStartOffset(); } + /** + * Returns the alignment for this view. This will be 1.0 for the Y_AXIS, + * and the super behaviour for the X_AXIS. + * + * @param axis the axis for which to calculate the alignment + * + * @return the alignment + */ + public float getAlignment(int axis) + { + float align; + if (axis == Y_AXIS) + align = 1.0F; + else + align = super.getAlignment(axis); + return align; + } } diff --git a/javax/swing/text/ParagraphView.java b/javax/swing/text/ParagraphView.java index 15bed7818..c4857863d 100644 --- a/javax/swing/text/ParagraphView.java +++ b/javax/swing/text/ParagraphView.java @@ -74,6 +74,39 @@ public class ParagraphView extends FlowView implements TabExpander return align; } + /** + * Allows rows to span the whole parent view. + */ + public float getMaximumSpan(int axis) + { + float max; + if (axis == X_AXIS) + max = Float.MAX_VALUE; + else + max = super.getMaximumSpan(axis); + return max; + } + + /** + * Overridden because child views are not necessarily laid out in model + * order. + */ + protected int getViewIndexAtPosition(int pos) + { + int index = -1; + if (pos >= getStartOffset() && pos < getEndOffset()) + { + int nviews = getViewCount(); + for (int i = 0; i < nviews && index == -1; i++) + { + View child = getView(i); + if (pos >= child.getStartOffset() && pos < child.getEndOffset()) + index = i; + } + } + return index; + } + protected void loadChildren(ViewFactory vf) { // Do nothing here. The children are added while layouting. @@ -140,7 +173,7 @@ public class ParagraphView extends FlowView implements TabExpander { float align; if (axis == X_AXIS) - align = super.getAlignment(axis); + align = 0.5F; else if (getViewCount() > 0) { float prefHeight = getPreferredSpan(Y_AXIS); @@ -148,7 +181,7 @@ public class ParagraphView extends FlowView implements TabExpander align = (firstRowHeight / 2.F) / prefHeight; } else - align = 0.0F; + align = 0.5F; return align; } diff --git a/javax/swing/text/View.java b/javax/swing/text/View.java index d8ad5f585..55a63f6b6 100644 --- a/javax/swing/text/View.java +++ b/javax/swing/text/View.java @@ -401,7 +401,10 @@ public abstract class View implements SwingConstants Element el = getElement(); DocumentEvent.ElementChange ec = ev.getChange(el); if (ec != null) - updateChildren(ec, ev, vf); + { + if (! updateChildren(ec, ev, vf)) + ec = null; + } forwardUpdate(ec, ev, shape, vf); updateLayout(ec, ev, shape); } @@ -493,27 +496,66 @@ public abstract class View implements SwingConstants int count = getViewCount(); if (count > 0) { + // Determine start index. int startOffset = ev.getOffset(); - int endOffset = startOffset + ev.getLength(); int startIndex = getViewIndex(startOffset, Position.Bias.Backward); - int endIndex = getViewIndex(endOffset, Position.Bias.Forward); - int index = -1; - int addLength = -1; - if (ec != null) + + // For REMOVE events we have to forward the event to the last element, + // for the case that an Element has been removed that represente + // the offset. + if (startIndex == -1 && ev.getType() == DocumentEvent.EventType.REMOVE + && startOffset >= getEndOffset()) { - index = ec.getIndex(); - addLength = ec.getChildrenAdded().length; + startIndex = getViewCount() - 1; } - if (startIndex >= 0 && endIndex >= 0) + // When startIndex is on a view boundary, forward event to the + // previous view too. + if (startIndex >= 0) { - for (int i = startIndex; i <= endIndex; i++) + View v = getView(startIndex); + if (v != null) + { + if (v.getStartOffset() == startOffset && startOffset > 0) + startIndex = Math.max(0, startIndex - 1); + } + } + startIndex = Math.max(0, startIndex); + + // Determine end index. + int endIndex = startIndex; + if (ev.getType() != DocumentEvent.EventType.REMOVE) + { + endIndex = getViewIndex(startOffset + ev.getLength(), + Position.Bias.Forward); + if (endIndex < 0) + endIndex = getViewCount() - 1; + } + + // Determine hole that comes from added elements (we don't forward + // the event to newly added views. + int startAdded = endIndex + 1; + int endAdded = startAdded; + Element[] added = (ec != null) ? ec.getChildrenAdded() : null; + if (added != null && added.length > 0) + { + startAdded = ec.getIndex(); + endAdded = startAdded + added.length - 1; + } + + // Forward event to all views between startIndex and endIndex, + // and leave out all views in the hole. + for (int i = startIndex; i <= endIndex; i++) + { + // Skip newly added child views. + if (! (i >= startAdded && i <= endAdded)) { - // Skip newly added child views. - if (index >= 0 && i >= index && i < (index+addLength)) - continue; View child = getView(i); - forwardUpdateToView(child, ev, shape, vf); + if (child != null) + { + Shape childAlloc = getChildAllocation(i, shape); + forwardUpdateToView(child, ev, childAlloc, vf); + } } } } @@ -611,9 +653,46 @@ public abstract class View implements SwingConstants if (b2 != Position.Bias.Forward && b2 != Position.Bias.Backward) throw new IllegalArgumentException ("b2 must be either Position.Bias.Forward or Position.Bias.Backward"); - Rectangle s1 = (Rectangle) modelToView(p1, a, b1); - Rectangle s2 = (Rectangle) modelToView(p2, a, b2); - return SwingUtilities.computeUnion(s1.x, s1.y, s1.width, s1.height, s2); + + Shape s1 = modelToView(p1, a, b1); + // Special case for p2 == end index. + Shape s2; + if (p2 != getEndOffset()) + { + s2 = modelToView(p2, a, b2); + } + else + { + try + { + s2 = modelToView(p2, a, b2); + } + catch (BadLocationException ex) + { + // Assume the end rectangle to be at the right edge of the + // view. + Rectangle aRect = a instanceof Rectangle ? (Rectangle) a + : a.getBounds(); + s2 = new Rectangle(aRect.x + aRect.width - 1, aRect.y, 1, + aRect.height); + } + } + + // Need to modify the rectangle, so we create a copy in all cases. + Rectangle r1 = s1.getBounds(); + Rectangle r2 = s2 instanceof Rectangle ? (Rectangle) s2 + : s2.getBounds(); + + // For multiline view, let the resulting rectangle span the whole view. + if (r1.y != r2.y) + { + Rectangle aRect = a instanceof Rectangle ? (Rectangle) a + : a.getBounds(); + r1.x = aRect.x; + r1.width = aRect.width; + } + + return SwingUtilities.computeUnion(r2.x, r2.y, r2.width, r2.height, r1); } /** -- cgit v1.2.1