View | Details | Raw Unified | Return to bug 194278 | Differences between
and this patch

Collapse All | Expand All

(-)src/org/eclipse/draw2d/text/FlowUtilities.java (-109 / +150 lines)
Lines 1-5 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2005 IBM Corporation and others.
2
 * Copyright (c) 2000, 2007 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
Lines 15-38 Link Here
15
15
16
import org.eclipse.swt.SWT;
16
import org.eclipse.swt.SWT;
17
import org.eclipse.swt.graphics.Font;
17
import org.eclipse.swt.graphics.Font;
18
import org.eclipse.swt.graphics.Rectangle;
18
import org.eclipse.swt.graphics.TextLayout;
19
import org.eclipse.swt.graphics.TextLayout;
19
import org.eclipse.swt.widgets.Display;
20
import org.eclipse.swt.widgets.Display;
20
21
21
import org.eclipse.draw2d.FigureUtilities;
22
import org.eclipse.draw2d.FigureUtilities;
23
import org.eclipse.draw2d.TextUtilities;
22
24
23
/**
25
/**
24
 * Utility class for FlowFigures.
26
 * Utility class for FlowFigures.
25
 * @author hudsonr
27
 * @author hudsonr
26
 * @since 2.1
28
 * @since 2.1
27
 */
29
 */
28
class FlowUtilities
30
public class FlowUtilities 
29
	extends FigureUtilities
30
{
31
{
31
32
32
interface LookAhead {
33
interface LookAhead {
33
	int getWidth();
34
	int getWidth();
34
}
35
}
35
private static int ELLIPSIS_SIZE;
36
37
/**
38
 * a singleton default instance
39
 */
40
public static FlowUtilities INSTANCE = new FlowUtilities();
41
36
private static final BreakIterator INTERNAL_LINE_BREAK = BreakIterator.getLineInstance();
42
private static final BreakIterator INTERNAL_LINE_BREAK = BreakIterator.getLineInstance();
37
private static TextLayout layout;
43
private static TextLayout layout;
38
44
Lines 61-70 Link Here
61
	return Math.min(macNL, unixNL);
67
	return Math.min(macNL, unixNL);
62
}
68
}
63
69
64
private static float getAverageCharWidth(TextFragmentBox fragment, Font font) {
70
/**
65
	if (fragment.getWidth() > 0 && fragment.length != 0)
71
 * Gets the average character width.
66
		return fragment.getWidth() / (float)fragment.length;
72
 * 
67
	return getFontMetrics(font).getAverageCharWidth();
73
 * @param fragment the supplied TextFragmentBox to use for calculation.
74
 *                 if the length is 0 or if the width is or below 0,
75
 *                 the average character width is taken from standard 
76
 *                 font metrics.
77
 * @param font     the font to use in case the TextFragmentBox conditions 
78
 *                 above are true.
79
 * @return         the average character width
80
 */
81
protected float getAverageCharWidth(TextFragmentBox fragment, Font font) {
82
    if (fragment.getWidth() > 0 && fragment.length != 0)
83
        return fragment.getWidth() / (float)fragment.length;
84
    return FigureUtilities.getFontMetrics(font).getAverageCharWidth();
68
}
85
}
69
86
70
static int getBorderAscent(InlineFlow owner) {
87
static int getBorderAscent(InlineFlow owner) {
Lines 129-162 Link Here
129
	}
146
	}
130
}
147
}
131
148
132
private static int measureString(TextFragmentBox frag, String string, int guess, Font font) {
149
private int measureString(TextFragmentBox frag, String string, int guess, Font font) {
133
	if (frag.requiresBidi()) {
150
    if (frag.requiresBidi()) {
134
		// The text and/or could have changed if the lookAhead was invoked.  This will
151
        // The text and/or could have changed if the lookAhead was invoked.  This will
135
		// happen at most once.
152
        // happen at most once.
136
		TextLayout layout = getTextLayout();
153
        return getTextLayoutBounds(string, font, 0, guess - 1).width;
137
		layout.setText(string);
154
    } else
138
		layout.setFont(font);
155
        return getTextUtilities().getStringExtents(string.substring(0, guess), font).width;
139
		return layout.getBounds(0, guess - 1).width;
156
}
140
	} else
157
141
		return getStringDimension(string.substring(0, guess), font).x;
158
protected void setupFragment(TextFragmentBox frag, Font f, String s) {
142
}
159
    if (frag.getWidth() == -1 || frag.isTruncated()) {
143
160
        int width;
144
static void setupFragment(TextFragmentBox frag, Font f, String s) {
161
        if (s.length() == 0 || frag.length == 0)
145
	if (frag.getWidth() == -1 || frag.isTruncated()) {
162
            width = 0;
146
		int width;
163
        else if (frag.requiresBidi()) {
147
		if (s.length() == 0 || frag.length == 0)
164
            width = getTextLayoutBounds(s, f, 0, frag.length - 1).width;
148
			width = 0;
165
        } else
149
		else if (frag.requiresBidi()) {
166
            width = getTextUtilities().getStringExtents(s.substring(0, frag.length), f).width;
150
			TextLayout textLayout = getTextLayout();
167
        if (frag.isTruncated())
151
			textLayout.setFont(f);
168
            width += getEllipsisWidth(f);
152
			textLayout.setText(s);
169
        frag.setWidth(width);
153
			width = textLayout.getBounds(0, frag.length - 1).width;
170
    }
154
		} else
155
			width = getStringDimension(s.substring(0, frag.length), f).x;
156
		if (frag.isTruncated())
157
			width += ELLIPSIS_SIZE;
158
		frag.setWidth(width);
159
	}
160
}
171
}
161
172
162
/**
173
/**
Lines 172-178 Link Here
172
 * @return the number of characters that will fit in the given space; can be 0 (eg., when
183
 * @return the number of characters that will fit in the given space; can be 0 (eg., when
173
 * the first character of the given string is a newline)
184
 * the first character of the given string is a newline)
174
 */
185
 */
