KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > swt > graphics > TextLayout


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 package org.eclipse.swt.graphics;
12
13 import org.eclipse.swt.internal.*;
14 import org.eclipse.swt.internal.gdip.*;
15 import org.eclipse.swt.internal.win32.*;
16 import org.eclipse.swt.*;
17
18 /**
19  * <code>TextLayout</code> is a graphic object that represents
20  * styled text.
21  * <p>
22  * Instances of this class provide support for drawing, cursor
23  * navigation, hit testing, text wrapping, alignment, tab expansion
24  * line breaking, etc. These are aspects required for rendering internationalized text.
25  * </p><p>
26  * Application code must explicitly invoke the <code>TextLayout#dispose()</code>
27  * method to release the operating system resources managed by each instance
28  * when those instances are no longer required.
29  * </p>
30  *
31  * @since 3.0
32  */

33 public final class TextLayout extends Resource {
34     Font font;
35     String JavaDoc text, segmentsText;
36     int lineSpacing;
37     int ascent, descent;
38     int alignment;
39     int wrapWidth;
40     int orientation;
41     int indent;
42     boolean justify;
43     int[] tabs;
44     int[] segments;
45     StyleItem[] styles;
46
47     StyleItem[] allRuns;
48     StyleItem[][] runs;
49     int[] lineOffset, lineY, lineWidth;
50     int mLangFontLink2;
51     
52     static final char LTR_MARK = '\u200E', RTL_MARK = '\u200F';
53     static final int SCRIPT_VISATTR_SIZEOF = 2;
54     static final int GOFFSET_SIZEOF = 8;
55     static final byte[] CLSID_CMultiLanguage = new byte[16];
56     static final byte[] IID_IMLangFontLink2 = new byte[16];
57     static {
58         OS.IIDFromString("{275c23e2-3747-11d0-9fea-00aa003f8646}\0".toCharArray(), CLSID_CMultiLanguage);
59         OS.IIDFromString("{DCCFC162-2B38-11d2-B7EC-00C04F8F5D9A}\0".toCharArray(), IID_IMLangFontLink2);
60     }
61     
62     class StyleItem {
63         TextStyle style;
64         int start, length;
65         boolean lineBreak, softBreak, tab;
66         
67         /*Script cache and analysis */
68         SCRIPT_ANALYSIS analysis;
69         int psc = 0;
70         
71         /*Shape info (malloc when the run is shaped) */
72         int glyphs;
73         int glyphCount;
74         int clusters;
75         int visAttrs;
76         
77         /*Place info (malloc when the run is placed) */
78         int advances;
79         int goffsets;
80         int width;
81         int ascent;
82         int descent;
83         int leading;
84         int x;
85
86         /* Justify info (malloc during computeRuns) */
87         int justify;
88
89         /* ScriptBreak */
90         int psla;
91
92         int fallbackFont;
93     
94     void free() {
95         int hHeap = OS.GetProcessHeap();
96         if (psc != 0) {
97             OS.ScriptFreeCache (psc);
98             OS.HeapFree(hHeap, 0, psc);
99             psc = 0;
100         }
101         if (glyphs != 0) {
102             OS.HeapFree(hHeap, 0, glyphs);
103             glyphs = 0;
104             glyphCount = 0;
105         }
106         if (clusters != 0) {
107             OS.HeapFree(hHeap, 0, clusters);
108             clusters = 0;
109         }
110         if (visAttrs != 0) {
111             OS.HeapFree(hHeap, 0, visAttrs);
112             visAttrs = 0;
113         }
114         if (advances != 0) {
115             OS.HeapFree(hHeap, 0, advances);
116             advances = 0;
117         }
118         if (goffsets != 0) {
119             OS.HeapFree(hHeap, 0, goffsets);
120             goffsets = 0;
121         }
122         if (justify != 0) {
123             OS.HeapFree(hHeap, 0, justify);
124             justify = 0;
125         }
126         if (psla != 0) {
127             OS.HeapFree(hHeap, 0, psla);
128             psla = 0;
129         }
130         if (fallbackFont != 0) {
131             if (mLangFontLink2 != 0) {
132                 /* ReleaseFont() */
133                 OS.VtblCall(8, mLangFontLink2, fallbackFont);
134             }
135             fallbackFont = 0;
136         }
137         width = ascent = descent = x = 0;
138         lineBreak = softBreak = false;
139     }
140     public String JavaDoc toString () {
141         return "StyleItem {" + start + ", " + style + "}";
142     }
143     }
144
145 /**
146  * Constructs a new instance of this class on the given device.
147  * <p>
148  * You must dispose the text layout when it is no longer required.
149  * </p>
150  *
151  * @param device the device on which to allocate the text layout
152  *
153  * @exception IllegalArgumentException <ul>
154  * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
155  * </ul>
156  *
157  * @see #dispose()
158  */

159 public TextLayout (Device device) {
160     if (device == null) device = Device.getDevice();
161     if (device == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
162     this.device = device;
163     wrapWidth = ascent = descent = -1;
164     lineSpacing = 0;
165     orientation = SWT.LEFT_TO_RIGHT;
166     styles = new StyleItem[2];
167     styles[0] = new StyleItem();
168     styles[1] = new StyleItem();
169     text = ""; //$NON-NLS-1$
170
int[] ppv = new int[1];
171     OS.OleInitialize(0);
172     if (OS.CoCreateInstance(CLSID_CMultiLanguage, 0, OS.CLSCTX_INPROC_SERVER, IID_IMLangFontLink2, ppv) == OS.S_OK) {
173         mLangFontLink2 = ppv[0];
174     }
175     if (device.tracking) device.new_Object(this);
176 }
177
178 void breakRun(StyleItem run) {
179     if (run.psla != 0) return;
180     char[] chars = new char[run.length];
181     segmentsText.getChars(run.start, run.start + run.length, chars, 0);
182     int hHeap = OS.GetProcessHeap();
183     run.psla = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, SCRIPT_LOGATTR.sizeof * chars.length);
184     if (run.psla == 0) SWT.error(SWT.ERROR_NO_HANDLES);
185     OS.ScriptBreak(chars, chars.length, run.analysis, run.psla);
186 }
187
188 void checkItem (int hDC, StyleItem item) {
189     if (item.fallbackFont != 0) {
190         /*
191         * Feature in Windows. The fallback font returned by the MLang service
192         * can be disposed by some other client running in the same thread.
193         * For example, disposing a Browser widget internally releases all fonts
194         * in the MLang cache. The fix is to use GetObject() to detect if the
195         * font was disposed and reshape the run.
196         */

197         LOGFONT logFont = OS.IsUnicode ? (LOGFONT)new LOGFONTW() : new LOGFONTA();
198         if (OS.GetObject(item.fallbackFont, LOGFONT.sizeof, logFont) == 0) {
199             item.free();
200             OS.SelectObject(hDC, getItemFont(item));
201             shape(hDC, item);
202         }
203     }
204 }
205
206 void checkLayout () {
207     if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
208 }
209
210 /*
211 * Compute the runs: itemize, shape, place, and reorder the runs.
212 * Break paragraphs into lines, wraps the text, and initialize caches.
213 */

214 void computeRuns (GC gc) {
215     if (runs != null) return;
216     int hDC = gc != null ? gc.handle : device.internal_new_GC(null);
217     int srcHdc = OS.CreateCompatibleDC(hDC);
218     allRuns = itemize();
219     for (int i=0; i<allRuns.length - 1; i++) {
220         StyleItem run = allRuns[i];
221         OS.SelectObject(srcHdc, getItemFont(run));
222         shape(srcHdc, run);
223     }
224     SCRIPT_LOGATTR logAttr = new SCRIPT_LOGATTR();
225     SCRIPT_PROPERTIES properties = new SCRIPT_PROPERTIES();
226     int lineWidth = indent, lineStart = 0, lineCount = 1;
227     for (int i=0; i<allRuns.length - 1; i++) {
228         StyleItem run = allRuns[i];
229         if (run.length == 1) {
230             char ch = segmentsText.charAt(run.start);
231             switch (ch) {
232                 case '\t': {
233                     run.tab = true;
234                     if (tabs == null) break;
235                     int tabsLength = tabs.length, j;
236                     for (j = 0; j < tabsLength; j++) {
237                         if (tabs[j] > lineWidth) {
238                             run.width = tabs[j] - lineWidth;
239                             break;
240                         }
241                     }
242                     if (j == tabsLength) {
243                         int tabX = tabs[tabsLength-1];
244                         int lastTabWidth = tabsLength > 1 ? tabs[tabsLength-1] - tabs[tabsLength-2] : tabs[0];
245                         if (lastTabWidth > 0) {
246                             while (tabX <= lineWidth) tabX += lastTabWidth;
247                             run.width = tabX - lineWidth;
248                         }
249                     }
250                     break;
251                 }
252                 case '\n': {
253                     run.lineBreak = true;
254                     break;
255                 }
256                 case '\r': {
257                     run.lineBreak = true;
258                     StyleItem next = allRuns[i + 1];
259                     if (next.length != 0 && segmentsText.charAt(next.start) == '\n') {
260                         run.length += 1;
261                         next.free();
262                         i++;
263                     }
264                     break;
265                 }
266             }
267         }
268         if (wrapWidth != -1 && lineWidth + run.width > wrapWidth && !run.tab) {
269             int start = 0;
270             int[] piDx = new int[run.length];
271             if (run.style != null && run.style.metrics != null) {
272                 piDx[0] = run.width;
273             } else {
274                 OS.ScriptGetLogicalWidths(run.analysis, run.length, run.glyphCount, run.advances, run.clusters, run.visAttrs, piDx);
275             }
276             int width = 0, maxWidth = wrapWidth - lineWidth;
277             while (width + piDx[start] < maxWidth) {
278                 width += piDx[start++];
279             }
280             int firstStart = start;
281             int firstIndice = i;
282             while (i >= lineStart) {
283                 breakRun(run);
284                 while (start >= 0) {
285                     OS.MoveMemory(logAttr, run.psla + (start * SCRIPT_LOGATTR.sizeof), SCRIPT_LOGATTR.sizeof);
286                     if (logAttr.fSoftBreak || logAttr.fWhiteSpace) break;
287                     start--;
288                 }
289                 
290                 /*
291                 * Bug in Windows. For some reason Uniscribe sets the fSoftBreak flag for the first letter
292                 * after a letter with an accent. This cause a break line to be set in the middle of a word.
293                 * The fix is to detect the case and ignore fSoftBreak forcing the algorithm keep searching.
294                 */

295                 if (start == 0 && i != lineStart && !run.tab) {
296                     if (logAttr.fSoftBreak && !logAttr.fWhiteSpace) {
297                         OS.MoveMemory(properties, device.scripts[run.analysis.eScript], SCRIPT_PROPERTIES.sizeof);
298                         int langID = properties.langid;
299                         StyleItem pRun = allRuns[i - 1];
300                         OS.MoveMemory(properties, device.scripts[pRun.analysis.eScript], SCRIPT_PROPERTIES.sizeof);
301                         if (properties.langid == langID || langID == OS.LANG_NEUTRAL || properties.langid == OS.LANG_NEUTRAL) {
302                             breakRun(pRun);
303                             OS.MoveMemory(logAttr, pRun.psla + ((pRun.length - 1) * SCRIPT_LOGATTR.sizeof), SCRIPT_LOGATTR.sizeof);
304                             if (!logAttr.fWhiteSpace) start = -1;
305                         }
306                     }
307                 }
308                 if (start >= 0 || i == lineStart) break;
309                 run = allRuns[--i];
310                 start = run.length - 1;
311             }
312             if (start == 0 && i != lineStart && !run.tab) {
313                 run = allRuns[--i];
314             } else if (start <= 0 && i == lineStart) {
315                 i = firstIndice;
316                 run = allRuns[i];
317                 start = Math.max(1, firstStart);
318             }
319             breakRun(run);
320             while (start < run.length) {
321                 OS.MoveMemory(logAttr, run.psla + (start * SCRIPT_LOGATTR.sizeof), SCRIPT_LOGATTR.sizeof);
322                 if (!logAttr.fWhiteSpace) break;
323                 start++;
324             }
325             if (0 < start && start < run.length) {
326                 StyleItem newRun = new StyleItem();
327                 newRun.start = run.start + start;
328                 newRun.length = run.length - start;
329                 newRun.style = run.style;
330                 newRun.analysis = run.analysis;
331                 run.free();
332                 run.length = start;
333                 OS.SelectObject(srcHdc, getItemFont(run));
334                 shape (srcHdc, run);
335                 OS.SelectObject(srcHdc, getItemFont(newRun));
336                 shape (srcHdc, newRun);
337                 StyleItem[] newAllRuns = new StyleItem[allRuns.length + 1];
338                 System.arraycopy(allRuns, 0, newAllRuns, 0, i + 1);
339                 System.arraycopy(allRuns, i + 1, newAllRuns, i + 2, allRuns.length - i - 1);
340                 allRuns = newAllRuns;
341                 allRuns[i + 1] = newRun;
342             }
343             if (i != allRuns.length - 2) {
344                 run.softBreak = run.lineBreak = true;
345             }
346         }
347         lineWidth += run.width;
348         if (run.lineBreak) {
349             lineStart = i + 1;
350             lineWidth = run.softBreak ? 0 : indent;
351             lineCount++;
352         }
353     }
354     lineWidth = 0;
355     runs = new StyleItem[lineCount][];
356     lineOffset = new int[lineCount + 1];
357     lineY = new int[lineCount + 1];
358     this.lineWidth = new int[lineCount];
359     int lineRunCount = 0, line = 0;
360     int ascent = Math.max(0, this.ascent);
361     int descent = Math.max(0, this.descent);
362     StyleItem[] lineRuns = new StyleItem[allRuns.length];
363     for (int i=0; i<allRuns.length; i++) {
364         StyleItem run = allRuns[i];
365         lineRuns[lineRunCount++] = run;
366         lineWidth += run.width;
367         ascent = Math.max(ascent, run.ascent);
368         descent = Math.max(descent, run.descent);
369         if (run.lineBreak || i == allRuns.length - 1) {
370             /* Update the run metrics if the last run is a hard break. */
371             if (lineRunCount == 1 && i == allRuns.length - 1) {
372                 TEXTMETRIC lptm = OS.IsUnicode ? (TEXTMETRIC)new TEXTMETRICW() : new TEXTMETRICA();
373                 OS.SelectObject(srcHdc, getItemFont(run));
374                 OS.GetTextMetrics(srcHdc, lptm);
375                 run.ascent = lptm.tmAscent;
376                 run.descent = lptm.tmDescent;
377                 ascent = Math.max(ascent, run.ascent);
378                 descent = Math.max(descent, run.descent);
379             }
380             runs[line] = new StyleItem[lineRunCount];
381             System.arraycopy(lineRuns, 0, runs[line], 0, lineRunCount);
382             
383             if (justify && wrapWidth != -1 && run.softBreak && lineWidth > 0) {
384                 if (line == 0) {
385                     lineWidth += indent;
386                 } else {
387                     StyleItem[] previousLine = runs[line - 1];
388                     StyleItem previousRun = previousLine[previousLine.length - 1];
389                     if (previousRun.lineBreak && !previousRun.softBreak) {
390                         lineWidth += indent;
391                     }
392                 }
393                 int hHeap = OS.GetProcessHeap();
394                 int newLineWidth = 0;
395                 for (int j = 0; j < runs[line].length; j++) {
396                     StyleItem item = runs[line][j];
397                     int iDx = item.width * wrapWidth / lineWidth;
398                     if (iDx != item.width) {
399                         item.justify = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, item.glyphCount * 4);
400                         if (item.justify == 0) SWT.error(SWT.ERROR_NO_HANDLES);
401                         OS.ScriptJustify(item.visAttrs, item.advances, item.glyphCount, iDx - item.width, 2, item.justify);
402                         item.width = iDx;
403                     }
404                     newLineWidth += item.width;
405                 }
406                 lineWidth = newLineWidth;
407             }
408             this.lineWidth[line] = lineWidth;
409             
410             StyleItem lastRun = runs[line][lineRunCount - 1];
411             int lastOffset = lastRun.start + lastRun.length;
412             runs[line] = reorder(runs[line], i == allRuns.length - 1);
413             lastRun = runs[line][lineRunCount - 1];
414             if (run.softBreak && run != lastRun) {
415                 run.softBreak = run.lineBreak = false;
416                 lastRun.softBreak = lastRun.lineBreak = true;
417             }
418             
419             lineWidth = getLineIndent(line);
420             for (int j = 0; j < runs[line].length; j++) {
421                 runs[line][j].x = lineWidth;
422                 lineWidth += runs[line][j].width;
423             }
424             line++;
425             lineY[line] = lineY[line - 1] + ascent + descent + lineSpacing;
426             lineOffset[line] = lastOffset;
427             lineRunCount = lineWidth = 0;
428             ascent = Math.max(0, this.ascent);
429             descent = Math.max(0, this.descent);
430         }
431     }
432     if (srcHdc != 0) OS.DeleteDC(srcHdc);
433     if (gc == null) device.internal_dispose_GC(hDC, null);
434 }
435
436 /**
437  * Disposes of the operating system resources associated with
438  * the text layout. Applications must dispose of all allocated text layouts.
439  */

