### Eclipse Workspace Patch 1.0 #P org.eclipse.draw2d Index: src/org/eclipse/draw2d/text/FlowUtilities.java =================================================================== RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.draw2d/src/org/eclipse/draw2d/text/FlowUtilities.java,v retrieving revision 1.56 diff -u -r1.56 FlowUtilities.java --- src/org/eclipse/draw2d/text/FlowUtilities.java 4 Apr 2006 12:43:12 -0000 1.56 +++ src/org/eclipse/draw2d/text/FlowUtilities.java 19 Sep 2007 15:03:44 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2005 IBM Corporation and others. + * Copyright (c) 2000, 2007 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -15,24 +15,30 @@ import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.graphics.TextLayout; import org.eclipse.swt.widgets.Display; import org.eclipse.draw2d.FigureUtilities; +import org.eclipse.draw2d.TextUtilities; /** * Utility class for FlowFigures. * @author hudsonr * @since 2.1 */ -class FlowUtilities - extends FigureUtilities +public class FlowUtilities { interface LookAhead { int getWidth(); } -private static int ELLIPSIS_SIZE; + +/** + * a singleton default instance + */ +public static FlowUtilities INSTANCE = new FlowUtilities(); + private static final BreakIterator INTERNAL_LINE_BREAK = BreakIterator.getLineInstance(); private static TextLayout layout; @@ -61,10 +67,21 @@ return Math.min(macNL, unixNL); } -private static float getAverageCharWidth(TextFragmentBox fragment, Font font) { - if (fragment.getWidth() > 0 && fragment.length != 0) - return fragment.getWidth() / (float)fragment.length; - return getFontMetrics(font).getAverageCharWidth(); +/** + * Gets the average character width. + * + * @param fragment the supplied TextFragmentBox to use for calculation. + * if the length is 0 or if the width is or below 0, + * the average character width is taken from standard + * font metrics. + * @param font the font to use in case the TextFragmentBox conditions + * above are true. + * @return the average character width + */ +protected float getAverageCharWidth(TextFragmentBox fragment, Font font) { + if (fragment.getWidth() > 0 && fragment.length != 0) + return fragment.getWidth() / (float)fragment.length; + return FigureUtilities.getFontMetrics(font).getAverageCharWidth(); } static int getBorderAscent(InlineFlow owner) { @@ -129,34 +146,28 @@ } } -private static int measureString(TextFragmentBox frag, String string, int guess, Font font) { - if (frag.requiresBidi()) { - // The text and/or could have changed if the lookAhead was invoked. This will - // happen at most once. - TextLayout layout = getTextLayout(); - layout.setText(string); - layout.setFont(font); - return layout.getBounds(0, guess - 1).width; - } else - return getStringDimension(string.substring(0, guess), font).x; -} - -static void setupFragment(TextFragmentBox frag, Font f, String s) { - if (frag.getWidth() == -1 || frag.isTruncated()) { - int width; - if (s.length() == 0 || frag.length == 0) - width = 0; - else if (frag.requiresBidi()) { - TextLayout textLayout = getTextLayout(); - textLayout.setFont(f); - textLayout.setText(s); - width = textLayout.getBounds(0, frag.length - 1).width; - } else - width = getStringDimension(s.substring(0, frag.length), f).x; - if (frag.isTruncated()) - width += ELLIPSIS_SIZE; - frag.setWidth(width); - } +private int measureString(TextFragmentBox frag, String string, int guess, Font font) { + if (frag.requiresBidi()) { + // The text and/or could have changed if the lookAhead was invoked. This will + // happen at most once. + return getTextLayoutBounds(string, font, 0, guess - 1).width; + } else + return getTextUtilities().getStringExtents(string.substring(0, guess), font).width; +} + +protected void setupFragment(TextFragmentBox frag, Font f, String s) { + if (frag.getWidth() == -1 || frag.isTruncated()) { + int width; + if (s.length() == 0 || frag.length == 0) + width = 0; + else if (frag.requiresBidi()) { + width = getTextLayoutBounds(s, f, 0, frag.length - 1).width; + } else + width = getTextUtilities().getStringExtents(s.substring(0, frag.length), f).width; + if (frag.isTruncated()) + width += getEllipsisWidth(f); + frag.setWidth(width); + } } /** @@ -172,7 +183,7 @@ * @return the number of characters that will fit in the given space; can be 0 (eg., when * the first character of the given string is a newline) */ -public static int wrapFragmentInContext(TextFragmentBox frag, String string, +protected int wrapFragmentInContext(TextFragmentBox frag, String string, FlowContext context, LookAhead lookahead, Font font, int wrapping) { frag.setTruncated(false); int strLen = string.length(); @@ -245,61 +256,59 @@ continue; } - if (guessSize <= availableWidth) { - min = guess; - frag.setWidth(guessSize); - if (guessSize == availableWidth) - max = guess + 1; - } else - max = guess; - } - - int result = min; - boolean continueOnLine = false; - if (min == strLen) { - //Everything fits - if (string.charAt(strLen - 1) == ' ') { - if (frag.getWidth() == -1) { - frag.length = result; - frag.setWidth(measureString(frag, string, result, font)); - } - if (lookahead.getWidth() > availableWidth - frag.getWidth()) { - frag.length = result - 1; - frag.setWidth(-1); - } else - frag.length = result; - } else { - continueOnLine = !canBreakAfter(string.charAt(strLen - 1)); - frag.length = result; - } - } else if (min == firstDelimiter) { - //move result past the delimiter - frag.length = result; - if (string.charAt(min) == '\r') { - result++; - if (++min < strLen && string.charAt(min) == '\n') - result++; - } else if (string.charAt(min) == '\n') - result++; - } else if (string.charAt(min) == ' ' - || canBreakAfter(string.charAt(min - 1)) - || INTERNAL_LINE_BREAK.isBoundary(min)) { - frag.length = min; - if (string.charAt(min) == ' ') - result++; - else if (string.charAt(min - 1) == ' ') { - frag.length--; - frag.setWidth(-1); - } - } else out: { - // In the middle of an unbreakable offset - result = INTERNAL_LINE_BREAK.preceding(min); - if (result == 0) { - switch (wrapping) { - case ParagraphTextLayout.WORD_WRAP_TRUNCATE : - ELLIPSIS_SIZE = FigureUtilities - .getStringExtents(TextFlow.ELLIPSIS, font).width; - int truncatedWidth = availableWidth - ELLIPSIS_SIZE; + if (guessSize <= availableWidth) { + min = guess; + frag.setWidth(guessSize); + if (guessSize == availableWidth) + max = guess + 1; + } else + max = guess; + } + + int result = min; + boolean continueOnLine = false; + if (min == strLen) { + //Everything fits + if (string.charAt(strLen - 1) == ' ') { + if (frag.getWidth() == -1) { + frag.length = result; + frag.setWidth(measureString(frag, string, result, font)); + } + if (lookahead.getWidth() > availableWidth - frag.getWidth()) { + frag.length = result - 1; + frag.setWidth(-1); + } else + frag.length = result; + } else { + continueOnLine = !canBreakAfter(string.charAt(strLen - 1)); + frag.length = result; + } + } else if (min == firstDelimiter) { + //move result past the delimiter + frag.length = result; + if (string.charAt(min) == '\r') { + result++; + if (++min < strLen && string.charAt(min) == '\n') + result++; + } else if (string.charAt(min) == '\n') + result++; + } else if (string.charAt(min) == ' ' + || canBreakAfter(string.charAt(min - 1)) + || INTERNAL_LINE_BREAK.isBoundary(min)) { + frag.length = min; + if (string.charAt(min) == ' ') + result++; + else if (string.charAt(min - 1) == ' ') { + frag.length--; + frag.setWidth(-1); + } + } else out: { + // In the middle of an unbreakable offset + result = INTERNAL_LINE_BREAK.preceding(min); + if (result == 0) { + switch (wrapping) { + case ParagraphTextLayout.WORD_WRAP_TRUNCATE : + int truncatedWidth = availableWidth - getEllipsisWidth(font); if (truncatedWidth > 0) { //$TODO this is very slow. It should be using avgCharWidth to go faster while (min > 0) { @@ -312,24 +321,56 @@ } else frag.length = 0; frag.setTruncated(true); - result = INTERNAL_LINE_BREAK.following(max - 1); - break out; + result = INTERNAL_LINE_BREAK.following(max - 1); + break out; - default: - result = min; - break; - } - } - frag.length = result; - if (string.charAt(result - 1) == ' ') - frag.length--; - frag.setWidth(-1); - } - - setupFragment(frag, font, string); - context.addToCurrentLine(frag); - context.setContinueOnSameLine(continueOnLine); - return result; + default: + result = min; + break; + } + } + frag.length = result; + if (string.charAt(result - 1) == ' ') + frag.length--; + frag.setWidth(-1); + } + + setupFragment(frag, font, string); + context.addToCurrentLine(frag); + context.setContinueOnSameLine(continueOnLine); + return result; } +/** + * @see TextLayout#getBounds() + */ +protected Rectangle getTextLayoutBounds(String s, Font f, int start, int end) { + TextLayout textLayout = getTextLayout(); + textLayout.setFont(f); + textLayout.setText(s); + return textLayout.getBounds(start, end); +} + +/** + * Returns an instance of a TextUtililities class on which + * text calculations can be performed. Clients may override to customize. + * + * @return the TextUtililities instance + * @since 3.4 + */ +protected TextUtilities getTextUtilities() { + return TextUtilities.INSTANCE; +} + +/** + * Gets the ellipsis width. + * + * @param font + * the font to be used in the calculation + * @return the width of the ellipsis + * @since 3.4 + */ +private int getEllipsisWidth(Font font) { + return getTextUtilities().getStringExtents(TextFlow.ELLIPSIS, font).width; +} } Index: src/org/eclipse/draw2d/text/TextFlow.java =================================================================== RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.draw2d/src/org/eclipse/draw2d/text/TextFlow.java,v retrieving revision 1.55 diff -u -r1.55 TextFlow.java --- src/org/eclipse/draw2d/text/TextFlow.java 14 Feb 2006 20:13:32 -0000 1.55 +++ src/org/eclipse/draw2d/text/TextFlow.java 19 Sep 2007 15:03:44 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2005 IBM Corporation and others. + * Copyright (c) 2000, 2007 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -12,12 +12,11 @@ import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.graphics.FontMetrics; import org.eclipse.swt.graphics.TextLayout; import org.eclipse.draw2d.ColorConstants; -import org.eclipse.draw2d.FigureUtilities; import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.TextUtilities; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; @@ -95,7 +94,7 @@ text = text.substring(1, index + 1); if (bidiInfo == null) - width[0] += FlowUtilities.getStringExtents(text, getFont()).width; + width[0] += getTextUtilities().getStringExtents(text, getFont()).width; else { TextLayout textLayout = FlowUtilities.getTextLayout(); textLayout.setFont(getFont()); @@ -175,8 +174,7 @@ } int getAscent() { - FontMetrics fm = FigureUtilities.getFontMetrics(getFont()); - return fm.getHeight() - fm.getDescent(); + return getTextUtilities().getAscent(getFont()); } /** @@ -267,7 +265,7 @@ if (trailing && offset < box.length) offset++; String substring = getText().substring(box.offset, box.offset + offset); - result.x = FigureUtilities.getStringExtents(substring, getFont()).width; + result.x = getTextUtilities().getStringExtents(substring, getFont()).width; } else { TextLayout layout = FlowUtilities.getTextLayout(); layout.setFont(getFont()); @@ -283,7 +281,7 @@ } int getDescent() { - return FigureUtilities.getFontMetrics(getFont()).getDescent(); + return getTextUtilities().getDescent(getFont()); } /** @@ -485,7 +483,7 @@ g.getClip(Rectangle.SINGLETON); int yStart = Rectangle.SINGLETON.y; int yEnd = Rectangle.SINGLETON.bottom(); - + for (int i = 0; i < fragments.size(); i++) { frag = (TextFragmentBox)fragments.get(i); // g.drawLine(frag.getX(), frag.getLineRoot().getVisibleTop(), @@ -499,7 +497,7 @@ //Break loop at first non-visible fragment if (yEnd < frag.getLineRoot().getVisibleTop()) break; - + String draw = getBidiSubstring(frag, i); if (frag.isTruncated()) @@ -629,4 +627,25 @@ return Math.max(0, y - (box.getBaseline() + box.getLineRoot().getDescent())); } +/** + * Gets the FlowUtilities instance to be used in measurement + * calculations. + * + * @return a FlowUtilities instance + * @since 3.2 + */ +protected FlowUtilities getFlowUtilities() { + return FlowUtilities.INSTANCE; +} + +/** + * Gets the TextUtilities instance to be used in measurement + * calculations. + * + * @return a TextUtilities instance + * @since 3.2 + */ +protected TextUtilities getTextUtilities() { + return TextUtilities.INSTANCE; +} } \ No newline at end of file Index: src/org/eclipse/draw2d/text/SimpleTextLayout.java =================================================================== RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.draw2d/src/org/eclipse/draw2d/text/SimpleTextLayout.java,v retrieving revision 1.10 diff -u -r1.10 SimpleTextLayout.java --- src/org/eclipse/draw2d/text/SimpleTextLayout.java 7 Sep 2005 21:34:54 -0000 1.10 +++ src/org/eclipse/draw2d/text/SimpleTextLayout.java 19 Sep 2007 15:03:44 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2005 IBM Corporation and others. + * Copyright (c) 2000, 2007 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -21,9 +21,9 @@ public class SimpleTextLayout extends TextLayout { private static final String[] DELIMITERS = { - "\r\n", //$NON-NLS-1$ - "\n", //$NON-NLS-1$ - "\r"};//$NON-NLS-1$ + "\r\n", //$NON-NLS-1$ + "\n", //$NON-NLS-1$ + "\r"};//$NON-NLS-1$ private static int result; private static int delimeterLength; @@ -33,49 +33,50 @@ * @param flow the TextFlow */ public SimpleTextLayout(TextFlow flow) { - super (flow); + super (flow); } /** * @see org.eclipse.draw2d.text.FlowFigureLayout#layout() */ protected void layout() { - TextFlow textFlow = (TextFlow)getFlowFigure(); - String text = textFlow.getText(); - List fragments = textFlow.getFragments(); - Font font = textFlow.getFont(); - TextFragmentBox fragment; - int i = 0; - int offset = 0; - - do { - nextLineBreak(text, offset); - fragment = getFragment(i++, fragments); - fragment.length = result - offset; - fragment.offset = offset; - fragment.setWidth(-1); - FlowUtilities.setupFragment(fragment, font, text); - getContext().addToCurrentLine(fragment); - getContext().endLine(); - offset = result + delimeterLength; - } while (offset < text.length()); - //Remove the remaining unused fragments. - while (i < fragments.size()) - fragments.remove(i++); + TextFlow textFlow = (TextFlow)getFlowFigure(); + String text = textFlow.getText(); + List fragments = textFlow.getFragments(); + Font font = textFlow.getFont(); + TextFragmentBox fragment; + int i = 0; + int offset = 0; + FlowUtilities flowUtilities = textFlow.getFlowUtilities(); + + do { + nextLineBreak(text, offset); + fragment = getFragment(i++, fragments); + fragment.length = result - offset; + fragment.offset = offset; + fragment.setWidth(-1); + flowUtilities.setupFragment(fragment, font, text); + getContext().addToCurrentLine(fragment); + getContext().endLine(); + offset = result + delimeterLength; + } while (offset < text.length()); + //Remove the remaining unused fragments. + while (i < fragments.size()) + fragments.remove(i++); } private int nextLineBreak(String text, int offset) { - result = text.length(); - delimeterLength = 0; - int current; - for (int i = 0; i < DELIMITERS.length; i++) { - current = text.indexOf(DELIMITERS[i], offset); - if (current != -1 && current < result) { - result = current; - delimeterLength = DELIMITERS[i].length(); - } - } - return result; + result = text.length(); + delimeterLength = 0; + int current; + for (int i = 0; i < DELIMITERS.length; i++) { + current = text.indexOf(DELIMITERS[i], offset); + if (current != -1 && current < result) { + result = current; + delimeterLength = DELIMITERS[i].length(); + } + } + return result; } } Index: src/org/eclipse/draw2d/text/ParagraphTextLayout.java =================================================================== RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.draw2d/src/org/eclipse/draw2d/text/ParagraphTextLayout.java,v retrieving revision 1.31 diff -u -r1.31 ParagraphTextLayout.java --- src/org/eclipse/draw2d/text/ParagraphTextLayout.java 1 Jun 2005 19:56:30 -0000 1.31 +++ src/org/eclipse/draw2d/text/ParagraphTextLayout.java 19 Sep 2007 15:03:44 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2005 IBM Corporation and others. + * Copyright (c) 2000, 2007 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -20,7 +20,7 @@ * @since 2.1 */ public class ParagraphTextLayout - extends TextLayout + extends TextLayout { /** @@ -48,23 +48,23 @@ * @param flow the TextFlow */ public ParagraphTextLayout(TextFlow flow) { - super(flow); + super(flow); } /** * Constructs the layout with the specified TextFlow and wrapping style. The wrapping * style must be one of: * * @param flow the textflow * @param style the style of wrapping */ public ParagraphTextLayout(TextFlow flow, int style) { - this(flow); - wrappingStyle = style; + this(flow); + wrappingStyle = style; } /** @@ -75,133 +75,134 @@ * @return the requested segment */ private String[] getSegments(String text, int levelInfo[]) { - if (levelInfo.length == 1) - return new String[] {text}; - - String result[] = new String[levelInfo.length / 2 + 1]; - - int i; - int endOffset; - int beginOffset = 0; - - for (i = 0; i < result.length - 1; i++) { - endOffset = levelInfo[i * 2 + 1]; - result[i] = text.substring(beginOffset, endOffset); - beginOffset = endOffset; - } - endOffset = text.length(); - result[i] = text.substring(beginOffset, endOffset); - return result; + if (levelInfo.length == 1) + return new String[] {text}; + + String result[] = new String[levelInfo.length / 2 + 1]; + + int i; + int endOffset; + int beginOffset = 0; + + for (i = 0; i < result.length - 1; i++) { + endOffset = levelInfo[i * 2 + 1]; + result[i] = text.substring(beginOffset, endOffset); + beginOffset = endOffset; + } + endOffset = text.length(); + result[i] = text.substring(beginOffset, endOffset); + return result; } class SegmentLookahead implements FlowUtilities.LookAhead { - private int seg = -1; - private String segs[]; - private int[] width; - private final int trailingBorderSize; - SegmentLookahead(String segs[], int trailingBorderSize) { - this.segs = segs; - this.trailingBorderSize = trailingBorderSize; - } - public int getWidth() { - if (width == null) { - width = new int[1]; - int startingIndex = seg + 1; - TextFlow textFlow = (TextFlow)getFlowFigure(); - - if (startingIndex == segs.length) { - width[0] += trailingBorderSize; - getContext().getWidthLookahead(textFlow, width); - } else { - String rest = segs[startingIndex]; - for (int k = startingIndex + 1; k < segs.length; k++) - rest += segs[k]; - if (!textFlow.addLeadingWordWidth(rest, width)) { - width[0] += trailingBorderSize; - getContext().getWidthLookahead(textFlow, width); - } - } - } - return width[0]; - } - public void setIndex(int value) { - this.seg = value; - width = null; - } + private int seg = -1; + private String segs[]; + private int[] width; + private final int trailingBorderSize; + SegmentLookahead(String segs[], int trailingBorderSize) { + this.segs = segs; + this.trailingBorderSize = trailingBorderSize; + } + public int getWidth() { + if (width == null) { + width = new int[1]; + int startingIndex = seg + 1; + TextFlow textFlow = (TextFlow)getFlowFigure(); + + if (startingIndex == segs.length) { + width[0] += trailingBorderSize; + getContext().getWidthLookahead(textFlow, width); + } else { + String rest = segs[startingIndex]; + for (int k = startingIndex + 1; k < segs.length; k++) + rest += segs[k]; + if (!textFlow.addLeadingWordWidth(rest, width)) { + width[0] += trailingBorderSize; + getContext().getWidthLookahead(textFlow, width); + } + } + } + return width[0]; + } + public void setIndex(int value) { + this.seg = value; + width = null; + } } /** * @see org.eclipse.draw2d.text.FlowFigureLayout#layout() */ protected void layout() { - TextFlow textFlow = (TextFlow)getFlowFigure(); - int offset = 0; - - FlowContext context = getContext(); - List fragments = textFlow.getFragments(); - Font font = textFlow.getFont(); - int fragIndex = 0; - int advance = 0; - - TextFragmentBox fragment; - int levelInfo[] = (textFlow.getBidiInfo() == null) - ? new int[] {-1} - : textFlow.getBidiInfo().levelInfo; - - String segment, segments[] = getSegments(textFlow.getText(), levelInfo); - FlowBorder border = null; - if (textFlow.getBorder() instanceof FlowBorder) - border = (FlowBorder)textFlow.getBorder(); - - SegmentLookahead lookahead = new SegmentLookahead(segments, border == null ? 0 : border.getRightMargin()); - int seg; - - if (border != null) { - fragment = getFragment(fragIndex++, fragments); - fragment.setBidiLevel(levelInfo[0]); - fragment.setTruncated(false); - fragment.offset = fragment.length = -1; - fragment.setWidth(border.getLeftMargin() + border.getInsets(textFlow).left); - if (context.getRemainingLineWidth() - < fragment.getWidth() + lookahead.getWidth()) - context.endLine(); - context.addToCurrentLine(fragment); - } - - for (seg = 0; seg < segments.length; seg++) { - segment = segments[seg]; - lookahead.setIndex(seg); - - do { - fragment = getFragment(fragIndex++, fragments); - - fragment.offset = offset; - fragment.setBidiLevel(levelInfo[seg * 2]); - - advance = FlowUtilities.wrapFragmentInContext(fragment, segment, - context, lookahead, font, wrappingStyle); - segment = segment.substring(advance); - offset += advance; - if ((segment.length() > 0 - || fragment.length < advance) - || fragment.isTruncated()) - context.endLine(); - } while (segment.length() > 0 - || (!fragment.isTruncated() && fragment.length < advance)); - } - - if (border != null) { - fragment = getFragment(fragIndex++, fragments); - fragment.setBidiLevel(levelInfo[0]); - fragment.setTruncated(false); - fragment.offset = fragment.length = -1; - fragment.setWidth(border.getRightMargin() + border.getInsets(textFlow).right); - context.addToCurrentLine(fragment); - } - - //Remove the remaining unused fragments. - while (fragIndex < fragments.size()) - fragments.remove(fragments.size() - 1); + TextFlow textFlow = (TextFlow)getFlowFigure(); + int offset = 0; + + FlowContext context = getContext(); + List fragments = textFlow.getFragments(); + Font font = textFlow.getFont(); + int fragIndex = 0; + int advance = 0; + + TextFragmentBox fragment; + int levelInfo[] = (textFlow.getBidiInfo() == null) + ? new int[] {-1} + : textFlow.getBidiInfo().levelInfo; + + String segment, segments[] = getSegments(textFlow.getText(), levelInfo); + FlowBorder border = null; + if (textFlow.getBorder() instanceof FlowBorder) + border = (FlowBorder)textFlow.getBorder(); + + SegmentLookahead lookahead = new SegmentLookahead(segments, border == null ? 0 : border.getRightMargin()); + int seg; + + if (border != null) { + fragment = getFragment(fragIndex++, fragments); + fragment.setBidiLevel(levelInfo[0]); + fragment.setTruncated(false); + fragment.offset = fragment.length = -1; + fragment.setWidth(border.getLeftMargin() + border.getInsets(textFlow).left); + if (context.getRemainingLineWidth() + < fragment.getWidth() + lookahead.getWidth()) + context.endLine(); + context.addToCurrentLine(fragment); + } + + FlowUtilities flowUtilities = textFlow.getFlowUtilities(); + for (seg = 0; seg < segments.length; seg++) { + segment = segments[seg]; + lookahead.setIndex(seg); + + do { + fragment = getFragment(fragIndex++, fragments); + + fragment.offset = offset; + fragment.setBidiLevel(levelInfo[seg * 2]); + + advance = flowUtilities.wrapFragmentInContext(fragment, segment, + context, lookahead, font, wrappingStyle); + segment = segment.substring(advance); + offset += advance; + if ((segment.length() > 0 + || fragment.length < advance) + || fragment.isTruncated()) + context.endLine(); + } while (segment.length() > 0 + || (!fragment.isTruncated() && fragment.length < advance)); + } + + if (border != null) { + fragment = getFragment(fragIndex++, fragments); + fragment.setBidiLevel(levelInfo[0]); + fragment.setTruncated(false); + fragment.offset = fragment.length = -1; + fragment.setWidth(border.getRightMargin() + border.getInsets(textFlow).right); + context.addToCurrentLine(fragment); + } + + //Remove the remaining unused fragments. + while (fragIndex < fragments.size()) + fragments.remove(fragments.size() - 1); } } Index: src/org/eclipse/draw2d/Label.java =================================================================== RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.draw2d/src/org/eclipse/draw2d/Label.java,v retrieving revision 1.20 diff -u -r1.20 Label.java --- src/org/eclipse/draw2d/Label.java 22 Mar 2007 14:00:25 -0000 1.20 +++ src/org/eclipse/draw2d/Label.java 19 Sep 2007 15:03:44 -0000 @@ -115,12 +115,12 @@ case EAST: case WEST: alignOnHeight(textLocation, getTextSize(), textAlignment); - alignOnHeight(iconLocation, iconSize, iconAlignment); + alignOnHeight(iconLocation, getIconSize(), iconAlignment); break; case NORTH: case SOUTH: alignOnWidth(textLocation, getSubStringTextSize(), textAlignment); - alignOnWidth(iconLocation, iconSize, iconAlignment); + alignOnWidth(iconLocation, getIconSize(), iconAlignment); break; } } @@ -134,16 +134,16 @@ * @since 2.0 */ protected Dimension calculateLabelSize(Dimension txtSize) { - int gap = iconTextGap; + int gap = getIconTextGap(); if (getIcon() == null || getText().equals("")) //$NON-NLS-1$ gap = 0; Dimension d = new Dimension(0, 0); if (textPlacement == WEST || textPlacement == EAST) { - d.width = iconSize.width + gap + txtSize.width; - d.height = Math.max(iconSize.height, txtSize.height); + d.width = getIconSize().width + gap + txtSize.width; + d.height = Math.max(getIconSize().height, txtSize.height); } else { - d.width = Math.max(iconSize.width, txtSize.width); - d.height = iconSize.height + gap + txtSize.height; + d.width = Math.max(getIconSize().width, txtSize.width); + d.height = getIconSize().height + gap + txtSize.height; } return d; } @@ -195,7 +195,7 @@ } private void calculatePlacement() { - int gap = iconTextGap; + int gap = getIconTextGap(); if (icon == null || text.equals("")) //$NON-NLS-1$ gap = 0; Insets insets = getInsets(); @@ -203,7 +203,7 @@ switch(textPlacement) { case EAST: iconLocation.x = insets.left; - textLocation.x = iconSize.width + gap + insets.left; + textLocation.x = getIconSize().width + gap + insets.left; break; case WEST: textLocation.x = insets.left; @@ -214,7 +214,7 @@ iconLocation.y = getTextSize().height + gap + insets.top; break; case SOUTH: - textLocation.y = iconSize.height + gap + insets.top; + textLocation.y = getIconSize().height + gap + insets.top; iconLocation.y = insets.top; } } @@ -228,7 +228,7 @@ * @since 2.0 */ protected Dimension calculateSubStringTextSize() { - return FigureUtilities.getTextExtents(getSubStringText(), getFont()); + return getTextUtilities().getTextExtents(getSubStringText(), getFont()); } /** @@ -241,7 +241,7 @@ * @since 2.0 */ protected Dimension calculateTextSize() { - return FigureUtilities.getTextExtents(getText(), getFont()); + return getTextUtilities().getTextExtents(getText(), getFont()); } private void clearLocations() { @@ -277,7 +277,7 @@ */ public Rectangle getIconBounds() { Rectangle bounds = getBounds(); - return new Rectangle(bounds.getLocation().translate(getIconLocation()), iconSize); + return new Rectangle(bounds.getLocation().translate(getIconLocation()), getIconSize()); } /** @@ -313,8 +313,8 @@ minSize.setSize(getLayoutManager().getMinimumSize(this, w, h)); Dimension labelSize = - calculateLabelSize(FigureUtilities.getTextExtents(ELLIPSIS, getFont()) - .intersect(FigureUtilities.getTextExtents(getText(), getFont()))); + calculateLabelSize(getTextUtilities().getTextExtents(getTruncationString(), getFont()) + .intersect(getTextUtilities().getTextExtents(getText(), getFont()))); Insets insets = getInsets(); labelSize.expand(insets.getWidth(), insets.getHeight()); minSize.union(labelSize); @@ -360,15 +360,15 @@ Dimension effectiveSize = getTextSize().getExpanded(-widthShrink, 0); Font currentFont = getFont(); - int dotsWidth = FigureUtilities.getTextWidth(ELLIPSIS, currentFont); + int dotsWidth = getTextUtilities().getTextExtents(getTruncationString(), currentFont).width; if (effectiveSize.width < dotsWidth) effectiveSize.width = dotsWidth; - int subStringLength = FigureUtilities.getLargestSubstringConfinedTo(text, + int subStringLength = getTextUtilities().getLargestSubstringConfinedTo(text, currentFont, effectiveSize.width - dotsWidth); - subStringText = new String(text.substring(0, subStringLength) + ELLIPSIS); + subStringText = new String(text.substring(0, subStringLength) + getTruncationString()); return subStringText; } @@ -551,7 +551,7 @@ * @since 2.0 */ public void setIconDimension(Dimension d) { - if (d.equals(iconSize)) + if (d.equals(getIconSize())) return; iconSize = d; revalidate(); @@ -654,4 +654,36 @@ repaint(); } +/** + * Gets the TextUtilities instance to be used in measurement + * calculations. + * + * @return a TextUtilities instance + * @since 3.2 + */ +public TextUtilities getTextUtilities() { + return TextUtilities.INSTANCE; +} + +/** + * Gets the string that will be appended to the text when the label is + * truncated. By default, this returns an ellipsis. + * + * @return the string to append to the text when truncated + * @since 3.4 + */ +protected String getTruncationString() { + return ELLIPSIS; +} + +/** + * Gets the icon size + * + * @return the icon size + * @since 3.4 + */ +protected Dimension getIconSize() { + return iconSize; +} + } Index: src/org/eclipse/draw2d/FigureUtilities.java =================================================================== RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.draw2d/src/org/eclipse/draw2d/FigureUtilities.java,v retrieving revision 1.34 diff -u -r1.34 FigureUtilities.java --- src/org/eclipse/draw2d/FigureUtilities.java 24 Apr 2006 18:15:12 -0000 1.34 +++ src/org/eclipse/draw2d/FigureUtilities.java 19 Sep 2007 15:03:44 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2005 IBM Corporation and others. + * Copyright (c) 2000, 2007 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -114,48 +114,6 @@ } /** - * Returns the largest substring of s in Font f that can be confined to the - * number of pixels in availableWidth. - * - * @param s the original string - * @param f the font - * @param availableWidth the available width - * @return the largest substring that fits in the given width - * @since 2.0 - */ -static int getLargestSubstringConfinedTo(String s, Font f, int availableWidth) { - FontMetrics metrics = getFontMetrics(f); - int min, max; - float avg = metrics.getAverageCharWidth(); - min = 0; - max = s.length() + 1; - - //The size of the current guess - int guess = 0, - guessSize = 0; - while ((max - min) > 1) { - //Pick a new guess size - // New guess is the last guess plus the missing width in pixels - // divided by the average character size in pixels - guess = guess + (int)((availableWidth - guessSize) / avg); - - if (guess >= max) guess = max - 1; - if (guess <= min) guess = min + 1; - - //Measure the current guess - guessSize = getTextExtents(s.substring(0, guess), f).width; - - if (guessSize < availableWidth) - //We did not use the available width - min = guess; - else - //We exceeded the available width - max = guess; - } - return min; -} - -/** * Returns the Dimensions of the given text, converting newlines and tabs appropriately. * * @param text the text Index: src/org/eclipse/draw2d/TextUtilities.java =================================================================== RCS file: src/org/eclipse/draw2d/TextUtilities.java diff -N src/org/eclipse/draw2d/TextUtilities.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/draw2d/TextUtilities.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.draw2d; + +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontMetrics; + +import org.eclipse.draw2d.geometry.Dimension; + +/** + * Provides miscellaneous text operations. Clients may subclass this class if + * necessary. + * + * @author crevells + * @since 3.4 + */ +public class TextUtilities { + + /** + * a singleton default instance + */ + public static TextUtilities INSTANCE = new TextUtilities(); + + /** + * Returns the Dimensions of s in Font f. + * + * @param s + * the string + * @param f + * the font + * @return the dimensions of the given string + */ + public Dimension getStringExtents(String s, Font f) { + return FigureUtilities.getStringExtents(s, f); + } + + /** + * Returns the Dimensions of the given text, converting newlines and tabs + * appropriately. + * + * @param s + * the text + * @param f + * the font + * @return the dimensions of the given text + */ + public Dimension getTextExtents(String s, Font f) { + return FigureUtilities.getTextExtents(s, f); + } + + /** + * Gets the font's ascent. + * + * @param font + * @return the font's ascent + */ + public int getAscent(Font font) { + FontMetrics fm = FigureUtilities.getFontMetrics(font); + return fm.getHeight() - fm.getDescent(); + } + + /** + * Gets the font's descent. + * + * @param font + * @return the font's descent + */ + public int getDescent(Font font) { + return FigureUtilities.getFontMetrics(font).getDescent(); + } + + /** + * Returns the largest substring of s in Font f that can be + * confined to the number of pixels in availableWidth. + * + * @param s + * the original string + * @param f + * the font + * @param availableWidth + * the available width + * @return the largest substring that fits in the given width + */ + public int getLargestSubstringConfinedTo(String s, Font f, + int availableWidth) { + FontMetrics metrics = FigureUtilities.getFontMetrics(f); + int min, max; + float avg = metrics.getAverageCharWidth(); + min = 0; + max = s.length() + 1; + + // The size of the current guess + int guess = 0, guessSize = 0; + while ((max - min) > 1) { + // Pick a new guess size + // New guess is the last guess plus the missing width in pixels + // divided by the average character size in pixels + guess = guess + (int) ((availableWidth - guessSize) / avg); + + if (guess >= max) + guess = max - 1; + if (guess <= min) + guess = min + 1; + + // Measure the current guess + guessSize = getTextExtents(s.substring(0, guess), f).width; + + if (guessSize < availableWidth) + // We did not use the available width + min = guess; + else + // We exceeded the available width + max = guess; + } + return min; + } +}