Mercurial > projects > dwt-mac
comparison dwt/graphics/TextLayout.d @ 0:380af2bdd8e5
Upload of whole dwt tree
author | Jacob Carlborg <doob@me.com> <jacob.carlborg@gmail.com> |
---|---|
date | Sat, 09 Aug 2008 17:00:02 +0200 |
parents | |
children | 649b8e223d5a |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:380af2bdd8e5 |
---|---|
1 /******************************************************************************* | |
2 * Copyright (c) 2000, 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 module dwt.graphics.TextLayout; | |
12 | |
13 import dwt.dwthelper.utils; | |
14 | |
15 import dwt.DWT; | |
16 import dwt.DWTException; | |
17 import dwt.internal.cocoa.NSColor; | |
18 import dwt.internal.cocoa.NSFont; | |
19 import dwt.internal.cocoa.NSLayoutManager; | |
20 import dwt.internal.cocoa.NSMutableParagraphStyle; | |
21 import dwt.internal.cocoa.NSNumber; | |
22 import dwt.internal.cocoa.NSPoint; | |
23 import dwt.internal.cocoa.NSRange; | |
24 import dwt.internal.cocoa.NSRect; | |
25 import dwt.internal.cocoa.NSSize; | |
26 import dwt.internal.cocoa.NSString; | |
27 import dwt.internal.cocoa.NSTextContainer; | |
28 import dwt.internal.cocoa.NSTextStorage; | |
29 import dwt.internal.cocoa.OS; | |
30 | |
31 /** | |
32 * <code>TextLayout</code> is a graphic object that represents | |
33 * styled text. | |
34 * <p> | |
35 * Instances of this class provide support for drawing, cursor | |
36 * navigation, hit testing, text wrapping, alignment, tab expansion | |
37 * line breaking, etc. These are aspects required for rendering internationalized text. | |
38 * </p><p> | |
39 * Application code must explicitly invoke the <code>TextLayout#dispose()</code> | |
40 * method to release the operating system resources managed by each instance | |
41 * when those instances are no longer required. | |
42 * </p> | |
43 * | |
44 * @since 3.0 | |
45 */ | |
46 public final class TextLayout extends Resource { | |
47 | |
48 NSTextStorage textStorage; | |
49 NSLayoutManager layoutManager; | |
50 NSTextContainer textContainer; | |
51 Font font; | |
52 String text; | |
53 StyleItem[] styles; | |
54 int spacing, ascent, descent, indent; | |
55 bool justify; | |
56 int alignment; | |
57 int[] tabs; | |
58 int[] segments; | |
59 int wrapWidth; | |
60 int orientation; | |
61 | |
62 int[] lineOffsets; | |
63 NSRect[] lineBounds; | |
64 | |
65 static class StyleItem { | |
66 TextStyle style; | |
67 int start; | |
68 | |
69 public String toString () { | |
70 return "StyleItem {" + start + ", " + style + "}"; | |
71 } | |
72 } | |
73 | |
74 // static final int TAB_COUNT = 32; | |
75 // static final char ZWS = '\u200B'; | |
76 // | |
77 // static final int UNDERLINE_IME_INPUT = 1 << 16; | |
78 // static final int UNDERLINE_IME_TARGET_CONVERTED = 2 << 16; | |
79 // static final int UNDERLINE_IME_CONVERTED = 3 << 16; | |
80 | |
81 /** | |
82 * Constructs a new instance of this class on the given device. | |
83 * <p> | |
84 * You must dispose the text layout when it is no longer required. | |
85 * </p> | |
86 * | |
87 * @param device the device on which to allocate the text layout | |
88 * | |
89 * @exception IllegalArgumentException <ul> | |
90 * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li> | |
91 * </ul> | |
92 * | |
93 * @see #dispose() | |
94 */ | |
95 public TextLayout (Device device) { | |
96 super(device); | |
97 wrapWidth = ascent = descent = -1; | |
98 alignment = DWT.LEFT; | |
99 orientation = DWT.LEFT_TO_RIGHT; | |
100 text = ""; | |
101 styles = new StyleItem[2]; | |
102 styles[0] = new StyleItem(); | |
103 styles[1] = new StyleItem(); | |
104 init(); | |
105 } | |
106 | |
107 void checkLayout() { | |
108 if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED); | |
109 } | |
110 | |
111 void computeRuns() { | |
112 if (textStorage !is null) return; | |
113 NSString str = NSString.stringWith(text); | |
114 textStorage = ((NSTextStorage)new NSTextStorage().alloc()); | |
115 textStorage.initWithString_(str); | |
116 layoutManager = (NSLayoutManager)new NSLayoutManager().alloc().init(); | |
117 textContainer = (NSTextContainer)new NSTextContainer().alloc(); | |
118 NSSize size = new NSSize(); | |
119 size.width = wrapWidth !is -1 ? wrapWidth : Float.MAX_VALUE; | |
120 size.height = Float.MAX_VALUE; | |
121 textContainer.initWithContainerSize(size); | |
122 textStorage.addLayoutManager(layoutManager); | |
123 layoutManager.addTextContainer(textContainer); | |
124 | |
125 textStorage.beginEditing(); | |
126 Font defaultFont = font !is null ? font : device.systemFont; | |
127 NSRange range = new NSRange(); | |
128 range.length = str.length(); | |
129 textStorage.addAttribute(OS.NSFontAttributeName(), defaultFont.handle, range); | |
130 | |
131 NSMutableParagraphStyle paragraph = (NSMutableParagraphStyle)new NSMutableParagraphStyle().alloc().init(); | |
132 int align = OS.NSLeftTextAlignment; | |
133 if (justify) { | |
134 align = OS.NSJustifiedTextAlignment; | |
135 } else { | |
136 switch (alignment) { | |
137 case DWT.CENTER: | |
138 align = OS.NSCenterTextAlignment; | |
139 break; | |
140 case DWT.RIGHT: | |
141 align = OS.NSRightTextAlignment; | |
142 } | |
143 } | |
144 paragraph.setAlignment(align); | |
145 paragraph.setLineSpacing(spacing); | |
146 paragraph.setFirstLineHeadIndent(indent); | |
147 | |
148 //TODO tabs ascend descent wrap | |
149 | |
150 textStorage.addAttribute(OS.NSParagraphStyleAttributeName(), paragraph, range); | |
151 paragraph.release(); | |
152 | |
153 int textLength = str.length(); | |
154 for (int i = 0; i < styles.length - 1; i++) { | |
155 StyleItem run = styles[i]; | |
156 if (run.style is null) continue; | |
157 TextStyle style = run.style; | |
158 range.location = textLength !is 0 ? translateOffset(run.start) : 0; | |
159 range.length = translateOffset(styles[i + 1].start) - range.location; | |
160 Font font = style.font; | |
161 if (font !is null) { | |
162 textStorage.addAttribute(OS.NSFontAttributeName(), font.handle, range); | |
163 } | |
164 Color foreground = style.foreground; | |
165 if (foreground !is null) { | |
166 NSColor color = NSColor.colorWithDeviceRed(foreground.handle[0], foreground.handle[1], foreground.handle[2], 1); | |
167 textStorage.addAttribute(OS.NSForegroundColorAttributeName(), color, range); | |
168 } | |
169 Color background = style.background; | |
170 if (background !is null) { | |
171 NSColor color = NSColor.colorWithDeviceRed(background.handle[0], background.handle[1], background.handle[2], 1); | |
172 textStorage.addAttribute(OS.NSBackgroundColorAttributeName(), color, range); | |
173 } | |
174 if (style.strikeout) { | |
175 textStorage.addAttribute(OS.NSStrikethroughStyleAttributeName(), NSNumber.numberWithInt(OS.NSUnderlineStyleSingle), range); | |
176 Color strikeColor = style.strikeoutColor; | |
177 if (strikeColor !is null) { | |
178 NSColor color = NSColor.colorWithDeviceRed(strikeColor.handle[0], strikeColor.handle[1], strikeColor.handle[2], 1); | |
179 textStorage.addAttribute(OS.NSStrikethroughColorAttributeName(), color, range); | |
180 } | |
181 } | |
182 if (style.underline) { | |
183 //TODO - IME - thick | |
184 int underlineStyle = 0; | |
185 switch (style.underlineStyle) { | |
186 case DWT.UNDERLINE_SINGLE: | |
187 underlineStyle = OS.NSUnderlineStyleSingle; | |
188 break; | |
189 case DWT.UNDERLINE_DOUBLE: | |
190 underlineStyle = OS.NSUnderlineStyleDouble; | |
191 break; | |
192 } | |
193 if (underlineStyle !is 0) { | |
194 textStorage.addAttribute(OS.NSUnderlineStyleAttributeName(), NSNumber.numberWithInt(underlineStyle), range); | |
195 Color underlineColor = style.underlineColor; | |
196 if (underlineColor !is null) { | |
197 NSColor color = NSColor.colorWithDeviceRed(underlineColor.handle[0], underlineColor.handle[1], underlineColor.handle[2], 1); | |
198 textStorage.addAttribute(OS.NSUnderlineColorAttributeName(), color, range); | |
199 } | |
200 } | |
201 } | |
202 if (style.rise !is 0) { | |
203 textStorage.addAttribute(OS.NSBaselineOffsetAttributeName(), NSNumber.numberWithInt(style.rise), range); | |
204 } | |
205 if (style.metrics !is null) { | |
206 //TODO | |
207 } | |
208 } | |
209 textStorage.endEditing(); | |
210 | |
211 textContainer.setLineFragmentPadding(0); | |
212 layoutManager.glyphRangeForTextContainer(textContainer); | |
213 | |
214 int numberOfLines, index, numberOfGlyphs = layoutManager.numberOfGlyphs(); | |
215 int rangePtr = OS.malloc(NSRange.sizeof); | |
216 NSRange lineRange = new NSRange(); | |
217 for (numberOfLines = 0, index = 0; index < numberOfGlyphs; numberOfLines++){ | |
218 layoutManager.lineFragmentUsedRectForGlyphAtIndex_effectiveRange_withoutAdditionalLayout_(index, rangePtr, true); | |
219 OS.memmove(lineRange, rangePtr, NSRange.sizeof); | |
220 index = lineRange.location + lineRange.length; | |
221 } | |
222 if (numberOfLines is 0) numberOfLines++; | |
223 int[] offsets = new int[numberOfLines + 1]; | |
224 NSRect[] bounds = new NSRect[numberOfLines]; | |
225 for (numberOfLines = 0, index = 0; index < numberOfGlyphs; numberOfLines++){ | |
226 bounds[numberOfLines] = layoutManager.lineFragmentUsedRectForGlyphAtIndex_effectiveRange_withoutAdditionalLayout_(index, rangePtr, true); | |
227 OS.memmove(lineRange, rangePtr, NSRange.sizeof); | |
228 offsets[numberOfLines] = lineRange.location; | |
229 index = lineRange.location + lineRange.length; | |
230 } | |
231 if (numberOfLines is 0) { | |
232 Font font = this.font !is null ? this.font : device.systemFont; | |
233 NSFont nsFont = font.handle; | |
234 bounds[0] = new NSRect(); | |
235 bounds[0].height = Math.max(layoutManager.defaultLineHeightForFont(nsFont), ascent + descent); | |
236 } | |
237 OS.free(rangePtr); | |
238 offsets[numberOfLines] = textStorage.length(); | |
239 this.lineOffsets = offsets; | |
240 this.lineBounds = bounds; | |
241 } | |
242 | |
243 void destroy() { | |
244 freeRuns(); | |
245 font = null; | |
246 text = null; | |
247 styles = null; | |
248 } | |
249 | |
250 /** | |
251 * Draws the receiver's text using the specified GC at the specified | |
252 * point. | |
253 * | |
254 * @param gc the GC to draw | |
255 * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn | |
256 * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn | |
257 * | |
258 * @exception DWTException <ul> | |
259 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
260 * </ul> | |
261 * @exception IllegalArgumentException <ul> | |
262 * <li>ERROR_NULL_ARGUMENT - if the gc is null</li> | |
263 * </ul> | |
264 */ | |
265 public void draw(GC gc, int x, int y) { | |
266 draw(gc, x, y, -1, -1, null, null); | |
267 } | |
268 | |
269 /** | |
270 * Draws the receiver's text using the specified GC at the specified | |
271 * point. | |
272 * | |
273 * @param gc the GC to draw | |
274 * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn | |
275 * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn | |
276 * @param selectionStart the offset where the selections starts, or -1 indicating no selection | |
277 * @param selectionEnd the offset where the selections ends, or -1 indicating no selection | |
278 * @param selectionForeground selection foreground, or NULL to use the system default color | |
279 * @param selectionBackground selection background, or NULL to use the system default color | |
280 * | |
281 * @exception DWTException <ul> | |
282 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
283 * </ul> | |
284 * @exception IllegalArgumentException <ul> | |
285 * <li>ERROR_NULL_ARGUMENT - if the gc is null</li> | |
286 * </ul> | |
287 */ | |
288 public void draw(GC gc, int x, int y, int selectionStart, int selectionEnd, Color selectionForeground, Color selectionBackground) { | |
289 draw(gc, x, y, selectionStart, selectionEnd, selectionForeground, selectionBackground, 0); | |
290 } | |
291 | |
292 /** | |
293 * Draws the receiver's text using the specified GC at the specified | |
294 * point. | |
295 * <p> | |
296 * The parameter <code>flags</code> can include one of <code>DWT.DELIMITER_SELECTION</code> | |
297 * or <code>DWT.FULL_SELECTION</code> to specify the selection behavior on all lines except | |
298 * for the last line, and can also include <code>DWT.LAST_LINE_SELECTION</code> to extend | |
299 * the specified selection behavior to the last line. | |
300 * </p> | |
301 * @param gc the GC to draw | |
302 * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn | |
303 * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn | |
304 * @param selectionStart the offset where the selections starts, or -1 indicating no selection | |
305 * @param selectionEnd the offset where the selections ends, or -1 indicating no selection | |
306 * @param selectionForeground selection foreground, or NULL to use the system default color | |
307 * @param selectionBackground selection background, or NULL to use the system default color | |
308 * @param flags drawing options | |
309 * | |
310 * @exception DWTException <ul> | |
311 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
312 * </ul> | |
313 * @exception IllegalArgumentException <ul> | |
314 * <li>ERROR_NULL_ARGUMENT - if the gc is null</li> | |
315 * </ul> | |
316 * | |
317 * @since 3.3 | |
318 */ | |
319 public void draw(GC gc, int x, int y, int selectionStart, int selectionEnd, Color selectionForeground, Color selectionBackground, int flags) { | |
320 checkLayout (); | |
321 computeRuns(); | |
322 if (gc is null) DWT.error(DWT.ERROR_NULL_ARGUMENT); | |
323 if (gc.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT); | |
324 if (selectionForeground !is null && selectionForeground.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT); | |
325 if (selectionBackground !is null && selectionBackground.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT); | |
326 gc.checkGC(GC.CLIPPING | GC.TRANSFORM | GC.FOREGROUND); | |
327 // float[] foreground = gc.data.foreground; | |
328 // NSColor color = NSColor.colorWithDeviceRed(foreground[0], foreground[1], foreground[2], foreground[3]); | |
329 // textStorage.setForegroundColor(color); | |
330 NSPoint pt = new NSPoint(); | |
331 pt.x = x; | |
332 pt.y = y; | |
333 NSRange range = new NSRange(); | |
334 range.length = layoutManager.numberOfGlyphs(); | |
335 bool hasSelection = selectionStart <= selectionEnd && selectionStart !is -1 && selectionEnd !is -1; | |
336 NSRange selectionRange = null; | |
337 if (hasSelection) { | |
338 selectionRange = new NSRange(); | |
339 selectionRange.location = selectionStart; | |
340 selectionRange.length = selectionEnd - selectionStart + 1; | |
341 if (selectionBackground is null) selectionBackground = device.getSystemColor(DWT.COLOR_LIST_SELECTION); | |
342 NSColor selectionColor = NSColor.colorWithDeviceRed(selectionBackground.handle[0], selectionBackground.handle[1], selectionBackground.handle[2], selectionBackground.handle[3]); | |
343 layoutManager.addTemporaryAttribute(OS.NSBackgroundColorAttributeName, selectionColor, selectionRange); | |
344 } | |
345 //TODO draw selection for flags (LAST_LINE_SELECTION and FULL_SELECTION) | |
346 if (range.length > 0) { | |
347 layoutManager.drawBackgroundForGlyphRange(range, pt); | |
348 layoutManager.drawGlyphsForGlyphRange(range, pt); | |
349 } | |
350 if (selectionRange !is null) { | |
351 layoutManager.removeTemporaryAttribute(OS.NSBackgroundColorAttributeName, selectionRange); | |
352 } | |
353 } | |
354 | |
355 void freeRuns() { | |
356 if (textStorage is null) return; | |
357 if (textStorage !is null) { | |
358 textStorage.release(); | |
359 } | |
360 if (layoutManager !is null) { | |
361 layoutManager.release(); | |
362 } | |
363 if (textContainer !is null) { | |
364 textContainer.release(); | |
365 } | |
366 textStorage = null; | |
367 layoutManager = null; | |
368 textContainer = null; | |
369 } | |
370 | |
371 /** | |
372 * Returns the receiver's horizontal text alignment, which will be one | |
373 * of <code>DWT.LEFT</code>, <code>DWT.CENTER</code> or | |
374 * <code>DWT.RIGHT</code>. | |
375 * | |
376 * @return the alignment used to positioned text horizontally | |
377 * | |
378 * @exception DWTException <ul> | |
379 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
380 * </ul> | |
381 */ | |
382 public int getAlignment() { | |
383 checkLayout(); | |
384 return alignment; | |
385 } | |
386 | |
387 /** | |
388 * Returns the ascent of the receiver. | |
389 * | |
390 * @return the ascent | |
391 * | |
392 * @exception DWTException <ul> | |
393 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
394 * </ul> | |
395 * | |
396 * @see #getDescent() | |
397 * @see #setDescent(int) | |
398 * @see #setAscent(int) | |
399 * @see #getLineMetrics(int) | |
400 */ | |
401 public int getAscent () { | |
402 checkLayout(); | |
403 return ascent; | |
404 } | |
405 | |
406 /** | |
407 * Returns the bounds of the receiver. | |
408 * | |
409 * @return the bounds of the receiver | |
410 * | |
411 * @exception DWTException <ul> | |
412 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
413 * </ul> | |
414 */ | |
415 public Rectangle getBounds() { | |
416 checkLayout(); | |
417 computeRuns(); | |
418 NSRect rect = layoutManager.usedRectForTextContainer(textContainer); | |
419 if (wrapWidth !is -1) rect.width = wrapWidth; | |
420 if (text.length() is 0) { | |
421 Font font = this.font !is null ? this.font : device.systemFont; | |
422 NSFont nsFont = font.handle; | |
423 rect.height = Math.max(rect.height, layoutManager.defaultLineHeightForFont(nsFont)); | |
424 } | |
425 rect.height = Math.max(rect.height, ascent + descent); | |
426 return new Rectangle(0, 0, (int)rect.width, (int)rect.height); | |
427 } | |
428 | |
429 /** | |
430 * Returns the bounds for the specified range of characters. The | |
431 * bounds is the smallest rectangle that encompasses all characters | |
432 * in the range. The start and end offsets are inclusive and will be | |
433 * clamped if out of range. | |
434 * | |
435 * @param start the start offset | |
436 * @param end the end offset | |
437 * @return the bounds of the character range | |
438 * | |
439 * @exception DWTException <ul> | |
440 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
441 * </ul> | |
442 */ | |
443 public Rectangle getBounds(int start, int end) { | |
444 checkLayout(); | |
445 computeRuns(); | |
446 int length = text.length(); | |
447 if (length is 0) return new Rectangle(0, 0, 0, 0); | |
448 if (start > end) return new Rectangle(0, 0, 0, 0); | |
449 start = Math.min(Math.max(0, start), length - 1); | |
450 end = Math.min(Math.max(0, end), length - 1); | |
451 start = translateOffset(start); | |
452 end = translateOffset(end); | |
453 NSRange range = new NSRange(); | |
454 range.location = layoutManager.glyphIndexForCharacterAtIndex(start); | |
455 range.length = layoutManager.glyphIndexForCharacterAtIndex(end + 1) - range.location; | |
456 NSRect rect = layoutManager.boundingRectForGlyphRange(range, textContainer); | |
457 return new Rectangle((int)rect.x, (int)rect.y, (int)Math.ceil(rect.width), (int)Math.ceil(rect.height)); | |
458 } | |
459 | |
460 /** | |
461 * Returns the descent of the receiver. | |
462 * | |
463 * @return the descent | |
464 * | |
465 * @exception DWTException <ul> | |
466 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
467 * </ul> | |
468 * | |
469 * @see #getAscent() | |
470 * @see #setAscent(int) | |
471 * @see #setDescent(int) | |
472 * @see #getLineMetrics(int) | |
473 */ | |
474 public int getDescent () { | |
475 checkLayout(); | |
476 return descent; | |
477 } | |
478 | |
479 /** | |
480 * Returns the default font currently being used by the receiver | |
481 * to draw and measure text. | |
482 * | |
483 * @return the receiver's font | |
484 * | |
485 * @exception DWTException <ul> | |
486 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
487 * </ul> | |
488 */ | |
489 public Font getFont () { | |
490 checkLayout(); | |
491 return font; | |
492 } | |
493 | |
494 /** | |
495 * Returns the receiver's indent. | |
496 * | |
497 * @return the receiver's indent | |
498 * | |
499 * @exception DWTException <ul> | |
500 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
501 * </ul> | |
502 * | |
503 * @since 3.2 | |
504 */ | |
505 public int getIndent () { | |
506 checkLayout(); | |
507 return indent; | |
508 } | |
509 | |
510 /** | |
511 * Returns the receiver's justification. | |
512 * | |
513 * @return the receiver's justification | |
514 * | |
515 * @exception DWTException <ul> | |
516 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
517 * </ul> | |
518 * | |
519 * @since 3.2 | |
520 */ | |
521 public bool getJustify () { | |
522 checkLayout(); | |
523 return justify; | |
524 } | |
525 | |
526 /** | |
527 * Returns the embedding level for the specified character offset. The | |
528 * embedding level is usually used to determine the directionality of a | |
529 * character in bidirectional text. | |
530 * | |
531 * @param offset the character offset | |
532 * @return the embedding level | |
533 * | |
534 * @exception IllegalArgumentException <ul> | |
535 * <li>ERROR_INVALID_ARGUMENT - if the character offset is out of range</li> | |
536 * </ul> | |
537 * @exception DWTException <ul> | |
538 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
539 */ | |
540 public int getLevel(int offset) { | |
541 checkLayout(); | |
542 computeRuns(); | |
543 int length = text.length(); | |
544 if (!(0 <= offset && offset <= length)) DWT.error(DWT.ERROR_INVALID_RANGE); | |
545 offset = translateOffset(offset); | |
546 int level = 0; | |
547 //TODO | |
548 return level; | |
549 } | |
550 | |
551 /** | |
552 * Returns the line offsets. Each value in the array is the | |
553 * offset for the first character in a line except for the last | |
554 * value, which contains the length of the text. | |
555 * | |
556 * @return the line offsets | |
557 * | |
558 * @exception DWTException <ul> | |
559 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
560 * </ul> | |
561 */ | |
562 public int[] getLineOffsets() { | |
563 checkLayout (); | |
564 computeRuns(); | |
565 int[] offsets = new int[lineOffsets.length]; | |
566 for (int i = 0; i < offsets.length; i++) { | |
567 offsets[i] = untranslateOffset(lineOffsets[i]); | |
568 } | |
569 return offsets; | |
570 } | |
571 | |
572 /** | |
573 * Returns the index of the line that contains the specified | |
574 * character offset. | |
575 * | |
576 * @param offset the character offset | |
577 * @return the line index | |
578 * | |
579 * @exception IllegalArgumentException <ul> | |
580 * <li>ERROR_INVALID_ARGUMENT - if the character offset is out of range</li> | |
581 * </ul> | |
582 * @exception DWTException <ul> | |
583 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
584 * </ul> | |
585 */ | |
586 public int getLineIndex(int offset) { | |
587 checkLayout (); | |
588 computeRuns(); | |
589 int length = text.length(); | |
590 if (!(0 <= offset && offset <= length)) DWT.error(DWT.ERROR_INVALID_RANGE); | |
591 offset = translateOffset(offset); | |
592 for (int line=0; line<lineOffsets.length - 1; line++) { | |
593 if (lineOffsets[line + 1] > offset) { | |
594 return line; | |
595 } | |
596 } | |
597 return lineBounds.length - 1; | |
598 } | |
599 | |
600 /** | |
601 * Returns the bounds of the line for the specified line index. | |
602 * | |
603 * @param lineIndex the line index | |
604 * @return the line bounds | |
605 * | |
606 * @exception IllegalArgumentException <ul> | |
607 * <li>ERROR_INVALID_ARGUMENT - if the line index is out of range</li> | |
608 * </ul> | |
609 * @exception DWTException <ul> | |
610 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
611 * </ul> | |
612 */ | |
613 public Rectangle getLineBounds(int lineIndex) { | |
614 checkLayout(); | |
615 computeRuns(); | |
616 if (!(0 <= lineIndex && lineIndex < lineBounds.length)) DWT.error(DWT.ERROR_INVALID_RANGE); | |
617 NSRect rect = lineBounds[lineIndex]; | |
618 return new Rectangle((int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height); | |
619 } | |
620 | |
621 /** | |
622 * Returns the receiver's line count. This includes lines caused | |
623 * by wrapping. | |
624 * | |
625 * @return the line count | |
626 * | |
627 * @exception DWTException <ul> | |
628 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
629 * </ul> | |
630 */ | |
631 public int getLineCount() { | |
632 checkLayout (); | |
633 computeRuns(); | |
634 return lineOffsets.length - 1; | |
635 } | |
636 | |
637 /** | |
638 * Returns the font metrics for the specified line index. | |
639 * | |
640 * @param lineIndex the line index | |
641 * @return the font metrics | |
642 * | |
643 * @exception IllegalArgumentException <ul> | |
644 * <li>ERROR_INVALID_ARGUMENT - if the line index is out of range</li> | |
645 * </ul> | |
646 * @exception DWTException <ul> | |
647 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
648 * </ul> | |
649 */ | |
650 public FontMetrics getLineMetrics (int lineIndex) { | |
651 checkLayout (); | |
652 computeRuns(); | |
653 int lineCount = getLineCount(); | |
654 if (!(0 <= lineIndex && lineIndex < lineCount)) DWT.error(DWT.ERROR_INVALID_RANGE); | |
655 int length = text.length(); | |
656 if (length is 0) { | |
657 Font font = this.font !is null ? this.font : device.systemFont; | |
658 NSFont nsFont = font.handle; | |
659 int ascent = (int)(0.5f + nsFont.ascender()); | |
660 int descent = (int)(0.5f + (-nsFont.descender() + nsFont.leading())); | |
661 ascent = Math.max(ascent, this.ascent); | |
662 descent = Math.max(descent, this.descent); | |
663 return FontMetrics.cocoa_new(ascent, descent, 0, 0, ascent + descent); | |
664 } | |
665 Rectangle rect = getLineBounds(lineIndex); | |
666 int baseline = (int)layoutManager.typesetter().baselineOffsetInLayoutManager(layoutManager, getLineOffsets()[lineIndex]); | |
667 return FontMetrics.cocoa_new(rect.height - baseline, baseline, 0, 0, rect.height); | |
668 } | |
669 | |
670 /** | |
671 * Returns the location for the specified character offset. The | |
672 * <code>trailing</code> argument indicates whether the offset | |
673 * corresponds to the leading or trailing edge of the cluster. | |
674 * | |
675 * @param offset the character offset | |
676 * @param trailing the trailing flag | |
677 * @return the location of the character offset | |
678 * | |
679 * @exception DWTException <ul> | |
680 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
681 * </ul> | |
682 * | |
683 * @see #getOffset(Point, int[]) | |
684 * @see #getOffset(int, int, int[]) | |
685 */ | |
686 public Point getLocation(int offset, bool trailing) { | |
687 checkLayout(); | |
688 computeRuns(); | |
689 int length = text.length(); | |
690 if (!(0 <= offset && offset <= length)) DWT.error(DWT.ERROR_INVALID_RANGE); | |
691 if (length is 0) return new Point(0, 0); | |
692 offset = translateOffset(offset); | |
693 int glyphIndex = layoutManager.glyphIndexForCharacterAtIndex(offset); | |
694 NSRect rect = layoutManager.lineFragmentUsedRectForGlyphAtIndex_effectiveRange_(glyphIndex, 0); | |
695 NSPoint point = layoutManager.locationForGlyphAtIndex(glyphIndex); | |
696 if (trailing) { | |
697 NSRange range = new NSRange(); | |
698 range.location = glyphIndex; | |
699 range.length = 1; | |
700 NSRect bounds = layoutManager.boundingRectForGlyphRange(range, textContainer); | |
701 point.x += bounds.width; | |
702 } | |
703 return new Point((int)point.x, (int)rect.y); | |
704 } | |
705 | |
706 /** | |
707 * Returns the next offset for the specified offset and movement | |
708 * type. The movement is one of <code>DWT.MOVEMENT_CHAR</code>, | |
709 * <code>DWT.MOVEMENT_CLUSTER</code>, <code>DWT.MOVEMENT_WORD</code>, | |
710 * <code>DWT.MOVEMENT_WORD_END</code> or <code>DWT.MOVEMENT_WORD_START</code>. | |
711 * | |
712 * @param offset the start offset | |
713 * @param movement the movement type | |
714 * @return the next offset | |
715 * | |
716 * @exception IllegalArgumentException <ul> | |
717 * <li>ERROR_INVALID_ARGUMENT - if the offset is out of range</li> | |
718 * </ul> | |
719 * @exception DWTException <ul> | |
720 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
721 * </ul> | |
722 * | |
723 * @see #getPreviousOffset(int, int) | |
724 */ | |
725 public int getNextOffset (int offset, int movement) { | |
726 return _getOffset(offset, movement, true); | |
727 } | |
728 | |
729 int _getOffset (int offset, int movement, bool forward) { | |
730 checkLayout(); | |
731 computeRuns(); | |
732 int length = text.length(); | |
733 if (!(0 <= offset && offset <= length)) DWT.error(DWT.ERROR_INVALID_RANGE); | |
734 if (length is 0) return 0; | |
735 offset = translateOffset(offset); | |
736 switch (movement) { | |
737 case DWT.MOVEMENT_CLUSTER://TODO cluster | |
738 case DWT.MOVEMENT_CHAR: { | |
739 if (forward) { | |
740 offset++; | |
741 } else { | |
742 offset--; | |
743 } | |
744 return untranslateOffset(offset); | |
745 } | |
746 case DWT.MOVEMENT_WORD: { | |
747 return untranslateOffset(textStorage.nextWordFromIndex(offset, forward)); | |
748 } | |
749 case DWT.MOVEMENT_WORD_END: { | |
750 NSRange range = textStorage.doubleClickAtIndex(length is offset ? length - 1 : offset); | |
751 return untranslateOffset(range.location + range.length); | |
752 } | |
753 case DWT.MOVEMENT_WORD_START: { | |
754 NSRange range = textStorage.doubleClickAtIndex(length is offset ? length - 1 : offset); | |
755 return untranslateOffset(range.location); | |
756 } | |
757 default: | |
758 break; | |
759 } | |
760 return -1; | |
761 } | |
762 | |
763 /** | |
764 * Returns the character offset for the specified point. | |
765 * For a typical character, the trailing argument will be filled in to | |
766 * indicate whether the point is closer to the leading edge (0) or | |
767 * the trailing edge (1). When the point is over a cluster composed | |
768 * of multiple characters, the trailing argument will be filled with the | |
769 * position of the character in the cluster that is closest to | |
770 * the point. | |
771 * | |
772 * @param point the point | |
773 * @param trailing the trailing buffer | |
774 * @return the character offset | |
775 * | |
776 * @exception IllegalArgumentException <ul> | |
777 * <li>ERROR_INVALID_ARGUMENT - if the trailing length is less than <code>1</code></li> | |
778 * <li>ERROR_NULL_ARGUMENT - if the point is null</li> | |
779 * </ul> | |
780 * @exception DWTException <ul> | |
781 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
782 * </ul> | |
783 * | |
784 * @see #getLocation(int, bool) | |
785 */ | |
786 public int getOffset(Point point, int[] trailing) { | |
787 checkLayout(); | |
788 computeRuns(); | |
789 if (point is null) DWT.error(DWT.ERROR_NULL_ARGUMENT); | |
790 return getOffset(point.x, point.y, trailing); | |
791 } | |
792 | |
793 /** | |
794 * Returns the character offset for the specified point. | |
795 * For a typical character, the trailing argument will be filled in to | |
796 * indicate whether the point is closer to the leading edge (0) or | |
797 * the trailing edge (1). When the point is over a cluster composed | |
798 * of multiple characters, the trailing argument will be filled with the | |
799 * position of the character in the cluster that is closest to | |
800 * the point. | |
801 * | |
802 * @param x the x coordinate of the point | |
803 * @param y the y coordinate of the point | |
804 * @param trailing the trailing buffer | |
805 * @return the character offset | |
806 * | |
807 * @exception IllegalArgumentException <ul> | |
808 * <li>ERROR_INVALID_ARGUMENT - if the trailing length is less than <code>1</code></li> | |
809 * </ul> | |
810 * @exception DWTException <ul> | |
811 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
812 * </ul> | |
813 * | |
814 * @see #getLocation(int, bool) | |
815 */ | |
816 public int getOffset(int x, int y, int[] trailing) { | |
817 checkLayout(); | |
818 computeRuns(); | |
819 if (trailing !is null && trailing.length < 1) DWT.error(DWT.ERROR_INVALID_ARGUMENT); | |
820 int length = text.length(); | |
821 if (length is 0) return 0; | |
822 NSPoint pt = new NSPoint(); | |
823 pt.x = x; | |
824 pt.y = y; | |
825 float[] partialFration = new float[1]; | |
826 int glyphIndex = layoutManager.glyphIndexForPoint_inTextContainer_fractionOfDistanceThroughGlyph_(pt, textContainer, partialFration); | |
827 int offset = layoutManager.characterIndexForGlyphAtIndex(glyphIndex); | |
828 if (trailing !is null) trailing[0] = Math.round(partialFration[0]); | |
829 return Math.min(untranslateOffset(offset), length - 1); | |
830 } | |
831 | |
832 /** | |
833 * Returns the orientation of the receiver. | |
834 * | |
835 * @return the orientation style | |
836 * | |
837 * @exception DWTException <ul> | |
838 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
839 * </ul> | |
840 */ | |
841 public int getOrientation() { | |
842 checkLayout(); | |
843 return orientation; | |
844 } | |
845 | |
846 /** | |
847 * Returns the previous offset for the specified offset and movement | |
848 * type. The movement is one of <code>DWT.MOVEMENT_CHAR</code>, | |
849 * <code>DWT.MOVEMENT_CLUSTER</code> or <code>DWT.MOVEMENT_WORD</code>, | |
850 * <code>DWT.MOVEMENT_WORD_END</code> or <code>DWT.MOVEMENT_WORD_START</code>. | |
851 * | |
852 * @param offset the start offset | |
853 * @param movement the movement type | |
854 * @return the previous offset | |
855 * | |
856 * @exception IllegalArgumentException <ul> | |
857 * <li>ERROR_INVALID_ARGUMENT - if the offset is out of range</li> | |
858 * </ul> | |
859 * @exception DWTException <ul> | |
860 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
861 * </ul> | |
862 * | |
863 * @see #getNextOffset(int, int) | |
864 */ | |
865 public int getPreviousOffset (int index, int movement) { | |
866 return _getOffset(index, movement, false); | |
867 } | |
868 | |
869 /** | |
870 * Gets the ranges of text that are associated with a <code>TextStyle</code>. | |
871 * | |
872 * @return the ranges, an array of offsets representing the start and end of each | |
873 * text style. | |
874 * | |
875 * @exception DWTException <ul> | |
876 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
877 * </ul> | |
878 * | |
879 * @see #getStyles() | |
880 * | |
881 * @since 3.2 | |
882 */ | |
883 public int[] getRanges () { | |
884 checkLayout(); | |
885 int[] result = new int[styles.length * 2]; | |
886 int count = 0; | |
887 for (int i=0; i<styles.length - 1; i++) { | |
888 if (styles[i].style !is null) { | |
889 result[count++] = styles[i].start; | |
890 result[count++] = styles[i + 1].start - 1; | |
891 } | |
892 } | |
893 if (count !is result.length) { | |
894 int[] newResult = new int[count]; | |
895 System.arraycopy(result, 0, newResult, 0, count); | |
896 result = newResult; | |
897 } | |
898 return result; | |
899 } | |
900 | |
901 /** | |
902 * Returns the text segments offsets of the receiver. | |
903 * | |
904 * @return the text segments offsets | |
905 * | |
906 * @exception DWTException <ul> | |
907 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
908 * </ul> | |
909 */ | |
910 public int[] getSegments() { | |
911 checkLayout(); | |
912 return segments; | |
913 } | |
914 | |
915 /** | |
916 * Returns the line spacing of the receiver. | |
917 * | |
918 * @return the line spacing | |
919 * | |
920 * @exception DWTException <ul> | |
921 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
922 * </ul> | |
923 */ | |
924 public int getSpacing () { | |
925 checkLayout(); | |
926 return spacing; | |
927 } | |
928 | |
929 /** | |
930 * Gets the style of the receiver at the specified character offset. | |
931 * | |
932 * @param offset the text offset | |
933 * @return the style or <code>null</code> if not set | |
934 * | |
935 * @exception IllegalArgumentException <ul> | |
936 * <li>ERROR_INVALID_ARGUMENT - if the character offset is out of range</li> | |
937 * </ul> | |
938 * @exception DWTException <ul> | |
939 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
940 * </ul> | |
941 */ | |
942 public TextStyle getStyle (int offset) { | |
943 checkLayout(); | |
944 int length = text.length(); | |
945 if (!(0 <= offset && offset < length)) DWT.error(DWT.ERROR_INVALID_RANGE); | |
946 for (int i=1; i<styles.length; i++) { | |
947 StyleItem item = styles[i]; | |
948 if (item.start > offset) { | |
949 return styles[i - 1].style; | |
950 } | |
951 } | |
952 return null; | |
953 } | |
954 | |
955 /** | |
956 * Gets all styles of the receiver. | |
957 * | |
958 * @return the styles | |
959 * | |
960 * @exception DWTException <ul> | |
961 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
962 * </ul> | |
963 * | |
964 * @see #getRanges() | |
965 * | |
966 * @since 3.2 | |
967 */ | |
968 public TextStyle[] getStyles () { | |
969 checkLayout(); | |
970 TextStyle[] result = new TextStyle[styles.length]; | |
971 int count = 0; | |
972 for (int i=0; i<styles.length; i++) { | |
973 if (styles[i].style !is null) { | |
974 result[count++] = styles[i].style; | |
975 } | |
976 } | |
977 if (count !is result.length) { | |
978 TextStyle[] newResult = new TextStyle[count]; | |
979 System.arraycopy(result, 0, newResult, 0, count); | |
980 result = newResult; | |
981 } | |
982 return result; | |
983 } | |
984 | |
985 /** | |
986 * Returns the tab list of the receiver. | |
987 * | |
988 * @return the tab list | |
989 * | |
990 * @exception DWTException <ul> | |
991 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
992 * </ul> | |
993 */ | |
994 public int[] getTabs() { | |
995 checkLayout(); | |
996 return tabs; | |
997 } | |
998 | |
999 /** | |
1000 * Gets the receiver's text, which will be an empty | |
1001 * string if it has never been set. | |
1002 * | |
1003 * @return the receiver's text | |
1004 * | |
1005 * @exception DWTException <ul> | |
1006 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
1007 * </ul> | |
1008 */ | |
1009 public String getText () { | |
1010 checkLayout (); | |
1011 return text; | |
1012 } | |
1013 | |
1014 /** | |
1015 * Returns the width of the receiver. | |
1016 * | |
1017 * @return the width | |
1018 * | |
1019 * @exception DWTException <ul> | |
1020 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
1021 * </ul> | |
1022 */ | |
1023 public int getWidth () { | |
1024 checkLayout(); | |
1025 return wrapWidth; | |
1026 } | |
1027 | |
1028 /** | |
1029 * Returns <code>true</code> if the text layout has been disposed, | |
1030 * and <code>false</code> otherwise. | |
1031 * <p> | |
1032 * This method gets the dispose state for the text layout. | |
1033 * When a text layout has been disposed, it is an error to | |
1034 * invoke any other method using the text layout. | |
1035 * </p> | |
1036 * | |
1037 * @return <code>true</code> when the text layout is disposed and <code>false</code> otherwise | |
1038 */ | |
1039 public bool isDisposed () { | |
1040 return device is null; | |
1041 } | |
1042 | |
1043 /** | |
1044 * Sets the text alignment for the receiver. The alignment controls | |
1045 * how a line of text is positioned horizontally. The argument should | |
1046 * be one of <code>DWT.LEFT</code>, <code>DWT.RIGHT</code> or <code>DWT.CENTER</code>. | |
1047 * <p> | |
1048 * The default alignment is <code>DWT.LEFT</code>. Note that the receiver's | |
1049 * width must be set in order to use <code>DWT.RIGHT</code> or <code>DWT.CENTER</code> | |
1050 * alignment. | |
1051 * </p> | |
1052 * | |
1053 * @param alignment the new alignment | |
1054 * | |
1055 * @exception DWTException <ul> | |
1056 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
1057 * </ul> | |
1058 * | |
1059 * @see #setWidth(int) | |
1060 */ | |
1061 public void setAlignment (int alignment) { | |
1062 checkLayout(); | |
1063 int mask = DWT.LEFT | DWT.CENTER | DWT.RIGHT; | |
1064 alignment &= mask; | |
1065 if (alignment is 0) return; | |
1066 if ((alignment & DWT.LEFT) !is 0) alignment = DWT.LEFT; | |
1067 if ((alignment & DWT.RIGHT) !is 0) alignment = DWT.RIGHT; | |
1068 if (this.alignment is alignment) return; | |
1069 freeRuns(); | |
1070 this.alignment = alignment; | |
1071 } | |
1072 | |
1073 /** | |
1074 * Sets the ascent of the receiver. The ascent is distance in pixels | |
1075 * from the baseline to the top of the line and it is applied to all | |
1076 * lines. The default value is <code>-1</code> which means that the | |
1077 * ascent is calculated from the line fonts. | |
1078 * | |
1079 * @param ascent the new ascent | |
1080 * | |
1081 * @exception IllegalArgumentException <ul> | |
1082 * <li>ERROR_INVALID_ARGUMENT - if the ascent is less than <code>-1</code></li> | |
1083 * </ul> | |
1084 * @exception DWTException <ul> | |
1085 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
1086 * </ul> | |
1087 * | |
1088 * @see #setDescent(int) | |
1089 * @see #getLineMetrics(int) | |
1090 */ | |
1091 public void setAscent (int ascent) { | |
1092 checkLayout (); | |
1093 if (ascent < -1) DWT.error(DWT.ERROR_INVALID_ARGUMENT); | |
1094 if (this.ascent is ascent) return; | |
1095 freeRuns(); | |
1096 this.ascent = ascent; | |
1097 } | |
1098 | |
1099 /** | |
1100 * Sets the descent of the receiver. The descent is distance in pixels | |
1101 * from the baseline to the bottom of the line and it is applied to all | |
1102 * lines. The default value is <code>-1</code> which means that the | |
1103 * descent is calculated from the line fonts. | |
1104 * | |
1105 * @param descent the new descent | |
1106 * | |
1107 * @exception IllegalArgumentException <ul> | |
1108 * <li>ERROR_INVALID_ARGUMENT - if the descent is less than <code>-1</code></li> | |
1109 * </ul> | |
1110 * @exception DWTException <ul> | |
1111 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
1112 * </ul> | |
1113 * | |
1114 * @see #setAscent(int) | |
1115 * @see #getLineMetrics(int) | |
1116 */ | |
1117 public void setDescent (int descent) { | |
1118 checkLayout (); | |
1119 if (descent < -1) DWT.error(DWT.ERROR_INVALID_ARGUMENT); | |
1120 if (this.descent is descent) return; | |
1121 freeRuns(); | |
1122 this.descent = descent; | |
1123 } | |
1124 | |
1125 /** | |
1126 * Sets the default font which will be used by the receiver | |
1127 * to draw and measure text. If the | |
1128 * argument is null, then a default font appropriate | |
1129 * for the platform will be used instead. Note that a text | |
1130 * style can override the default font. | |
1131 * | |
1132 * @param font the new font for the receiver, or null to indicate a default font | |
1133 * | |
1134 * @exception IllegalArgumentException <ul> | |
1135 * <li>ERROR_INVALID_ARGUMENT - if the font has been disposed</li> | |
1136 * </ul> | |
1137 * @exception DWTException <ul> | |
1138 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
1139 * </ul> | |
1140 */ | |
1141 public void setFont (Font font) { | |
1142 checkLayout (); | |
1143 if (font !is null && font.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT); | |
1144 Font oldFont = this.font; | |
1145 if (oldFont is font) return; | |
1146 this.font = font; | |
1147 if (oldFont !is null && oldFont.equals(font)) return; | |
1148 freeRuns(); | |
1149 } | |
1150 | |
1151 /** | |
1152 * Sets the indent of the receiver. This indent it applied of the first line of | |
1153 * each paragraph. | |
1154 * | |
1155 * @param indent new indent | |
1156 * | |
1157 * @exception DWTException <ul> | |
1158 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
1159 * </ul> | |
1160 * | |
1161 * @since 3.2 | |
1162 */ | |
1163 public void setIndent (int indent) { | |
1164 checkLayout (); | |
1165 if (indent < 0) return; | |
1166 if (this.indent is indent) return; | |
1167 freeRuns(); | |
1168 this.indent = indent; | |
1169 } | |
1170 | |
1171 /** | |
1172 * Sets the justification of the receiver. Note that the receiver's | |
1173 * width must be set in order to use justification. | |
1174 * | |
1175 * @param justify new justify | |
1176 * | |
1177 * @exception DWTException <ul> | |
1178 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
1179 * </ul> | |
1180 * | |
1181 * @since 3.2 | |
1182 */ | |
1183 public void setJustify (bool justify) { | |
1184 checkLayout (); | |
1185 if (justify is this.justify) return; | |
1186 freeRuns(); | |
1187 this.justify = justify; | |
1188 } | |
1189 | |
1190 /** | |
1191 * Sets the orientation of the receiver, which must be one | |
1192 * of <code>DWT.LEFT_TO_RIGHT</code> or <code>DWT.RIGHT_TO_LEFT</code>. | |
1193 * | |
1194 * @param orientation new orientation style | |
1195 * | |
1196 * @exception DWTException <ul> | |
1197 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
1198 * </ul> | |
1199 */ | |
1200 public void setOrientation(int orientation) { | |
1201 checkLayout(); | |
1202 int mask = DWT.LEFT_TO_RIGHT | DWT.RIGHT_TO_LEFT; | |
1203 orientation &= mask; | |
1204 if (orientation is 0) return; | |
1205 if ((orientation & DWT.LEFT_TO_RIGHT) !is 0) orientation = DWT.LEFT_TO_RIGHT; | |
1206 if (this.orientation is orientation) return; | |
1207 this.orientation = orientation; | |
1208 freeRuns(); | |
1209 } | |
1210 | |
1211 /** | |
1212 * Sets the offsets of the receiver's text segments. Text segments are used to | |
1213 * override the default behaviour of the bidirectional algorithm. | |
1214 * Bidirectional reordering can happen within a text segment but not | |
1215 * between two adjacent segments. | |
1216 * <p> | |
1217 * Each text segment is determined by two consecutive offsets in the | |
1218 * <code>segments</code> arrays. The first element of the array should | |
1219 * always be zero and the last one should always be equals to length of | |
1220 * the text. | |
1221 * </p> | |
1222 * | |
1223 * @param segments the text segments offset | |
1224 * | |
1225 * @exception DWTException <ul> | |
1226 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
1227 * </ul> | |
1228 */ | |
1229 public void setSegments(int[] segments) { | |
1230 checkLayout(); | |
1231 if (this.segments is null && segments is null) return; | |
1232 if (this.segments !is null && segments !isnull) { | |
1233 if (this.segments.length is segments.length) { | |
1234 int i; | |
1235 for (i = 0; i <segments.length; i++) { | |
1236 if (this.segments[i] !is segments[i]) break; | |
1237 } | |
1238 if (i is segments.length) return; | |
1239 } | |
1240 } | |
1241 freeRuns(); | |
1242 this.segments = segments; | |
1243 } | |
1244 | |
1245 /** | |
1246 * Sets the line spacing of the receiver. The line spacing | |
1247 * is the space left between lines. | |
1248 * | |
1249 * @param spacing the new line spacing | |
1250 * | |
1251 * @exception IllegalArgumentException <ul> | |
1252 * <li>ERROR_INVALID_ARGUMENT - if the spacing is negative</li> | |
1253 * </ul> | |
1254 * @exception DWTException <ul> | |
1255 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
1256 * </ul> | |
1257 */ | |
1258 public void setSpacing (int spacing) { | |
1259 checkLayout(); | |
1260 if (spacing < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT); | |
1261 if (this.spacing is spacing) return; | |
1262 freeRuns(); | |
1263 this.spacing = spacing; | |
1264 } | |
1265 | |
1266 /** | |
1267 * Sets the style of the receiver for the specified range. Styles previously | |
1268 * set for that range will be overwritten. The start and end offsets are | |
1269 * inclusive and will be clamped if out of range. | |
1270 * | |
1271 * @param style the style | |
1272 * @param start the start offset | |
1273 * @param end the end offset | |
1274 * | |
1275 * @exception DWTException <ul> | |
1276 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
1277 * </ul> | |
1278 */ | |
1279 public void setStyle (TextStyle style, int start, int end) { | |
1280 checkLayout(); | |
1281 int length = text.length(); | |
1282 if (length is 0) return; | |
1283 if (start > end) return; | |
1284 start = Math.min(Math.max(0, start), length - 1); | |
1285 end = Math.min(Math.max(0, end), length - 1); | |
1286 int low = -1; | |
1287 int high = styles.length; | |
1288 while (high - low > 1) { | |
1289 int index = (high + low) / 2; | |
1290 if (styles[index + 1].start > start) { | |
1291 high = index; | |
1292 } else { | |
1293 low = index; | |
1294 } | |
1295 } | |
1296 if (0 <= high && high < styles.length) { | |
1297 StyleItem item = styles[high]; | |
1298 if (item.start is start && styles[high + 1].start - 1 is end) { | |
1299 if (style is null) { | |
1300 if (item.style is null) return; | |
1301 } else { | |
1302 if (style.equals(item.style)) return; | |
1303 } | |
1304 } | |
1305 } | |
1306 freeRuns(); | |
1307 int modifyStart = high; | |
1308 int modifyEnd = modifyStart; | |
1309 while (modifyEnd < styles.length) { | |
1310 if (styles[modifyEnd + 1].start > end) break; | |
1311 modifyEnd++; | |
1312 } | |
1313 if (modifyStart is modifyEnd) { | |
1314 int styleStart = styles[modifyStart].start; | |
1315 int styleEnd = styles[modifyEnd + 1].start - 1; | |
1316 if (styleStart is start && styleEnd is end) { | |
1317 styles[modifyStart].style = style; | |
1318 return; | |
1319 } | |
1320 if (styleStart !is start && styleEnd !is end) { | |
1321 StyleItem[] newStyles = new StyleItem[styles.length + 2]; | |
1322 System.arraycopy(styles, 0, newStyles, 0, modifyStart + 1); | |
1323 StyleItem item = new StyleItem(); | |
1324 item.start = start; | |
1325 item.style = style; | |
1326 newStyles[modifyStart + 1] = item; | |
1327 item = new StyleItem(); | |
1328 item.start = end + 1; | |
1329 item.style = styles[modifyStart].style; | |
1330 newStyles[modifyStart + 2] = item; | |
1331 System.arraycopy(styles, modifyEnd + 1, newStyles, modifyEnd + 3, styles.length - modifyEnd - 1); | |
1332 styles = newStyles; | |
1333 return; | |
1334 } | |
1335 } | |
1336 if (start is styles[modifyStart].start) modifyStart--; | |
1337 if (end is styles[modifyEnd + 1].start - 1) modifyEnd++; | |
1338 int newLength = styles.length + 1 - (modifyEnd - modifyStart - 1); | |
1339 StyleItem[] newStyles = new StyleItem[newLength]; | |
1340 System.arraycopy(styles, 0, newStyles, 0, modifyStart + 1); | |
1341 StyleItem item = new StyleItem(); | |
1342 item.start = start; | |
1343 item.style = style; | |
1344 newStyles[modifyStart + 1] = item; | |
1345 styles[modifyEnd].start = end + 1; | |
1346 System.arraycopy(styles, modifyEnd, newStyles, modifyStart + 2, styles.length - modifyEnd); | |
1347 styles = newStyles; | |
1348 } | |
1349 | |
1350 /** | |
1351 * Sets the receiver's tab list. Each value in the tab list specifies | |
1352 * the space in pixels from the origin of the text layout to the respective | |
1353 * tab stop. The last tab stop width is repeated continuously. | |
1354 * | |
1355 * @param tabs the new tab list | |
1356 * | |
1357 * @exception DWTException <ul> | |
1358 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
1359 * </ul> | |
1360 */ | |
1361 public void setTabs(int[] tabs) { | |
1362 checkLayout(); | |
1363 if (this.tabs is null && tabs is null) return; | |
1364 if (this.tabs !is null && tabs !isnull) { | |
1365 if (this.tabs.length is tabs.length) { | |
1366 int i; | |
1367 for (i = 0; i < tabs.length; i++) { | |
1368 if (this.tabs[i] !is tabs[i]) break; | |
1369 } | |
1370 if (i is tabs.length) return; | |
1371 } | |
1372 } | |
1373 freeRuns(); | |
1374 this.tabs = tabs; | |
1375 } | |
1376 | |
1377 /** | |
1378 * Sets the receiver's text. | |
1379 * | |
1380 * @param text the new text | |
1381 * | |
1382 * @exception IllegalArgumentException <ul> | |
1383 * <li>ERROR_NULL_ARGUMENT - if the text is null</li> | |
1384 * </ul> | |
1385 * @exception DWTException <ul> | |
1386 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
1387 * </ul> | |
1388 */ | |
1389 public void setText (String text) { | |
1390 checkLayout (); | |
1391 if (text is null) DWT.error(DWT.ERROR_NULL_ARGUMENT); | |
1392 if (text.equals(this.text)) return; | |
1393 freeRuns(); | |
1394 this.text = text; | |
1395 styles = new StyleItem[2]; | |
1396 styles[0] = new StyleItem(); | |
1397 styles[1] = new StyleItem(); | |
1398 styles[styles.length - 1].start = text.length(); | |
1399 } | |
1400 | |
1401 /** | |
1402 * Sets the line width of the receiver, which determines how | |
1403 * text should be wrapped and aligned. The default value is | |
1404 * <code>-1</code> which means wrapping is disabled. | |
1405 * | |
1406 * @param width the new width | |
1407 * | |
1408 * @exception IllegalArgumentException <ul> | |
1409 * <li>ERROR_INVALID_ARGUMENT - if the width is <code>0</code> or less than <code>-1</code></li> | |
1410 * </ul> | |
1411 * @exception DWTException <ul> | |
1412 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | |
1413 * </ul> | |
1414 * | |
1415 * @see #setAlignment(int) | |
1416 */ | |
1417 public void setWidth (int width) { | |
1418 checkLayout(); | |
1419 if (width < -1 || width is 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT); | |
1420 if (this.wrapWidth is width) return; | |
1421 freeRuns(); | |
1422 this.wrapWidth = width; | |
1423 } | |
1424 | |
1425 /** | |
1426 * Returns a string containing a concise, human-readable | |
1427 * description of the receiver. | |
1428 * | |
1429 * @return a string representation of the receiver | |
1430 */ | |
1431 public String toString () { | |
1432 if (isDisposed()) return "TextLayout {*DISPOSED*}"; | |
1433 return "TextLayout {" + text + "}"; | |
1434 } | |
1435 | |
1436 /* | |
1437 * Translate a client offset to an internal offset | |
1438 */ | |
1439 int translateOffset (int offset) { | |
1440 return offset; | |
1441 } | |
1442 | |
1443 /* | |
1444 * Translate an internal offset to a client offset | |
1445 */ | |
1446 int untranslateOffset (int offset) { | |
1447 return offset; | |
1448 } | |
1449 | |
1450 } |