440 public void dispose () {
441     if (device == null) return;
442     freeRuns();
443     font = null;
444     text = null;
445     segmentsText = null;
446     tabs = null;
447     styles = null;
448     runs = null;
449     lineOffset = null;
450     lineY = null;
451     lineWidth = null;
452     if (mLangFontLink2 != 0) {
453         /* Release() */
454         OS.VtblCall(2, mLangFontLink2);
455         mLangFontLink2 = 0;
456     }
457     OS.OleUninitialize();
458     if (device.tracking) device.dispose_Object(this);
459     device = null;
460 }
461
462 /**
463  * Draws the receiver's text using the specified GC at the specified
464  * point.
465  *
466  * @param gc the GC to draw
467  * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn
468  * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn
469  *
470  * @exception SWTException <ul>
471  * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
472  * </ul>
473  * @exception IllegalArgumentException <ul>
474  * <li>ERROR_NULL_ARGUMENT - if the gc is null</li>
475  * </ul>
476  */

477 public void draw (GC gc, int x, int y) {
478     draw(gc, x, y, -1, -1, null, null);
479 }
480
481 /**
482  * Draws the receiver's text using the specified GC at the specified
483  * point.
484  *
485  * @param gc the GC to draw
486  * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn
487  * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn
488  * @param selectionStart the offset where the selections starts, or -1 indicating no selection
489  * @param selectionEnd the offset where the selections ends, or -1 indicating no selection
490  * @param selectionForeground selection foreground, or NULL to use the system default color
491  * @param selectionBackground selection background, or NULL to use the system default color
492  *
493  * @exception SWTException <ul>
494  * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
495  * </ul>
496  * @exception IllegalArgumentException <ul>
497  * <li>ERROR_NULL_ARGUMENT - if the gc is null</li>
498  * </ul>
499  */

500 public void draw (GC gc, int x, int y, int selectionStart, int selectionEnd, Color selectionForeground, Color selectionBackground) {
501     draw(gc, x, y, selectionStart, selectionEnd, selectionForeground, selectionBackground, 0);
502 }
503
504 /**
505  * Draws the receiver's text using the specified GC at the specified
506  * point.
507  * <p>
508  * The parameter <code>flags</code> can include one of <code>SWT.DELIMITER_SELECTION</code>
509  * or <code>SWT.FULL_SELECTION</code> to specify the selection behavior on all lines except
510  * for the last line, and can also include <code>SWT.LAST_LINE_SELECTION</code> to extend
511  * the specified selection behavior to the last line.
512  * </p>
513  * @param gc the GC to draw
514  * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn
515  * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn
516  * @param selectionStart the offset where the selections starts, or -1 indicating no selection
517  * @param selectionEnd the offset where the selections ends, or -1 indicating no selection
518  * @param selectionForeground selection foreground, or NULL to use the system default color
519  * @param selectionBackground selection background, or NULL to use the system default color
520  * @param flags drawing options
521  *
522  * @exception SWTException <ul>
523  * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
524  * </ul>
525  * @exception IllegalArgumentException <ul>
526  * <li>ERROR_NULL_ARGUMENT - if the gc is null</li>
527  * </ul>
528  *
529  * @since 3.3
530  */

