1 /*******************************************************************************
2  * Copyright (c) 2000, 2020 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 
free()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
toString()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  */
TextLayout(Device device)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 
addClipRect(StyleItem run, RECT clipRect, RECT rect, int selectionStart, int selectionEnd)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 
breakRun(StyleItem run)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 
checkLayout()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 */
computeRuns(GC gc)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
destroy()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 
cloneScriptAnalysis(SCRIPT_ANALYSIS src)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 
computePolyline(int left, int top, int right, int bottom)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 
createGdipBrush(int pixel, int alpha)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 
createGdipBrush(Color color, int alpha)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  */
draw(GC gc, int x, int y)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 
drawInPixels(GC gc, int x, int y)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  */
draw(GC gc, int x, int y, int selectionStart, int selectionEnd, Color selectionForeground, Color selectionBackground)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 
drawInPixels(GC gc, int x, int y, int selectionStart, int selectionEnd, Color selectionForeground, Color selectionBackground)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  */
draw(GC gc, int x, int y, int selectionStart, int selectionEnd, Color selectionForeground, Color selectionBackground, int flags)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 
drawInPixels(GC gc, int x, int y, int selectionStart, int selectionEnd, Color selectionForeground, Color selectionBackground, int flags)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 (StyleItem run : lineRuns) {
728 			if (run.length == 0) continue;
729 			if (drawX > clip.x + clip.width) break;
730 			if (drawX + run.width >= clip.x) {
731 				if (!run.lineBreak || run.softBreak) {
732 					OS.SetRect(rect, drawX, drawY, drawX + run.width, drawY + lineHeight);
733 					if (gdip) {
734 						drawRunBackgroundGDIP(run, gdipGraphics, rect, selectionStart, selectionEnd, alpha, gdipSelBackground, hasSelection);
735 					} else {
736 						drawRunBackground(run, hdc, rect, selectionStart, selectionEnd, selBackground, hasSelection);
737 					}
738 				}
739 			}
740 			drawX += run.width;
741 		}
742 
743 		//Draw the text, underline, strikeout, and border of the runs in the line
744 		int baselineInPixels = Math.max(0, this.ascentInPixels);
745 		int lineUnderlinePos = 0;
746 		for (StyleItem run : lineRuns) {
747 			baselineInPixels = Math.max(baselineInPixels, DPIUtil.autoScaleUp(getDevice(), run.ascentInPoints));
748 			lineUnderlinePos = Math.min(lineUnderlinePos, run.underlinePos);
749 		}
750 		RECT borderClip = null, underlineClip = null, strikeoutClip = null, pRect = null;
751 		drawX = alignmentX;
752 		for (int i = 0; i < lineRuns.length; i++) {
753 			StyleItem run = lineRuns[i];
754 			TextStyle style = run.style;
755 			boolean hasAdorners = style != null && (style.underline || style.strikeout || style.borderStyle != SWT.NONE);
756 			if (run.length == 0) continue;
757 			if (drawX > clip.x + clip.width) break;
758 			if (drawX + run.width >= clip.x) {
759 				boolean skipTab = run.tab && !hasAdorners;
760 				if (!skipTab && (!run.lineBreak || run.softBreak) && !(style != null && style.metrics != null)) {
761 					OS.SetRect(rect, drawX, drawY, drawX + run.width, drawY + lineHeight);
762 					if (gdip) {
763 						long hFont = getItemFont(run);
764 						if (hFont != lastHFont) {
765 							lastHFont = hFont;
766 							if (gdipFont != 0) Gdip.Font_delete(gdipFont);
767 							long oldFont = OS.SelectObject(hdc, hFont);
768 							gdipFont = Gdip.Font_new(hdc, hFont);
769 							OS.SelectObject(hdc, oldFont);
770 							if (gdipFont == 0) SWT.error(SWT.ERROR_NO_HANDLES);
771 							if (!Gdip.Font_IsAvailable(gdipFont)) {
772 								Gdip.Font_delete(gdipFont);
773 								gdipFont = 0;
774 							}
775 						}
776 						long gdipFg = gdipForeground;
777 						if (style != null && style.underline && style.underlineStyle == SWT.UNDERLINE_LINK) {
778 							if (gdipLinkColor == 0) gdipLinkColor = createGdipBrush(linkColor, alpha);
779 							gdipFg = gdipLinkColor;
780 						}
781 						if (gdipFont != 0 && !run.analysis.fNoGlyphIndex) {
782 							pRect = drawRunTextGDIP(gdipGraphics, run, rect, gdipFont, baselineInPixels, gdipFg, gdipSelForeground, selectionStart, selectionEnd, alpha);
783 						} else {
784 							int fg = style != null && style.underline && style.underlineStyle == SWT.UNDERLINE_LINK ? linkColor : foreground;
785 							pRect = drawRunTextGDIPRaster(gdipGraphics, run, rect, baselineInPixels, fg, selForeground, selectionStart, selectionEnd);
786 						}
787 						underlineClip = drawUnderlineGDIP(gdipGraphics, x, drawY + baselineInPixels, lineUnderlinePos, drawY + lineHeight, lineRuns, i, gdipFg, gdipSelForeground, underlineClip, pRect, selectionStart, selectionEnd, alpha, clip);
788 						strikeoutClip = drawStrikeoutGDIP(gdipGraphics, x, drawY + baselineInPixels, lineRuns, i, gdipFg, gdipSelForeground, strikeoutClip, pRect, selectionStart, selectionEnd, alpha, clip);
789 						borderClip = drawBorderGDIP(gdipGraphics, x, drawY, lineHeight, lineRuns, i, gdipFg, gdipSelForeground, borderClip, pRect, selectionStart, selectionEnd, alpha, clip);
790 					}  else {
791 						int fg = style != null && style.underline && style.underlineStyle == SWT.UNDERLINE_LINK ? linkColor : foreground;
792 						pRect = drawRunText(hdc, run, rect, baselineInPixels, fg, selForeground, selectionStart, selectionEnd);
793 						underlineClip = drawUnderline(hdc, x, drawY + baselineInPixels, lineUnderlinePos, drawY + lineHeight, lineRuns, i, fg, selForeground, underlineClip, pRect, selectionStart, selectionEnd, clip);
794 						strikeoutClip = drawStrikeout(hdc, x, drawY + baselineInPixels, lineRuns, i, fg, selForeground, strikeoutClip, pRect, selectionStart, selectionEnd, clip);
795 						borderClip = drawBorder(hdc, x, drawY, lineHeight, lineRuns, i, fg, selForeground, borderClip, pRect,  selectionStart, selectionEnd, clip);
796 					}
797 				}
798 			}
799 			drawX += run.width;
800 		}
801 	}
802 	if (gdipSelBackground != 0) Gdip.SolidBrush_delete(gdipSelBackground);
803 	if (gdipSelForeground != 0) Gdip.SolidBrush_delete(gdipSelForeground);
804 	if (gdipLinkColor != 0) Gdip.SolidBrush_delete(gdipLinkColor);
805 	if (gdipFont != 0) Gdip.Font_delete(gdipFont);
806 	if (state != 0)	OS.RestoreDC(hdc, state);
807 	if (selBackground != 0) OS.DeleteObject (selBackground);
808 }
809 
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)810 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) {
811 	StyleItem run = line[index];
812 	TextStyle style = run.style;
813 	if (style == null) return null;
814 	if (style.borderStyle == SWT.NONE) return null;
815 	clipRect = addClipRect(run, clipRect, pRect, selectionStart, selectionEnd);
816 	boolean lastRunVisible = drawClip != null && (x + run.x + run.width) > (drawClip.x + drawClip.width);
817 	if (index + 1 >= line.length || lastRunVisible || !style.isAdherentBorder(line[index + 1].style)) {
818 		int left = run.x;
819 		int start = run.start;
820 		int end = run.start + run.length - 1;
821 		for (int i = index; i > 0 && style.isAdherentBorder(line[i - 1].style); i--) {
822 			left = line[i - 1].x;
823 			start = Math.min(start, line[i - 1].start);
824 			end = Math.max(end, line[i - 1].start + line[i - 1].length - 1);
825 		}
826 		boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1;
827 		boolean fullSelection = hasSelection && selectionStart <= start && end <= selectionEnd;
828 		if (style.borderColor != null) {
829 			color = style.borderColor.handle;
830 			clipRect = null;
831 		} else {
832 			if (fullSelection) {
833 				color = selectionColor;
834 				clipRect = null;
835 			} else {
836 				if (style.foreground != null) {
837 					color = style.foreground.handle;
838 				}
839 			}
840 		}
841 		int lineWidth = 1;
842 		int pattern = 1;
843 		int lineStyle = OS.PS_SOLID;
844 		switch (style.borderStyle) {
845 			case SWT.BORDER_SOLID: break;
846 			case SWT.BORDER_DASH: {
847 				lineStyle = OS.PS_DASH;
848 				pattern = 4;
849 				break;
850 			}
851 			case SWT.BORDER_DOT: {
852 				lineStyle = OS.PS_DOT;
853 				pattern = 2;
854 				break;
855 			}
856 		}
857 		long oldBrush = OS.SelectObject(hdc, OS.GetStockObject(OS.NULL_BRUSH));
858 		LOGBRUSH logBrush = new LOGBRUSH();
859 		logBrush.lbStyle = OS.BS_SOLID;
860 		logBrush.lbColor = color;
861 		long newPen = OS.ExtCreatePen(lineStyle | OS.PS_GEOMETRIC, lineWidth, logBrush, 0, null);
862 		long oldPen = OS.SelectObject(hdc, newPen);
863 		RECT drawRect = new RECT();
864 		OS.SetRect(drawRect, x + left, y, x + run.x + run.width, y + lineHeight);
865 		if (drawClip != null) {
866 			if (drawRect.left < drawClip.x) {
867 				int remainder = drawRect.left % pattern;
868 				drawRect.left = drawClip.x / pattern * pattern + remainder - pattern;
869 			}
870 			if (drawRect.right > drawClip.x + drawClip.width) {
871 				int remainder = drawRect.right % pattern;
872 				drawRect.right = (drawClip.x + drawClip.width) / pattern * pattern + remainder + pattern;
873 			}
874 		}
875 		OS.Rectangle(hdc, drawRect.left,drawRect.top, drawRect.right, drawRect.bottom);
876 		OS.SelectObject(hdc, oldPen);
877 		OS.DeleteObject(newPen);
878 		if (clipRect != null) {
879 			int state = OS.SaveDC(hdc);
880 			if (clipRect.left == -1) clipRect.left = 0;
881 			if (clipRect.right == -1) clipRect.right = 0x7ffff;
882 			OS.IntersectClipRect(hdc, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
883 			logBrush.lbColor = selectionColor;
884 			long selPen = OS.ExtCreatePen (lineStyle | OS.PS_GEOMETRIC, lineWidth, logBrush, 0, null);
885 			oldPen = OS.SelectObject(hdc, selPen);
886 			OS.Rectangle(hdc, drawRect.left, drawRect.top, drawRect.right, drawRect.bottom);
887 			OS.RestoreDC(hdc, state);
888 			OS.SelectObject(hdc, oldPen);
889 			OS.DeleteObject(selPen);
890 		}
891 		OS.SelectObject(hdc, oldBrush);
892 		return null;
893 	}
894 	return clipRect;
895 }
896 
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)897 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) {
898 	StyleItem run = line[index];
899 	TextStyle style = run.style;
900 	if (style == null) return null;
901 	if (style.borderStyle == SWT.NONE) return null;
902 	clipRect = addClipRect(run, clipRect, pRect, selectionStart, selectionEnd);
903 	boolean lastRunVisible = drawClip != null && (x + run.x + run.width) > (drawClip.x + drawClip.width);
904 	if (index + 1 >= line.length || lastRunVisible || !style.isAdherentBorder(line[index + 1].style)) {
905 		int left = run.x;
906 		int start = run.start;
907 		int end = run.start + run.length - 1;
908 		for (int i = index; i > 0 && style.isAdherentBorder(line[i - 1].style); i--) {
909 			left = line[i - 1].x;
910 			start = Math.min(start, line[i - 1].start);
911 			end = Math.max(end, line[i - 1].start + line[i - 1].length - 1);
912 		}
913 		boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1;
914 		boolean fullSelection = hasSelection && selectionStart <= start && end <= selectionEnd;
915 		long brush = color;
916 		if (style.borderColor != null) {
917 			brush = createGdipBrush(style.borderColor, alpha);
918 			clipRect = null;
919 		} else {
920 			if (fullSelection) {
921 				brush = selectionColor;
922 				clipRect = null;
923 			} else {
924 				if (style.foreground != null) {
925 					brush = createGdipBrush(style.foreground, alpha);
926 				}
927 			}
928 		}
929 		int lineWidth = 1;
930 		int lineStyle = Gdip.DashStyleSolid;
931 		switch (style.borderStyle) {
932 			case SWT.BORDER_SOLID: break;
933 			case SWT.BORDER_DASH: lineStyle = Gdip.DashStyleDash; break;
934 			case SWT.BORDER_DOT: lineStyle = Gdip.DashStyleDot; break;
935 		}
936 		long pen = Gdip.Pen_new(brush, lineWidth);
937 		Gdip.Pen_SetDashStyle(pen, lineStyle);
938 		Gdip.Graphics_SetPixelOffsetMode(graphics, Gdip.PixelOffsetModeNone);
939 		int smoothingMode = Gdip.Graphics_GetSmoothingMode(graphics);
940 		Gdip.Graphics_SetSmoothingMode(graphics, Gdip.SmoothingModeNone);
941 		if (clipRect != null) {
942 			int gstate = Gdip.Graphics_Save(graphics);
943 			if (clipRect.left == -1) clipRect.left = 0;
944 			if (clipRect.right == -1) clipRect.right = 0x7ffff;
945 			Rect gdipRect = new Rect();
946 			gdipRect.X = clipRect.left;
947 			gdipRect.Y = clipRect.top;
948 			gdipRect.Width = clipRect.right - clipRect.left;
949 			gdipRect.Height = clipRect.bottom - clipRect.top;
950 			Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeExclude);
951 			Gdip.Graphics_DrawRectangle(graphics, pen, x + left, y, run.x + run.width - left - 1, lineHeight - 1);
952 			Gdip.Graphics_Restore(graphics, gstate);
953 			gstate = Gdip.Graphics_Save(graphics);
954 			Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeIntersect);
955 			long selPen = Gdip.Pen_new(selectionColor, lineWidth);
956 			Gdip.Pen_SetDashStyle(selPen, lineStyle);
957 			Gdip.Graphics_DrawRectangle(graphics, selPen, x + left, y, run.x + run.width - left - 1, lineHeight - 1);
958 			Gdip.Pen_delete(selPen);
959 			Gdip.Graphics_Restore(graphics, gstate);
960 		} else {
961 			Gdip.Graphics_DrawRectangle(graphics, pen, x + left, y, run.x + run.width - left - 1, lineHeight - 1);
962 		}
963 		Gdip.Graphics_SetPixelOffsetMode(graphics, Gdip.PixelOffsetModeHalf);
964 		Gdip.Graphics_SetSmoothingMode(graphics, smoothingMode);
965 		Gdip.Pen_delete(pen);
966 		if (brush != selectionColor && brush != color) Gdip.SolidBrush_delete(brush);
967 		return null;
968 	}
969 	return clipRect;
970 }
971 
drawRunBackground(StyleItem run, long hdc, RECT rect, int selectionStart, int selectionEnd, long selBrush, boolean hasSelection)972 void drawRunBackground(StyleItem run, long hdc, RECT rect, int selectionStart, int selectionEnd, long selBrush, boolean hasSelection) {
973 	int end = run.start + run.length - 1;
974 	boolean fullSelection = hasSelection && selectionStart <= run.start && selectionEnd >= end;
975 	if (fullSelection) {
976 		OS.SelectObject(hdc, selBrush);
977 		OS.PatBlt(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY);
978 	} else {
979 		if (run.style != null && run.style.background != null) {
980 			int bg = run.style.background.handle;
981 			long hBrush = OS.CreateSolidBrush (bg);
982 			long oldBrush = OS.SelectObject(hdc, hBrush);
983 			OS.PatBlt(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY);
984 			OS.SelectObject(hdc, oldBrush);
985 			OS.DeleteObject(hBrush);
986 		}
987 		boolean partialSelection = hasSelection && !(selectionStart > end || run.start > selectionEnd);
988 		if (partialSelection) {
989 			getPartialSelection(run, selectionStart, selectionEnd, rect);
990 			OS.SelectObject(hdc, selBrush);
991 			OS.PatBlt(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY);
992 		}
993 	}
994 }
995 
drawRunBackgroundGDIP(StyleItem run, long graphics, RECT rect, int selectionStart, int selectionEnd, int alpha, long selBrush, boolean hasSelection)996 void drawRunBackgroundGDIP(StyleItem run, long graphics, RECT rect, int selectionStart, int selectionEnd, int alpha, long selBrush, boolean hasSelection) {
997 	int end = run.start + run.length - 1;
998 	boolean fullSelection = hasSelection && selectionStart <= run.start && selectionEnd >= end;
999 	if (fullSelection) {
1000 		Gdip.Graphics_FillRectangle(graphics, selBrush, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
1001 	} else {
1002 		if (run.style != null && run.style.background != null) {
1003 			long brush = createGdipBrush(run.style.background, alpha);
1004 			Gdip.Graphics_FillRectangle(graphics, brush, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
1005 			Gdip.SolidBrush_delete(brush);
1006 		}
1007 		boolean partialSelection = hasSelection && !(selectionStart > end || run.start > selectionEnd);
1008 		if (partialSelection) {
1009 			getPartialSelection(run, selectionStart, selectionEnd, rect);
1010 			if (rect.left > rect.right) {
1011 				int tmp = rect.left;
1012 				rect.left = rect.right;
1013 				rect.right = tmp;
1014 			}
1015 			Gdip.Graphics_FillRectangle(graphics, selBrush, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
1016 		}
1017 	}
1018 }
1019 
drawRunText(long hdc, StyleItem run, RECT rect, int baselineInPixels, int color, int selectionColor, int selectionStart, int selectionEnd)1020 RECT drawRunText(long hdc, StyleItem run, RECT rect, int baselineInPixels, int color, int selectionColor, int selectionStart, int selectionEnd) {
1021 	int end = run.start + run.length - 1;
1022 	boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1;
1023 	boolean fullSelection = hasSelection && selectionStart <= run.start && selectionEnd >= end;
1024 	boolean partialSelection = hasSelection && !fullSelection && !(selectionStart > end || run.start > selectionEnd);
1025 	int offset = (orientation & SWT.RIGHT_TO_LEFT) != 0 ? -1 : 0;
1026 	int x = rect.left + offset;
1027 	int y = rect.top + (baselineInPixels - DPIUtil.autoScaleUp(getDevice(), run.ascentInPoints));
1028 	long hFont = getItemFont(run);
1029 	OS.SelectObject(hdc, hFont);
1030 	if (fullSelection) {
1031 		color = selectionColor;
1032 	} else {
1033 		if (run.style != null && run.style.foreground != null) {
1034 			color = run.style.foreground.handle;
1035 		}
1036 	}
1037 	OS.SetTextColor(hdc, color);
1038 	OS.ScriptTextOut(hdc, run.psc, x, y, 0, null, run.analysis , 0, 0, run.glyphs, run.glyphCount, run.advances, run.justify, run.goffsets);
1039 	if (partialSelection) {
1040 		getPartialSelection(run, selectionStart, selectionEnd, rect);
1041 		OS.SetTextColor(hdc, selectionColor);
1042 		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);
1043 	}
1044 	return fullSelection || partialSelection ? rect : null;
1045 }
1046 
drawRunTextGDIP(long graphics, StyleItem run, RECT rect, long gdipFont, int baselineInPixels, long color, long selectionColor, int selectionStart, int selectionEnd, int alpha)1047 RECT drawRunTextGDIP(long graphics, StyleItem run, RECT rect, long gdipFont, int baselineInPixels, long color, long selectionColor, int selectionStart, int selectionEnd, int alpha) {
1048 	int end = run.start + run.length - 1;
1049 	boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1;
1050 	boolean fullSelection = hasSelection && selectionStart <= run.start && selectionEnd >= end;
1051 	boolean partialSelection = hasSelection && !fullSelection && !(selectionStart > end || run.start > selectionEnd);
1052 	int drawY = rect.top + baselineInPixels;
1053 	if (run.style != null && run.style.rise != 0) drawY -= DPIUtil.autoScaleUp(getDevice(), run.style.rise);
1054 	int drawX = rect.left;
1055 	long brush = color;
1056 	if (fullSelection) {
1057 		brush = selectionColor;
1058 	} else {
1059 		if (run.style != null && run.style.foreground != null) {
1060 			brush = createGdipBrush(run.style.foreground, alpha);
1061 		}
1062 	}
1063 	int gstate = 0;
1064 	Rect gdipRect = null;
1065 	if (partialSelection) {
1066 		gdipRect = new Rect();
1067 		getPartialSelection(run, selectionStart, selectionEnd, rect);
1068 		gdipRect.X = rect.left;
1069 		gdipRect.Y = rect.top;
1070 		gdipRect.Width = rect.right - rect.left;
1071 		gdipRect.Height = rect.bottom - rect.top;
1072 		gstate = Gdip.Graphics_Save(graphics);
1073 		Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeExclude);
1074 	}
1075 	int gstateMirrored = 0;
1076 	boolean isMirrored = (orientation & SWT.RIGHT_TO_LEFT) != 0;
1077 	if (isMirrored) {
1078 		switch (Gdip.Brush_GetType(brush)) {
1079 			case Gdip.BrushTypeLinearGradient:
1080 				Gdip.LinearGradientBrush_ScaleTransform(brush, -1, 1, Gdip.MatrixOrderPrepend);
1081 				Gdip.LinearGradientBrush_TranslateTransform(brush, -2 * drawX - run.width, 0, Gdip.MatrixOrderPrepend);
1082 				break;
1083 			case Gdip.BrushTypeTextureFill:
1084 				Gdip.TextureBrush_ScaleTransform(brush, -1, 1, Gdip.MatrixOrderPrepend);
1085 				Gdip.TextureBrush_TranslateTransform(brush, -2 * drawX - run.width, 0, Gdip.MatrixOrderPrepend);
1086 				break;
1087 		}
1088 		gstateMirrored = Gdip.Graphics_Save(graphics);
1089 		Gdip.Graphics_ScaleTransform(graphics, -1, 1, Gdip.MatrixOrderPrepend);
1090 		Gdip.Graphics_TranslateTransform(graphics, -2 * drawX - run.width, 0, Gdip.MatrixOrderPrepend);
1091 	}
1092 	int[] advances = new int[run.glyphCount];
1093 	float[] points = new float[run.glyphCount * 2];
1094 	C.memmove(advances, run.justify != 0 ? run.justify : run.advances, run.glyphCount * 4);
1095 	int glyphX = drawX;
1096 	for (int h = 0, j = 0; h < advances.length; h++) {
1097 		points[j++] = glyphX;
1098 		points[j++] = drawY;
1099 		glyphX += advances[h];
1100 	}
1101 	Gdip.Graphics_DrawDriverString(graphics, run.glyphs, run.glyphCount, gdipFont, brush, points, 0, 0);
1102 	if (partialSelection) {
1103 		if (isMirrored) {
1104 			Gdip.Graphics_Restore(graphics, gstateMirrored);
1105 		}
1106 		Gdip.Graphics_Restore(graphics, gstate);
1107 		gstate = Gdip.Graphics_Save(graphics);
1108 		Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeIntersect);
1109 		if (isMirrored) {
1110 			gstateMirrored = Gdip.Graphics_Save(graphics);
1111 			Gdip.Graphics_ScaleTransform(graphics, -1, 1, Gdip.MatrixOrderPrepend);
1112 			Gdip.Graphics_TranslateTransform(graphics, -2 * drawX - run.width, 0, Gdip.MatrixOrderPrepend);
1113 		}
1114 		Gdip.Graphics_DrawDriverString(graphics, run.glyphs, run.glyphCount, gdipFont, selectionColor, points, 0, 0);
1115 		Gdip.Graphics_Restore(graphics, gstate);
1116 	}
1117 	if (isMirrored) {
1118 		switch (Gdip.Brush_GetType(brush)) {
1119 			case Gdip.BrushTypeLinearGradient:
1120 				Gdip.LinearGradientBrush_ResetTransform(brush);
1121 				break;
1122 			case Gdip.BrushTypeTextureFill:
1123 				Gdip.TextureBrush_ResetTransform(brush);
1124 				break;
1125 		}
1126 		Gdip.Graphics_Restore(graphics, gstateMirrored);
1127 	}
1128 	if (brush != selectionColor && brush != color) Gdip.SolidBrush_delete(brush);
1129 	return fullSelection || partialSelection ? rect : null;
1130 }
1131 
drawRunTextGDIPRaster(long graphics, StyleItem run, RECT rect, int baselineInPixels, int color, int selectionColor, int selectionStart, int selectionEnd)1132 RECT drawRunTextGDIPRaster(long graphics, StyleItem run, RECT rect, int baselineInPixels, int color, int selectionColor, int selectionStart, int selectionEnd) {
1133 	long clipRgn = 0;
1134 	Gdip.Graphics_SetPixelOffsetMode(graphics, Gdip.PixelOffsetModeNone);
1135 	long rgn = Gdip.Region_new();
1136 	if (rgn == 0) SWT.error(SWT.ERROR_NO_HANDLES);
1137 	Gdip.Graphics_GetClip(graphics, rgn);
1138 	if (!Gdip.Region_IsInfinite(rgn, graphics)) {
1139 		clipRgn = Gdip.Region_GetHRGN(rgn, graphics);
1140 	}
1141 	Gdip.Region_delete(rgn);
1142 	Gdip.Graphics_SetPixelOffsetMode(graphics, Gdip.PixelOffsetModeHalf);
1143 	float[] lpXform = null;
1144 	long matrix = Gdip.Matrix_new(1, 0, 0, 1, 0, 0);
1145 	if (matrix == 0) SWT.error(SWT.ERROR_NO_HANDLES);
1146 	Gdip.Graphics_GetTransform(graphics, matrix);
1147 	if (!Gdip.Matrix_IsIdentity(matrix)) {
1148 		lpXform = new float[6];
1149 		Gdip.Matrix_GetElements(matrix, lpXform);
1150 	}
1151 	Gdip.Matrix_delete(matrix);
1152 	long hdc = Gdip.Graphics_GetHDC(graphics);
1153 	int state = OS.SaveDC(hdc);
1154 	if (lpXform != null) {
1155 		OS.SetGraphicsMode(hdc, OS.GM_ADVANCED);
1156 		OS.SetWorldTransform(hdc, lpXform);
1157 	}
1158 	if (clipRgn != 0) {
1159 		OS.SelectClipRgn(hdc, clipRgn);
1160 		OS.DeleteObject(clipRgn);
1161 	}
1162 	if ((orientation & SWT.RIGHT_TO_LEFT) != 0) {
1163 		OS.SetLayout(hdc, OS.GetLayout(hdc) | OS.LAYOUT_RTL);
1164 	}
1165 	OS.SetBkMode(hdc, OS.TRANSPARENT);
1166 	RECT pRect = drawRunText(hdc, run, rect, baselineInPixels, color, selectionColor, selectionStart, selectionEnd);
1167 	OS.RestoreDC(hdc, state);
1168 	Gdip.Graphics_ReleaseHDC(graphics, hdc);
1169 	return pRect;
1170 }
1171 
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)1172 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) {
1173 	StyleItem run = line[index];
1174 	TextStyle style = run.style;
1175 	if (style == null) return null;
1176 	if (!style.strikeout) return null;
1177 	clipRect = addClipRect(run, clipRect, pRect, selectionStart, selectionEnd);
1178 	boolean lastRunVisible = drawClip != null && (x + run.x + run.width) > (drawClip.x + drawClip.width);
1179 	if (index + 1 >= line.length || lastRunVisible || !style.isAdherentStrikeout(line[index + 1].style)) {
1180 		int left = run.x;
1181 		int start = run.start;
1182 		int end = run.start + run.length - 1;
1183 		for (int i = index; i > 0 && style.isAdherentStrikeout(line[i - 1].style); i--) {
1184 			left = line[i - 1].x;
1185 			start = Math.min(start, line[i - 1].start);
1186 			end = Math.max(end, line[i - 1].start + line[i - 1].length - 1);
1187 		}
1188 		boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1;
1189 		boolean fullSelection = hasSelection && selectionStart <= start && end <= selectionEnd;
1190 		if (style.strikeoutColor != null) {
1191 			color = style.strikeoutColor.handle;
1192 			clipRect = null;
1193 		} else {
1194 			if (fullSelection) {
1195 				color = selectionColor;
1196 				clipRect = null;
1197 			} else {
1198 				if (style.foreground != null) {
1199 					color = style.foreground.handle;
1200 				}
1201 			}
1202 		}
1203 		RECT rect = new RECT();
1204 		int riseInPixels = DPIUtil.autoScaleUp(getDevice(), style.rise);
1205 		OS.SetRect(rect, x + left, baselineInPixels - run.strikeoutPos - riseInPixels, x + run.x + run.width, baselineInPixels - run.strikeoutPos + run.strikeoutThickness - riseInPixels);
1206 		long brush = OS.CreateSolidBrush(color);
1207 		OS.FillRect(hdc, rect, brush);
1208 		OS.DeleteObject(brush);
1209 		if (clipRect != null) {
1210 			long selBrush = OS.CreateSolidBrush(selectionColor);
1211 			if (clipRect.left == -1) clipRect.left = 0;
1212 			if (clipRect.right == -1) clipRect.right = 0x7ffff;
1213 			OS.SetRect(clipRect, Math.max(rect.left, clipRect.left), rect.top, Math.min(rect.right, clipRect.right), rect.bottom);
1214 			OS.FillRect(hdc, clipRect, selBrush);
1215 			OS.DeleteObject(selBrush);
1216 		}
1217 		return null;
1218 	}
1219 	return clipRect;
1220 }
1221 
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)1222 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) {
1223 	StyleItem run = line[index];
1224 	TextStyle style = run.style;
1225 	if (style == null) return null;
1226 	if (!style.strikeout) return null;
1227 	clipRect = addClipRect(run, clipRect, pRect, selectionStart, selectionEnd);
1228 	boolean lastRunVisible = drawClip != null && (x + run.x + run.width) > (drawClip.x + drawClip.width);
1229 	if (index + 1 >= line.length || lastRunVisible || !style.isAdherentStrikeout(line[index + 1].style)) {
1230 		int left = run.x;
1231 		int start = run.start;
1232 		int end = run.start + run.length - 1;
1233 		for (int i = index; i > 0 && style.isAdherentStrikeout(line[i - 1].style); i--) {
1234 			left = line[i - 1].x;
1235 			start = Math.min(start, line[i - 1].start);
1236 			end = Math.max(end, line[i - 1].start + line[i - 1].length - 1);
1237 		}
1238 		boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1;
1239 		boolean fullSelection = hasSelection && selectionStart <= start && end <= selectionEnd;
1240 		long brush = color;
1241 		if (style.strikeoutColor != null) {
1242 			brush = createGdipBrush(style.strikeoutColor, alpha);
1243 			clipRect = null;
1244 		} else {
1245 			if (fullSelection) {
1246 				brush = selectionColor;
1247 				clipRect = null;
1248 			} else {
1249 				if (style.foreground != null) {
1250 					brush = createGdipBrush(style.foreground, alpha);
1251 				}
1252 			}
1253 		}
1254 		int riseInPixels = DPIUtil.autoScaleUp(getDevice(), style.rise);
1255 		if (clipRect != null) {
1256 			int gstate = Gdip.Graphics_Save(graphics);
1257 			if (clipRect.left == -1) clipRect.left = 0;
1258 			if (clipRect.right == -1) clipRect.right = 0x7ffff;
1259 			Rect gdipRect = new Rect();
1260 			gdipRect.X = clipRect.left;
1261 			gdipRect.Y = clipRect.top;
1262 			gdipRect.Width = clipRect.right - clipRect.left;
1263 			gdipRect.Height = clipRect.bottom - clipRect.top;
1264 			Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeExclude);
1265 			Gdip.Graphics_FillRectangle(graphics, brush, x + left, baselineInPixels - run.strikeoutPos - riseInPixels, run.x + run.width - left, run.strikeoutThickness);
1266 			Gdip.Graphics_Restore(graphics, gstate);
1267 			gstate = Gdip.Graphics_Save(graphics);
1268 			Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeIntersect);
1269 			Gdip.Graphics_FillRectangle(graphics, selectionColor, x + left, baselineInPixels - run.strikeoutPos - riseInPixels, run.x + run.width - left, run.strikeoutThickness);
1270 			Gdip.Graphics_Restore(graphics, gstate);
1271 		} else {
1272 			Gdip.Graphics_FillRectangle(graphics, brush, x + left, baselineInPixels - run.strikeoutPos - riseInPixels, run.x + run.width - left, run.strikeoutThickness);
1273 		}
1274 		if (brush != selectionColor && brush != color) Gdip.SolidBrush_delete(brush);
1275 		return null;
1276 	}
1277 	return clipRect;
1278 }
1279 
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)1280 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) {
1281 	StyleItem run = line[index];
1282 	TextStyle style = run.style;
1283 	if (style == null) return null;
1284 	if (!style.underline) return null;
1285 	clipRect = addClipRect(run, clipRect, pRect, selectionStart, selectionEnd);
1286 	boolean lastRunVisible = drawClip != null && (x + run.x + run.width) > (drawClip.x + drawClip.width);
1287 	if (index + 1 >= line.length || lastRunVisible || !style.isAdherentUnderline(line[index + 1].style)) {
1288 		int left = run.x;
1289 		int start = run.start;
1290 		int end = run.start + run.length - 1;
1291 		for (int i = index; i > 0 && style.isAdherentUnderline(line[i - 1].style); i--) {
1292 			left = line[i - 1].x;
1293 			start = Math.min(start, line[i - 1].start);
1294 			end = Math.max(end, line[i - 1].start + line[i - 1].length - 1);
1295 		}
1296 		boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1;
1297 		boolean fullSelection = hasSelection && selectionStart <= start && end <= selectionEnd;
1298 		if (style.underlineColor != null) {
1299 			color = style.underlineColor.handle;
1300 			clipRect = null;
1301 		} else {
1302 			if (fullSelection) {
1303 				color = selectionColor;
1304 				clipRect = null;
1305 			} else {
1306 				if (style.foreground != null) {
1307 					color = style.foreground.handle;
1308 				}
1309 			}
1310 		}
1311 		RECT rect = new RECT();
1312 		int riseInPixels = DPIUtil.autoScaleUp(getDevice(), style.rise);
1313 		OS.SetRect(rect, x + left, baselineInPixels - lineUnderlinePos - riseInPixels, x + run.x + run.width, baselineInPixels - lineUnderlinePos + run.underlineThickness - riseInPixels);
1314 		if (clipRect != null) {
1315 			if (clipRect.left == -1) clipRect.left = 0;
1316 			if (clipRect.right == -1) clipRect.right = 0x7ffff;
1317 			OS.SetRect(clipRect, Math.max(rect.left, clipRect.left), rect.top, Math.min(rect.right, clipRect.right), rect.bottom);
1318 		}
1319 		switch (style.underlineStyle) {
1320 			case SWT.UNDERLINE_SQUIGGLE:
1321 			case SWT.UNDERLINE_ERROR: {
1322 				int squigglyThickness = 1;
1323 				int squigglyHeight = 2 * squigglyThickness;
1324 				int squigglyY = Math.min(rect.top - squigglyHeight / 2, lineBottom - squigglyHeight - 1);
1325 				int[] points = computePolyline(rect.left, squigglyY, rect.right, squigglyY + squigglyHeight);
1326 				long pen = OS.CreatePen(OS.PS_SOLID, squigglyThickness, color);
1327 				long oldPen = OS.SelectObject(hdc, pen);
1328 				int state = OS.SaveDC(hdc);
1329 				OS.IntersectClipRect(hdc, rect.left, squigglyY, rect.right + 1, squigglyY + squigglyHeight + 1);
1330 				OS.Polyline(hdc, points, points.length / 2);
1331 				int length = points.length;
1332 				if (length >= 2 && squigglyThickness <= 1) {
1333 					OS.SetPixel (hdc, points[length - 2], points[length - 1], color);
1334 				}
1335 				OS.SelectObject(hdc, oldPen);
1336 				OS.DeleteObject(pen);
1337 				OS.RestoreDC(hdc, state);
1338 				if (clipRect != null) {
1339 					pen = OS.CreatePen(OS.PS_SOLID, squigglyThickness, selectionColor);
1340 					oldPen = OS.SelectObject(hdc, pen);
1341 					state = OS.SaveDC(hdc);
1342 					OS.IntersectClipRect(hdc, clipRect.left, squigglyY, clipRect.right + 1, squigglyY + squigglyHeight + 1);
1343 					OS.Polyline(hdc, points, points.length / 2);
1344 					if (length >= 2 && squigglyThickness <= 1) {
1345 						OS.SetPixel (hdc, points[length - 2], points[length - 1], selectionColor);
1346 					}
1347 					OS.SelectObject(hdc, oldPen);
1348 					OS.DeleteObject(pen);
1349 					OS.RestoreDC(hdc, state);
1350 				}
1351 				break;
1352 			}
1353 			case SWT.UNDERLINE_SINGLE:
1354 			case SWT.UNDERLINE_DOUBLE:
1355 			case SWT.UNDERLINE_LINK:
1356 			case UNDERLINE_IME_THICK:
1357 				if (style.underlineStyle == UNDERLINE_IME_THICK) {
1358 					rect.top -= run.underlineThickness;
1359 					if (clipRect != null) clipRect.top -= run.underlineThickness;
1360 				}
1361 				int bottom = style.underlineStyle == SWT.UNDERLINE_DOUBLE ? rect.bottom + run.underlineThickness * 2 : rect.bottom;
1362 				if (bottom > lineBottom) {
1363 					OS.OffsetRect(rect, 0, lineBottom - bottom);
1364 					if (clipRect != null) OS.OffsetRect(clipRect, 0, lineBottom - bottom);
1365 				}
1366 				long brush = OS.CreateSolidBrush(color);
1367 				OS.FillRect(hdc, rect, brush);
1368 				if (style.underlineStyle == SWT.UNDERLINE_DOUBLE) {
1369 					OS.SetRect(rect, rect.left, rect.top + run.underlineThickness * 2, rect.right, rect.bottom + run.underlineThickness * 2);
1370 					OS.FillRect(hdc, rect, brush);
1371 				}
1372 				OS.DeleteObject(brush);
1373 				if (clipRect != null) {
1374 					long selBrush = OS.CreateSolidBrush(selectionColor);
1375 					OS.FillRect(hdc, clipRect, selBrush);
1376 					if (style.underlineStyle == SWT.UNDERLINE_DOUBLE) {
1377 						OS.SetRect(clipRect, clipRect.left, rect.top, clipRect.right, rect.bottom);
1378 						OS.FillRect(hdc, clipRect, selBrush);
1379 					}
1380 					OS.DeleteObject(selBrush);
1381 				}
1382 				break;
1383 			case UNDERLINE_IME_DASH:
1384 			case UNDERLINE_IME_DOT: {
1385 				int penStyle = style.underlineStyle == UNDERLINE_IME_DASH ? OS.PS_DASH : OS.PS_DOT;
1386 				long pen = OS.CreatePen(penStyle, 1, color);
1387 				long oldPen = OS.SelectObject(hdc, pen);
1388 				int descentInPixels = DPIUtil.autoScaleUp(getDevice(), run.descentInPoints);
1389 				OS.SetRect(rect, rect.left, baselineInPixels + descentInPixels, rect.right, baselineInPixels + descentInPixels + run.underlineThickness);
1390 				OS.MoveToEx(hdc, rect.left, rect.top, 0);
1391 				OS.LineTo(hdc, rect.right, rect.top);
1392 				OS.SelectObject(hdc, oldPen);
1393 				OS.DeleteObject(pen);
1394 				if (clipRect != null) {
1395 					pen = OS.CreatePen(penStyle, 1, selectionColor);
1396 					oldPen = OS.SelectObject(hdc, pen);
1397 					OS.SetRect(clipRect, clipRect.left, rect.top, clipRect.right, rect.bottom);
1398 					OS.MoveToEx(hdc, clipRect.left, clipRect.top, 0);
1399 					OS.LineTo(hdc, clipRect.right, clipRect.top);
1400 					OS.SelectObject(hdc, oldPen);
1401 					OS.DeleteObject(pen);
1402 				}
1403 				break;
1404 			}
1405 		}
1406 		return null;
1407 	}
1408 	return clipRect;
1409 }
1410 
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)1411 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) {
1412 	StyleItem run = line[index];
1413 	TextStyle style = run.style;
1414 	if (style == null) return null;
1415 	if (!style.underline) return null;
1416 	clipRect = addClipRect(run, clipRect, pRect, selectionStart, selectionEnd);
1417 	boolean lastRunVisible = drawClip != null && (x + run.x + run.width) > (drawClip.x + drawClip.width);
1418 	if (index + 1 >= line.length || lastRunVisible || !style.isAdherentUnderline(line[index + 1].style)) {
1419 		int left = run.x;
1420 		int start = run.start;
1421 		int end = run.start + run.length - 1;
1422 		for (int i = index; i > 0 && style.isAdherentUnderline(line[i - 1].style); i--) {
1423 			left = line[i - 1].x;
1424 			start = Math.min(start, line[i - 1].start);
1425 			end = Math.max(end, line[i - 1].start + line[i - 1].length - 1);
1426 		}
1427 		boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1;
1428 		boolean fullSelection = hasSelection && selectionStart <= start && end <= selectionEnd;
1429 		long brush = color;
1430 		if (style.underlineColor != null) {
1431 			brush = createGdipBrush(style.underlineColor, alpha);
1432 			clipRect = null;
1433 		} else {
1434 			if (fullSelection) {
1435 				brush = selectionColor;
1436 				clipRect = null;
1437 			} else {
1438 				if (style.foreground != null) {
1439 					brush = createGdipBrush(style.foreground, alpha);
1440 				}
1441 			}
1442 		}
1443 		RECT rect = new RECT();
1444 		int riseInPixels = DPIUtil.autoScaleUp(getDevice(), style.rise);
1445 		OS.SetRect(rect, x + left, baselineInPixels - lineUnderlinePos - riseInPixels, x + run.x + run.width, baselineInPixels - lineUnderlinePos + run.underlineThickness - riseInPixels);
1446 		Rect gdipRect = null;
1447 		if (clipRect != null) {
1448 			if (clipRect.left == -1) clipRect.left = 0;
1449 			if (clipRect.right == -1) clipRect.right = 0x7ffff;
1450 			OS.SetRect(clipRect, Math.max(rect.left, clipRect.left), rect.top, Math.min(rect.right, clipRect.right), rect.bottom);
1451 			gdipRect = new Rect();
1452 			gdipRect.X = clipRect.left;
1453 			gdipRect.Y = clipRect.top;
1454 			gdipRect.Width = clipRect.right - clipRect.left;
1455 			gdipRect.Height = clipRect.bottom - clipRect.top;
1456 		}
1457 		int gstate = 0;
1458 		Gdip.Graphics_SetPixelOffsetMode(graphics, Gdip.PixelOffsetModeNone);
1459 		int smoothingMode = Gdip.Graphics_GetSmoothingMode(graphics);
1460 		Gdip.Graphics_SetSmoothingMode(graphics, Gdip.SmoothingModeNone);
1461 		switch (style.underlineStyle) {
1462 			case SWT.UNDERLINE_SQUIGGLE:
1463 			case SWT.UNDERLINE_ERROR: {
1464 				int squigglyThickness = 1;
1465 				int squigglyHeight = 2 * squigglyThickness;
1466 				int squigglyY = Math.min(rect.top - squigglyHeight / 2, lineBottom - squigglyHeight - 1);
1467 				int[] points = computePolyline(rect.left, squigglyY, rect.right, squigglyY + squigglyHeight);
1468 				long pen = Gdip.Pen_new(brush, squigglyThickness);
1469 				gstate = Gdip.Graphics_Save(graphics);
1470 				if (gdipRect != null) {
1471 					Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeExclude);
1472 				} else {
1473 					Rect r = new Rect();
1474 					r.X = rect.left;
1475 					r.Y = squigglyY;
1476 					r.Width = rect.right - rect.left;
1477 					r.Height = squigglyHeight + 1;
1478 					Gdip.Graphics_SetClip(graphics, r, Gdip.CombineModeIntersect);
1479 				}
1480 				Gdip.Graphics_DrawLines(graphics, pen, points, points.length / 2);
1481 				if (gdipRect != null) {
1482 					long selPen = Gdip.Pen_new(selectionColor, squigglyThickness);
1483 					Gdip.Graphics_Restore(graphics, gstate);
1484 					gstate = Gdip.Graphics_Save(graphics);
1485 					Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeIntersect);
1486 					Gdip.Graphics_DrawLines(graphics, selPen, points, points.length / 2);
1487 					Gdip.Pen_delete(selPen);
1488 				}
1489 				Gdip.Graphics_Restore(graphics, gstate);
1490 				Gdip.Pen_delete(pen);
1491 				if (gstate != 0) Gdip.Graphics_Restore(graphics, gstate);
1492 				break;
1493 			}
1494 			case SWT.UNDERLINE_SINGLE:
1495 			case SWT.UNDERLINE_DOUBLE:
1496 			case SWT.UNDERLINE_LINK:
1497 			case UNDERLINE_IME_THICK:
1498 				if (style.underlineStyle == UNDERLINE_IME_THICK) {
1499 					rect.top -= run.underlineThickness;
1500 				}
1501 				int bottom = style.underlineStyle == SWT.UNDERLINE_DOUBLE ? rect.bottom + run.underlineThickness * 2 : rect.bottom;
1502 				if (bottom > lineBottom) {
1503 					OS.OffsetRect(rect, 0, lineBottom - bottom);
1504 				}
1505 				if (gdipRect != null) {
1506 					gdipRect.Y = rect.top;
1507 					if (style.underlineStyle == UNDERLINE_IME_THICK) {
1508 						gdipRect.Height = run.underlineThickness * 2;
1509 					}
1510 					if (style.underlineStyle == SWT.UNDERLINE_DOUBLE) {
1511 						gdipRect.Height = run.underlineThickness * 3;
1512 					}
1513 					gstate = Gdip.Graphics_Save(graphics);
1514 					Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeExclude);
1515 				}
1516 				Gdip.Graphics_FillRectangle(graphics, brush, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
1517 				if (style.underlineStyle == SWT.UNDERLINE_DOUBLE) {
1518 					Gdip.Graphics_FillRectangle(graphics, brush, rect.left, rect.top + run.underlineThickness * 2, rect.right - rect.left, rect.bottom - rect.top);
1519 				}
1520 				if (gdipRect != null) {
1521 					Gdip.Graphics_Restore(graphics, gstate);
1522 					gstate = Gdip.Graphics_Save(graphics);
1523 					Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeIntersect);
1524 					Gdip.Graphics_FillRectangle(graphics, selectionColor, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
1525 					if (style.underlineStyle == SWT.UNDERLINE_DOUBLE) {
1526 						Gdip.Graphics_FillRectangle(graphics, selectionColor, rect.left, rect.top + run.underlineThickness * 2, rect.right - rect.left, rect.bottom - rect.top);
1527 					}
1528 					Gdip.Graphics_Restore(graphics, gstate);
1529 				}
1530 				break;
1531 			case UNDERLINE_IME_DOT:
1532 			case UNDERLINE_IME_DASH: {
1533 				long pen = Gdip.Pen_new(brush, 1);
1534 				int dashStyle = style.underlineStyle == UNDERLINE_IME_DOT ? Gdip.DashStyleDot : Gdip.DashStyleDash;
1535 				Gdip.Pen_SetDashStyle(pen, dashStyle);
1536 				if (gdipRect != null) {
1537 					gstate = Gdip.Graphics_Save(graphics);
1538 					Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeExclude);
1539 				}
1540 				int descentInPixels = DPIUtil.autoScaleUp(getDevice(), run.descentInPoints);
1541 				Gdip.Graphics_DrawLine(graphics, pen, rect.left, baselineInPixels + descentInPixels, run.width - run.length, baselineInPixels + descentInPixels);
1542 				if (gdipRect != null) {
1543 					Gdip.Graphics_Restore(graphics, gstate);
1544 					gstate = Gdip.Graphics_Save(graphics);
1545 					Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeIntersect);
1546 					long selPen = Gdip.Pen_new(brush, 1);
1547 					Gdip.Pen_SetDashStyle(selPen, dashStyle);
1548 					Gdip.Graphics_DrawLine(graphics, selPen, rect.left, baselineInPixels + descentInPixels, run.width - run.length, baselineInPixels + descentInPixels);
1549 					Gdip.Graphics_Restore(graphics, gstate);
1550 					Gdip.Pen_delete(selPen);
1551 				}
1552 				Gdip.Pen_delete(pen);
1553 				break;
1554 			}
1555 		}
1556 		if (brush != selectionColor && brush != color) Gdip.SolidBrush_delete(brush);
1557 		Gdip.Graphics_SetPixelOffsetMode(graphics, Gdip.PixelOffsetModeHalf);
1558 		Gdip.Graphics_SetSmoothingMode(graphics, smoothingMode);
1559 		return null;
1560 	}
1561 	return clipRect;
1562 }
1563 
freeRuns()1564 void freeRuns () {
1565 	if (allRuns == null) return;
1566 	for (StyleItem run : allRuns) {
1567 		run.free();
1568 	}
1569 	allRuns = null;
1570 	runs = null;
1571 	segmentsText = null;
1572 }
1573 
1574 /**
1575  * Returns the receiver's horizontal text alignment, which will be one
1576  * of <code>SWT.LEFT</code>, <code>SWT.CENTER</code> or
1577  * <code>SWT.RIGHT</code>.
1578  *
1579  * @return the alignment used to positioned text horizontally
1580  *
1581  * @exception SWTException <ul>
1582  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1583  * </ul>
1584  */
getAlignment()1585 public int getAlignment () {
1586 	checkLayout();
1587 	return alignment;
1588 }
1589 
1590 /**
1591  * Returns the ascent of the receiver.
1592  *
1593  * @return the ascent
1594  *
1595  * @exception SWTException <ul>
1596  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1597  * </ul>
1598  *
1599  * @see #getDescent()
1600  * @see #setDescent(int)
1601  * @see #setAscent(int)
1602  * @see #getLineMetrics(int)
1603  */
getAscent()1604 public int getAscent () {
1605 	checkLayout();
1606 	return DPIUtil.autoScaleDown(getDevice(), ascentInPixels);
1607 }
1608 
1609 /**
1610  * Returns the bounds of the receiver. The width returned is either the
1611  * width of the longest line or the width set using {@link TextLayout#setWidth(int)}.
1612  * To obtain the text bounds of a line use {@link TextLayout#getLineBounds(int)}.
1613  *
1614  * @return the bounds of the receiver
1615  *
1616  * @exception SWTException <ul>
1617  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1618  * </ul>
1619  *
1620  * @see #setWidth(int)
1621  * @see #getLineBounds(int)
1622  */
getBounds()1623 public Rectangle getBounds () {
1624 	checkLayout();
1625 	computeRuns(null);
1626 	int width = 0;
1627 	if (wrapWidth != -1) {
1628 		width = wrapWidth;
1629 	} else {
1630 		for (int line=0; line<runs.length; line++) {
1631 			width = Math.max(width, lineWidth[line] + getLineIndent(line));
1632 		}
1633 	}
1634 	return new Rectangle (0, 0, DPIUtil.autoScaleDown(getDevice(), width), lineY[lineY.length - 1] + getScaledVerticalIndent());
1635 }
1636 
1637 /**
1638  * Returns the bounds for the specified range of characters. The
1639  * bounds is the smallest rectangle that encompasses all characters
1640  * in the range. The start and end offsets are inclusive and will be
1641  * clamped if out of range.
1642  *
1643  * @param start the start offset
1644  * @param end the end offset
1645  * @return the bounds of the character range
1646  *
1647  * @exception SWTException <ul>
1648  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1649  * </ul>
1650  */
getBounds(int start, int end)1651 public Rectangle getBounds (int start, int end) {
1652 	checkLayout();
1653 	return DPIUtil.autoScaleDown(getDevice(), getBoundsInPixels(start, end));
1654 }
1655 
getBoundsInPixels(int start, int end)1656 Rectangle getBoundsInPixels (int start, int end) {
1657 	computeRuns(null);
1658 	int length = text.length();
1659 	if (length == 0) return new Rectangle(0, 0, 0, 0);
1660 	if (start > end) return new Rectangle(0, 0, 0, 0);
1661 	start = Math.min(Math.max(0, start), length - 1);
1662 	end = Math.min(Math.max(0, end), length - 1);
1663 	start = translateOffset(start);
1664 	end = translateOffset(end);
1665 	/* use the high surrogate for the start offset and the low surrogate for the end offset */
1666 	length = segmentsText.length();
1667 	char ch = segmentsText.charAt(start);
1668 	if (0xDC00 <= ch && ch <= 0xDFFF) {
1669 		if (start - 1 >= 0) {
1670 			ch = segmentsText.charAt(start - 1);
1671 			if (0xD800 <= ch && ch <= 0xDBFF) {
1672 				start--;
1673 			}
1674 		}
1675 	}
1676 	ch = segmentsText.charAt(end);
1677 	if (0xD800 <= ch && ch <= 0xDBFF) {
1678 		if (end + 1 < length) {
1679 			ch = segmentsText.charAt(end + 1);
1680 			if (0xDC00 <= ch && ch <= 0xDFFF) {
1681 				end++;
1682 			}
1683 		}
1684 	}
1685 	int left = 0x7fffffff, right = 0;
1686 	int top = 0x7fffffff, bottom = 0;
1687 	boolean isRTL = (orientation & SWT.RIGHT_TO_LEFT) != 0;
1688 	for (int i = 0; i < allRuns.length - 1; i++) {
1689 		StyleItem run = allRuns[i];
1690 		int runEnd = run.start + run.length;
1691 		if (runEnd <= start) continue;
1692 		if (run.start > end) break;
1693 		int runLead = run.x;
1694 		int runTrail = run.x + run.width;
1695 		if (run.start <= start && start < runEnd) {
1696 			int cx = 0;
1697 			if (run.style != null && run.style.metrics != null) {
1698 				GlyphMetrics metrics = run.style.metrics;
1699 				cx = metrics.getWidthInPixels() * (start - run.start);
1700 			} else if (!run.tab) {
1701 				int[] piX = new int[1];
1702 				long advances = run.justify != 0 ? run.justify : run.advances;
1703 				OS.ScriptCPtoX(start - run.start, false, run.length, run.glyphCount, run.clusters, run.visAttrs, advances, run.analysis, piX);
1704 				cx = isRTL ? run.width - piX[0] : piX[0];
1705 			}
1706 			if (run.analysis.fRTL ^ isRTL) {
1707 				runTrail = run.x + cx;
1708 			} else {
1709 				runLead = run.x + cx;
1710 			}
1711 		}
1712 		if (run.start <= end && end < runEnd) {
1713 			int cx = run.width;
1714 			if (run.style != null && run.style.metrics != null) {
1715 				GlyphMetrics metrics = run.style.metrics;
1716 				cx = metrics.getWidthInPixels() * (end - run.start + 1);
1717 			} else if (!run.tab) {
1718 				int[] piX = new int[1];
1719 				long advances = run.justify != 0 ? run.justify : run.advances;
1720 				OS.ScriptCPtoX(end - run.start, true, run.length, run.glyphCount, run.clusters, run.visAttrs, advances, run.analysis, piX);
1721 				cx = isRTL ? run.width - piX[0] : piX[0];
1722 			}
1723 			if (run.analysis.fRTL ^ isRTL) {
1724 				runLead = run.x + cx;
1725 			} else {
1726 				runTrail = run.x + cx;
1727 			}
1728 		}
1729 		int lineIndex = 0;
1730 		while (lineIndex < runs.length && lineOffset[lineIndex + 1] <= run.start) {
1731 			lineIndex++;
1732 		}
1733 		left = Math.min(left, runLead);
1734 		right = Math.max(right, runTrail);
1735 		top = Math.min(top, DPIUtil.autoScaleUp(getDevice(), lineY[lineIndex]));
1736 		bottom = Math.max(bottom, DPIUtil.autoScaleUp(getDevice(), lineY[lineIndex + 1] - lineSpacingInPoints));
1737 	}
1738 	return new Rectangle(left, top, right - left, bottom - top + getScaledVerticalIndent());
1739 }
1740 
1741 /**
1742  * Returns the descent of the receiver.
1743  *
1744  * @return the descent
1745  *
1746  * @exception SWTException <ul>
1747  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1748  * </ul>
1749  *
1750  * @see #getAscent()
1751  * @see #setAscent(int)
1752  * @see #setDescent(int)
1753  * @see #getLineMetrics(int)
1754  */
getDescent()1755 public int getDescent () {
1756 	checkLayout();
1757 	return DPIUtil.autoScaleDown(getDevice(), descentInPixels);
1758 }
1759 
1760 /**
1761  * Returns the default font currently being used by the receiver
1762  * to draw and measure text.
1763  *
1764  * @return the receiver's font
1765  *
1766  * @exception SWTException <ul>
1767  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1768  * </ul>
1769  */
getFont()1770 public Font getFont () {
1771 	checkLayout();
1772 	return font;
1773 }
1774 
1775 /**
1776 * Returns the receiver's indent.
1777 *
1778 * @return the receiver's indent
1779 *
1780 * @exception SWTException <ul>
1781 *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1782 * </ul>
1783 *
1784 * @since 3.2
1785 */
getIndent()1786 public int getIndent () {
1787 	checkLayout();
1788 	return DPIUtil.autoScaleDown(getDevice(), getIndentInPixels());
1789 }
1790 
getIndentInPixels()1791 int getIndentInPixels () {
1792 	return indent;
1793 }
1794 
1795 /**
1796 * Returns the receiver's justification.
1797 *
1798 * @return the receiver's justification
1799 *
1800 * @exception SWTException <ul>
1801 *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1802 * </ul>
1803 *
1804 * @since 3.2
1805 */
getJustify()1806 public boolean getJustify () {
1807 	checkLayout();
1808 	return justify;
1809 }
1810 
getItemFont(StyleItem item)1811 long getItemFont (StyleItem item) {
1812 	if (item.fallbackFont != 0) return item.fallbackFont;
1813 	if (item.style != null && item.style.font != null) {
1814 		return item.style.font.handle;
1815 	}
1816 	if (this.font != null) {
1817 		return this.font.handle;
1818 	}
1819 	return device.systemFont.handle;
1820 }
1821 
1822 /**
1823  * Returns the embedding level for the specified character offset. The
1824  * embedding level is usually used to determine the directionality of a
1825  * character in bidirectional text.
1826  *
1827  * @param offset the character offset
1828  * @return the embedding level
1829  *
1830  * @exception IllegalArgumentException <ul>
1831  *    <li>ERROR_INVALID_ARGUMENT - if the character offset is out of range</li>
1832  * </ul>
1833  * @exception SWTException <ul>
1834  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li></ul>
1835  */
getLevel(int offset)1836 public int getLevel (int offset) {
1837 	checkLayout();
1838 	computeRuns(null);
1839 	int length = text.length();
1840 	if (!(0 <= offset && offset <= length)) SWT.error(SWT.ERROR_INVALID_RANGE);
1841 	offset = translateOffset(offset);
1842 	for (int i=1; i<allRuns.length; i++) {
1843 		if (allRuns[i].start > offset) {
1844 			return allRuns[i - 1].analysis.s.uBidiLevel;
1845 		}
1846 	}
1847 	return (resolveTextDirection() & SWT.RIGHT_TO_LEFT) != 0 ? 1 : 0;
1848 }
1849 
1850 /**
1851  * Returns the bounds of the line for the specified line index.
1852  *
1853  * @param lineIndex the line index
1854  * @return the line bounds
1855  *
1856  * @exception IllegalArgumentException <ul>
1857  *    <li>ERROR_INVALID_ARGUMENT - if the line index is out of range</li>
1858  * </ul>
1859  * @exception SWTException <ul>
1860  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1861  * </ul>
1862  */
getLineBounds(int lineIndex)1863 public Rectangle getLineBounds (int lineIndex) {
1864 	checkLayout();
1865 	return DPIUtil.autoScaleDown(getDevice(), getLineBoundsInPixels(lineIndex));
1866 }
1867 
getLineBoundsInPixels(int lineIndex)1868 Rectangle getLineBoundsInPixels(int lineIndex) {
1869 	computeRuns(null);
1870 	if (!(0 <= lineIndex && lineIndex < runs.length)) SWT.error(SWT.ERROR_INVALID_RANGE);
1871 	int x = getLineIndent(lineIndex);
1872 	int y = DPIUtil.autoScaleUp(getDevice(), lineY[lineIndex]);
1873 	int width = lineWidth[lineIndex];
1874 	int height = DPIUtil.autoScaleUp(getDevice(), lineY[lineIndex + 1] - lineY[lineIndex] - lineSpacingInPoints);
1875 	return new Rectangle (x, y, width, height);
1876 }
1877 
1878 /**
1879  * Returns the receiver's line count. This includes lines caused
1880  * by wrapping.
1881  *
1882  * @return the line count
1883  *
1884  * @exception SWTException <ul>
1885  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1886  * </ul>
1887  */
getLineCount()1888 public int getLineCount () {
1889 	checkLayout();
1890 	computeRuns(null);
1891 	return runs.length;
1892 }
1893 
getLineIndent(int lineIndex)1894 int getLineIndent (int lineIndex) {
1895 	int lineIndent = wrapIndent;
1896 	if (lineIndex == 0) {
1897 		lineIndent = indent;
1898 	} else {
1899 		StyleItem[] previousLine = runs[lineIndex - 1];
1900 		StyleItem previousRun = previousLine[previousLine.length - 1];
1901 		if (previousRun.lineBreak && !previousRun.softBreak) {
1902 			lineIndent = indent;
1903 		}
1904 	}
1905 	if (wrapWidth != -1) {
1906 		boolean partialLine = true;
1907 		if (justify) {
1908 			StyleItem[] lineRun = runs[lineIndex];
1909 			if (lineRun[lineRun.length - 1].softBreak) {
1910 				partialLine = false;
1911 			}
1912 		}
1913 		if (partialLine) {
1914 			int lineWidth = this.lineWidth[lineIndex] + lineIndent;
1915 			switch (alignment) {
1916 				case SWT.CENTER: lineIndent += (wrapWidth - lineWidth) / 2; break;
1917 				case SWT.RIGHT: lineIndent += wrapWidth - lineWidth; break;
1918 			}
1919 		}
1920 	}
1921 	return lineIndent;
1922 }
1923 
1924 /**
1925  * Returns the index of the line that contains the specified
1926  * character offset.
1927  *
1928  * @param offset the character offset
1929  * @return the line index
1930  *
1931  * @exception IllegalArgumentException <ul>
1932  *    <li>ERROR_INVALID_ARGUMENT - if the character offset is out of range</li>
1933  * </ul>
1934  * @exception SWTException <ul>
1935  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1936  * </ul>
1937  */
getLineIndex(int offset)1938 public int getLineIndex (int offset) {
1939 	checkLayout();
1940 	computeRuns(null);
1941 	int length = text.length();
1942 	if (!(0 <= offset && offset <= length)) SWT.error(SWT.ERROR_INVALID_RANGE);
1943 	offset = translateOffset(offset);
1944 	for (int line=0; line<runs.length; line++) {
1945 		if (lineOffset[line + 1] > offset) {
1946 			return line;
1947 		}
1948 	}
1949 	return runs.length - 1;
1950 }
1951 
1952 /**
1953  * Returns the font metrics for the specified line index.
1954  *
1955  * @param lineIndex the line index
1956  * @return the font metrics
1957  *
1958  * @exception IllegalArgumentException <ul>
1959  *    <li>ERROR_INVALID_ARGUMENT - if the line index is out of range</li>
1960  * </ul>
1961  * @exception SWTException <ul>
1962  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1963  * </ul>
1964  */
getLineMetrics(int lineIndex)1965 public FontMetrics getLineMetrics (int lineIndex) {
1966 	checkLayout();
1967 	computeRuns(null);
1968 	if (!(0 <= lineIndex && lineIndex < runs.length)) SWT.error(SWT.ERROR_INVALID_RANGE);
1969 	long hDC = device.internal_new_GC(null);
1970 	long srcHdc = OS.CreateCompatibleDC(hDC);
1971 	TEXTMETRIC lptm = new TEXTMETRIC();
1972 	OS.SelectObject(srcHdc, font != null ? font.handle : device.systemFont.handle);
1973 	OS.GetTextMetrics(srcHdc, lptm);
1974 	OS.DeleteDC(srcHdc);
1975 	device.internal_dispose_GC(hDC, null);
1976 
1977 	int ascentInPoints = DPIUtil.autoScaleDown(getDevice(), Math.max(lptm.tmAscent, this.ascentInPixels));
1978 	int descentInPoints = DPIUtil.autoScaleDown(getDevice(), Math.max(lptm.tmDescent, this.descentInPixels));
1979 	int leadingInPoints = DPIUtil.autoScaleDown(getDevice(), lptm.tmInternalLeading);
1980 	if (text.length() != 0) {
1981 		for (StyleItem run : runs[lineIndex]) {
1982 			if (run.ascentInPoints > ascentInPoints) {
1983 				ascentInPoints = run.ascentInPoints;
1984 				leadingInPoints = run.leadingInPoints;
1985 			}
1986 			descentInPoints = Math.max(descentInPoints, run.descentInPoints);
1987 		}
1988 	}
1989 	lptm.tmAscent = DPIUtil.autoScaleUp(getDevice(), ascentInPoints);
1990 	lptm.tmDescent = DPIUtil.autoScaleUp(getDevice(), descentInPoints);
1991 	lptm.tmHeight = DPIUtil.autoScaleUp(getDevice(), ascentInPoints + descentInPoints);
1992 	lptm.tmInternalLeading = DPIUtil.autoScaleUp(getDevice(), leadingInPoints);
1993 	lptm.tmAveCharWidth = 0;
1994 	return FontMetrics.win32_new(lptm);
1995 }
1996 
1997 /**
1998  * Returns the line offsets.  Each value in the array is the
1999  * offset for the first character in a line except for the last
2000  * value, which contains the length of the text.
2001  *
2002  * @return the line offsets
2003  *
2004  * @exception SWTException <ul>
2005  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2006  * </ul>
2007  */
getLineOffsets()2008 public int[] getLineOffsets () {
2009 	checkLayout();
2010 	computeRuns(null);
2011 	int[] offsets = new int[lineOffset.length];
2012 	for (int i = 0; i < offsets.length; i++) {
2013 		offsets[i] = untranslateOffset(lineOffset[i]);
2014 	}
2015 	return offsets;
2016 }
2017 
2018 /**
2019  * Returns the location for the specified character offset. The
2020  * <code>trailing</code> argument indicates whether the offset
2021  * corresponds to the leading or trailing edge of the cluster.
2022  *
2023  * @param offset the character offset
2024  * @param trailing the trailing flag
2025  * @return the location of the character offset
2026  *
2027  * @exception SWTException <ul>
2028  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2029  * </ul>
2030  *
2031  * @see #getOffset(Point, int[])
2032  * @see #getOffset(int, int, int[])
2033  */
getLocation(int offset, boolean trailing)2034 public Point getLocation (int offset, boolean trailing) {
2035 	checkLayout();
2036 	return DPIUtil.autoScaleDown(getDevice(), getLocationInPixels(offset, trailing));
2037 }
2038 
getLocationInPixels(int offset, boolean trailing)2039 Point getLocationInPixels (int offset, boolean trailing) {
2040 	computeRuns(null);
2041 	int length = text.length();
2042 	if (!(0 <= offset && offset <= length)) SWT.error(SWT.ERROR_INVALID_RANGE);
2043 	length = segmentsText.length();
2044 	offset = translateOffset(offset);
2045 	int line;
2046 	for (line=0; line<runs.length; line++) {
2047 		if (lineOffset[line + 1] > offset) break;
2048 	}
2049 	line = Math.min(line, runs.length - 1);
2050 	if (offset == length) {
2051 		return new Point(getLineIndent(line) + lineWidth[line], DPIUtil.autoScaleUp(getDevice(), lineY[line]));
2052 	}
2053 	/* For trailing use the low surrogate and for lead use the high surrogate */
2054 	char ch = segmentsText.charAt(offset);
2055 	if (trailing) {
2056 		if (0xD800 <= ch && ch <= 0xDBFF) {
2057 			if (offset + 1 < length) {
2058 				ch = segmentsText.charAt(offset + 1);
2059 				if (0xDC00 <= ch && ch <= 0xDFFF) {
2060 					offset++;
2061 				}
2062 			}
2063 		}
2064 	} else {
2065 		if (0xDC00 <= ch && ch <= 0xDFFF) {
2066 			if (offset - 1 >= 0) {
2067 				ch = segmentsText.charAt(offset - 1);
2068 				if (0xD800 <= ch && ch <= 0xDBFF) {
2069 					offset--;
2070 				}
2071 			}
2072 		}
2073 	}
2074 	int low = -1;
2075 	int high = allRuns.length;
2076 	while (high - low > 1) {
2077 		int index = ((high + low) / 2);
2078 		StyleItem run = allRuns[index];
2079 		if (run.start > offset) {
2080 			high = index;
2081 		} else if (run.start + run.length <= offset) {
2082 			low = index;
2083 		} else {
2084 			int width;
2085 			if (run.style != null && run.style.metrics != null) {
2086 				GlyphMetrics metrics = run.style.metrics;
2087 				width = metrics.getWidthInPixels() * (offset - run.start + (trailing ? 1 : 0));
2088 			} else if (run.tab) {
2089 				width = (trailing || (offset == length)) ? run.width : 0;
2090 			} else {
2091 				int runOffset = offset - run.start;
2092 				int cChars = run.length;
2093 				int gGlyphs = run.glyphCount;
2094 				int[] piX = new int[1];
2095 				long advances = run.justify != 0 ? run.justify : run.advances;
2096 				OS.ScriptCPtoX(runOffset, trailing, cChars, gGlyphs, run.clusters, run.visAttrs, advances, run.analysis, piX);
2097 				width = (orientation & SWT.RIGHT_TO_LEFT) != 0 ? run.width - piX[0] : piX[0];
2098 			}
2099 			return new Point(run.x + width, DPIUtil.autoScaleUp(getDevice(), lineY[line]) + getScaledVerticalIndent());
2100 		}
2101 	}
2102 	return new Point(0, 0);
2103 }
2104 
2105 /**
2106  * Returns the next offset for the specified offset and movement
2107  * type.  The movement is one of <code>SWT.MOVEMENT_CHAR</code>,
2108  * <code>SWT.MOVEMENT_CLUSTER</code>, <code>SWT.MOVEMENT_WORD</code>,
2109  * <code>SWT.MOVEMENT_WORD_END</code> or <code>SWT.MOVEMENT_WORD_START</code>.
2110  *
2111  * @param offset the start offset
2112  * @param movement the movement type
2113  * @return the next offset
2114  *
2115  * @exception IllegalArgumentException <ul>
2116  *    <li>ERROR_INVALID_ARGUMENT - if the offset is out of range</li>
2117  * </ul>
2118  * @exception SWTException <ul>
2119  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2120  * </ul>
2121  *
2122  * @see #getPreviousOffset(int, int)
2123  */
getNextOffset(int offset, int movement)2124 public int getNextOffset (int offset, int movement) {
2125 	checkLayout();
2126 	return _getOffset (offset, movement, true);
2127 }
2128 
_getOffset(int offset, int movement, boolean forward)2129 int _getOffset(int offset, int movement, boolean forward) {
2130 	computeRuns(null);
2131 	int length = text.length();
2132 	if (!(0 <= offset && offset <= length)) SWT.error(SWT.ERROR_INVALID_RANGE, null, " [offset value: " + offset + "]");//$NON-NLS-1$ $NON-NLS-2$
2133 	if (forward && offset == length) return length;
2134 	if (!forward && offset == 0) return 0;
2135 	int step = forward ? 1 : -1;
2136 	if ((movement & SWT.MOVEMENT_CHAR) != 0) return offset + step;
2137 	length = segmentsText.length();
2138 	offset = translateOffset(offset);
2139 	SCRIPT_LOGATTR logAttr = new SCRIPT_LOGATTR();
2140 	SCRIPT_PROPERTIES properties = new  SCRIPT_PROPERTIES();
2141 	int i = forward ? 0 : allRuns.length - 1;
2142 	offset = validadeOffset(offset, step);
2143 	do {
2144 		StyleItem run = allRuns[i];
2145 		if (run.start <= offset && offset < run.start + run.length) {
2146 			if (run.lineBreak && !run.softBreak) return untranslateOffset(run.start);
2147 			if (run.tab) return untranslateOffset(run.start);
2148 			OS.MoveMemory(properties, device.scripts[run.analysis.eScript], SCRIPT_PROPERTIES.sizeof);
2149 			boolean isComplex = properties.fNeedsCaretInfo || properties.fNeedsWordBreaking;
2150 			if (isComplex) breakRun(run);
2151 			while (run.start <= offset && offset < run.start + run.length) {
2152 				if (isComplex) {
2153 					OS.MoveMemory(logAttr, run.psla + ((offset - run.start) * SCRIPT_LOGATTR.sizeof), SCRIPT_LOGATTR.sizeof);
2154 				}
2155 				switch (movement) {
2156 					case SWT.MOVEMENT_CLUSTER: {
2157 						if (!properties.fNeedsCaretInfo || (!logAttr.fInvalid && logAttr.fCharStop)) {
2158 							char ch = segmentsText.charAt(offset);
2159 							if (0xDC00 <= ch && ch <= 0xDFFF) {
2160 								if (offset > 0) {
2161 									ch = segmentsText.charAt(offset - 1);
2162 									if (0xD800 <= ch && ch <= 0xDBFF) {
2163 										offset += step;
2164 									}
2165 								}
2166 							}
2167 							return untranslateOffset(offset);
2168 						}
2169 						break;
2170 					}
2171 					case SWT.MOVEMENT_WORD_START:
2172 					case SWT.MOVEMENT_WORD: {
2173 						if (properties.fNeedsWordBreaking) {
2174 							if (!logAttr.fInvalid && logAttr.fWordStop) return untranslateOffset(offset);
2175 						} else {
2176 							if (offset > 0) {
2177 								boolean letterOrDigit = Character.isLetterOrDigit(segmentsText.charAt(offset));
2178 								boolean previousLetterOrDigit = Character.isLetterOrDigit(segmentsText.charAt(offset - 1));
2179 								if (letterOrDigit != previousLetterOrDigit || !letterOrDigit) {
2180 									if (!Character.isWhitespace(segmentsText.charAt(offset))) {
2181 										return untranslateOffset(offset);
2182 									}
2183 								}
2184 							}
2185 						}
2186 						break;
2187 					}
2188 					case SWT.MOVEMENT_WORD_END: {
2189 						if (offset > 0) {
2190 							boolean isLetterOrDigit = Character.isLetterOrDigit(segmentsText.charAt(offset));
2191 							boolean previousLetterOrDigit = Character.isLetterOrDigit(segmentsText.charAt(offset - 1));
2192 							if (!isLetterOrDigit && previousLetterOrDigit) {
2193 								return untranslateOffset(offset);
2194 							}
2195 						}
2196 						break;
2197 					}
2198 				}
2199 				offset = validadeOffset(offset, step);
2200 			}
2201 		}
2202 		i += step;
2203 	} while (0 <= i && i < allRuns.length - 1 && 0 <= offset && offset < length);
2204 	return forward ? text.length() : 0;
2205 }
2206 
2207 /**
2208  * Returns the character offset for the specified point.
2209  * For a typical character, the trailing argument will be filled in to
2210  * indicate whether the point is closer to the leading edge (0) or
2211  * the trailing edge (1).  When the point is over a cluster composed
2212  * of multiple characters, the trailing argument will be filled with the
2213  * position of the character in the cluster that is closest to
2214  * the point.
2215  *
2216  * @param point the point
2217  * @param trailing the trailing buffer
2218  * @return the character offset
2219  *
2220  * @exception IllegalArgumentException <ul>
2221  *    <li>ERROR_INVALID_ARGUMENT - if the trailing length is less than <code>1</code></li>
2222  *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
2223  * </ul>
2224  * @exception SWTException <ul>
2225  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2226  * </ul>
2227  *
2228  * @see #getLocation(int, boolean)
2229  */
getOffset(Point point, int[] trailing)2230 public int getOffset (Point point, int[] trailing) {
2231 	checkLayout();
2232 	if (point == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);	return getOffsetInPixels(DPIUtil.autoScaleUp(getDevice(), point), trailing);
2233 }
2234 
getOffsetInPixels(Point point, int[] trailing)2235 int getOffsetInPixels (Point point, int[] trailing) {
2236 	return getOffsetInPixels (point.x, point.y, trailing) ;
2237 }
2238 
2239 /**
2240  * Returns the character offset for the specified point.
2241  * For a typical character, the trailing argument will be filled in to
2242  * indicate whether the point is closer to the leading edge (0) or
2243  * the trailing edge (1).  When the point is over a cluster composed
2244  * of multiple characters, the trailing argument will be filled with the
2245  * position of the character in the cluster that is closest to
2246  * the point.
2247  *
2248  * @param x the x coordinate of the point
2249  * @param y the y coordinate of the point
2250  * @param trailing the trailing buffer
2251  * @return the character offset
2252  *
2253  * @exception IllegalArgumentException <ul>
2254  *    <li>ERROR_INVALID_ARGUMENT - if the trailing length is less than <code>1</code></li>
2255  * </ul>
2256  * @exception SWTException <ul>
2257  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2258  * </ul>
2259  *
2260  * @see #getLocation(int, boolean)
2261  */
getOffset(int x, int y, int[] trailing)2262 public int getOffset (int x, int y, int[] trailing) {
2263 	checkLayout();
2264 	return getOffsetInPixels(DPIUtil.autoScaleUp(getDevice(), x), DPIUtil.autoScaleUp(getDevice(), y), trailing);
2265 }
2266 
getOffsetInPixels(int x, int y, int[] trailing)2267 int getOffsetInPixels (int x, int y, int[] trailing) {
2268 	computeRuns(null);
2269 	if (trailing != null && trailing.length < 1) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2270 	int line;
2271 	int lineCount = runs.length;
2272 	for (line=0; line<lineCount; line++) {
2273 		if (DPIUtil.autoScaleUp(getDevice(), lineY[line + 1]) > y) break;
2274 	}
2275 	line = Math.min(line, runs.length - 1);
2276 	StyleItem[] lineRuns = runs[line];
2277 	int lineIndent = getLineIndent(line);
2278 	if (x >= lineIndent + lineWidth[line]) x = lineIndent + lineWidth[line] - 1;
2279 	if (x < lineIndent) x = lineIndent;
2280 	int low = -1;
2281 	int high = lineRuns.length;
2282 	while (high - low > 1) {
2283 		int index = ((high + low) / 2);
2284 		StyleItem run = lineRuns[index];
2285 		if (run.x > x) {
2286 			high = index;
2287 		} else if (run.x + run.width <= x) {
2288 			low = index;
2289 		} else {
2290 			if (run.lineBreak && !run.softBreak) return untranslateOffset(run.start);
2291 			int xRun = x - run.x;
2292 			if (run.style != null && run.style.metrics != null) {
2293 				GlyphMetrics metrics = run.style.metrics;
2294 				if (metrics.getWidthInPixels() > 0) {
2295 					if (trailing != null) {
2296 						trailing[0] = (xRun % metrics.getWidthInPixels() < metrics.getWidthInPixels() / 2) ? 0 : 1;
2297 					}
2298 					return untranslateOffset(run.start + xRun / metrics.getWidthInPixels());
2299 				}
2300 			}
2301 			if (run.tab) {
2302 				if (trailing != null) trailing[0] = x < (run.x + run.width / 2) ? 0 : 1;
2303 				return untranslateOffset(run.start);
2304 			}
2305 			int cChars = run.length;
2306 			int cGlyphs = run.glyphCount;
2307 			int[] piCP = new int[1];
2308 			int[] piTrailing = new int[1];
2309 			if ((orientation & SWT.RIGHT_TO_LEFT) != 0) {
2310 				xRun = run.width - xRun;
2311 			}
2312 			long advances = run.justify != 0 ? run.justify : run.advances;
2313 			OS.ScriptXtoCP(xRun, cChars, cGlyphs, run.clusters, run.visAttrs, advances, run.analysis, piCP, piTrailing);
2314 			int offset = run.start + piCP[0];
2315 			int length = segmentsText.length();
2316 			char ch = offset < length ? segmentsText.charAt(offset) : 0;
2317 			if (0xD800 <= ch && ch <= 0xDBFF && piTrailing[0] <= 1) {
2318 				if (offset + 1 < length) {
2319 					ch = segmentsText.charAt(offset + 1);
2320 					if (0xDC00 <= ch && ch <= 0xDFFF) {
2321 						if (trailing != null) trailing[0] = 0;
2322 					}
2323 				}
2324 			} else if (0xDC00 <= ch && ch <= 0xDFFF && piTrailing[0] <= 1) {
2325 				if (offset - 1 >= 0) {
2326 					ch = segmentsText.charAt(offset - 1);
2327 					if (0xD800 <= ch && ch <= 0xDBFF) {
2328 						offset--;
2329 						if (trailing != null) trailing[0] = 2;
2330 					}
2331 				}
2332 			} else {
2333 				if (trailing != null) trailing[0] = piTrailing[0];
2334 			}
2335 			return untranslateOffset(offset);
2336 		}
2337 	}
2338 	if (trailing != null) trailing[0] = 0;
2339 	if (lineRuns.length == 1) {
2340 		StyleItem run = lineRuns[0];
2341 		if (run.lineBreak && !run.softBreak) return untranslateOffset(run.start);
2342 	}
2343 	return untranslateOffset(lineOffset[line + 1]);
2344 }
2345 
2346 /**
2347  * Returns the orientation of the receiver.
2348  *
2349  * @return the orientation style
2350  *
2351  * @exception SWTException <ul>
2352  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2353  * </ul>
2354  */
2355 public int getOrientation () {
2356 	checkLayout();
2357 	return orientation;
2358 }
2359 
2360 void getPartialSelection(StyleItem run, int selectionStart, int selectionEnd, RECT rect) {
2361 	int end = run.start + run.length - 1;
2362 	int selStart = Math.max(selectionStart, run.start) - run.start;
2363 	int selEnd = Math.min(selectionEnd, end) - run.start;
2364 	int cChars = run.length;
2365 	int gGlyphs = run.glyphCount;
2366 	int[] piX = new int[1];
2367 	int x = rect.left;
2368 	long advances = run.justify != 0 ? run.justify : run.advances;
2369 	OS.ScriptCPtoX(selStart, false, cChars, gGlyphs, run.clusters, run.visAttrs, advances, run.analysis, piX);
2370 	int runX = (orientation & SWT.RIGHT_TO_LEFT) != 0 ? run.width - piX[0] : piX[0];
2371 	rect.left = x + runX;
2372 	OS.ScriptCPtoX(selEnd, true, cChars, gGlyphs, run.clusters, run.visAttrs, advances, run.analysis, piX);
2373 	runX = (orientation & SWT.RIGHT_TO_LEFT) != 0 ? run.width - piX[0] : piX[0];
2374 	rect.right = x + runX;
2375 }
2376 
2377 /**
2378  * Returns the previous offset for the specified offset and movement
2379  * type.  The movement is one of <code>SWT.MOVEMENT_CHAR</code>,
2380  * <code>SWT.MOVEMENT_CLUSTER</code> or <code>SWT.MOVEMENT_WORD</code>,
2381  * <code>SWT.MOVEMENT_WORD_END</code> or <code>SWT.MOVEMENT_WORD_START</code>.
2382  *
2383  * @param offset the start offset
2384  * @param movement the movement type
2385  * @return the previous offset
2386  *
2387  * @exception IllegalArgumentException <ul>
2388  *    <li>ERROR_INVALID_ARGUMENT - if the offset is out of range</li>
2389  * </ul>
2390  * @exception SWTException <ul>
2391  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2392  * </ul>
2393  *
2394  * @see #getNextOffset(int, int)
2395  */
2396 public int getPreviousOffset (int offset, int movement) {
2397 	checkLayout();
2398 	return _getOffset (offset, movement, false);
2399 }
2400 
2401 /**
2402  * Gets the ranges of text that are associated with a <code>TextStyle</code>.
2403  *
2404  * @return the ranges, an array of offsets representing the start and end of each
2405  * text style.
2406  *
2407  * @exception SWTException <ul>
2408  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2409  * </ul>
2410  *
2411  * @see #getStyles()
2412  *
2413  * @since 3.2
2414  */
2415 public int[] getRanges () {
2416 	checkLayout();
2417 	int[] result = new int[stylesCount * 2];
2418 	int count = 0;
2419 	for (int i=0; i<stylesCount - 1; i++) {
2420 		if (styles[i].style != null) {
2421 			result[count++] = styles[i].start;
2422 			result[count++] = styles[i + 1].start - 1;
2423 		}
2424 	}
2425 	if (count != result.length) {
2426 		int[] newResult = new int[count];
2427 		System.arraycopy(result, 0, newResult, 0, count);
2428 		result = newResult;
2429 	}
2430 	return result;
2431 }
2432 
2433 /**
2434  * Returns the text segments offsets of the receiver.
2435  *
2436  * @return the text segments offsets
2437  *
2438  * @exception SWTException <ul>
2439  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2440  * </ul>
2441  */
2442 public int[] getSegments () {
2443 	checkLayout();
2444 	return segments;
2445 }
2446 
2447 /**
2448  * Returns the segments characters of the receiver.
2449  *
2450  * @return the segments characters
2451  *
2452  * @exception SWTException <ul>
2453  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2454  * </ul>
2455  *
2456  * @since 3.6
2457  */
2458 public char[] getSegmentsChars () {
2459 	checkLayout();
2460 	return segmentsChars;
2461 }
2462 
2463 String getSegmentsText() {
2464 	int length = text.length();
2465 	if (length == 0) return text;
2466 	if (segments == null) return text;
2467 	int nSegments = segments.length;
2468 	if (nSegments == 0) return text;
2469 	if (segmentsChars == null) {
2470 		if (nSegments == 1) return text;
2471 		if (nSegments == 2) {
2472 			if (segments[0] == 0 && segments[1] == length) return text;
2473 		}
2474 	}
2475 	char[] oldChars = new char[length];
2476 	text.getChars(0, length, oldChars, 0);
2477 	char[] newChars = new char[length + nSegments];
2478 	int charCount = 0, segmentCount = 0;
2479 	char defaultSeparator = (resolveTextDirection() & SWT.RIGHT_TO_LEFT) != 0 ? RTL_MARK : LTR_MARK;
2480 	while (charCount < length) {
2481 		if (segmentCount < nSegments && charCount == segments[segmentCount]) {
2482 			char separator = segmentsChars != null && segmentsChars.length > segmentCount ? segmentsChars[segmentCount] : defaultSeparator;
2483 			newChars[charCount + segmentCount++] = separator;
2484 		} else {
2485 			newChars[charCount + segmentCount] = oldChars[charCount++];
2486 		}
2487 	}
2488 	while (segmentCount < nSegments) {
2489 		segments[segmentCount] = charCount;
2490 		char separator = segmentsChars != null && segmentsChars.length > segmentCount ? segmentsChars[segmentCount] : defaultSeparator;
2491 		newChars[charCount + segmentCount++] = separator;
2492 	}
2493 	return new String(newChars, 0, newChars.length);
2494 }
2495 
2496 /**
2497  * Returns the line spacing of the receiver.
2498  *
2499  * @return the line spacing
2500  *
2501  * @exception SWTException <ul>
2502  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2503  * </ul>
2504  */
2505 public int getSpacing () {
2506 	checkLayout();
2507 	return lineSpacingInPoints;
2508 }
2509 
2510 /**
2511  * Returns the vertical indent of the receiver.
2512  *
2513  * @return the vertical indent
2514  *
2515  * @exception SWTException <ul>
2516  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2517  * </ul>
2518  * @since 3.109
2519  */
2520 public int getVerticalIndent () {
2521 	checkLayout();
2522 	return verticalIndentInPoints;
2523 }
2524 
2525 /**
2526  * Returns the scaled vertical indent.
2527  *
2528  * @return the scaled vertical indent.
2529  * @since 3.109
2530  */
2531 private int getScaledVerticalIndent() {
2532 	if (verticalIndentInPoints == 0) {
2533 		return verticalIndentInPoints;
2534 	}
2535 	return DPIUtil.autoScaleUp(getDevice(), verticalIndentInPoints);
2536 }
2537 
2538 /**
2539  * Gets the style of the receiver at the specified character offset.
2540  *
2541  * @param offset the text offset
2542  * @return the style or <code>null</code> if not set
2543  *
2544  * @exception IllegalArgumentException <ul>
2545  *    <li>ERROR_INVALID_ARGUMENT - if the character offset is out of range</li>
2546  * </ul>
2547  * @exception SWTException <ul>
2548  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2549  * </ul>
2550  */
2551 public TextStyle getStyle (int offset) {
2552 	checkLayout();
2553 	int length = text.length();
2554 	if (!(0 <= offset && offset < length)) SWT.error(SWT.ERROR_INVALID_RANGE);
2555 	for (int i=1; i<stylesCount; i++) {
2556 		if (styles[i].start > offset) {
2557 			return styles[i - 1].style;
2558 		}
2559 	}
2560 	return null;
2561 }
2562 
2563 /**
2564  * Gets all styles of the receiver.
2565  *
2566  * @return the styles
2567  *
2568  * @exception SWTException <ul>
2569  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2570  * </ul>
2571  *
2572  * @see #getRanges()
2573  *
2574  * @since 3.2
2575  */
2576 public TextStyle[] getStyles () {
2577 	checkLayout();
2578 	TextStyle[] result = new TextStyle[stylesCount];
2579 	int count = 0;
2580 	for (int i=0; i<stylesCount; i++) {
2581 		if (styles[i].style != null) {
2582 			result[count++] = styles[i].style;
2583 		}
2584 	}
2585 	if (count != result.length) {
2586 		TextStyle[] newResult = new TextStyle[count];
2587 		System.arraycopy(result, 0, newResult, 0, count);
2588 		result = newResult;
2589 	}
2590 	return result;
2591 }
2592 
2593 /**
2594  * Returns the tab list of the receiver.
2595  *
2596  * @return the tab list
2597  *
2598  * @exception SWTException <ul>
2599  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2600  * </ul>
2601  */
2602 public int[] getTabs () {
2603 	checkLayout();
2604 	return DPIUtil.autoScaleDown (getDevice(), getTabsInPixels ());
2605 }
2606 
2607 int[] getTabsInPixels () {
2608 	return tabs;
2609 }
2610 
2611 /**
2612  * Gets the receiver's text, which will be an empty
2613  * string if it has never been set.
2614  *
2615  * @return the receiver's text
2616  *
2617  * @exception SWTException <ul>
2618  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2619  * </ul>
2620  */
2621 public String getText () {
2622 	checkLayout();
2623 	return text;
2624 }
2625 
2626 /**
2627  * Returns the text direction of the receiver.
2628  *
2629  * @return the text direction value
2630  *
2631  * @exception SWTException <ul>
2632  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2633  * </ul>
2634  * @since 3.103
2635  */
2636 public int getTextDirection () {
2637 	checkLayout();
2638 	return resolveTextDirection();
2639 }
2640 
2641 /**
2642  * Returns the width of the receiver.
2643  *
2644  * @return the width
2645  *
2646  * @exception SWTException <ul>
2647  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2648  * </ul>
2649  */
2650 public int getWidth () {
2651 	checkLayout();
2652 	return DPIUtil.autoScaleDown(getDevice(), getWidthInPixels());
2653 }
2654 
2655 int getWidthInPixels () {
2656 	return wrapWidth;
2657 }
2658 
2659 /**
2660 * Returns the receiver's wrap indent.
2661 *
2662 * @return the receiver's wrap indent
2663 *
2664 * @exception SWTException <ul>
2665 *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
2666 * </ul>
2667 *
2668 * @since 3.6
2669 */
2670 public int getWrapIndent () {
2671 	checkLayout();
2672 	return DPIUtil.autoScaleDown(getDevice(), getWrapIndentInPixels());
2673 }
2674 
2675 int getWrapIndentInPixels () {
2676 	return wrapIndent;
2677 }
2678 
2679 /**
2680  * Returns <code>true</code> if the text layout has been disposed,
2681  * and <code>false</code> otherwise.
2682  * <p>
2683  * This method gets the dispose state for the text layout.
2684  * When a text layout has been disposed, it is an error to
2685  * invoke any other method (except {@link #dispose()}) using the text layout.
2686  * </p>
2687  *
2688  * @return <code>true</code> when the text layout is disposed and <code>false</code> otherwise
2689  */
2690 @Override
2691 public boolean isDisposed () {
2692 	return device == null;
2693 }
2694 
2695 /*
2696  *  Itemize the receiver text
2697  */
2698 StyleItem[] itemize () {
2699 	segmentsText = getSegmentsText();
2700 	int length = segmentsText.length();
2701 	SCRIPT_CONTROL scriptControl = new SCRIPT_CONTROL();
2702 	SCRIPT_STATE scriptState = new SCRIPT_STATE();
2703 	final int MAX_ITEM = length + 1;
2704 
2705 	if ((resolveTextDirection() & SWT.RIGHT_TO_LEFT) != 0) {
2706 		scriptState.uBidiLevel = 1;
2707 		scriptState.fArabicNumContext = true;
2708 	}
2709 
2710 	/*
2711 	* In the version of Usp10.h that SWT is compiled the fReserved field is declared
2712 	* as a bitfield size 8. In newer versions of the Uniscribe, the first bit of fReserved
2713 	* was used to implement the fMergeNeutralItems feature which can be used to increase
2714 	* performance by reducing the number of SCRIPT_ITEM returned by ScriptItemize.
2715 	*
2716 	* Note: This code is wrong on a big endian machine.
2717 	*
2718 	* Note: This code is intentionally commented because it causes bug#377472.
2719 	*/
2720 //	scriptControl.fReserved = 0x1;
2721 
2722 	OS.ScriptApplyDigitSubstitution(null, scriptControl, scriptState);
2723 
2724 	long hHeap = OS.GetProcessHeap();
2725 	long pItems = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, MAX_ITEM * SCRIPT_ITEM.sizeof);
2726 	if (pItems == 0) SWT.error(SWT.ERROR_NO_HANDLES);
2727 	int[] pcItems = new int[1];
2728 	char[] chars = new char[length];
2729 	segmentsText.getChars(0, length, chars, 0);
2730 	// enable font ligatures
2731 	scriptControl.fMergeNeutralItems = true;
2732 
2733 	OS.ScriptItemize(chars, length, MAX_ITEM, scriptControl, scriptState, pItems, pcItems);
2734 
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 			OS.EnumEnhMetaFile(0, metaFile, callback.getAddress(), 0, null);
3551 			OS.DeleteEnhMetaFile(metaFile);
3552 			callback.dispose();
3553 			newFont = OS.CreateFontIndirect(emr.elfw.elfLogFont);
3554 		} else {
3555 			/*
3556 			* The run is composed only by white spaces, this happens when a run is split
3557 			* by a visual style. The font fallback for the script can not be determined
3558 			* using only white spaces. The solution is to use the font of the previous
3559 			* or next run of the same script.
3560 			*/
3561 			int index = 0;
3562 			while (index < allRuns.length - 1) {
3563 				if (allRuns[index] == run) {
3564 					if (index > 0) {
3565 						StyleItem pRun = allRuns[index - 1];
3566 						if (pRun.analysis.eScript == run.analysis.eScript) {
3567 							long pFont = getItemFont(pRun);
3568 							LOGFONT logFont = new LOGFONT ();
3569 							OS.GetObject(pFont, LOGFONT.sizeof, logFont);
3570 							newFont = OS.CreateFontIndirect(logFont);
3571 						}
3572 					}
3573 					if (newFont == 0) {
3574 						if (index + 1 < allRuns.length - 1) {
3575 							StyleItem nRun = allRuns[index + 1];
3576 							if (nRun.analysis.eScript == run.analysis.eScript) {
3577 								OS.SelectObject(hdc, getItemFont(nRun));
3578 								shape(hdc, nRun);
3579 								long nFont = getItemFont(nRun);
3580 								LOGFONT logFont = new LOGFONT ();
3581 								OS.GetObject(nFont, LOGFONT.sizeof, logFont);
3582 								newFont = OS.CreateFontIndirect(logFont);
3583 							}
3584 						}
3585 					}
3586 					break;
3587 				}
3588 				index++;
3589 			}
3590 		}
3591 		if (newFont != 0) {
3592 			OS.SelectObject(hdc, newFont);
3593 			if (shapeSucceed = shape(hdc, run, chars, buffer,  maxGlyphs, sp)) {
3594 				run.fallbackFont = newFont;
3595 			}
3596 		}
3597 		if (!shapeSucceed) {
3598 			if (!sp.fComplex) {
3599 				run.analysis.fNoGlyphIndex = true;
3600 				if (shapeSucceed = shape(hdc, run, chars, buffer,  maxGlyphs, sp)) {
3601 					run.fallbackFont = newFont;
3602 				} else {
3603 					run.analysis.fNoGlyphIndex = false;
3604 				}
3605 			}
3606 		}
3607 		if (!shapeSucceed) {
3608 			if (mLangFontLink2 != null) {
3609 				long [] hNewFont = new long [1];
3610 				int[] dwCodePages = new int[1], cchCodePages = new int[1];
3611 				mLangFontLink2.GetStrCodePages(chars, chars.length, 0, dwCodePages, cchCodePages);
3612 				if (mLangFontLink2.MapFont(hdc, dwCodePages[0], chars[0], hNewFont) == OS.S_OK) {
3613 					LOGFONT logFont = new LOGFONT ();
3614 					OS.GetObject(hNewFont[0], LOGFONT.sizeof, logFont);
3615 					mLangFontLink2.ReleaseFont(hNewFont[0]);
3616 					long mLangFont = OS.CreateFontIndirect(logFont);
3617 					long oldFont = OS.SelectObject(hdc, mLangFont);
3618 					if (shapeSucceed = shape(hdc, run, chars, buffer,  maxGlyphs, sp)) {
3619 						run.fallbackFont = mLangFont;
3620 					} else {
3621 						OS.SelectObject(hdc, oldFont);
3622 						OS.DeleteObject(mLangFont);
3623 					}
3624 				}
3625 			}
3626 		}
3627 		if (!shapeSucceed) OS.SelectObject(hdc, hFont);
3628 		if (newFont != 0 && newFont != run.fallbackFont) OS.DeleteObject(newFont);
3629 	}
3630 
3631 	if (!shapeSucceed) {
3632 		/*
3633 		* Shape Failed.
3634 		* Give up and shape the run with the default font.
3635 		* Missing glyphs typically will be represent as black boxes in the text.
3636 		*/
3637 		OS.ScriptShape(hdc, run.psc, chars, chars.length, maxGlyphs, run.analysis, run.glyphs, run.clusters, run.visAttrs, buffer);
3638 		run.glyphCount = buffer[0];
3639 	}
3640 	int[] abc = new int[3];
3641 	run.advances = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, run.glyphCount * 4);
3642 	if (run.advances == 0) SWT.error(SWT.ERROR_NO_HANDLES);
3643 	run.goffsets = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, run.glyphCount * GOFFSET_SIZEOF);
3644 	if (run.goffsets == 0) SWT.error(SWT.ERROR_NO_HANDLES);
3645 	OS.ScriptPlace(hdc, run.psc, run.glyphs, run.glyphCount, run.visAttrs, run.analysis, run.advances, run.goffsets, abc);
3646 	run.width = abc[0] + abc[1] + abc[2];
3647 	TextStyle style = run.style;
3648 	if (style != null) {
3649 		OUTLINETEXTMETRIC lotm = null;
3650 		if (style.underline || style.strikeout) {
3651 			lotm = new OUTLINETEXTMETRIC();
3652 			if (OS.GetOutlineTextMetrics(hdc, OUTLINETEXTMETRIC.sizeof, lotm) == 0) {
3653 				lotm = null;
3654 			}
3655 		}
3656 		if (style.metrics != null) {
3657 			GlyphMetrics metrics = style.metrics;
3658 			/*
3659 			 *  Bug in Windows, on a Japanese machine, Uniscribe returns glyphcount
3660 			 *  equals zero for FFFC (possibly other unicode code points), the fix
3661 			 *  is to make sure the glyph is at least one pixel wide.
3662 			 */
3663 			run.width = metrics.getWidthInPixels() * Math.max (1, run.glyphCount);
3664 			run.ascentInPoints = metrics.ascent;
3665 			run.descentInPoints = metrics.descent;
3666 			run.leadingInPoints = 0;
3667 		} else {
3668 			TEXTMETRIC lptm = null;
3669 			if (lotm != null) {
3670 				lptm = lotm.otmTextMetrics;
3671 			} else {
3672 				lptm = new TEXTMETRIC();
3673 				OS.GetTextMetrics(hdc, lptm);
3674 			}
3675 			run.ascentInPoints = DPIUtil.autoScaleDown(getDevice(), lptm.tmAscent);
3676 			run.descentInPoints = DPIUtil.autoScaleDown(getDevice(), lptm.tmDescent);
3677 			run.leadingInPoints = DPIUtil.autoScaleDown(getDevice(), lptm.tmInternalLeading);
3678 		}
3679 		if (lotm != null) {
3680 			run.underlinePos = lotm.otmsUnderscorePosition;
3681 			run.underlineThickness = Math.max(1, lotm.otmsUnderscoreSize);
3682 			run.strikeoutPos = lotm.otmsStrikeoutPosition;
3683 			run.strikeoutThickness = Math.max(1, lotm.otmsStrikeoutSize);
3684 		} else {
3685 			run.underlinePos = 1;
3686 			run.underlineThickness = 1;
3687 			run.strikeoutPos = DPIUtil.autoScaleUp(getDevice(), run.ascentInPoints) / 2;
3688 			run.strikeoutThickness = 1;
3689 		}
3690 		run.ascentInPoints += style.rise;
3691 		run.descentInPoints -= style.rise;
3692 	} else {
3693 		TEXTMETRIC lptm = new TEXTMETRIC();
3694 		OS.GetTextMetrics(hdc, lptm);
3695 		run.ascentInPoints = DPIUtil.autoScaleDown(getDevice(), lptm.tmAscent);
3696 		run.descentInPoints = DPIUtil.autoScaleDown(getDevice(), lptm.tmDescent);
3697 		run.leadingInPoints = DPIUtil.autoScaleDown(getDevice(), lptm.tmInternalLeading);
3698 	}
3699 }
3700 
3701 int validadeOffset(int offset, int step) {
3702 	offset = untranslateOffset(offset);
3703 	return translateOffset(offset + step);
3704 }
3705 
3706 /**
3707  * Returns a string containing a concise, human-readable
3708  * description of the receiver.
3709  *
3710  * @return a string representation of the receiver
3711  */
3712 @Override
3713 public String toString () {
3714 	if (isDisposed()) return "TextLayout {*DISPOSED*}";
3715 	return "TextLayout {}";
3716 }
3717 
3718 int translateOffset(int offset) {
3719 	int length = text.length();
3720 	if (length == 0) return offset;
3721 	if (segments == null) return offset;
3722 	int nSegments = segments.length;
3723 	if (nSegments == 0) return offset;
3724 	if (segmentsChars == null) {
3725 		if (nSegments == 1) return offset;
3726 		if (nSegments == 2) {
3727 			if (segments[0] == 0 && segments[1] == length) return offset;
3728 		}
3729 	}
3730 	for (int i = 0; i < nSegments && offset - i >= segments[i]; i++) {
3731 		offset++;
3732 	}
3733 	return offset;
3734 }
3735 
3736 int untranslateOffset(int offset) {
3737 	int length = text.length();
3738 	if (length == 0) return offset;
3739 	if (segments == null) return offset;
3740 	int nSegments = segments.length;
3741 	if (nSegments == 0) return offset;
3742 	if (segmentsChars == null) {
3743 		if (nSegments == 1) return offset;
3744 		if (nSegments == 2) {
3745 			if (segments[0] == 0 && segments[1] == length) return offset;
3746 		}
3747 	}
3748 	for (int i = 0; i < nSegments && offset > segments[i]; i++) {
3749 		offset--;
3750 	}
3751 	return offset;
3752 }
3753 
3754 /**
3755  * Sets Default Tab Width in terms if number of space characters.
3756  *
3757  * @param tabLength in number of characters
3758  *
3759  * @exception IllegalArgumentException <ul>
3760  *    <li>ERROR_INVALID_ARGUMENT - if the tabLength is less than <code>0</code></li>
3761  * </ul>
3762  * @exception SWTException <ul>
3763  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
3764  * </ul>
3765  *
3766  * @noreference This method is not intended to be referenced by clients.
3767  *
3768  * DO NOT USE This might be removed in 4.8
3769  * @since 3.107
3770  */
3771 public void setDefaultTabWidth(int tabLength) {
3772 	// unused in win32
3773 }
3774 }
3775