175
public static int wrapFragmentInContext(TextFragmentBox frag, String string,
186
protected int wrapFragmentInContext(TextFragmentBox frag, String string,
176
		FlowContext context, LookAhead lookahead, Font font, int wrapping) {
187
		FlowContext context, LookAhead lookahead, Font font, int wrapping) {
177
	frag.setTruncated(false);
188
	frag.setTruncated(false);
178
	int strLen = string.length();
189
	int strLen = string.length();
Lines 245-305 Link Here
245
			continue;
256
			continue;
246
		}
257
		}
247
258
248
		if (guessSize <= availableWidth) {
259
        if (guessSize <= availableWidth) {
249
			min = guess;
260
            min = guess;
250
			frag.setWidth(guessSize);
261
            frag.setWidth(guessSize);
251
			if (guessSize == availableWidth)
262
            if (guessSize == availableWidth)
252
				max = guess + 1;
263
                max = guess + 1;
253
		} else
264
        } else
254
			max = guess;
265
            max = guess;
255
	}
266
    }
256
	
267
    
257
	int result = min;
268
    int result = min;
258
	boolean continueOnLine = false;
269
    boolean continueOnLine = false;
259
	if (min == strLen) {
270
    if (min == strLen) {
260
		//Everything fits
271
        //Everything fits
261
		if (string.charAt(strLen - 1) == ' ') {
272
        if (string.charAt(strLen - 1) == ' ') {
262
			if (frag.getWidth() == -1) {
273
            if (frag.getWidth() == -1) {
263
				frag.length = result;
274
                frag.length = result;
264
				frag.setWidth(measureString(frag, string, result, font));
275
                frag.setWidth(measureString(frag, string, result, font));
265
			}
276
            }
266
			if (lookahead.getWidth() > availableWidth - frag.getWidth()) {
277
            if (lookahead.getWidth() > availableWidth - frag.getWidth()) {
267
				frag.length = result - 1;
278
                frag.length = result - 1;
268
				frag.setWidth(-1);
279
                frag.setWidth(-1);
269
			} else
280
            } else
270
				frag.length = result;
281
                frag.length = result;
271
		} else {
282
        } else {
272
			continueOnLine = !canBreakAfter(string.charAt(strLen - 1));
283
            continueOnLine = !canBreakAfter(string.charAt(strLen - 1));
273
			frag.length = result;
284
            frag.length = result;
274
		}
285
        }
275
	} else if (min == firstDelimiter) {
286
    } else if (min == firstDelimiter) {
276
		//move result past the delimiter
287
        //move result past the delimiter
277
		frag.length = result;
288
        frag.length = result;
278
		if (string.charAt(min) == '\r') {
289
        if (string.charAt(min) == '\r') {
279
			result++;
290
            result++;
280
			if (++min < strLen && string.charAt(min) == '\n')
291
            if (++min < strLen && string.charAt(min) == '\n')
281
				result++;
292
                result++;
282
		} else if (string.charAt(min) == '\n')
293
        } else if (string.charAt(min) == '\n')
283
			result++;
294
            result++;
284
	} else if (string.charAt(min) == ' '
295
    } else if (string.charAt(min) == ' '
285
			|| canBreakAfter(string.charAt(min - 1))
296
            || canBreakAfter(string.charAt(min - 1))
286
			|| INTERNAL_LINE_BREAK.isBoundary(min)) {
297
            || INTERNAL_LINE_BREAK.isBoundary(min)) {
287
		frag.length = min;
298
        frag.length = min;
288
		if (string.charAt(min) == ' ')
299
        if (string.charAt(min) == ' ')
289
			result++;
300
            result++;
290
		else if (string.charAt(min - 1) == ' ') {
301
        else if (string.charAt(min - 1) == ' ') {
291
			frag.length--;
302
            frag.length--;
292
			frag.setWidth(-1);
303
            frag.setWidth(-1);
293
		}
304
        }
294
	} else out: {
305
    } else out: {
295
		// In the middle of an unbreakable offset
306
        // In the middle of an unbreakable offset
296
		result = INTERNAL_LINE_BREAK.preceding(min);
307
        result = INTERNAL_LINE_BREAK.preceding(min);
297
		if (result == 0) {
308
        if (result == 0) {
298
			switch (wrapping) {
309
            switch (wrapping) {
299
				case ParagraphTextLayout.WORD_WRAP_TRUNCATE :
310
                case ParagraphTextLayout.WORD_WRAP_TRUNCATE :
300
					ELLIPSIS_SIZE = FigureUtilities
311
					int truncatedWidth = availableWidth - getEllipsisWidth(font);
301
							.getStringExtents(TextFlow.ELLIPSIS, font).width;
302
					int truncatedWidth = availableWidth - ELLIPSIS_SIZE;
303
					if (truncatedWidth > 0) {
312
					if (truncatedWidth > 0) {
304
						//$TODO this is very slow.  It should be using avgCharWidth to go faster
313
						//$TODO this is very slow.  It should be using avgCharWidth to go faster
305
						while (min > 0) {
314
						while (min > 0) {
Lines 312-335 Link Here
312
					} else
321
					} else
313
						frag.length = 0;
322
						frag.length = 0;
314
					frag.setTruncated(true);
323
					frag.setTruncated(true);
315
					result = INTERNAL_LINE_BREAK.following(max - 1);
324
                    result = INTERNAL_LINE_BREAK.following(max - 1);
316
					break out;
325
                    break out;
317
326
318
				default:
327
                default:
319
					result = min;
328
                    result = min;
320
					break;
329
                    break;
321
			}
330
            }
322
		}
331
        }
323
		frag.length = result;
332
        frag.length = result;
324
		if (string.charAt(result - 1) == ' ')
333
        if (string.charAt(result - 1) == ' ')
325
			frag.length--;
334
            frag.length--;
326
		frag.setWidth(-1);
335
        frag.setWidth(-1);
327
	}
336
    }
328
	
337
    
329
	setupFragment(frag, font, string);
338
    setupFragment(frag, font, string);
330
	context.addToCurrentLine(frag);
339
    context.addToCurrentLine(frag);
331
	context.setContinueOnSameLine(continueOnLine);
340
    context.setContinueOnSameLine(continueOnLine);
332
	return result;
341
    return result;
333
}
342
}
334
343
344
/**
345
 * @see TextLayout#getBounds()
346
 */