531 public void draw (GC gc, int x, int y, int selectionStart, int selectionEnd, Color selectionForeground, Color selectionBackground, int flags) {
532     checkLayout();
533     computeRuns(gc);
534     if (gc == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
535     if (gc.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
536     if (selectionForeground != null && selectionForeground.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
537     if (selectionBackground != null && selectionBackground.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
538     int length = text.length();
539     if (length == 0 && flags == 0) return;
540     int hdc = gc.handle;
541     Rectangle clip = gc.getClipping();
542     GCData data = gc.data;
543     int gdipGraphics = data.gdipGraphics;
544     int foreground = data.foreground;
545     int alpha = data.alpha;
546     boolean gdip = gdipGraphics != 0 && (alpha != 0xFF || data.foregroundPattern != null);
547     int clipRgn = 0;
548     float[] lpXform = null;
549     Rect gdipRect = new Rect();
550     if (gdipGraphics != 0 && !gdip) {
551         int matrix = Gdip.Matrix_new(1, 0, 0, 1, 0, 0);
552         if (matrix == 0) SWT.error(SWT.ERROR_NO_HANDLES);
553         Gdip.Graphics_GetTransform(gdipGraphics, matrix);
554         int identity = gc.identity();
555         Gdip.Matrix_Invert(identity);
556         Gdip.Matrix_Multiply(matrix, identity, Gdip.MatrixOrderAppend);
557         Gdip.Matrix_delete(identity);
558         if (!Gdip.Matrix_IsIdentity(matrix)) {
559             lpXform = new float[6];
560             Gdip.Matrix_GetElements(matrix, lpXform);
561         }
562         Gdip.Matrix_delete(matrix);
563         if ((data.style & SWT.MIRRORED) != 0 && lpXform != null) {
564             gdip = true;
565             lpXform = null;
566         } else {
567             Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeNone);
568             int rgn = Gdip.Region_new();
569             Gdip.Graphics_GetClip(gdipGraphics, rgn);
570             if (!Gdip.Region_IsInfinite(rgn, gdipGraphics)) {
571                 clipRgn = Gdip.Region_GetHRGN(rgn, gdipGraphics);
572             }
573             Gdip.Region_delete(rgn);
574             Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeHalf);
575             hdc = Gdip.Graphics_GetHDC(gdipGraphics);
576         }
577     }
578     int foregroundBrush = 0, state = 0;
579     if (gdip) {
580         gc.checkGC(GC.FOREGROUND);
581         foregroundBrush = gc.getFgBrush();
582     } else {
583         state = OS.SaveDC(hdc);
584         if ((data.style & SWT.MIRRORED) != 0) {
585             OS.SetLayout(hdc, OS.GetLayout(hdc) | OS.LAYOUT_RTL);
586         }
587         if (lpXform != null) {
588             OS.SetGraphicsMode(hdc, OS.GM_ADVANCED);
589             OS.SetWorldTransform(hdc, lpXform);
590         }
591         if (clipRgn != 0) {
592             OS.SelectClipRgn(hdc, clipRgn);
593             OS.DeleteObject(clipRgn);
594         }
595     }
596     boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1;
597     if (hasSelection || (flags & SWT.LAST_LINE_SELECTION) != 0) {
598         selectionStart = Math.min(Math.max(0, selectionStart), length - 1);
599         selectionEnd = Math.min(Math.max(0, selectionEnd), length - 1);
600         if (selectionForeground == null) selectionForeground = device.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT);
601         if (selectionBackground == null) selectionBackground = device.getSystemColor(SWT.COLOR_LIST_SELECTION);
602         selectionStart = translateOffset(selectionStart);
603         selectionEnd = translateOffset(selectionEnd);
604     }
605     RECT rect = new RECT();
606     int selBrush = 0, selPen = 0, selBrushFg = 0;
607     if (hasSelection || (flags & SWT.LAST_LINE_SELECTION) != 0) {
608         if (gdip) {
609             int bg = selectionBackground.handle;
610             int argb = ((alpha & 0xFF) << 24) | ((bg >> 16) & 0xFF) | (bg & 0xFF00) | ((bg & 0xFF) << 16);
611             int color = Gdip.Color_new(argb);
612             selBrush = Gdip.SolidBrush_new(color);
613             Gdip.Color_delete(color);
614             int fg = selectionForeground.handle;
615             argb = ((alpha & 0xFF) << 24) | ((fg >> 16) & 0xFF) | (fg & 0xFF00) | ((fg & 0xFF) << 16);
616             color = Gdip.Color_new(argb);
617             selBrushFg = Gdip.SolidBrush_new(color);
618             selPen = Gdip.Pen_new(selBrushFg, 1);
619             Gdip.Color_delete(color);
620         } else {
621             selBrush = OS.CreateSolidBrush(selectionBackground.handle);
622             selPen = OS.CreatePen(OS.PS_SOLID, 1, selectionForeground.handle);
623         }
624     }
625     int offset = (orientation & SWT.RIGHT_TO_LEFT) != 0 ? -1 : 0;
626     OS.SetBkMode(hdc, OS.TRANSPARENT);
627     for (int line=0; line<runs.length; line++) {
628         int drawX = x + getLineIndent(line);
629         int drawY = y + lineY[line];
630         StyleItem[] lineRuns = runs[line];
631         int lineHeight = lineY[line+1] - lineY[line];
632         if (flags != 0 && (hasSelection || (flags & SWT.LAST_LINE_SELECTION) != 0)) {
633             boolean extents = false;
634             if (line == runs.length - 1 && (flags & SWT.LAST_LINE_SELECTION) != 0) {
635                 extents = true;
636             } else {
637                 StyleItem run = lineRuns[lineRuns.length - 1];
638                 if (run.lineBreak && !run.softBreak) {
639                     if (selectionStart <= run.start && run.start <= selectionEnd) extents = true;
640                 } else {
641                     int endOffset = run.start + run.length - 1;
642                     if (selectionStart <= endOffset && endOffset < selectionEnd && (flags & SWT.FULL_SELECTION) != 0) {
643                         extents = true;
644                     }
645                 }
646             }
647             if (extents) {
648                 int width;
649                 if ((flags & SWT.FULL_SELECTION) != 0) {
650                     width = OS.IsWin95 ? 0x7FFF : 0x6FFFFFF;
651                 } else {
652                     width = (lineHeight - lineSpacing) / 3;
653                 }
654                 if (gdip) {
655                     Gdip.Graphics_FillRectangle(gdipGraphics, selBrush, drawX + lineWidth[line], drawY, width, lineHeight - lineSpacing);
656                 } else {
657                     OS.SelectObject(hdc, selBrush);
658                     OS.PatBlt(hdc, drawX + lineWidth[line], drawY, width, lineHeight - lineSpacing, OS.PATCOPY);
659                 }
660             }
661         }
662         if (drawX > clip.x + clip.width) continue;
663         if (drawX + lineWidth[line] < clip.x) continue;
664         int baseline = Math.max(0, this.ascent);
665         for (int i = 0; i < lineRuns.length; i++) {
666             baseline = Math.max(baseline, lineRuns[i].ascent);
667         }
668         int alignmentX = drawX;
669         for (int i = 0; i < lineRuns.length; i++) {
670             StyleItem run = lineRuns[i];
671             if (run.length == 0) continue;
672             if (drawX > clip.x + clip.width) break;
673             if (drawX + run.width >= clip.x) {
674                 if (!run.lineBreak || run.softBreak) {
675                     int end = run.start + run.length - 1;
676                     boolean fullSelection = hasSelection && selectionStart <= run.start && selectionEnd >= end;
677                     if (fullSelection) {
678                         if (gdip) {
679                             Gdip.Graphics_FillRectangle(gdipGraphics, selBrush, drawX, drawY, run.width, lineHeight - lineSpacing);
680                         } else {
681                             OS.SelectObject(hdc, selBrush);
682                             OS.PatBlt(hdc, drawX, drawY, run.width, lineHeight - lineSpacing, OS.PATCOPY);
683                         }
684                     } else {
685                         if (run.style != null && run.style.background != null) {
686                             int bg = run.style.background.handle;
687                             int drawRunY = drawY + (baseline - run.ascent);
688                             if (gdip) {
689                                 int argb = ((alpha & 0xFF) << 24) | ((bg >> 16) & 0xFF) | (bg & 0xFF00) | ((bg & 0xFF) << 16);
690                                 int color = Gdip.Color_new(argb);
691                                 int brush = Gdip.SolidBrush_new(color);
692                                 Gdip.Graphics_FillRectangle(gdipGraphics, brush, drawX, drawRunY, run.width, run.ascent + run.descent);
693                                 Gdip.Color_delete(color);
694                                 Gdip.SolidBrush_delete(brush);
695                             } else {
696                                 int hBrush = OS.CreateSolidBrush (bg);
697                                 int oldBrush = OS.SelectObject(hdc, hBrush);
698                                 OS.PatBlt(hdc, drawX, drawRunY, run.width, run.ascent + run.descent, OS.PATCOPY);
699                                 OS.SelectObject(hdc, oldBrush);
700                                 OS.DeleteObject(hBrush);
701                             }
702                         }
703                         boolean partialSelection = hasSelection && !(selectionStart > end || run.start > selectionEnd);
704                         if (partialSelection) {
705                             int selStart = Math.max(selectionStart, run.start) - run.start;
706                             int selEnd = Math.min(selectionEnd, end) - run.start;
707                             int cChars = run.length;
708                             int gGlyphs = run.glyphCount;
709                             int[] piX = new int[1];
710                             int advances = run.justify != 0 ? run.justify : run.advances;
711                             OS.ScriptCPtoX(selStart, false, cChars, gGlyphs, run.clusters, run.visAttrs, advances, run.analysis, piX);
712                             int runX = (orientation & SWT.RIGHT_TO_LEFT) != 0 ? run.width - piX[0] : piX[0];
713                             rect.left = drawX + runX;
714                             rect.top = drawY;
715                             OS.ScriptCPtoX(selEnd, true, cChars, gGlyphs, run.clusters, run.visAttrs, advances, run.analysis, piX);
716                             runX = (orientation & SWT.RIGHT_TO_LEFT) != 0 ? run.width - piX[0] : piX[0];
717                             rect.right = drawX + runX;
718                             rect.bottom = drawY + lineHeight - lineSpacing;
719                             if (gdip) {
720                                 Gdip.Graphics_FillRectangle(gdipGraphics, selBrush, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
721                             } else {
722                                 OS.SelectObject(hdc, selBrush);
723                                 OS.PatBlt(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY);
724                             }
725                         }
726                     }
727                 }
728             }
729             drawX += run.width;
730         }
731         drawX = alignmentX;
732         for (int i = 0; i < lineRuns.length; i++) {
733             StyleItem run = lineRuns[i];
734             if (run.length == 0) continue;
735             if (drawX > clip.x + clip.width) break;
736             if (drawX + run.width >= clip.x) {
737                 if (!run.tab && (!run.lineBreak || run.softBreak) && !(run.style != null && run.style.metrics != null)) {
738                     int end = run.start + run.length - 1;
739                     boolean fullSelection = hasSelection && selectionStart <= run.start && selectionEnd >= end;
740                     boolean partialSelection = hasSelection && !fullSelection && !(selectionStart > end || run.start > selectionEnd);
741                     checkItem(hdc, run);
742                     OS.SelectObject(hdc, getItemFont(run));
743                     int drawRunY = drawY + (baseline - run.ascent);
744                     if (partialSelection) {
745                         int selStart = Math.max(selectionStart, run.start) - run.start;
746                         int selEnd = Math.min(selectionEnd, end) - run.start;
747                         int cChars = run.length;
748                         int gGlyphs = run.glyphCount;
749                         int[] piX = new int[1];
750                         int advances = run.justify != 0 ? run.justify : run.advances;
751                         OS.ScriptCPtoX(selStart, false, cChars, gGlyphs, run.clusters, run.visAttrs, advances, run.analysis, piX);
752                         int runX = (orientation & SWT.RIGHT_TO_LEFT) != 0 ? run.width - piX[0] : piX[0];
753                         rect.left = drawX + runX;
754                         rect.top = drawY;
755                         OS.ScriptCPtoX(selEnd, true, cChars, gGlyphs, run.clusters, run.visAttrs, advances, run.analysis, piX);
756                         runX = (orientation & SWT.RIGHT_TO_LEFT) != 0 ? run.width - piX[0] : piX[0];
757                         rect.right = drawX + runX;
758                         rect.bottom = drawY + lineHeight;
759                     }
760                     if (gdip) {
761                         OS.BeginPath(hdc);
762                         OS.ScriptTextOut(hdc, run.psc, drawX, drawRunY, 0, null, run.analysis , 0, 0, run.glyphs, run.glyphCount, run.advances, run.justify, run.goffsets);
763                         OS.EndPath(hdc);
764                         int count = OS.GetPath(hdc, null, null, 0);
765                         int[] points = new int[count*2];
766                         byte[] types = new byte[count];
767                         OS.GetPath(hdc, points, types, count);
768                         for (int typeIndex = 0; typeIndex < types.length; typeIndex++) {
769                             int newType = 0;
770                             int type = types[typeIndex] & 0xFF;
771                             switch (type & ~OS.PT_CLOSEFIGURE) {
772                                 case OS.PT_MOVETO: newType = Gdip.PathPointTypeStart; break;
773                                 case OS.PT_LINETO: newType = Gdip.PathPointTypeLine; break;
774                                 case OS.PT_BEZIERTO: newType = Gdip.PathPointTypeBezier; break;
775                             }
776                             if ((type & OS.PT_CLOSEFIGURE) != 0) newType |= Gdip.PathPointTypeCloseSubpath;
777                             types[typeIndex] = (byte)newType;
778                         }
779                         int path = Gdip.GraphicsPath_new(points, types, count, Gdip.FillModeAlternate);
780                         if (path == 0) SWT.error(SWT.ERROR_NO_HANDLES);
781                         int brush = foregroundBrush;
782                         if (fullSelection) {
783                             brush = selBrushFg;
784                         } else {
785                             if (run.style != null && run.style.foreground != null) {
786                                 int fg = run.style.foreground.handle;
787                                 int argb = ((alpha & 0xFF) << 24) | ((fg >> 16) & 0xFF) | (fg & 0xFF00) | ((fg & 0xFF) << 16);
788                                 int color = Gdip.Color_new(argb);
789                                 brush = Gdip.SolidBrush_new(color);
790                                 Gdip.Color_delete(color);
791                             }
792                         }
793                         int gstate = 0;
794                         if (partialSelection) {
795                             gdipRect.X = rect.left;
796                             gdipRect.Y = rect.top;
797                             gdipRect.Width = rect.right - rect.left;
798                             gdipRect.Height = rect.bottom - rect.top;
799                             gstate = Gdip.Graphics_Save(gdipGraphics);
800                             Gdip.Graphics_SetClip(gdipGraphics, gdipRect, Gdip.CombineModeExclude);
801                         }
802                         int antialias = Gdip.Graphics_GetSmoothingMode(gdipGraphics), textAntialias = 0;
803                         int mode = Gdip.Graphics_GetTextRenderingHint(data.gdipGraphics);
804                         switch (mode) {
805                             case Gdip.TextRenderingHintSystemDefault: textAntialias = Gdip.SmoothingModeAntiAlias; break;
806                             case Gdip.TextRenderingHintSingleBitPerPixel:
807                             case Gdip.TextRenderingHintSingleBitPerPixelGridFit: textAntialias = Gdip.SmoothingModeNone; break;
808                             case Gdip.TextRenderingHintAntiAlias:
809                             case Gdip.TextRenderingHintAntiAliasGridFit:
810                             case Gdip.TextRenderingHintClearTypeGridFit: textAntialias = Gdip.SmoothingModeAntiAlias; break;
811                         }
812                         Gdip.Graphics_SetSmoothingMode(gdipGraphics, textAntialias);
813                         int gstate2 = 0;
814                         if ((data.style & SWT.MIRRORED) != 0) {
815                             gstate2 = Gdip.Graphics_Save(gdipGraphics);
816                             Gdip.Graphics_ScaleTransform(gdipGraphics, -1, 1, Gdip.MatrixOrderPrepend);
817                             Gdip.Graphics_TranslateTransform(gdipGraphics, -2 * drawX - run.width, 0, Gdip.MatrixOrderPrepend);
818                         }
819                         Gdip.Graphics_FillPath(gdipGraphics, brush, path);
820                         if ((data.style & SWT.MIRRORED) != 0) {
821                             Gdip.Graphics_Restore(gdipGraphics, gstate2);
822                         }
823                         Gdip.Graphics_SetSmoothingMode(gdipGraphics, antialias);
824                         if (run.style != null && (run.style.underline || run.style.strikeout)) {
825                             int newPen = hasSelection ? selPen : Gdip.Pen_new(brush, 1);
826                             Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeNone);
827                             if (run.style.underline) {
828                                 int underlineY = drawY + baseline + 1 - run.style.rise;
829                                 Gdip.Graphics_DrawLine(gdipGraphics, newPen, drawX, underlineY, drawX + run.width, underlineY);
830                             }
831                             if (run.style.strikeout) {
832                                 int strikeoutY = drawRunY + run.leading + (run.ascent - run.style.rise) / 2;
833                                 Gdip.Graphics_DrawLine(gdipGraphics, newPen, drawX, strikeoutY, drawX + run.width, strikeoutY);
834                             }
835                             if (newPen != selPen) Gdip.Pen_delete(newPen);
836                             Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeHalf);
837                         }
838                         if (partialSelection) {
839                             Gdip.Graphics_Restore(gdipGraphics, gstate);
840                             gstate = Gdip.Graphics_Save(gdipGraphics);
841                             Gdip.Graphics_SetClip(gdipGraphics, gdipRect, Gdip.CombineModeIntersect);
842                             Gdip.Graphics_SetSmoothingMode(gdipGraphics, textAntialias);
843                             Gdip.Graphics_FillPath(gdipGraphics, selBrushFg, path);
844                             Gdip.Graphics_SetSmoothingMode(gdipGraphics, antialias);
845                             if (run.style != null && (run.style.underline || run.style.strikeout)) {
846                                 Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeNone);
847                                 if (run.style.underline) {
848                                     int underlineY = drawY + baseline + 1 - run.style.rise;
849                                     Gdip.Graphics_DrawLine(gdipGraphics, selPen, rect.left, underlineY, rect.right, underlineY);
850                                 }
851                                 if (run.style.strikeout) {
852                                     int strikeoutY = drawRunY + run.leading + (run.ascent - run.style.rise) / 2;
853                                     Gdip.Graphics_DrawLine(gdipGraphics, selPen, rect.left, strikeoutY, rect.right, strikeoutY);
854                                 }
855                                 Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeHalf);
856                             }
857                             Gdip.Graphics_Restore(gdipGraphics, gstate);
858                         }
859                         Gdip.GraphicsPath_delete(path);
860                         if (brush != selBrushFg && brush != foregroundBrush) Gdip.SolidBrush_delete(brush);
861                     } else {
862                         int fg = foreground;
863                         if (fullSelection) {
864                             fg = selectionForeground.handle;
865                         } else {
866                             if (run.style != null && run.style.foreground != null) fg = run.style.foreground.handle;
867                         }
868                         OS.SetTextColor(hdc, fg);
869                         OS.ScriptTextOut(hdc, run.psc, drawX + offset, drawRunY, 0, null, run.analysis , 0, 0, run.glyphs, run.glyphCount, run.advances, run.justify, run.goffsets);
870                         if (run.style != null && (run.style.underline || run.style.strikeout)) {
871                             int newPen = hasSelection && fg == selectionForeground.handle ? selPen : OS.CreatePen(OS.PS_SOLID, 1, fg);
872                             int oldPen = OS.SelectObject(hdc, newPen);
873                             if (run.style.underline) {
874                                 int underlineY = drawY + baseline + 1 - run.style.rise;
875                                 OS.MoveToEx(hdc, drawX, underlineY, 0);
876                                 OS.LineTo(hdc, drawX + run.width, underlineY);
877                             }
878                             if (run.style.strikeout) {
879                                 int strikeoutY = drawRunY + run.leading + (run.ascent - run.style.rise) / 2;
880                                 OS.MoveToEx(hdc, drawX, strikeoutY, 0);
881                                 OS.LineTo(hdc, drawX + run.width, strikeoutY);
882                             }
883                             OS.SelectObject(hdc, oldPen);
884                             if (!hasSelection || fg != selectionForeground.handle) OS.DeleteObject(newPen);
885                         }
886                         if (partialSelection && fg != selectionForeground.handle) {
887                             OS.SetTextColor(hdc, selectionForeground.handle);
888                             OS.ScriptTextOut(hdc, run.psc, drawX + offset, drawRunY, OS.ETO_CLIPPED, rect, run.analysis , 0, 0, run.glyphs, run.glyphCount, run.advances, run.justify, run.goffsets);
889                             if (run.style != null && (run.style.underline || run.style.strikeout)) {
890                                 int oldPen = OS.SelectObject(hdc, selPen);
891                                 if (run.style.underline) {
892                                     int underlineY = drawY + baseline + 1 - run.style.rise;
893                                     OS.MoveToEx(hdc, rect.left, underlineY, 0);
894                                     OS.LineTo(hdc, rect.right, underlineY);
895                                 }
896                                 if (run.style.strikeout) {
897                                     int strikeoutY = drawRunY + run.leading + (run.ascent - run.style.rise) / 2;
898                                     OS.MoveToEx(hdc, rect.left, strikeoutY, 0);
899                                     OS.LineTo(hdc, rect.right, strikeoutY);
900                                 }
901                                 OS.SelectObject(hdc, oldPen);
902                             }
903                         }
904                     }
905                 }
906             }
907             drawX += run.width;
908         }
909     }
910     if (gdip) {
911         if (selBrush != 0) Gdip.SolidBrush_delete(selBrush);
912         if (selBrushFg != 0) Gdip.SolidBrush_delete(selBrushFg);
913         if (selPen != 0) Gdip.Pen_delete(selPen);
914     } else {
915         OS.RestoreDC(hdc, state);
916         if (gdipGraphics != 0) Gdip.Graphics_ReleaseHDC(gdipGraphics, hdc);
917         if (selBrush != 0) OS.DeleteObject (selBrush);
918         if (selPen != 0) OS.DeleteObject (selPen);
919     }
920 }
921
922 void freeRuns () {
923     if (allRuns == null) return;
924     for (int i=0; i<allRuns.length; i++) {
925         StyleItem run = allRuns[i];
926         run.free();
927     }
928     allRuns = null;
929     runs = null;
930     segmentsText = null;
931 }
932
933 /**
934  * Returns the receiver's horizontal text alignment, which will be one
935  * of <code>SWT.LEFT</code>, <code>SWT.CENTER</code> or
936  * <code>SWT.RIGHT</code>.
937  *
938  * @return the alignment used to positioned text horizontally
939  *
940  * @exception SWTException <ul>
941  * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
942  * </ul>
943  */

