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 |
} |