347
protected Rectangle getTextLayoutBounds(String s, Font f, int start, int end) {
348
    TextLayout textLayout = getTextLayout();
349
    textLayout.setFont(f);
350
    textLayout.setText(s);
351
    return textLayout.getBounds(start, end);
352
}
353
354
/**
355
 * Returns an instance of a <code>TextUtililities</code> class on which
356
 * text calculations can be performed. Clients may override to customize.
357
 * 
358
 * @return the <code>TextUtililities</code> instance
359
 * @since 3.4
360
 */
361
protected TextUtilities getTextUtilities() {
362
    return TextUtilities.INSTANCE;
363
}
364
365
/**
366
 * Gets the ellipsis width.
367
 * 
368
 * @param font
369
 *            the font to be used in the calculation
370
 * @return the width of the ellipsis
371
 * @since 3.4
372
 */
373
private int getEllipsisWidth(Font font) {
374
    return getTextUtilities().getStringExtents(TextFlow.ELLIPSIS, font).width;
375
}
335
}
376
}
(-)src/org/eclipse/draw2d/text/TextFlow.java (-10 / +29 lines)
Lines 1-5 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2005 IBM Corporation and others.
2
 * Copyright (c) 2000, 2007 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
Lines 12-23 Link Here
12
12
13
import org.eclipse.swt.SWT;
13
import org.eclipse.swt.SWT;
14
import org.eclipse.swt.graphics.Color;
14
import org.eclipse.swt.graphics.Color;
15
import org.eclipse.swt.graphics.FontMetrics;
16
import org.eclipse.swt.graphics.TextLayout;
15
import org.eclipse.swt.graphics.TextLayout;
17
16
18
import org.eclipse.draw2d.ColorConstants;
17
import org.eclipse.draw2d.ColorConstants;
19
import org.eclipse.draw2d.FigureUtilities;
20
import org.eclipse.draw2d.Graphics;
18
import org.eclipse.draw2d.Graphics;
19
import org.eclipse.draw2d.TextUtilities;
21
import org.eclipse.draw2d.geometry.Dimension;
20
import org.eclipse.draw2d.geometry.Dimension;
22
import org.eclipse.draw2d.geometry.Point;
21
import org.eclipse.draw2d.geometry.Point;
23
import org.eclipse.draw2d.geometry.Rectangle;
22
import org.eclipse.draw2d.geometry.Rectangle;
Lines 95-101 Link Here
95
	text = text.substring(1, index + 1);
94
	text = text.substring(1, index + 1);
96
95
97
	if (bidiInfo == null)
96
	if (bidiInfo == null)
98
		width[0] += FlowUtilities.getStringExtents(text, getFont()).width;
97
		width[0] += getTextUtilities().getStringExtents(text, getFont()).width;
99
	else {
98
	else {
100
		TextLayout textLayout = FlowUtilities.getTextLayout();
99
		TextLayout textLayout = FlowUtilities.getTextLayout();
101
		textLayout.setFont(getFont());
100
		textLayout.setFont(getFont());
Lines 175-182 Link Here
175
}
174
}
176
175
177
int getAscent() {
176
int getAscent() {
178
	FontMetrics fm = FigureUtilities.getFontMetrics(getFont());
177
    return getTextUtilities().getAscent(getFont());
179
	return fm.getHeight() - fm.getDescent();
180
}
178
}
181
179
182
/**
180
/**
Lines 267-273 Link Here
267
		if (trailing && offset < box.length)
265
		if (trailing && offset < box.length)
268
			offset++;
266
			offset++;
269
		String substring = getText().substring(box.offset, box.offset + offset);
267
		String substring = getText().substring(box.offset, box.offset + offset);
270
		result.x = FigureUtilities.getStringExtents(substring, getFont()).width;
268
		result.x = getTextUtilities().getStringExtents(substring, getFont()).width;
271
	} else {
269
	} else {
272
		TextLayout layout = FlowUtilities.getTextLayout();
270
		TextLayout layout = FlowUtilities.getTextLayout();
273
		layout.setFont(getFont());
271
		layout.setFont(getFont());
Lines 283-289 Link Here
283
}
281
}
284
282
285
int getDescent() {
283
int getDescent() {
286
	return FigureUtilities.getFontMetrics(getFont()).getDescent();
284
    return getTextUtilities().getDescent(getFont());
287
}
285
}
288
286
289
/**
287
/**
Lines 485-491 Link Here
485
	g.getClip(Rectangle.SINGLETON);
483
	g.getClip(Rectangle.SINGLETON);
486
	int yStart = Rectangle.SINGLETON.y;
484
	int yStart = Rectangle.SINGLETON.y;
487
	int yEnd = Rectangle.SINGLETON.bottom();
485
	int yEnd = Rectangle.SINGLETON.bottom();
488
		
486
	
489
	for (int i = 0; i < fragments.size(); i++) {
487
	for (int i = 0; i < fragments.size(); i++) {
490
		frag = (TextFragmentBox)fragments.get(i);
488
		frag = (TextFragmentBox)fragments.get(i);
491
//		g.drawLine(frag.getX(), frag.getLineRoot().getVisibleTop(),
489
//		g.drawLine(frag.getX(), frag.getLineRoot().getVisibleTop(),
Lines 499-505 Link Here
499
		//Break loop at first non-visible fragment
497
		//Break loop at first non-visible fragment
500
		if (yEnd < frag.getLineRoot().getVisibleTop())
498
		if (yEnd < frag.getLineRoot().getVisibleTop())
501
			break;
499
			break;
502
500
		
503
		String draw = getBidiSubstring(frag, i);
501
		String draw = getBidiSubstring(frag, i);
504
		
502
		
505
		if (frag.isTruncated())
503
		if (frag.isTruncated())
Lines 629-632 Link Here
629
	return Math.max(0, y - (box.getBaseline() + box.getLineRoot().getDescent()));
627
	return Math.max(0, y - (box.getBaseline() + box.getLineRoot().getDescent()));
630
}
628
}
631
629
630
/**
631
 * Gets the <code>FlowUtilities</code> instance to be used in measurement
632
 * calculations.
633
 * 
634
 * @return a <code>FlowUtilities</code> instance
635
 * @since 3.2
636
 */
637
protected FlowUtilities getFlowUtilities() {
638
    return FlowUtilities.INSTANCE;
639
}
640
641
/**
642
 * Gets the <code>TextUtilities</code> instance to be used in measurement
643
 * calculations.
644
 * 
645
 * @return a <code>TextUtilities</code> instance
646
 * @since 3.2
647
 */