944 public int getAlignment () {
945     checkLayout();
946     return alignment;
947 }
948
949 /**
950  * Returns the ascent of the receiver.
951  *
952  * @return the ascent
953  *
954  * @exception SWTException <ul>
955  * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
956  * </ul>
957  *
958  * @see #getDescent()
959  * @see #setDescent(int)
960  * @see #setAscent(int)
961  * @see #getLineMetrics(int)
962  */

963 public int getAscent () {
964     checkLayout();
965     return ascent;
966 }
967
968 /**
969  * Returns the bounds of the receiver.
970  *
971  * @return the bounds of the receiver
972  *
973  * @exception SWTException <ul>
974  * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
975  * </ul>
976  */

977 public Rectangle getBounds () {
978     checkLayout();
979     computeRuns(null);
980     int width = 0;
981     if (wrapWidth != -1) {
982         width = wrapWidth;
983     } else {
984         for (int line=0; line<runs.length; line++) {
985             width = Math.max(width, lineWidth[line] + getLineIndent(line));
986         }
987     }
988     return new Rectangle (0, 0, width, lineY[lineY.length - 1]);
989 }
990
991 /**
992  * Returns the bounds for the specified range of characters. The
993  * bounds is the smallest rectangle that encompasses all characters
994  * in the range. The start and end offsets are inclusive and will be
995  * clamped if out of range.
996  *
997  * @param start the start offset
998  * @param end the end offset
999  * @return the bounds of the character range
1000 *
1001 * @exception SWTException <ul>
1002 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1003 * </ul>
1004 */

1005public Rectangle getBounds (int start, int end) {
1006    checkLayout();
1007    computeRuns(null);
1008    int length = text.length();
1009    if (length == 0) return new Rectangle(0, 0, 0, 0);
1010    if (start > end) return new Rectangle(0, 0, 0, 0);
1011    start = Math.min(Math.max(0, start), length - 1);
1012    end = Math.min(Math.max(0, end), length - 1);
1013    start = translateOffset(start);
1014    end = translateOffset(end);
1015    int left = 0x7fffffff, right = 0;
1016    int top = 0x7fffffff, bottom = 0;
1017    boolean isRTL = (orientation & SWT.RIGHT_TO_LEFT) != 0;
1018    for (int i = 0; i < allRuns.length - 1; i++) {
1019        StyleItem run = allRuns[i];
1020        int runEnd = run.start + run.length;
1021        if (runEnd <= start) continue;
1022        if (run.start > end) break;
1023        int runLead = run.x;
1024        int runTrail = run.x + run.width;
1025        if (run.start <= start && start < runEnd) {
1026            int cx = 0;
1027            if (run.style != null && run.style.metrics != null) {
1028                GlyphMetrics metrics = run.style.metrics;
1029                cx = metrics.width * (start - run.start);
1030            } else if (!run.tab) {
1031                int[] piX = new int[1];
1032                int advances = run.justify != 0 ? run.justify : run.advances;
1033                OS.ScriptCPtoX(start - run.start, false, run.length, run.glyphCount, run.clusters, run.visAttrs, advances, run.analysis, piX);
1034                cx = isRTL ? run.width - piX[0] : piX[0];
1035            }
1036            if (run.analysis.fRTL ^ isRTL) {
1037                runTrail = run.x + cx;
1038            } else {
1039                runLead = run.x + cx;
1040            }
1041        }
1042        if (run.start <= end && end < runEnd) {
1043            int cx = run.width;
1044            if (run.style != null && run.style.metrics != null) {
1045                GlyphMetrics metrics = run.style.metrics;
1046                cx = metrics.width * (end - run.start + 1);
1047            } else if (!run.tab) {
1048                int[] piX = new int[1];
1049                int advances = run.justify != 0 ? run.justify : run.advances;
1050                OS.ScriptCPtoX(end - run.start, true, run.length, run.glyphCount, run.clusters, run.visAttrs, advances, run.analysis, piX);
1051                cx = isRTL ? run.width - piX[0] : piX[0];
1052            }
1053            if (run.analysis.fRTL ^ isRTL) {
1054                runLead = run.x + cx;
1055            } else {
1056                runTrail = run.x + cx;
1057            }
1058        }
1059        int lineIndex = 0;
1060        while (lineIndex < runs.length && lineOffset[lineIndex + 1] <= run.start) {
1061            lineIndex++;
1062        }
1063        left = Math.min(left, runLead);
1064        right = Math.max(right, runTrail);
1065        top = Math.min(top, lineY[lineIndex]);
1066        bottom = Math.max(bottom, lineY[lineIndex + 1] - lineSpacing);
1067    }
1068    return new Rectangle(left, top, right - left, bottom - top);
1069}
1070
1071/**
1072 * Returns the descent of the receiver.
1073 *
1074 * @return the descent
1075 *
1076 * @exception SWTException <ul>
1077 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1078 * </ul>
1079 *
1080 * @see #getAscent()
1081 * @see #setAscent(int)
1082 * @see #setDescent(int)
1083 * @see #getLineMetrics(int)
1084 */

1085public int getDescent () {
1086    checkLayout();
1087    return descent;
1088}
1089
1090/**
1091 * Returns the default font currently being used by the receiver
1092 * to draw and measure text.
1093 *
1094 * @return the receiver's font
1095 *
1096 * @exception SWTException <ul>
1097 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1098 * </ul>
1099 */

1100public Font getFont () {
1101    checkLayout();
1102    return font;
1103}
1104
1105/**
1106* Returns the receiver's indent.
1107*
1108* @return the receiver's indent
1109*
1110* @exception SWTException <ul>
1111* <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1112* </ul>
1113*
1114* @since 3.2
1115*/

1116public int getIndent () {
1117    checkLayout();
1118    return indent;
1119}
1120
1121/**
1122* Returns the receiver's justification.
1123*
1124* @return the receiver's justification
1125*
1126* @exception SWTException <ul>
1127* <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1128* </ul>
1129*
1130* @since 3.2
1131*/

1132public boolean getJustify () {
1133    checkLayout();
1134    return justify;
1135}
1136
1137int getItemFont (StyleItem item) {
1138    if (item.fallbackFont != 0) return item.fallbackFont;
1139    if (item.style != null && item.style.font != null) {
1140        return item.style.font.handle;
1141    }
1142    if (this.font != null) {
1143        return this.font.handle;
1144    }
1145    return device.systemFont;
1146}
1147
1148/**
1149 * Returns the embedding level for the specified character offset. The
1150 * embedding level is usually used to determine the directionality of a
1151 * character in bidirectional text.
1152 *
1153 * @param offset the character offset
1154 * @return the embedding level
1155 *
1156 * @exception IllegalArgumentException <ul>
1157 * <li>ERROR_INVALID_ARGUMENT - if the character offset is out of range</li>
1158 * </ul>
1159 * @exception SWTException <ul>
1160 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1161 */

