Mercurial > projects > dwt-addons
comparison dwtx/draw2d/text/TextFlow.d @ 98:95307ad235d9
Added Draw2d code, still work in progress
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sun, 03 Aug 2008 00:52:14 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
96:b492ba44e44d | 98:95307ad235d9 |
---|---|
1 /******************************************************************************* | |
2 * Copyright (c) 2000, 2008 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 * Port to the D programming language: | |
11 * Frank Benoit <benoit@tionex.de> | |
12 *******************************************************************************/ | |
13 module dwtx.draw2d.text.TextFlow; | |
14 | |
15 import dwt.dwthelper.utils; | |
16 import dwtx.dwtxhelper.Collection; | |
17 | |
18 import dwt.DWT; | |
19 import dwt.graphics.Color; | |
20 import dwt.graphics.TextLayout; | |
21 import dwtx.draw2d.ColorConstants; | |
22 import dwtx.draw2d.Graphics; | |
23 import dwtx.draw2d.TextUtilities; | |
24 import dwtx.draw2d.geometry.Dimension; | |
25 import dwtx.draw2d.geometry.Point; | |
26 import dwtx.draw2d.geometry.Rectangle; | |
27 import dwtx.draw2d.text.InlineFlow; | |
28 import dwtx.draw2d.text.BidiInfo; | |
29 import dwtx.draw2d.text.BidiProcessor; | |
30 import dwtx.draw2d.text.FlowFigureLayout; | |
31 import dwtx.draw2d.text.TextFragmentBox; | |
32 import dwtx.draw2d.text.CaretInfo; | |
33 import dwtx.draw2d.text.FlowUtilities; | |
34 import dwtx.draw2d.text.ParagraphTextLayout; | |
35 import dwtx.draw2d.text.BidiChars; | |
36 import dwtx.draw2d.text.FlowBorder; | |
37 | |
38 import tango.text.convert.Format; | |
39 | |
40 /** | |
41 * An inline flow figure that renders a string of text across one or more lines. A | |
42 * TextFlow cannot contain children. All <code>InlineFlow</code> figure's must be | |
43 * parented by a <code>FlowFigure</code>. | |
44 * <p> | |
45 * WARNING: This class is not intended to be subclassed by clients. | |
46 * @author hudsonr | |
47 * @author Pratik Shah | |
48 * @since 2.1 | |
49 */ | |
50 public class TextFlow | |
51 : InlineFlow | |
52 { | |
53 | |
54 static final String ELLIPSIS = "..."; //$NON-NLS-1$ | |
55 private BidiInfo bidiInfo; | |
56 private int selectionEnd = -1; | |
57 private String text; | |
58 | |
59 /** | |
60 * Constructs a new TextFlow with the empty String. | |
61 * @see java.lang.Object#Object() | |
62 */ | |
63 public this() { | |
64 this(""/+new String()+/); | |
65 } | |
66 | |
67 /** | |
68 * Constructs a new TextFlow with the specified String. | |
69 * @param s the string | |
70 */ | |
71 public this(String s) { | |
72 text = s; | |
73 } | |
74 | |
75 /** | |
76 * Returns the width of the text until the first line-break. | |
77 * @see dwtx.draw2d.text.FlowFigure#addLeadingWordRequirements(int[]) | |
78 */ | |
79 public bool addLeadingWordRequirements(int[] width) { | |
80 return addLeadingWordWidth(getText(), width); | |
81 } | |
82 | |
83 /** | |
84 * Calculates the width taken up by the given text before a line-break is encountered. | |
85 * | |
86 * @param text the text in which the break is to be found | |
87 * @param width the width before the next line-break (if one's found; the width of all | |
88 * the given text, otherwise) will be added on to the first int in the given array | |
89 * @return <code>true</code> if a line-break was found | |
90 * @since 3.1 | |
91 */ | |
92 bool addLeadingWordWidth(String text, int[] width) { | |
93 if (text.length is 0) | |
94 return false; | |
95 if (CharacterIsWhitespace(text.firstCodePoint())) | |
96 return true; | |
97 | |
98 text = 'a' ~ text ~ 'a'; | |
99 FlowUtilities.LINE_BREAK.setText(text); | |
100 int index = FlowUtilities.LINE_BREAK.next() - 1; | |
101 if (index is 0) | |
102 return true; | |
103 while (CharacterIsWhitespace(text[index..$].firstCodePoint())) | |
104 index--; | |
105 bool result = index < text.length - 1; | |
106 // index should point to the end of the actual text (not including the 'a' that was | |
107 // appended), if there were no breaks | |
108 if (index is text.length - 1) | |
109 index--; | |
110 text = text.substring(1, index + 1); | |
111 | |
112 if (bidiInfo is null) | |
113 width[0] += getTextUtilities().getStringExtents(text, getFont()).width; | |
114 else { | |
115 TextLayout textLayout = FlowUtilities.getTextLayout(); | |
116 textLayout.setFont(getFont()); | |
117 textLayout.setText(text); | |
118 width[0] += textLayout.getBounds().width; | |
119 } | |
120 return result; | |
121 } | |
122 | |
123 /** | |
124 * A TextFlow contributes its text. | |
125 * @see dwtx.draw2d.text.FlowFigure#contributeBidi(dwtx.draw2d.text.BidiProcessor) | |
126 */ | |
127 protected void contributeBidi(BidiProcessor proc) { | |
128 bidiInfo = null; | |
129 proc.add(this, getText()); | |
130 } | |
131 | |
132 /** | |
133 * @see dwtx.draw2d.text.InlineFlow#createDefaultFlowLayout() | |
134 */ | |
135 protected FlowFigureLayout createDefaultFlowLayout() { | |
136 return new ParagraphTextLayout(this); | |
137 } | |
138 | |
139 private int findNextLineOffset(Point p, int[] trailing) { | |
140 if (getBounds().bottom() <= p.y) | |
141 return -1; | |
142 | |
143 TextFragmentBox closestBox = null; | |
144 int index = 0; | |
145 List fragments = getFragmentsWithoutBorder(); | |
146 for (int i = fragments.size() - 1; i >= 0; i--) { | |
147 TextFragmentBox box = cast(TextFragmentBox)fragments.get(i); | |
148 if (box.getBaseline() - box.getLineRoot().getAscent() > p.y | |
149 && (closestBox is null | |
150 || box.getBaseline() < closestBox.getBaseline() | |
151 || (box.getBaseline() is closestBox.getBaseline() | |
152 && hDistanceBetween(box, p.x) < hDistanceBetween(closestBox, p.x)))) { | |
153 closestBox = box; | |
154 index = i; | |
155 } | |
156 } | |
157 return findOffset(p, trailing, closestBox, index); | |
158 } | |
159 | |
160 private int findOffset(Point p, int[] trailing, TextFragmentBox box, int boxIndex) { | |
161 if (box is null) | |
162 return -1; | |
163 TextLayout layout = FlowUtilities.getTextLayout(); | |
164 layout.setFont(getFont()); | |
165 layout.setText(getBidiSubstring(box, boxIndex)); | |
166 int x = p.x - box.getX(); | |
167 if (isMirrored()) | |
168 x = box.getWidth() - x; | |
169 int layoutOffset = layout.getOffset(x, p.y - box.getTextTop(), trailing); | |
170 return box.offset + layoutOffset - getBidiPrefixLength(box, boxIndex); | |
171 } | |
172 | |
173 private int findPreviousLineOffset(Point p, int[] trailing) { | |
174 if (getBounds().y > p.y) | |
175 return -1; | |
176 | |
177 TextFragmentBox closestBox = null; | |
178 int index = 0; | |
179 List fragments = getFragmentsWithoutBorder(); | |
180 for (int i = fragments.size() - 1; i >= 0; i--) { | |
181 TextFragmentBox box = cast(TextFragmentBox)fragments.get(i); | |
182 if (box.getBaseline() + box.getLineRoot().getDescent() < p.y | |
183 && (closestBox is null | |
184 || box.getBaseline() > closestBox.getBaseline() | |
185 || (box.getBaseline() is closestBox.getBaseline() | |
186 && hDistanceBetween(box, p.x) < hDistanceBetween(closestBox, p.x)))) { | |
187 closestBox = box; | |
188 index = i; | |
189 } | |
190 } | |
191 return findOffset(p, trailing, closestBox, index); | |
192 } | |
193 | |
194 int getAscent() { | |
195 return getTextUtilities().getAscent(getFont()); | |
196 } | |
197 | |
198 /** | |
199 * Returns the BidiInfo for this figure or <code>null</code>. | |
200 * @return <code>null</code> or the info | |
201 * @since 3.1 | |
202 */ | |
203 public BidiInfo getBidiInfo() { | |
204 return bidiInfo; | |
205 } | |
206 | |
207 private int getBidiPrefixLength(TextFragmentBox box, int index) { | |
208 if (box.getBidiLevel() < 1) | |
209 return 0; | |
210 if (index > 0 || !bidiInfo.leadingJoiner) | |
211 return 1; | |
212 return 2; | |
213 } | |
214 | |
215 /** | |
216 * @param box which fragment | |
217 * @param index the fragment index | |
218 * @return the bidi string for that fragment | |
219 * @since 3.1 | |
220 */ | |
221 protected String getBidiSubstring(TextFragmentBox box, int index) { | |
222 if (box.getBidiLevel() < 1) | |
223 return getText().substring(box.offset, box.offset + box.length); | |
224 | |
225 StringBuffer buffer = new StringBuffer(box.length + 3); | |
226 buffer.append( dcharToString( box.isRightToLeft() ? BidiChars.RLO : BidiChars.LRO )); | |
227 if (index is 0 && bidiInfo.leadingJoiner) | |
228 buffer.append(dcharToString(BidiChars.ZWJ)); | |
229 buffer.append(getText().substring(box.offset, box.offset + box.length)); | |
230 if (index is getFragmentsWithoutBorder().size() - 1 && bidiInfo.trailingJoiner) | |
231 buffer.append(dcharToString(BidiChars.ZWJ)); | |
232 return buffer.toString(); | |
233 } | |
234 | |
235 /** | |
236 * Returns the CaretInfo in absolute coordinates. The offset must be between 0 and the | |
237 * length of the String being displayed. | |
238 * @since 3.1 | |
239 * @param offset the location in this figure's text | |
240 * @param trailing true if the caret is being placed after the offset | |
241 * @exception IllegalArgumentException If the offset is not between <code>0</code> and the | |
242 * length of the string inclusively | |
243 * @return the caret bounds relative to this figure | |
244 */ | |
245 public CaretInfo getCaretPlacement(int offset, bool trailing) { | |
246 if (offset < 0 || offset > getText().length) | |
247 throw new IllegalArgumentException(Format("Offset: {} is invalid", offset //$NON-NLS-1$ | |
248 )); //$NON-NLS-1$ | |
249 | |
250 if (offset is getText().length) | |
251 trailing = false; | |
252 | |
253 List fragments = getFragmentsWithoutBorder(); | |
254 int i = fragments.size(); | |
255 TextFragmentBox box; | |
256 do | |
257 box = cast(TextFragmentBox)fragments.get(--i); | |
258 while (offset < box.offset && i > 0); | |
259 | |
260 // Cannot be trailing and after the last char, so go to first char in next box | |
261 if (trailing && box.offset + box.length <= offset) { | |
262 box = cast(TextFragmentBox)fragments.get(++i); | |
263 offset = box.offset; | |
264 trailing = false; | |
265 } | |
266 | |
267 Point where = getPointInBox(box, offset, i, trailing); | |
268 CaretInfo info = new CaretInfo(where.x, where.y, box.getAscent(), box.getDescent(), | |
269 box.getLineRoot().getAscent(), box.getLineRoot().getDescent()); | |
270 translateToAbsolute(info); | |
271 return info; | |
272 } | |
273 | |
274 Point getPointInBox(TextFragmentBox box, int offset, int index, bool trailing) { | |
275 offset -= box.offset; | |
276 offset = Math.min(box.length, offset); | |
277 Point result = new Point(0, box.getTextTop()); | |
278 if (bidiInfo is null) { | |
279 if (trailing && offset < box.length) | |
280 offset++; | |
281 String substring = getText().substring(box.offset, box.offset + offset); | |
282 result.x = getTextUtilities().getStringExtents(substring, getFont()).width; | |
283 } else { | |
284 TextLayout layout = FlowUtilities.getTextLayout(); | |
285 layout.setFont(getFont()); | |
286 String fragString = getBidiSubstring(box, index); | |
287 layout.setText(fragString); | |
288 offset += getBidiPrefixLength(box, index); | |
289 result.x = layout.getLocation(offset, trailing).x; | |
290 if (isMirrored()) | |
291 result.x = box.width - result.x; | |
292 } | |
293 result.x += box.getX(); | |
294 return result; | |
295 } | |
296 | |
297 int getDescent() { | |
298 return getTextUtilities().getDescent(getFont()); | |
299 } | |
300 | |
301 /** | |
302 * Returns the minimum character offset which is on the given baseline y-coordinate. The y | |
303 * location should be relative to this figure. The return value will be between | |
304 * 0 and N-1. If no fragment is located on the baseline, <code>-1</code> is returned. | |
305 * @since 3.1 | |
306 * @param baseline the relative baseline coordinate | |
307 * @return -1 or the lowest offset for the line | |
308 */ | |
309 public int getFirstOffsetForLine(int baseline) { | |
310 TextFragmentBox box; | |
311 List fragments = getFragmentsWithoutBorder(); | |
312 for (int i = 0; i < fragments.size(); i++) { | |
313 box = cast(TextFragmentBox)fragments.get(i); | |
314 if (baseline is box.getBaseline()) | |
315 return box.offset; | |
316 } | |
317 return -1; | |
318 } | |
319 | |
320 /** | |
321 * Returns the <code>TextFragmentBox</code> fragments contained in this TextFlow, not | |
322 * including the border fragments. The returned list should not be modified. | |
323 * @return list of fragments without the border fragments | |
324 * @since 3.4 | |
325 */ | |
326 protected List getFragmentsWithoutBorder() { | |
327 List fragments = getFragments(); | |
328 if (getBorder() !is null) | |
329 fragments = fragments.subList(1, fragments.size() - 1); | |
330 return fragments; | |
331 } | |
332 | |
333 /** | |
334 * Returns the maximum offset for a character which is on the given baseline y-coordinate. | |
335 * The y location should be relative to this figure. The return value will be between | |
336 * 0 and N-1. If no fragment is located on the baseline, <code>-1</code> is returned. | |
337 * @since 3.1 | |
338 * @param baseline the relative baseline coordinate | |
339 * @return -1 or the highest offset at the given baseline | |
340 */ | |
341 public int getLastOffsetForLine(int baseline) { | |
342 TextFragmentBox box; | |
343 List fragments = getFragmentsWithoutBorder(); | |
344 for (int i = fragments.size() - 1; i >= 0; i--) { | |
345 box = cast(TextFragmentBox)fragments.get(i); | |
346 if (baseline is box.getBaseline()) | |
347 return box.offset + box.length - 1; | |
348 } | |
349 return -1; | |
350 } | |
351 | |
352 /** | |
353 * Returns the offset nearest the given point either up or down one line. If no offset | |
354 * is found, -1 is returned. <code>trailing[0]</code> will be set to 1 if the reference | |
355 * point is closer to the trailing edge of the offset than it is to the leading edge. | |
356 * @since 3.1 | |
357 * @param p a reference point | |
358 * @param down <code>true</code> if the search is down | |
359 * @param trailing an int array | |
360 * @return the next offset or <code>-1</code> | |
361 */ | |
362 public int getNextOffset(Point p, bool down, int[] trailing) { | |
363 return down ? findNextLineOffset(p, trailing) : findPreviousLineOffset(p, trailing); | |
364 } | |
365 | |
366 /** | |
367 * Returns the next offset which is visible in at least one fragment or -1 if there is | |
368 * not one. A visible offset means that the character or the one preceding it is | |
369 * displayed, which implies that a caret can be positioned at such an offset. This is | |
370 * useful for advancing a caret past characters which resulted in a line wrap. | |
371 * | |
372 * @param offset the reference offset | |
373 * @return the next offset which is visible | |
374 * @since 3.1 | |
375 */ | |
376 public int getNextVisibleOffset(int offset) { | |
377 TextFragmentBox box; | |
378 List fragments = getFragmentsWithoutBorder(); | |
379 for (int i = 0; i < fragments.size(); i++) { | |
380 box = cast(TextFragmentBox)fragments.get(i); | |
381 if (box.offset + box.length <= offset) | |
382 continue; | |
383 return Math.max(box.offset, offset + 1); | |
384 } | |
385 return -1; | |
386 } | |
387 | |
388 /** | |
389 * Returns the offset of the character directly below or nearest the given location. The | |
390 * point must be relative to this figure. The return value will be between 0 and N-1. If | |
391 * the proximity argument is not <code>null</code>, the result may also be <code>-1</code> | |
392 * if no offset was found within the proximity. | |
393 * <P> | |
394 * For a typical character, the trailing argument will be filled in to indicate whether | |
395 * the point is closer to the leading edge (0) or the trailing edge (1). When the point | |
396 * is over a cluster composed of multiple characters, the trailing argument will be filled | |
397 * with the position of the character in the cluster that is closest to the point. | |
398 * <P> | |
399 * If the proximity argument is not <code>null</code>, then the location may be no further | |
400 * than the proximity given. Passing <code>null</code> is equivalent to passing <code>new | |
401 * Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE)</code>. The <code>width</code> field of | |
402 * the proximity will contain the horizontal distance, <code>height</code> will contain | |
403 * vertical. Vertical proximity is more important than horizontal. The returned offset is | |
404 * the lowest index with minimum vertical proximity not exceeding the given limit, with | |
405 * horizontal proximity not exceeding the given limit. If an offset that is within the | |
406 * proximity is found, then the given <code>Dimension</code> will be updated to reflect | |
407 * the new proximity. | |
408 * | |
409 * | |
410 * @since 3.1 | |
411 * @param p the point relative to this figure | |
412 * @param trailing the trailing buffer | |
413 * @param proximity restricts and records the distance of the returned offset | |
414 * @return the nearest offset in this figure's text | |
415 */ | |
416 public int getOffset(Point p, int trailing[], Dimension proximity) { | |
417 if (proximity is null) | |
418 proximity = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); | |
419 TextFragmentBox closestBox = null; | |
420 int index = 0; | |
421 int dy; | |
422 int dx; | |
423 int i = 0; | |
424 int size = fragments.size(); | |
425 if (null !is cast(FlowBorder)getBorder() ) { | |
426 i++; | |
427 size--; | |
428 } | |
429 for (; i < size; i++) { | |
430 TextFragmentBox box = cast(TextFragmentBox)fragments.get(i); | |
431 dy = vDistanceBetween(box, p.y); | |
432 if (dy > proximity.height) | |
433 continue; | |
434 dx = hDistanceBetween(box, p.x); | |
435 if (dy is proximity.height && dx >= proximity.width) | |
436 continue; | |
437 proximity.height = dy; | |
438 proximity.width = dx; | |
439 closestBox = box; | |
440 index = i; | |
441 } | |
442 return findOffset(p, trailing, closestBox, index); | |
443 } | |
444 | |
445 /** | |
446 * Returns the previous offset which is visible in at least one fragment or -1 if there | |
447 * is not one. See {@link #getNextVisibleOffset(int)} for more. | |
448 * | |
449 * @param offset a reference offset | |
450 * @return -1 or the previous offset which is visible | |
451 * @since 3.1 | |
452 */ | |
453 | |
454 public int getPreviousVisibleOffset(int offset) { | |
455 TextFragmentBox box; | |
456 if (offset is -1) | |
457 offset = Integer.MAX_VALUE; | |
458 List fragments = getFragmentsWithoutBorder(); | |
459 for (int i = fragments.size() - 1; i >= 0; i--) { | |
460 box = cast(TextFragmentBox)fragments.get(i); | |
461 if (box.offset >= offset) | |
462 continue; | |
463 return Math.min(box.offset + box.length, offset - 1); | |
464 } | |
465 return -1; | |
466 } | |
467 | |
468 /** | |
469 * @return the String being displayed; will not be <code>null</code> | |
470 */ | |
471 public String getText() { | |
472 return text; | |
473 } | |
474 | |
475 int getVisibleAscent() { | |
476 if (null !is cast(FlowBorder)getBorder() ) { | |
477 FlowBorder border = cast(FlowBorder)getBorder(); | |
478 return border.getInsets(this).top + getAscent(); | |
479 } | |
480 return getAscent(); | |
481 } | |
482 | |
483 int getVisibleDescent() { | |
484 if (null !is cast(FlowBorder)getBorder() ) { | |
485 FlowBorder border = cast(FlowBorder)getBorder(); | |
486 return border.getInsets(this).bottom + getDescent(); | |
487 } | |
488 return getDescent(); | |
489 } | |
490 | |
491 private int hDistanceBetween(TextFragmentBox box, int x) { | |
492 if (x < box.getX()) | |
493 return box.getX() - x; | |
494 return Math.max(0, x - (box.getX() + box.getWidth())); | |
495 } | |
496 | |
497 /** | |
498 * Returns <code>true</code> if a portion if the text is truncated using ellipses ("..."). | |
499 * @return <code>true</code> if the text is truncated with ellipses | |
500 */ | |
501 public bool isTextTruncated() { | |
502 for (int i = 0; i < fragments.size(); i++) { | |
503 if ((cast(TextFragmentBox)fragments.get(i)).isTruncated()) | |
504 return true; | |
505 } | |
506 return false; | |
507 } | |
508 | |
509 /** | |
510 * @see dwtx.draw2d.Figure#paintFigure(Graphics) | |
511 */ | |
512 protected void paintFigure(Graphics g) { | |
513 TextFragmentBox frag; | |
514 g.getClip(Rectangle.SINGLETON); | |
515 int yStart = Rectangle.SINGLETON.y; | |
516 int yEnd = Rectangle.SINGLETON.bottom(); | |
517 | |
518 for (int i = 0; i < fragments.size(); i++) { | |
519 frag = cast(TextFragmentBox)fragments.get(i); | |
520 // g.drawLine(frag.getX(), frag.getLineRoot().getVisibleTop(), | |
521 // frag.getWidth() + frag.getX(), frag.getLineRoot().getVisibleTop()); | |
522 // g.drawLine(frag.getX(), frag.getBaseline(), frag.getWidth() + frag.getX(), frag.getBaseline()); | |
523 if (frag.offset is -1) | |
524 continue; | |
525 //Loop until first visible fragment | |
526 if (yStart > frag.getLineRoot().getVisibleBottom() + 1)//The + 1 is for disabled text | |
527 continue; | |
528 //Break loop at first non-visible fragment | |
529 if (yEnd < frag.getLineRoot().getVisibleTop()) | |
530 break; | |
531 | |
532 String draw = getBidiSubstring(frag, i); | |
533 | |
534 if (frag.isTruncated()) | |
535 draw ~= ELLIPSIS; | |
536 | |
537 if (!isEnabled()) { | |
538 Color fgColor = g.getForegroundColor(); | |
539 g.setForegroundColor(ColorConstants.buttonLightest); | |
540 paintText(g, draw, | |
541 frag.getX() + 1, | |
542 frag.getBaseline() - getAscent() + 1, | |
543 frag.getBidiLevel()); | |
544 g.setForegroundColor(ColorConstants.buttonDarker); | |
545 paintText(g, draw, | |
546 frag.getX(), | |
547 frag.getBaseline() - getAscent(), | |
548 frag.getBidiLevel()); | |
549 g.setForegroundColor(fgColor); | |
550 } else { | |
551 paintText(g, draw, | |
552 frag.getX(), | |
553 frag.getBaseline() - getAscent(), | |
554 frag.getBidiLevel()); | |
555 } | |
556 } | |
557 } | |
558 | |
559 /** | |
560 * @see InlineFlow#paintSelection(dwtx.draw2d.Graphics) | |
561 */ | |
562 protected void paintSelection(Graphics graphics) { | |
563 if (selectionStart is -1) | |
564 return; | |
565 graphics.setXORMode(true); | |
566 graphics.setBackgroundColor(ColorConstants.white); | |
567 | |
568 TextFragmentBox frag; | |
569 for (int i = 0; i < fragments.size(); i++) { | |
570 frag = cast(TextFragmentBox)fragments.get(i); | |
571 //Loop until first visible fragment | |
572 if (frag.offset + frag.length <= selectionStart) | |
573 continue; | |
574 if (frag.offset > selectionEnd) | |
575 return; | |
576 if (selectionStart <= frag.offset && selectionEnd >= frag.offset + frag.length) { | |
577 int y = frag.getLineRoot().getVisibleTop(); | |
578 int height = frag.getLineRoot().getVisibleBottom() - y; | |
579 graphics.fillRectangle(frag.getX(), y, frag.getWidth(), height); | |
580 } else if (selectionEnd > frag.offset && selectionStart < frag.offset + frag.length) { | |
581 Point p1 = getPointInBox(frag, Math.max(frag.offset, selectionStart), i, false); | |
582 Point p2 = getPointInBox(frag, Math.min(frag.offset + frag.length, selectionEnd) - 1, i, true); | |
583 Rectangle rect = new Rectangle(p1, p2); | |
584 rect.width--; | |
585 rect.y = frag.getLineRoot().getVisibleTop(); | |
586 rect.height = frag.getLineRoot().getVisibleBottom() - rect.y; | |
587 graphics.fillRectangle(rect); | |
588 } | |
589 } | |
590 } | |
591 | |
592 protected void paintText(Graphics g, String draw, int x, int y, int bidiLevel) { | |
593 if (bidiLevel is -1) { | |
594 g.drawString(draw, x, y); | |
595 } else { | |
596 TextLayout tl = FlowUtilities.getTextLayout(); | |
597 if (isMirrored()) | |
598 tl.setOrientation(DWT.RIGHT_TO_LEFT); | |
599 tl.setFont(g.getFont()); | |
600 tl.setText(draw); | |
601 g.drawTextLayout(tl, x, y); | |
602 } | |
603 } | |
604 | |
605 /** | |
606 * @see dwtx.draw2d.text.FlowFigure#setBidiInfo(dwtx.draw2d.text.BidiInfo) | |
607 */ | |
608 public void setBidiInfo(BidiInfo info) { | |
609 this.bidiInfo = info; | |
610 } | |
611 | |
612 /** | |
613 * Sets the extent of selection. The selection range is inclusive. For example, the | |
614 * range [0, 0] indicates that the first character is selected. | |
615 * @param start the start offset | |
616 * @param end the end offset | |
617 * @since 3.1 | |
618 */ | |
619 public void setSelection(int start, int end) { | |
620 bool repaint_ = false; | |
621 | |
622 if (selectionStart is start) { | |
623 if (selectionEnd is end) | |
624 return; | |
625 repaint_ = true; | |
626 } else | |
627 repaint_ = selectionStart !is selectionEnd || start !is end; | |
628 | |
629 selectionStart = start; | |
630 selectionEnd = end; | |
631 if (repaint_) | |
632 repaint(); | |
633 } | |
634 | |
635 /** | |
636 * Sets the text being displayed. The string may not be <code>null</code>. | |
637 * @param s The new text | |
638 */ | |
639 public void setText(String s) { | |
640 if (s !is null && !s.equals(text)) { | |
641 text = s; | |
642 revalidateBidi(this); | |
643 repaint(); | |
644 } | |
645 } | |
646 | |
647 /** | |
648 * @see java.lang.Object#toString() | |
649 */ | |
650 public String toString() { | |
651 return text; | |
652 } | |
653 | |
654 private int vDistanceBetween(TextFragmentBox box, int y) { | |
655 int top = box.getBaseline() - box.getLineRoot().getAscent(); | |
656 if (y < top) | |
657 return top - y; | |
658 return Math.max(0, y - (box.getBaseline() + box.getLineRoot().getDescent())); | |
659 } | |
660 | |
661 /** | |
662 * Gets the <code>FlowUtilities</code> instance to be used in measurement | |
663 * calculations. | |
664 * | |
665 * @return a <code>FlowUtilities</code> instance | |
666 * @since 3.4 | |
667 */ | |
668 protected FlowUtilities getFlowUtilities() { | |
669 return FlowUtilities.INSTANCE; | |
670 } | |
671 package FlowUtilities getFlowUtilities_package() { | |
672 return getFlowUtilities(); | |
673 } | |
674 | |
675 /** | |
676 * Gets the <code>TextUtilities</code> instance to be used in measurement | |
677 * calculations. | |
678 * | |
679 * @return a <code>TextUtilities</code> instance | |
680 * @since 3.4 | |
681 */ | |
682 protected TextUtilities getTextUtilities() { | |
683 return TextUtilities.INSTANCE; | |
684 } | |
685 | |
686 } |