648
protected TextUtilities getTextUtilities() {
649
    return TextUtilities.INSTANCE;
650
}
632
}
651
}
(-)src/org/eclipse/draw2d/text/SimpleTextLayout.java (-38 / +39 lines)
Lines 1-5 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2005 IBM Corporation and others.
2
 * Copyright (c) 2000, 2007 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
Lines 21-29 Link Here
21
public class SimpleTextLayout extends TextLayout {
21
public class SimpleTextLayout extends TextLayout {
22
22
23
private static final String[] DELIMITERS = {
23
private static final String[] DELIMITERS = {
24
	"\r\n", //$NON-NLS-1$
24
    "\r\n", //$NON-NLS-1$
25
	 "\n", //$NON-NLS-1$
25
     "\n", //$NON-NLS-1$
26
	 "\r"};//$NON-NLS-1$
26
     "\r"};//$NON-NLS-1$
27
27
28
private static int result;
28
private static int result;
29
private static int delimeterLength;
29
private static int delimeterLength;
Lines 33-81 Link Here
33
 * @param flow the TextFlow
33
 * @param flow the TextFlow
34
 */
34
 */
35
public SimpleTextLayout(TextFlow flow) {
35
public SimpleTextLayout(TextFlow flow) {
36
	super (flow);
36
    super (flow);
37
}
37
}
38
38
39
/**
39
/**
40
 * @see org.eclipse.draw2d.text.FlowFigureLayout#layout()
40
 * @see org.eclipse.draw2d.text.FlowFigureLayout#layout()
41
 */
41
 */
42
protected void layout() {
42
protected void layout() {
43
	TextFlow textFlow = (TextFlow)getFlowFigure();
43
    TextFlow textFlow = (TextFlow)getFlowFigure();
44
	String text = textFlow.getText();
44
    String text = textFlow.getText();
45
	List fragments = textFlow.getFragments();
45
    List fragments = textFlow.getFragments();
46
	Font font = textFlow.getFont();
46
    Font font = textFlow.getFont();
47
	TextFragmentBox fragment;
47
    TextFragmentBox fragment;
48
	int i = 0;
48
    int i = 0;
49
	int offset = 0;
49
    int offset = 0;
50
	
50
    FlowUtilities flowUtilities = textFlow.getFlowUtilities();
51
	do {
51
    
52
		nextLineBreak(text, offset);
52
    do {
53
		fragment = getFragment(i++, fragments);
53
        nextLineBreak(text, offset);
54
		fragment.length = result - offset;
54
        fragment = getFragment(i++, fragments);
55
		fragment.offset = offset;
55
        fragment.length = result - offset;
56
		fragment.setWidth(-1);
56
        fragment.offset = offset;
57
		FlowUtilities.setupFragment(fragment, font, text);
57
        fragment.setWidth(-1);
58
		getContext().addToCurrentLine(fragment);
58
        flowUtilities.setupFragment(fragment, font, text);
59
		getContext().endLine();
59
        getContext().addToCurrentLine(fragment);
60
		offset = result + delimeterLength;
60
        getContext().endLine();
61
	} while (offset < text.length());
61
        offset = result + delimeterLength;
62
	//Remove the remaining unused fragments.
62
    } while (offset < text.length());
63
	while (i < fragments.size())
63
    //Remove the remaining unused fragments.
64
		fragments.remove(i++);
64
    while (i < fragments.size())
65
        fragments.remove(i++);
65
}
66
}
66
67
67
private int nextLineBreak(String text, int offset) {
68
private int nextLineBreak(String text, int offset) {
68
	result = text.length();
69
    result = text.length();
69
	delimeterLength = 0;
70
    delimeterLength = 0;
70
	int current;
71
    int current;
71
	for (int i = 0; i < DELIMITERS.length; i++) {
72
    for (int i = 0; i < DELIMITERS.length; i++) {
72
		current = text.indexOf(DELIMITERS[i], offset);
73
        current = text.indexOf(DELIMITERS[i], offset);
73
		if (current != -1 && current < result) {
74
        if (current != -1 && current < result) {
74
			result = current;
75
            result = current;
75
			delimeterLength = DELIMITERS[i].length();
76
            delimeterLength = DELIMITERS[i].length();
76
		}
77
        }
77
	}
78
    }
78
	return result;
79
    return result;
79
}
80
}
80
81
81
}
82
}
(-)src/org/eclipse/draw2d/text/ParagraphTextLayout.java (-126 / +127 lines)
Lines 1-5 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2005 IBM Corporation and others.
2
 * Copyright (c) 2000, 2007 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
Lines 20-26 Link Here
20
 * @since 2.1
20
 * @since 2.1
21
 */
21
 */
22
public class ParagraphTextLayout
22
public class ParagraphTextLayout
23
	extends TextLayout
23
    extends TextLayout