1162public int getLevel (int offset) {
1163    checkLayout();
1164    computeRuns(null);
1165    int length = text.length();
1166    if (!(0 <= offset && offset <= length)) SWT.error(SWT.ERROR_INVALID_RANGE);
1167    offset = translateOffset(offset);
1168    for (int i=1; i<allRuns.length; i++) {
1169        if (allRuns[i].start > offset) {
1170            return allRuns[i - 1].analysis.s.uBidiLevel;
1171        }
1172    }
1173    return (orientation & SWT.RIGHT_TO_LEFT) != 0 ? 1 : 0;
1174}
1175
1176/**
1177 * Returns the bounds of the line for the specified line index.
1178 *
1179 * @param lineIndex the line index
1180 * @return the line bounds
1181 *
1182 * @exception IllegalArgumentException <ul>
1183 * <li>ERROR_INVALID_ARGUMENT - if the line index is out of range</li>
1184 * </ul>
1185 * @exception SWTException <ul>
1186 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1187 * </ul>
1188 */

1189public Rectangle getLineBounds(int lineIndex) {
1190    checkLayout();
1191    computeRuns(null);
1192    if (!(0 <= lineIndex && lineIndex < runs.length)) SWT.error(SWT.ERROR_INVALID_RANGE);
1193    int x = getLineIndent(lineIndex);
1194    int y = lineY[lineIndex];
1195    int width = lineWidth[lineIndex];
1196    int height = lineY[lineIndex + 1] - y - lineSpacing;
1197    return new Rectangle (x, y, width, height);
1198}
1199
1200/**
1201 * Returns the receiver's line count. This includes lines caused
1202 * by wrapping.
1203 *
1204 * @return the line count
1205 *
1206 * @exception SWTException <ul>
1207 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1208 * </ul>
1209 */

1210public int getLineCount () {
1211    checkLayout();
1212    computeRuns(null);
1213    return runs.length;
1214}
1215
1216int getLineIndent (int lineIndex) {
1217    int lineIndent = 0;
1218    if (lineIndex == 0) {
1219        lineIndent = indent;
1220    } else {
1221        StyleItem[] previousLine = runs[lineIndex - 1];
1222        StyleItem previousRun = previousLine[previousLine.length - 1];
1223        if (previousRun.lineBreak && !previousRun.softBreak) {
1224            lineIndent = indent;
1225        }
1226    }
1227    if (wrapWidth != -1) {
1228        boolean partialLine = true;
1229        if (justify) {
1230            StyleItem[] lineRun = runs[lineIndex];
1231            if (lineRun[lineRun.length - 1].softBreak) {
1232                partialLine = false;
1233            }
1234        }
1235        if (partialLine) {
1236            int lineWidth = this.lineWidth[lineIndex] + lineIndent;
1237            switch (alignment) {
1238                case SWT.CENTER: lineIndent += (wrapWidth - lineWidth) / 2; break;
1239                case SWT.RIGHT: lineIndent += wrapWidth - lineWidth; break;
1240            }
1241        }
1242    }
1243    return lineIndent;
1244}
1245
1246/**
1247 * Returns the index of the line that contains the specified
1248 * character offset.
1249 *
1250 * @param offset the character offset
1251 * @return the line index
1252 *
1253 * @exception IllegalArgumentException <ul>
1254 * <li>ERROR_INVALID_ARGUMENT - if the character offset is out of range</li>
1255 * </ul>
1256 * @exception SWTException <ul>
1257 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1258 * </ul>
1259 */

1260public int getLineIndex (int offset) {
1261    checkLayout();
1262    computeRuns(null);
1263    int length = text.length();
1264    if (!(0 <= offset && offset <= length)) SWT.error(SWT.ERROR_INVALID_RANGE);
1265    offset = translateOffset(offset);
1266    for (int line=0; line<runs.length; line++) {
1267        if (lineOffset[line + 1] > offset) {
1268            return line;
1269        }
1270    }
1271    return runs.length - 1;
1272}
1273
1274/**
1275 * Returns the font metrics for the specified line index.
1276 *
1277 * @param lineIndex the line index
1278 * @return the font metrics
1279 *
1280 * @exception IllegalArgumentException <ul>
1281 * <li>ERROR_INVALID_ARGUMENT - if the line index is out of range</li>
1282 * </ul>
1283 * @exception SWTException <ul>
1284 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1285 * </ul>
1286 */

1287public FontMetrics getLineMetrics (int lineIndex) {
1288    checkLayout();
1289    computeRuns(null);
1290    if (!(0 <= lineIndex && lineIndex < runs.length)) SWT.error(SWT.ERROR_INVALID_RANGE);
1291    int hDC = device.internal_new_GC(null);
1292    int srcHdc = OS.CreateCompatibleDC(hDC);
1293    TEXTMETRIC lptm = OS.IsUnicode ? (TEXTMETRIC)new TEXTMETRICW() : new TEXTMETRICA();
1294    OS.SelectObject(srcHdc, font != null ? font.handle : device.systemFont);
1295    OS.GetTextMetrics(srcHdc, lptm);
1296    OS.DeleteDC(srcHdc);
1297    device.internal_dispose_GC(hDC, null);
1298    
1299    int ascent = Math.max(lptm.tmAscent, this.ascent);
1300    int descent = Math.max(lptm.tmDescent, this.descent);
1301    int leading = lptm.tmInternalLeading;
1302    if (text.length() != 0) {
1303        StyleItem[] lineRuns = runs[lineIndex];
1304        for (int i = 0; i<lineRuns.length; i++) {
1305            StyleItem run = lineRuns[i];
1306            if (run.ascent > ascent) {
1307                ascent = run.ascent;
1308                leading = run.leading;
1309            }
1310            descent = Math.max(descent, run.descent);
1311        }
1312    }
1313    lptm.tmAscent = ascent;
1314    lptm.tmDescent = descent;
1315    lptm.tmHeight = ascent + descent;
1316    lptm.tmInternalLeading = leading;
1317    lptm.tmAveCharWidth = 0;
1318    return FontMetrics.win32_new(lptm);
1319}
1320
1321/**
1322 * Returns the line offsets. Each value in the array is the
1323 * offset for the first character in a line except for the last
1324 * value, which contains the length of the text.
1325 *
1326 * @return the line offsets
1327 *
1328 * @exception SWTException <ul>
1329 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1330 * </ul>
1331 */

1332public int[] getLineOffsets () {
1333    checkLayout();
1334    computeRuns(null);
1335    int[] offsets = new int[lineOffset.length];
1336    for (int i = 0; i < offsets.length; i++) {
1337        offsets[i] = untranslateOffset(lineOffset[i]);
1338    }
1339    return offsets;
1340}
1341
1342/**
1343 * Returns the location for the specified character offset. The
1344 * <code>trailing</code> argument indicates whether the offset
1345 * corresponds to the leading or trailing edge of the cluster.
1346 *
1347 * @param offset the character offset
1348 * @param trailing the trailing flag
1349 * @return the location of the character offset
1350 *
1351 * @exception SWTException <ul>
1352 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1353 * </ul>
1354 *
1355 * @see #getOffset(Point, int[])
1356 * @see #getOffset(int, int, int[])
1357 */

1358public Point getLocation (int offset, boolean trailing) {
1359    checkLayout();
1360    computeRuns(null);
1361    int length = text.length();
1362    if (!(0 <= offset && offset <= length)) SWT.error(SWT.ERROR_INVALID_RANGE);
1363    length = segmentsText.length();
1364    offset = translateOffset(offset);
1365    int line;
1366    for (line=0; line<runs.length; line++) {
1367        if (lineOffset[line + 1] > offset) break;
1368    }
1369    line = Math.min(line, runs.length - 1);
1370    StyleItem[] lineRuns = runs[line];
1371    Point result = null;
1372    if (offset == length) {
1373        result = new Point(lineWidth[line], lineY[line]);
1374    } else {
1375        int width = 0;
1376        for (int i=0; i<lineRuns.length; i++) {
1377            StyleItem run = lineRuns[i];
1378            int end = run.start + run.length;
1379            if (run.start <= offset && offset < end) {
1380                if (run.style != null && run.style.metrics != null) {
1381                    GlyphMetrics metrics = run.style.metrics;
1382                    width += metrics.width * (offset - run.start + (trailing ? 1 : 0));
1383                    result = new Point(width, lineY[line]);
1384                } else if (run.tab) {
1385                    if (trailing || (offset == length)) width += run.width;
1386                    result = new Point(width, lineY[line]);
1387                } else {
1388                    int runOffset = offset - run.start;
1389                    int cChars = run.length;
1390                    int gGlyphs = run.glyphCount;
1391                    int[] piX = new int[1];
1392                    int advances = run.justify != 0 ? run.justify : run.advances;
1393                    OS.ScriptCPtoX(runOffset, trailing, cChars, gGlyphs, run.clusters, run.visAttrs, advances, run.analysis, piX);
1394                    if ((orientation & SWT.RIGHT_TO_LEFT) != 0) {
1395                        result = new Point(width + (run.width - piX[0]), lineY[line]);
1396                    } else {
1397                        result = new Point(width + piX[0], lineY[line]);
1398                    }
1399                }
1400                break;
1401            }
1402            width += run.width;
1403        }
1404    }
1405    if (result == null) result = new Point(0, 0);
1406    result.x += getLineIndent(line);
1407    return result;
1408}
1409
1410/**
1411 * Returns the next offset for the specified offset and movement
1412 * type. The movement is one of <code>SWT.MOVEMENT_CHAR</code>,
1413 * <code>SWT.MOVEMENT_CLUSTER</code>, <code>SWT.MOVEMENT_WORD</code>,
1414 * <code>SWT.MOVEMENT_WORD_END</code> or <code>SWT.MOVEMENT_WORD_START</code>.
1415 *
1416 * @param offset the start offset
1417 * @param movement the movement type
1418 * @return the next offset
1419 *
1420 * @exception IllegalArgumentException <ul>
1421 * <li>ERROR_INVALID_ARGUMENT - if the offset is out of range</li>
1422 * </ul>
1423 * @exception SWTException <ul>
1424 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1425 * </ul>
1426 *
1427 * @see #getPreviousOffset(int, int)
1428 */

1429public int getNextOffset (int offset, int movement) {
1430    checkLayout();
1431    return _getOffset (offset, movement, true);
1432}
1433
1434int _getOffset(int offset, int movement, boolean forward) {
1435    computeRuns(null);
1436    int length = text.length();
1437    if (!(0 <= offset && offset <= length)) SWT.error(SWT.ERROR_INVALID_RANGE);
1438    if (forward && offset == length) return length;
1439    if (!forward && offset == 0) return 0;
1440    int step = forward ? 1 : -1;
1441    if ((movement & SWT.MOVEMENT_CHAR) != 0) return offset + step;
1442    length = segmentsText.length();
1443    offset = translateOffset(offset);
1444    SCRIPT_LOGATTR logAttr = new SCRIPT_LOGATTR();
1445    SCRIPT_PROPERTIES properties = new SCRIPT_PROPERTIES();
1446    int i = forward ? 0 : allRuns.length - 1;
1447    offset = validadeOffset(offset, step);
1448    do {
1449        StyleItem run = allRuns[i];
1450        if (run.start <= offset && offset < run.start + run.length) {
1451            if (run.lineBreak && !run.softBreak) return untranslateOffset(run.start);
1452            if (run.tab) return untranslateOffset(run.start);
1453            OS.MoveMemory(properties, device.scripts[run.analysis.eScript], SCRIPT_PROPERTIES.sizeof);
1454            boolean isComplex = properties.fNeedsCaretInfo || properties.fNeedsWordBreaking;
1455            if (isComplex) breakRun(run);
1456            while (run.start <= offset && offset < run.start + run.length) {
1457                if (isComplex) {
1458                    OS.MoveMemory(logAttr, run.psla + ((offset - run.start) * SCRIPT_LOGATTR.sizeof), SCRIPT_LOGATTR.sizeof);
1459                }
1460                switch (movement) {
1461                    case SWT.MOVEMENT_CLUSTER: {
1462                        if (properties.fNeedsCaretInfo) {
1463                            if (!logAttr.fInvalid && logAttr.fCharStop) return untranslateOffset(offset);
1464                        } else {
1465                            return untranslateOffset(offset);
1466                        }
1467                        break;
1468                    }
1469                    case SWT.MOVEMENT_WORD_START:
1470                    case SWT.MOVEMENT_WORD: {
1471                        if (properties.fNeedsWordBreaking) {
1472                            if (!logAttr.fInvalid && logAttr.fWordStop) return untranslateOffset(offset);
1473                        } else {
1474                            if (offset > 0) {
1475                                boolean letterOrDigit = Compatibility.isLetterOrDigit(segmentsText.charAt(offset));
1476                                boolean previousLetterOrDigit = Compatibility.isLetterOrDigit(segmentsText.charAt(offset - 1));
1477                                if (letterOrDigit != previousLetterOrDigit || !letterOrDigit) {
1478                                    if (!Compatibility.isWhitespace(segmentsText.charAt(offset))) {
1479                                        return untranslateOffset(offset);
1480                                    }
1481                                }
1482                            }
1483                        }
1484                        break;
1485                    }
1486                    case SWT.MOVEMENT_WORD_END: {
1487                        if (offset > 0) {
1488                            boolean isLetterOrDigit = Compatibility.isLetterOrDigit(segmentsText.charAt(offset));
1489                            boolean previousLetterOrDigit = Compatibility.isLetterOrDigit(segmentsText.charAt(offset - 1));
1490                            if (!isLetterOrDigit && previousLetterOrDigit) {
1491                                return untranslateOffset(offset);
1492                            }
1493                        }
1494                        break;
1495                    }
1496                }
1497                offset = validadeOffset(offset, step);
1498            }
1499        }
1500        i += step;
1501    } while (0 <= i && i < allRuns.length - 1 && 0 <= offset && offset < length);
1502    return forward ? text.length() : 0;
1503}
1504
1505/**
1506 * Returns the character offset for the specified point.
1507 * For a typical character, the trailing argument will be filled in to
1508 * indicate whether the point is closer to the leading edge (0) or
1509 * the trailing edge (1). When the point is over a cluster composed
1510 * of multiple characters, the trailing argument will be filled with the
1511 * position of the character in the cluster that is closest to
1512 * the point.
1513 *
1514 * @param point the point
1515 * @param trailing the trailing buffer
1516 * @return the character offset
1517 *
1518 * @exception IllegalArgumentException <ul>
1519 * <li>ERROR_INVALID_ARGUMENT - if the trailing length is less than <code>1</code></li>
1520 * <li>ERROR_NULL_ARGUMENT - if the point is null</li>
1521 * </ul>
1522 * @exception SWTException <ul>
1523 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1524 * </ul>
1525 *
1526 * @see #getLocation(int, boolean)
1527 */

