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

Collapse All | Expand All

(-)src/org/eclipse/draw2d/text/FlowUtilities.java (-227 / +275 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-36 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.FontMetrics;
19
import org.eclipse.swt.graphics.Rectangle;
18
import org.eclipse.swt.graphics.TextLayout;
20
import org.eclipse.swt.graphics.TextLayout;
19
import org.eclipse.swt.widgets.Display;
21
import org.eclipse.swt.widgets.Display;
20
22
21
import org.eclipse.draw2d.FigureUtilities;
23
import org.eclipse.draw2d.FigureUtilities;
24
import org.eclipse.draw2d.geometry.Dimension;
22
25
23
/**
26
/**
24
 * Utility class for FlowFigures.
27
 * Utility class for FlowFigures.
25
 * @author hudsonr
28
 * @author hudsonr
26
 * @since 2.1
29
 * @since 2.1
27
 */
30
 */
28
class FlowUtilities
31
public class FlowUtilities 
29
	extends FigureUtilities
30
{
32
{
31
33
32
interface LookAhead {
34
interface LookAhead {
33
	int getWidth();
35
    int getWidth();
34
}
36
}
35
private static int ELLIPSIS_SIZE;
37
private static int ELLIPSIS_SIZE;
36
private static final BreakIterator INTERNAL_LINE_BREAK = BreakIterator.getLineInstance();
38
private static final BreakIterator INTERNAL_LINE_BREAK = BreakIterator.getLineInstance();
Lines 39-102 Link Here
39
static final BreakIterator LINE_BREAK = BreakIterator.getLineInstance();
41
static final BreakIterator LINE_BREAK = BreakIterator.getLineInstance();
40
42
41
static boolean canBreakAfter(char c) {
43
static boolean canBreakAfter(char c) {
42
	boolean result = Character.isWhitespace(c) || c == '-';
44
    boolean result = Character.isWhitespace(c) || c == '-';
43
	if (!result && (c < 'a' || c > 'z')) {
45
    if (!result && (c < 'a' || c > 'z')) {
44
		// chinese characters and such would be caught in here
46
        // chinese characters and such would be caught in here
45
		// LINE_BREAK is used here because INTERNAL_LINE_BREAK might be in use
47
        // LINE_BREAK is used here because INTERNAL_LINE_BREAK might be in use
46
		LINE_BREAK.setText(c + "a"); //$NON-NLS-1$
48
        LINE_BREAK.setText(c + "a"); //$NON-NLS-1$
47
		result = LINE_BREAK.isBoundary(1);
49
        result = LINE_BREAK.isBoundary(1);
48
	}
50
    }
49
	return result;
51
    return result;
50
}
52
}
51
53
52
private static int findFirstDelimeter(String string) {
54
private static int findFirstDelimeter(String string) {
53
	int macNL = string.indexOf('\r');
55
    int macNL = string.indexOf('\r');
54
	int unixNL = string.indexOf('\n');
56
    int unixNL = string.indexOf('\n');
55
	
57
    
56
	if (macNL == -1)
58
    if (macNL == -1)
57
		macNL = Integer.MAX_VALUE;
59
        macNL = Integer.MAX_VALUE;
58
	if (unixNL == -1)
60
    if (unixNL == -1)
59
		unixNL = Integer.MAX_VALUE;
61
        unixNL = Integer.MAX_VALUE;
60
62
61
	return Math.min(macNL, unixNL);
63
    return Math.min(macNL, unixNL);
62
}
64
}
63
65
64
private static float getAverageCharWidth(TextFragmentBox fragment, Font font) {
66
/**
65
	if (fragment.getWidth() > 0 && fragment.length != 0)
67
 * Gets the average character width.
66
		return fragment.getWidth() / (float)fragment.length;
68
 * 
67
	return getFontMetrics(font).getAverageCharWidth();
69
 * @param fragment the supplied TextFragmentBox to use for calculation.
70
 *                 if the length is 0 or if the width is or below 0,
71
 *                 the average character width is taken from standard 
72
 *                 font metrics.
73
 * @param font     the font to use in case the TextFragmentBox conditions 
74
 *                 above are true.
75
 * @return         the average character width
76
 */
77
protected float getAverageCharWidth(TextFragmentBox fragment, Font font) {
78
    if (fragment.getWidth() > 0 && fragment.length != 0)
79
        return fragment.getWidth() / (float)fragment.length;
80
    return FigureUtilities.getFontMetrics(font).getAverageCharWidth();
68
}
81
}
69
82
70
static int getBorderAscent(InlineFlow owner) {
83
static int getBorderAscent(InlineFlow owner) {
71
	if (owner.getBorder() instanceof FlowBorder) {
84
    if (owner.getBorder() instanceof FlowBorder) {
72
		FlowBorder border = (FlowBorder)owner.getBorder();
85
        FlowBorder border = (FlowBorder)owner.getBorder();
73
		return border.getInsets(owner).top;
86
        return border.getInsets(owner).top;
74
	}
87
    }
75
	return 0;
88
    return 0;
76
}
89
}
77
90
78
static int getBorderAscentWithMargin(InlineFlow owner) {
91
static int getBorderAscentWithMargin(InlineFlow owner) {
79
	if (owner.getBorder() instanceof FlowBorder) {
92
    if (owner.getBorder() instanceof FlowBorder) {
80
		FlowBorder border = (FlowBorder)owner.getBorder();
93
        FlowBorder border = (FlowBorder)owner.getBorder();
81
		return border.getTopMargin() + border.getInsets(owner).top;
94
        return border.getTopMargin() + border.getInsets(owner).top;
82
	}
95
    }
83
	return 0;
96
    return 0;
84
}
97
}
85
98
86
static int getBorderDescent(InlineFlow owner) {
99
static int getBorderDescent(InlineFlow owner) {
87
	if (owner.getBorder() instanceof FlowBorder) {
100
    if (owner.getBorder() instanceof FlowBorder) {
88
		FlowBorder border = (FlowBorder)owner.getBorder();
101
        FlowBorder border = (FlowBorder)owner.getBorder();
89
		return border.getInsets(owner).bottom;
102
        return border.getInsets(owner).bottom;
90
	}
103
    }
91
	return 0;
104
    return 0;
92
}
105
}
93
106
94
static int getBorderDescentWithMargin(InlineFlow owner) {
107
static int getBorderDescentWithMargin(InlineFlow owner) {
95
	if (owner.getBorder() instanceof FlowBorder) {
108
    if (owner.getBorder() instanceof FlowBorder) {
96
		FlowBorder border = (FlowBorder)owner.getBorder();
109
        FlowBorder border = (FlowBorder)owner.getBorder();
97
		return border.getBottomMargin() + border.getInsets(owner).bottom;
110
        return border.getBottomMargin() + border.getInsets(owner).bottom;
98
	}
111
    }
99
	return 0;
112
    return 0;
100
}
113
}
101
114
102
/**
115
/**
Lines 108-117 Link Here
108
 * @since 3.1
121
 * @since 3.1
109
 */
122
 */
110
static TextLayout getTextLayout() {
123
static TextLayout getTextLayout() {
111
	if (layout == null)
124
    if (layout == null)
112
		layout = new TextLayout(Display.getDefault());
125
        layout = new TextLayout(Display.getDefault());
113
	layout.setOrientation(SWT.LEFT_TO_RIGHT);
126
    layout.setOrientation(SWT.LEFT_TO_RIGHT);
114
	return layout;
127
    return layout;
115
}
128
}
116
129
117
/**
130
/**
Lines 121-162 Link Here
121
 * @since 3.1
134
 * @since 3.1
122
 */
135
 */
123
private static void initBidi(TextFragmentBox frag, String string, Font font) {
136
private static void initBidi(TextFragmentBox frag, String string, Font font) {
124
	if (frag.requiresBidi()) {
137
    if (frag.requiresBidi()) {
125
		TextLayout textLayout = getTextLayout();
138
        TextLayout textLayout = getTextLayout();
126
		textLayout.setFont(font);
139
        textLayout.setFont(font);
127
		//$TODO need to insert overrides in front of string.
140
        //$TODO need to insert overrides in front of string.
128
		textLayout.setText(string);
141
        textLayout.setText(string);
129
	}
142
    }
130
}
143
}
131
144
132
private static int measureString(TextFragmentBox frag, String string, int guess, Font font) {
145
private int measureString(TextFragmentBox frag, String string, int guess, Font font) {
133
	if (frag.requiresBidi()) {
146
    if (frag.requiresBidi()) {
134
		// The text and/or could have changed if the lookAhead was invoked.  This will
147
        // The text and/or could have changed if the lookAhead was invoked.  This will
135
		// happen at most once.
148
        // happen at most once.
136
		TextLayout layout = getTextLayout();
149
        return getTextLayoutBounds(string, font, 0, guess - 1).width;
137
		layout.setText(string);
150
    } else
138
		layout.setFont(font);
151
        return getStringExtents(string.substring(0, guess), font).width;
139
		return layout.getBounds(0, guess - 1).width;
152
}
140
	} else
153
141
		return getStringDimension(string.substring(0, guess), font).x;
154
void setupFragment(TextFragmentBox frag, Font f, String s) {
142
}
155
    if (frag.getWidth() == -1 || frag.isTruncated()) {
143
156
        int width;
144
static void setupFragment(TextFragmentBox frag, Font f, String s) {
157
        if (s.length() == 0 || frag.length == 0)
145
	if (frag.getWidth() == -1 || frag.isTruncated()) {
158
            width = 0;
146
		int width;
159
        else if (frag.requiresBidi()) {
147
		if (s.length() == 0 || frag.length == 0)
160
            width = getTextLayoutBounds(s, f, 0, frag.length - 1).width;
148
			width = 0;
161
        } else
149
		else if (frag.requiresBidi()) {
162
            width = getStringExtents(s.substring(0, frag.length), f).width;
150
			TextLayout textLayout = getTextLayout();
163
        if (frag.isTruncated())
151
			textLayout.setFont(f);
164
            width += ELLIPSIS_SIZE;
152
			textLayout.setText(s);
165
        frag.setWidth(width);
153
			width = textLayout.getBounds(0, frag.length - 1).width;
166
    }
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
}
167
}
161
168
162
/**
169
/**
Lines 172-302 Link Here
172
 * @return the number of characters that will fit in the given space; can be 0 (eg., when
179
 * @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)
180
 * the first character of the given string is a newline)
174
 */
181
 */
175
public static int wrapFragmentInContext(TextFragmentBox frag, String string,
182
public int wrapFragmentInContext(TextFragmentBox frag, String string,
176
		FlowContext context, LookAhead lookahead, Font font, int wrapping) {
183
        FlowContext context, LookAhead lookahead, Font font, int wrapping) {
177
	frag.setTruncated(false);
184
    frag.setTruncated(false);
178
	int strLen = string.length();
185
    int strLen = string.length();
179
	if (strLen == 0) {
186
    if (strLen == 0) {
180
		frag.setWidth(-1);
187
        frag.setWidth(-1);
181
		frag.length = 0;
188
        frag.length = 0;
182
		setupFragment(frag, font, string);
189
        setupFragment(frag, font, string);
183
		context.addToCurrentLine(frag);
190
        context.addToCurrentLine(frag);
184
		return 0;
191
        return 0;
185
	}
192
    }
186
	
193
    
187
	INTERNAL_LINE_BREAK.setText(string);
194
    INTERNAL_LINE_BREAK.setText(string);
188
195
189
	initBidi(frag, string, font);
196
    initBidi(frag, string, font);
190
	float avgCharWidth = getAverageCharWidth(frag, font);
197
    float avgCharWidth = getAverageCharWidth(frag, font);
191
	frag.setWidth(-1);
198
    frag.setWidth(-1);
192
	
199
    
193
	/*
200
    /*
194
	 * Setup initial boundaries within the string.
201
     * Setup initial boundaries within the string.
195
	 */
202
     */
196
	int absoluteMin = 0;
203
    int absoluteMin = 0;
197
	int max, min = 1;
204
    int max, min = 1;
198
	if (wrapping == ParagraphTextLayout.WORD_WRAP_HARD) {
205
    if (wrapping == ParagraphTextLayout.WORD_WRAP_HARD) {
199
		absoluteMin = INTERNAL_LINE_BREAK.next();
206
        absoluteMin = INTERNAL_LINE_BREAK.next();
200
		while (absoluteMin > 0 && Character.isWhitespace(string.charAt(absoluteMin - 1)))
207
        while (absoluteMin > 0 && Character.isWhitespace(string.charAt(absoluteMin - 1)))
201
			absoluteMin--;
208
            absoluteMin--;
202
		min = Math.max(absoluteMin, 1);
209
        min = Math.max(absoluteMin, 1);
203
	}
210
    }
204
	int firstDelimiter = findFirstDelimeter(string);
211
    int firstDelimiter = findFirstDelimeter(string);
205
	if (firstDelimiter == 0)
212
    if (firstDelimiter == 0)
206
		min = max = 0;
213
        min = max = 0;
207
	else
214
    else
208
		max = Math.min(strLen, firstDelimiter) + 1;
215
        max = Math.min(strLen, firstDelimiter) + 1;
209
216
210
	
217
    
211
	int availableWidth = context.getRemainingLineWidth();
218
    int availableWidth = context.getRemainingLineWidth();
212
	int guess = 0, guessSize = 0;
219
    int guess = 0, guessSize = 0;
213
	
220
    
214
	while (true) {
221
    while (true) {
215
		if ((max - min) <= 1) {
222
        if ((max - min) <= 1) {
216
			if (min == absoluteMin
223
            if (min == absoluteMin
217
					&& context.isCurrentLineOccupied() 
224
                    && context.isCurrentLineOccupied() 
218
					&& !context.getContinueOnSameLine()
225
                    && !context.getContinueOnSameLine()
219
					&& availableWidth < measureString(frag, string, min, font)
226
                    && availableWidth < measureString(frag, string, min, font)
220
						+ ((min == strLen && lookahead != null) ? lookahead.getWidth() : 0)
227
                        + ((min == strLen && lookahead != null) ? lookahead.getWidth() : 0)
221
			) {
228
            ) {
222
				context.endLine();
229
                context.endLine();
223
				availableWidth = context.getRemainingLineWidth();
230
                availableWidth = context.getRemainingLineWidth();
224
				max = Math.min(strLen, firstDelimiter) + 1;
231
                max = Math.min(strLen, firstDelimiter) + 1;
225
				if ((max - min) <= 1)
232
                if ((max - min) <= 1)
226
					break;
233
                    break;
227
			} else
234
            } else
228
				break;
235
                break;
229
		}
236
        }
230
		// Pick a new guess size
237
        // Pick a new guess size
231
		// New guess is the last guess plus the missing width in pixels
238
        // New guess is the last guess plus the missing width in pixels
232
		// divided by the average character size in pixels
239
        // divided by the average character size in pixels
233
		guess += 0.5f + (availableWidth - guessSize) / avgCharWidth;
240
        guess += 0.5f + (availableWidth - guessSize) / avgCharWidth;
234
241
235
		if (guess >= max) guess = max - 1;
242
        if (guess >= max) guess = max - 1;
236
		if (guess <= min) guess = min + 1;
243
        if (guess <= min) guess = min + 1;
237
244
238
		guessSize = measureString(frag, string, guess, font);
245
        guessSize = measureString(frag, string, guess, font);
239
		
246
        
240
		if (guess == strLen
247
        if (guess == strLen
241
				&& lookahead != null
248
                && lookahead != null
242
				&& !canBreakAfter(string.charAt(strLen - 1))
249
                && !canBreakAfter(string.charAt(strLen - 1))
243
				&& guessSize + lookahead.getWidth() > availableWidth) {
250
                && guessSize + lookahead.getWidth() > availableWidth) {
244
			max = guess;
251
            max = guess;
245
			continue;
252
            continue;
246
		}
253
        }
247
254
248
		if (guessSize <= availableWidth) {
255
        if (guessSize <= availableWidth) {
249
			min = guess;
256
            min = guess;
250
			frag.setWidth(guessSize);
257
            frag.setWidth(guessSize);
251
			if (guessSize == availableWidth)
258
            if (guessSize == availableWidth)
252
				max = guess + 1;
259
                max = guess + 1;
253
		} else
260
        } else
254
			max = guess;
261
            max = guess;
255
	}
262
    }
256
	
263
    
257
	int result = min;
264
    int result = min;
258
	boolean continueOnLine = false;
265
    boolean continueOnLine = false;
259
	if (min == strLen) {
266
    if (min == strLen) {
260
		//Everything fits
267
        //Everything fits
261
		if (string.charAt(strLen - 1) == ' ') {
268
        if (string.charAt(strLen - 1) == ' ') {
262
			if (frag.getWidth() == -1) {
269
            if (frag.getWidth() == -1) {
263
				frag.length = result;
270
                frag.length = result;
264
				frag.setWidth(measureString(frag, string, result, font));
271
                frag.setWidth(measureString(frag, string, result, font));
265
			}
272
            }
266
			if (lookahead.getWidth() > availableWidth - frag.getWidth()) {
273
            if (lookahead.getWidth() > availableWidth - frag.getWidth()) {
267
				frag.length = result - 1;
274
                frag.length = result - 1;
268
				frag.setWidth(-1);
275
                frag.setWidth(-1);
269
			} else
276
            } else
270
				frag.length = result;
277
                frag.length = result;
271
		} else {
278
        } else {
272
			continueOnLine = !canBreakAfter(string.charAt(strLen - 1));
279
            continueOnLine = !canBreakAfter(string.charAt(strLen - 1));
273
			frag.length = result;
280
            frag.length = result;
274
		}
281
        }
275
	} else if (min == firstDelimiter) {
282
    } else if (min == firstDelimiter) {
276
		//move result past the delimiter
283
        //move result past the delimiter
277
		frag.length = result;
284
        frag.length = result;
278
		if (string.charAt(min) == '\r') {
285
        if (string.charAt(min) == '\r') {
279
			result++;
286
            result++;
280
			if (++min < strLen && string.charAt(min) == '\n')
287
            if (++min < strLen && string.charAt(min) == '\n')
281
				result++;
288
                result++;
282
		} else if (string.charAt(min) == '\n')
289
        } else if (string.charAt(min) == '\n')
283
			result++;
290
            result++;
284
	} else if (string.charAt(min) == ' '
291
    } else if (string.charAt(min) == ' '
285
			|| canBreakAfter(string.charAt(min - 1))
292
            || canBreakAfter(string.charAt(min - 1))
286
			|| INTERNAL_LINE_BREAK.isBoundary(min)) {
293
            || INTERNAL_LINE_BREAK.isBoundary(min)) {
287
		frag.length = min;
294
        frag.length = min;
288
		if (string.charAt(min) == ' ')
295
        if (string.charAt(min) == ' ')
289
			result++;
296
            result++;
290
		else if (string.charAt(min - 1) == ' ') {
297
        else if (string.charAt(min - 1) == ' ') {
291
			frag.length--;
298
            frag.length--;
292
			frag.setWidth(-1);
299
            frag.setWidth(-1);
293
		}
300
        }
294
	} else out: {
301
    } else out: {
295
		// In the middle of an unbreakable offset
302
        // In the middle of an unbreakable offset
296
		result = INTERNAL_LINE_BREAK.preceding(min);
303
        result = INTERNAL_LINE_BREAK.preceding(min);
297
		if (result == 0) {
304
        if (result == 0) {
298
			switch (wrapping) {
305
            switch (wrapping) {
299
				case ParagraphTextLayout.WORD_WRAP_TRUNCATE :
306
                case ParagraphTextLayout.WORD_WRAP_TRUNCATE :
300
					ELLIPSIS_SIZE = FigureUtilities
307
					ELLIPSIS_SIZE = FigureUtilities
301
							.getStringExtents(TextFlow.ELLIPSIS, font).width;
308
							.getStringExtents(TextFlow.ELLIPSIS, font).width;
302
					int truncatedWidth = availableWidth - ELLIPSIS_SIZE;
309
					int truncatedWidth = availableWidth - ELLIPSIS_SIZE;
Lines 312-335 Link Here
312
					} else
319
					} else
313
						frag.length = 0;
320
						frag.length = 0;
314
					frag.setTruncated(true);
321
					frag.setTruncated(true);
315
					result = INTERNAL_LINE_BREAK.following(max - 1);
322
                    result = INTERNAL_LINE_BREAK.following(max - 1);
316
					break out;
323
                    break out;
324
325
                default:
326
                    result = min;
327
                    break;
328
            }
329
        }
330
        frag.length = result;
331
        if (string.charAt(result - 1) == ' ')
332
            frag.length--;
333
        frag.setWidth(-1);
334
    }
335
    
336
    setupFragment(frag, font, string);
337
    context.addToCurrentLine(frag);
338
    context.setContinueOnSameLine(continueOnLine);
339
    return result;
340
}
317
341
318
				default:
342
/**
319
					result = min;
343
 * Returns the Dimensions of <i>s</i> in Font <i>f</i>.
320
					break;
344
 * 
321
			}
345
 * @param s the string
322
		}
346
 * @param f the font
323
		frag.length = result;
347
 * @return the dimensions of the given string
324
		if (string.charAt(result - 1) == ' ')
348
 * @since 2.0
325
			frag.length--;
349
 */
326
		frag.setWidth(-1);
350
public Dimension getStringExtents(String s, Font f) {
327
	}
351
    return FigureUtilities.getStringExtents(s, f);
328
	
352
}
329
	setupFragment(frag, font, string);
353
330
	context.addToCurrentLine(frag);
354
/**
331
	context.setContinueOnSameLine(continueOnLine);
355
 * @see TextLayout#getBounds()
332
	return result;
356
 */
357
protected Rectangle getTextLayoutBounds(String s, Font f, int start, int end) {
358
    TextLayout textLayout = getTextLayout();
359
    textLayout.setFont(f);
360
    textLayout.setText(s);
361
    return textLayout.getBounds(start, end);
362
}
363
364
/**
365
 * Gets the font's ascent.
366
 * @param font
367
 * @return the font's ascent
368
 */
369
public int getAscent(Font font) {
370
    FontMetrics fm = FigureUtilities.getFontMetrics(font);
371
    return fm.getHeight() - fm.getDescent();
372
}
373
374
/**
375
 * Gets the font's descent.
376
 * @param font
377
 * @return the font's descent
378
 */
379
public int getDescent(Font font) {
380
    return FigureUtilities.getFontMetrics(font).getDescent();
333
}
381
}
334
382
335
}
383
}
(-)src/org/eclipse/draw2d/text/TextFlow.java (-11 / +18 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-22 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;
21
import org.eclipse.draw2d.geometry.Dimension;
19
import org.eclipse.draw2d.geometry.Dimension;
22
import org.eclipse.draw2d.geometry.Point;
20
import org.eclipse.draw2d.geometry.Point;
Lines 36-45 Link Here
36
	extends InlineFlow 
34
	extends InlineFlow 
37
{
35
{
38
36
39
static final String ELLIPSIS = "..."; //$NON-NLS-1$
37
public static final String ELLIPSIS = "..."; //$NON-NLS-1$
40
private BidiInfo bidiInfo;
38
private BidiInfo bidiInfo;
41
private int selectionEnd = -1;
39
private int selectionEnd = -1;
42
private String text;
40
private String text;
41
private FlowUtilities flowUtilities = new FlowUtilities();
42
43
43
44
/**
44
/**
45
 * Constructs a new TextFlow with the empty String.
45
 * Constructs a new TextFlow with the empty String.
Lines 95-101 Link Here
95
	text = text.substring(1, index + 1);
95
	text = text.substring(1, index + 1);
96
96
97
	if (bidiInfo == null)
97
	if (bidiInfo == null)
98
		width[0] += FlowUtilities.getStringExtents(text, getFont()).width;
98
		width[0] += flowUtilities.getStringExtents(text, getFont()).width;
99
	else {
99
	else {
100
		TextLayout textLayout = FlowUtilities.getTextLayout();
100
		TextLayout textLayout = FlowUtilities.getTextLayout();
101
		textLayout.setFont(getFont());
101
		textLayout.setFont(getFont());
Lines 174-182 Link Here
174
	return findOffset(p, trailing, closestBox, index);
174
	return findOffset(p, trailing, closestBox, index);
175
}
175
}
176
176
177
int getAscent() {
177
protected int getAscent() {
178
	FontMetrics fm = FigureUtilities.getFontMetrics(getFont());
178
    return getFlowUtilities().getAscent(getFont());
179
	return fm.getHeight() - fm.getDescent();
180
}
179
}
181
180
182
/**
181
/**
Lines 267-273 Link Here
267
		if (trailing && offset < box.length)
266
		if (trailing && offset < box.length)
268
			offset++;
267
			offset++;
269
		String substring = getText().substring(box.offset, box.offset + offset);
268
		String substring = getText().substring(box.offset, box.offset + offset);
270
		result.x = FigureUtilities.getStringExtents(substring, getFont()).width;
269
		result.x = getFlowUtilities().getStringExtents(substring, getFont()).width;
271
	} else {
270
	} else {
272
		TextLayout layout = FlowUtilities.getTextLayout();
271
		TextLayout layout = FlowUtilities.getTextLayout();
273
		layout.setFont(getFont());
272
		layout.setFont(getFont());
Lines 282-289 Link Here
282
	return result;
281
	return result;
283
}
282
}
284
283
285
int getDescent() {
284
protected int getDescent() {
286
	return FigureUtilities.getFontMetrics(getFont()).getDescent();
285
    return getFlowUtilities().getDescent(getFont());
287
}
286
}
288
287
289
/**
288
/**
Lines 629-632 Link Here
629
	return Math.max(0, y - (box.getBaseline() + box.getLineRoot().getDescent()));
628
	return Math.max(0, y - (box.getBaseline() + box.getLineRoot().getDescent()));
630
}
629
}
631
630
631
public FlowUtilities getFlowUtilities() {
632
    return flowUtilities;
633
}
634
635
public void setFlowUtilities(FlowUtilities flowUtilities) {
636
    this.flowUtilities = flowUtilities;
637
}
638
632
}
639
}
(-)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
}

Return to bug 194278