24
{
24
{
25
25
26
/**
26
/**
Lines 48-70 Link Here
48
 * @param flow the TextFlow
48
 * @param flow the TextFlow
49
 */
49
 */
50
public ParagraphTextLayout(TextFlow flow) {
50
public ParagraphTextLayout(TextFlow flow) {
51
	super(flow);
51
    super(flow);
52
}
52
}
53
53
54
/**
54
/**
55
 * Constructs the layout with the specified TextFlow and wrapping style.  The wrapping
55
 * Constructs the layout with the specified TextFlow and wrapping style.  The wrapping
56
 * style must be one of:
56
 * style must be one of:
57
 * <UL>
57
 * <UL>
58
 * 	<LI>{@link #WORD_WRAP_HARD}</LI>
58
 *  <LI>{@link #WORD_WRAP_HARD}</LI>
59
 * 	<LI>{@link #WORD_WRAP_SOFT}</LI>
59
 *  <LI>{@link #WORD_WRAP_SOFT}</LI>
60
 * 	<LI>{@link #WORD_WRAP_TRUNCATE}</LI>
60
 *  <LI>{@link #WORD_WRAP_TRUNCATE}</LI>
61
 * </UL>
61
 * </UL>
62
 * @param flow the textflow
62
 * @param flow the textflow
63
 * @param style the style of wrapping
63
 * @param style the style of wrapping
64
 */
64
 */
65
public ParagraphTextLayout(TextFlow flow, int style) {
65
public ParagraphTextLayout(TextFlow flow, int style) {
66
	this(flow);
66
    this(flow);
67
	wrappingStyle = style;
67
    wrappingStyle = style;
68
}
68
}
69
69
70
/**
70
/**
Lines 75-207 Link Here
75
 * @return the requested segment
75
 * @return the requested segment
76
 */
76
 */
77
private String[] getSegments(String text, int levelInfo[]) {
77
private String[] getSegments(String text, int levelInfo[]) {
78
	if (levelInfo.length == 1)
78
    if (levelInfo.length == 1)
79
		return new String[] {text};
79
        return new String[] {text};
80
 	
80
    
81
	String result[] = new String[levelInfo.length / 2 + 1];
81
    String result[] = new String[levelInfo.length / 2 + 1];
82
	
82
    
83
	int i;
83
    int i;
84
	int endOffset;
84
    int endOffset;
85
	int beginOffset = 0;
85
    int beginOffset = 0;
86
86
87
	for (i = 0; i < result.length - 1; i++) {
87
    for (i = 0; i < result.length - 1; i++) {
88
		endOffset = levelInfo[i * 2 + 1];
88
        endOffset = levelInfo[i * 2 + 1];
89
		result[i] = text.substring(beginOffset, endOffset);
89
        result[i] = text.substring(beginOffset, endOffset);
90
		beginOffset = endOffset;
90
        beginOffset = endOffset;
91
	}
91
    }
92
	endOffset = text.length();
92
    endOffset = text.length();
93
	result[i] = text.substring(beginOffset, endOffset);
93
    result[i] = text.substring(beginOffset, endOffset);
94
	return result;
94
    return result;
95
}
95
}
96
96
97
class SegmentLookahead implements FlowUtilities.LookAhead {
97
class SegmentLookahead implements FlowUtilities.LookAhead {
98
	private int seg = -1;
98
    private int seg = -1;
99
	private String segs[];
99
    private String segs[];
100
	private int[] width;
100
    private int[] width;
101
	private final int trailingBorderSize;
101
    private final int trailingBorderSize;
102
	SegmentLookahead(String segs[], int trailingBorderSize) {
102
    SegmentLookahead(String segs[], int trailingBorderSize) {
103
		this.segs = segs;
103
        this.segs = segs;
104
		this.trailingBorderSize = trailingBorderSize;
104
        this.trailingBorderSize = trailingBorderSize;
105
	}
105
    }
106
	public int getWidth() {
106
    public int getWidth() {
107
		if (width == null) {
107
        if (width == null) {
108
			width = new int[1];
108
            width = new int[1];
109
			int startingIndex = seg + 1;
109
            int startingIndex = seg + 1;
110
			TextFlow textFlow = (TextFlow)getFlowFigure();
110
            TextFlow textFlow = (TextFlow)getFlowFigure();
111
		
111
        
112
			if (startingIndex == segs.length) {
112
            if (startingIndex == segs.length) {
113
				width[0] += trailingBorderSize;
113
                width[0] += trailingBorderSize;
114
				getContext().getWidthLookahead(textFlow, width);
114
                getContext().getWidthLookahead(textFlow, width);
115
			} else {
115
            } else {
116
				String rest = segs[startingIndex];
116
                String rest = segs[startingIndex];
117
				for (int k = startingIndex + 1; k < segs.length; k++)
117
                for (int k = startingIndex + 1; k < segs.length; k++)
118
					rest += segs[k];
118
                    rest += segs[k];
119
				if (!textFlow.addLeadingWordWidth(rest, width)) {
119
                if (!textFlow.addLeadingWordWidth(rest, width)) {
120
					width[0] += trailingBorderSize;
120
                    width[0] += trailingBorderSize;
121
					getContext().getWidthLookahead(textFlow, width);
121
                    getContext().getWidthLookahead(textFlow, width);
122
				}
122
                }
123
			}
123
            }
124
		}
124
        }
125
		return width[0];
125
        return width[0];
126
	}
126
    }
127
	public void setIndex(int value) {
127
    public void setIndex(int value) {
128
		this.seg = value;
128
        this.seg = value;
129
		width = null;
129
        width = null;
130
	}
130
    }
131
}
131
}
132
132
133
/**
133
/**
134
 * @see org.eclipse.draw2d.text.FlowFigureLayout#layout()
134
 * @see org.eclipse.draw2d.text.FlowFigureLayout#layout()
135
 */
135
 */
136
protected void layout() {
136
protected void layout() {
137
	TextFlow textFlow = (TextFlow)getFlowFigure();
137
    TextFlow textFlow = (TextFlow)getFlowFigure();
138
	int offset = 0;
138
    int offset = 0;
139
	
139
    
140
	FlowContext context = getContext();
140
    FlowContext context = getContext();
141
	List fragments = textFlow.getFragments();
141
    List fragments = textFlow.getFragments();
142
	Font font = textFlow.getFont();
142
    Font font = textFlow.getFont();
143
	int fragIndex = 0;
143
    int fragIndex = 0;
144
	int advance = 0;
144
    int advance = 0;
145
	
145
    
146
	TextFragmentBox fragment;
146
    TextFragmentBox fragment;
147
	int levelInfo[] = (textFlow.getBidiInfo() == null)
147
    int levelInfo[] = (textFlow.getBidiInfo() == null)
148
		? new int[] {-1}
148
        ? new int[] {-1}
149
		: textFlow.getBidiInfo().levelInfo;
149
        : textFlow.getBidiInfo().levelInfo;
150
	
150
    
151
	String segment, segments[] = getSegments(textFlow.getText(), levelInfo);
151
    String segment, segments[] = getSegments(textFlow.getText(), levelInfo);
152
	FlowBorder border = null;
152
    FlowBorder border = null;
153
	if (textFlow.getBorder() instanceof FlowBorder)
153
    if (textFlow.getBorder() instanceof FlowBorder)
154
		border = (FlowBorder)textFlow.getBorder();
154
        border = (FlowBorder)textFlow.getBorder();
155
155
156
	SegmentLookahead lookahead = new SegmentLookahead(segments, border == null ? 0 : border.getRightMargin());
156
    SegmentLookahead lookahead = new SegmentLookahead(segments, border == null ? 0 : border.getRightMargin());
157
	int seg;
157
    int seg;
158
	
158
    
159
	if (border != null) {
159
    if (border != null) {
160
		fragment = getFragment(fragIndex++, fragments);
160
        fragment = getFragment(fragIndex++, fragments);
161
		fragment.setBidiLevel(levelInfo[0]);
161
        fragment.setBidiLevel(levelInfo[0]);
162
		fragment.setTruncated(false);
162
        fragment.setTruncated(false);
163
		fragment.offset = fragment.length = -1;
163
        fragment.offset = fragment.length = -1;
164
		fragment.setWidth(border.getLeftMargin() + border.getInsets(textFlow).left);
164
        fragment.setWidth(border.getLeftMargin() + border.getInsets(textFlow).left);
165
		if (context.getRemainingLineWidth()
165
        if (context.getRemainingLineWidth()
166
				< fragment.getWidth() + lookahead.getWidth())
166
                < fragment.getWidth() + lookahead.getWidth())
167
			context.endLine();
167
            context.endLine();
168
		context.addToCurrentLine(fragment);
168
        context.addToCurrentLine(fragment);
169
	}
169
    }
170
	
170
    
171
	for (seg = 0; seg < segments.length; seg++) {
171
    FlowUtilities flowUtilities = textFlow.getFlowUtilities();
172
		segment = segments[seg];
172
    for (seg = 0; seg < segments.length; seg++) {
173
		lookahead.setIndex(seg);
173
        segment = segments[seg];
174
		
174
        lookahead.setIndex(seg);
175
		do {
175
        
176
			fragment = getFragment(fragIndex++, fragments);
176
        do {
177
			
177
            fragment = getFragment(fragIndex++, fragments);
178
			fragment.offset = offset;
178
            
179
			fragment.setBidiLevel(levelInfo[seg * 2]);
179
            fragment.offset = offset;
180
			
180
            fragment.setBidiLevel(levelInfo[seg * 2]);
181
			advance = FlowUtilities.wrapFragmentInContext(fragment, segment,
181
            
182
					context, lookahead, font, wrappingStyle);
182
            advance = flowUtilities.wrapFragmentInContext(fragment, segment,
183
			segment = segment.substring(advance);
183
                    context, lookahead, font, wrappingStyle);
184
			offset += advance;
184
            segment = segment.substring(advance);
185
			if ((segment.length() > 0
185
            offset += advance;
186
					|| fragment.length < advance)
186
            if ((segment.length() > 0
187
					|| fragment.isTruncated())
187
                    || fragment.length < advance)
188
				context.endLine();
188
                    || fragment.isTruncated())
189
		} while (segment.length() > 0 
189
                context.endLine();
190
				|| (!fragment.isTruncated() && fragment.length < advance));
190
        } while (segment.length() > 0 
191
	}
191
                || (!fragment.isTruncated() && fragment.length < advance));
192
	
192
    }
193
	if (border != null) {
193
    
194
		fragment = getFragment(fragIndex++, fragments);
194
    if (border != null) {
195
		fragment.setBidiLevel(levelInfo[0]);
195
        fragment = getFragment(fragIndex++, fragments);
196
		fragment.setTruncated(false);
196
        fragment.setBidiLevel(levelInfo[0]);
197
		fragment.offset = fragment.length = -1;
197
        fragment.setTruncated(false);
198
		fragment.setWidth(border.getRightMargin() + border.getInsets(textFlow).right);
198
        fragment.offset = fragment.length = -1;
199
		context.addToCurrentLine(fragment);
199
        fragment.setWidth(border.getRightMargin() + border.getInsets(textFlow).right);
200
	}
200
        context.addToCurrentLine(fragment);
201
	
201
    }
202
	//Remove the remaining unused fragments.
202
    
203
	while (fragIndex < fragments.size())
203
    //Remove the remaining unused fragments.
204
		fragments.remove(fragments.size() - 1);
204
    while (fragIndex < fragments.size())
205
        fragments.remove(fragments.size() - 1);
205
}
206
}
206
207
207
}
208
}
(-)src/org/eclipse/draw2d/Label.java (-19 / +51 lines)
Lines 115-126 Link Here
115
		case EAST:
115
		case EAST:
116
		case WEST:
116
		case WEST:
117
			alignOnHeight(textLocation, getTextSize(), textAlignment);
117
			alignOnHeight(textLocation, getTextSize(), textAlignment);
118
			alignOnHeight(iconLocation, iconSize, iconAlignment);
118
			alignOnHeight(iconLocation, getIconSize(), iconAlignment);
119
			break;
119
			break;
120
		case NORTH:
120
		case NORTH:
121
		case SOUTH:
121
		case SOUTH:
122
			alignOnWidth(textLocation, getSubStringTextSize(), textAlignment);
122
			alignOnWidth(textLocation, getSubStringTextSize(), textAlignment);
123
			alignOnWidth(iconLocation, iconSize, iconAlignment);
123
			alignOnWidth(iconLocation, getIconSize(), iconAlignment);
124
			break;
124
			break;
125
	}
125
	}
126
}
126
}
Lines 134-149 Link Here
134
 * @since 2.0
134
 * @since 2.0
135
 */ 
135
 */ 
136
protected Dimension calculateLabelSize(Dimension txtSize) {
136
protected Dimension calculateLabelSize(Dimension txtSize) {
137
	int gap = iconTextGap;
137
	int gap = getIconTextGap();
138
	if (getIcon() == null || getText().equals("")) //$NON-NLS-1$
138
	if (getIcon() == null || getText().equals("")) //$NON-NLS-1$
139
		gap = 0;
139
		gap = 0;
140
	Dimension d = new Dimension(0, 0);
140
	Dimension d = new Dimension(0, 0);
141
	if (textPlacement == WEST || textPlacement == EAST) {
141
	if (textPlacement == WEST || textPlacement == EAST) {
142
		d.width = iconSize.width + gap + txtSize.width;
142
		d.width = getIconSize().width + gap + txtSize.width;
143
		d.height = Math.max(iconSize.height, txtSize.height);
143
		d.height = Math.max(getIconSize().height, txtSize.height);
144
	} else {
144
	} else {
145
		d.width = Math.max(iconSize.width, txtSize.width);
145
		d.width = Math.max(getIconSize().width, txtSize.width);
146
		d.height = iconSize.height + gap + txtSize.height;
146
		d.height = getIconSize().height + gap + txtSize.height;
147
	}
147
	}
148
	return d;
148
	return d;
149
}
149
}
Lines 195-201 Link Here
195
}
195
}
196
196
197
private void calculatePlacement() {
197
private void calculatePlacement() {
198
	int gap = iconTextGap;
198
	int gap = getIconTextGap();
199
	if (icon == null || text.equals("")) //$NON-NLS-1$
199
	if (icon == null || text.equals("")) //$NON-NLS-1$
200
		gap = 0;
200
		gap = 0;
201
	Insets insets = getInsets();
201
	Insets insets = getInsets();
Lines 203-209 Link Here
203
	switch(textPlacement) {
203
	switch(textPlacement) {
204
	case EAST:
204
	case EAST:
205
		iconLocation.x = insets.left;
205
		iconLocation.x = insets.left;
206
		textLocation.x = iconSize.width + gap + insets.left;	
206
		textLocation.x = getIconSize().width + gap + insets.left;	
207
		break;
207
		break;
208
	case WEST:
208
	case WEST:
209
		textLocation.x = insets.left;
209
		textLocation.x = insets.left;
Lines 214-220 Link Here
214
		iconLocation.y = getTextSize().height + gap + insets.top;
214
		iconLocation.y = getTextSize().height + gap + insets.top;
215
		break;
215
		break;
216
	case SOUTH:
216
	case SOUTH:
217
		textLocation.y = iconSize.height + gap + insets.top;
217
		textLocation.y = getIconSize().height + gap + insets.top;
218
		iconLocation.y = insets.top;
218
		iconLocation.y = insets.top;
219
	}
219
	}
220
}
220
}
Lines 228-234 Link Here
228
 * @since 2.0