1528public int getOffset (Point point, int[] trailing) {
1529    checkLayout();
1530    if (point == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
1531    return getOffset (point.x, point.y, trailing) ;
1532}
1533
1534/**
1535 * Returns the character offset for the specified point.
1536 * For a typical character, the trailing argument will be filled in to
1537 * indicate whether the point is closer to the leading edge (0) or
1538 * the trailing edge (1). When the point is over a cluster composed
1539 * of multiple characters, the trailing argument will be filled with the
1540 * position of the character in the cluster that is closest to
1541 * the point.
1542 *
1543 * @param x the x coordinate of the point
1544 * @param y the y coordinate of the point
1545 * @param trailing the trailing buffer
1546 * @return the character offset
1547 *
1548 * @exception IllegalArgumentException <ul>
1549 * <li>ERROR_INVALID_ARGUMENT - if the trailing length is less than <code>1</code></li>
1550 * </ul>
1551 * @exception SWTException <ul>
1552 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1553 * </ul>
1554 *
1555 * @see #getLocation(int, boolean)
1556 */

1557public int getOffset (int x, int y, int[] trailing) {
1558    checkLayout();
1559    computeRuns(null);
1560    if (trailing != null && trailing.length < 1) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1561    int line;
1562    int lineCount = runs.length;
1563    for (line=0; line<lineCount; line++) {
1564        if (lineY[line + 1] > y) break;
1565    }
1566    line = Math.min(line, runs.length - 1);
1567    x -= getLineIndent(line);
1568    StyleItem[] lineRuns = runs[line];
1569    if (x >= lineWidth[line]) x = lineWidth[line] - 1;
1570    if (x < 0) x = 0;
1571    int width = 0;
1572    for (int i = 0; i < lineRuns.length; i++) {
1573        StyleItem run = lineRuns[i];
1574        if (run.lineBreak && !run.softBreak) return untranslateOffset(run.start);
1575        if (width + run.width > x) {
1576            int xRun = x - width;
1577            if (run.style != null && run.style.metrics != null) {
1578                GlyphMetrics metrics = run.style.metrics;
1579                if (metrics.width > 0) {
1580                    if (trailing != null) {
1581                        trailing[0] = (xRun % metrics.width < metrics.width / 2) ? 0 : 1;
1582                    }
1583                    return untranslateOffset(run.start + xRun / metrics.width);
1584                }
1585            }
1586            if (run.tab) {
1587                if (trailing != null) trailing[0] = x < (width + run.width / 2) ? 0 : 1;
1588                return untranslateOffset(run.start);
1589            }
1590            int cChars = run.length;
1591            int cGlyphs = run.glyphCount;
1592            int[] piCP = new int[1];
1593            int[] piTrailing = new int[1];
1594            if ((orientation & SWT.RIGHT_TO_LEFT) != 0) {
1595                xRun = run.width - xRun;
1596            }
1597            int advances = run.justify != 0 ? run.justify : run.advances;
1598            OS.ScriptXtoCP(xRun, cChars, cGlyphs, run.clusters, run.visAttrs, advances, run.analysis, piCP, piTrailing);
1599            if (trailing != null) trailing[0] = piTrailing[0];
1600            return untranslateOffset(run.start + piCP[0]);
1601        }
1602        width += run.width;
1603    }
1604    if (trailing != null) trailing[0] = 0;
1605    return untranslateOffset(lineOffset[line + 1]);
1606}
1607
1608/**
1609 * Returns the orientation of the receiver.
1610 *
1611 * @return the orientation style
1612 *
1613 * @exception SWTException <ul>
1614 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1615 * </ul>
1616 */

1617public int getOrientation () {
1618    checkLayout();
1619    return orientation;
1620}
1621
1622/**
1623 * Returns the previous offset for the specified offset and movement
1624 * type. The movement is one of <code>SWT.MOVEMENT_CHAR</code>,
1625 * <code>SWT.MOVEMENT_CLUSTER</code> or <code>SWT.MOVEMENT_WORD</code>,
1626 * <code>SWT.MOVEMENT_WORD_END</code> or <code>SWT.MOVEMENT_WORD_START</code>.
1627 *
1628 * @param offset the start offset
1629 * @param movement the movement type
1630 * @return the previous offset
1631 *
1632 * @exception IllegalArgumentException <ul>
1633 * <li>ERROR_INVALID_ARGUMENT - if the offset is out of range</li>
1634 * </ul>
1635 * @exception SWTException <ul>
1636 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1637 * </ul>
1638 *
1639 * @see #getNextOffset(int, int)
1640 */

1641public int getPreviousOffset (int offset, int movement) {
1642    checkLayout();
1643    return _getOffset (offset, movement, false);
1644}
1645
1646/**
1647 * Gets the ranges of text that are associated with a <code>TextStyle</code>.
1648 *
1649 * @return the ranges, an array of offsets representing the start and end of each
1650 * text style.
1651 *
1652 * @exception SWTException <ul>
1653 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1654 * </ul>
1655 *
1656 * @see #getStyles()
1657 *
1658 * @since 3.2
1659 */

1660public int[] getRanges () {
1661    checkLayout();
1662    int[] result = new int[styles.length * 2];
1663    int count = 0;
1664    for (int i=0; i<styles.length - 1; i++) {
1665        if (styles[i].style != null) {
1666            result[count++] = styles[i].start;
1667            result[count++] = styles[i + 1].start - 1;
1668        }
1669    }
1670    if (count != result.length) {
1671        int[] newResult = new int[count];
1672        System.arraycopy(result, 0, newResult, 0, count);
1673        result = newResult;
1674    }
1675    return result;
1676}
1677
1678/**
1679 * Returns the text segments offsets of the receiver.
1680 *
1681 * @return the text segments offsets
1682 *
1683 * @exception SWTException <ul>
1684 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1685 * </ul>
1686 */

1687public int[] getSegments () {
1688    checkLayout();
1689    return segments;
1690}
1691
1692String JavaDoc getSegmentsText() {
1693    if (segments == null) return text;
1694    int nSegments = segments.length;
1695    if (nSegments <= 1) return text;
1696    int length = text.length();
1697    if (length == 0) return text;
1698    if (nSegments == 2) {
1699        if (segments[0] == 0 && segments[1] == length) return text;
1700    }
1701    char[] oldChars = new char[length];
1702    text.getChars(0, length, oldChars, 0);
1703    char[] newChars = new char[length + nSegments];
1704    int charCount = 0, segmentCount = 0;
1705    char separator = orientation == SWT.RIGHT_TO_LEFT ? RTL_MARK : LTR_MARK;
1706    while (charCount < length) {
1707        if (segmentCount < nSegments && charCount == segments[segmentCount]) {
1708            newChars[charCount + segmentCount++] = separator;
1709        } else {
1710            newChars[charCount + segmentCount] = oldChars[charCount++];
1711        }
1712    }
1713    if (segmentCount < nSegments) {
1714        segments[segmentCount] = charCount;
1715        newChars[charCount + segmentCount++] = separator;
1716    }
1717    return new String JavaDoc(newChars, 0, Math.min(charCount + segmentCount, newChars.length));
1718}
1719
1720/**
1721 * Returns the line spacing of the receiver.
1722 *
1723 * @return the line spacing
1724 *
1725 * @exception SWTException <ul>
1726 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1727 * </ul>
1728 */

1729public int getSpacing () {
1730    checkLayout();
1731    return lineSpacing;
1732}
1733
1734/**
1735 * Gets the style of the receiver at the specified character offset.
1736 *
1737 * @param offset the text offset
1738 * @return the style or <code>null</code> if not set
1739 *
1740 * @exception IllegalArgumentException <ul>
1741 * <li>ERROR_INVALID_ARGUMENT - if the character offset is out of range</li>
1742 * </ul>
1743 * @exception SWTException <ul>
1744 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1745 * </ul>
1746 */

1747public TextStyle getStyle (int offset) {
1748    checkLayout();
1749    int length = text.length();
1750    if (!(0 <= offset && offset < length)) SWT.error(SWT.ERROR_INVALID_RANGE);
1751    for (int i=1; i<styles.length; i++) {
1752        if (styles[i].start > offset) {
1753            return styles[i - 1].style;
1754        }
1755    }
1756    return null;
1757}
1758
1759/**
1760 * Gets all styles of the receiver.
1761 *
1762 * @return the styles
1763 *
1764 * @exception SWTException <ul>
1765 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1766 * </ul>
1767 *
1768 * @see #getRanges()
1769 *
1770 * @since 3.2
1771 */

1772public TextStyle[] getStyles () {
1773    checkLayout();
1774    TextStyle[] result = new TextStyle[styles.length];
1775    int count = 0;
1776    for (int i=0; i<styles.length; i++) {
1777        if (styles[i].style != null) {
1778            result[count++] = styles[i].style;
1779        }
1780    }
1781    if (count != result.length) {
1782        TextStyle[] newResult = new TextStyle[count];
1783        System.arraycopy(result, 0, newResult, 0, count);
1784        result = newResult;
1785    }
1786    return result;
1787}
1788
1789/**
1790 * Returns the tab list of the receiver.
1791 *
1792 * @return the tab list
1793 *
1794 * @exception SWTException <ul>
1795 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1796 * </ul>
1797 */

1798public int[] getTabs () {
1799    checkLayout();
1800    return tabs;
1801}
1802
1803/**
1804 * Gets the receiver's text, which will be an empty
1805 * string if it has never been set.
1806 *
1807 * @return the receiver's text
1808 *
1809 * @exception SWTException <ul>
1810 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1811 * </ul>
1812 */

1813public String JavaDoc getText () {
1814    checkLayout();
1815    return text;
1816}
1817
1818/**
1819 * Returns the width of the receiver.
1820 *
1821 * @return the width
1822 *
1823 * @exception SWTException <ul>
1824 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1825 * </ul>
1826 */

1827public int getWidth () {
1828    checkLayout();
1829    return wrapWidth;
1830}
1831
1832/**
1833 * Returns <code>true</code> if the text layout has been disposed,
1834 * and <code>false</code> otherwise.
1835 * <p>
1836 * This method gets the dispose state for the text layout.
1837 * When a text layout has been disposed, it is an error to
1838 * invoke any other method using the text layout.
1839 * </p>
1840 *
1841 * @return <code>true</code> when the text layout is disposed and <code>false</code> otherwise
1842 */

1843public boolean isDisposed () {
1844    return device == null;
1845}
1846
1847/*
1848 * Itemize the receiver text
1849 */

1850StyleItem[] itemize () {
1851    segmentsText = getSegmentsText();
1852    int length = segmentsText.length();
1853    SCRIPT_CONTROL scriptControl = new SCRIPT_CONTROL();
1854    SCRIPT_STATE scriptState = new SCRIPT_STATE();
1855    final int MAX_ITEM = length + 1;
1856    
1857    if ((orientation & SWT.RIGHT_TO_LEFT) != 0) {
1858        scriptState.uBidiLevel = 1;
1859        scriptState.fArabicNumContext = true;
1860        SCRIPT_DIGITSUBSTITUTE psds = new SCRIPT_DIGITSUBSTITUTE();
1861        OS.ScriptRecordDigitSubstitution(OS.LOCALE_USER_DEFAULT, psds);
1862        OS.ScriptApplyDigitSubstitution(psds, scriptControl, scriptState);
1863    }
1864    
1865    int hHeap = OS.GetProcessHeap();
1866    int pItems = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, MAX_ITEM * SCRIPT_ITEM.sizeof);
1867    if (pItems == 0) SWT.error(SWT.ERROR_NO_HANDLES);
1868    int[] pcItems = new int[1];
1869    char[] chars = new char[length];
1870    segmentsText.getChars(0, length, chars, 0);
1871    OS.ScriptItemize(chars, length, MAX_ITEM, scriptControl, scriptState, pItems, pcItems);
1872// if (hr == E_OUTOFMEMORY) //TODO handle it
1873

1874    StyleItem[] runs = merge(pItems, pcItems[0]);
1875    OS.HeapFree(hHeap, 0, pItems);
1876    return runs;
1877}
1878
1879/*
1880 * Merge styles ranges and script items
1881 */

