]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/graphics/TextLayout.java
52778ace813eaf55b22fe1cf2ade93b8da7652ae
[simantics/platform.git] / bundles / org.eclipse.swt.win32.win32.x86_64 / src / org / eclipse / swt / graphics / TextLayout.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2018 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.swt.graphics;
15
16 import java.util.*;
17
18 import org.eclipse.swt.*;
19 import org.eclipse.swt.internal.*;
20 import org.eclipse.swt.internal.gdip.*;
21 import org.eclipse.swt.internal.ole.win32.*;
22 import org.eclipse.swt.internal.win32.*;
23
24 /**
25  * <code>TextLayout</code> is a graphic object that represents
26  * styled text.
27  * <p>
28  * Instances of this class provide support for drawing, cursor
29  * navigation, hit testing, text wrapping, alignment, tab expansion
30  * line breaking, etc.  These are aspects required for rendering internationalized text.
31  * </p><p>
32  * Application code must explicitly invoke the <code>TextLayout#dispose()</code>
33  * method to release the operating system resources managed by each instance
34  * when those instances are no longer required.
35  * </p>
36  *
37  * @see <a href="http://www.eclipse.org/swt/snippets/#textlayout">TextLayout, TextStyle snippets</a>
38  * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: CustomControlExample, StyledText tab</a>
39  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
40  *
41  * @since 3.0
42  */
43 public final class TextLayout extends Resource {
44         Font font;
45         String text, segmentsText;
46         int lineSpacingInPoints;
47         int ascentInPixels, descentInPixels;
48         int alignment;
49         int wrapWidth;
50         int orientation;
51         int textDirection;
52         int indent;
53         int wrapIndent;
54         boolean justify;
55         int[] tabs;
56         int[] segments;
57         char[] segmentsChars;
58         StyleItem[] styles;
59         int stylesCount;
60
61         StyleItem[] allRuns;
62         StyleItem[][] runs;
63         int[] lineOffset, lineY, lineWidth;
64         IMLangFontLink2 mLangFontLink2;
65         int verticalIndentInPoints;
66
67         static final char LTR_MARK = '\u200E', RTL_MARK = '\u200F';
68         static final int SCRIPT_VISATTR_SIZEOF = 2;
69         static final int GOFFSET_SIZEOF = 8;
70         static final int MERGE_MAX = 512;
71         static final int TOO_MANY_RUNS = 1024;
72
73         /* IME has a copy of these constants */
74         static final int UNDERLINE_IME_DOT = 1 << 16;
75         static final int UNDERLINE_IME_DASH = 2 << 16;
76         static final int UNDERLINE_IME_THICK = 3 << 16;
77
78         class StyleItem {
79                 TextStyle style;
80                 int start, length;
81                 boolean lineBreak, softBreak, tab;
82
83                 /*Script cache and analysis */
84                 SCRIPT_ANALYSIS analysis;
85                 long psc = 0;
86
87                 /*Shape info (malloc when the run is shaped) */
88                 long glyphs;
89                 int glyphCount;
90                 long clusters;
91                 long visAttrs;
92
93                 /*Place info (malloc when the run is placed) */
94                 long advances;
95                 long goffsets;
96                 int width;
97                 int ascentInPoints;
98                 int descentInPoints;
99                 int leadingInPoints;
100                 int x;
101                 int underlinePos, underlineThickness;
102                 int strikeoutPos, strikeoutThickness;
103
104                 /* Justify info (malloc during computeRuns) */
105                 long justify;
106
107                 /* ScriptBreak */
108                 long psla;
109
110                 long fallbackFont;
111
112         void free() {
113                 long hHeap = OS.GetProcessHeap();
114                 if (psc != 0) {
115                         OS.ScriptFreeCache (psc);
116                         OS.HeapFree(hHeap, 0, psc);
117                         psc = 0;
118                 }
119                 if (glyphs != 0) {
120                         OS.HeapFree(hHeap, 0, glyphs);
121                         glyphs = 0;
122                         glyphCount = 0;
123                 }
124                 if (clusters != 0) {
125                         OS.HeapFree(hHeap, 0, clusters);
126                         clusters = 0;
127                 }
128                 if (visAttrs != 0) {
129                         OS.HeapFree(hHeap, 0, visAttrs);
130                         visAttrs = 0;
131                 }
132                 if (advances != 0) {
133                         OS.HeapFree(hHeap, 0, advances);
134                         advances = 0;
135                 }
136                 if (goffsets != 0) {
137                         OS.HeapFree(hHeap, 0, goffsets);
138                         goffsets = 0;
139                 }
140                 if (justify != 0) {
141                         OS.HeapFree(hHeap, 0, justify);
142                         justify = 0;
143                 }
144                 if (psla != 0) {
145                         OS.HeapFree(hHeap, 0, psla);
146                         psla = 0;
147                 }
148                 if (fallbackFont != 0) {
149                         OS.DeleteObject(fallbackFont);
150                         fallbackFont = 0;
151                 }
152                 width = ascentInPoints = descentInPoints = x = 0;
153                 lineBreak = softBreak = false;
154         }
155         @Override
156         public String toString () {
157                 return "StyleItem {" + start + ", " + style + "}";
158         }
159         }
160
161 /**
162  * Constructs a new instance of this class on the given device.
163  * <p>
164  * You must dispose the text layout when it is no longer required.
165  * </p>
166  *
167  * @param device the device on which to allocate the text layout
168  *
169  * @exception IllegalArgumentException <ul>
170  *    <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
171  * </ul>
172  *
173  * @see #dispose()
174  */
175 public TextLayout (Device device) {
176         super(device);
177         wrapWidth = ascentInPixels = descentInPixels = -1;
178         lineSpacingInPoints = 0;
179         verticalIndentInPoints = 0;
180         orientation = SWT.LEFT_TO_RIGHT;
181         textDirection = SWT.LEFT_TO_RIGHT;
182         styles = new StyleItem[2];
183         styles[0] = new StyleItem();
184         styles[1] = new StyleItem();
185         stylesCount = 2;
186         text = ""; //$NON-NLS-1$
187         long[] ppv = new long[1];
188         OS.OleInitialize(0);
189         if (COM.CoCreateInstance(COM.CLSID_CMultiLanguage, 0, COM.CLSCTX_INPROC_SERVER, COM.IID_IMLangFontLink2, ppv) == OS.S_OK) {
190                 mLangFontLink2 = new IMLangFontLink2(ppv[0]);
191         }
192         init();
193 }
194
195 RECT addClipRect(StyleItem run, RECT clipRect, RECT rect, int selectionStart, int selectionEnd) {
196         if (rect != null) {
197                 if (clipRect == null) {
198                         clipRect = new RECT ();
199                         OS.SetRect(clipRect, -1, rect.top, -1, rect.bottom);
200                 }
201                 boolean isRTL = (orientation & SWT.RIGHT_TO_LEFT) != 0;
202                 if (run.start <= selectionStart && selectionStart <= run.start + run.length) {
203                         if (run.analysis.fRTL ^ isRTL) {
204                                 clipRect.right = rect.left;
205                         } else {
206                                 clipRect.left = rect.left;
207                         }
208                 }
209                 if (run.start <= selectionEnd && selectionEnd <= run.start + run.length) {
210                         if (run.analysis.fRTL ^ isRTL) {
211                                 clipRect.left = rect.right;
212                         } else {
213                                 clipRect.right = rect.right;
214                         }
215                 }
216         }
217         return clipRect;
218 }
219
220 void breakRun(StyleItem run) {
221         if (run.psla != 0) return;
222         char[] chars = new char[run.length];
223         segmentsText.getChars(run.start, run.start + run.length, chars, 0);
224         long hHeap = OS.GetProcessHeap();
225         run.psla = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, SCRIPT_LOGATTR.sizeof * chars.length);
226         if (run.psla == 0) SWT.error(SWT.ERROR_NO_HANDLES);
227         OS.ScriptBreak(chars, chars.length, run.analysis, run.psla);
228 }
229
230 void checkLayout () {
231         if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
232 }
233
234 /*
235 *  Compute the runs: itemize, shape, place, and reorder the runs.
236 *       Break paragraphs into lines, wraps the text, and initialize caches.
237 */
238 void computeRuns (GC gc) {
239         if (runs != null) return;
240         long hDC = gc != null ? gc.handle : device.internal_new_GC(null);
241         long srcHdc = OS.CreateCompatibleDC(hDC);
242         allRuns = itemize();
243         for (int i=0; i<allRuns.length - 1; i++) {
244                 StyleItem run = allRuns[i];
245                 OS.SelectObject(srcHdc, getItemFont(run));
246                 shape(srcHdc, run);
247         }
248         SCRIPT_LOGATTR logAttr = new SCRIPT_LOGATTR();
249         SCRIPT_PROPERTIES properties = new SCRIPT_PROPERTIES();
250         int lineWidth = indent, lineStart = 0, lineCount = 1;
251         for (int i=0; i<allRuns.length - 1; i++) {
252                 StyleItem run = allRuns[i];
253                 if (tabs != null && run.tab) {
254                         int tabsLength = tabs.length, j;
255                         for (j = 0; j < tabsLength; j++) {
256                                 if (tabs[j] > lineWidth) {
257                                         run.width = tabs[j] - lineWidth;
258                                         break;
259                                 }
260                         }
261                         if (j == tabsLength) {
262                                 int tabX = tabs[tabsLength-1];
263                                 int lastTabWidth = tabsLength > 1 ? tabs[tabsLength-1] - tabs[tabsLength-2] : tabs[0];
264                                 if (lastTabWidth > 0) {
265                                         while (tabX <= lineWidth) tabX += lastTabWidth;
266                                         run.width = tabX - lineWidth;
267                                 }
268                         }
269                         int length = run.length;
270                         if (length > 1) {
271                                 int stop = j + length - 1;
272                                 if (stop < tabsLength) {
273                                         run.width += tabs[stop] - tabs[j];
274                                 } else {
275                                         if (j < tabsLength) {
276                                                 run.width += tabs[tabsLength - 1] - tabs[j];
277                                                 length -= (tabsLength - 1) - j;
278                                         }
279                                         int lastTabWidth = tabsLength > 1 ? tabs[tabsLength-1] - tabs[tabsLength-2] : tabs[0];
280                                         run.width += lastTabWidth * (length - 1);
281                                 }
282                         }
283                 }
284                 if (wrapWidth != -1 && lineWidth + run.width > wrapWidth && !run.tab && !run.lineBreak) {
285                         int start = 0;
286                         int[] piDx = new int[run.length];
287                         if (run.style != null && run.style.metrics != null) {
288                                 piDx[0] = run.width;
289                         } else {
290                                 OS.ScriptGetLogicalWidths(run.analysis, run.length, run.glyphCount, run.advances, run.clusters, run.visAttrs, piDx);
291                         }
292                         int width = 0, maxWidth = wrapWidth - lineWidth;
293                         while (width + piDx[start] < maxWidth) {
294                                 width += piDx[start++];
295                         }
296                         int firstStart = start;
297                         int firstIndice = i;
298                         while (i >= lineStart) {
299                                 breakRun(run);
300                                 while (start >= 0) {
301                                         OS.MoveMemory(logAttr, run.psla + (start * SCRIPT_LOGATTR.sizeof), SCRIPT_LOGATTR.sizeof);
302                                         if (logAttr.fSoftBreak || logAttr.fWhiteSpace) break;
303                                         start--;
304                                 }
305
306                                 /*
307                                 *  Bug in Windows. For some reason Uniscribe sets the fSoftBreak flag for the first letter
308                                 *  after a letter with an accent. This cause a break line to be set in the middle of a word.
309                                 *  The fix is to detect the case and ignore fSoftBreak forcing the algorithm keep searching.
310                                 */
311                                 if (start == 0 && i != lineStart && !run.tab) {
312                                         if (logAttr.fSoftBreak && !logAttr.fWhiteSpace) {
313                                                 OS.MoveMemory(properties, device.scripts[run.analysis.eScript], SCRIPT_PROPERTIES.sizeof);
314                                                 int langID = properties.langid;
315                                                 StyleItem pRun = allRuns[i - 1];
316                                                 OS.MoveMemory(properties, device.scripts[pRun.analysis.eScript], SCRIPT_PROPERTIES.sizeof);
317                                                 if (properties.langid == langID || langID == OS.LANG_NEUTRAL || properties.langid == OS.LANG_NEUTRAL) {
318                                                         breakRun(pRun);
319                                                         OS.MoveMemory(logAttr, pRun.psla + ((pRun.length - 1) * SCRIPT_LOGATTR.sizeof), SCRIPT_LOGATTR.sizeof);
320                                                         if (!logAttr.fWhiteSpace) start = -1;
321                                                 }
322                                         }
323                                 }
324                                 if (start >= 0 || i == lineStart) break;
325                                 run = allRuns[--i];
326                                 start = run.length - 1;
327                         }
328                         boolean wrapEntireRun = start == 0 && i != lineStart && !run.tab;
329                         if (wrapEntireRun) {
330                                 breakRun(run);
331                                 OS.MoveMemory(logAttr, run.psla + (start * SCRIPT_LOGATTR.sizeof), SCRIPT_LOGATTR.sizeof);
332                                 wrapEntireRun = !logAttr.fWhiteSpace;
333                         }
334                         if (wrapEntireRun) {
335                                 run = allRuns[--i];
336                                 start = run.length;
337                         } else if (start <= 0 && i == lineStart) {
338                                 /*
339                                  * No soft-break or line-feed found. Avoid breaking a run at
340                                  * the first character (firstStart == 0) unless it's the
341                                  * only run available (firstIndice == lineStart). See bug 408833.
342                                  */
343                                 if (firstStart == 0 && firstIndice > lineStart) {
344                                         i = firstIndice - 1;
345                                         run = allRuns[i];
346                                         start = run.length;
347                                 } else {
348                                         i = firstIndice;
349                                         run = allRuns[i];
350                                         start = Math.max(1, firstStart);
351                                 }
352                         }
353                         breakRun(run);
354                         while (start < run.length) {
355                                 OS.MoveMemory(logAttr, run.psla + (start * SCRIPT_LOGATTR.sizeof), SCRIPT_LOGATTR.sizeof);
356                                 if (!logAttr.fWhiteSpace) break;
357                                 start++;
358                         }
359                         if (0 < start && start < run.length) {
360                                 StyleItem newRun = new StyleItem();
361                                 newRun.start = run.start + start;
362                                 newRun.length = run.length - start;
363                                 newRun.style = run.style;
364                                 newRun.analysis = cloneScriptAnalysis(run.analysis);
365                                 run.free();
366                                 run.length = start;
367                                 OS.SelectObject(srcHdc, getItemFont(run));
368                                 run.analysis.fNoGlyphIndex = false;
369                                 shape (srcHdc, run);
370                                 OS.SelectObject(srcHdc, getItemFont(newRun));
371                                 newRun.analysis.fNoGlyphIndex = false;
372                                 shape (srcHdc, newRun);
373                                 StyleItem[] newAllRuns = new StyleItem[allRuns.length + 1];
374                                 System.arraycopy(allRuns, 0, newAllRuns, 0, i + 1);
375                                 System.arraycopy(allRuns, i + 1, newAllRuns, i + 2, allRuns.length - i - 1);
376                                 allRuns = newAllRuns;
377                                 allRuns[i + 1] = newRun;
378                         }
379                         if (i != allRuns.length - 2) {
380                                 run.softBreak = run.lineBreak = true;
381                         }
382                 }
383                 lineWidth += run.width;
384                 if (run.lineBreak) {
385                         lineStart = i + 1;
386                         lineWidth = run.softBreak ?  wrapIndent : indent;
387                         lineCount++;
388                 }
389         }
390         lineWidth = 0;
391         runs = new StyleItem[lineCount][];
392         lineOffset = new int[lineCount + 1];
393         lineY = new int[lineCount + 1];
394         this.lineWidth = new int[lineCount];
395         int lineRunCount = 0, line = 0;
396         int ascentInPoints = Math.max(0, DPIUtil.autoScaleDown(getDevice(), this.ascentInPixels));
397         int descentInPoints = Math.max(0, DPIUtil.autoScaleDown(getDevice(), this.descentInPixels));
398         StyleItem[] lineRuns = new StyleItem[allRuns.length];
399         for (int i=0; i<allRuns.length; i++) {
400                 StyleItem run = allRuns[i];
401                 lineRuns[lineRunCount++] = run;
402                 lineWidth += run.width;
403                 ascentInPoints = Math.max(ascentInPoints, run.ascentInPoints);
404                 descentInPoints = Math.max(descentInPoints, run.descentInPoints);
405                 if (run.lineBreak || i == allRuns.length - 1) {
406                         /* Update the run metrics if the last run is a hard break. */
407                         if (lineRunCount == 1 && (i == allRuns.length - 1 || !run.softBreak)) {
408                                 TEXTMETRIC lptm = new TEXTMETRIC();
409                                 OS.SelectObject(srcHdc, getItemFont(run));
410                                 OS.GetTextMetrics(srcHdc, lptm);
411                                 run.ascentInPoints = DPIUtil.autoScaleDown(getDevice(), lptm.tmAscent);
412                                 run.descentInPoints = DPIUtil.autoScaleDown(getDevice(), lptm.tmDescent);
413                                 ascentInPoints = Math.max(ascentInPoints, run.ascentInPoints);
414                                 descentInPoints = Math.max(descentInPoints, run.descentInPoints);
415                         }
416                         runs[line] = new StyleItem[lineRunCount];
417                         System.arraycopy(lineRuns, 0, runs[line], 0, lineRunCount);
418
419                         if (justify && wrapWidth != -1 && run.softBreak && lineWidth > 0) {
420                                 int lineIndent = wrapIndent;
421                                 if (line == 0) {
422                                         lineIndent = indent;
423                                 } else {
424                                         StyleItem[] previousLine = runs[line - 1];
425                                         StyleItem previousRun = previousLine[previousLine.length - 1];
426                                         if (previousRun.lineBreak && !previousRun.softBreak) {
427                                                 lineIndent = indent;
428                                         }
429                                 }
430                                 lineWidth += lineIndent;
431                                 long hHeap = OS.GetProcessHeap();
432                                 int newLineWidth = 0;
433                                 for (int j = 0; j < runs[line].length; j++) {
434                                         StyleItem item = runs[line][j];
435                                         int iDx = item.width * wrapWidth / lineWidth;
436                                         if (iDx != item.width) {
437                                                 item.justify = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, item.glyphCount * 4);
438                                                 if (item.justify == 0) SWT.error(SWT.ERROR_NO_HANDLES);
439                                                 OS.ScriptJustify(item.visAttrs, item.advances, item.glyphCount, iDx - item.width, 2, item.justify);
440                                                 item.width = iDx;
441                                         }
442                                         newLineWidth += item.width;
443                                 }
444                                 lineWidth = newLineWidth;
445                         }
446                         this.lineWidth[line] = lineWidth;
447
448                         StyleItem lastRun = runs[line][lineRunCount - 1];
449                         int lastOffset = lastRun.start + lastRun.length;
450                         runs[line] = reorder(runs[line], i == allRuns.length - 1);
451                         lastRun = runs[line][lineRunCount - 1];
452                         if (run.softBreak && run != lastRun) {
453                                 run.softBreak = run.lineBreak = false;
454                                 lastRun.softBreak = lastRun.lineBreak = true;
455                         }
456
457                         lineWidth = getLineIndent(line);
458                         for (int j = 0; j < runs[line].length; j++) {
459                                 runs[line][j].x = lineWidth;
460                                 lineWidth += runs[line][j].width;
461                         }
462                         line++;
463                         lineY[line] = lineY[line - 1] + ascentInPoints + descentInPoints + lineSpacingInPoints;
464                         lineOffset[line] = lastOffset;
465                         lineRunCount = lineWidth = 0;
466                         ascentInPoints = Math.max(0, DPIUtil.autoScaleDown(getDevice(), this.ascentInPixels));
467                         descentInPoints = Math.max(0, DPIUtil.autoScaleDown(getDevice(), this.descentInPixels));
468                 }
469         }
470         if (srcHdc != 0) OS.DeleteDC(srcHdc);
471         if (gc == null) device.internal_dispose_GC(hDC, null);
472 }
473
474 @Override
475 void destroy () {
476         freeRuns();
477         font = null;
478         text = null;
479         segmentsText = null;
480         tabs = null;
481         styles = null;
482         runs = null;
483         lineOffset = null;
484         lineY = null;
485         lineWidth = null;
486         segments = null;
487         segmentsChars = null;
488         if (mLangFontLink2 != null) {
489                 mLangFontLink2.Release();
490                 mLangFontLink2 = null;
491         }
492         OS.OleUninitialize();
493 }
494
495 SCRIPT_ANALYSIS cloneScriptAnalysis (SCRIPT_ANALYSIS src) {
496         SCRIPT_ANALYSIS dst = new SCRIPT_ANALYSIS();
497         dst.eScript = src.eScript;
498         dst.fRTL = src.fRTL;
499         dst.fLayoutRTL = src.fLayoutRTL;
500         dst.fLinkBefore = src.fLinkBefore;
501         dst.fLinkAfter = src.fLinkAfter;
502         dst.fLogicalOrder = src.fLogicalOrder;
503         dst.fNoGlyphIndex = src.fNoGlyphIndex;
504         dst.s = new SCRIPT_STATE();
505         dst.s.uBidiLevel = src.s.uBidiLevel;
506         dst.s.fOverrideDirection = src.s.fOverrideDirection;
507         dst.s.fInhibitSymSwap = src.s.fInhibitSymSwap;
508         dst.s.fCharShape = src.s.fCharShape;
509         dst.s.fDigitSubstitute = src.s.fDigitSubstitute;
510         dst.s.fInhibitLigate = src.s.fInhibitLigate;
511         dst.s.fDisplayZWG = src.s.fDisplayZWG;
512         dst.s.fArabicNumContext = src.s.fArabicNumContext;
513         dst.s.fGcpClusters = src.s.fGcpClusters;
514         dst.s.fReserved = src.s.fReserved;
515         dst.s.fEngineReserved = src.s.fEngineReserved;
516         return dst;
517 }
518
519 int[] computePolyline(int left, int top, int right, int bottom) {
520         int height = bottom - top; // can be any number
521         int width = 2 * height; // must be even
522         int peaks = Compatibility.ceil(right - left, width);
523         if (peaks == 0 && right - left > 2) {
524                 peaks = 1;
525         }
526         int length = ((2 * peaks) + 1) * 2;
527         if (length < 0) return new int[0];
528
529         int[] coordinates = new int[length];
530         for (int i = 0; i < peaks; i++) {
531                 int index = 4 * i;
532                 coordinates[index] = left + (width * i);
533                 coordinates[index+1] = bottom;
534                 coordinates[index+2] = coordinates[index] + width / 2;
535                 coordinates[index+3] = top;
536         }
537         coordinates[length-2] = left + (width * peaks);
538         coordinates[length-1] = bottom;
539         return coordinates;
540 }
541
542 long createGdipBrush(int pixel, int alpha) {
543         int argb = ((alpha & 0xFF) << 24) | ((pixel >> 16) & 0xFF) | (pixel & 0xFF00) | ((pixel & 0xFF) << 16);
544         return Gdip.SolidBrush_new(argb);
545 }
546
547 long createGdipBrush(Color color, int alpha) {
548         return createGdipBrush(color.handle, alpha);
549 }
550
551 /**
552  * Draws the receiver's text using the specified GC at the specified
553  * point.
554  *
555  * @param gc the GC to draw
556  * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn
557  * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn
558  *
559  * @exception SWTException <ul>
560  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
561  * </ul>
562  * @exception IllegalArgumentException <ul>
563  *    <li>ERROR_NULL_ARGUMENT - if the gc is null</li>
564  * </ul>
565  */
566 public void draw (GC gc, int x, int y) {
567         checkLayout();
568         drawInPixels(gc, DPIUtil.autoScaleUp(getDevice(), x), DPIUtil.autoScaleUp(getDevice(), y));
569 }
570
571 void drawInPixels (GC gc, int x, int y) {
572         drawInPixels(gc, x, y, -1, -1, null, null);
573 }
574
575 /**
576  * Draws the receiver's text using the specified GC at the specified
577  * point.
578  *
579  * @param gc the GC to draw
580  * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn
581  * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn
582  * @param selectionStart the offset where the selections starts, or -1 indicating no selection
583  * @param selectionEnd the offset where the selections ends, or -1 indicating no selection
584  * @param selectionForeground selection foreground, or NULL to use the system default color
585  * @param selectionBackground selection background, or NULL to use the system default color
586  *
587  * @exception SWTException <ul>
588  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
589  * </ul>
590  * @exception IllegalArgumentException <ul>
591  *    <li>ERROR_NULL_ARGUMENT - if the gc is null</li>
592  * </ul>
593  */
594 public void draw (GC gc, int x, int y, int selectionStart, int selectionEnd, Color selectionForeground, Color selectionBackground) {
595         checkLayout();
596         drawInPixels(gc, DPIUtil.autoScaleUp(getDevice(), x), DPIUtil.autoScaleUp(getDevice(), y), selectionStart, selectionEnd, selectionForeground, selectionBackground);
597 }
598
599 void drawInPixels (GC gc, int x, int y, int selectionStart, int selectionEnd, Color selectionForeground, Color selectionBackground) {
600         drawInPixels(gc, x, y, selectionStart, selectionEnd, selectionForeground, selectionBackground, 0);
601 }
602
603 /**
604  * Draws the receiver's text using the specified GC at the specified
605  * point.
606  * <p>
607  * The parameter <code>flags</code> can include one of <code>SWT.DELIMITER_SELECTION</code>
608  * or <code>SWT.FULL_SELECTION</code> to specify the selection behavior on all lines except
609  * for the last line, and can also include <code>SWT.LAST_LINE_SELECTION</code> to extend
610  * the specified selection behavior to the last line.
611  * </p>
612  * @param gc the GC to draw
613  * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn
614  * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn
615  * @param selectionStart the offset where the selections starts, or -1 indicating no selection
616  * @param selectionEnd the offset where the selections ends, or -1 indicating no selection
617  * @param selectionForeground selection foreground, or NULL to use the system default color
618  * @param selectionBackground selection background, or NULL to use the system default color
619  * @param flags drawing options
620  *
621  * @exception SWTException <ul>
622  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
623  * </ul>
624  * @exception IllegalArgumentException <ul>
625  *    <li>ERROR_NULL_ARGUMENT - if the gc is null</li>
626  * </ul>
627  *
628  * @since 3.3
629  */
630 public void draw (GC gc, int x, int y, int selectionStart, int selectionEnd, Color selectionForeground, Color selectionBackground, int flags) {
631         checkLayout();
632         drawInPixels(gc, DPIUtil.autoScaleUp(getDevice(), x), DPIUtil.autoScaleUp(getDevice(), y), selectionStart, selectionEnd, selectionForeground, selectionBackground, flags);
633 }
634
635 void drawInPixels (GC gc, int x, int y, int selectionStart, int selectionEnd, Color selectionForeground, Color selectionBackground, int flags) {
636         computeRuns(gc);
637         if (gc == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
638         if (gc.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
639         if (selectionForeground != null && selectionForeground.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
640         if (selectionBackground != null && selectionBackground.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
641         int length = text.length();
642         if (length == 0 && flags == 0) return;
643         y += getScaledVerticalIndent();
644         long hdc = gc.handle;
645         Rectangle clip = gc.getClippingInPixels();
646         GCData data = gc.data;
647         long gdipGraphics = data.gdipGraphics;
648         int foreground = data.foreground;
649         int linkColor = OS.GetSysColor (OS.COLOR_HOTLIGHT);
650         int alpha = data.alpha;
651         boolean gdip = gdipGraphics != 0;
652         long gdipForeground = 0;
653         long gdipLinkColor = 0;
654         int state = 0;
655         if (gdip) {
656                 gc.checkGC(GC.FOREGROUND);
657                 gdipForeground = gc.getFgBrush();
658         } else {
659                 state = OS.SaveDC(hdc);
660                 if ((data.style & SWT.MIRRORED) != 0) {
661                         OS.SetLayout(hdc, OS.GetLayout(hdc) | OS.LAYOUT_RTL);
662                 }
663         }
664         boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1;
665         long gdipSelBackground = 0, gdipSelForeground = 0, gdipFont = 0, lastHFont = 0;
666         long selBackground = 0;
667         int selForeground = 0;
668         if (hasSelection || ((flags & SWT.LAST_LINE_SELECTION) != 0 && (flags & (SWT.FULL_SELECTION | SWT.DELIMITER_SELECTION)) != 0)) {
669                 int fgSel = selectionForeground != null ? selectionForeground.handle : OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT);
670                 int bgSel = selectionBackground != null ? selectionBackground.handle : OS.GetSysColor (OS.COLOR_HIGHLIGHT);
671                 if (gdip) {
672                         gdipSelBackground = createGdipBrush(bgSel, alpha);
673                         gdipSelForeground = createGdipBrush(fgSel, alpha);
674                 } else {
675                         selBackground = OS.CreateSolidBrush(bgSel);
676                         selForeground = fgSel;
677                 }
678                 if (hasSelection) {
679                         selectionStart = translateOffset(Math.min(Math.max(0, selectionStart), length - 1));
680                         selectionEnd = translateOffset(Math.min(Math.max(0, selectionEnd), length - 1));
681                 }
682         }
683         RECT rect = new RECT();
684         OS.SetBkMode(hdc, OS.TRANSPARENT);
685         for (int line=0; line<runs.length; line++) {
686                 int drawX = x + getLineIndent(line);
687                 int drawY = y + DPIUtil.autoScaleUp(getDevice(), lineY[line]);
688                 StyleItem[] lineRuns = runs[line];
689                 int lineHeight = DPIUtil.autoScaleUp(getDevice(), lineY[line+1] - lineY[line] - lineSpacingInPoints);
690
691                 //Draw last line selection
692                 if ((flags & (SWT.FULL_SELECTION | SWT.DELIMITER_SELECTION)) != 0 && (hasSelection || (flags & SWT.LAST_LINE_SELECTION) != 0)) {
693                         boolean extents = false;
694                         if (line == runs.length - 1 && (flags & SWT.LAST_LINE_SELECTION) != 0) {
695                                 extents = true;
696                         } else {
697                                 StyleItem run = lineRuns[lineRuns.length - 1];
698                                 if (run.lineBreak && !run.softBreak) {
699                                         if (selectionStart <= run.start && run.start <= selectionEnd) extents = true;
700                                 } else {
701                                         int endOffset = run.start + run.length - 1;
702                                         if (selectionStart <= endOffset && endOffset < selectionEnd && (flags & SWT.FULL_SELECTION) != 0) {
703                                                 extents = true;
704                                         }
705                                 }
706                         }
707                         if (extents) {
708                                 int width;
709                                 if ((flags & SWT.FULL_SELECTION) != 0) {
710                                         width = 0x6FFFFFF;
711                                 } else {
712                                         width = lineHeight / 3;
713                                 }
714                                 if (gdip) {
715                                         Gdip.Graphics_FillRectangle(gdipGraphics, gdipSelBackground, drawX + lineWidth[line], drawY, width, lineHeight);
716                                 } else {
717                                         OS.SelectObject(hdc, selBackground);
718                                         OS.PatBlt(hdc, drawX + lineWidth[line], drawY, width, lineHeight, OS.PATCOPY);
719                                 }
720                         }
721                 }
722                 if (drawX > clip.x + clip.width) continue;
723                 if (drawX + lineWidth[line] < clip.x) continue;
724
725                 //Draw the background of the runs in the line
726                 int alignmentX = drawX;
727                 for (int i = 0; i < lineRuns.length; i++) {
728                         StyleItem run = lineRuns[i];
729                         if (run.length == 0) continue;
730                         if (drawX > clip.x + clip.width) break;
731                         if (drawX + run.width >= clip.x) {
732                                 if (!run.lineBreak || run.softBreak) {
733                                         OS.SetRect(rect, drawX, drawY, drawX + run.width, drawY + lineHeight);
734                                         if (gdip) {
735                                                 drawRunBackgroundGDIP(run, gdipGraphics, rect, selectionStart, selectionEnd, alpha, gdipSelBackground, hasSelection);
736                                         } else {
737                                                 drawRunBackground(run, hdc, rect, selectionStart, selectionEnd, selBackground, hasSelection);
738                                         }
739                                 }
740                         }
741                         drawX += run.width;
742                 }
743
744                 //Draw the text, underline, strikeout, and border of the runs in the line
745                 int baselineInPixels = Math.max(0, this.ascentInPixels);
746                 int lineUnderlinePos = 0;
747                 for (int i = 0; i < lineRuns.length; i++) {
748                         baselineInPixels = Math.max(baselineInPixels, DPIUtil.autoScaleUp(getDevice(), lineRuns[i].ascentInPoints));
749                         lineUnderlinePos = Math.min(lineUnderlinePos, lineRuns[i].underlinePos);
750                 }
751                 RECT borderClip = null, underlineClip = null, strikeoutClip = null, pRect = null;
752                 drawX = alignmentX;
753                 for (int i = 0; i < lineRuns.length; i++) {
754                         StyleItem run = lineRuns[i];
755                         TextStyle style = run.style;
756                         boolean hasAdorners = style != null && (style.underline || style.strikeout || style.borderStyle != SWT.NONE);
757                         if (run.length == 0) continue;
758                         if (drawX > clip.x + clip.width) break;
759                         if (drawX + run.width >= clip.x) {
760                                 boolean skipTab = run.tab && !hasAdorners;
761                                 if (!skipTab && (!run.lineBreak || run.softBreak) && !(style != null && style.metrics != null)) {
762                                         OS.SetRect(rect, drawX, drawY, drawX + run.width, drawY + lineHeight);
763                                         if (gdip) {
764                                                 long hFont = getItemFont(run);
765                                                 if (hFont != lastHFont) {
766                                                         lastHFont = hFont;
767                                                         if (gdipFont != 0) Gdip.Font_delete(gdipFont);
768                                                         long oldFont = OS.SelectObject(hdc, hFont);
769                                                         gdipFont = Gdip.Font_new(hdc, hFont);
770                                                         OS.SelectObject(hdc, oldFont);
771                                                         if (gdipFont == 0) SWT.error(SWT.ERROR_NO_HANDLES);
772                                                         if (!Gdip.Font_IsAvailable(gdipFont)) {
773                                                                 Gdip.Font_delete(gdipFont);
774                                                                 gdipFont = 0;
775                                                         }
776                                                 }
777                                                 long gdipFg = gdipForeground;
778                                                 if (style != null && style.underline && style.underlineStyle == SWT.UNDERLINE_LINK) {
779                                                         if (gdipLinkColor == 0) gdipLinkColor = createGdipBrush(linkColor, alpha);
780                                                         gdipFg = gdipLinkColor;
781                                                 }
782                                                 if (gdipFont != 0 && !run.analysis.fNoGlyphIndex) {
783                                                         pRect = drawRunTextGDIP(gdipGraphics, run, rect, gdipFont, baselineInPixels, gdipFg, gdipSelForeground, selectionStart, selectionEnd, alpha);
784                                                 } else {
785                                                         int fg = style != null && style.underline && style.underlineStyle == SWT.UNDERLINE_LINK ? linkColor : foreground;
786                                                         pRect = drawRunTextGDIPRaster(gdipGraphics, run, rect, baselineInPixels, fg, selForeground, selectionStart, selectionEnd);
787                                                 }
788                                                 underlineClip = drawUnderlineGDIP(gdipGraphics, x, drawY + baselineInPixels, lineUnderlinePos, drawY + lineHeight, lineRuns, i, gdipFg, gdipSelForeground, underlineClip, pRect, selectionStart, selectionEnd, alpha, clip);
789                                                 strikeoutClip = drawStrikeoutGDIP(gdipGraphics, x, drawY + baselineInPixels, lineRuns, i, gdipFg, gdipSelForeground, strikeoutClip, pRect, selectionStart, selectionEnd, alpha, clip);
790                                                 borderClip = drawBorderGDIP(gdipGraphics, x, drawY, lineHeight, lineRuns, i, gdipFg, gdipSelForeground, borderClip, pRect, selectionStart, selectionEnd, alpha, clip);
791                                         }  else {
792                                                 int fg = style != null && style.underline && style.underlineStyle == SWT.UNDERLINE_LINK ? linkColor : foreground;
793                                                 pRect = drawRunText(hdc, run, rect, baselineInPixels, fg, selForeground, selectionStart, selectionEnd);
794                                                 underlineClip = drawUnderline(hdc, x, drawY + baselineInPixels, lineUnderlinePos, drawY + lineHeight, lineRuns, i, fg, selForeground, underlineClip, pRect, selectionStart, selectionEnd, clip);
795                                                 strikeoutClip = drawStrikeout(hdc, x, drawY + baselineInPixels, lineRuns, i, fg, selForeground, strikeoutClip, pRect, selectionStart, selectionEnd, clip);
796                                                 borderClip = drawBorder(hdc, x, drawY, lineHeight, lineRuns, i, fg, selForeground, borderClip, pRect,  selectionStart, selectionEnd, clip);
797                                         }
798                                 }
799                         }
800                         drawX += run.width;
801                 }
802         }
803         if (gdipSelBackground != 0) Gdip.SolidBrush_delete(gdipSelBackground);
804         if (gdipSelForeground != 0) Gdip.SolidBrush_delete(gdipSelForeground);
805         if (gdipLinkColor != 0) Gdip.SolidBrush_delete(gdipLinkColor);
806         if (gdipFont != 0) Gdip.Font_delete(gdipFont);
807         if (state != 0) OS.RestoreDC(hdc, state);
808         if (selBackground != 0) OS.DeleteObject (selBackground);
809 }
810
811 RECT drawBorder(long hdc, int x, int y, int lineHeight, StyleItem[] line, int index, int color, int selectionColor, RECT clipRect, RECT pRect, int selectionStart, int selectionEnd, Rectangle drawClip) {
812         StyleItem run = line[index];
813         TextStyle style = run.style;
814         if (style == null) return null;
815         if (style.borderStyle == SWT.NONE) return null;
816         clipRect = addClipRect(run, clipRect, pRect, selectionStart, selectionEnd);
817         boolean lastRunVisible = drawClip != null && (x + run.x + run.width) > (drawClip.x + drawClip.width);
818         if (index + 1 >= line.length || lastRunVisible || !style.isAdherentBorder(line[index + 1].style)) {
819                 int left = run.x;
820                 int start = run.start;
821                 int end = run.start + run.length - 1;
822                 for (int i = index; i > 0 && style.isAdherentBorder(line[i - 1].style); i--) {
823                         left = line[i - 1].x;
824                         start = Math.min(start, line[i - 1].start);
825                         end = Math.max(end, line[i - 1].start + line[i - 1].length - 1);
826                 }
827                 boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1;
828                 boolean fullSelection = hasSelection && selectionStart <= start && end <= selectionEnd;
829                 if (style.borderColor != null) {
830                         color = style.borderColor.handle;
831                         clipRect = null;
832                 } else {
833                         if (fullSelection) {
834                                 color = selectionColor;
835                                 clipRect = null;
836                         } else {
837                                 if (style.foreground != null) {
838                                         color = style.foreground.handle;
839                                 }
840                         }
841                 }
842                 int lineWidth = 1;
843                 int pattern = 1;
844                 int lineStyle = OS.PS_SOLID;
845                 switch (style.borderStyle) {
846                         case SWT.BORDER_SOLID: break;
847                         case SWT.BORDER_DASH: {
848                                 lineStyle = OS.PS_DASH;
849                                 pattern = 4;
850                                 break;
851                         }
852                         case SWT.BORDER_DOT: {
853                                 lineStyle = OS.PS_DOT;
854                                 pattern = 2;
855                                 break;
856                         }
857                 }
858                 long oldBrush = OS.SelectObject(hdc, OS.GetStockObject(OS.NULL_BRUSH));
859                 LOGBRUSH logBrush = new LOGBRUSH();
860                 logBrush.lbStyle = OS.BS_SOLID;
861                 logBrush.lbColor = (int)color;
862                 long newPen = OS.ExtCreatePen(lineStyle | OS.PS_GEOMETRIC, lineWidth, logBrush, 0, null);
863                 long oldPen = OS.SelectObject(hdc, newPen);
864                 RECT drawRect = new RECT();
865                 OS.SetRect(drawRect, x + left, y, x + run.x + run.width, y + lineHeight);
866                 if (drawClip != null) {
867                         if (drawRect.left < drawClip.x) {
868                                 int remainder = drawRect.left % pattern;
869                                 drawRect.left = drawClip.x / pattern * pattern + remainder - pattern;
870                         }
871                         if (drawRect.right > drawClip.x + drawClip.width) {
872                                 int remainder = drawRect.right % pattern;
873                                 drawRect.right = (drawClip.x + drawClip.width) / pattern * pattern + remainder + pattern;
874                         }
875                 }
876                 OS.Rectangle(hdc, drawRect.left,drawRect.top, drawRect.right, drawRect.bottom);
877                 OS.SelectObject(hdc, oldPen);
878                 OS.DeleteObject(newPen);
879                 if (clipRect != null) {
880                         int state = OS.SaveDC(hdc);
881                         if (clipRect.left == -1) clipRect.left = 0;
882                         if (clipRect.right == -1) clipRect.right = 0x7ffff;
883                         OS.IntersectClipRect(hdc, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
884                         logBrush.lbColor = (int)selectionColor;
885                         long selPen = OS.ExtCreatePen (lineStyle | OS.PS_GEOMETRIC, lineWidth, logBrush, 0, null);
886                         oldPen = OS.SelectObject(hdc, selPen);
887                         OS.Rectangle(hdc, drawRect.left, drawRect.top, drawRect.right, drawRect.bottom);
888                         OS.RestoreDC(hdc, state);
889                         OS.SelectObject(hdc, oldPen);
890                         OS.DeleteObject(selPen);
891                 }
892                 OS.SelectObject(hdc, oldBrush);
893                 return null;
894         }
895         return clipRect;
896 }
897
898 RECT drawBorderGDIP(long graphics, int x, int y, int lineHeight, StyleItem[] line, int index, long color, long selectionColor, RECT clipRect, RECT pRect,  int selectionStart, int selectionEnd, int alpha, Rectangle drawClip) {
899         StyleItem run = line[index];
900         TextStyle style = run.style;
901         if (style == null) return null;
902         if (style.borderStyle == SWT.NONE) return null;
903         clipRect = addClipRect(run, clipRect, pRect, selectionStart, selectionEnd);
904         boolean lastRunVisible = drawClip != null && (x + run.x + run.width) > (drawClip.x + drawClip.width);
905         if (index + 1 >= line.length || lastRunVisible || !style.isAdherentBorder(line[index + 1].style)) {
906                 int left = run.x;
907                 int start = run.start;
908                 int end = run.start + run.length - 1;
909                 for (int i = index; i > 0 && style.isAdherentBorder(line[i - 1].style); i--) {
910                         left = line[i - 1].x;
911                         start = Math.min(start, line[i - 1].start);
912                         end = Math.max(end, line[i - 1].start + line[i - 1].length - 1);
913                 }
914                 boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1;
915                 boolean fullSelection = hasSelection && selectionStart <= start && end <= selectionEnd;
916                 long brush = color;
917                 if (style.borderColor != null) {
918                         brush = createGdipBrush(style.borderColor, alpha);
919                         clipRect = null;
920                 } else {
921                         if (fullSelection) {
922                                 brush = selectionColor;
923                                 clipRect = null;
924                         } else {
925                                 if (style.foreground != null) {
926                                         brush = createGdipBrush(style.foreground, alpha);
927                                 }
928                         }
929                 }
930                 int lineWidth = 1;
931                 int lineStyle = Gdip.DashStyleSolid;
932                 switch (style.borderStyle) {
933                         case SWT.BORDER_SOLID: break;
934                         case SWT.BORDER_DASH: lineStyle = Gdip.DashStyleDash; break;
935                         case SWT.BORDER_DOT: lineStyle = Gdip.DashStyleDot; break;
936                 }
937                 long pen = Gdip.Pen_new(brush, lineWidth);
938                 Gdip.Pen_SetDashStyle(pen, lineStyle);
939                 Gdip.Graphics_SetPixelOffsetMode(graphics, Gdip.PixelOffsetModeNone);
940                 int smoothingMode = Gdip.Graphics_GetSmoothingMode(graphics);
941                 Gdip.Graphics_SetSmoothingMode(graphics, Gdip.SmoothingModeNone);
942                 if (clipRect != null) {
943                         int gstate = Gdip.Graphics_Save(graphics);
944                         if (clipRect.left == -1) clipRect.left = 0;
945                         if (clipRect.right == -1) clipRect.right = 0x7ffff;
946                         Rect gdipRect = new Rect();
947                         gdipRect.X = clipRect.left;
948                         gdipRect.Y = clipRect.top;
949                         gdipRect.Width = clipRect.right - clipRect.left;
950                         gdipRect.Height = clipRect.bottom - clipRect.top;
951                         Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeExclude);
952                         Gdip.Graphics_DrawRectangle(graphics, pen, x + left, y, run.x + run.width - left - 1, lineHeight - 1);
953                         Gdip.Graphics_Restore(graphics, gstate);
954                         gstate = Gdip.Graphics_Save(graphics);
955                         Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeIntersect);
956                         long selPen = Gdip.Pen_new(selectionColor, lineWidth);
957                         Gdip.Pen_SetDashStyle(selPen, lineStyle);
958                         Gdip.Graphics_DrawRectangle(graphics, selPen, x + left, y, run.x + run.width - left - 1, lineHeight - 1);
959                         Gdip.Pen_delete(selPen);
960                         Gdip.Graphics_Restore(graphics, gstate);
961                 } else {
962                         Gdip.Graphics_DrawRectangle(graphics, pen, x + left, y, run.x + run.width - left - 1, lineHeight - 1);
963                 }
964                 Gdip.Graphics_SetPixelOffsetMode(graphics, Gdip.PixelOffsetModeHalf);
965                 Gdip.Graphics_SetSmoothingMode(graphics, smoothingMode);
966                 Gdip.Pen_delete(pen);
967                 if (brush != selectionColor && brush != color) Gdip.SolidBrush_delete(brush);
968                 return null;
969         }
970         return clipRect;
971 }
972
973 void drawRunBackground(StyleItem run, long hdc, RECT rect, int selectionStart, int selectionEnd, long selBrush, boolean hasSelection) {
974         int end = run.start + run.length - 1;
975         boolean fullSelection = hasSelection && selectionStart <= run.start && selectionEnd >= end;
976         if (fullSelection) {
977                 OS.SelectObject(hdc, selBrush);
978                 OS.PatBlt(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY);
979         } else {
980                 if (run.style != null && run.style.background != null) {
981                         int bg = run.style.background.handle;
982                         long hBrush = OS.CreateSolidBrush (bg);
983                         long oldBrush = OS.SelectObject(hdc, hBrush);
984                         OS.PatBlt(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY);
985                         OS.SelectObject(hdc, oldBrush);
986                         OS.DeleteObject(hBrush);
987                 }
988                 boolean partialSelection = hasSelection && !(selectionStart > end || run.start > selectionEnd);
989                 if (partialSelection) {
990                         getPartialSelection(run, selectionStart, selectionEnd, rect);
991                         OS.SelectObject(hdc, selBrush);
992                         OS.PatBlt(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY);
993                 }
994         }
995 }
996
997 void drawRunBackgroundGDIP(StyleItem run, long graphics, RECT rect, int selectionStart, int selectionEnd, int alpha, long selBrush, boolean hasSelection) {
998         int end = run.start + run.length - 1;
999         boolean fullSelection = hasSelection && selectionStart <= run.start && selectionEnd >= end;
1000         if (fullSelection) {
1001                 Gdip.Graphics_FillRectangle(graphics, selBrush, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
1002         } else {
1003                 if (run.style != null && run.style.background != null) {
1004                         long brush = createGdipBrush(run.style.background, alpha);
1005                         Gdip.Graphics_FillRectangle(graphics, brush, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
1006                         Gdip.SolidBrush_delete(brush);
1007                 }
1008                 boolean partialSelection = hasSelection && !(selectionStart > end || run.start > selectionEnd);
1009                 if (partialSelection) {
1010                         getPartialSelection(run, selectionStart, selectionEnd, rect);
1011                         if (rect.left > rect.right) {
1012                                 int tmp = rect.left;
1013                                 rect.left = rect.right;
1014                                 rect.right = tmp;
1015                         }
1016                         Gdip.Graphics_FillRectangle(graphics, selBrush, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
1017                 }
1018         }
1019 }
1020
1021 RECT drawRunText(long hdc, StyleItem run, RECT rect, int baselineInPixels, int color, int selectionColor, int selectionStart, int selectionEnd) {
1022         int end = run.start + run.length - 1;
1023         boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1;
1024         boolean fullSelection = hasSelection && selectionStart <= run.start && selectionEnd >= end;
1025         boolean partialSelection = hasSelection && !fullSelection && !(selectionStart > end || run.start > selectionEnd);
1026         int offset = (orientation & SWT.RIGHT_TO_LEFT) != 0 ? -1 : 0;
1027         int x = rect.left + offset;
1028         int y = rect.top + (baselineInPixels - DPIUtil.autoScaleUp(getDevice(), run.ascentInPoints));
1029         long hFont = getItemFont(run);
1030         OS.SelectObject(hdc, hFont);
1031         if (fullSelection) {
1032                 color = selectionColor;
1033         } else {
1034                 if (run.style != null && run.style.foreground != null) {
1035                         color = run.style.foreground.handle;
1036                 }
1037         }
1038         OS.SetTextColor(hdc, color);
1039         OS.ScriptTextOut(hdc, run.psc, x, y, 0, null, run.analysis , 0, 0, run.glyphs, run.glyphCount, run.advances, run.justify, run.goffsets);
1040         if (partialSelection) {
1041                 getPartialSelection(run, selectionStart, selectionEnd, rect);
1042                 OS.SetTextColor(hdc, selectionColor);
1043                 OS.ScriptTextOut(hdc, run.psc, x, y, OS.ETO_CLIPPED, rect, run.analysis , 0, 0, run.glyphs, run.glyphCount, run.advances, run.justify, run.goffsets);
1044         }
1045         return fullSelection || partialSelection ? rect : null;
1046 }
1047
1048 RECT drawRunTextGDIP(long graphics, StyleItem run, RECT rect, long gdipFont, int baselineInPixels, long color, long selectionColor, int selectionStart, int selectionEnd, int alpha) {
1049         int end = run.start + run.length - 1;
1050         boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1;
1051         boolean fullSelection = hasSelection && selectionStart <= run.start && selectionEnd >= end;
1052         boolean partialSelection = hasSelection && !fullSelection && !(selectionStart > end || run.start > selectionEnd);
1053         int drawY = rect.top + baselineInPixels;
1054         if (run.style != null && run.style.rise != 0) drawY -= DPIUtil.autoScaleUp(getDevice(), run.style.rise);
1055         int drawX = rect.left;
1056         long brush = color;
1057         if (fullSelection) {
1058                 brush = selectionColor;
1059         } else {
1060                 if (run.style != null && run.style.foreground != null) {
1061                         brush = createGdipBrush(run.style.foreground, alpha);
1062                 }
1063         }
1064         int gstate = 0;
1065         Rect gdipRect = null;
1066         if (partialSelection) {
1067                 gdipRect = new Rect();
1068                 getPartialSelection(run, selectionStart, selectionEnd, rect);
1069                 gdipRect.X = rect.left;
1070                 gdipRect.Y = rect.top;
1071                 gdipRect.Width = rect.right - rect.left;
1072                 gdipRect.Height = rect.bottom - rect.top;
1073                 gstate = Gdip.Graphics_Save(graphics);
1074                 Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeExclude);
1075         }
1076         int gstateMirrored = 0;
1077         boolean isMirrored = (orientation & SWT.RIGHT_TO_LEFT) != 0;
1078         if (isMirrored) {
1079                 switch (Gdip.Brush_GetType(brush)) {
1080                         case Gdip.BrushTypeLinearGradient:
1081                                 Gdip.LinearGradientBrush_ScaleTransform(brush, -1, 1, Gdip.MatrixOrderPrepend);
1082                                 Gdip.LinearGradientBrush_TranslateTransform(brush, -2 * drawX - run.width, 0, Gdip.MatrixOrderPrepend);
1083                                 break;
1084                         case Gdip.BrushTypeTextureFill:
1085                                 Gdip.TextureBrush_ScaleTransform(brush, -1, 1, Gdip.MatrixOrderPrepend);
1086                                 Gdip.TextureBrush_TranslateTransform(brush, -2 * drawX - run.width, 0, Gdip.MatrixOrderPrepend);
1087                                 break;
1088                 }
1089                 gstateMirrored = Gdip.Graphics_Save(graphics);
1090                 Gdip.Graphics_ScaleTransform(graphics, -1, 1, Gdip.MatrixOrderPrepend);
1091                 Gdip.Graphics_TranslateTransform(graphics, -2 * drawX - run.width, 0, Gdip.MatrixOrderPrepend);
1092         }
1093         int[] advances = new int[run.glyphCount];
1094         float[] points = new float[run.glyphCount * 2];
1095         C.memmove(advances, run.justify != 0 ? run.justify : run.advances, run.glyphCount * 4);
1096         int glyphX = drawX;
1097         for (int h = 0, j = 0; h < advances.length; h++) {
1098                 points[j++] = glyphX;
1099                 points[j++] = drawY;
1100                 glyphX += advances[h];
1101         }
1102         Gdip.Graphics_DrawDriverString(graphics, run.glyphs, run.glyphCount, gdipFont, brush, points, 0, 0);
1103         if (partialSelection) {
1104                 if (isMirrored) {
1105                         Gdip.Graphics_Restore(graphics, gstateMirrored);
1106                 }
1107                 Gdip.Graphics_Restore(graphics, gstate);
1108                 gstate = Gdip.Graphics_Save(graphics);
1109                 Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeIntersect);
1110                 if (isMirrored) {
1111                         gstateMirrored = Gdip.Graphics_Save(graphics);
1112                         Gdip.Graphics_ScaleTransform(graphics, -1, 1, Gdip.MatrixOrderPrepend);
1113                         Gdip.Graphics_TranslateTransform(graphics, -2 * drawX - run.width, 0, Gdip.MatrixOrderPrepend);
1114                 }
1115                 Gdip.Graphics_DrawDriverString(graphics, run.glyphs, run.glyphCount, gdipFont, selectionColor, points, 0, 0);
1116                 Gdip.Graphics_Restore(graphics, gstate);
1117         }
1118         if (isMirrored) {
1119                 switch (Gdip.Brush_GetType(brush)) {
1120                         case Gdip.BrushTypeLinearGradient:
1121                                 Gdip.LinearGradientBrush_ResetTransform(brush);
1122                                 break;
1123                         case Gdip.BrushTypeTextureFill:
1124                                 Gdip.TextureBrush_ResetTransform(brush);
1125                                 break;
1126                 }
1127                 Gdip.Graphics_Restore(graphics, gstateMirrored);
1128         }
1129         if (brush != selectionColor && brush != color) Gdip.SolidBrush_delete(brush);
1130         return fullSelection || partialSelection ? rect : null;
1131 }
1132
1133 RECT drawRunTextGDIPRaster(long graphics, StyleItem run, RECT rect, int baselineInPixels, int color, int selectionColor, int selectionStart, int selectionEnd) {
1134         long clipRgn = 0;
1135         Gdip.Graphics_SetPixelOffsetMode(graphics, Gdip.PixelOffsetModeNone);
1136         long rgn = Gdip.Region_new();
1137         if (rgn == 0) SWT.error(SWT.ERROR_NO_HANDLES);
1138         Gdip.Graphics_GetClip(graphics, rgn);
1139         if (!Gdip.Region_IsInfinite(rgn, graphics)) {
1140                 clipRgn = Gdip.Region_GetHRGN(rgn, graphics);
1141         }
1142         Gdip.Region_delete(rgn);
1143         Gdip.Graphics_SetPixelOffsetMode(graphics, Gdip.PixelOffsetModeHalf);
1144         float[] lpXform = null;
1145         long matrix = Gdip.Matrix_new(1, 0, 0, 1, 0, 0);
1146         if (matrix == 0) SWT.error(SWT.ERROR_NO_HANDLES);
1147         Gdip.Graphics_GetTransform(graphics, matrix);
1148         if (!Gdip.Matrix_IsIdentity(matrix)) {
1149                 lpXform = new float[6];
1150                 Gdip.Matrix_GetElements(matrix, lpXform);
1151         }
1152         Gdip.Matrix_delete(matrix);
1153         long hdc = Gdip.Graphics_GetHDC(graphics);
1154         int state = OS.SaveDC(hdc);
1155         if (lpXform != null) {
1156                 OS.SetGraphicsMode(hdc, OS.GM_ADVANCED);
1157                 OS.SetWorldTransform(hdc, lpXform);
1158         }
1159         if (clipRgn != 0) {
1160                 OS.SelectClipRgn(hdc, clipRgn);
1161                 OS.DeleteObject(clipRgn);
1162         }
1163         if ((orientation & SWT.RIGHT_TO_LEFT) != 0) {
1164                 OS.SetLayout(hdc, OS.GetLayout(hdc) | OS.LAYOUT_RTL);
1165         }
1166         OS.SetBkMode(hdc, OS.TRANSPARENT);
1167         RECT pRect = drawRunText(hdc, run, rect, baselineInPixels, color, selectionColor, selectionStart, selectionEnd);
1168         OS.RestoreDC(hdc, state);
1169         Gdip.Graphics_ReleaseHDC(graphics, hdc);
1170         return pRect;
1171 }
1172
1173 RECT drawStrikeout(long hdc, int x, int baselineInPixels, StyleItem[] line, int index, int color, int selectionColor, RECT clipRect, RECT pRect, int selectionStart, int selectionEnd, Rectangle drawClip) {
1174         StyleItem run = line[index];
1175         TextStyle style = run.style;
1176         if (style == null) return null;
1177         if (!style.strikeout) return null;
1178         clipRect = addClipRect(run, clipRect, pRect, selectionStart, selectionEnd);
1179         boolean lastRunVisible = drawClip != null && (x + run.x + run.width) > (drawClip.x + drawClip.width);
1180         if (index + 1 >= line.length || lastRunVisible || !style.isAdherentStrikeout(line[index + 1].style)) {
1181                 int left = run.x;
1182                 int start = run.start;
1183                 int end = run.start + run.length - 1;
1184                 for (int i = index; i > 0 && style.isAdherentStrikeout(line[i - 1].style); i--) {
1185                         left = line[i - 1].x;
1186                         start = Math.min(start, line[i - 1].start);
1187                         end = Math.max(end, line[i - 1].start + line[i - 1].length - 1);
1188                 }
1189                 boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1;
1190                 boolean fullSelection = hasSelection && selectionStart <= start && end <= selectionEnd;
1191                 if (style.strikeoutColor != null) {
1192                         color = style.strikeoutColor.handle;
1193                         clipRect = null;
1194                 } else {
1195                         if (fullSelection) {
1196                                 color = selectionColor;
1197                                 clipRect = null;
1198                         } else {
1199                                 if (style.foreground != null) {
1200                                         color = style.foreground.handle;
1201                                 }
1202                         }
1203                 }
1204                 RECT rect = new RECT();
1205                 int riseInPixels = DPIUtil.autoScaleUp(getDevice(), style.rise);
1206                 OS.SetRect(rect, x + left, baselineInPixels - run.strikeoutPos - riseInPixels, x + run.x + run.width, baselineInPixels - run.strikeoutPos + run.strikeoutThickness - riseInPixels);
1207                 long brush = OS.CreateSolidBrush(color);
1208                 OS.FillRect(hdc, rect, brush);
1209                 OS.DeleteObject(brush);
1210                 if (clipRect != null) {
1211                         long selBrush = OS.CreateSolidBrush(selectionColor);
1212                         if (clipRect.left == -1) clipRect.left = 0;
1213                         if (clipRect.right == -1) clipRect.right = 0x7ffff;
1214                         OS.SetRect(clipRect, Math.max(rect.left, clipRect.left), rect.top, Math.min(rect.right, clipRect.right), rect.bottom);
1215                         OS.FillRect(hdc, clipRect, selBrush);
1216                         OS.DeleteObject(selBrush);
1217                 }
1218                 return null;
1219         }
1220         return clipRect;
1221 }
1222
1223 RECT drawStrikeoutGDIP(long graphics, int x, int baselineInPixels, StyleItem[] line, int index, long color, long selectionColor, RECT clipRect, RECT pRect, int selectionStart, int selectionEnd, int alpha, Rectangle drawClip) {
1224         StyleItem run = line[index];
1225         TextStyle style = run.style;
1226         if (style == null) return null;
1227         if (!style.strikeout) return null;
1228         clipRect = addClipRect(run, clipRect, pRect, selectionStart, selectionEnd);
1229         boolean lastRunVisible = drawClip != null && (x + run.x + run.width) > (drawClip.x + drawClip.width);
1230         if (index + 1 >= line.length || lastRunVisible || !style.isAdherentStrikeout(line[index + 1].style)) {
1231                 int left = run.x;
1232                 int start = run.start;
1233                 int end = run.start + run.length - 1;
1234                 for (int i = index; i > 0 && style.isAdherentStrikeout(line[i - 1].style); i--) {
1235                         left = line[i - 1].x;
1236                         start = Math.min(start, line[i - 1].start);
1237                         end = Math.max(end, line[i - 1].start + line[i - 1].length - 1);
1238                 }
1239                 boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1;
1240                 boolean fullSelection = hasSelection && selectionStart <= start && end <= selectionEnd;
1241                 long brush = color;
1242                 if (style.strikeoutColor != null) {
1243                         brush = createGdipBrush(style.strikeoutColor, alpha);
1244                         clipRect = null;
1245                 } else {
1246                         if (fullSelection) {
1247                                 brush = selectionColor;
1248                                 clipRect = null;
1249                         } else {
1250                                 if (style.foreground != null) {
1251                                         brush = createGdipBrush(style.foreground, alpha);
1252                                 }
1253                         }
1254                 }
1255                 int riseInPixels = DPIUtil.autoScaleUp(getDevice(), style.rise);
1256                 if (clipRect != null) {
1257                         int gstate = Gdip.Graphics_Save(graphics);
1258                         if (clipRect.left == -1) clipRect.left = 0;
1259                         if (clipRect.right == -1) clipRect.right = 0x7ffff;
1260                         Rect gdipRect = new Rect();
1261                         gdipRect.X = clipRect.left;
1262                         gdipRect.Y = clipRect.top;
1263                         gdipRect.Width = clipRect.right - clipRect.left;
1264                         gdipRect.Height = clipRect.bottom - clipRect.top;
1265                         Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeExclude);
1266                         Gdip.Graphics_FillRectangle(graphics, brush, x + left, baselineInPixels - run.strikeoutPos - riseInPixels, run.x + run.width - left, run.strikeoutThickness);
1267                         Gdip.Graphics_Restore(graphics, gstate);
1268                         gstate = Gdip.Graphics_Save(graphics);
1269                         Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeIntersect);
1270                         Gdip.Graphics_FillRectangle(graphics, selectionColor, x + left, baselineInPixels - run.strikeoutPos - riseInPixels, run.x + run.width - left, run.strikeoutThickness);
1271                         Gdip.Graphics_Restore(graphics, gstate);
1272                 } else {
1273                         Gdip.Graphics_FillRectangle(graphics, brush, x + left, baselineInPixels - run.strikeoutPos - riseInPixels, run.x + run.width - left, run.strikeoutThickness);
1274                 }
1275                 if (brush != selectionColor && brush != color) Gdip.SolidBrush_delete(brush);
1276                 return null;
1277         }
1278         return clipRect;
1279 }
1280
1281 RECT drawUnderline(long hdc, int x, int baselineInPixels, int lineUnderlinePos, int lineBottom, StyleItem[] line, int index, int color, int selectionColor, RECT clipRect, RECT pRect, int selectionStart, int selectionEnd, Rectangle drawClip) {
1282         StyleItem run = line[index];
1283         TextStyle style = run.style;
1284         if (style == null) return null;
1285         if (!style.underline) return null;
1286         clipRect = addClipRect(run, clipRect, pRect, selectionStart, selectionEnd);
1287         boolean lastRunVisible = drawClip != null && (x + run.x + run.width) > (drawClip.x + drawClip.width);
1288         if (index + 1 >= line.length || lastRunVisible || !style.isAdherentUnderline(line[index + 1].style)) {
1289                 int left = run.x;
1290                 int start = run.start;
1291                 int end = run.start + run.length - 1;
1292                 for (int i = index; i > 0 && style.isAdherentUnderline(line[i - 1].style); i--) {
1293                         left = line[i - 1].x;
1294                         start = Math.min(start, line[i - 1].start);
1295                         end = Math.max(end, line[i - 1].start + line[i - 1].length - 1);
1296                 }
1297                 boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1;
1298                 boolean fullSelection = hasSelection && selectionStart <= start && end <= selectionEnd;
1299                 if (style.underlineColor != null) {
1300                         color = style.underlineColor.handle;
1301                         clipRect = null;
1302                 } else {
1303                         if (fullSelection) {
1304                                 color = selectionColor;
1305                                 clipRect = null;
1306                         } else {
1307                                 if (style.foreground != null) {
1308                                         color = style.foreground.handle;
1309                                 }
1310                         }
1311                 }
1312                 RECT rect = new RECT();
1313                 int riseInPixels = DPIUtil.autoScaleUp(getDevice(), style.rise);
1314                 OS.SetRect(rect, x + left, baselineInPixels - lineUnderlinePos - riseInPixels, x + run.x + run.width, baselineInPixels - lineUnderlinePos + run.underlineThickness - riseInPixels);
1315                 if (clipRect != null) {
1316                         if (clipRect.left == -1) clipRect.left = 0;
1317                         if (clipRect.right == -1) clipRect.right = 0x7ffff;
1318                         OS.SetRect(clipRect, Math.max(rect.left, clipRect.left), rect.top, Math.min(rect.right, clipRect.right), rect.bottom);
1319                 }
1320                 switch (style.underlineStyle) {
1321                         case SWT.UNDERLINE_SQUIGGLE:
1322                         case SWT.UNDERLINE_ERROR: {
1323                                 int squigglyThickness = 1;
1324                                 int squigglyHeight = 2 * squigglyThickness;
1325                                 int squigglyY = Math.min(rect.top - squigglyHeight / 2, lineBottom - squigglyHeight - 1);
1326                                 int[] points = computePolyline(rect.left, squigglyY, rect.right, squigglyY + squigglyHeight);
1327                                 long pen = OS.CreatePen(OS.PS_SOLID, squigglyThickness, color);
1328                                 long oldPen = OS.SelectObject(hdc, pen);
1329                                 int state = OS.SaveDC(hdc);
1330                                 OS.IntersectClipRect(hdc, rect.left, squigglyY, rect.right + 1, squigglyY + squigglyHeight + 1);
1331                                 OS.Polyline(hdc, points, points.length / 2);
1332                                 int length = points.length;
1333                                 if (length >= 2 && squigglyThickness <= 1) {
1334                                         OS.SetPixel (hdc, points[length - 2], points[length - 1], color);
1335                                 }
1336                                 OS.SelectObject(hdc, oldPen);
1337                                 OS.DeleteObject(pen);
1338                                 OS.RestoreDC(hdc, state);
1339                                 if (clipRect != null) {
1340                                         pen = OS.CreatePen(OS.PS_SOLID, squigglyThickness, selectionColor);
1341                                         oldPen = OS.SelectObject(hdc, pen);
1342                                         state = OS.SaveDC(hdc);
1343                                         OS.IntersectClipRect(hdc, clipRect.left, squigglyY, clipRect.right + 1, squigglyY + squigglyHeight + 1);
1344                                         OS.Polyline(hdc, points, points.length / 2);
1345                                         if (length >= 2 && squigglyThickness <= 1) {
1346                                                 OS.SetPixel (hdc, points[length - 2], points[length - 1], selectionColor);
1347                                         }
1348                                         OS.SelectObject(hdc, oldPen);
1349                                         OS.DeleteObject(pen);
1350                                         OS.RestoreDC(hdc, state);
1351                                 }
1352                                 break;
1353                         }
1354                         case SWT.UNDERLINE_SINGLE:
1355                         case SWT.UNDERLINE_DOUBLE:
1356                         case SWT.UNDERLINE_LINK:
1357                         case UNDERLINE_IME_THICK:
1358                                 if (style.underlineStyle == UNDERLINE_IME_THICK) {
1359                                         rect.top -= run.underlineThickness;
1360                                         if (clipRect != null) clipRect.top -= run.underlineThickness;
1361                                 }
1362                                 int bottom = style.underlineStyle == SWT.UNDERLINE_DOUBLE ? rect.bottom + run.underlineThickness * 2 : rect.bottom;
1363                                 if (bottom > lineBottom) {
1364                                         OS.OffsetRect(rect, 0, lineBottom - bottom);
1365                                         if (clipRect != null) OS.OffsetRect(clipRect, 0, lineBottom - bottom);
1366                                 }
1367                                 long brush = OS.CreateSolidBrush(color);
1368                                 OS.FillRect(hdc, rect, brush);
1369                                 if (style.underlineStyle == SWT.UNDERLINE_DOUBLE) {
1370                                         OS.SetRect(rect, rect.left, rect.top + run.underlineThickness * 2, rect.right, rect.bottom + run.underlineThickness * 2);
1371                                         OS.FillRect(hdc, rect, brush);
1372                                 }
1373                                 OS.DeleteObject(brush);
1374                                 if (clipRect != null) {
1375                                         long selBrush = OS.CreateSolidBrush(selectionColor);
1376                                         OS.FillRect(hdc, clipRect, selBrush);
1377                                         if (style.underlineStyle == SWT.UNDERLINE_DOUBLE) {
1378                                                 OS.SetRect(clipRect, clipRect.left, rect.top, clipRect.right, rect.bottom);
1379                                                 OS.FillRect(hdc, clipRect, selBrush);
1380                                         }
1381                                         OS.DeleteObject(selBrush);
1382                                 }
1383                                 break;
1384                         case UNDERLINE_IME_DASH:
1385                         case UNDERLINE_IME_DOT: {
1386                                 int penStyle = style.underlineStyle == UNDERLINE_IME_DASH ? OS.PS_DASH : OS.PS_DOT;
1387                                 long pen = OS.CreatePen(penStyle, 1, color);
1388                                 long oldPen = OS.SelectObject(hdc, pen);
1389                                 int descentInPixels = DPIUtil.autoScaleUp(getDevice(), run.descentInPoints);
1390                                 OS.SetRect(rect, rect.left, baselineInPixels + descentInPixels, rect.right, baselineInPixels + descentInPixels + run.underlineThickness);
1391                                 OS.MoveToEx(hdc, rect.left, rect.top, 0);
1392                                 OS.LineTo(hdc, rect.right, rect.top);
1393                                 OS.SelectObject(hdc, oldPen);
1394                                 OS.DeleteObject(pen);
1395                                 if (clipRect != null) {
1396                                         pen = OS.CreatePen(penStyle, 1, selectionColor);
1397                                         oldPen = OS.SelectObject(hdc, pen);
1398                                         OS.SetRect(clipRect, clipRect.left, rect.top, clipRect.right, rect.bottom);
1399                                         OS.MoveToEx(hdc, clipRect.left, clipRect.top, 0);
1400                                         OS.LineTo(hdc, clipRect.right, clipRect.top);
1401                                         OS.SelectObject(hdc, oldPen);
1402                                         OS.DeleteObject(pen);
1403                                 }
1404                                 break;
1405                         }
1406                 }
1407                 return null;
1408         }
1409         return clipRect;
1410 }
1411
1412 RECT drawUnderlineGDIP (long graphics, int x, int baselineInPixels, int lineUnderlinePos, int lineBottom, StyleItem[] line, int index, long color, long selectionColor, RECT clipRect, RECT pRect, int selectionStart, int selectionEnd, int alpha, Rectangle drawClip) {
1413         StyleItem run = line[index];
1414         TextStyle style = run.style;
1415         if (style == null) return null;
1416         if (!style.underline) return null;
1417         clipRect = addClipRect(run, clipRect, pRect, selectionStart, selectionEnd);
1418         boolean lastRunVisible = drawClip != null && (x + run.x + run.width) > (drawClip.x + drawClip.width);
1419         if (index + 1 >= line.length || lastRunVisible || !style.isAdherentUnderline(line[index + 1].style)) {
1420                 int left = run.x;
1421                 int start = run.start;
1422                 int end = run.start + run.length - 1;
1423                 for (int i = index; i > 0 && style.isAdherentUnderline(line[i - 1].style); i--) {
1424                         left = line[i - 1].x;
1425                         start = Math.min(start, line[i - 1].start);
1426                         end = Math.max(end, line[i - 1].start + line[i - 1].length - 1);
1427                 }
1428                 boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1;
1429                 boolean fullSelection = hasSelection && selectionStart <= start && end <= selectionEnd;
1430                 long brush = color;
1431                 if (style.underlineColor != null) {
1432                         brush = createGdipBrush(style.underlineColor, alpha);
1433                         clipRect = null;
1434                 } else {
1435                         if (fullSelection) {
1436                                 brush = selectionColor;
1437                                 clipRect = null;
1438                         } else {
1439                                 if (style.foreground != null) {
1440                                         brush = createGdipBrush(style.foreground, alpha);
1441                                 }
1442                         }
1443                 }
1444                 RECT rect = new RECT();
1445                 int riseInPixels = DPIUtil.autoScaleUp(getDevice(), style.rise);
1446                 OS.SetRect(rect, x + left, baselineInPixels - lineUnderlinePos - riseInPixels, x + run.x + run.width, baselineInPixels - lineUnderlinePos + run.underlineThickness - riseInPixels);
1447                 Rect gdipRect = null;
1448                 if (clipRect != null) {
1449                         if (clipRect.left == -1) clipRect.left = 0;
1450                         if (clipRect.right == -1) clipRect.right = 0x7ffff;
1451                         OS.SetRect(clipRect, Math.max(rect.left, clipRect.left), rect.top, Math.min(rect.right, clipRect.right), rect.bottom);
1452                         gdipRect = new Rect();
1453                         gdipRect.X = clipRect.left;
1454                         gdipRect.Y = clipRect.top;
1455                         gdipRect.Width = clipRect.right - clipRect.left;
1456                         gdipRect.Height = clipRect.bottom - clipRect.top;
1457                 }
1458                 int gstate = 0;
1459                 Gdip.Graphics_SetPixelOffsetMode(graphics, Gdip.PixelOffsetModeNone);
1460                 int smoothingMode = Gdip.Graphics_GetSmoothingMode(graphics);
1461                 Gdip.Graphics_SetSmoothingMode(graphics, Gdip.SmoothingModeNone);
1462                 switch (style.underlineStyle) {
1463                         case SWT.UNDERLINE_SQUIGGLE:
1464                         case SWT.UNDERLINE_ERROR: {
1465                                 int squigglyThickness = 1;
1466                                 int squigglyHeight = 2 * squigglyThickness;
1467                                 int squigglyY = Math.min(rect.top - squigglyHeight / 2, lineBottom - squigglyHeight - 1);
1468                                 int[] points = computePolyline(rect.left, squigglyY, rect.right, squigglyY + squigglyHeight);
1469                                 long pen = Gdip.Pen_new(brush, squigglyThickness);
1470                                 gstate = Gdip.Graphics_Save(graphics);
1471                                 if (gdipRect != null) {
1472                                         Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeExclude);
1473                                 } else {
1474                                         Rect r = new Rect();
1475                                         r.X = rect.left;
1476                                         r.Y = squigglyY;
1477                                         r.Width = rect.right - rect.left;
1478                                         r.Height = squigglyHeight + 1;
1479                                         Gdip.Graphics_SetClip(graphics, r, Gdip.CombineModeIntersect);
1480                                 }
1481                                 Gdip.Graphics_DrawLines(graphics, pen, points, points.length / 2);
1482                                 if (gdipRect != null) {
1483                                         long selPen = Gdip.Pen_new(selectionColor, squigglyThickness);
1484                                         Gdip.Graphics_Restore(graphics, gstate);
1485                                         gstate = Gdip.Graphics_Save(graphics);
1486                                         Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeIntersect);
1487                                         Gdip.Graphics_DrawLines(graphics, selPen, points, points.length / 2);
1488                                         Gdip.Pen_delete(selPen);
1489                                 }
1490                                 Gdip.Graphics_Restore(graphics, gstate);
1491                                 Gdip.Pen_delete(pen);
1492                                 if (gstate != 0) Gdip.Graphics_Restore(graphics, gstate);
1493                                 break;
1494                         }
1495                         case SWT.UNDERLINE_SINGLE:
1496                         case SWT.UNDERLINE_DOUBLE:
1497                         case SWT.UNDERLINE_LINK:
1498                         case UNDERLINE_IME_THICK:
1499                                 if (style.underlineStyle == UNDERLINE_IME_THICK) {
1500                                         rect.top -= run.underlineThickness;
1501                                 }
1502                                 int bottom = style.underlineStyle == SWT.UNDERLINE_DOUBLE ? rect.bottom + run.underlineThickness * 2 : rect.bottom;
1503                                 if (bottom > lineBottom) {
1504                                         OS.OffsetRect(rect, 0, lineBottom - bottom);
1505                                 }
1506                                 if (gdipRect != null) {
1507                                         gdipRect.Y = rect.top;
1508                                         if (style.underlineStyle == UNDERLINE_IME_THICK) {
1509                                                 gdipRect.Height = run.underlineThickness * 2;
1510                                         }
1511                                         if (style.underlineStyle == SWT.UNDERLINE_DOUBLE) {
1512                                                 gdipRect.Height = run.underlineThickness * 3;
1513                                         }
1514                                         gstate = Gdip.Graphics_Save(graphics);
1515                                         Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeExclude);
1516                                 }
1517                                 Gdip.Graphics_FillRectangle(graphics, brush, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
1518                                 if (style.underlineStyle == SWT.UNDERLINE_DOUBLE) {
1519                                         Gdip.Graphics_FillRectangle(graphics, brush, rect.left, rect.top + run.underlineThickness * 2, rect.right - rect.left, rect.bottom - rect.top);
1520                                 }
1521                                 if (gdipRect != null) {
1522                                         Gdip.Graphics_Restore(graphics, gstate);
1523                                         gstate = Gdip.Graphics_Save(graphics);
1524                                         Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeIntersect);
1525                                         Gdip.Graphics_FillRectangle(graphics, selectionColor, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
1526                                         if (style.underlineStyle == SWT.UNDERLINE_DOUBLE) {
1527                                                 Gdip.Graphics_FillRectangle(graphics, selectionColor, rect.left, rect.top + run.underlineThickness * 2, rect.right - rect.left, rect.bottom - rect.top);
1528                                         }
1529                                         Gdip.Graphics_Restore(graphics, gstate);
1530                                 }
1531                                 break;
1532                         case UNDERLINE_IME_DOT:
1533                         case UNDERLINE_IME_DASH: {
1534                                 long pen = Gdip.Pen_new(brush, 1);
1535                                 int dashStyle = style.underlineStyle == UNDERLINE_IME_DOT ? Gdip.DashStyleDot : Gdip.DashStyleDash;
1536                                 Gdip.Pen_SetDashStyle(pen, dashStyle);
1537                                 if (gdipRect != null) {
1538                                         gstate = Gdip.Graphics_Save(graphics);
1539                                         Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeExclude);
1540                                 }
1541                                 int descentInPixels = DPIUtil.autoScaleUp(getDevice(), run.descentInPoints);
1542                                 Gdip.Graphics_DrawLine(graphics, pen, rect.left, baselineInPixels + descentInPixels, run.width - run.length, baselineInPixels + descentInPixels);
1543                                 if (gdipRect != null) {
1544                                         Gdip.Graphics_Restore(graphics, gstate);
1545                                         gstate = Gdip.Graphics_Save(graphics);
1546                                         Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeIntersect);
1547                                         long selPen = Gdip.Pen_new(brush, 1);
1548                                         Gdip.Pen_SetDashStyle(selPen, dashStyle);
1549                                         Gdip.Graphics_DrawLine(graphics, selPen, rect.left, baselineInPixels + descentInPixels, run.width - run.length, baselineInPixels + descentInPixels);
1550                                         Gdip.Graphics_Restore(graphics, gstate);
1551                                         Gdip.Pen_delete(selPen);
1552                                 }
1553                                 Gdip.Pen_delete(pen);
1554                                 break;
1555                         }
1556                 }
1557                 if (brush != selectionColor && brush != color) Gdip.SolidBrush_delete(brush);
1558                 Gdip.Graphics_SetPixelOffsetMode(graphics, Gdip.PixelOffsetModeHalf);
1559                 Gdip.Graphics_SetSmoothingMode(graphics, smoothingMode);
1560                 return null;
1561         }
1562         return clipRect;
1563 }
1564
1565 void freeRuns () {
1566         if (allRuns == null) return;
1567         for (int i=0; i<allRuns.length; i++) {
1568                 StyleItem run = allRuns[i];
1569                 run.free();
1570         }
1571         allRuns = null;
1572         runs = null;
1573         segmentsText = null;
1574 }
1575
1576 /**
1577  * Returns the receiver's horizontal text alignment, which will be one
1578  * of <code>SWT.LEFT</code>, <code>SWT.CENTER</code> or
1579  * <code>SWT.RIGHT</code>.
1580  *
1581  * @return the alignment used to positioned text horizontally
1582  *
1583  * @exception SWTException <ul>
1584  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1585  * </ul>
1586  */
1587 public int getAlignment () {
1588         checkLayout();
1589         return alignment;
1590 }
1591
1592 /**
1593  * Returns the ascent of the receiver.
1594  *
1595  * @return the ascent
1596  *
1597  * @exception SWTException <ul>
1598  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1599  * </ul>
1600  *
1601  * @see #getDescent()
1602  * @see #setDescent(int)
1603  * @see #setAscent(int)
1604  * @see #getLineMetrics(int)
1605  */
1606 public int getAscent () {
1607         checkLayout();
1608         return DPIUtil.autoScaleDown(getDevice(), ascentInPixels);
1609 }
1610
1611 /**
1612  * Returns the bounds of the receiver. The width returned is either the
1613  * width of the longest line or the width set using {@link TextLayout#setWidth(int)}.
1614  * To obtain the text bounds of a line use {@link TextLayout#getLineBounds(int)}.
1615  *
1616  * @return the bounds of the receiver
1617  *
1618  * @exception SWTException <ul>
1619  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1620  * </ul>
1621  *
1622  * @see #setWidth(int)
1623  * @see #getLineBounds(int)
1624  */
1625 public Rectangle getBounds () {
1626         checkLayout();
1627         computeRuns(null);
1628         int width = 0;
1629         if (wrapWidth != -1) {
1630                 width = wrapWidth;
1631         } else {
1632                 for (int line=0; line<runs.length; line++) {
1633                         width = Math.max(width, lineWidth[line] + getLineIndent(line));
1634                 }
1635         }
1636         return new Rectangle (0, 0, DPIUtil.autoScaleDown(getDevice(), width), lineY[lineY.length - 1] + getScaledVerticalIndent());
1637 }
1638
1639 /**
1640  * Returns the bounds for the specified range of characters. The
1641  * bounds is the smallest rectangle that encompasses all characters
1642  * in the range. The start and end offsets are inclusive and will be
1643  * clamped if out of range.
1644  *
1645  * @param start the start offset
1646  * @param end the end offset
1647  * @return the bounds of the character range
1648  *
1649  * @exception SWTException <ul>
1650  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1651  * </ul>
1652  */
1653 public Rectangle getBounds (int start, int end) {
1654         checkLayout();
1655         return DPIUtil.autoScaleDown(getDevice(), getBoundsInPixels(start, end));
1656 }
1657
1658 Rectangle getBoundsInPixels (int start, int end) {
1659         computeRuns(null);
1660         int length = text.length();
1661         if (length == 0) return new Rectangle(0, 0, 0, 0);
1662         if (start > end) return new Rectangle(0, 0, 0, 0);
1663         start = Math.min(Math.max(0, start), length - 1);
1664         end = Math.min(Math.max(0, end), length - 1);
1665         start = translateOffset(start);
1666         end = translateOffset(end);
1667         /* use the high surrogate for the start offset and the low surrogate for the end offset */
1668         length = segmentsText.length();
1669         char ch = segmentsText.charAt(start);
1670         if (0xDC00 <= ch && ch <= 0xDFFF) {
1671                 if (start - 1 >= 0) {
1672                         ch = segmentsText.charAt(start - 1);
1673                         if (0xD800 <= ch && ch <= 0xDBFF) {
1674                                 start--;
1675                         }
1676                 }
1677         }
1678         ch = segmentsText.charAt(end);
1679         if (0xD800 <= ch && ch <= 0xDBFF) {
1680                 if (end + 1 < length) {
1681                         ch = segmentsText.charAt(end + 1);
1682                         if (0xDC00 <= ch && ch <= 0xDFFF) {
1683                                 end++;
1684                         }
1685                 }
1686         }
1687         int left = 0x7fffffff, right = 0;
1688         int top = 0x7fffffff, bottom = 0;
1689         boolean isRTL = (orientation & SWT.RIGHT_TO_LEFT) != 0;
1690         for (int i = 0; i < allRuns.length - 1; i++) {
1691                 StyleItem run = allRuns[i];
1692                 int runEnd = run.start + run.length;
1693                 if (runEnd <= start) continue;
1694                 if (run.start > end) break;
1695                 int runLead = run.x;
1696                 int runTrail = run.x + run.width;
1697                 if (run.start <= start && start < runEnd) {
1698                         int cx = 0;
1699                         if (run.style != null && run.style.metrics != null) {
1700                                 GlyphMetrics metrics = run.style.metrics;
1701                                 cx = metrics.getWidthInPixels() * (start - run.start);
1702                         } else if (!run.tab) {
1703                                 int[] piX = new int[1];
1704                                 long advances = run.justify != 0 ? run.justify : run.advances;
1705                                 OS.ScriptCPtoX(start - run.start, false, run.length, run.glyphCount, run.clusters, run.visAttrs, advances, run.analysis, piX);
1706                                 cx = isRTL ? run.width - piX[0] : piX[0];
1707                         }
1708                         if (run.analysis.fRTL ^ isRTL) {
1709                                 runTrail = run.x + cx;
1710                         } else {
1711                                 runLead = run.x + cx;
1712                         }
1713                 }
1714                 if (run.start <= end && end < runEnd) {
1715                         int cx = run.width;
1716                         if (run.style != null && run.style.metrics != null) {
1717                                 GlyphMetrics metrics = run.style.metrics;
1718                                 cx = metrics.getWidthInPixels() * (end - run.start + 1);
1719                         } else if (!run.tab) {
1720                                 int[] piX = new int[1];
1721                                 long advances = run.justify != 0 ? run.justify : run.advances;
1722                                 OS.ScriptCPtoX(end - run.start, true, run.length, run.glyphCount, run.clusters, run.visAttrs, advances, run.analysis, piX);
1723                                 cx = isRTL ? run.width - piX[0] : piX[0];
1724                         }
1725                         if (run.analysis.fRTL ^ isRTL) {
1726                                 runLead = run.x + cx;
1727                         } else {
1728                                 runTrail = run.x + cx;
1729                         }
1730                 }
1731                 int lineIndex = 0;
1732                 while (lineIndex < runs.length && lineOffset[lineIndex + 1] <= run.start) {
1733                         lineIndex++;
1734                 }
1735                 left = Math.min(left, runLead);
1736                 right = Math.max(right, runTrail);
1737                 top = Math.min(top, DPIUtil.autoScaleUp(getDevice(), lineY[lineIndex]));
1738                 bottom = Math.max(bottom, DPIUtil.autoScaleUp(getDevice(), lineY[lineIndex + 1] - lineSpacingInPoints));
1739         }
1740         return new Rectangle(left, top, right - left, bottom - top + getScaledVerticalIndent());
1741 }
1742
1743 /**
1744  * Returns the descent of the receiver.
1745  *
1746  * @return the descent
1747  *
1748  * @exception SWTException <ul>
1749  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1750  * </ul>
1751  *
1752  * @see #getAscent()
1753  * @see #setAscent(int)
1754  * @see #setDescent(int)
1755  * @see #getLineMetrics(int)
1756  */
1757 public int getDescent () {
1758         checkLayout();
1759         return DPIUtil.autoScaleDown(getDevice(), descentInPixels);
1760 }
1761
1762 /**
1763  * Returns the default font currently being used by the receiver
1764  * to draw and measure text.
1765  *
1766  * @return the receiver's font
1767  *
1768  * @exception SWTException <ul>
1769  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1770  * </ul>
1771  */
1772 public Font getFont () {
1773         checkLayout();
1774         return font;
1775 }
1776
1777 /**
1778 * Returns the receiver's indent.
1779 *
1780 * @return the receiver's indent
1781 *
1782 * @exception SWTException <ul>
1783 *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1784 * </ul>
1785 *
1786 * @since 3.2
1787 */
1788 public int getIndent () {
1789         checkLayout();
1790         return DPIUtil.autoScaleDown(getDevice(), getIndentInPixels());
1791 }
1792
1793 int getIndentInPixels () {
1794         return indent;
1795 }
1796
1797 /**
1798 * Returns the receiver's justification.
1799 *
1800 * @return the receiver's justification
1801 *
1802 * @exception SWTException <ul>
1803 *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1804 * </ul>
1805 *
1806 * @since 3.2
1807 */
1808 public boolean getJustify () {
1809         checkLayout();
1810         return justify;
1811 }
1812
1813 long getItemFont (StyleItem item) {
1814         if (item.fallbackFont != 0) return item.fallbackFont;
1815         if (item.style != null && item.style.font != null) {
1816                 return item.style.font.handle;
1817         }
1818         if (this.font != null) {
1819                 return this.font.handle;
1820         }
1821         return device.systemFont.handle;
1822 }
1823
1824 /**
1825  * Returns the embedding level for the specified character offset. The
1826  * embedding level is usually used to determine the directionality of a
1827  * character in bidirectional text.
1828  *
1829  * @param offset the character offset
1830  * @return the embedding level
1831  *
1832  * @exception IllegalArgumentException <ul>
1833  *    <li>ERROR_INVALID_ARGUMENT - if the character offset is out of range</li>
1834  * </ul>
1835  * @exception SWTException <ul>
1836  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li></ul>
1837  */
1838 public int getLevel (int offset) {
1839         checkLayout();
1840         computeRuns(null);
1841         int length = text.length();
1842         if (!(0 <= offset && offset <= length)) SWT.error(SWT.ERROR_INVALID_RANGE);
1843         offset = translateOffset(offset);
1844         for (int i=1; i<allRuns.length; i++) {
1845                 if (allRuns[i].start > offset) {
1846                         return allRuns[i - 1].analysis.s.uBidiLevel;
1847                 }
1848         }
1849         return (resolveTextDirection() & SWT.RIGHT_TO_LEFT) != 0 ? 1 : 0;
1850 }
1851
1852 /**
1853  * Returns the bounds of the line for the specified line index.
1854  *
1855  * @param lineIndex the line index
1856  * @return the line bounds
1857  *
1858  * @exception IllegalArgumentException <ul>
1859  *    <li>ERROR_INVALID_ARGUMENT - if the line index is out of range</li>
1860  * </ul>
1861  * @exception SWTException <ul>
1862  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1863  * </ul>
1864  */
1865 public Rectangle getLineBounds (int lineIndex) {
1866         checkLayout();
1867         return DPIUtil.autoScaleDown(getDevice(), getLineBoundsInPixels(lineIndex));
1868 }
1869
1870 Rectangle getLineBoundsInPixels(int lineIndex) {
1871         computeRuns(null);
1872         if (!(0 <= lineIndex && lineIndex < runs.length)) SWT.error(SWT.ERROR_INVALID_RANGE);
1873         int x = getLineIndent(lineIndex);
1874         int y = DPIUtil.autoScaleUp(getDevice(), lineY[lineIndex]);
1875         int width = lineWidth[lineIndex];
1876         int height = DPIUtil.autoScaleUp(getDevice(), lineY[lineIndex + 1] - lineY[lineIndex] - lineSpacingInPoints);
1877         return new Rectangle (x, y, width, height);
1878 }
1879
1880 /**
1881  * Returns the receiver's line count. This includes lines caused
1882  * by wrapping.
1883  *
1884  * @return the line count
1885  *
1886  * @exception SWTException <ul>
1887  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1888  * </ul>
1889  */
1890 public int getLineCount () {
1891         checkLayout();
1892         computeRuns(null);
1893         return runs.length;
1894 }
1895
1896 int getLineIndent (int lineIndex) {
1897         int lineIndent = wrapIndent;
1898         if (lineIndex == 0) {
1899                 lineIndent = indent;
1900         } else {
1901                 StyleItem[] previousLine = runs[lineIndex - 1];
1902                 StyleItem previousRun = previousLine[previousLine.length - 1];
1903                 if (previousRun.lineBreak && !previousRun.softBreak) {
1904                         lineIndent = indent;
1905                 }
1906         }
1907         if (wrapWidth != -1) {
1908                 boolean partialLine = true;
1909                 if (justify) {
1910                         StyleItem[] lineRun = runs[lineIndex];
1911                         if (lineRun[lineRun.length - 1].softBreak) {
1912                                 partialLine = false;
1913                         }
1914                 }
1915                 if (partialLine) {
1916                         int lineWidth = this.lineWidth[lineIndex] + lineIndent;
1917                         switch (alignment) {
1918                                 case SWT.CENTER: lineIndent += (wrapWidth - lineWidth) / 2; break;
1919                                 case SWT.RIGHT: lineIndent += wrapWidth - lineWidth; break;
1920                         }
1921                 }
1922         }
1923         return lineIndent;
1924 }
1925
1926 /**
1927  * Returns the index of the line that contains the specified
1928  * character offset.
1929  *
1930  * @param offset the character offset
1931  * @return the line index
1932  *
1933  * @exception IllegalArgumentException <ul>
1934  *    <li>ERROR_INVALID_ARGUMENT - if the character offset is out of range</li>
1935  * </ul>
1936  * @exception SWTException <ul>
1937  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1938  * </ul>
1939  */
1940 public int getLineIndex (int offset) {
1941         checkLayout();
1942         computeRuns(null);
1943         int length = text.length();
1944         if (!(0 <= offset && offset <= length)) SWT.error(SWT.ERROR_INVALID_RANGE);
1945         offset = translateOffset(offset);
1946         for (int line=0; line<runs.length; line++) {
1947                 if (lineOffset[line + 1] > offset) {
1948                         return line;
1949                 }
1950         }
1951         return runs.length - 1;
1952 }
1953
1954 /**
1955  * Returns the font metrics for the specified line index.
1956  *
1957  * @param lineIndex the line index
1958  * @return the font metrics
1959  *
1960  * @exception IllegalArgumentException <ul>
1961  *    <li>ERROR_INVALID_ARGUMENT - if the line index is out of range</li>
1962  * </ul>
1963  * @exception SWTException <ul>
1964  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1965  * </ul>
1966  */
1967 public FontMetrics getLineMetrics (int lineIndex) {
1968         checkLayout();
1969         computeRuns(null);
1970         if (!(0 <= lineIndex && lineIndex < runs.length)) SWT.error(SWT.ERROR_INVALID_RANGE);
1971         long hDC = device.internal_new_GC(null);
1972         long srcHdc = OS.CreateCompatibleDC(hDC);
1973         TEXTMETRIC lptm = new TEXTMETRIC();
1974         OS.SelectObject(srcHdc, font != null ? font.handle : device.systemFont.handle);
1975         OS.GetTextMetrics(srcHdc, lptm);
1976         OS.DeleteDC(srcHdc);
1977         device.internal_dispose_GC(hDC, null);
1978
1979         int ascentInPoints = DPIUtil.autoScaleDown(getDevice(), Math.max(lptm.tmAscent, this.ascentInPixels));
1980         int descentInPoints = DPIUtil.autoScaleDown(getDevice(), Math.max(lptm.tmDescent, this.descentInPixels));
1981         int leadingInPoints = DPIUtil.autoScaleDown(getDevice(), lptm.tmInternalLeading);
1982         if (text.length() != 0) {
1983                 StyleItem[] lineRuns = runs[lineIndex];
1984                 for (int i = 0; i<lineRuns.length; i++) {
1985                         StyleItem run = lineRuns[i];
1986                         if (run.ascentInPoints > ascentInPoints) {
1987                                 ascentInPoints = run.ascentInPoints;
1988                                 leadingInPoints = run.leadingInPoints;
1989                         }
1990                         descentInPoints = Math.max(descentInPoints, run.descentInPoints);
1991                 }
1992         }
1993         lptm.tmAscent = DPIUtil.autoScaleUp(getDevice(), ascentInPoints);
1994         lptm.tmDescent = DPIUtil.autoScaleUp(getDevice(), descentInPoints);
1995         lptm.tmHeight = DPIUtil.autoScaleUp(getDevice(), ascentInPoints + descentInPoints);
1996         lptm.tmInternalLeading = DPIUtil.autoScaleUp(getDevice(), leadingInPoints);
1997         lptm.tmAveCharWidth = 0;
1998         return FontMetrics.win32_new(lptm);
1999 }
2000
2001 /**
2002  * Returns the line offsets.  Each value in the array is the
2003  * offset for the first character in a line except for the last
2004  * value, which contains the length of the text.
2005  *
2006  * @return the line offsets
2007  *
2008  * @exception SWTException <ul>
2009  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2010  * </ul>
2011  */
2012 public int[] getLineOffsets () {
2013         checkLayout();
2014         computeRuns(null);
2015         int[] offsets = new int[lineOffset.length];
2016         for (int i = 0; i < offsets.length; i++) {
2017                 offsets[i] = untranslateOffset(lineOffset[i]);
2018         }
2019         return offsets;
2020 }
2021
2022 /**
2023  * Returns the location for the specified character offset. The
2024  * <code>trailing</code> argument indicates whether the offset
2025  * corresponds to the leading or trailing edge of the cluster.
2026  *
2027  * @param offset the character offset
2028  * @param trailing the trailing flag
2029  * @return the location of the character offset
2030  *
2031  * @exception SWTException <ul>
2032  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2033  * </ul>
2034  *
2035  * @see #getOffset(Point, int[])
2036  * @see #getOffset(int, int, int[])
2037  */
2038 public Point getLocation (int offset, boolean trailing) {
2039         checkLayout();
2040         return DPIUtil.autoScaleDown(getDevice(), getLocationInPixels(offset, trailing));
2041 }
2042
2043 Point getLocationInPixels (int offset, boolean trailing) {
2044         computeRuns(null);
2045         int length = text.length();
2046         if (!(0 <= offset && offset <= length)) SWT.error(SWT.ERROR_INVALID_RANGE);
2047         length = segmentsText.length();
2048         offset = translateOffset(offset);
2049         int line;
2050         for (line=0; line<runs.length; line++) {
2051                 if (lineOffset[line + 1] > offset) break;
2052         }
2053         line = Math.min(line, runs.length - 1);
2054         if (offset == length) {
2055                 return new Point(getLineIndent(line) + lineWidth[line], DPIUtil.autoScaleUp(getDevice(), lineY[line]));
2056         }
2057         /* For trailing use the low surrogate and for lead use the high surrogate */
2058         char ch = segmentsText.charAt(offset);
2059         if (trailing) {
2060                 if (0xD800 <= ch && ch <= 0xDBFF) {
2061                         if (offset + 1 < length) {
2062                                 ch = segmentsText.charAt(offset + 1);
2063                                 if (0xDC00 <= ch && ch <= 0xDFFF) {
2064                                         offset++;
2065                                 }
2066                         }
2067                 }
2068         } else {
2069                 if (0xDC00 <= ch && ch <= 0xDFFF) {
2070                         if (offset - 1 >= 0) {
2071                                 ch = segmentsText.charAt(offset - 1);
2072                                 if (0xD800 <= ch && ch <= 0xDBFF) {
2073                                         offset--;
2074                                 }
2075                         }
2076                 }
2077         }
2078         int low = -1;
2079         int high = allRuns.length;
2080         while (high - low > 1) {
2081                 int index = ((high + low) / 2);
2082                 StyleItem run = allRuns[index];
2083                 if (run.start > offset) {
2084                         high = index;
2085                 } else if (run.start + run.length <= offset) {
2086                         low = index;
2087                 } else {
2088                         int width;
2089                         if (run.style != null && run.style.metrics != null) {
2090                                 GlyphMetrics metrics = run.style.metrics;
2091                                 width = metrics.getWidthInPixels() * (offset - run.start + (trailing ? 1 : 0));
2092                         } else if (run.tab) {
2093                                 width = (trailing || (offset == length)) ? run.width : 0;
2094                         } else {
2095                                 int runOffset = offset - run.start;
2096                                 int cChars = run.length;
2097                                 int gGlyphs = run.glyphCount;
2098                                 int[] piX = new int[1];
2099                                 long advances = run.justify != 0 ? run.justify : run.advances;
2100                                 OS.ScriptCPtoX(runOffset, trailing, cChars, gGlyphs, run.clusters, run.visAttrs, advances, run.analysis, piX);
2101                                 width = (orientation & SWT.RIGHT_TO_LEFT) != 0 ? run.width - piX[0] : piX[0];
2102                         }
2103                         return new Point(run.x + width, DPIUtil.autoScaleUp(getDevice(), lineY[line]) + getScaledVerticalIndent());
2104                 }
2105         }
2106         return new Point(0, 0);
2107 }
2108
2109 /**
2110  * Returns the next offset for the specified offset and movement
2111  * type.  The movement is one of <code>SWT.MOVEMENT_CHAR</code>,
2112  * <code>SWT.MOVEMENT_CLUSTER</code>, <code>SWT.MOVEMENT_WORD</code>,
2113  * <code>SWT.MOVEMENT_WORD_END</code> or <code>SWT.MOVEMENT_WORD_START</code>.
2114  *
2115  * @param offset the start offset
2116  * @param movement the movement type
2117  * @return the next offset
2118  *
2119  * @exception IllegalArgumentException <ul>
2120  *    <li>ERROR_INVALID_ARGUMENT - if the offset is out of range</li>
2121  * </ul>
2122  * @exception SWTException <ul>
2123  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2124  * </ul>
2125  *
2126  * @see #getPreviousOffset(int, int)
2127  */
2128 public int getNextOffset (int offset, int movement) {
2129         checkLayout();
2130         return _getOffset (offset, movement, true);
2131 }
2132
2133 int _getOffset(int offset, int movement, boolean forward) {
2134         computeRuns(null);
2135         int length = text.length();
2136         if (!(0 <= offset && offset <= length)) SWT.error(SWT.ERROR_INVALID_RANGE, null, " [offset value: " + offset + "]");//$NON-NLS-1$ $NON-NLS-2$
2137         if (forward && offset == length) return length;
2138         if (!forward && offset == 0) return 0;
2139         int step = forward ? 1 : -1;
2140         if ((movement & SWT.MOVEMENT_CHAR) != 0) return offset + step;
2141         length = segmentsText.length();
2142         offset = translateOffset(offset);
2143         SCRIPT_LOGATTR logAttr = new SCRIPT_LOGATTR();
2144         SCRIPT_PROPERTIES properties = new  SCRIPT_PROPERTIES();
2145         int i = forward ? 0 : allRuns.length - 1;
2146         offset = validadeOffset(offset, step);
2147         do {
2148                 StyleItem run = allRuns[i];
2149                 if (run.start <= offset && offset < run.start + run.length) {
2150                         if (run.lineBreak && !run.softBreak) return untranslateOffset(run.start);
2151                         if (run.tab) return untranslateOffset(run.start);
2152                         OS.MoveMemory(properties, device.scripts[run.analysis.eScript], SCRIPT_PROPERTIES.sizeof);
2153                         boolean isComplex = properties.fNeedsCaretInfo || properties.fNeedsWordBreaking;
2154                         if (isComplex) breakRun(run);
2155                         while (run.start <= offset && offset < run.start + run.length) {
2156                                 if (isComplex) {
2157                                         OS.MoveMemory(logAttr, run.psla + ((offset - run.start) * SCRIPT_LOGATTR.sizeof), SCRIPT_LOGATTR.sizeof);
2158                                 }
2159                                 switch (movement) {
2160                                         case SWT.MOVEMENT_CLUSTER: {
2161                                                 if (!properties.fNeedsCaretInfo || (!logAttr.fInvalid && logAttr.fCharStop)) {
2162                                                         char ch = segmentsText.charAt(offset);
2163                                                         if (0xDC00 <= ch && ch <= 0xDFFF) {
2164                                                                 if (offset > 0) {
2165                                                                         ch = segmentsText.charAt(offset - 1);
2166                                                                         if (0xD800 <= ch && ch <= 0xDBFF) {
2167                                                                                 offset += step;
2168                                                                         }
2169                                                                 }
2170                                                         }
2171                                                         return untranslateOffset(offset);
2172                                                 }
2173                                                 break;
2174                                         }
2175                                         case SWT.MOVEMENT_WORD_START:
2176                                         case SWT.MOVEMENT_WORD: {
2177                                                 if (properties.fNeedsWordBreaking) {
2178                                                         if (!logAttr.fInvalid && logAttr.fWordStop) return untranslateOffset(offset);
2179                                                 } else {
2180                                                         if (offset > 0) {
2181                                                                 boolean letterOrDigit = Character.isLetterOrDigit(segmentsText.charAt(offset));
2182                                                                 boolean previousLetterOrDigit = Character.isLetterOrDigit(segmentsText.charAt(offset - 1));
2183                                                                 if (letterOrDigit != previousLetterOrDigit || !letterOrDigit) {
2184                                                                         if (!Character.isWhitespace(segmentsText.charAt(offset))) {
2185                                                                                 return untranslateOffset(offset);
2186                                                                         }
2187                                                                 }
2188                                                         }
2189                                                 }
2190                                                 break;
2191                                         }
2192                                         case SWT.MOVEMENT_WORD_END: {
2193                                                 if (offset > 0) {
2194                                                         boolean isLetterOrDigit = Character.isLetterOrDigit(segmentsText.charAt(offset));
2195                                                         boolean previousLetterOrDigit = Character.isLetterOrDigit(segmentsText.charAt(offset - 1));
2196                                                         if (!isLetterOrDigit && previousLetterOrDigit) {
2197                                                                 return untranslateOffset(offset);
2198                                                         }
2199                                                 }
2200                                                 break;
2201                                         }
2202                                 }
2203                                 offset = validadeOffset(offset, step);
2204                         }
2205                 }
2206                 i += step;
2207         } while (0 <= i && i < allRuns.length - 1 && 0 <= offset && offset < length);
2208         return forward ? text.length() : 0;
2209 }
2210
2211 /**
2212  * Returns the character offset for the specified point.
2213  * For a typical character, the trailing argument will be filled in to
2214  * indicate whether the point is closer to the leading edge (0) or
2215  * the trailing edge (1).  When the point is over a cluster composed
2216  * of multiple characters, the trailing argument will be filled with the
2217  * position of the character in the cluster that is closest to
2218  * the point.
2219  *
2220  * @param point the point
2221  * @param trailing the trailing buffer
2222  * @return the character offset
2223  *
2224  * @exception IllegalArgumentException <ul>
2225  *    <li>ERROR_INVALID_ARGUMENT - if the trailing length is less than <code>1</code></li>
2226  *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
2227  * </ul>
2228  * @exception SWTException <ul>
2229  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2230  * </ul>
2231  *
2232  * @see #getLocation(int, boolean)
2233  */
2234 public int getOffset (Point point, int[] trailing) {
2235         checkLayout();
2236         if (point == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); return getOffsetInPixels(DPIUtil.autoScaleUp(getDevice(), point), trailing);
2237 }
2238
2239 int getOffsetInPixels (Point point, int[] trailing) {
2240         return getOffsetInPixels (point.x, point.y, trailing) ;
2241 }
2242
2243 /**
2244  * Returns the character offset for the specified point.
2245  * For a typical character, the trailing argument will be filled in to
2246  * indicate whether the point is closer to the leading edge (0) or
2247  * the trailing edge (1).  When the point is over a cluster composed
2248  * of multiple characters, the trailing argument will be filled with the
2249  * position of the character in the cluster that is closest to
2250  * the point.
2251  *
2252  * @param x the x coordinate of the point
2253  * @param y the y coordinate of the point
2254  * @param trailing the trailing buffer
2255  * @return the character offset
2256  *
2257  * @exception IllegalArgumentException <ul>
2258  *    <li>ERROR_INVALID_ARGUMENT - if the trailing length is less than <code>1</code></li>
2259  * </ul>
2260  * @exception SWTException <ul>
2261  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2262  * </ul>
2263  *
2264  * @see #getLocation(int, boolean)
2265  */
2266 public int getOffset (int x, int y, int[] trailing) {
2267         checkLayout();
2268         return getOffsetInPixels(DPIUtil.autoScaleUp(getDevice(), x), DPIUtil.autoScaleUp(getDevice(), y), trailing);
2269 }
2270
2271 int getOffsetInPixels (int x, int y, int[] trailing) {
2272         computeRuns(null);
2273         if (trailing != null && trailing.length < 1) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2274         int line;
2275         int lineCount = runs.length;
2276         for (line=0; line<lineCount; line++) {
2277                 if (DPIUtil.autoScaleUp(getDevice(), lineY[line + 1]) > y) break;
2278         }
2279         line = Math.min(line, runs.length - 1);
2280         StyleItem[] lineRuns = runs[line];
2281         int lineIndent = getLineIndent(line);
2282         if (x >= lineIndent + lineWidth[line]) x = lineIndent + lineWidth[line] - 1;
2283         if (x < lineIndent) x = lineIndent;
2284         int low = -1;
2285         int high = lineRuns.length;
2286         while (high - low > 1) {
2287                 int index = ((high + low) / 2);
2288                 StyleItem run = lineRuns[index];
2289                 if (run.x > x) {
2290                         high = index;
2291                 } else if (run.x + run.width <= x) {
2292                         low = index;
2293                 } else {
2294                         if (run.lineBreak && !run.softBreak) return untranslateOffset(run.start);
2295                         int xRun = x - run.x;
2296                         if (run.style != null && run.style.metrics != null) {
2297                                 GlyphMetrics metrics = run.style.metrics;
2298                                 if (metrics.getWidthInPixels() > 0) {
2299                                         if (trailing != null) {
2300                                                 trailing[0] = (xRun % metrics.getWidthInPixels() < metrics.getWidthInPixels() / 2) ? 0 : 1;
2301                                         }
2302                                         return untranslateOffset(run.start + xRun / metrics.getWidthInPixels());
2303                                 }
2304                         }
2305                         if (run.tab) {
2306                                 if (trailing != null) trailing[0] = x < (run.x + run.width / 2) ? 0 : 1;
2307                                 return untranslateOffset(run.start);
2308                         }
2309                         int cChars = run.length;
2310                         int cGlyphs = run.glyphCount;
2311                         int[] piCP = new int[1];
2312                         int[] piTrailing = new int[1];
2313                         if ((orientation & SWT.RIGHT_TO_LEFT) != 0) {
2314                                 xRun = run.width - xRun;
2315                         }
2316                         long advances = run.justify != 0 ? run.justify : run.advances;
2317                         OS.ScriptXtoCP(xRun, cChars, cGlyphs, run.clusters, run.visAttrs, advances, run.analysis, piCP, piTrailing);
2318                         int offset = run.start + piCP[0];
2319                         int length = segmentsText.length();
2320                         char ch = offset < length ? segmentsText.charAt(offset) : 0;
2321                         if (0xD800 <= ch && ch <= 0xDBFF && piTrailing[0] <= 1) {
2322                                 if (offset + 1 < length) {
2323                                         ch = segmentsText.charAt(offset + 1);
2324                                         if (0xDC00 <= ch && ch <= 0xDFFF) {
2325                                                 if (trailing != null) trailing[0] = 0;
2326                                         }
2327                                 }
2328                         } else if (0xDC00 <= ch && ch <= 0xDFFF && piTrailing[0] <= 1) {
2329                                 if (offset - 1 >= 0) {
2330                                         ch = segmentsText.charAt(offset - 1);
2331                                         if (0xD800 <= ch && ch <= 0xDBFF) {
2332                                                 offset--;
2333                                                 if (trailing != null) trailing[0] = 2;
2334                                         }
2335                                 }
2336                         } else {
2337                                 if (trailing != null) trailing[0] = piTrailing[0];
2338                         }
2339                         return untranslateOffset(offset);
2340                 }
2341         }
2342         if (trailing != null) trailing[0] = 0;
2343         if (lineRuns.length == 1) {
2344                 StyleItem run = lineRuns[0];
2345                 if (run.lineBreak && !run.softBreak) return untranslateOffset(run.start);
2346         }
2347         return untranslateOffset(lineOffset[line + 1]);
2348 }
2349
2350 /**
2351  * Returns the orientation of the receiver.
2352  *
2353  * @return the orientation style
2354  *
2355  * @exception SWTException <ul>
2356  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2357  * </ul>
2358  */
2359 public int getOrientation () {
2360         checkLayout();
2361         return orientation;
2362 }
2363
2364 void getPartialSelection(StyleItem run, int selectionStart, int selectionEnd, RECT rect) {
2365         int end = run.start + run.length - 1;
2366         int selStart = Math.max(selectionStart, run.start) - run.start;
2367         int selEnd = Math.min(selectionEnd, end) - run.start;
2368         int cChars = run.length;
2369         int gGlyphs = run.glyphCount;
2370         int[] piX = new int[1];
2371         int x = rect.left;
2372         long advances = run.justify != 0 ? run.justify : run.advances;
2373         OS.ScriptCPtoX(selStart, false, cChars, gGlyphs, run.clusters, run.visAttrs, advances, run.analysis, piX);
2374         int runX = (orientation & SWT.RIGHT_TO_LEFT) != 0 ? run.width - piX[0] : piX[0];
2375         rect.left = x + runX;
2376         OS.ScriptCPtoX(selEnd, true, cChars, gGlyphs, run.clusters, run.visAttrs, advances, run.analysis, piX);
2377         runX = (orientation & SWT.RIGHT_TO_LEFT) != 0 ? run.width - piX[0] : piX[0];
2378         rect.right = x + runX;
2379 }
2380
2381 /**
2382  * Returns the previous offset for the specified offset and movement
2383  * type.  The movement is one of <code>SWT.MOVEMENT_CHAR</code>,
2384  * <code>SWT.MOVEMENT_CLUSTER</code> or <code>SWT.MOVEMENT_WORD</code>,
2385  * <code>SWT.MOVEMENT_WORD_END</code> or <code>SWT.MOVEMENT_WORD_START</code>.
2386  *
2387  * @param offset the start offset
2388  * @param movement the movement type
2389  * @return the previous offset
2390  *
2391  * @exception IllegalArgumentException <ul>
2392  *    <li>ERROR_INVALID_ARGUMENT - if the offset is out of range</li>
2393  * </ul>
2394  * @exception SWTException <ul>
2395  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2396  * </ul>
2397  *
2398  * @see #getNextOffset(int, int)
2399  */
2400 public int getPreviousOffset (int offset, int movement) {
2401         checkLayout();
2402         return _getOffset (offset, movement, false);
2403 }
2404
2405 /**
2406  * Gets the ranges of text that are associated with a <code>TextStyle</code>.
2407  *
2408  * @return the ranges, an array of offsets representing the start and end of each
2409  * text style.
2410  *
2411  * @exception SWTException <ul>
2412  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2413  * </ul>
2414  *
2415  * @see #getStyles()
2416  *
2417  * @since 3.2
2418  */
2419 public int[] getRanges () {
2420         checkLayout();
2421         int[] result = new int[stylesCount * 2];
2422         int count = 0;
2423         for (int i=0; i<stylesCount - 1; i++) {
2424                 if (styles[i].style != null) {
2425                         result[count++] = styles[i].start;
2426                         result[count++] = styles[i + 1].start - 1;
2427                 }
2428         }
2429         if (count != result.length) {
2430                 int[] newResult = new int[count];
2431                 System.arraycopy(result, 0, newResult, 0, count);
2432                 result = newResult;
2433         }
2434         return result;
2435 }
2436
2437 /**
2438  * Returns the text segments offsets of the receiver.
2439  *
2440  * @return the text segments offsets
2441  *
2442  * @exception SWTException <ul>
2443  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2444  * </ul>
2445  */
2446 public int[] getSegments () {
2447         checkLayout();
2448         return segments;
2449 }
2450
2451 /**
2452  * Returns the segments characters of the receiver.
2453  *
2454  * @return the segments characters
2455  *
2456  * @exception SWTException <ul>
2457  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2458  * </ul>
2459  *
2460  * @since 3.6
2461  */
2462 public char[] getSegmentsChars () {
2463         checkLayout();
2464         return segmentsChars;
2465 }
2466
2467 String getSegmentsText() {
2468         int length = text.length();
2469         if (length == 0) return text;
2470         if (segments == null) return text;
2471         int nSegments = segments.length;
2472         if (nSegments == 0) return text;
2473         if (segmentsChars == null) {
2474                 if (nSegments == 1) return text;
2475                 if (nSegments == 2) {
2476                         if (segments[0] == 0 && segments[1] == length) return text;
2477                 }
2478         }
2479         char[] oldChars = new char[length];
2480         text.getChars(0, length, oldChars, 0);
2481         char[] newChars = new char[length + nSegments];
2482         int charCount = 0, segmentCount = 0;
2483         char defaultSeparator = (resolveTextDirection() & SWT.RIGHT_TO_LEFT) != 0 ? RTL_MARK : LTR_MARK;
2484         while (charCount < length) {
2485                 if (segmentCount < nSegments && charCount == segments[segmentCount]) {
2486                         char separator = segmentsChars != null && segmentsChars.length > segmentCount ? segmentsChars[segmentCount] : defaultSeparator;
2487                         newChars[charCount + segmentCount++] = separator;
2488                 } else {
2489                         newChars[charCount + segmentCount] = oldChars[charCount++];
2490                 }
2491         }
2492         while (segmentCount < nSegments) {
2493                 segments[segmentCount] = charCount;
2494                 char separator = segmentsChars != null && segmentsChars.length > segmentCount ? segmentsChars[segmentCount] : defaultSeparator;
2495                 newChars[charCount + segmentCount++] = separator;
2496         }
2497         return new String(newChars, 0, newChars.length);
2498 }
2499
2500 /**
2501  * Returns the line spacing of the receiver.
2502  *
2503  * @return the line spacing
2504  *
2505  * @exception SWTException <ul>
2506  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2507  * </ul>
2508  */
2509 public int getSpacing () {
2510         checkLayout();
2511         return lineSpacingInPoints;
2512 }
2513
2514 /**
2515  * Returns the vertical indent of the receiver.
2516  *
2517  * @return the vertical indent
2518  *
2519  * @exception SWTException <ul>
2520  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2521  * </ul>
2522  * @since 3.109
2523  */
2524 public int getVerticalIndent () {
2525         checkLayout();
2526         return verticalIndentInPoints;
2527 }
2528
2529 /**
2530  * Returns the scaled vertical indent.
2531  *
2532  * @return the scaled vertical indent.
2533  * @since 3.109
2534  */
2535 private int getScaledVerticalIndent() {
2536         if (verticalIndentInPoints == 0) {
2537                 return verticalIndentInPoints;
2538         }
2539         return DPIUtil.autoScaleUp(getDevice(), verticalIndentInPoints);
2540 }
2541
2542 /**
2543  * Gets the style of the receiver at the specified character offset.
2544  *
2545  * @param offset the text offset
2546  * @return the style or <code>null</code> if not set
2547  *
2548  * @exception IllegalArgumentException <ul>
2549  *    <li>ERROR_INVALID_ARGUMENT - if the character offset is out of range</li>
2550  * </ul>
2551  * @exception SWTException <ul>
2552  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2553  * </ul>
2554  */
2555 public TextStyle getStyle (int offset) {
2556         checkLayout();
2557         int length = text.length();
2558         if (!(0 <= offset && offset < length)) SWT.error(SWT.ERROR_INVALID_RANGE);
2559         for (int i=1; i<stylesCount; i++) {
2560                 if (styles[i].start > offset) {
2561                         return styles[i - 1].style;
2562                 }
2563         }
2564         return null;
2565 }
2566
2567 /**
2568  * Gets all styles of the receiver.
2569  *
2570  * @return the styles
2571  *
2572  * @exception SWTException <ul>
2573  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2574  * </ul>
2575  *
2576  * @see #getRanges()
2577  *
2578  * @since 3.2
2579  */
2580 public TextStyle[] getStyles () {
2581         checkLayout();
2582         TextStyle[] result = new TextStyle[stylesCount];
2583         int count = 0;
2584         for (int i=0; i<stylesCount; i++) {
2585                 if (styles[i].style != null) {
2586                         result[count++] = styles[i].style;
2587                 }
2588         }
2589         if (count != result.length) {
2590                 TextStyle[] newResult = new TextStyle[count];
2591                 System.arraycopy(result, 0, newResult, 0, count);
2592                 result = newResult;
2593         }
2594         return result;
2595 }
2596
2597 /**
2598  * Returns the tab list of the receiver.
2599  *
2600  * @return the tab list
2601  *
2602  * @exception SWTException <ul>
2603  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2604  * </ul>
2605  */
2606 public int[] getTabs () {
2607         checkLayout();
2608         return DPIUtil.autoScaleDown (getDevice(), getTabsInPixels ());
2609 }
2610
2611 int[] getTabsInPixels () {
2612         return tabs;
2613 }
2614
2615 /**
2616  * Gets the receiver's text, which will be an empty
2617  * string if it has never been set.
2618  *
2619  * @return the receiver's text
2620  *
2621  * @exception SWTException <ul>
2622  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2623  * </ul>
2624  */
2625 public String getText () {
2626         checkLayout();
2627         return text;
2628 }
2629
2630 /**
2631  * Returns the text direction of the receiver.
2632  *
2633  * @return the text direction value
2634  *
2635  * @exception SWTException <ul>
2636  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2637  * </ul>
2638  * @since 3.103
2639  */
2640 public int getTextDirection () {
2641         checkLayout();
2642         return resolveTextDirection();
2643 }
2644
2645 /**
2646  * Returns the width of the receiver.
2647  *
2648  * @return the width
2649  *
2650  * @exception SWTException <ul>
2651  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2652  * </ul>
2653  */
2654 public int getWidth () {
2655         checkLayout();
2656         return DPIUtil.autoScaleDown(getDevice(), getWidthInPixels());
2657 }
2658
2659 int getWidthInPixels () {
2660         return wrapWidth;
2661 }
2662
2663 /**
2664 * Returns the receiver's wrap indent.
2665 *
2666 * @return the receiver's wrap indent
2667 *
2668 * @exception SWTException <ul>
2669 *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2670 * </ul>
2671 *
2672 * @since 3.6
2673 */
2674 public int getWrapIndent () {
2675         checkLayout();
2676         return DPIUtil.autoScaleDown(getDevice(), getWrapIndentInPixels());
2677 }
2678
2679 int getWrapIndentInPixels () {
2680         return wrapIndent;
2681 }
2682
2683 /**
2684  * Returns <code>true</code> if the text layout has been disposed,
2685  * and <code>false</code> otherwise.
2686  * <p>
2687  * This method gets the dispose state for the text layout.
2688  * When a text layout has been disposed, it is an error to
2689  * invoke any other method (except {@link #dispose()}) using the text layout.
2690  * </p>
2691  *
2692  * @return <code>true</code> when the text layout is disposed and <code>false</code> otherwise
2693  */
2694 @Override
2695 public boolean isDisposed () {
2696         return device == null;
2697 }
2698
2699 /*
2700  *  Itemize the receiver text
2701  */
2702 StyleItem[] itemize () {
2703         segmentsText = getSegmentsText();
2704         int length = segmentsText.length();
2705         SCRIPT_CONTROL scriptControl = new SCRIPT_CONTROL();
2706         SCRIPT_STATE scriptState = new SCRIPT_STATE();
2707         final int MAX_ITEM = length + 1;
2708
2709         if ((resolveTextDirection() & SWT.RIGHT_TO_LEFT) != 0) {
2710                 scriptState.uBidiLevel = 1;
2711                 scriptState.fArabicNumContext = true;
2712         }
2713
2714         /*
2715         * In the version of Usp10.h that SWT is compiled the fReserved field is declared
2716         * as a bitfield size 8. In newer versions of the Uniscribe, the first bit of fReserved
2717         * was used to implement the fMergeNeutralItems feature which can be used to increase
2718         * performance by reducing the number of SCRIPT_ITEM returned by ScriptItemize.
2719         *
2720         * Note: This code is wrong on a big endian machine.
2721         *
2722         * Note: This code is intentionally commented because it causes bug#377472.
2723         */
2724 //      scriptControl.fReserved = 0x1;
2725
2726         OS.ScriptApplyDigitSubstitution(null, scriptControl, scriptState);
2727
2728         long hHeap = OS.GetProcessHeap();
2729         long pItems = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, MAX_ITEM * SCRIPT_ITEM.sizeof);
2730         if (pItems == 0) SWT.error(SWT.ERROR_NO_HANDLES);
2731         int[] pcItems = new int[1];
2732         char[] chars = new char[length];
2733         segmentsText.getChars(0, length, chars, 0);
2734         OS.ScriptItemize(chars, length, MAX_ITEM, scriptControl, scriptState, pItems, pcItems);
2735 //      if (hr == E_OUTOFMEMORY) //TODO handle it
2736
2737         StyleItem[] runs = merge(pItems, pcItems[0]);
2738         OS.HeapFree(hHeap, 0, pItems);
2739         return runs;
2740 }
2741
2742 /*
2743  *  Merge styles ranges and script items
2744  */
2745 StyleItem[] merge (long items, int itemCount) {
2746         if (styles.length > stylesCount) {
2747                 StyleItem[] newStyles = new StyleItem[stylesCount];
2748                 System.arraycopy(styles, 0, newStyles, 0, stylesCount);
2749                 styles = newStyles;
2750         }
2751         int count = 0, start = 0, end = segmentsText.length(), itemIndex = 0, styleIndex = 0;
2752         StyleItem[] runs = new StyleItem[itemCount + stylesCount];
2753         SCRIPT_ITEM scriptItem = new SCRIPT_ITEM();
2754         int itemLimit = -1;
2755         int nextItemIndex = 0;
2756         boolean linkBefore = false;
2757         boolean merge = itemCount > TOO_MANY_RUNS;
2758         SCRIPT_PROPERTIES sp = new SCRIPT_PROPERTIES();
2759         while (start < end) {
2760                 StyleItem item = new StyleItem();
2761                 item.start = start;
2762                 item.style = styles[styleIndex].style;
2763                 runs[count++] = item;
2764                 OS.MoveMemory(scriptItem, items + itemIndex * SCRIPT_ITEM.sizeof, SCRIPT_ITEM.sizeof);
2765                 item.analysis = scriptItem.a;
2766                 scriptItem.a = new SCRIPT_ANALYSIS();
2767                 if (linkBefore) {
2768                         item.analysis.fLinkBefore = true;
2769                         linkBefore = false;
2770                 }
2771                 char ch = segmentsText.charAt(start);
2772                 switch (ch) {
2773                         case '\r':
2774                         case '\n':
2775                                 item.lineBreak = true;
2776                                 break;
2777                         case '\t':
2778                                 item.tab = true;
2779                                 break;
2780                 }
2781                 if (itemLimit == -1) {
2782                         nextItemIndex = itemIndex + 1;
2783                         OS.MoveMemory(scriptItem, items + nextItemIndex * SCRIPT_ITEM.sizeof, SCRIPT_ITEM.sizeof);
2784                         itemLimit = scriptItem.iCharPos;
2785                         if (nextItemIndex < itemCount && ch == '\r' && segmentsText.charAt(itemLimit) == '\n') {
2786                                 nextItemIndex = itemIndex + 2;
2787                                 OS.MoveMemory(scriptItem, items + nextItemIndex * SCRIPT_ITEM.sizeof, SCRIPT_ITEM.sizeof);
2788                                 itemLimit = scriptItem.iCharPos;
2789                         }
2790                         if (nextItemIndex < itemCount && merge) {
2791                                 if (!item.lineBreak) {
2792                                         OS.MoveMemory(sp, device.scripts[item.analysis.eScript], SCRIPT_PROPERTIES.sizeof);
2793                                         if (!sp.fComplex || item.tab) {
2794                                                 for (int i = 0; i < MERGE_MAX; i++) {
2795                                                         if (nextItemIndex == itemCount) break;
2796                                                         char c = segmentsText.charAt(itemLimit);
2797                                                         if (c == '\n' || c == '\r') break;
2798                                                         if (c == '\t' != item.tab) break;
2799                                                         OS.MoveMemory(sp, device.scripts[scriptItem.a.eScript], SCRIPT_PROPERTIES.sizeof);
2800                                                         if (!item.tab && sp.fComplex) break;
2801                                                         nextItemIndex++;
2802                                                         OS.MoveMemory(scriptItem, items + nextItemIndex * SCRIPT_ITEM.sizeof, SCRIPT_ITEM.sizeof);
2803                                                         itemLimit = scriptItem.iCharPos;
2804                                                 }
2805                                         }
2806                                 }
2807                         }
2808                 }
2809
2810                 int styleLimit = translateOffset(styles[styleIndex + 1].start);
2811                 if (styleLimit <= itemLimit) {
2812                         styleIndex++;
2813                         start = styleLimit;
2814                         if (start < itemLimit && 0 < start && start < end) {
2815                                 char pChar = segmentsText.charAt(start - 1);
2816                                 char tChar = segmentsText.charAt(start);
2817                                 if (Character.isLetter(pChar) && Character.isLetter(tChar)) {
2818                                         item.analysis.fLinkAfter = true;
2819                                         linkBefore = true;
2820                                 }
2821                         }
2822                 }
2823                 if (itemLimit <= styleLimit) {
2824                         itemIndex = nextItemIndex;
2825                         start = itemLimit;
2826                         itemLimit = -1;
2827                 }
2828                 item.length = start - item.start;
2829         }
2830         StyleItem item = new StyleItem();
2831         item.start = end;
2832         OS.MoveMemory(scriptItem, items + itemCount * SCRIPT_ITEM.sizeof, SCRIPT_ITEM.sizeof);
2833         item.analysis = scriptItem.a;
2834         runs[count++] = item;
2835         if (runs.length != count) {
2836                 StyleItem[] result = new StyleItem[count];
2837                 System.arraycopy(runs, 0, result, 0, count);
2838                 return result;
2839         }
2840         return runs;
2841 }
2842
2843 /*
2844  *  Resolves text direction. If the nominal direction is LTR or RTL, no
2845  *  resolution is needed; if the nominal direction is "auto", have BidiUtil
2846  *  resolve it according to the first strong bidi character.
2847  */
2848 int resolveTextDirection () {
2849         return textDirection == SWT.AUTO_TEXT_DIRECTION ? BidiUtil.resolveTextDirection (text) : textDirection;
2850 }
2851
2852 /*
2853  *  Reorder the run
2854  */
2855 StyleItem[] reorder (StyleItem[] runs, boolean terminate) {
2856         int length = runs.length;
2857         if (length <= 1) return runs;
2858         byte[] bidiLevels = new byte[length];
2859         for (int i=0; i<length; i++) {
2860                 bidiLevels[i] = (byte)(runs[i].analysis.s.uBidiLevel & 0x1F);
2861         }
2862         /*
2863         * Feature in Windows.  If the orientation is RTL Uniscribe will
2864         * resolve the level of line breaks to 1, this can cause the line
2865         * break to be reorder to the middle of the line. The fix is to set
2866         * the level to zero to prevent it to be reordered.
2867         */
2868         StyleItem lastRun = runs[length - 1];
2869         if (lastRun.lineBreak && !lastRun.softBreak) {
2870                 bidiLevels[length - 1] = 0;
2871         }
2872         int[] log2vis = new int[length];
2873         OS.ScriptLayout(length, bidiLevels, null, log2vis);
2874         StyleItem[] result = new StyleItem[length];
2875         for (int i=0; i<length; i++) {
2876                 result[log2vis[i]] = runs[i];
2877         }
2878         if ((orientation & SWT.RIGHT_TO_LEFT) != 0) {
2879                 if (terminate) length--;
2880                 for (int i = 0; i < length / 2 ; i++) {
2881                         StyleItem tmp = result[i];
2882                         result[i] = result[length - i - 1];
2883                         result[length - i - 1] = tmp;
2884                 }
2885         }
2886         return result;
2887 }
2888
2889 /**
2890  * Sets the text alignment for the receiver. The alignment controls
2891  * how a line of text is positioned horizontally. The argument should
2892  * be one of <code>SWT.LEFT</code>, <code>SWT.RIGHT</code> or <code>SWT.CENTER</code>.
2893  * <p>
2894  * The default alignment is <code>SWT.LEFT</code>.  Note that the receiver's
2895  * width must be set in order to use <code>SWT.RIGHT</code> or <code>SWT.CENTER</code>
2896  * alignment.
2897  * </p>
2898  *
2899  * @param alignment the new alignment
2900  *
2901  * @exception SWTException <ul>
2902  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2903  * </ul>
2904  *
2905  * @see #setWidth(int)
2906  */
2907 public void setAlignment (int alignment) {
2908         checkLayout();
2909         int mask = SWT.LEFT | SWT.CENTER | SWT.RIGHT;
2910         alignment &= mask;
2911         if (alignment == 0) return;
2912         if ((alignment & SWT.LEFT) != 0) alignment = SWT.LEFT;
2913         if ((alignment & SWT.RIGHT) != 0) alignment = SWT.RIGHT;
2914         if (this.alignment == alignment) return;
2915         freeRuns();
2916         this.alignment = alignment;
2917 }
2918
2919 /**
2920  * Sets the ascent of the receiver. The ascent is distance in points
2921  * from the baseline to the top of the line and it is applied to all
2922  * lines. The default value is <code>-1</code> which means that the
2923  * ascent is calculated from the line fonts.
2924  *
2925  * @param ascent the new ascent
2926  *
2927  * @exception IllegalArgumentException <ul>
2928  *    <li>ERROR_INVALID_ARGUMENT - if the ascent is less than <code>-1</code></li>
2929  * </ul>
2930  * @exception SWTException <ul>
2931  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2932  * </ul>
2933  *
2934  * @see #setDescent(int)
2935  * @see #getLineMetrics(int)
2936  */
2937 public void setAscent (int ascent) {
2938         checkLayout();
2939         if (ascent < -1) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2940         ascent = DPIUtil.autoScaleUp(getDevice(), ascent);
2941         if (this.ascentInPixels == ascent) return;
2942         freeRuns();
2943         this.ascentInPixels = ascent;
2944 }
2945
2946 /**
2947  * Sets the descent of the receiver. The descent is distance in points
2948  * from the baseline to the bottom of the line and it is applied to all
2949  * lines. The default value is <code>-1</code> which means that the
2950  * descent is calculated from the line fonts.
2951  *
2952  * @param descent the new descent
2953  *
2954  * @exception IllegalArgumentException <ul>
2955  *    <li>ERROR_INVALID_ARGUMENT - if the descent is less than <code>-1</code></li>
2956  * </ul>
2957  * @exception SWTException <ul>
2958  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2959  * </ul>
2960  *
2961  * @see #setAscent(int)
2962  * @see #getLineMetrics(int)
2963  */
2964 public void setDescent (int descent) {
2965         checkLayout();
2966         if (descent < -1) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2967         descent = DPIUtil.autoScaleUp(getDevice(), descent);
2968         if (this.descentInPixels == descent) return;
2969         freeRuns();
2970         this.descentInPixels = descent;
2971 }
2972
2973 /**
2974  * Sets the default font which will be used by the receiver
2975  * to draw and measure text. If the
2976  * argument is null, then a default font appropriate
2977  * for the platform will be used instead. Note that a text
2978  * style can override the default font.
2979  *
2980  * @param font the new font for the receiver, or null to indicate a default font
2981  *
2982  * @exception IllegalArgumentException <ul>
2983  *    <li>ERROR_INVALID_ARGUMENT - if the font has been disposed</li>
2984  * </ul>
2985  * @exception SWTException <ul>
2986  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2987  * </ul>
2988  */
2989 public void setFont (Font font) {
2990         checkLayout();
2991         if (font != null && font.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2992         Font oldFont = this.font;
2993         if (oldFont == font) return;
2994         this.font = font;
2995         if (oldFont != null && oldFont.equals(font)) return;
2996         freeRuns();
2997 }
2998
2999 /**
3000  * Sets the indent of the receiver. This indent is applied to the first line of
3001  * each paragraph.
3002  *
3003  * @param indent new indent
3004  *
3005  * @exception SWTException <ul>
3006  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3007  * </ul>
3008  *
3009  * @see #setWrapIndent(int)
3010  *
3011  * @since 3.2
3012  */
3013 public void setIndent (int indent) {
3014         checkLayout();
3015         setIndentInPixels(DPIUtil.autoScaleUp(getDevice(), indent));
3016 }
3017
3018 void setIndentInPixels (int indent) {
3019         if (indent < 0) return;
3020         if (this.indent == indent) return;
3021         freeRuns();
3022         this.indent = indent;
3023 }
3024
3025 /**
3026  * Sets the justification of the receiver. Note that the receiver's
3027  * width must be set in order to use justification.
3028  *
3029  * @param justify new justify
3030  *
3031  * @exception SWTException <ul>
3032  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3033  * </ul>
3034  *
3035  * @since 3.2
3036  */
3037 public void setJustify (boolean justify) {
3038         checkLayout();
3039         if (this.justify == justify) return;
3040         freeRuns();
3041         this.justify = justify;
3042 }
3043
3044 /**
3045  * Sets the orientation of the receiver, which must be one
3046  * of <code>SWT.LEFT_TO_RIGHT</code> or <code>SWT.RIGHT_TO_LEFT</code>.
3047  *
3048  * @param orientation new orientation style
3049  *
3050  * @exception SWTException <ul>
3051  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3052  * </ul>
3053  */
3054 public void setOrientation (int orientation) {
3055         checkLayout();
3056         int mask = SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT;
3057         orientation &= mask;
3058         if (orientation == 0) return;
3059         if ((orientation & SWT.LEFT_TO_RIGHT) != 0) orientation = SWT.LEFT_TO_RIGHT;
3060         if (this.orientation == orientation) return;
3061         textDirection = this.orientation = orientation;
3062         freeRuns();
3063 }
3064
3065 /**
3066  * Sets the offsets of the receiver's text segments. Text segments are used to
3067  * override the default behavior of the bidirectional algorithm.
3068  * Bidirectional reordering can happen within a text segment but not
3069  * between two adjacent segments.
3070  * <p>
3071  * Each text segment is determined by two consecutive offsets in the
3072  * <code>segments</code> arrays. The first element of the array should
3073  * always be zero and the last one should always be equals to length of
3074  * the text.
3075  * </p>
3076  * <p>
3077  * When segments characters are set, the segments are the offsets where
3078  * the characters are inserted in the text.
3079  * <p>
3080  *
3081  * @param segments the text segments offset
3082  *
3083  * @exception SWTException <ul>
3084  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3085  * </ul>
3086  *
3087  * @see #setSegmentsChars(char[])
3088  */
3089 public void setSegments(int[] segments) {
3090         checkLayout();
3091         if (this.segments == null && segments == null) return;
3092         if (this.segments != null && segments != null) {
3093                 if (this.segments.length == segments.length) {
3094                         int i;
3095                         for (i = 0; i <segments.length; i++) {
3096                                 if (this.segments[i] != segments[i]) break;
3097                         }
3098                         if (i == segments.length) return;
3099                 }
3100         }
3101         freeRuns();
3102         this.segments = segments;
3103 }
3104
3105 /**
3106  * Sets the characters to be used in the segments boundaries. The segments
3107  * are set by calling <code>setSegments(int[])</code>. The application can
3108  * use this API to insert Unicode Control Characters in the text to control
3109  * the display of the text and bidi reordering. The characters are not
3110  * accessible by any other API in <code>TextLayout</code>.
3111  *
3112  * @param segmentsChars the segments characters
3113  *
3114  * @exception SWTException <ul>
3115  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3116  * </ul>
3117  *
3118  * @see #setSegments(int[])
3119  *
3120  * @since 3.6
3121  */
3122 public void setSegmentsChars(char[] segmentsChars) {
3123         checkLayout();
3124         if (this.segmentsChars == null && segmentsChars == null) return;
3125         if (this.segmentsChars != null && segmentsChars != null) {
3126                 if (this.segmentsChars.length == segmentsChars.length) {
3127                         int i;
3128                         for (i = 0; i <segmentsChars.length; i++) {
3129                                 if (this.segmentsChars[i] != segmentsChars[i]) break;
3130                         }
3131                         if (i == segmentsChars.length) return;
3132                 }
3133         }
3134         freeRuns();
3135         this.segmentsChars = segmentsChars;
3136 }
3137
3138 /**
3139  * Sets the line spacing of the receiver.  The line spacing
3140  * is the space left between lines.
3141  *
3142  * @param spacing the new line spacing
3143  *
3144  * @exception IllegalArgumentException <ul>
3145  *    <li>ERROR_INVALID_ARGUMENT - if the spacing is negative</li>
3146  * </ul>
3147  * @exception SWTException <ul>
3148  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3149  * </ul>
3150  */
3151 public void setSpacing (int spacing) {
3152         checkLayout();
3153         if (spacing < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3154         if (this.lineSpacingInPoints == spacing) return;
3155         freeRuns();
3156         this.lineSpacingInPoints = spacing;
3157 }
3158
3159 /**
3160  * Sets the vertical indent of the receiver.  The vertical indent
3161  * is the space left before the first line.
3162  *
3163  * @param verticalIndent the new vertical indent
3164  *
3165  * @exception IllegalArgumentException <ul>
3166  *    <li>ERROR_INVALID_ARGUMENT - if the vertical indent is negative</li>
3167  * </ul>
3168  * @exception SWTException <ul>
3169  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3170  * </ul>
3171  * @since 3.109
3172  */
3173 public void setVerticalIndent (int verticalIndent) {
3174         checkLayout();
3175         if (verticalIndent < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3176         if (this.verticalIndentInPoints == verticalIndent) return;
3177         this.verticalIndentInPoints = verticalIndent;
3178 }
3179
3180 /**
3181  * Sets the style of the receiver for the specified range.  Styles previously
3182  * set for that range will be overwritten.  The start and end offsets are
3183  * inclusive and will be clamped if out of range.
3184  *
3185  * @param style the style
3186  * @param start the start offset
3187  * @param end the end offset
3188  *
3189  * @exception SWTException <ul>
3190  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3191  * </ul>
3192  */
3193 public void setStyle (TextStyle style, int start, int end) {
3194         checkLayout();
3195         int length = text.length();
3196         if (length == 0) return;
3197         if (start > end) return;
3198         start = Math.min(Math.max(0, start), length - 1);
3199         end = Math.min(Math.max(0, end), length - 1);
3200         int low = -1;
3201         int high = stylesCount;
3202         while (high - low > 1) {
3203                 int index = (high + low) / 2;
3204                 if (styles[index + 1].start > start) {
3205                         high = index;
3206                 } else {
3207                         low = index;
3208                 }
3209         }
3210         if (0 <= high && high < stylesCount) {
3211                 StyleItem item = styles[high];
3212                 if (item.start == start && styles[high + 1].start - 1 == end) {
3213                         if (style == null) {
3214                                 if (item.style == null) return;
3215                         } else {
3216                                 if (style.equals(item.style)) return;
3217                         }
3218                 }
3219         }
3220         freeRuns();
3221         int modifyStart = high;
3222         int modifyEnd = modifyStart;
3223         while (modifyEnd < stylesCount) {
3224                 if (styles[modifyEnd + 1].start > end) break;
3225                 modifyEnd++;
3226         }
3227         if (modifyStart == modifyEnd) {
3228                 int styleStart = styles[modifyStart].start;
3229                 int styleEnd = styles[modifyEnd + 1].start - 1;
3230                 if (styleStart == start && styleEnd == end) {
3231                         styles[modifyStart].style = style;
3232                         return;
3233                 }
3234                 if (styleStart != start && styleEnd != end) {
3235                         int newLength = stylesCount + 2;
3236                         if (newLength > styles.length) {
3237                                 int newSize = Math.min(newLength + 1024, Math.max(64, newLength * 2));
3238                                 StyleItem[] newStyles = new StyleItem[newSize];
3239                                 System.arraycopy(styles, 0, newStyles, 0, stylesCount);
3240                                 styles = newStyles;
3241                         }
3242                         System.arraycopy(styles, modifyEnd + 1, styles, modifyEnd + 3, stylesCount - modifyEnd - 1);
3243                         StyleItem item = new StyleItem();
3244                         item.start = start;
3245                         item.style = style;
3246                         styles[modifyStart + 1] = item;
3247                         item = new StyleItem();
3248                         item.start = end + 1;
3249                         item.style = styles[modifyStart].style;
3250                         styles[modifyStart + 2] = item;
3251                         stylesCount = newLength;
3252                         return;
3253                 }
3254         }
3255         if (start == styles[modifyStart].start) modifyStart--;
3256         if (end == styles[modifyEnd + 1].start - 1) modifyEnd++;
3257         int newLength = stylesCount + 1 - (modifyEnd - modifyStart - 1);
3258         if (newLength > styles.length) {
3259                 int newSize = Math.min(newLength + 1024, Math.max(64, newLength * 2));
3260                 StyleItem[] newStyles = new StyleItem[newSize];
3261                 System.arraycopy(styles, 0, newStyles, 0, stylesCount);
3262                 styles = newStyles;
3263         }
3264         System.arraycopy(styles, modifyEnd, styles, modifyStart + 2, stylesCount - modifyEnd);
3265         StyleItem item = new StyleItem();
3266         item.start = start;
3267         item.style = style;
3268         styles[modifyStart + 1] = item;
3269         styles[modifyStart + 2].start = end + 1;
3270         stylesCount = newLength;
3271 }
3272
3273 /**
3274  * Sets the receiver's tab list. Each value in the tab list specifies
3275  * the space in points from the origin of the text layout to the respective
3276  * tab stop.  The last tab stop width is repeated continuously.
3277  *
3278  * @param tabs the new tab list
3279  *
3280  * @exception SWTException <ul>
3281  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3282  * </ul>
3283  */
3284 public void setTabs (int[] tabs) {
3285         checkLayout();
3286         if (this.tabs == null && tabs == null) return;
3287         setTabsInPixels (DPIUtil.autoScaleUp (getDevice(), tabs));
3288 }
3289
3290 void setTabsInPixels (int[] tabs) {
3291         if (Arrays.equals (this.tabs, tabs)) return;
3292         freeRuns();
3293         this.tabs = tabs;
3294 }
3295
3296 /**
3297  * Sets the receiver's text.
3298  *<p>
3299  * Note: Setting the text also clears all the styles. This method
3300  * returns without doing anything if the new text is the same as
3301  * the current text.
3302  * </p>
3303  *
3304  * @param text the new text
3305  *
3306  * @exception IllegalArgumentException <ul>
3307  *    <li>ERROR_NULL_ARGUMENT - if the text is null</li>
3308  * </ul>
3309  * @exception SWTException <ul>
3310  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3311  * </ul>
3312  */
3313 public void setText (String text) {
3314         checkLayout();
3315         if (text == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
3316         if (text.equals(this.text)) return;
3317         freeRuns();
3318         this.text = text;
3319         styles = new StyleItem[2];
3320         styles[0] = new StyleItem();
3321         styles[1] = new StyleItem();
3322         styles[1].start = text.length();
3323         stylesCount = 2;
3324 }
3325
3326 /**
3327  * Sets the text direction of the receiver, which must be one
3328  * of <code>SWT.LEFT_TO_RIGHT</code>, <code>SWT.RIGHT_TO_LEFT</code>
3329  * or <code>SWT.AUTO_TEXT_DIRECTION</code>.
3330  *
3331  * <p>
3332  * <b>Warning</b>: This API is currently only implemented on Windows.
3333  * It doesn't set the base text direction on GTK and Cocoa.
3334  * </p>
3335  *
3336  * @param textDirection the new text direction
3337  *
3338  * @exception SWTException <ul>
3339  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3340  * </ul>
3341  * @since 3.103
3342  */
3343 public void setTextDirection (int textDirection) {
3344         checkLayout();
3345         int mask = SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT;
3346         textDirection &= mask;
3347         if (textDirection == 0) return;
3348         if (textDirection != SWT.AUTO_TEXT_DIRECTION) {
3349                 if ((textDirection & SWT.LEFT_TO_RIGHT) != 0) textDirection = SWT.LEFT_TO_RIGHT;
3350                 if (this.textDirection == textDirection) return;
3351         }
3352         this.textDirection = textDirection;
3353         freeRuns();
3354 }
3355
3356 /**
3357  * Sets the line width of the receiver, which determines how
3358  * text should be wrapped and aligned. The default value is
3359  * <code>-1</code> which means wrapping is disabled.
3360  *
3361  * @param width the new width
3362  *
3363  * @exception IllegalArgumentException <ul>
3364  *    <li>ERROR_INVALID_ARGUMENT - if the width is <code>0</code> or less than <code>-1</code></li>
3365  * </ul>
3366  * @exception SWTException <ul>
3367  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3368  * </ul>
3369  *
3370  * @see #setAlignment(int)
3371  */
3372 public void setWidth (int width) {
3373         checkLayout();
3374         setWidthInPixels(width != SWT.DEFAULT ? DPIUtil.autoScaleUp(getDevice(), width) : width);
3375 }
3376
3377 void setWidthInPixels (int width) {
3378         if (width < -1 || width == 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3379         if (this.wrapWidth == width) return;
3380         freeRuns();
3381         this.wrapWidth = width;
3382 }
3383
3384 /**
3385  * Sets the wrap indent of the receiver. This indent is applied to all lines
3386  * in the paragraph except the first line.
3387  *
3388  * @param wrapIndent new wrap indent
3389  *
3390  * @exception SWTException <ul>
3391  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3392  * </ul>
3393  *
3394  * @see #setIndent(int)
3395  *
3396  * @since 3.6
3397  */
3398 public void setWrapIndent (int wrapIndent) {
3399         checkLayout();
3400         setWrapIndentInPixels(DPIUtil.autoScaleUp(getDevice(), wrapIndent));
3401 }
3402
3403 void setWrapIndentInPixels (int wrapIndent) {
3404         if (wrapIndent < 0) return;
3405         if (this.wrapIndent == wrapIndent) return;
3406         freeRuns();
3407         this.wrapIndent = wrapIndent;
3408 }
3409
3410 boolean shape (long hdc, StyleItem run, char[] chars, int[] glyphCount, int maxGlyphs, SCRIPT_PROPERTIES sp) {
3411         boolean useCMAPcheck = !sp.fComplex && !run.analysis.fNoGlyphIndex;
3412         if (useCMAPcheck) {
3413                 short[] glyphs = new short[chars.length];
3414                 if (OS.ScriptGetCMap(hdc, run.psc, chars, chars.length, 0, glyphs) != OS.S_OK) {
3415                         if (run.psc != 0) {
3416                                 OS.ScriptFreeCache(run.psc);
3417                                 glyphCount[0] = 0;
3418                                 OS.MoveMemory(run.psc, new long [1], C.PTR_SIZEOF);
3419                         }
3420                         return false;
3421                 }
3422         }
3423         int hr = OS.ScriptShape(hdc, run.psc, chars, chars.length, maxGlyphs, run.analysis, run.glyphs, run.clusters, run.visAttrs, glyphCount);
3424         run.glyphCount = glyphCount[0];
3425         if (useCMAPcheck) return true;
3426
3427         if (hr != OS.USP_E_SCRIPT_NOT_IN_FONT) {
3428                 if (run.analysis.fNoGlyphIndex) return true;
3429                 SCRIPT_FONTPROPERTIES fp = new SCRIPT_FONTPROPERTIES ();
3430                 fp.cBytes = SCRIPT_FONTPROPERTIES.sizeof;
3431                 OS.ScriptGetFontProperties(hdc, run.psc, fp);
3432                 short[] glyphs = new short[glyphCount[0]];
3433                 OS.MoveMemory(glyphs, run.glyphs, glyphs.length * 2);
3434                 int i;
3435                 for (i = 0; i < glyphs.length; i++) {
3436                         if (glyphs[i] == fp.wgDefault) break;
3437                 }
3438                 if (i == glyphs.length) return true;
3439         }
3440         if (run.psc != 0) {
3441                 OS.ScriptFreeCache(run.psc);
3442                 glyphCount[0] = 0;
3443                 OS.MoveMemory(run.psc, new long [1], C.PTR_SIZEOF);
3444         }
3445         run.glyphCount = 0;
3446         return false;
3447 }
3448
3449 long createMetafileWithChars(long hdc, long hFont, char[] chars, int charCount) {
3450         long hHeap = OS.GetProcessHeap();
3451
3452         /*
3453          * The native string must remain unchanged between ScriptStringAnalyse and ScriptStringOut.
3454          * According to debugging, ScriptStringAnalyse implicitly saves string to SCRIPT_STRING_ANALYSIS.
3455          * Then, ScriptStringOut uses the saved string in internal call to ExtTextOutW.
3456          * I believe this is due to OS.SSA_METAFILE, which is documented as follows:
3457          *     Write items with ExtTextOutW calls, not with glyphs.
3458          * Note: passing Java chars to native function is wrong, because JNI will allocate
3459          * temporary native string which will be deallocated upon return from ScriptStringAnalyse.
3460          */
3461         int nativeStringSize = charCount * Character.BYTES;
3462         long nativeString = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, nativeStringSize);
3463         OS.MoveMemory (nativeString, chars, nativeStringSize);
3464
3465         long ssa = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, OS.SCRIPT_STRING_ANALYSIS_sizeof());
3466         long metaFileDc = OS.CreateEnhMetaFile(hdc, null, null, null);
3467         long oldMetaFont = OS.SelectObject(metaFileDc, hFont);
3468         int flags = OS.SSA_METAFILE | OS.SSA_FALLBACK | OS.SSA_GLYPHS | OS.SSA_LINK;
3469         if (OS.ScriptStringAnalyse(metaFileDc, nativeString, charCount, 0, -1, flags, 0, null, null, 0, 0, 0, ssa) == OS.S_OK) {
3470                 OS.ScriptStringOut(ssa, 0, 0, 0, null, 0, 0, false);
3471                 OS.ScriptStringFree(ssa);
3472         }
3473         OS.HeapFree(hHeap, 0, nativeString);
3474         OS.HeapFree(hHeap, 0, ssa);
3475         OS.SelectObject(metaFileDc, oldMetaFont);
3476         return OS.CloseEnhMetaFile(metaFileDc);
3477 }
3478
3479 /*
3480  * Generate glyphs for one Run.
3481  */
3482 void shape (final long hdc, final StyleItem run) {
3483         if (run.lineBreak) return;
3484         if (run.glyphs != 0) return;
3485         final int[] buffer = new int[1];
3486         final char[] chars = new char[run.length];
3487         segmentsText.getChars(run.start, run.start + run.length, chars, 0);
3488
3489         final int maxGlyphs = (chars.length * 3 / 2) + 16;
3490         long hHeap = OS.GetProcessHeap();
3491         run.glyphs = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, maxGlyphs * 2);
3492         if (run.glyphs == 0) SWT.error(SWT.ERROR_NO_HANDLES);
3493         run.clusters = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, maxGlyphs * 2);
3494         if (run.clusters == 0) SWT.error(SWT.ERROR_NO_HANDLES);
3495         run.visAttrs = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, maxGlyphs * SCRIPT_VISATTR_SIZEOF);
3496         if (run.visAttrs == 0) SWT.error(SWT.ERROR_NO_HANDLES);
3497         run.psc = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, C.PTR_SIZEOF);
3498         if (run.psc == 0) SWT.error(SWT.ERROR_NO_HANDLES);
3499         final short script = run.analysis.eScript;
3500         final SCRIPT_PROPERTIES sp = new SCRIPT_PROPERTIES();
3501         OS.MoveMemory(sp, device.scripts[script], SCRIPT_PROPERTIES.sizeof);
3502         boolean shapeSucceed = shape(hdc, run, chars, buffer,  maxGlyphs, sp);
3503         if (!shapeSucceed) {
3504                 if (sp.fPrivateUseArea) {
3505                         run.analysis.fNoGlyphIndex = true;
3506                         shapeSucceed = shape(hdc, run, chars, buffer,  maxGlyphs, sp);
3507                 }
3508         }
3509         if (!shapeSucceed) {
3510                 long hFont = OS.GetCurrentObject(hdc, OS.OBJ_FONT);
3511                 long newFont = 0;
3512                 /*
3513                 * Bug in Uniscribe. In some version of Uniscribe, ScriptStringAnalyse crashes
3514                 * when the character array is too long. The fix is to limit the size of character
3515                 * array to two. Note, limiting the array to only one character would cause surrogate
3516                 * pairs to stop working.
3517                 */
3518                 char[] sampleChars = new char[Math.min(chars.length, 2)];
3519                 SCRIPT_LOGATTR logAttr = new SCRIPT_LOGATTR();
3520                 breakRun(run);
3521                 int count = 0;
3522                 for (int i = 0; i < chars.length; i++) {
3523                         OS.MoveMemory(logAttr, run.psla + (i * SCRIPT_LOGATTR.sizeof), SCRIPT_LOGATTR.sizeof);
3524                         if (!logAttr.fWhiteSpace) {
3525                                 sampleChars[count++] = chars[i];
3526                                 if (count == sampleChars.length) break;
3527                         }
3528                 }
3529                 if (count > 0) {
3530                         long metaFile = createMetafileWithChars(hdc, hFont, sampleChars, count);
3531                         final EMREXTCREATEFONTINDIRECTW emr = new EMREXTCREATEFONTINDIRECTW();
3532                         class MetaFileEnumProc {
3533                                 long metaFileEnumProc (long hDC, long table, long record, long nObj, long lpData) {
3534                                         OS.MoveMemory(emr.emr, record, EMR.sizeof);
3535                                         switch (emr.emr.iType) {
3536                                                 case OS.EMR_EXTCREATEFONTINDIRECTW:
3537                                                         OS.MoveMemory(emr, record, EMREXTCREATEFONTINDIRECTW.sizeof);
3538                                                         break;
3539                                                 case OS.EMR_EXTTEXTOUTW:
3540                                                         return 0;
3541                                         }
3542                                         return 1;
3543                                 }
3544                         }
3545                         MetaFileEnumProc object = new MetaFileEnumProc();
3546                         /* Avoid compiler warnings */
3547                         boolean compilerWarningWorkaround = false;
3548                         if (compilerWarningWorkaround) object.metaFileEnumProc(0, 0, 0, 0, 0);
3549                         Callback callback = new Callback(object, "metaFileEnumProc", 5);
3550                         long address = callback.getAddress();
3551                         if (address == 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS);
3552                         OS.EnumEnhMetaFile(0, metaFile, address, 0, null);
3553                         OS.DeleteEnhMetaFile(metaFile);
3554                         callback.dispose();
3555                         newFont = OS.CreateFontIndirect(emr.elfw.elfLogFont);
3556                 } else {
3557                         /*
3558                         * The run is composed only by white spaces, this happens when a run is split
3559                         * by a visual style. The font fallback for the script can not be determined
3560                         * using only white spaces. The solution is to use the font of the previous
3561                         * or next run of the same script.
3562                         */
3563                         int index = 0;
3564                         while (index < allRuns.length - 1) {
3565                                 if (allRuns[index] == run) {
3566                                         if (index > 0) {
3567                                                 StyleItem pRun = allRuns[index - 1];
3568                                                 if (pRun.analysis.eScript == run.analysis.eScript) {
3569                                                         long pFont = getItemFont(pRun);
3570                                                         LOGFONT logFont = new LOGFONT ();
3571                                                         OS.GetObject(pFont, LOGFONT.sizeof, logFont);
3572                                                         newFont = OS.CreateFontIndirect(logFont);
3573                                                 }
3574                                         }
3575                                         if (newFont == 0) {
3576                                                 if (index + 1 < allRuns.length - 1) {
3577                                                         StyleItem nRun = allRuns[index + 1];
3578                                                         if (nRun.analysis.eScript == run.analysis.eScript) {
3579                                                                 OS.SelectObject(hdc, getItemFont(nRun));
3580                                                                 shape(hdc, nRun);
3581                                                                 long nFont = getItemFont(nRun);
3582                                                                 LOGFONT logFont = new LOGFONT ();
3583                                                                 OS.GetObject(nFont, LOGFONT.sizeof, logFont);
3584                                                                 newFont = OS.CreateFontIndirect(logFont);
3585                                                         }
3586                                                 }
3587                                         }
3588                                         break;
3589                                 }
3590                                 index++;
3591                         }
3592                 }
3593                 if (newFont != 0) {
3594                         OS.SelectObject(hdc, newFont);
3595                         if (shapeSucceed = shape(hdc, run, chars, buffer,  maxGlyphs, sp)) {
3596                                 run.fallbackFont = newFont;
3597                         }
3598                 }
3599                 if (!shapeSucceed) {
3600                         if (!sp.fComplex) {
3601                                 run.analysis.fNoGlyphIndex = true;
3602                                 if (shapeSucceed = shape(hdc, run, chars, buffer,  maxGlyphs, sp)) {
3603                                         run.fallbackFont = newFont;
3604                                 } else {
3605                                         run.analysis.fNoGlyphIndex = false;
3606                                 }
3607                         }
3608                 }
3609                 if (!shapeSucceed) {
3610                         if (mLangFontLink2 != null) {
3611                                 long [] hNewFont = new long [1];
3612                                 int[] dwCodePages = new int[1], cchCodePages = new int[1];
3613                                 mLangFontLink2.GetStrCodePages(chars, chars.length, 0, dwCodePages, cchCodePages);
3614                                 if (mLangFontLink2.MapFont(hdc, dwCodePages[0], chars[0], hNewFont) == OS.S_OK) {
3615                                         LOGFONT logFont = new LOGFONT ();
3616                                         OS.GetObject(hNewFont[0], LOGFONT.sizeof, logFont);
3617                                         mLangFontLink2.ReleaseFont(hNewFont[0]);
3618                                         long mLangFont = OS.CreateFontIndirect(logFont);
3619                                         long oldFont = OS.SelectObject(hdc, mLangFont);
3620                                         if (shapeSucceed = shape(hdc, run, chars, buffer,  maxGlyphs, sp)) {
3621                                                 run.fallbackFont = mLangFont;
3622                                         } else {
3623                                                 OS.SelectObject(hdc, oldFont);
3624                                                 OS.DeleteObject(mLangFont);
3625                                         }
3626                                 }
3627                         }
3628                 }
3629                 if (!shapeSucceed) OS.SelectObject(hdc, hFont);
3630                 if (newFont != 0 && newFont != run.fallbackFont) OS.DeleteObject(newFont);
3631         }
3632
3633         if (!shapeSucceed) {
3634                 /*
3635                 * Shape Failed.
3636                 * Give up and shape the run with the default font.
3637                 * Missing glyphs typically will be represent as black boxes in the text.
3638                 */
3639                 OS.ScriptShape(hdc, run.psc, chars, chars.length, maxGlyphs, run.analysis, run.glyphs, run.clusters, run.visAttrs, buffer);
3640                 run.glyphCount = buffer[0];
3641         }
3642         int[] abc = new int[3];
3643         run.advances = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, run.glyphCount * 4);
3644         if (run.advances == 0) SWT.error(SWT.ERROR_NO_HANDLES);
3645         run.goffsets = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, run.glyphCount * GOFFSET_SIZEOF);
3646         if (run.goffsets == 0) SWT.error(SWT.ERROR_NO_HANDLES);
3647         OS.ScriptPlace(hdc, run.psc, run.glyphs, run.glyphCount, run.visAttrs, run.analysis, run.advances, run.goffsets, abc);
3648         run.width = abc[0] + abc[1] + abc[2];
3649         TextStyle style = run.style;
3650         if (style != null) {
3651                 OUTLINETEXTMETRIC lotm = null;
3652                 if (style.underline || style.strikeout) {
3653                         lotm = new OUTLINETEXTMETRIC();
3654                         if (OS.GetOutlineTextMetrics(hdc, OUTLINETEXTMETRIC.sizeof, lotm) == 0) {
3655                                 lotm = null;
3656                         }
3657                 }
3658                 if (style.metrics != null) {
3659                         GlyphMetrics metrics = style.metrics;
3660                         /*
3661                          *  Bug in Windows, on a Japanese machine, Uniscribe returns glyphcount
3662                          *  equals zero for FFFC (possibly other unicode code points), the fix
3663                          *  is to make sure the glyph is at least one pixel wide.
3664                          */
3665                         run.width = metrics.getWidthInPixels() * Math.max (1, run.glyphCount);
3666                         run.ascentInPoints = metrics.ascent;
3667                         run.descentInPoints = metrics.descent;
3668                         run.leadingInPoints = 0;
3669                 } else {
3670                         TEXTMETRIC lptm = null;
3671                         if (lotm != null) {
3672                                 lptm = lotm.otmTextMetrics;
3673                         } else {
3674                                 lptm = new TEXTMETRIC();
3675                                 OS.GetTextMetrics(hdc, lptm);
3676                         }
3677                         run.ascentInPoints = DPIUtil.autoScaleDown(getDevice(), lptm.tmAscent);
3678                         run.descentInPoints = DPIUtil.autoScaleDown(getDevice(), lptm.tmDescent);
3679                         run.leadingInPoints = DPIUtil.autoScaleDown(getDevice(), lptm.tmInternalLeading);
3680                 }
3681                 if (lotm != null) {
3682                         run.underlinePos = lotm.otmsUnderscorePosition;
3683                         run.underlineThickness = Math.max(1, lotm.otmsUnderscoreSize);
3684                         run.strikeoutPos = lotm.otmsStrikeoutPosition;
3685                         run.strikeoutThickness = Math.max(1, lotm.otmsStrikeoutSize);
3686                 } else {
3687                         run.underlinePos = 1;
3688                         run.underlineThickness = 1;
3689                         run.strikeoutPos = DPIUtil.autoScaleUp(getDevice(), run.ascentInPoints) / 2;
3690                         run.strikeoutThickness = 1;
3691                 }
3692                 run.ascentInPoints += style.rise;
3693                 run.descentInPoints -= style.rise;
3694         } else {
3695                 TEXTMETRIC lptm = new TEXTMETRIC();
3696                 OS.GetTextMetrics(hdc, lptm);
3697                 run.ascentInPoints = DPIUtil.autoScaleDown(getDevice(), lptm.tmAscent);
3698                 run.descentInPoints = DPIUtil.autoScaleDown(getDevice(), lptm.tmDescent);
3699                 run.leadingInPoints = DPIUtil.autoScaleDown(getDevice(), lptm.tmInternalLeading);
3700         }
3701 }
3702
3703 int validadeOffset(int offset, int step) {
3704         offset = untranslateOffset(offset);
3705         return translateOffset(offset + step);
3706 }
3707
3708 /**
3709  * Returns a string containing a concise, human-readable
3710  * description of the receiver.
3711  *
3712  * @return a string representation of the receiver
3713  */
3714 @Override
3715 public String toString () {
3716         if (isDisposed()) return "TextLayout {*DISPOSED*}";
3717         return "TextLayout {}";
3718 }
3719
3720 int translateOffset(int offset) {
3721         int length = text.length();
3722         if (length == 0) return offset;
3723         if (segments == null) return offset;
3724         int nSegments = segments.length;
3725         if (nSegments == 0) return offset;
3726         if (segmentsChars == null) {
3727                 if (nSegments == 1) return offset;
3728                 if (nSegments == 2) {
3729                         if (segments[0] == 0 && segments[1] == length) return offset;
3730                 }
3731         }
3732         for (int i = 0; i < nSegments && offset - i >= segments[i]; i++) {
3733                 offset++;
3734         }
3735         return offset;
3736 }
3737
3738 int untranslateOffset(int offset) {
3739         int length = text.length();
3740         if (length == 0) return offset;
3741         if (segments == null) return offset;
3742         int nSegments = segments.length;
3743         if (nSegments == 0) return offset;
3744         if (segmentsChars == null) {
3745                 if (nSegments == 1) return offset;
3746                 if (nSegments == 2) {
3747                         if (segments[0] == 0 && segments[1] == length) return offset;
3748                 }
3749         }
3750         for (int i = 0; i < nSegments && offset > segments[i]; i++) {
3751                 offset--;
3752         }
3753         return offset;
3754 }
3755
3756 /**
3757  * Sets Default Tab Width in terms if number of space characters.
3758  *
3759  * @param tabLength in number of characters
3760  *
3761  * @exception IllegalArgumentException <ul>
3762  *    <li>ERROR_INVALID_ARGUMENT - if the tabLength is less than <code>0</code></li>
3763  * </ul>
3764  * @exception SWTException <ul>
3765  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3766  * </ul>
3767  *
3768  * @noreference This method is not intended to be referenced by clients.
3769  *
3770  * DO NOT USE This might be removed in 4.8
3771  * @since 3.107
3772  */
3773 public void setDefaultTabWidth(int tabLength) {
3774         // unused in win32
3775 }
3776 }