228
 * @since 2.0
229
 */
229
 */
230
protected Dimension calculateSubStringTextSize() {
230
protected Dimension calculateSubStringTextSize() {
231
	return FigureUtilities.getTextExtents(getSubStringText(), getFont());
231
	return getTextUtilities().getTextExtents(getSubStringText(), getFont());
232
}
232
}
233
233
234
/**
234
/**
Lines 241-247 Link Here
241
 * @since 2.0
241
 * @since 2.0
242
 */
242
 */
243
protected Dimension calculateTextSize() {
243
protected Dimension calculateTextSize() {
244
	return FigureUtilities.getTextExtents(getText(), getFont());
244
	return getTextUtilities().getTextExtents(getText(), getFont());
245
}
245
}
246
246
247
private void clearLocations() {
247
private void clearLocations() {
Lines 277-283 Link Here
277
 */
277
 */
278
public Rectangle getIconBounds() {
278
public Rectangle getIconBounds() {
279
	Rectangle bounds = getBounds();
279
	Rectangle bounds = getBounds();
280
	return new Rectangle(bounds.getLocation().translate(getIconLocation()), iconSize);
280
	return new Rectangle(bounds.getLocation().translate(getIconLocation()), getIconSize());
281
}
281
}
282
282
283
/**
283
/**
Lines 313-320 Link Here
313
		minSize.setSize(getLayoutManager().getMinimumSize(this, w, h));
313
		minSize.setSize(getLayoutManager().getMinimumSize(this, w, h));
314
	
314
	
315
	Dimension labelSize = 
315
	Dimension labelSize = 
316
		calculateLabelSize(FigureUtilities.getTextExtents(ELLIPSIS, getFont())
316
		calculateLabelSize(getTextUtilities().getTextExtents(getTruncationString(), getFont())
317
				.intersect(FigureUtilities.getTextExtents(getText(), getFont())));
317
				.intersect(getTextUtilities().getTextExtents(getText(), getFont())));
318
	Insets insets = getInsets();
318
	Insets insets = getInsets();
319
	labelSize.expand(insets.getWidth(), insets.getHeight());
319
	labelSize.expand(insets.getWidth(), insets.getHeight());
320
	minSize.union(labelSize);
320
	minSize.union(labelSize);
Lines 360-374 Link Here
360
	
360
	
361
	Dimension effectiveSize = getTextSize().getExpanded(-widthShrink, 0);
361
	Dimension effectiveSize = getTextSize().getExpanded(-widthShrink, 0);
362
	Font currentFont = getFont();
362
	Font currentFont = getFont();
363
	int dotsWidth = FigureUtilities.getTextWidth(ELLIPSIS, currentFont);
363
	int dotsWidth = getTextUtilities().getTextExtents(getTruncationString(), currentFont).width;
364
	
364
	
365
	if (effectiveSize.width < dotsWidth)
365
	if (effectiveSize.width < dotsWidth)
366
		effectiveSize.width = dotsWidth;
366
		effectiveSize.width = dotsWidth;
367
	
367
	
368
	int subStringLength = FigureUtilities.getLargestSubstringConfinedTo(text,
368
	int subStringLength = getTextUtilities().getLargestSubstringConfinedTo(text,
369
												  currentFont,
369
												  currentFont,
370
												  effectiveSize.width - dotsWidth);
370
												  effectiveSize.width - dotsWidth);
371
	subStringText = new String(text.substring(0, subStringLength) + ELLIPSIS);
371
	subStringText = new String(text.substring(0, subStringLength) + getTruncationString());
372
	return subStringText;
372
	return subStringText;
373
}
373
}
374
374
Lines 551-557 Link Here
551
 * @since 2.0
551
 * @since 2.0
552
 */
552
 */
553
public void setIconDimension(Dimension d) {
553
public void setIconDimension(Dimension d) {
554
	if (d.equals(iconSize)) 
554
	if (d.equals(getIconSize())) 
555
		return;
555
		return;
556
	iconSize = d;
556
	iconSize = d;
557
	revalidate();
557
	revalidate();
Lines 654-657 Link Here
654
	repaint();
654
	repaint();
655
}
655
}
656
656
657
/**
658
 * Gets the <code>TextUtilities</code> instance to be used in measurement
659
 * calculations.
660
 * 
661
 * @return a <code>TextUtilities</code> instance
662
 * @since 3.2
663
 */
664
public TextUtilities getTextUtilities() {
665
    return TextUtilities.INSTANCE;
666
}
667
668
/**
669
 * Gets the string that will be appended to the text when the label is
670
 * truncated. By default, this returns an ellipsis.
671
 * 
672
 * @return the string to append to the text when truncated
673
 * @since 3.4
674
 */
675
protected String getTruncationString() {
676
    return ELLIPSIS;
677
}
678
679
/**
680
 * Gets the icon size
681
 * 
682
 * @return the icon size
683
 * @since 3.4
684
 */
685
protected Dimension getIconSize() {
686
    return iconSize;
687
}
688
657
}
689
}
(-)src/org/eclipse/draw2d/FigureUtilities.java (-43 / +1 lines)
Lines 1-5 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2005 IBM Corporation and others.
2
 * Copyright (c) 2000, 2007 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