1882StyleItem[] merge (int items, int itemCount) {
1883    int count = 0, start = 0, end = segmentsText.length(), itemIndex = 0, styleIndex = 0;
1884    StyleItem[] runs = new StyleItem[itemCount + styles.length];
1885    SCRIPT_ITEM scriptItem = new SCRIPT_ITEM();
1886    boolean linkBefore = false;
1887    while (start < end) {
1888        StyleItem item = new StyleItem();
1889        item.start = start;
1890        item.style = styles[styleIndex].style;
1891        runs[count++] = item;
1892        OS.MoveMemory(scriptItem, items + itemIndex * SCRIPT_ITEM.sizeof, SCRIPT_ITEM.sizeof);
1893        item.analysis = scriptItem.a;
1894        if (linkBefore) {
1895            item.analysis.fLinkBefore = true;
1896            linkBefore = false;
1897        }
1898        scriptItem.a = new SCRIPT_ANALYSIS();
1899        OS.MoveMemory(scriptItem, items + (itemIndex + 1) * SCRIPT_ITEM.sizeof, SCRIPT_ITEM.sizeof);
1900        int itemLimit = scriptItem.iCharPos;
1901        int styleLimit = translateOffset(styles[styleIndex + 1].start);
1902        if (styleLimit <= itemLimit) {
1903            styleIndex++;
1904            start = styleLimit;
1905            if (start < itemLimit && 0 < start && start < end) {
1906                char pChar = segmentsText.charAt(start - 1);
1907                char tChar = segmentsText.charAt(start);
1908                if (!Compatibility.isWhitespace(pChar) && !Compatibility.isWhitespace(tChar)) {
1909                    item.analysis.fLinkAfter = true;
1910                    linkBefore = true;
1911                }
1912            }
1913        }
1914        if (itemLimit <= styleLimit) {
1915            itemIndex++;
1916            start = itemLimit;
1917        }
1918        item.length = start - item.start;
1919    }
1920    StyleItem item = new StyleItem();
1921    item.start = end;
1922    OS.MoveMemory(scriptItem, items + itemCount * SCRIPT_ITEM.sizeof, SCRIPT_ITEM.sizeof);
1923    item.analysis = scriptItem.a;
1924    runs[count++] = item;
1925    if (runs.length != count) {
1926        StyleItem[] result = new StyleItem[count];
1927        System.arraycopy(runs, 0, result, 0, count);
1928        return result;
1929    }
1930    return runs;
1931}
1932
1933/*
1934 * Reorder the run
1935 */

1936StyleItem[] reorder (StyleItem[] runs, boolean terminate) {
1937    int length = runs.length;
1938    if (length <= 1) return runs;
1939    byte[] bidiLevels = new byte[length];
1940    for (int i=0; i<length; i++) {
1941        bidiLevels[i] = (byte)(runs[i].analysis.s.uBidiLevel & 0x1F);
1942    }
1943    /*
1944    * Feature in Windows. If the orientation is RTL Uniscribe will
1945    * resolve the level of line breaks to 1, this can cause the line
1946    * break to be reorder to the middle of the line. The fix is to set
1947    * the level to zero to prevent it to be reordered.
1948    */

1949    StyleItem lastRun = runs[length - 1];
1950    if (lastRun.lineBreak && !lastRun.softBreak) {
1951        bidiLevels[length - 1] = 0;
1952    }
1953    int[] log2vis = new int[length];
1954    OS.ScriptLayout(length, bidiLevels, null, log2vis);
1955    StyleItem[] result = new StyleItem[length];
1956    for (int i=0; i<length; i++) {
1957        result[log2vis[i]] = runs[i];
1958    }
1959    if ((orientation & SWT.RIGHT_TO_LEFT) != 0) {
1960        if (terminate) length--;
1961        for (int i = 0; i < length / 2 ; i++) {
1962            StyleItem tmp = result[i];
1963            result[i] = result[length - i - 1];
1964            result[length - i - 1] = tmp;
1965        }
1966    }
1967    return result;
1968}
1969
1970/**
1971 * Sets the text alignment for the receiver. The alignment controls
1972 * how a line of text is positioned horizontally. The argument should
1973 * be one of <code>SWT.LEFT</code>, <code>SWT.RIGHT</code> or <code>SWT.CENTER</code>.
1974 * <p>
1975 * The default alignment is <code>SWT.LEFT</code>. Note that the receiver's
1976 * width must be set in order to use <code>SWT.RIGHT</code> or <code>SWT.CENTER</code>
1977 * alignment.
1978 * </p>
1979 *
1980 * @param alignment the new alignment
1981 *
1982 * @exception SWTException <ul>
1983 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1984 * </ul>
1985 *
1986 * @see #setWidth(int)
1987 */

1988public void setAlignment (int alignment) {
1989    checkLayout();
1990    int mask = SWT.LEFT | SWT.CENTER | SWT.RIGHT;
1991    alignment &= mask;
1992    if (alignment == 0) return;
1993    if ((alignment & SWT.LEFT) != 0) alignment = SWT.LEFT;
1994    if ((alignment & SWT.RIGHT) != 0) alignment = SWT.RIGHT;
1995    if (this.alignment == alignment) return;
1996    freeRuns();
1997    this.alignment = alignment;
1998}
1999
2000/**
2001 * Sets the ascent of the receiver. The ascent is distance in pixels
2002 * from the baseline to the top of the line and it is applied to all
2003 * lines. The default value is <code>-1</code> which means that the
2004 * ascent is calculated from the line fonts.
2005 *
2006 * @param ascent the new ascent
2007 *
2008 * @exception IllegalArgumentException <ul>
2009 * <li>ERROR_INVALID_ARGUMENT - if the ascent is less than <code>-1</code></li>
2010 * </ul>
2011 * @exception SWTException <ul>
2012 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2013 * </ul>
2014 *
2015 * @see #setDescent(int)
2016 * @see #getLineMetrics(int)
2017 */

2018public void setAscent(int ascent) {
2019    checkLayout();
2020    if (ascent < -1) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2021    if (this.ascent == ascent) return;
2022    freeRuns();
2023    this.ascent = ascent;
2024}
2025
2026/**
2027 * Sets the descent of the receiver. The descent is distance in pixels
2028 * from the baseline to the bottom of the line and it is applied to all
2029 * lines. The default value is <code>-1</code> which means that the
2030 * descent is calculated from the line fonts.
2031 *
2032 * @param descent the new descent
2033 *
2034 * @exception IllegalArgumentException <ul>
2035 * <li>ERROR_INVALID_ARGUMENT - if the descent is less than <code>-1</code></li>
2036 * </ul>
2037 * @exception SWTException <ul>
2038 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2039 * </ul>
2040 *
2041 * @see #setAscent(int)
2042 * @see #getLineMetrics(int)
2043 */

2044public void setDescent(int descent) {
2045    checkLayout();
2046    if (descent < -1) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2047    if (this.descent == descent) return;
2048    freeRuns();
2049    this.descent = descent;
2050}
2051
2052/**
2053 * Sets the default font which will be used by the receiver
2054 * to draw and measure text. If the
2055 * argument is null, then a default font appropriate
2056 * for the platform will be used instead. Note that a text
2057 * style can override the default font.
2058 *
2059 * @param font the new font for the receiver, or null to indicate a default font
2060 *
2061 * @exception IllegalArgumentException <ul>
2062 * <li>ERROR_INVALID_ARGUMENT - if the font has been disposed</li>
2063 * </ul>
2064 * @exception SWTException <ul>
2065 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2066 * </ul>
2067 */

2068public void setFont (Font font) {
2069    checkLayout();
2070    if (font != null && font.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2071    if (this.font == font) return;
2072    if (font != null && font.equals(this.font)) return;
2073    freeRuns();
2074    this.font = font;
2075}
2076
2077/**
2078 * Sets the indent of the receiver. This indent it applied of the first line of
2079 * each paragraph.
2080 *
2081 * @param indent new indent
2082 *
2083 * @exception SWTException <ul>
2084 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2085 * </ul>
2086 *
2087 * @since 3.2
2088 */

2089public void setIndent (int indent) {
2090    checkLayout();
2091    if (indent < 0) return;
2092    if (this.indent == indent) return;
2093    freeRuns();
2094    this.indent = indent;
2095}
2096
2097/**
2098 * Sets the justification of the receiver. Note that the receiver's
2099 * width must be set in order to use justification.
2100 *
2101 * @param justify new justify
2102 *
2103 * @exception SWTException <ul>
2104 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2105 * </ul>
2106 *
2107 * @since 3.2
2108 */

2109public void setJustify (boolean justify) {
2110    checkLayout();
2111    if (this.justify == justify) return;
2112    freeRuns();
2113    this.justify = justify;
2114}
2115 
2116/**
2117 * Sets the orientation of the receiver, which must be one
2118 * of <code>SWT.LEFT_TO_RIGHT</code> or <code>SWT.RIGHT_TO_LEFT</code>.
2119 *
2120 * @param orientation new orientation style
2121 *
2122 * @exception SWTException <ul>
2123 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2124 * </ul>
2125 */

2126public void setOrientation (int orientation) {
2127    checkLayout();
2128    int mask = SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT;
2129    orientation &= mask;
2130    if (orientation == 0) return;
2131    if ((orientation & SWT.LEFT_TO_RIGHT) != 0) orientation = SWT.LEFT_TO_RIGHT;
2132    if (this.orientation == orientation) return;
2133    this.orientation = orientation;
2134    freeRuns();
2135}
2136
2137/**
2138 * Sets the offsets of the receiver's text segments. Text segments are used to
2139 * override the default behaviour of the bidirectional algorithm.
2140 * Bidirectional reordering can happen within a text segment but not
2141 * between two adjacent segments.
2142 * <p>
2143 * Each text segment is determined by two consecutive offsets in the
2144 * <code>segments</code> arrays. The first element of the array should
2145 * always be zero and the last one should always be equals to length of
2146 * the text.
2147 * </p>
2148 *
2149 * @param segments the text segments offset
2150 *
2151 * @exception SWTException <ul>
2152 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2153 * </ul>
2154 */

2155public void setSegments(int[] segments) {
2156    checkLayout();
2157    if (this.segments == null && segments == null) return;
2158    if (this.segments != null && segments != null) {
2159        if (this.segments.length == segments.length) {
2160            int i;
2161            for (i = 0; i <segments.length; i++) {
2162                if (this.segments[i] != segments[i]) break;
2163            }
2164            if (i == segments.length) return;
2165        }
2166    }
2167    freeRuns();
2168    this.segments = segments;
2169}
2170
2171/**
2172 * Sets the line spacing of the receiver. The line spacing
2173 * is the space left between lines.
2174 *
2175 * @param spacing the new line spacing
2176 *
2177 * @exception IllegalArgumentException <ul>
2178 * <li>ERROR_INVALID_ARGUMENT - if the spacing is negative</li>
2179 * </ul>
2180 * @exception SWTException <ul>
2181 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2182 * </ul>
2183 */

2184public void setSpacing (int spacing) {
2185    checkLayout();
2186    if (spacing < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2187    if (this.lineSpacing == spacing) return;
2188    freeRuns();
2189    this.lineSpacing = spacing;
2190}
2191
2192/**
2193 * Sets the style of the receiver for the specified range. Styles previously
2194 * set for that range will be overwritten. The start and end offsets are
2195 * inclusive and will be clamped if out of range.
2196 *
2197 * @param style the style
2198 * @param start the start offset
2199 * @param end the end offset
2200 *
2201 * @exception SWTException <ul>
2202 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2203 * </ul>
2204 */

2205public void setStyle (TextStyle style, int start, int end) {
2206    checkLayout();
2207    int length = text.length();
2208    if (length == 0) return;
2209    if (start > end) return;
2210    start = Math.min(Math.max(0, start), length - 1);
2211    end = Math.min(Math.max(0, end), length - 1);
2212    int low = -1;
2213    int high = styles.length;
2214    while (high - low > 1) {
2215        int index = (high + low) / 2;
2216        if (styles[index + 1].start > start) {
2217            high = index;
2218        } else {
2219            low = index;
2220        }
2221    }
2222    if (0 <= high && high < styles.length) {
2223        StyleItem item = styles[high];
2224        if (item.start == start && styles[high + 1].start - 1 == end) {
2225            if (style == null) {
2226                if (item.style == null) return;
2227            } else {
2228                if (style.equals(item.style)) return;
2229            }
2230        }
2231    }
2232    freeRuns();
2233    int modifyStart = high;
2234    int modifyEnd = modifyStart;
2235    while (modifyEnd < styles.length) {
2236        if (styles[modifyEnd + 1].start > end) break;
2237        modifyEnd++;
2238    }
2239    if (modifyStart == modifyEnd) {
2240        int styleStart = styles[modifyStart].start;
2241        int styleEnd = styles[modifyEnd + 1].start - 1;
2242        if (styleStart == start && styleEnd == end) {
2243            styles[modifyStart].style = style;
2244            return;
2245        }
2246        if (styleStart != start && styleEnd != end) {
2247            StyleItem[] newStyles = new StyleItem[styles.length + 2];
2248            System.arraycopy(styles, 0, newStyles, 0, modifyStart + 1);
2249            StyleItem item = new StyleItem();
2250            item.start = start;
2251            item.style = style;
2252            newStyles[modifyStart + 1] = item;
2253            item = new StyleItem();
2254            item.start = end + 1;
2255            item.style = styles[modifyStart].style;
2256            newStyles[modifyStart + 2] = item;
2257            System.arraycopy(styles, modifyEnd + 1, newStyles, modifyEnd + 3, styles.length - modifyEnd - 1);
2258            styles = newStyles;
2259            return;
2260        }
2261    }
2262    if (start == styles[modifyStart].start) modifyStart--;
2263    if (end == styles[modifyEnd + 1].start - 1) modifyEnd++;
2264    int newLength = styles.length + 1 - (modifyEnd - modifyStart - 1);
2265    StyleItem[] newStyles = new StyleItem[newLength];
2266    System.arraycopy(styles, 0, newStyles, 0, modifyStart + 1);
2267    StyleItem item = new StyleItem();
2268    item.start = start;
2269    item.style = style;
2270    newStyles[modifyStart + 1] = item;
2271    styles[modifyEnd].start = end + 1;
2272    System.arraycopy(styles, modifyEnd, newStyles, modifyStart + 2, styles.length - modifyEnd);
2273    styles = newStyles;
2274}
2275
2276/**
2277 * Sets the receiver's tab list. Each value in the tab list specifies
2278 * the space in pixels from the origin of the text layout to the respective
2279 * tab stop. The last tab stop width is repeated continuously.
2280 *
2281 * @param tabs the new tab list
2282 *
2283 * @exception SWTException <ul>
2284 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2285 * </ul>
2286 */

2287public void setTabs (int[] tabs) {
2288    checkLayout();
2289    if (this.tabs == null && tabs == null) return;
2290    if (this.tabs != null && tabs !=null) {
2291        if (this.tabs.length == tabs.length) {
2292            int i;
2293            for (i = 0; i <tabs.length; i++) {
2294                if (this.tabs[i] != tabs[i]) break;
2295            }
2296            if (i == tabs.length) return;
2297        }
2298    }
2299    freeRuns();
2300    this.tabs = tabs;
2301}
2302
2303/**
2304 * Sets the receiver's text.
2305 *
2306 * @param text the new text
2307 *
2308 * @exception IllegalArgumentException <ul>
2309 * <li>ERROR_NULL_ARGUMENT - if the text is null</li>
2310 * </ul>
2311 * @exception SWTException <ul>
2312 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2313 * </ul>
2314 */

2315public void setText (String JavaDoc text) {
2316    checkLayout();
2317    if (text == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
2318    if (text.equals(this.text)) return;
2319    freeRuns();
2320    this.text = text;
2321    styles = new StyleItem[2];
2322    styles[0] = new StyleItem();
2323    styles[1] = new StyleItem();
2324    styles[1].start = text.length();
2325}
2326
2327/**
2328 * Sets the line width of the receiver, which determines how
2329 * text should be wrapped and aligned. The default value is
2330 * <code>-1</code> which means wrapping is disabled.
2331 *
2332 * @param width the new width
2333 *
2334 * @exception IllegalArgumentException <ul>
2335 * <li>ERROR_INVALID_ARGUMENT - if the width is <code>0</code> or less than <code>-1</code></li>
2336 * </ul>
2337 * @exception SWTException <ul>
2338 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2339 * </ul>
2340 *
2341 * @see #setAlignment(int)
2342 */

2343public void setWidth (int width) {
2344    checkLayout();
2345    if (width < -1 || width == 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2346    if (this.wrapWidth == width) return;
2347    freeRuns();
2348    this.wrapWidth = width;
2349}
2350
2351boolean shape (int hdc, StyleItem run, char[] chars, int[] glyphCount, int maxGlyphs) {
2352    int hr = OS.ScriptShape(hdc, run.psc, chars, chars.length, maxGlyphs, run.analysis, run.glyphs, run.clusters, run.visAttrs, glyphCount);
2353    run.glyphCount = glyphCount[0];
2354    if (hr != OS.USP_E_SCRIPT_NOT_IN_FONT) {
2355        SCRIPT_FONTPROPERTIES fp = new SCRIPT_FONTPROPERTIES ();
2356        fp.cBytes = SCRIPT_FONTPROPERTIES.sizeof;
2357        OS.ScriptGetFontProperties(hdc, run.psc, fp);
2358        short[] glyphs = new short[glyphCount[0]];
2359        OS.MoveMemory(glyphs, run.glyphs, glyphs.length * 2);
2360        int i;
2361        for (i = 0; i < glyphs.length; i++) {
2362            if (glyphs[i] == fp.wgDefault) break;
2363        }
2364        if (i == glyphs.length) return true;
2365    }
2366    if (run.psc != 0) {
2367        OS.ScriptFreeCache(run.psc);
2368        glyphCount[0] = 0;
2369        OS.MoveMemory(run.psc, glyphCount, 4);
2370    }
2371    run.glyphCount = 0;
2372    return false;
2373}
2374
2375/*
2376 * Generate glyphs for one Run.
2377 */

2378void shape (final int hdc, final StyleItem run) {
2379    int[] buffer = new int[1];
2380    char[] chars = new char[run.length];
2381    segmentsText.getChars(run.start, run.start + run.length, chars, 0);
2382    int maxGlyphs = (chars.length * 3 / 2) + 16;
2383    int hHeap = OS.GetProcessHeap();
2384    run.glyphs = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, maxGlyphs * 2);
2385    if (run.glyphs == 0) SWT.error(SWT.ERROR_NO_HANDLES);
2386    run.clusters = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, maxGlyphs * 2);
2387    if (run.clusters == 0) SWT.error(SWT.ERROR_NO_HANDLES);
2388    run.visAttrs = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, maxGlyphs * SCRIPT_VISATTR_SIZEOF);
2389    if (run.visAttrs == 0) SWT.error(SWT.ERROR_NO_HANDLES);
2390    run.psc = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, 4);
2391    if (run.psc == 0) SWT.error(SWT.ERROR_NO_HANDLES);
2392    if (!shape(hdc, run, chars, buffer, maxGlyphs)) {
2393        if (mLangFontLink2 != 0) {
2394            int[] dwCodePages = new int[1];
2395            int[] cchCodePages = new int[1];
2396            /* GetStrCodePages() */
2397            OS.VtblCall(4, mLangFontLink2, chars, chars.length, 0, dwCodePages, cchCodePages);
2398            int[] hNewFont = new int[1];
2399            /* MapFont() */
2400            if (OS.VtblCall(10, mLangFontLink2, hdc, dwCodePages[0], chars[0], hNewFont) == OS.S_OK) {
2401                int hFont = OS.SelectObject(hdc, hNewFont[0]);
2402                if (shape(hdc, run, chars, buffer, maxGlyphs)) {
2403                    run.fallbackFont = hNewFont[0];
2404                } else {
2405                    /* ReleaseFont() */
2406                    OS.VtblCall(8, mLangFontLink2, hNewFont[0]);
2407                    OS.SelectObject(hdc, hFont);
2408                    SCRIPT_PROPERTIES properties = new SCRIPT_PROPERTIES();
2409                    OS.MoveMemory(properties, device.scripts[run.analysis.eScript], SCRIPT_PROPERTIES.sizeof);
2410                    if (properties.fPrivateUseArea) {
2411                        run.analysis.fNoGlyphIndex = true;
2412                    }
2413                    OS.ScriptShape(hdc, run.psc, chars, chars.length, maxGlyphs, run.analysis, run.glyphs, run.clusters, run.visAttrs, buffer);
2414                    run.glyphCount = buffer[0];
2415                }
2416            }
2417        }
2418    }
2419    int[] abc = new int[3];
2420    run.advances = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, run.glyphCount * 4);
2421    if (run.advances == 0) SWT.error(SWT.ERROR_NO_HANDLES);
2422    run.goffsets = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, run.glyphCount * GOFFSET_SIZEOF);
2423    if (run.goffsets == 0) SWT.error(SWT.ERROR_NO_HANDLES);
2424    OS.ScriptPlace(hdc, run.psc, run.glyphs, run.glyphCount, run.visAttrs, run.analysis, run.advances, run.goffsets, abc);
2425    if (run.style != null && run.style.metrics != null) {
2426        GlyphMetrics metrics = run.style.metrics;
2427        /*
2428        * Bug in Windows, on a Japanese machine, Uniscribe returns glyphcount
2429        * equals zero for FFFC (possibly other unicode code points), the fix
2430        * is to make sure the glyph is at least one pixel wide.
2431        */

2432        run.width = metrics.width * Math.max (1, run.glyphCount);
2433        run.ascent = metrics.ascent;
2434        run.descent = metrics.descent;
2435        run.leading = 0;
2436    } else {
2437        run.width = abc[0] + abc[1] + abc[2];
2438        TEXTMETRIC lptm = OS.IsUnicode ? (TEXTMETRIC)new TEXTMETRICW() : new TEXTMETRICA();
2439        OS.GetTextMetrics(hdc, lptm);
2440        run.ascent = lptm.tmAscent;
2441        run.descent = lptm.tmDescent;
2442        run.leading = lptm.tmInternalLeading;
2443    }
2444    if (run.style != null) {
2445        run.ascent += run.style.rise;
2446        run.descent -= +run.style.rise;
2447    }
2448}
2449
2450int validadeOffset(int offset, int step) {
2451    offset += step;
2452    if (segments != null && segments.length > 2) {
2453        for (int i = 0; i < segments.length; i++) {
2454            if (translateOffset(segments[i]) - 1 == offset) {
2455                offset += step;
2456                break;
2457            }
2458        }
2459    }
2460    return offset;
2461}
2462
2463/**
2464 * Returns a string containing a concise, human-readable
2465 * description of the receiver.
2466 *
2467 * @return a string representation of the receiver
2468 */

2469public String JavaDoc toString () {
2470    if (isDisposed()) return "TextLayout {*DISPOSED*}";
2471    return "TextLayout {}";
2472}
2473
2474int translateOffset(int offset) {
2475    if (segments == null) return offset;
2476    int nSegments = segments.length;
2477    if (nSegments <= 1) return offset;
2478    int length = text.length();
2479    if (length == 0) return offset;
2480    if (nSegments == 2) {
2481        if (segments[0] == 0 && segments[1] == length) return offset;
2482    }
2483    for (int i = 0; i < nSegments && offset - i >= segments[i]; i++) {
2484        offset++;
2485    }
2486    return offset;
2487}
2488
2489int untranslateOffset(int offset) {
2490    if (segments == null) return offset;
2491    int nSegments = segments.length;
2492    if (nSegments <= 1) return offset;
2493    int length = text.length();
2494    if (length == 0) return offset;
2495    if (nSegments == 2) {
2496        if (segments[0] == 0 && segments[1] == length) return offset;
2497    }
2498    for (int i = 0; i < nSegments && offset > segments[i]; i++) {
2499        offset--;
2500    }
2501    return offset;
2502}
2503}
2504
Popular Tags