Lines 114-161 Link Here
114
}
114
}
115
115
116
/**
116
/**
117
 * Returns the largest substring of <i>s</i> in Font <i>f</i> that can be confined to the 
118
 * number of pixels in <i>availableWidth<i>.
119
 * 
120
 * @param s the original string
121
 * @param f the font
122
 * @param availableWidth the available width
123
 * @return the largest substring that fits in the given width
124
 * @since 2.0
125
 */
126
static int getLargestSubstringConfinedTo(String s, Font f, int availableWidth) {
127
	FontMetrics metrics = getFontMetrics(f);
128
	int min, max;
129
	float avg = metrics.getAverageCharWidth();
130
	min = 0;
131
	max = s.length() + 1;
132
133
	//The size of the current guess
134
	int guess = 0,
135
	    guessSize = 0;
136
	while ((max - min) > 1) {
137
		//Pick a new guess size
138
		//	New guess is the last guess plus the missing width in pixels
139
		//	divided by the average character size in pixels
140
		guess = guess + (int)((availableWidth - guessSize) / avg);
141
142
		if (guess >= max) guess = max - 1;
143
		if (guess <= min) guess = min + 1;
144
145
		//Measure the current guess
146
		guessSize = getTextExtents(s.substring(0, guess), f).width;
147
148
		if (guessSize < availableWidth)
149
			//We did not use the available width
150
			min = guess;
151
		else
152
			//We exceeded the available width
153
			max = guess;
154
	}
155
	return min;
156
}
157
158
/**
159
 * Returns the Dimensions of the given text, converting newlines and tabs appropriately.
117
 * Returns the Dimensions of the given text, converting newlines and tabs appropriately.
160
 * 
118
 * 
161
 * @param text the text
119
 * @param text the text
(-)src/org/eclipse/draw2d/TextUtilities.java (+126 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2007 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
12
package org.eclipse.draw2d;
13
14
import org.eclipse.swt.graphics.Font;
15
import org.eclipse.swt.graphics.FontMetrics;
16
17
import org.eclipse.draw2d.geometry.Dimension;
18
19
/**
20
 * Provides miscellaneous text operations. Clients may subclass this class if
21
 * necessary.
22
 * 
23
 * @author crevells
24
 * @since 3.4
25
 */
26
public class TextUtilities {
27
28
    /**
29
     * a singleton default instance
30
     */
31
    public static TextUtilities INSTANCE = new TextUtilities();
32
33
    /**
34
     * Returns the Dimensions of <i>s</i> in Font <i>f</i>.
35
     * 
36
     * @param s
37
     *            the string
38
     * @param f
39
     *            the font
40
     * @return the dimensions of the given string
41
     */
42
    public Dimension getStringExtents(String s, Font f) {
43
        return FigureUtilities.getStringExtents(s, f);
44
    }
45
46
    /**
47
     * Returns the Dimensions of the given text, converting newlines and tabs
48
     * appropriately.
49
     * 
50
     * @param s
51
     *            the text
52
     * @param f
53
     *            the font
54
     * @return the dimensions of the given text
55
     */
56
    public Dimension getTextExtents(String s, Font f) {
57
        return FigureUtilities.getTextExtents(s, f);
58
    }
59
60
    /**
61
     * Gets the font's ascent.
62
     * 
63
     * @param font
64
     * @return the font's ascent
65
     */
66
    public int getAscent(Font font) {
67
        FontMetrics fm = FigureUtilities.getFontMetrics(font);
68
        return fm.getHeight() - fm.getDescent();
69
    }
70
71
    /**
72
     * Gets the font's descent.
73
     * 
74
     * @param font
75
     * @return the font's descent
76
     */
77
    public int getDescent(Font font) {
78
        return FigureUtilities.getFontMetrics(font).getDescent();
79
    }
80
81
    /**
82
     * Returns the largest substring of <i>s</i> in Font <i>f</i> that can be
83
     * confined to the number of pixels in <i>availableWidth<i>.
84
     * 
85
     * @param s
86
     *            the original string
87
     * @param f
88
     *            the font
89
     * @param availableWidth
90
     *            the available width
91
     * @return the largest substring that fits in the given width
92
     */
93
    public int getLargestSubstringConfinedTo(String s, Font f,
94
            int availableWidth) {
95
        FontMetrics metrics = FigureUtilities.getFontMetrics(f);
96
        int min, max;
97
        float avg = metrics.getAverageCharWidth();
98
        min = 0;
99
        max = s.length() + 1;
100
101
        // The size of the current guess
102
        int guess = 0, guessSize = 0;
103
        while ((max - min) > 1) {
104
            // Pick a new guess size
105
            // New guess is the last guess plus the missing width in pixels
106
            // divided by the average character size in pixels
107
            guess = guess + (int) ((availableWidth - guessSize) / avg);
108
109
            if (guess >= max)
110
                guess = max - 1;
111
            if (guess <= min)
112
                guess = min + 1;
113
114
            // Measure the current guess
115
            guessSize = getTextExtents(s.substring(0, guess), f).width;
116
117
            if (guessSize < availableWidth)
118
                // We did not use the available width
119
                min = guess;
120
            else
121
                // We exceeded the available width
122
                max = guess;
123
        }
124
        return min;
125
    }
126
}

Return to bug 194278