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  *     Andrey Loskutov <loskutov@gmx.de> - bug 488172
14  *     Stefan Xenos (Google) - bug 487254 - StyledText.getTopIndex() can return negative values
15  *     Angelo Zerr <angelo.zerr@gmail.com> - Customize different line spacing of StyledText - Bug 522020
16  *     Karsten Thoms <thoms@itemis.de> - bug 528746 add getOffsetAtPoint(Point)
17  *******************************************************************************/
18 package org.eclipse.swt.custom;
19 
20 
21 import java.util.*;
22 import java.util.List;
23 
24 import org.eclipse.swt.*;
25 import org.eclipse.swt.accessibility.*;
26 import org.eclipse.swt.dnd.*;
27 import org.eclipse.swt.events.*;
28 import org.eclipse.swt.graphics.*;
29 import org.eclipse.swt.internal.*;
30 import org.eclipse.swt.printing.*;
31 import org.eclipse.swt.widgets.*;
32 
33 /**
34  * A StyledText is an editable user interface object that displays lines
35  * of text.  The following style attributes can be defined for the text:
36  * <ul>
37  * <li>foreground color
38  * <li>background color
39  * <li>font style (bold, italic, bold-italic, regular)
40  * <li>underline
41  * <li>strikeout
42  * </ul>
43  * <p>
44  * In addition to text style attributes, the background color of a line may
45  * be specified.
46  * </p><p>
47  * There are two ways to use this widget when specifying text style information.
48  * You may use the API that is defined for StyledText or you may define your own
49  * LineStyleListener.  If you define your own listener, you will be responsible
50  * for maintaining the text style information for the widget.  IMPORTANT: You may
51  * not define your own listener and use the StyledText API.  The following
52  * StyledText API is not supported if you have defined a LineStyleListener:</p>
53  * <ul>
54  * <li>getStyleRangeAtOffset(int)
55  * <li>getStyleRanges()
56  * <li>replaceStyleRanges(int,int,StyleRange[])
57  * <li>setStyleRange(StyleRange)
58  * <li>setStyleRanges(StyleRange[])
59  * </ul>
60  * <p>
61  * There are two ways to use this widget when specifying line background colors.
62  * You may use the API that is defined for StyledText or you may define your own
63  * LineBackgroundListener.  If you define your own listener, you will be responsible
64  * for maintaining the line background color information for the widget.
65  * IMPORTANT: You may not define your own listener and use the StyledText API.
66  * The following StyledText API is not supported if you have defined a
67  * LineBackgroundListener:</p>
68  * <ul>
69  * <li>getLineBackground(int)
70  * <li>setLineBackground(int,int,Color)
71  * </ul>
72  * <p>
73  * The content implementation for this widget may also be user-defined.  To do so,
74  * you must implement the StyledTextContent interface and use the StyledText API
75  * setContent(StyledTextContent) to initialize the widget.
76  * </p>
77  * <dl>
78  * <dt><b>Styles:</b><dd>FULL_SELECTION, MULTI, READ_ONLY, SINGLE, WRAP
79  * <dt><b>Events:</b><dd>ExtendedModify, LineGetBackground, LineGetSegments, LineGetStyle, Modify, Selection, Verify, VerifyKey, OrientationChange
80  * </dl>
81  * <p>
82  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
83  * </p>
84  *
85  * @see <a href="http://www.eclipse.org/swt/snippets/#styledtext">StyledText snippets</a>
86  * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Examples: CustomControlExample, TextEditor</a>
87  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
88  * @noextend This class is not intended to be subclassed by clients.
89  */
90 public class StyledText extends Canvas {
91 	static final char TAB = '\t';
92 	static final String PlatformLineDelimiter = System.lineSeparator();
93 	static final int BIDI_CARET_WIDTH = 3;
94 	static final int DEFAULT_WIDTH	= 64;
95 	static final int DEFAULT_HEIGHT = 64;
96 	static final int V_SCROLL_RATE = 50;
97 	static final int H_SCROLL_RATE = 10;
98 	static final int PREVIOUS_OFFSET_TRAILING = 0;
99 	static final int OFFSET_LEADING = 1;
100 
101 	static final String STYLEDTEXT_KEY = "org.eclipse.swt.internal.cocoa.styledtext"; //$NON-NLS-1$
102 
103 	Color selectionBackground;	// selection background color
104 	Color selectionForeground;	// selection foreground color
105 	StyledTextContent content;			// native content (default or user specified)
106 	StyledTextRenderer renderer;
107 	Listener listener;
108 	TextChangeListener textChangeListener;	// listener for TextChanging, TextChanged and TextSet events from StyledTextContent
109 	int verticalScrollOffset = 0;		// pixel based
110 	int horizontalScrollOffset = 0;		// pixel based
111 	boolean alwaysShowScroll = true;
112 	int ignoreResize = 0;
113 	int topIndex = 0;					// top visible line
114 	int topIndexY;
115 	int clientAreaHeight = 0;			// the client area height. Needed to calculate content width for new visible lines during Resize callback
116 	int clientAreaWidth = 0;			// the client area width. Needed during Resize callback to determine if line wrap needs to be recalculated
117 	int tabLength = 4;					// number of characters in a tab
118 	int [] tabs;
119 	int leftMargin;
120 	int topMargin;
121 	int rightMargin;
122 	int bottomMargin;
123 	Color marginColor;
124 	int columnX;						// keep track of the horizontal caret position when changing lines/pages. Fixes bug 5935
125 	int caretOffset;
126 	int caretAlignment;
127 	Point selection = new Point(0, 0);	// x and y are start and end caret offsets of selection (x <= y)
128 	Point clipboardSelection;           // x and y are start and end caret offsets of previous selection
129 	int selectionAnchor;				// position of selection anchor. 0 based offset from beginning of text
130 	Point doubleClickSelection;			// selection after last mouse double click
131 	boolean editable = true;
132 	boolean wordWrap = false;			// text is wrapped automatically
133 	boolean visualWrap = false;		// process line breaks inside logical lines (inserted by BidiSegmentEvent)
134 	boolean hasStyleWithVariableHeight = false;
135 	boolean hasVerticalIndent = false;
136 	boolean doubleClickEnabled = true;	// see getDoubleClickEnabled
137 	boolean overwrite = false;			// insert/overwrite edit mode
138 	int textLimit = -1;					// limits the number of characters the user can type in the widget. Unlimited by default.
139 	Map<Integer, Integer> keyActionMap = new HashMap<>();
140 	Color background = null;			// workaround for bug 4791
141 	Color foreground = null;			//
142 	/** True if a non-default background color is set */
143 	boolean customBackground;
144 	/** True if a non-default foreground color is set */
145 	boolean customForeground;
146 	/** False iff the widget is disabled */
147 	boolean enabled = true;
148 	/** True iff the widget is in the midst of being enabled or disabled */
149 	boolean insideSetEnableCall;
150 	Clipboard clipboard;
151 	int clickCount;
152 	int autoScrollDirection = SWT.NULL;	// the direction of autoscrolling (up, down, right, left)
153 	int autoScrollDistance = 0;
154 	int lastTextChangeStart;			// cache data of the
155 	int lastTextChangeNewLineCount;		// last text changing
156 	int lastTextChangeNewCharCount;		// event for use in the
157 	int lastTextChangeReplaceLineCount;	// text changed handler
158 	int lastTextChangeReplaceCharCount;
159 	int lastCharCount = 0;
160 	int lastLineBottom;					// the bottom pixel of the last line been replaced
161 	boolean bidiColoring = false;		// apply the BIDI algorithm on text segments of the same color
162 	Image leftCaretBitmap = null;
163 	Image rightCaretBitmap = null;
164 	int caretDirection = SWT.NULL;
165 	int caretWidth = 0;
166 	Caret defaultCaret = null;
167 	boolean updateCaretDirection = true;
168 	boolean dragDetect = true;
169 	IME ime;
170 	Cursor cursor;
171 	int alignment;
172 	boolean justify;
173 	int indent, wrapIndent;
174 	int lineSpacing;
175 	int alignmentMargin;
176 	int newOrientation = SWT.NONE;
177 	int accCaretOffset;
178 	Accessible acc;
179 	AccessibleControlAdapter accControlAdapter;
180 	AccessibleAttributeAdapter accAttributeAdapter;
181 	AccessibleEditableTextListener accEditableTextListener;
182 	AccessibleTextExtendedAdapter accTextExtendedAdapter;
183 	AccessibleAdapter accAdapter;
184 	MouseNavigator mouseNavigator;
185 	boolean middleClickPressed;
186 
187 	//block selection
188 	boolean blockSelection;
189 	int blockXAnchor = -1, blockYAnchor = -1;
190 	int blockXLocation = -1, blockYLocation = -1;
191 
192 	final static boolean IS_MAC, IS_GTK;
193 	static {
194 		String platform = SWT.getPlatform();
195 		IS_MAC = "cocoa".equals(platform);
196 		IS_GTK = "gtk".equals(platform);
197 	}
198 
199 	/**
200 	 * The Printing class implements printing of a range of text.
201 	 * An instance of <code>Printing</code> is returned in the
202 	 * StyledText#print(Printer) API. The run() method may be
203 	 * invoked from any thread.
204 	 */
205 	static class Printing implements Runnable {
206 		final static int LEFT = 0;						// left aligned header/footer segment
207 		final static int CENTER = 1;					// centered header/footer segment
208 		final static int RIGHT = 2;						// right aligned header/footer segment
209 
210 		Printer printer;
211 		StyledTextRenderer printerRenderer;
212 		StyledTextPrintOptions printOptions;
213 		Rectangle clientArea;
214 		FontData fontData;
215 		Font printerFont;
216 		Map<Resource, Resource> resources;
217 		int tabLength;
218 		GC gc;											// printer GC
219 		int pageWidth;									// width of a printer page in pixels
220 		int startPage;									// first page to print
221 		int endPage;									// last page to print
222 		int scope;										// scope of print job
223 		int startLine;									// first (wrapped) line to print
224 		int endLine;									// last (wrapped) line to print
225 		boolean singleLine;								// widget single line mode
226 		Point selection = null;					// selected text
227 		boolean mirrored;						// indicates the printing gc should be mirrored
228 		int lineSpacing;
229 		int printMargin;
230 
231 	/**
232 	 * Creates an instance of <code>Printing</code>.
233 	 * Copies the widget content and rendering data that needs
234 	 * to be requested from listeners.
235 	 *
236 	 * @param parent StyledText widget to print.
237 	 * @param printer printer device to print on.
238 	 * @param printOptions print options
239 	 */
Printing(StyledText styledText, Printer printer, StyledTextPrintOptions printOptions)240 	Printing(StyledText styledText, Printer printer, StyledTextPrintOptions printOptions) {
241 		this.printer = printer;
242 		this.printOptions = printOptions;
243 		this.mirrored = (styledText.getStyle() & SWT.MIRRORED) != 0;
244 		singleLine = styledText.isSingleLine();
245 		startPage = 1;
246 		endPage = Integer.MAX_VALUE;
247 		PrinterData data = printer.getPrinterData();
248 		scope = data.scope;
249 		if (scope == PrinterData.PAGE_RANGE) {
250 			startPage = data.startPage;
251 			endPage = data.endPage;
252 			if (endPage < startPage) {
253 				int temp = endPage;
254 				endPage = startPage;
255 				startPage = temp;
256 			}
257 		} else if (scope == PrinterData.SELECTION) {
258 			selection = styledText.getSelectionRange();
259 		}
260 		printerRenderer = new StyledTextRenderer(printer, null);
261 		printerRenderer.setContent(copyContent(styledText.getContent()));
262 		cacheLineData(styledText);
263 	}
264 	/**
265 	 * Caches all line data that needs to be requested from a listener.
266 	 *
267 	 * @param printerContent <code>StyledTextContent</code> to request
268 	 * 	line data for.
269 	 */
cacheLineData(StyledText styledText)270 	void cacheLineData(StyledText styledText) {
271 		StyledTextRenderer renderer = styledText.renderer;
272 		renderer.copyInto(printerRenderer);
273 		fontData = styledText.getFont().getFontData()[0];
274 		tabLength = styledText.tabLength;
275 		int lineCount = printerRenderer.lineCount;
276 		if (styledText.isListening(ST.LineGetBackground) || (styledText.isListening(ST.LineGetSegments)) || styledText.isListening(ST.LineGetStyle)) {
277 			StyledTextContent content = printerRenderer.content;
278 			for (int i = 0; i < lineCount; i++) {
279 				String line = content.getLine(i);
280 				int lineOffset = content.getOffsetAtLine(i);
281 				StyledTextEvent event = styledText.getLineBackgroundData(lineOffset, line);
282 				if (event != null && event.lineBackground != null) {
283 					printerRenderer.setLineBackground(i, 1, event.lineBackground);
284 				}
285 				event = styledText.getBidiSegments(lineOffset, line);
286 				if (event != null) {
287 					printerRenderer.setLineSegments(i, 1, event.segments);
288 					printerRenderer.setLineSegmentChars(i, 1, event.segmentsChars);
289 				}
290 				event = styledText.getLineStyleData(lineOffset, line);
291 				if (event != null) {
292 					printerRenderer.setLineIndent(i, 1, event.indent);
293 					printerRenderer.setLineAlignment(i, 1, event.alignment);
294 					printerRenderer.setLineJustify(i, 1, event.justify);
295 					printerRenderer.setLineBullet(i, 1, event.bullet);
296 					StyleRange[] styles = event.styles;
297 					if (styles != null && styles.length > 0) {
298 						printerRenderer.setStyleRanges(event.ranges, styles);
299 					}
300 				}
301 			}
302 		}
303 		Point screenDPI = styledText.getDisplay().getDPI();
304 		Point printerDPI = printer.getDPI();
305 		resources = new HashMap<> ();
306 		for (int i = 0; i < lineCount; i++) {
307 			Color color = printerRenderer.getLineBackground(i, null);
308 			if (color != null) {
309 				if (printOptions.printLineBackground) {
310 					Color printerColor = (Color)resources.get(color);
311 					if (printerColor == null) {
312 						printerColor = new Color (color.getRGB());
313 						resources.put(color, printerColor);
314 					}
315 					printerRenderer.setLineBackground(i, 1, printerColor);
316 				} else {
317 					printerRenderer.setLineBackground(i, 1, null);
318 				}
319 			}
320 			int indent = printerRenderer.getLineIndent(i, 0);
321 			if (indent != 0) {
322 				printerRenderer.setLineIndent(i, 1, indent * printerDPI.x / screenDPI.x);
323 			}
324 		}
325 		StyleRange[] styles = printerRenderer.styles;
326 		for (int i = 0; i < printerRenderer.styleCount; i++) {
327 			StyleRange style = styles[i];
328 			Font font = style.font;
329 			if (style.font != null) {
330 				Font printerFont = (Font)resources.get(font);
331 				if (printerFont == null) {
332 					printerFont = new Font (printer, font.getFontData());
333 					resources.put(font, printerFont);
334 				}
335 				style.font = printerFont;
336 			}
337 			Color color = style.foreground;
338 			if (color != null) {
339 				Color printerColor = (Color)resources.get(color);
340 				if (printOptions.printTextForeground) {
341 					if (printerColor == null) {
342 						printerColor = new Color (color.getRGB());
343 						resources.put(color, printerColor);
344 					}
345 					style.foreground = printerColor;
346 				} else {
347 					style.foreground = null;
348 				}
349 			}
350 			color = style.background;
351 			if (color != null) {
352 				Color printerColor = (Color)resources.get(color);
353 				if (printOptions.printTextBackground) {
354 					if (printerColor == null) {
355 						printerColor = new Color (color.getRGB());
356 						resources.put(color, printerColor);
357 					}
358 					style.background = printerColor;
359 				} else {
360 					style.background = null;
361 				}
362 			}
363 			if (!printOptions.printTextFontStyle) {
364 				style.fontStyle = SWT.NORMAL;
365 			}
366 			style.rise = style.rise * printerDPI.y / screenDPI.y;
367 			GlyphMetrics metrics = style.metrics;
368 			if (metrics != null) {
369 				metrics.ascent = metrics.ascent * printerDPI.y / screenDPI.y;
370 				metrics.descent = metrics.descent * printerDPI.y / screenDPI.y;
371 				metrics.width = metrics.width * printerDPI.x / screenDPI.x;
372 			}
373 		}
374 		lineSpacing = styledText.lineSpacing * printerDPI.y / screenDPI.y;
375 		if (printOptions.printLineNumbers) {
376 			printMargin = 3 * printerDPI.x / screenDPI.x;
377 		}
378 	}
379 	/**
380 	 * Copies the text of the specified <code>StyledTextContent</code>.
381 	 *
382 	 * @param original the <code>StyledTextContent</code> to copy.
383 	 */
copyContent(StyledTextContent original)384 	StyledTextContent copyContent(StyledTextContent original) {
385 		StyledTextContent printerContent = new DefaultContent();
386 		int insertOffset = 0;
387 		for (int i = 0; i < original.getLineCount(); i++) {
388 			int insertEndOffset;
389 			if (i < original.getLineCount() - 1) {
390 				insertEndOffset = original.getOffsetAtLine(i + 1);
391 			} else {
392 				insertEndOffset = original.getCharCount();
393 			}
394 			printerContent.replaceTextRange(insertOffset, 0, original.getTextRange(insertOffset, insertEndOffset - insertOffset));
395 			insertOffset = insertEndOffset;
396 		}
397 		return printerContent;
398 	}
399 	/**
400 	 * Disposes of the resources and the <code>PrintRenderer</code>.
401 	 */
dispose()402 	void dispose() {
403 		if (gc != null) {
404 			gc.dispose();
405 			gc = null;
406 		}
407 		if (resources != null) {
408 			for (Resource resource : resources.values()) {
409 				resource.dispose();
410 			}
411 			resources = null;
412 		}
413 		if (printerFont != null) {
414 			printerFont.dispose();
415 			printerFont = null;
416 		}
417 		if (printerRenderer != null) {
418 			printerRenderer.dispose();
419 			printerRenderer = null;
420 		}
421 	}
init()422 	void init() {
423 		Rectangle trim = printer.computeTrim(0, 0, 0, 0);
424 		Point dpi = printer.getDPI();
425 
426 		printerFont = new Font(printer, fontData.getName(), fontData.getHeight(), SWT.NORMAL);
427 		clientArea = printer.getClientArea();
428 		pageWidth = clientArea.width;
429 		// one inch margin around text
430 		clientArea.x = dpi.x + trim.x;
431 		clientArea.y = dpi.y + trim.y;
432 		clientArea.width -= (clientArea.x + trim.width);
433 		clientArea.height -= (clientArea.y + trim.height);
434 
435 		int style = mirrored ? SWT.RIGHT_TO_LEFT : SWT.LEFT_TO_RIGHT;
436 		gc = new GC(printer, style);
437 		gc.setFont(printerFont);
438 		printerRenderer.setFont(printerFont, tabLength);
439 		int lineHeight = printerRenderer.getLineHeight();
440 		if (printOptions.header != null) {
441 			clientArea.y += lineHeight * 2;
442 			clientArea.height -= lineHeight * 2;
443 		}
444 		if (printOptions.footer != null) {
445 			clientArea.height -= lineHeight * 2;
446 		}
447 
448 		// TODO not wrapped
449 		StyledTextContent content = printerRenderer.content;
450 		startLine = 0;
451 		endLine = singleLine ? 0 : content.getLineCount() - 1;
452 		if (scope == PrinterData.PAGE_RANGE) {
453 			int pageSize = clientArea.height / lineHeight;//WRONG
454 			startLine = (startPage - 1) * pageSize;
455 		} else if (scope == PrinterData.SELECTION) {
456 			startLine = content.getLineAtOffset(selection.x);
457 			if (selection.y > 0) {
458 				endLine = content.getLineAtOffset(selection.x + selection.y - 1);
459 			} else {
460 				endLine = startLine - 1;
461 			}
462 		}
463 	}
464 	/**
465 	 * Prints the lines in the specified page range.
466 	 */
print()467 	void print() {
468 		Color background = gc.getBackground();
469 		Color foreground = gc.getForeground();
470 		int paintY = clientArea.y;
471 		int paintX = clientArea.x;
472 		int width = clientArea.width;
473 		int page = startPage;
474 		int pageBottom = clientArea.y + clientArea.height;
475 		int orientation =  gc.getStyle() & (SWT.RIGHT_TO_LEFT | SWT.LEFT_TO_RIGHT);
476 		TextLayout printLayout = null;
477 		if (printOptions.printLineNumbers || printOptions.header != null || printOptions.footer != null) {
478 			printLayout = new TextLayout(printer);
479 			printLayout.setFont(printerFont);
480 		}
481 		if (printOptions.printLineNumbers) {
482 			int numberingWidth = 0;
483 			int count = endLine - startLine + 1;
484 			String[] lineLabels = printOptions.lineLabels;
485 			if (lineLabels != null) {
486 				for (int i = startLine; i < Math.min(count, lineLabels.length); i++) {
487 					if (lineLabels[i] != null) {
488 						printLayout.setText(lineLabels[i]);
489 						int lineWidth = printLayout.getBounds().width;
490 						numberingWidth = Math.max(numberingWidth, lineWidth);
491 					}
492 				}
493 			} else {
494 				StringBuilder buffer = new StringBuilder("0");
495 				while ((count /= 10) > 0) buffer.append("0");
496 				printLayout.setText(buffer.toString());
497 				numberingWidth = printLayout.getBounds().width;
498 			}
499 			numberingWidth += printMargin;
500 			if (numberingWidth > width) numberingWidth = width;
501 			paintX += numberingWidth;
502 			width -= numberingWidth;
503 		}
504 		for (int i = startLine; i <= endLine && page <= endPage; i++) {
505 			if (paintY == clientArea.y) {
506 				printer.startPage();
507 				printDecoration(page, true, printLayout);
508 			}
509 			TextLayout layout = printerRenderer.getTextLayout(i, orientation, width, lineSpacing);
510 			Color lineBackground = printerRenderer.getLineBackground(i, background);
511 			int paragraphBottom = paintY + layout.getBounds().height;
512 			if (paragraphBottom <= pageBottom) {
513 				//normal case, the whole paragraph fits in the current page
514 				printLine(paintX, paintY, gc, foreground, lineBackground, layout, printLayout, i);
515 				paintY = paragraphBottom;
516 			} else {
517 				int lineCount = layout.getLineCount();
518 				while (paragraphBottom > pageBottom && lineCount > 0) {
519 					lineCount--;
520 					paragraphBottom -= layout.getLineBounds(lineCount).height + layout.getSpacing();
521 				}
522 				if (lineCount == 0) {
523 					//the whole paragraph goes to the next page
524 					printDecoration(page, false, printLayout);
525 					printer.endPage();
526 					page++;
527 					if (page <= endPage) {
528 						printer.startPage();
529 						printDecoration(page, true, printLayout);
530 						paintY = clientArea.y;
531 						printLine(paintX, paintY, gc, foreground, lineBackground, layout, printLayout, i);
532 						paintY += layout.getBounds().height;
533 					}
534 				} else {
535 					//draw paragraph top in the current page and paragraph bottom in the next
536 					int height = paragraphBottom - paintY;
537 					gc.setClipping(clientArea.x, paintY, clientArea.width, height);
538 					printLine(paintX, paintY, gc, foreground, lineBackground, layout, printLayout, i);
539 					gc.setClipping((Rectangle)null);
540 					printDecoration(page, false, printLayout);
541 					printer.endPage();
542 					page++;
543 					if (page <= endPage) {
544 						printer.startPage();
545 						printDecoration(page, true, printLayout);
546 						paintY = clientArea.y - height;
547 						int layoutHeight = layout.getBounds().height;
548 						gc.setClipping(clientArea.x, clientArea.y, clientArea.width, layoutHeight - height);
549 						printLine(paintX, paintY, gc, foreground, lineBackground, layout, printLayout, i);
550 						gc.setClipping((Rectangle)null);
551 						paintY += layoutHeight;
552 					}
553 				}
554 			}
555 			printerRenderer.disposeTextLayout(layout);
556 		}
557 		if (page <= endPage && paintY > clientArea.y) {
558 			// close partial page
559 			printDecoration(page, false, printLayout);
560 			printer.endPage();
561 		}
562 		if (printLayout != null) printLayout.dispose();
563 	}
564 	/**
565 	 * Print header or footer decorations.
566 	 *
567 	 * @param page page number to print, if specified in the StyledTextPrintOptions header or footer.
568 	 * @param header true = print the header, false = print the footer
569 	 */
printDecoration(int page, boolean header, TextLayout layout)570 	void printDecoration(int page, boolean header, TextLayout layout) {
571 		String text = header ? printOptions.header : printOptions.footer;
572 		if (text == null) return;
573 		int lastSegmentIndex = 0;
574 		for (int i = 0; i < 3; i++) {
575 			int segmentIndex = text.indexOf(StyledTextPrintOptions.SEPARATOR, lastSegmentIndex);
576 			String segment;
577 			if (segmentIndex == -1) {
578 				segment = text.substring(lastSegmentIndex);
579 				printDecorationSegment(segment, i, page, header, layout);
580 				break;
581 			} else {
582 				segment = text.substring(lastSegmentIndex, segmentIndex);
583 				printDecorationSegment(segment, i, page, header, layout);
584 				lastSegmentIndex = segmentIndex + StyledTextPrintOptions.SEPARATOR.length();
585 			}
586 		}
587 	}
588 	/**
589 	 * Print one segment of a header or footer decoration.
590 	 * Headers and footers have three different segments.
591 	 * One each for left aligned, centered, and right aligned text.
592 	 *
593 	 * @param segment decoration segment to print
594 	 * @param alignment alignment of the segment. 0=left, 1=center, 2=right
595 	 * @param page page number to print, if specified in the decoration segment.
596 	 * @param header true = print the header, false = print the footer
597 	 */
printDecorationSegment(String segment, int alignment, int page, boolean header, TextLayout layout)598 	void printDecorationSegment(String segment, int alignment, int page, boolean header, TextLayout layout) {
599 		int pageIndex = segment.indexOf(StyledTextPrintOptions.PAGE_TAG);
600 		if (pageIndex != -1) {
601 			int pageTagLength = StyledTextPrintOptions.PAGE_TAG.length();
602 			StringBuilder buffer = new StringBuilder(segment.substring (0, pageIndex));
603 			buffer.append (page);
604 			buffer.append (segment.substring(pageIndex + pageTagLength));
605 			segment = buffer.toString();
606 		}
607 		if (segment.length() > 0) {
608 			layout.setText(segment);
609 			int segmentWidth = layout.getBounds().width;
610 			int segmentHeight = printerRenderer.getLineHeight();
611 			int drawX = 0, drawY;
612 			if (alignment == LEFT) {
613 				drawX = clientArea.x;
614 			} else if (alignment == CENTER) {
615 				drawX = (pageWidth - segmentWidth) / 2;
616 			} else if (alignment == RIGHT) {
617 				drawX = clientArea.x + clientArea.width - segmentWidth;
618 			}
619 			if (header) {
620 				drawY = clientArea.y - segmentHeight * 2;
621 			} else {
622 				drawY = clientArea.y + clientArea.height + segmentHeight;
623 			}
624 			layout.draw(gc, drawX, drawY);
625 		}
626 	}
printLine(int x, int y, GC gc, Color foreground, Color background, TextLayout layout, TextLayout printLayout, int index)627 	void printLine(int x, int y, GC gc, Color foreground, Color background, TextLayout layout, TextLayout printLayout, int index) {
628 		if (background != null) {
629 			Rectangle rect = layout.getBounds();
630 			gc.setBackground(background);
631 			gc.fillRectangle(x, y, rect.width, rect.height);
632 
633 //			int lineCount = layout.getLineCount();
634 //			for (int i = 0; i < lineCount; i++) {
635 //				Rectangle rect = layout.getLineBounds(i);
636 //				rect.x += paintX;
637 //				rect.y += paintY + layout.getSpacing();
638 //				rect.width = width;//layout bounds
639 //				gc.fillRectangle(rect);
640 //			}
641 		}
642 		if (printOptions.printLineNumbers) {
643 			FontMetrics metrics = layout.getLineMetrics(0);
644 			printLayout.setAscent(metrics.getAscent() + metrics.getLeading());
645 			printLayout.setDescent(metrics.getDescent());
646 			String[] lineLabels = printOptions.lineLabels;
647 			if (lineLabels != null) {
648 				if (0 <= index && index < lineLabels.length && lineLabels[index] != null) {
649 					printLayout.setText(lineLabels[index]);
650 				} else {
651 					printLayout.setText("");
652 				}
653 			} else {
654 				printLayout.setText(String.valueOf(index));
655 			}
656 			int paintX = x - printMargin - printLayout.getBounds().width;
657 			printLayout.draw(gc, paintX, y);
658 			printLayout.setAscent(-1);
659 			printLayout.setDescent(-1);
660 		}
661 		gc.setForeground(foreground);
662 		layout.draw(gc, x, y);
663 	}
664 	/**
665 	 * Starts a print job and prints the pages specified in the constructor.
666 	 */
667 	@Override
run()668 	public void run() {
669 		String jobName = printOptions.jobName;
670 		if (jobName == null) {
671 			jobName = "Printing";
672 		}
673 		if (printer.startJob(jobName)) {
674 			init();
675 			print();
676 			dispose();
677 			printer.endJob();
678 		}
679 	}
680 	}
681 	/**
682 	 * The <code>RTFWriter</code> class is used to write widget content as
683 	 * rich text. The implementation complies with the RTF specification
684 	 * version 1.5.
685 	 * <p>
686 	 * toString() is guaranteed to return a valid RTF string only after
687 	 * close() has been called.
688 	 * </p><p>
689 	 * Whole and partial lines and line breaks can be written. Lines will be
690 	 * formatted using the styles queried from the LineStyleListener, if
691 	 * set, or those set directly in the widget. All styles are applied to
692 	 * the RTF stream like they are rendered by the widget. In addition, the
693 	 * widget font name and size is used for the whole text.
694 	 * </p>
695 	 */
696 	class RTFWriter extends TextWriter {
697 		static final int DEFAULT_FOREGROUND = 0;
698 		static final int DEFAULT_BACKGROUND = 1;
699 		List<Color> colorTable;
700 		List<Font> fontTable;
701 
702 	/**
703 	 * Creates a RTF writer that writes content starting at offset "start"
704 	 * in the document.  <code>start</code> and <code>length</code>can be set to specify partial
705 	 * lines.
706 	 *
707 	 * @param start start offset of content to write, 0 based from
708 	 * 	beginning of document
709 	 * @param length length of content to write
710 	 */
RTFWriter(int start, int length)711 	public RTFWriter(int start, int length) {
712 		super(start, length);
713 		colorTable = new ArrayList<>();
714 		fontTable = new ArrayList<>();
715 		colorTable.add(getForeground());
716 		colorTable.add(getBackground());
717 		fontTable.add(getFont());
718 	}
719 	/**
720 	 * Closes the RTF writer. Once closed no more content can be written.
721 	 * <b>NOTE:</b>  <code>toString()</code> does not return a valid RTF string until
722 	 * <code>close()</code> has been called.
723 	 */
724 	@Override
close()725 	public void close() {
726 		if (!isClosed()) {
727 			writeHeader();
728 			write("\n}}\0");
729 			super.close();
730 		}
731 	}
732 	/**
733 	 * Returns the index of the specified color in the RTF color table.
734 	 *
735 	 * @param color the color
736 	 * @param defaultIndex return value if color is null
737 	 * @return the index of the specified color in the RTF color table
738 	 * 	or "defaultIndex" if "color" is null.
739 	 */
getColorIndex(Color color, int defaultIndex)740 	int getColorIndex(Color color, int defaultIndex) {
741 		if (color == null) return defaultIndex;
742 		int index = colorTable.indexOf(color);
743 		if (index == -1) {
744 			index = colorTable.size();
745 			colorTable.add(color);
746 		}
747 		return index;
748 	}
749 	/**
750 	 * Returns the index of the specified color in the RTF color table.
751 	 *
752 	 * @param color the color
753 	 * @param defaultIndex return value if color is null
754 	 * @return the index of the specified color in the RTF color table
755 	 * 	or "defaultIndex" if "color" is null.
756 	 */
getFontIndex(Font font)757 	int getFontIndex(Font font) {
758 		int index = fontTable.indexOf(font);
759 		if (index == -1) {
760 			index = fontTable.size();
761 			fontTable.add(font);
762 		}
763 		return index;
764 	}
765 	/**
766 	 * Appends the specified segment of "string" to the RTF data.
767 	 * Copy from <code>start</code> up to, but excluding, <code>end</code>.
768 	 *
769 	 * @param string string to copy a segment from. Must not contain
770 	 * 	line breaks. Line breaks should be written using writeLineDelimiter()
771 	 * @param start start offset of segment. 0 based.
772 	 * @param end end offset of segment
773 	 */
write(String string, int start, int end)774 	void write(String string, int start, int end) {
775 		for (int index = start; index < end; index++) {
776 			char ch = string.charAt(index);
777 			if (ch > 0x7F) {
778 				// write the sub string from the last escaped character
779 				// to the current one. Fixes bug 21698.
780 				if (index > start) {
781 					write(string.substring(start, index));
782 				}
783 				write("\\u");
784 				write(Integer.toString((short) ch));
785 				write('?');						// ANSI representation (1 byte long, \\uc1)
786 				start = index + 1;
787 			} else if (ch == '}' || ch == '{' || ch == '\\') {
788 				// write the sub string from the last escaped character
789 				// to the current one. Fixes bug 21698.
790 				if (index > start) {
791 					write(string.substring(start, index));
792 				}
793 				write('\\');
794 				write(ch);
795 				start = index + 1;
796 			}
797 		}
798 		// write from the last escaped character to the end.
799 		// Fixes bug 21698.
800 		if (start < end) {
801 			write(string.substring(start, end));
802 		}
803 	}
804 	/**
805 	 * Writes the RTF header including font table and color table.
806 	 */
writeHeader()807 	void writeHeader() {
808 		StringBuilder header = new StringBuilder();
809 		FontData fontData = getFont().getFontData()[0];
810 		header.append("{\\rtf1\\ansi");
811 		// specify code page, necessary for copy to work in bidi
812 		// systems that don't support Unicode RTF.
813 		String cpg = System.getProperty("file.encoding").toLowerCase();
814 		if (cpg.startsWith("cp") || cpg.startsWith("ms")) {
815 			cpg = cpg.substring(2, cpg.length());
816 			header.append("\\ansicpg");
817 			header.append(cpg);
818 		}
819 		header.append("\\uc1\\deff0{\\fonttbl{\\f0\\fnil ");
820 		header.append(fontData.getName());
821 		header.append(";");
822 		for (int i = 1; i < fontTable.size(); i++) {
823 			header.append("\\f");
824 			header.append(i);
825 			header.append(" ");
826 			FontData fd = fontTable.get(i).getFontData()[0];
827 			header.append(fd.getName());
828 			header.append(";");
829 		}
830 		header.append("}}\n{\\colortbl");
831 		for (Color color : colorTable) {
832 			header.append("\\red");
833 			header.append(color.getRed());
834 			header.append("\\green");
835 			header.append(color.getGreen());
836 			header.append("\\blue");
837 			header.append(color.getBlue());
838 			header.append(";");
839 		}
840 		// some RTF readers ignore the deff0 font tag. Explicitly
841 		// set the font for the whole document to work around this.
842 		header.append("}\n{\\f0\\fs");
843 		// font size is specified in half points
844 		header.append(fontData.getHeight() * 2);
845 		header.append(" ");
846 		write(header.toString(), 0);
847 	}
848 	/**
849 	 * Appends the specified line text to the RTF data.  Lines will be formatted
850 	 * using the styles queried from the LineStyleListener, if set, or those set
851 	 * directly in the widget.
852 	 *
853 	 * @param line line text to write as RTF. Must not contain line breaks
854 	 * 	Line breaks should be written using writeLineDelimiter()
855 	 * @param lineOffset offset of the line. 0 based from the start of the
856 	 * 	widget document. Any text occurring before the start offset or after the
857 	 * 	end offset specified during object creation is ignored.
858 	 * @exception SWTException <ul>
859 	 *   <li>ERROR_IO when the writer is closed.</li>
860 	 * </ul>
861 	 */
862 	@Override
writeLine(String line, int lineOffset)863 	public void writeLine(String line, int lineOffset) {
864 		if (isClosed()) {
865 			SWT.error(SWT.ERROR_IO);
866 		}
867 		int lineIndex = content.getLineAtOffset(lineOffset);
868 		int lineAlignment, lineIndent;
869 		boolean lineJustify;
870 		int[] ranges;
871 		StyleRange[] styles;
872 		StyledTextEvent event = getLineStyleData(lineOffset, line);
873 		if (event != null) {
874 			lineAlignment = event.alignment;
875 			lineIndent = event.indent;
876 			lineJustify = event.justify;
877 			ranges = event.ranges;
878 			styles = event.styles;
879 		} else {
880 			lineAlignment = renderer.getLineAlignment(lineIndex, alignment);
881 			lineIndent =  renderer.getLineIndent(lineIndex, indent);
882 			lineJustify = renderer.getLineJustify(lineIndex, justify);
883 			ranges = renderer.getRanges(lineOffset, line.length());
884 			styles = renderer.getStyleRanges(lineOffset, line.length(), false);
885 		}
886 		if (styles == null) styles = new StyleRange[0];
887 		Color lineBackground = renderer.getLineBackground(lineIndex, null);
888 		event = getLineBackgroundData(lineOffset, line);
889 		if (event != null && event.lineBackground != null) lineBackground = event.lineBackground;
890 		writeStyledLine(line, lineOffset, ranges, styles, lineBackground, lineIndent, lineAlignment, lineJustify);
891 	}
892 	/**
893 	 * Appends the specified line delimiter to the RTF data.
894 	 *
895 	 * @param lineDelimiter line delimiter to write as RTF.
896 	 * @exception SWTException <ul>
897 	 *   <li>ERROR_IO when the writer is closed.</li>
898 	 * </ul>
899 	 */
900 	@Override
writeLineDelimiter(String lineDelimiter)901 	public void writeLineDelimiter(String lineDelimiter) {
902 		if (isClosed()) {
903 			SWT.error(SWT.ERROR_IO);
904 		}
905 		write(lineDelimiter, 0, lineDelimiter.length());
906 		write("\\par ");
907 	}
908 	/**
909 	 * Appends the specified line text to the RTF data.
910 	 * <p>
911 	 * Use the colors and font styles specified in "styles" and "lineBackground".
912 	 * Formatting is written to reflect the text rendering by the text widget.
913 	 * Style background colors take precedence over the line background color.
914 	 * Background colors are written using the \chshdng0\chcbpat tag (vs. the \cb tag).
915 	 * </p>
916 	 *
917 	 * @param line line text to write as RTF. Must not contain line breaks
918 	 * 	Line breaks should be written using writeLineDelimiter()
919 	 * @param lineOffset offset of the line. 0 based from the start of the
920 	 * 	widget document. Any text occurring before the start offset or after the
921 	 * 	end offset specified during object creation is ignored.
922 	 * @param styles styles to use for formatting. Must not be null.
923 	 * @param lineBackground line background color to use for formatting.
924 	 * 	May be null.
925 	 */
writeStyledLine(String line, int lineOffset, int ranges[], StyleRange[] styles, Color lineBackground, int indent, int alignment, boolean justify)926 	void writeStyledLine(String line, int lineOffset, int ranges[], StyleRange[] styles, Color lineBackground, int indent, int alignment, boolean justify) {
927 		int lineLength = line.length();
928 		int startOffset = getStart();
929 		int writeOffset = startOffset - lineOffset;
930 		if (writeOffset >= lineLength) return;
931 		int lineIndex = Math.max(0, writeOffset);
932 
933 		write("\\fi");
934 		write(indent);
935 		switch (alignment) {
936 			case SWT.LEFT: write("\\ql"); break;
937 			case SWT.CENTER: write("\\qc"); break;
938 			case SWT.RIGHT: write("\\qr"); break;
939 		}
940 		if (justify) write("\\qj");
941 		write(" ");
942 
943 		if (lineBackground != null) {
944 			write("{\\chshdng0\\chcbpat");
945 			write(getColorIndex(lineBackground, DEFAULT_BACKGROUND));
946 			write(" ");
947 		}
948 		int endOffset = startOffset + super.getCharCount();
949 		int lineEndOffset = Math.min(lineLength, endOffset - lineOffset);
950 		for (int i = 0; i < styles.length; i++) {
951 			StyleRange style = styles[i];
952 			int start, end;
953 			if (ranges != null) {
954 				start = ranges[i << 1] - lineOffset;
955 				end = start + ranges[(i << 1) + 1];
956 			} else {
957 				start = style.start - lineOffset;
958 				end = start + style.length;
959 			}
960 			// skip over partial first line
961 			if (end < writeOffset) {
962 				continue;
963 			}
964 			// style starts beyond line end or RTF write end
965 			if (start >= lineEndOffset) {
966 				break;
967 			}
968 			// write any unstyled text
969 			if (lineIndex < start) {
970 				// copy to start of style
971 				// style starting beyond end of write range or end of line
972 				// is guarded against above.
973 				write(line, lineIndex, start);
974 				lineIndex = start;
975 			}
976 			// write styled text
977 			write("{\\cf");
978 			write(getColorIndex(style.foreground, DEFAULT_FOREGROUND));
979 			int colorIndex = getColorIndex(style.background, DEFAULT_BACKGROUND);
980 			if (colorIndex != DEFAULT_BACKGROUND) {
981 				write("\\chshdng0\\chcbpat");
982 				write(colorIndex);
983 			}
984 			int fontStyle = style.fontStyle;
985 			Font font = style.font;
986 			if (font != null) {
987 				int fontIndex = getFontIndex(font);
988 				write("\\f");
989 				write(fontIndex);
990 				FontData fontData = font.getFontData()[0];
991 				write("\\fs");
992 				write(fontData.getHeight() * 2);
993 				fontStyle = fontData.getStyle();
994 			}
995 			if ((fontStyle & SWT.BOLD) != 0) {
996 				write("\\b");
997 			}
998 			if ((fontStyle & SWT.ITALIC) != 0) {
999 				write("\\i");
1000 			}
1001 			if (style.underline) {
1002 				write("\\ul");
1003 			}
1004 			if (style.strikeout) {
1005 				write("\\strike");
1006 			}
1007 			write(" ");
1008 			// copy to end of style or end of write range or end of line
1009 			int copyEnd = Math.min(end, lineEndOffset);
1010 			// guard against invalid styles and let style processing continue
1011 			copyEnd = Math.max(copyEnd, lineIndex);
1012 			write(line, lineIndex, copyEnd);
1013 			if ((fontStyle & SWT.BOLD) != 0) {
1014 				write("\\b0");
1015 			}
1016 			if ((style.fontStyle & SWT.ITALIC) != 0) {
1017 				write("\\i0");
1018 			}
1019 			if (style.underline) {
1020 				write("\\ul0");
1021 			}
1022 			if (style.strikeout) {
1023 				write("\\strike0");
1024 			}
1025 			write("}");
1026 			lineIndex = copyEnd;
1027 		}
1028 		// write unstyled text at the end of the line
1029 		if (lineIndex < lineEndOffset) {
1030 			write(line, lineIndex, lineEndOffset);
1031 		}
1032 		if (lineBackground != null) write("}");
1033 	}
1034 	}
1035 	/**
1036 	 * The <code>TextWriter</code> class is used to write widget content to
1037 	 * a string.  Whole and partial lines and line breaks can be written. To write
1038 	 * partial lines, specify the start and length of the desired segment
1039 	 * during object creation.
1040 	 * <p>
1041 	 * <b>NOTE:</b> <code>toString()</code> is guaranteed to return a valid string only after close()
1042 	 * has been called.
1043 	 * </p>
1044 	 */
1045 	class TextWriter {
1046 		private StringBuilder buffer;
1047 		private int startOffset;	// offset of first character that will be written
1048 		private int endOffset;		// offset of last character that will be written.
1049 									// 0 based from the beginning of the widget text.
1050 		private boolean isClosed = false;
1051 
1052 	/**
1053 	 * Creates a writer that writes content starting at offset "start"
1054 	 * in the document.  <code>start</code> and <code>length</code> can be set to specify partial lines.
1055 	 *
1056 	 * @param start start offset of content to write, 0 based from beginning of document
1057 	 * @param length length of content to write
1058 	 */
TextWriter(int start, int length)1059 	public TextWriter(int start, int length) {
1060 		buffer = new StringBuilder(length);
1061 		startOffset = start;
1062 		endOffset = start + length;
1063 	}
1064 	/**
1065 	 * Closes the writer. Once closed no more content can be written.
1066 	 * <b>NOTE:</b>  <code>toString()</code> is not guaranteed to return a valid string unless
1067 	 * the writer is closed.
1068 	 */
close()1069 	public void close() {
1070 		if (!isClosed) {
1071 			isClosed = true;
1072 		}
1073 	}
1074 	/**
1075 	 * Returns the number of characters to write.
1076 	 * @return the integer number of characters to write
1077 	 */
getCharCount()1078 	public int getCharCount() {
1079 		return endOffset - startOffset;
1080 	}
1081 	/**
1082 	 * Returns the offset where writing starts. 0 based from the start of
1083 	 * the widget text. Used to write partial lines.
1084 	 * @return the integer offset where writing starts
1085 	 */
getStart()1086 	public int getStart() {
1087 		return startOffset;
1088 	}
1089 	/**
1090 	 * Returns whether the writer is closed.
1091 	 * @return a boolean specifying whether or not the writer is closed
1092 	 */
isClosed()1093 	public boolean isClosed() {
1094 		return isClosed;
1095 	}
1096 	/**
1097 	 * Returns the string.  <code>close()</code> must be called before <code>toString()</code>
1098 	 * is guaranteed to return a valid string.
1099 	 *
1100 	 * @return the string
1101 	 */
1102 	@Override
toString()1103 	public String toString() {
1104 		return buffer.toString();
1105 	}
1106 	/**
1107 	 * Appends the given string to the data.
1108 	 */
write(String string)1109 	void write(String string) {
1110 		buffer.append(string);
1111 	}
1112 	/**
1113 	 * Inserts the given string to the data at the specified offset.
1114 	 * <p>
1115 	 * Do nothing if "offset" is &lt; 0 or &gt; getCharCount()
1116 	 * </p>
1117 	 *
1118 	 * @param string text to insert
1119 	 * @param offset offset in the existing data to insert "string" at.
1120 	 */
write(String string, int offset)1121 	void write(String string, int offset) {
1122 		if (offset < 0 || offset > buffer.length()) {
1123 			return;
1124 		}
1125 		buffer.insert(offset, string);
1126 	}
1127 	/**
1128 	 * Appends the given int to the data.
1129 	 */
write(int i)1130 	void write(int i) {
1131 		buffer.append(i);
1132 	}
1133 	/**
1134 	 * Appends the given character to the data.
1135 	 */
write(char i)1136 	void write(char i) {
1137 		buffer.append(i);
1138 	}
1139 	/**
1140 	 * Appends the specified line text to the data.
1141 	 *
1142 	 * @param line line text to write. Must not contain line breaks
1143 	 * 	Line breaks should be written using writeLineDelimiter()
1144 	 * @param lineOffset offset of the line. 0 based from the start of the
1145 	 * 	widget document. Any text occurring before the start offset or after the
1146 	 *	end offset specified during object creation is ignored.
1147 	 * @exception SWTException <ul>
1148 	 *   <li>ERROR_IO when the writer is closed.</li>
1149 	 * </ul>
1150 	 */
writeLine(String line, int lineOffset)1151 	public void writeLine(String line, int lineOffset) {
1152 		if (isClosed) {
1153 			SWT.error(SWT.ERROR_IO);
1154 		}
1155 		int writeOffset = startOffset - lineOffset;
1156 		int lineLength = line.length();
1157 		int lineIndex;
1158 		if (writeOffset >= lineLength) {
1159 			return;							// whole line is outside write range
1160 		} else if (writeOffset > 0) {
1161 			lineIndex = writeOffset;		// line starts before write start
1162 		} else {
1163 			lineIndex = 0;
1164 		}
1165 		int copyEnd = Math.min(lineLength, endOffset - lineOffset);
1166 		if (lineIndex < copyEnd) {
1167 			write(line.substring(lineIndex, copyEnd));
1168 		}
1169 	}
1170 	/**
1171 	 * Appends the specified line delimiter to the data.
1172 	 *
1173 	 * @param lineDelimiter line delimiter to write
1174 	 * @exception SWTException <ul>
1175 	 *   <li>ERROR_IO when the writer is closed.</li>
1176 	 * </ul>
1177 	 */
writeLineDelimiter(String lineDelimiter)1178 	public void writeLineDelimiter(String lineDelimiter) {
1179 		if (isClosed) {
1180 			SWT.error(SWT.ERROR_IO);
1181 		}
1182 		write(lineDelimiter);
1183 	}
1184 	}
1185 
1186 /**
1187  * Constructs a new instance of this class given its parent
1188  * and a style value describing its behavior and appearance.
1189  * <p>
1190  * The style value is either one of the style constants defined in
1191  * class <code>SWT</code> which is applicable to instances of this
1192  * class, or must be built by <em>bitwise OR</em>'ing together
1193  * (that is, using the <code>int</code> "|" operator) two or more
1194  * of those <code>SWT</code> style constants. The class description
1195  * lists the style constants that are applicable to the class.
1196  * Style bits are also inherited from superclasses.
1197  * </p>
1198  *
1199  * @param parent a widget which will be the parent of the new instance (cannot be null)
1200  * @param style the style of widget to construct
1201  *
1202  * @exception IllegalArgumentException <ul>
1203  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
1204  * </ul>
1205  * @exception SWTException <ul>
1206  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
1207  * </ul>
1208  *
1209  * @see SWT#FULL_SELECTION
1210  * @see SWT#MULTI
1211  * @see SWT#READ_ONLY
1212  * @see SWT#SINGLE
1213  * @see SWT#WRAP
1214  * @see #getStyle
1215  */
StyledText(Composite parent, int style)1216 public StyledText(Composite parent, int style) {
1217 	super(parent, checkStyle(style));
1218 	// set the fg in the OS to ensure that these are the same as StyledText, necessary
1219 	// for ensuring that the bg/fg the IME box uses is the same as what StyledText uses
1220 	super.setForeground(getForeground());
1221 	super.setDragDetect(false);
1222 	Display display = getDisplay();
1223 	if ((style & SWT.READ_ONLY) != 0) {
1224 		setEditable(false);
1225 	}
1226 	leftMargin = rightMargin = isBidiCaret() ? BIDI_CARET_WIDTH - 1: 0;
1227 	if ((style & SWT.SINGLE) != 0 && (style & SWT.BORDER) != 0) {
1228 		leftMargin = topMargin = rightMargin = bottomMargin = 2;
1229 	}
1230 	alignment = style & (SWT.LEFT | SWT.RIGHT | SWT.CENTER);
1231 	if (alignment == 0) alignment = SWT.LEFT;
1232 	clipboard = new Clipboard(display);
1233 	installDefaultContent();
1234 	renderer = new StyledTextRenderer(getDisplay(), this);
1235 	renderer.setContent(content);
1236 	renderer.setFont(getFont(), tabLength);
1237 	ime = new IME(this, SWT.NONE);
1238 	defaultCaret = new Caret(this, SWT.NONE);
1239 	if ((style & SWT.WRAP) != 0) {
1240 		setWordWrap(true);
1241 	}
1242 	if (isBidiCaret()) {
1243 		createCaretBitmaps();
1244 		Runnable runnable = () -> {
1245 			int direction = BidiUtil.getKeyboardLanguage() == BidiUtil.KEYBOARD_BIDI ? SWT.RIGHT : SWT.LEFT;
1246 			if (direction == caretDirection) return;
1247 			if (getCaret() != defaultCaret) return;
1248 			Point newCaretPos = getPointAtOffset(caretOffset);
1249 			setCaretLocation(newCaretPos, direction);
1250 		};
1251 		BidiUtil.addLanguageListener(this, runnable);
1252 	}
1253 	setCaret(defaultCaret);
1254 	calculateScrollBars();
1255 	createKeyBindings();
1256 	super.setCursor(display.getSystemCursor(SWT.CURSOR_IBEAM));
1257 	installListeners();
1258 	initializeAccessible();
1259 	setData("DEFAULT_DROP_TARGET_EFFECT", new StyledTextDropTargetEffect(this));
1260 	if (IS_MAC) setData(STYLEDTEXT_KEY);
1261 }
1262 /**
1263  * Adds an extended modify listener. An ExtendedModify event is sent by the
1264  * widget when the widget text has changed.
1265  *
1266  * @param extendedModifyListener the listener
1267  * @exception SWTException <ul>
1268  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1269  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1270  * </ul>
1271  * @exception IllegalArgumentException <ul>
1272  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
1273  * </ul>
1274  */
addExtendedModifyListener(ExtendedModifyListener extendedModifyListener)1275 public void addExtendedModifyListener(ExtendedModifyListener extendedModifyListener) {
1276 	checkWidget();
1277 	if (extendedModifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1278 	StyledTextListener typedListener = new StyledTextListener(extendedModifyListener);
1279 	addListener(ST.ExtendedModify, typedListener);
1280 }
1281 /**
1282  * Adds a bidirectional segment listener.
1283  * <p>
1284  * A BidiSegmentEvent is sent
1285  * whenever a line of text is measured or rendered. You can
1286  * specify text ranges in the line that should be treated as if they
1287  * had a different direction than the surrounding text.
1288  * This may be used when adjacent segments of right-to-left text should
1289  * not be reordered relative to each other.
1290  * E.g., multiple Java string literals in a right-to-left language
1291  * should generally remain in logical order to each other, that is, the
1292  * way they are stored.
1293  * </p>
1294  *
1295  * @param listener the listener
1296  * @exception SWTException <ul>
1297  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1298  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1299  * </ul>
1300  * @exception IllegalArgumentException <ul>
1301  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
1302  * </ul>
1303  * @see BidiSegmentEvent
1304  * @since 2.0
1305  */
addBidiSegmentListener(BidiSegmentListener listener)1306 public void addBidiSegmentListener(BidiSegmentListener listener) {
1307 	checkWidget();
1308 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1309 	addListener(ST.LineGetSegments, new StyledTextListener(listener));
1310 	resetCache(0, content.getLineCount());
1311 	setCaretLocation();
1312 	super.redraw();
1313 }
1314 /**
1315  * Adds a caret listener. CaretEvent is sent when the caret offset changes.
1316  *
1317  * @param listener the listener
1318  * @exception SWTException <ul>
1319  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1320  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1321  * </ul>
1322  * @exception IllegalArgumentException <ul>
1323  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
1324  * </ul>
1325  *
1326  * @since 3.5
1327  */
addCaretListener(CaretListener listener)1328 public void addCaretListener(CaretListener listener) {
1329 	checkWidget();
1330 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1331 	addListener(ST.CaretMoved, new StyledTextListener(listener));
1332 }
1333 /**
1334  * Adds a line background listener. A LineGetBackground event is sent by the
1335  * widget to determine the background color for a line.
1336  *
1337  * @param listener the listener
1338  * @exception SWTException <ul>
1339  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1340  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1341  * </ul>
1342  * @exception IllegalArgumentException <ul>
1343  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
1344  * </ul>
1345  */
addLineBackgroundListener(LineBackgroundListener listener)1346 public void addLineBackgroundListener(LineBackgroundListener listener) {
1347 	checkWidget();
1348 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1349 	if (!isListening(ST.LineGetBackground)) {
1350 		renderer.clearLineBackground(0, content.getLineCount());
1351 	}
1352 	addListener(ST.LineGetBackground, new StyledTextListener(listener));
1353 }
1354 /**
1355  * Adds a line style listener. A LineGetStyle event is sent by the widget to
1356  * determine the styles for a line.
1357  *
1358  * @param listener the listener
1359  * @exception SWTException <ul>
1360  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1361  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1362  * </ul>
1363  * @exception IllegalArgumentException <ul>
1364  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
1365  * </ul>
1366  */
addLineStyleListener(LineStyleListener listener)1367 public void addLineStyleListener(LineStyleListener listener) {
1368 	checkWidget();
1369 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1370 	if (!isListening(ST.LineGetStyle)) {
1371 		setStyleRanges(0, 0, null, null, true);
1372 		renderer.clearLineStyle(0, content.getLineCount());
1373 	}
1374 	addListener(ST.LineGetStyle, new StyledTextListener(listener));
1375 	setCaretLocation();
1376 }
1377 /**
1378  * Adds a modify listener. A Modify event is sent by the widget when the widget text
1379  * has changed.
1380  *
1381  * @param modifyListener the listener
1382  * @exception SWTException <ul>
1383  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1384  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1385  * </ul>
1386  * @exception IllegalArgumentException <ul>
1387  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
1388  * </ul>
1389  */
addModifyListener(ModifyListener modifyListener)1390 public void addModifyListener(ModifyListener modifyListener) {
1391 	checkWidget();
1392 	if (modifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1393 	addListener(SWT.Modify, new TypedListener(modifyListener));
1394 }
1395 /**
1396  * Adds a paint object listener. A paint object event is sent by the widget when an object
1397  * needs to be drawn.
1398  *
1399  * @param listener the listener
1400  * @exception SWTException <ul>
1401  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1402  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1403  * </ul>
1404  * @exception IllegalArgumentException <ul>
1405  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
1406  * </ul>
1407  *
1408  * @since 3.2
1409  *
1410  * @see PaintObjectListener
1411  * @see PaintObjectEvent
1412  */
addPaintObjectListener(PaintObjectListener listener)1413 public void addPaintObjectListener(PaintObjectListener listener) {
1414 	checkWidget();
1415 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1416 	addListener(ST.PaintObject, new StyledTextListener(listener));
1417 }
1418 /**
1419  * Adds a selection listener. A Selection event is sent by the widget when the
1420  * user changes the selection.
1421  * <p>
1422  * When <code>widgetSelected</code> is called, the event x and y fields contain
1423  * the start and end caret indices of the selection. The selection values returned are visual
1424  * (i.e., x will always always be &lt;= y).
1425  * No event is sent when the caret is moved while the selection length is 0.
1426  * </p><p>
1427  * <code>widgetDefaultSelected</code> is not called for StyledTexts.
1428  * </p>
1429  *
1430  * @param listener the listener which should be notified when the user changes the receiver's selection
1431 
1432  * @exception IllegalArgumentException <ul>
1433  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
1434  * </ul>
1435  * @exception SWTException <ul>
1436  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1437  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1438  * </ul>
1439  *
1440  * @see SelectionListener
1441  * @see #removeSelectionListener
1442  * @see SelectionEvent
1443  */
addSelectionListener(SelectionListener listener)1444 public void addSelectionListener(SelectionListener listener) {
1445 	checkWidget();
1446 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1447 	addListener(SWT.Selection, new TypedListener(listener));
1448 }
1449 /**
1450  * Adds a verify key listener. A VerifyKey event is sent by the widget when a key
1451  * is pressed. The widget ignores the key press if the listener sets the doit field
1452  * of the event to false.
1453  *
1454  * @param listener the listener
1455  * @exception SWTException <ul>
1456  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1457  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1458  * </ul>
1459  * @exception IllegalArgumentException <ul>
1460  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
1461  * </ul>
1462  */
addVerifyKeyListener(VerifyKeyListener listener)1463 public void addVerifyKeyListener(VerifyKeyListener listener) {
1464 	checkWidget();
1465 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1466 	addListener(ST.VerifyKey, new StyledTextListener(listener));
1467 }
1468 /**
1469  * Adds a verify listener. A Verify event is sent by the widget when the widget text
1470  * is about to change. The listener can set the event text and the doit field to
1471  * change the text that is set in the widget or to force the widget to ignore the
1472  * text change.
1473  *
1474  * @param verifyListener the listener
1475  * @exception SWTException <ul>
1476  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1477  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1478  * </ul>
1479  * @exception IllegalArgumentException <ul>
1480  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
1481  * </ul>
1482  */
addVerifyListener(VerifyListener verifyListener)1483 public void addVerifyListener(VerifyListener verifyListener) {
1484 	checkWidget();
1485 	if (verifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1486 	addListener(SWT.Verify, new TypedListener(verifyListener));
1487 }
1488 /**
1489  * Adds a word movement listener. A movement event is sent when the boundary
1490  * of a word is needed. For example, this occurs during word next and word
1491  * previous actions.
1492  *
1493  * @param movementListener the listener
1494  * @exception SWTException <ul>
1495  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1496  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1497  * </ul>
1498  * @exception IllegalArgumentException <ul>
1499  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
1500  * </ul>
1501  *
1502  * @see MovementEvent
1503  * @see MovementListener
1504  * @see #removeWordMovementListener
1505  *
1506  * @since 3.3
1507  */
addWordMovementListener(MovementListener movementListener)1508 public void addWordMovementListener(MovementListener movementListener) {
1509 	checkWidget();
1510 	if (movementListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1511 	addListener(ST.WordNext, new StyledTextListener(movementListener));
1512 	addListener(ST.WordPrevious, new StyledTextListener(movementListener));
1513 }
1514 /**
1515  * Appends a string to the text at the end of the widget.
1516  *
1517  * @param string the string to be appended
1518  * @see #replaceTextRange(int,int,String)
1519  * @exception SWTException <ul>
1520  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1521  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1522  * </ul>
1523  * @exception IllegalArgumentException <ul>
1524  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
1525  * </ul>
1526  */
append(String string)1527 public void append(String string) {
1528 	checkWidget();
1529 	if (string == null) {
1530 		SWT.error(SWT.ERROR_NULL_ARGUMENT);
1531 	}
1532 	int lastChar = Math.max(getCharCount(), 0);
1533 	replaceTextRange(lastChar, 0, string);
1534 }
1535 /**
1536  * Calculates the scroll bars
1537  */
calculateScrollBars()1538 void calculateScrollBars() {
1539 	ScrollBar horizontalBar = getHorizontalBar();
1540 	ScrollBar verticalBar = getVerticalBar();
1541 	setScrollBars(true);
1542 	if (verticalBar != null) {
1543 		verticalBar.setIncrement(getVerticalIncrement());
1544 	}
1545 	if (horizontalBar != null) {
1546 		horizontalBar.setIncrement(getHorizontalIncrement());
1547 	}
1548 }
1549 /**
1550  * Calculates the top index based on the current vertical scroll offset.
1551  * The top index is the index of the topmost fully visible line or the
1552  * topmost partially visible line if no line is fully visible.
1553  * The top index starts at 0.
1554  */
calculateTopIndex(int delta)1555 void calculateTopIndex(int delta) {
1556 	int oldDelta = delta;
1557 	int oldTopIndex = topIndex;
1558 	int oldTopIndexY = topIndexY;
1559 	if (isFixedLineHeight()) {
1560 		int verticalIncrement = getVerticalIncrement();
1561 		if (verticalIncrement == 0) {
1562 			return;
1563 		}
1564 		topIndex = Compatibility.ceil(getVerticalScrollOffset(), verticalIncrement);
1565 		// Set top index to partially visible top line if no line is fully
1566 		// visible but at least some of the widget client area is visible.
1567 		// Fixes bug 15088.
1568 		if (topIndex > 0) {
1569 			if (clientAreaHeight > 0) {
1570 				int bottomPixel = getVerticalScrollOffset() + clientAreaHeight;
1571 				topIndexY = getLinePixel(topIndex);
1572 				int fullLineTopPixel = topIndex * verticalIncrement;
1573 				int fullLineVisibleHeight = bottomPixel - fullLineTopPixel;
1574 				// set top index to partially visible line if no line fully fits in
1575 				// client area or if space is available but not used (the latter should
1576 				// never happen because we use claimBottomFreeSpace)
1577 				if (fullLineVisibleHeight < verticalIncrement) {
1578 					topIndex = getVerticalScrollOffset() / verticalIncrement;
1579 				}
1580 			} else if (topIndex >= content.getLineCount()) {
1581 				topIndex = content.getLineCount() - 1;
1582 			}
1583 		}
1584 	} else {
1585 		if (delta >= 0) {
1586 			delta -= topIndexY;
1587 			int lineIndex = topIndex;
1588 			int lineCount = content.getLineCount();
1589 			while (lineIndex < lineCount) {
1590 				if (delta <= 0) break;
1591 				delta -= renderer.getCachedLineHeight(lineIndex++);
1592 			}
1593 			if (lineIndex < lineCount && -delta + renderer.getCachedLineHeight(lineIndex) <= clientAreaHeight - topMargin - bottomMargin) {
1594 				topIndex = lineIndex;
1595 				topIndexY = -delta;
1596 			} else {
1597 				topIndex = lineIndex - 1;
1598 				topIndexY = -renderer.getCachedLineHeight(topIndex) - delta;
1599 			}
1600 		} else {
1601 			delta -= topIndexY;
1602 			int lineIndex = topIndex;
1603 			while (lineIndex > 0) {
1604 				int lineHeight = renderer.getCachedLineHeight(lineIndex - 1);
1605 				if (delta + lineHeight > 0) break;
1606 				delta += lineHeight;
1607 				lineIndex--;
1608 			}
1609 			if (lineIndex == 0 || -delta + renderer.getCachedLineHeight(lineIndex) <= clientAreaHeight - topMargin - bottomMargin) {
1610 				topIndex = lineIndex;
1611 				topIndexY = - delta;
1612 			} else {
1613 				topIndex = lineIndex - 1;
1614 				topIndexY = - renderer.getCachedLineHeight(topIndex) - delta;
1615 			}
1616 		}
1617 	}
1618 	if (topIndex < 0) {
1619 		// TODO: This logging is in place to determine why topIndex is getting set to negative values.
1620 		// It should be deleted once we fix the root cause of this issue. See bug 487254 for details.
1621 		System.err.println("StyledText: topIndex was " + topIndex
1622 				+ ", isFixedLineHeight() = " + isFixedLineHeight()
1623 				+ ", delta = " + delta
1624 				+ ", content.getLineCount() = " + content.getLineCount()
1625 				+ ", clientAreaHeight = " + clientAreaHeight
1626 				+ ", oldTopIndex = " + oldTopIndex
1627 				+ ", oldTopIndexY = " + oldTopIndexY
1628 				+ ", getVerticalScrollOffset = " + getVerticalScrollOffset()
1629 				+ ", oldDelta = " + oldDelta
1630 				+ ", getVerticalIncrement() = " + getVerticalIncrement());
1631 		topIndex = 0;
1632 	}
1633 	if (topIndex != oldTopIndex || oldTopIndexY != topIndexY) {
1634 		int width = renderer.getWidth();
1635 		renderer.calculateClientArea();
1636 		if (width != renderer.getWidth()) {
1637 			setScrollBars(false);
1638 		}
1639 	}
1640 }
1641 /**
1642  * Hides the scroll bars if widget is created in single line mode.
1643  */
checkStyle(int style)1644 static int checkStyle(int style) {
1645 	if ((style & SWT.SINGLE) != 0) {
1646 		style &= ~(SWT.H_SCROLL | SWT.V_SCROLL | SWT.WRAP | SWT.MULTI);
1647 	} else {
1648 		style |= SWT.MULTI;
1649 		if ((style & SWT.WRAP) != 0) {
1650 			style &= ~SWT.H_SCROLL;
1651 		}
1652 	}
1653 	style |= SWT.NO_REDRAW_RESIZE | SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND;
1654 	/* Clear SWT.CENTER to avoid the conflict with SWT.EMBEDDED */
1655 	return style & ~SWT.CENTER;
1656 }
1657 /**
1658  * Scrolls down the text to use new space made available by a resize or by
1659  * deleted lines.
1660  */
claimBottomFreeSpace()1661 void claimBottomFreeSpace() {
1662 	if (ime.getCompositionOffset() != -1) return;
1663 	if (isFixedLineHeight()) {
1664 		int newVerticalOffset = Math.max(0, renderer.getHeight() - clientAreaHeight);
1665 		if (newVerticalOffset < getVerticalScrollOffset()) {
1666 			scrollVertical(newVerticalOffset - getVerticalScrollOffset(), true);
1667 		}
1668 	} else {
1669 		int bottomIndex = getPartialBottomIndex();
1670 		int height = getLinePixel(bottomIndex + 1);
1671 		if (clientAreaHeight > height) {
1672 			scrollVertical(-getAvailableHeightAbove(clientAreaHeight - height), true);
1673 		}
1674 	}
1675 }
1676 /**
1677  * Scrolls text to the right to use new space made available by a resize.
1678  */
claimRightFreeSpace()1679 void claimRightFreeSpace() {
1680 	int newHorizontalOffset = Math.max(0, renderer.getWidth() - clientAreaWidth);
1681 	if (newHorizontalOffset < horizontalScrollOffset) {
1682 		// item is no longer drawn past the right border of the client area
1683 		// align the right end of the item with the right border of the
1684 		// client area (window is scrolled right).
1685 		scrollHorizontal(newHorizontalOffset - horizontalScrollOffset, true);
1686 	}
1687 }
clearBlockSelection(boolean reset, boolean sendEvent)1688 void clearBlockSelection(boolean reset, boolean sendEvent) {
1689 	if (reset) resetSelection();
1690 	blockXAnchor = blockYAnchor = -1;
1691 	blockXLocation = blockYLocation = -1;
1692 	caretDirection = SWT.NULL;
1693 	updateCaretVisibility();
1694 	super.redraw();
1695 	if (sendEvent) sendSelectionEvent();
1696 }
1697 /**
1698  * Removes the widget selection.
1699  *
1700  * @param sendEvent a Selection event is sent when set to true and when the selection is actually reset.
1701  */
clearSelection(boolean sendEvent)1702 void clearSelection(boolean sendEvent) {
1703 	int selectionStart = selection.x;
1704 	int selectionEnd = selection.y;
1705 	resetSelection();
1706 	// redraw old selection, if any
1707 	if (selectionEnd - selectionStart > 0) {
1708 		int length = content.getCharCount();
1709 		// called internally to remove selection after text is removed
1710 		// therefore make sure redraw range is valid.
1711 		int redrawStart = Math.min(selectionStart, length);
1712 		int redrawEnd = Math.min(selectionEnd, length);
1713 		if (redrawEnd - redrawStart > 0) {
1714 			internalRedrawRange(redrawStart, redrawEnd - redrawStart);
1715 		}
1716 		if (sendEvent) {
1717 			sendSelectionEvent();
1718 		}
1719 	}
1720 }
1721 @Override
computeSize(int wHint, int hHint, boolean changed)1722 public Point computeSize (int wHint, int hHint, boolean changed) {
1723 	checkWidget();
1724 	int lineCount = (getStyle() & SWT.SINGLE) != 0 ? 1 : content.getLineCount();
1725 	int width = 0;
1726 	int height = 0;
1727 	if (wHint == SWT.DEFAULT || hHint == SWT.DEFAULT) {
1728 		Display display = getDisplay();
1729 		int maxHeight = display.getClientArea().height;
1730 		for (int lineIndex = 0; lineIndex < lineCount; lineIndex++) {
1731 			TextLayout layout = renderer.getTextLayout(lineIndex);
1732 			int wrapWidth = layout.getWidth();
1733 			if (wordWrap) layout.setWidth(wHint == 0 ? 1 : wHint == SWT.DEFAULT ? SWT.DEFAULT : Math.max(1, wHint - leftMargin - rightMargin));
1734 			Rectangle rect = layout.getBounds();
1735 			height += rect.height;
1736 			width = Math.max(width, rect.width);
1737 			layout.setWidth(wrapWidth);
1738 			renderer.disposeTextLayout(layout);
1739 			if (isFixedLineHeight() && height > maxHeight) break;
1740 		}
1741 		if (isFixedLineHeight()) {
1742 			height = lineCount * renderer.getLineHeight();
1743 		}
1744 	}
1745 	// Use default values if no text is defined.
1746 	if (width == 0) width = DEFAULT_WIDTH;
1747 	if (height == 0) height = DEFAULT_HEIGHT;
1748 	if (wHint != SWT.DEFAULT) width = wHint;
1749 	if (hHint != SWT.DEFAULT) height = hHint;
1750 	int wTrim = getLeftMargin() + rightMargin + getCaretWidth();
1751 	int hTrim = topMargin + bottomMargin;
1752 	Rectangle rect = computeTrim(0, 0, width + wTrim, height + hTrim);
1753 	return new Point (rect.width, rect.height);
1754 }
1755 /**
1756  * Copies the selected text to the <code>DND.CLIPBOARD</code> clipboard.
1757  * <p>
1758  * The text will be put on the clipboard in plain text format and RTF format.
1759  * The <code>DND.CLIPBOARD</code> clipboard is used for data that is
1760  * transferred by keyboard accelerator (such as Ctrl+C/Ctrl+V) or
1761  * by menu action.
1762  * </p>
1763  *
1764  * @exception SWTException <ul>
1765  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1766  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1767  * </ul>
1768  */
copy()1769 public void copy() {
1770 	checkWidget();
1771 	copySelection(DND.CLIPBOARD);
1772 }
1773 /**
1774  * Copies the selected text to the specified clipboard.  The text will be put in the
1775  * clipboard in plain text format and RTF format.
1776  * <p>
1777  * The clipboardType is  one of the clipboard constants defined in class
1778  * <code>DND</code>.  The <code>DND.CLIPBOARD</code>  clipboard is
1779  * used for data that is transferred by keyboard accelerator (such as Ctrl+C/Ctrl+V)
1780  * or by menu action.  The <code>DND.SELECTION_CLIPBOARD</code>
1781  * clipboard is used for data that is transferred by selecting text and pasting
1782  * with the middle mouse button.
1783  * </p>
1784  *
1785  * @param clipboardType indicates the type of clipboard
1786  *
1787  * @exception SWTException <ul>
1788  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1789  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1790  * </ul>
1791  *
1792  * @since 3.1
1793  */
copy(int clipboardType)1794 public void copy(int clipboardType) {
1795 	checkWidget();
1796 	copySelection(clipboardType);
1797 }
copySelection(int type)1798 boolean copySelection(int type) {
1799 	if (type != DND.CLIPBOARD && type != DND.SELECTION_CLIPBOARD) return false;
1800 	try {
1801 		if (blockSelection && blockXLocation != -1) {
1802 			String text = getBlockSelectionText(PlatformLineDelimiter);
1803 			if (text.length() > 0) {
1804 				//TODO RTF support
1805 				TextTransfer plainTextTransfer = TextTransfer.getInstance();
1806 				Object[] data = new Object[]{text};
1807 				Transfer[] types = new Transfer[]{plainTextTransfer};
1808 				clipboard.setContents(data, types, type);
1809 				return true;
1810 			}
1811 		} else {
1812 			int length = selection.y - selection.x;
1813 			if (length > 0) {
1814 				setClipboardContent(selection.x, length, type);
1815 				return true;
1816 			}
1817 		}
1818 	} catch (SWTError error) {
1819 		// Copy to clipboard failed. This happens when another application
1820 		// is accessing the clipboard while we copy. Ignore the error.
1821 		// Rethrow all other errors. Fixes bug 17578.
1822 		if (error.code != DND.ERROR_CANNOT_SET_CLIPBOARD) {
1823 			throw error;
1824 		}
1825 	}
1826 	return false;
1827 }
1828 /**
1829  * Returns the alignment of the widget.
1830  *
1831  * @return the alignment
1832  *
1833  * @exception SWTException <ul>
1834  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1835  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1836  * </ul>
1837  *
1838  * @see #getLineAlignment(int)
1839  *
1840  * @since 3.2
1841  */
getAlignment()1842 public int getAlignment() {
1843 	checkWidget();
1844 	return alignment;
1845 }
1846 /**
1847  * Returns the Always Show Scrollbars flag.  True if the scrollbars are
1848  * always shown even if they are not required.  False if the scrollbars are only
1849  * visible when some part of the content needs to be scrolled to be seen.
1850  * The H_SCROLL and V_SCROLL style bits are also required to enable scrollbars in the
1851  * horizontal and vertical directions.
1852  *
1853  * @return the Always Show Scrollbars flag value
1854  *
1855  * @exception SWTException <ul>
1856  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1857  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1858  * </ul>
1859  *
1860  * @since 3.8
1861  */
getAlwaysShowScrollBars()1862 public boolean getAlwaysShowScrollBars() {
1863 	checkWidget();
1864 	return alwaysShowScroll;
1865 }
getAvailableHeightAbove(int height)1866 int getAvailableHeightAbove(int height) {
1867 	int maxHeight = verticalScrollOffset;
1868 	if (maxHeight == -1) {
1869 		int lineIndex = topIndex - 1;
1870 		maxHeight = -topIndexY;
1871 		if (topIndexY > 0) {
1872 			maxHeight += renderer.getLineHeight(lineIndex--);
1873 		}
1874 		while (height > maxHeight && lineIndex >= 0) {
1875 			maxHeight += renderer.getLineHeight(lineIndex--);
1876 		}
1877 	}
1878 	return Math.min(height, maxHeight);
1879 }
getAvailableHeightBellow(int height)1880 int getAvailableHeightBellow(int height) {
1881 	int partialBottomIndex = getPartialBottomIndex();
1882 	int topY = getLinePixel(partialBottomIndex);
1883 	int lineHeight = renderer.getLineHeight(partialBottomIndex);
1884 	int availableHeight = 0;
1885 	int clientAreaHeight = this.clientAreaHeight - topMargin - bottomMargin;
1886 	if (topY + lineHeight > clientAreaHeight) {
1887 		availableHeight = lineHeight - (clientAreaHeight - topY);
1888 	}
1889 	int lineIndex = partialBottomIndex + 1;
1890 	int lineCount = content.getLineCount();
1891 	while (height > availableHeight && lineIndex < lineCount) {
1892 		availableHeight += renderer.getLineHeight(lineIndex++);
1893 	}
1894 	return Math.min(height, availableHeight);
1895 }
1896 /**
1897  * Returns the color of the margins.
1898  *
1899  * @return the color of the margins.
1900  * @exception SWTException <ul>
1901  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1902  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1903  * </ul>
1904  *
1905  * @since 3.5
1906  */
getMarginColor()1907 public Color getMarginColor() {
1908 	checkWidget();
1909 	return marginColor != null ? marginColor : getBackground();
1910 }
1911 /**
1912  * Returns a string that uses only the line delimiter specified by the
1913  * StyledTextContent implementation.
1914  * <p>
1915  * Returns only the first line if the widget has the SWT.SINGLE style.
1916  * </p>
1917  *
1918  * @param text the text that may have line delimiters that don't
1919  * 	match the model line delimiter. Possible line delimiters
1920  * 	are CR ('\r'), LF ('\n'), CR/LF ("\r\n")
1921  * @return the converted text that only uses the line delimiter
1922  * 	specified by the model. Returns only the first line if the widget
1923  * 	has the SWT.SINGLE style.
1924  */
getModelDelimitedText(String text)1925 String getModelDelimitedText(String text) {
1926 	int length = text.length();
1927 	if (length == 0) {
1928 		return text;
1929 	}
1930 	int crIndex = 0;
1931 	int lfIndex = 0;
1932 	int i = 0;
1933 	StringBuilder convertedText = new StringBuilder(length);
1934 	String delimiter = getLineDelimiter();
1935 	while (i < length) {
1936 		if (crIndex != -1) {
1937 			crIndex = text.indexOf(SWT.CR, i);
1938 		}
1939 		if (lfIndex != -1) {
1940 			lfIndex = text.indexOf(SWT.LF, i);
1941 		}
1942 		if (lfIndex == -1 && crIndex == -1) {	// no more line breaks?
1943 			break;
1944 		} else if ((crIndex < lfIndex && crIndex != -1) || lfIndex == -1) {
1945 			convertedText.append(text.substring(i, crIndex));
1946 			if (lfIndex == crIndex + 1) {		// CR/LF combination?
1947 				i = lfIndex + 1;
1948 			} else {
1949 				i = crIndex + 1;
1950 			}
1951 		} else {									// LF occurs before CR!
1952 			convertedText.append(text.substring(i, lfIndex));
1953 			i = lfIndex + 1;
1954 		}
1955 		if (isSingleLine()) {
1956 			break;
1957 		}
1958 		convertedText.append(delimiter);
1959 	}
1960 	// copy remaining text if any and if not in single line mode or no
1961 	// text copied thus far (because there only is one line)
1962 	if (i < length && (!isSingleLine() || convertedText.length() == 0)) {
1963 		convertedText.append(text.substring(i));
1964 	}
1965 	return convertedText.toString();
1966 }
checkDragDetect(Event event)1967 boolean checkDragDetect(Event event) {
1968 	if (!isListening(SWT.DragDetect)) return false;
1969 	if (event.button != 1) return false;
1970 	if (blockSelection && blockXLocation != -1) {
1971 		Rectangle rect = getBlockSelectionRectangle();
1972 		if (rect.contains(event.x, event.y)) {
1973 			return dragDetect(event);
1974 		}
1975 	} else {
1976 		if (selection.x == selection.y) return false;
1977 		int offset = getOffsetAtPoint(event.x, event.y, null, true);
1978 		if (selection.x <= offset && offset < selection.y) {
1979 			return dragDetect(event);
1980 		}
1981 
1982 	}
1983 	return false;
1984 }
1985 
1986 /**
1987  * Creates default key bindings.
1988  */
createKeyBindings()1989 void createKeyBindings() {
1990 	int nextKey = isMirrored() ? SWT.ARROW_LEFT : SWT.ARROW_RIGHT;
1991 	int previousKey = isMirrored() ? SWT.ARROW_RIGHT : SWT.ARROW_LEFT;
1992 
1993 	// Navigation
1994 	setKeyBinding(SWT.ARROW_UP, ST.LINE_UP);
1995 	setKeyBinding(SWT.ARROW_DOWN, ST.LINE_DOWN);
1996 	if (IS_MAC) {
1997 		setKeyBinding(previousKey | SWT.MOD1, ST.LINE_START);
1998 		setKeyBinding(nextKey | SWT.MOD1, ST.LINE_END);
1999 		setKeyBinding(SWT.HOME, ST.TEXT_START);
2000 		setKeyBinding(SWT.END, ST.TEXT_END);
2001 		setKeyBinding(SWT.ARROW_UP | SWT.MOD1, ST.TEXT_START);
2002 		setKeyBinding(SWT.ARROW_DOWN | SWT.MOD1, ST.TEXT_END);
2003 		setKeyBinding(nextKey | SWT.MOD3, ST.WORD_NEXT);
2004 		setKeyBinding(previousKey | SWT.MOD3, ST.WORD_PREVIOUS);
2005 	} else {
2006 		setKeyBinding(SWT.HOME, ST.LINE_START);
2007 		setKeyBinding(SWT.END, ST.LINE_END);
2008 		setKeyBinding(SWT.HOME | SWT.MOD1, ST.TEXT_START);
2009 		setKeyBinding(SWT.END | SWT.MOD1, ST.TEXT_END);
2010 		setKeyBinding(nextKey | SWT.MOD1, ST.WORD_NEXT);
2011 		setKeyBinding(previousKey | SWT.MOD1, ST.WORD_PREVIOUS);
2012 	}
2013 	setKeyBinding(SWT.PAGE_UP, ST.PAGE_UP);
2014 	setKeyBinding(SWT.PAGE_DOWN, ST.PAGE_DOWN);
2015 	setKeyBinding(SWT.PAGE_UP | SWT.MOD1, ST.WINDOW_START);
2016 	setKeyBinding(SWT.PAGE_DOWN | SWT.MOD1, ST.WINDOW_END);
2017 	setKeyBinding(nextKey, ST.COLUMN_NEXT);
2018 	setKeyBinding(previousKey, ST.COLUMN_PREVIOUS);
2019 
2020 	// Selection
2021 	setKeyBinding(SWT.ARROW_UP | SWT.MOD2, ST.SELECT_LINE_UP);
2022 	setKeyBinding(SWT.ARROW_DOWN | SWT.MOD2, ST.SELECT_LINE_DOWN);
2023 	if (IS_MAC) {
2024 		setKeyBinding(previousKey | SWT.MOD1 | SWT.MOD2, ST.SELECT_LINE_START);
2025 		setKeyBinding(nextKey | SWT.MOD1 | SWT.MOD2, ST.SELECT_LINE_END);
2026 		setKeyBinding(SWT.HOME | SWT.MOD2, ST.SELECT_TEXT_START);
2027 		setKeyBinding(SWT.END | SWT.MOD2, ST.SELECT_TEXT_END);
2028 		setKeyBinding(SWT.ARROW_UP | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_START);
2029 		setKeyBinding(SWT.ARROW_DOWN | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_END);
2030 		setKeyBinding(nextKey | SWT.MOD2 | SWT.MOD3, ST.SELECT_WORD_NEXT);
2031 		setKeyBinding(previousKey | SWT.MOD2 | SWT.MOD3, ST.SELECT_WORD_PREVIOUS);
2032 	} else  {
2033 		setKeyBinding(SWT.HOME | SWT.MOD2, ST.SELECT_LINE_START);
2034 		setKeyBinding(SWT.END | SWT.MOD2, ST.SELECT_LINE_END);
2035 		setKeyBinding(SWT.HOME | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_START);
2036 		setKeyBinding(SWT.END | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_END);
2037 		setKeyBinding(nextKey | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_NEXT);
2038 		setKeyBinding(previousKey | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_PREVIOUS);
2039 	}
2040 	setKeyBinding(SWT.PAGE_UP | SWT.MOD2, ST.SELECT_PAGE_UP);
2041 	setKeyBinding(SWT.PAGE_DOWN | SWT.MOD2, ST.SELECT_PAGE_DOWN);
2042 	setKeyBinding(SWT.PAGE_UP | SWT.MOD1 | SWT.MOD2, ST.SELECT_WINDOW_START);
2043 	setKeyBinding(SWT.PAGE_DOWN | SWT.MOD1 | SWT.MOD2, ST.SELECT_WINDOW_END);
2044 	setKeyBinding(nextKey | SWT.MOD2, ST.SELECT_COLUMN_NEXT);
2045 	setKeyBinding(previousKey | SWT.MOD2, ST.SELECT_COLUMN_PREVIOUS);
2046 
2047 	// Modification
2048 	// Cut, Copy, Paste
2049 	setKeyBinding('X' | SWT.MOD1, ST.CUT);
2050 	setKeyBinding('C' | SWT.MOD1, ST.COPY);
2051 	setKeyBinding('V' | SWT.MOD1, ST.PASTE);
2052 	if (IS_MAC) {
2053 		setKeyBinding(SWT.DEL | SWT.MOD2, ST.DELETE_NEXT);
2054 		setKeyBinding(SWT.BS | SWT.MOD3, ST.DELETE_WORD_PREVIOUS);
2055 		setKeyBinding(SWT.DEL | SWT.MOD3, ST.DELETE_WORD_NEXT);
2056 	} else {
2057 		// Cut, Copy, Paste Wordstar style
2058 		setKeyBinding(SWT.DEL | SWT.MOD2, ST.CUT);
2059 		setKeyBinding(SWT.INSERT | SWT.MOD1, ST.COPY);
2060 		setKeyBinding(SWT.INSERT | SWT.MOD2, ST.PASTE);
2061 	}
2062 	setKeyBinding(SWT.BS | SWT.MOD2, ST.DELETE_PREVIOUS);
2063 	setKeyBinding(SWT.BS, ST.DELETE_PREVIOUS);
2064 	setKeyBinding(SWT.DEL, ST.DELETE_NEXT);
2065 	setKeyBinding(SWT.BS | SWT.MOD1, ST.DELETE_WORD_PREVIOUS);
2066 	setKeyBinding(SWT.DEL | SWT.MOD1, ST.DELETE_WORD_NEXT);
2067 
2068 	// Miscellaneous
2069 	setKeyBinding(SWT.INSERT, ST.TOGGLE_OVERWRITE);
2070 }
2071 /**
2072  * Create the bitmaps to use for the caret in bidi mode.  This
2073  * method only needs to be called upon widget creation and when the
2074  * font changes (the caret bitmap height needs to match font height).
2075  */
createCaretBitmaps()2076 void createCaretBitmaps() {
2077 	int caretWidth = BIDI_CARET_WIDTH;
2078 	Display display = getDisplay();
2079 	if (leftCaretBitmap != null) {
2080 		if (defaultCaret != null && leftCaretBitmap.equals(defaultCaret.getImage())) {
2081 			defaultCaret.setImage(null);
2082 		}
2083 		leftCaretBitmap.dispose();
2084 	}
2085 	int lineHeight = renderer.getLineHeight();
2086 	leftCaretBitmap = new Image(display, caretWidth, lineHeight);
2087 	GC gc = new GC (leftCaretBitmap);
2088 	gc.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
2089 	gc.fillRectangle(0, 0, caretWidth, lineHeight);
2090 	gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
2091 	gc.drawLine(0,0,0,lineHeight);
2092 	gc.drawLine(0,0,caretWidth-1,0);
2093 	gc.drawLine(0,1,1,1);
2094 	gc.dispose();
2095 
2096 	if (rightCaretBitmap != null) {
2097 		if (defaultCaret != null && rightCaretBitmap.equals(defaultCaret.getImage())) {
2098 			defaultCaret.setImage(null);
2099 		}
2100 		rightCaretBitmap.dispose();
2101 	}
2102 	rightCaretBitmap = new Image(display, caretWidth, lineHeight);
2103 	gc = new GC (rightCaretBitmap);
2104 	gc.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
2105 	gc.fillRectangle(0, 0, caretWidth, lineHeight);
2106 	gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
2107 	gc.drawLine(caretWidth-1,0,caretWidth-1,lineHeight);
2108 	gc.drawLine(0,0,caretWidth-1,0);
2109 	gc.drawLine(caretWidth-1,1,1,1);
2110 	gc.dispose();
2111 }
2112 /**
2113  * Moves the selected text to the clipboard.  The text will be put in the
2114  * clipboard in plain text format and RTF format.
2115  *
2116  * @exception SWTException <ul>
2117  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2118  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2119  * </ul>
2120  */
cut()2121 public void cut() {
2122 	checkWidget();
2123 	// Abort cut operation if copy to clipboard fails.
2124 	// Fixes bug 21030.
2125 	if (copySelection(DND.CLIPBOARD)) {
2126 		if (blockSelection && blockXLocation != -1) {
2127 			insertBlockSelectionText((char)0, SWT.NULL);
2128 		} else {
2129 			doDelete();
2130 		}
2131 	}
2132 }
2133 /**
2134  * A mouse move event has occurred.  See if we should start autoscrolling.  If
2135  * the move position is outside of the client area, initiate autoscrolling.
2136  * Otherwise, we've moved back into the widget so end autoscrolling.
2137  */
doAutoScroll(Event event)2138 void doAutoScroll(Event event) {
2139 	int caretLine = getCaretLine();
2140 	if (event.y > clientAreaHeight - bottomMargin && caretLine != content.getLineCount() - 1) {
2141 		doAutoScroll(SWT.DOWN, event.y - (clientAreaHeight - bottomMargin));
2142 	} else if (event.y < topMargin && caretLine != 0) {
2143 		doAutoScroll(SWT.UP, topMargin - event.y);
2144 	} else if (event.x < leftMargin && !wordWrap) {
2145 		doAutoScroll(ST.COLUMN_PREVIOUS, leftMargin - event.x);
2146 	} else if (event.x > clientAreaWidth - rightMargin && !wordWrap) {
2147 		doAutoScroll(ST.COLUMN_NEXT, event.x - (clientAreaWidth - rightMargin));
2148 	} else {
2149 		endAutoScroll();
2150 	}
2151 }
2152 /**
2153  * Initiates autoscrolling.
2154  *
2155  * @param direction SWT.UP, SWT.DOWN, SWT.COLUMN_NEXT, SWT.COLUMN_PREVIOUS
2156  */
doAutoScroll(int direction, int distance)2157 void doAutoScroll(int direction, int distance) {
2158 	autoScrollDistance = distance;
2159 	// If we're already autoscrolling in the given direction do nothing
2160 	if (autoScrollDirection == direction) {
2161 		return;
2162 	}
2163 
2164 	Runnable timer = null;
2165 	final Display display = getDisplay();
2166 	// Set a timer that will simulate the user pressing and holding
2167 	// down a cursor key (i.e., arrowUp, arrowDown).
2168 	if (direction == SWT.UP) {
2169 		timer = new Runnable() {
2170 			@Override
2171 			public void run() {
2172 				/* Bug 437357 - NPE in StyledText.getCaretLine
2173 				 * StyledText.content is null at times, probably because the
2174 				 * widget itself has been disposed.
2175 				 */
2176 				if (isDisposed()) return;
2177 				if (autoScrollDirection == SWT.UP) {
2178 					if (blockSelection) {
2179 						int verticalScrollOffset = getVerticalScrollOffset();
2180 						int y = blockYLocation - verticalScrollOffset;
2181 						int pixels = Math.max(-autoScrollDistance, -verticalScrollOffset);
2182 						if (pixels != 0) {
2183 							setBlockSelectionLocation(blockXLocation - horizontalScrollOffset, y + pixels, true);
2184 							scrollVertical(pixels, true);
2185 						}
2186 					} else {
2187 						doSelectionPageUp(autoScrollDistance);
2188 					}
2189 					display.timerExec(V_SCROLL_RATE, this);
2190 				}
2191 			}
2192 		};
2193 		autoScrollDirection = direction;
2194 		display.timerExec(V_SCROLL_RATE, timer);
2195 	} else if (direction == SWT.DOWN) {
2196 		timer = new Runnable() {
2197 			@Override
2198 			public void run() {
2199 				/* Bug 437357 - NPE in StyledText.getCaretLine
2200 				 * StyledText.content is null at times, probably because the
2201 				 * widget itself has been disposed.
2202 				 */
2203 				if (isDisposed()) return;
2204 				if (autoScrollDirection == SWT.DOWN) {
2205 					if (blockSelection) {
2206 						int verticalScrollOffset = getVerticalScrollOffset();
2207 						int y = blockYLocation - verticalScrollOffset;
2208 						int max = renderer.getHeight() - verticalScrollOffset - clientAreaHeight;
2209 						int pixels = Math.min(autoScrollDistance, Math.max(0,max));
2210 						if (pixels != 0) {
2211 							setBlockSelectionLocation(blockXLocation - horizontalScrollOffset, y + pixels, true);
2212 							scrollVertical(pixels, true);
2213 						}
2214 					} else {
2215 						doSelectionPageDown(autoScrollDistance);
2216 					}
2217 					display.timerExec(V_SCROLL_RATE, this);
2218 				}
2219 			}
2220 		};
2221 		autoScrollDirection = direction;
2222 		display.timerExec(V_SCROLL_RATE, timer);
2223 	} else if (direction == ST.COLUMN_NEXT) {
2224 		timer = new Runnable() {
2225 			@Override
2226 			public void run() {
2227 				/* Bug 437357 - NPE in StyledText.getCaretLine
2228 				 * StyledText.content is null at times, probably because the
2229 				 * widget itself has been disposed.
2230 				 */
2231 				if (isDisposed()) return;
2232 				if (autoScrollDirection == ST.COLUMN_NEXT) {
2233 					if (blockSelection) {
2234 						int x = blockXLocation - horizontalScrollOffset;
2235 						int max = renderer.getWidth() - horizontalScrollOffset - clientAreaWidth;
2236 						int pixels = Math.min(autoScrollDistance, Math.max(0,max));
2237 						if (pixels != 0) {
2238 							setBlockSelectionLocation(x + pixels, blockYLocation - getVerticalScrollOffset(), true);
2239 							scrollHorizontal(pixels, true);
2240 						}
2241 					} else {
2242 						doVisualNext();
2243 						setMouseWordSelectionAnchor();
2244 						doMouseSelection();
2245 					}
2246 					display.timerExec(H_SCROLL_RATE, this);
2247 				}
2248 			}
2249 		};
2250 		autoScrollDirection = direction;
2251 		display.timerExec(H_SCROLL_RATE, timer);
2252 	} else if (direction == ST.COLUMN_PREVIOUS) {
2253 		timer = new Runnable() {
2254 			@Override
2255 			public void run() {
2256 				/* Bug 437357 - NPE in StyledText.getCaretLine
2257 				 * StyledText.content is null at times, probably because the
2258 				 * widget itself has been disposed.
2259 				 */
2260 				if (isDisposed()) return;
2261 				if (autoScrollDirection == ST.COLUMN_PREVIOUS) {
2262 					if (blockSelection) {
2263 						int x = blockXLocation - horizontalScrollOffset;
2264 						int pixels = Math.max(-autoScrollDistance, -horizontalScrollOffset);
2265 						if (pixels != 0) {
2266 							setBlockSelectionLocation(x + pixels, blockYLocation - getVerticalScrollOffset(), true);
2267 							scrollHorizontal(pixels, true);
2268 						}
2269 					} else {
2270 						doVisualPrevious();
2271 						setMouseWordSelectionAnchor();
2272 						doMouseSelection();
2273 					}
2274 					display.timerExec(H_SCROLL_RATE, this);
2275 				}
2276 			}
2277 		};
2278 		autoScrollDirection = direction;
2279 		display.timerExec(H_SCROLL_RATE, timer);
2280 	}
2281 }
2282 /**
2283  * Deletes the previous character. Delete the selected text if any.
2284  * Move the caret in front of the deleted text.
2285  */
doBackspace()2286 void doBackspace() {
2287 	Event event = new Event();
2288 	event.text = "";
2289 	if (selection.x != selection.y) {
2290 		event.start = selection.x;
2291 		event.end = selection.y;
2292 		sendKeyEvent(event);
2293 	} else if (caretOffset > 0) {
2294 		int lineIndex = content.getLineAtOffset(caretOffset);
2295 		int lineOffset = content.getOffsetAtLine(lineIndex);
2296 		if (caretOffset == lineOffset) {
2297 			lineOffset = content.getOffsetAtLine(lineIndex - 1);
2298 			event.start = lineOffset + content.getLine(lineIndex - 1).length();
2299 			event.end = caretOffset;
2300 		} else {
2301 			boolean isSurrogate = false;
2302 			String lineText = content.getLine(lineIndex);
2303 			char ch = lineText.charAt(caretOffset - lineOffset - 1);
2304 			if (0xDC00 <= ch && ch <= 0xDFFF) {
2305 				if (caretOffset - lineOffset - 2 >= 0) {
2306 					ch = lineText.charAt(caretOffset - lineOffset - 2);
2307 					isSurrogate = 0xD800 <= ch && ch <= 0xDBFF;
2308 				}
2309 			}
2310 			TextLayout layout = renderer.getTextLayout(lineIndex);
2311 			int start = layout.getPreviousOffset(caretOffset - lineOffset, isSurrogate ? SWT.MOVEMENT_CLUSTER : SWT.MOVEMENT_CHAR);
2312 			renderer.disposeTextLayout(layout);
2313 			event.start = start + lineOffset;
2314 			event.end = caretOffset;
2315 		}
2316 		sendKeyEvent(event);
2317 	}
2318 }
doBlockColumn(boolean next)2319 void doBlockColumn(boolean next) {
2320 	if (blockXLocation == -1) setBlockSelectionOffset(caretOffset, false);
2321 	int x = blockXLocation - horizontalScrollOffset;
2322 	int y = blockYLocation - getVerticalScrollOffset();
2323 	int[] trailing = new int[1];
2324 	int offset = getOffsetAtPoint(x, y, trailing, true);
2325 	if (offset != -1) {
2326 		offset += trailing[0];
2327 		int lineIndex = content.getLineAtOffset(offset);
2328 		int newOffset;
2329 		if (next) {
2330 			newOffset = getClusterNext(offset, lineIndex);
2331 		} else {
2332 			newOffset = getClusterPrevious(offset, lineIndex);
2333 		}
2334 		offset = newOffset != offset ? newOffset : -1;
2335 	}
2336 	if (offset != -1) {
2337 		setBlockSelectionOffset(offset, true);
2338 		showCaret();
2339 	} else {
2340 		int width = next ? renderer.averageCharWidth : -renderer.averageCharWidth;
2341 		int maxWidth = Math.max(clientAreaWidth - rightMargin - leftMargin, renderer.getWidth());
2342 		x = Math.max(0, Math.min(blockXLocation + width, maxWidth)) - horizontalScrollOffset;
2343 		setBlockSelectionLocation(x, y, true);
2344 		Rectangle rect = new Rectangle(x, y, 0, 0);
2345 		showLocation(rect, true);
2346 	}
2347 }
doBlockContentStartEnd(boolean end)2348 void doBlockContentStartEnd(boolean end) {
2349 	if (blockXLocation == -1) setBlockSelectionOffset(caretOffset, false);
2350 	int offset = end ? content.getCharCount() : 0;
2351 	setBlockSelectionOffset(offset, true);
2352 	showCaret();
2353 }
doBlockWord(boolean next)2354 void doBlockWord(boolean next) {
2355 	if (blockXLocation == -1) setBlockSelectionOffset(caretOffset, false);
2356 	int x = blockXLocation - horizontalScrollOffset;
2357 	int y = blockYLocation - getVerticalScrollOffset();
2358 	int[] trailing = new int[1];
2359 	int offset = getOffsetAtPoint(x, y, trailing, true);
2360 	if (offset != -1) {
2361 		offset += trailing[0];
2362 		int lineIndex = content.getLineAtOffset(offset);
2363 		int lineOffset = content.getOffsetAtLine(lineIndex);
2364 		String lineText = content.getLine(lineIndex);
2365 		int lineLength = lineText.length();
2366 		int newOffset = offset;
2367 		if (next) {
2368 			if (offset < lineOffset + lineLength) {
2369 				newOffset = getWordNext(offset, SWT.MOVEMENT_WORD);
2370 			}
2371 		} else {
2372 			if (offset > lineOffset) {
2373 				newOffset = getWordPrevious(offset, SWT.MOVEMENT_WORD);
2374 			}
2375 		}
2376 		offset = newOffset != offset ? newOffset : -1;
2377 	}
2378 	if (offset != -1) {
2379 		setBlockSelectionOffset(offset, true);
2380 		showCaret();
2381 	} else {
2382 		int width = (next ? renderer.averageCharWidth : -renderer.averageCharWidth) * 6;
2383 		int maxWidth = Math.max(clientAreaWidth - rightMargin - leftMargin, renderer.getWidth());
2384 		x = Math.max(0, Math.min(blockXLocation + width, maxWidth)) - horizontalScrollOffset;
2385 		setBlockSelectionLocation(x, y, true);
2386 		Rectangle rect = new Rectangle(x, y, 0, 0);
2387 		showLocation(rect, true);
2388 	}
2389 }
doBlockLineVertical(boolean up)2390 void doBlockLineVertical(boolean up) {
2391 	if (blockXLocation == -1) setBlockSelectionOffset(caretOffset, false);
2392 	int y = blockYLocation - getVerticalScrollOffset();
2393 	int lineIndex = getLineIndex(y);
2394 	if (up) {
2395 		if (lineIndex > 0) {
2396 			y = getLinePixel(lineIndex - 1);
2397 			setBlockSelectionLocation(blockXLocation - horizontalScrollOffset, y, true);
2398 			if (y < topMargin) {
2399 				scrollVertical(y - topMargin, true);
2400 			}
2401 		}
2402 	} else {
2403 		int lineCount = content.getLineCount();
2404 		if (lineIndex + 1 < lineCount) {
2405 			y = getLinePixel(lineIndex + 2) - 1;
2406 			setBlockSelectionLocation(blockXLocation - horizontalScrollOffset, y, true);
2407 			int bottom = clientAreaHeight - bottomMargin;
2408 			if (y > bottom) {
2409 				scrollVertical(y - bottom, true);
2410 			}
2411 		}
2412 	}
2413 }
doBlockLineHorizontal(boolean end)2414 void doBlockLineHorizontal(boolean end) {
2415 	if (blockXLocation == -1) setBlockSelectionOffset(caretOffset, false);
2416 	int x = blockXLocation - horizontalScrollOffset;
2417 	int y = blockYLocation - getVerticalScrollOffset();
2418 	int lineIndex = getLineIndex(y);
2419 	int lineOffset = content.getOffsetAtLine(lineIndex);
2420 	String lineText = content.getLine(lineIndex);
2421 	int lineLength = lineText.length();
2422 	int[] trailing = new int[1];
2423 	int offset = getOffsetAtPoint(x, y, trailing, true);
2424 	if (offset != -1) {
2425 		offset += trailing[0];
2426 		int newOffset = offset;
2427 		if (end) {
2428 			if (offset < lineOffset + lineLength) {
2429 				newOffset = lineOffset + lineLength;
2430 			}
2431 		} else {
2432 			if (offset > lineOffset) {
2433 				newOffset = lineOffset;
2434 			}
2435 		}
2436 		offset = newOffset != offset ? newOffset : -1;
2437 	} else {
2438 		if (!end) offset = lineOffset + lineLength;
2439 	}
2440 	if (offset != -1) {
2441 		setBlockSelectionOffset(offset, true);
2442 		showCaret();
2443 	} else {
2444 		int maxWidth = Math.max(clientAreaWidth - rightMargin - leftMargin, renderer.getWidth());
2445 		x = (end ? maxWidth : 0) - horizontalScrollOffset;
2446 		setBlockSelectionLocation(x, y, true);
2447 		Rectangle rect = new Rectangle(x, y, 0, 0);
2448 		showLocation(rect, true);
2449 	}
2450 }
doBlockSelection(boolean sendEvent)2451 void doBlockSelection(boolean sendEvent) {
2452 	if (caretOffset > selectionAnchor) {
2453 		selection.x = selectionAnchor;
2454 		selection.y = caretOffset;
2455 	} else {
2456 		selection.x = caretOffset;
2457 		selection.y = selectionAnchor;
2458 	}
2459 	updateCaretVisibility();
2460 	setCaretLocation();
2461 	super.redraw();
2462 	if (sendEvent) {
2463 		sendSelectionEvent();
2464 	}
2465 	sendAccessibleTextCaretMoved();
2466 }
2467 /**
2468  * Replaces the selection with the character or insert the character at the
2469  * current caret position if no selection exists.
2470  * <p>
2471  * If a carriage return was typed replace it with the line break character
2472  * used by the widget on this platform.
2473  * </p>
2474  *
2475  * @param key the character typed by the user
2476  */
doContent(char key)2477 void doContent(char key) {
2478 	if (blockSelection && blockXLocation != -1) {
2479 		insertBlockSelectionText(key, SWT.NULL);
2480 		return;
2481 	}
2482 
2483 	Event event = new Event();
2484 	event.start = selection.x;
2485 	event.end = selection.y;
2486 	// replace a CR line break with the widget line break
2487 	// CR does not make sense on Windows since most (all?) applications
2488 	// don't recognize CR as a line break.
2489 	if (key == SWT.CR || key == SWT.LF) {
2490 		if (!isSingleLine()) {
2491 			event.text = getLineDelimiter();
2492 		}
2493 	} else if (selection.x == selection.y && overwrite && key != TAB) {
2494 		// no selection and overwrite mode is on and the typed key is not a
2495 		// tab character (tabs are always inserted without overwriting)?
2496 		int lineIndex = content.getLineAtOffset(event.end);
2497 		int lineOffset = content.getOffsetAtLine(lineIndex);
2498 		String line = content.getLine(lineIndex);
2499 		// replace character at caret offset if the caret is not at the
2500 		// end of the line
2501 		if (event.end < lineOffset + line.length()) {
2502 			event.end++;
2503 		}
2504 		event.text = new String(new char[] {key});
2505 	} else {
2506 		event.text = new String(new char[] {key});
2507 	}
2508 	if (event.text != null) {
2509 		if (textLimit > 0 && content.getCharCount() - (event.end - event.start) >= textLimit) {
2510 			return;
2511 		}
2512 		sendKeyEvent(event);
2513 	}
2514 }
2515 /**
2516  * Moves the caret after the last character of the widget content.
2517  */
doContentEnd()2518 void doContentEnd() {
2519 	// place caret at end of first line if receiver is in single
2520 	// line mode. fixes 4820.
2521 	if (isSingleLine()) {
2522 		doLineEnd();
2523 	} else {
2524 		int length = content.getCharCount();
2525 		setCaretOffset(length, SWT.DEFAULT);
2526 		showCaret();
2527 	}
2528 }
2529 /**
2530  * Moves the caret in front of the first character of the widget content.
2531  */
doContentStart()2532 void doContentStart() {
2533 	setCaretOffset(0, SWT.DEFAULT);
2534 	showCaret();
2535 }
2536 /**
2537  * Moves the caret to the start of the selection if a selection exists.
2538  * Otherwise, if no selection exists move the cursor according to the
2539  * cursor selection rules.
2540  *
2541  * @see #doSelectionCursorPrevious
2542  */
doCursorPrevious()2543 void doCursorPrevious() {
2544 	if (selection.y - selection.x > 0) {
2545 		setCaretOffset(selection.x, OFFSET_LEADING);
2546 		showCaret();
2547 	} else {
2548 		doSelectionCursorPrevious();
2549 	}
2550 }
2551 /**
2552  * Moves the caret to the end of the selection if a selection exists.
2553  * Otherwise, if no selection exists move the cursor according to the
2554  * cursor selection rules.
2555  *
2556  * @see #doSelectionCursorNext
2557  */
doCursorNext()2558 void doCursorNext() {
2559 	if (selection.y - selection.x > 0) {
2560 		setCaretOffset(selection.y, PREVIOUS_OFFSET_TRAILING);
2561 		showCaret();
2562 	} else {
2563 		doSelectionCursorNext();
2564 	}
2565 }
2566 /**
2567  * Deletes the next character. Delete the selected text if any.
2568  */
doDelete()2569 void doDelete() {
2570 	Event event = new Event();
2571 	event.text = "";
2572 	if (selection.x != selection.y) {
2573 		event.start = selection.x;
2574 		event.end = selection.y;
2575 		sendKeyEvent(event);
2576 	} else if (caretOffset < content.getCharCount()) {
2577 		int line = content.getLineAtOffset(caretOffset);
2578 		int lineOffset = content.getOffsetAtLine(line);
2579 		int lineLength = content.getLine(line).length();
2580 		if (caretOffset == lineOffset + lineLength) {
2581 			event.start = caretOffset;
2582 			event.end = content.getOffsetAtLine(line + 1);
2583 		} else {
2584 			event.start = caretOffset;
2585 			event.end = getClusterNext(caretOffset, line);
2586 		}
2587 		sendKeyEvent(event);
2588 	}
2589 }
2590 /**
2591  * Deletes the next word.
2592  */
doDeleteWordNext()2593 void doDeleteWordNext() {
2594 	if (selection.x != selection.y) {
2595 		// if a selection exists, treat the as if
2596 		// only the delete key was pressed
2597 		doDelete();
2598 	} else {
2599 		Event event = new Event();
2600 		event.text = "";
2601 		event.start = caretOffset;
2602 		event.end = getWordNext(caretOffset, SWT.MOVEMENT_WORD);
2603 		sendKeyEvent(event);
2604 	}
2605 }
2606 /**
2607  * Deletes the previous word.
2608  */
doDeleteWordPrevious()2609 void doDeleteWordPrevious() {
2610 	if (selection.x != selection.y) {
2611 		// if a selection exists, treat as if
2612 		// only the backspace key was pressed
2613 		doBackspace();
2614 	} else {
2615 		Event event = new Event();
2616 		event.text = "";
2617 		event.start = getWordPrevious(caretOffset, SWT.MOVEMENT_WORD);
2618 		event.end = caretOffset;
2619 		sendKeyEvent(event);
2620 	}
2621 }
2622 /**
2623  * Moves the caret one line down and to the same character offset relative
2624  * to the beginning of the line. Move the caret to the end of the new line
2625  * if the new line is shorter than the character offset. Moves the caret to
2626  * the end of the text if the caret already is on the last line.
2627  */
doLineDown(boolean select)2628 void doLineDown(boolean select) {
2629 	int caretLine = getCaretLine();
2630 	int lineCount = content.getLineCount();
2631 	int y = 0;
2632 	boolean lastLine = false;
2633 	if (isWordWrap()) {
2634 		int lineOffset = content.getOffsetAtLine(caretLine);
2635 		int offsetInLine = caretOffset - lineOffset;
2636 		TextLayout layout = renderer.getTextLayout(caretLine);
2637 		int lineIndex = getVisualLineIndex(layout, offsetInLine);
2638 		int layoutLineCount = layout.getLineCount();
2639 		if (lineIndex == layoutLineCount - 1) {
2640 			lastLine = caretLine == lineCount - 1;
2641 			caretLine++;
2642 		} else {
2643 			y = layout.getLineBounds(lineIndex + 1).y;
2644 			y++; // bug 485722: workaround for fractional line heights
2645 		}
2646 		renderer.disposeTextLayout(layout);
2647 	} else {
2648 		lastLine = caretLine == lineCount - 1;
2649 		caretLine++;
2650 	}
2651 	if (lastLine) {
2652 		setCaretOffset(content.getCharCount(), SWT.DEFAULT);
2653 	} else {
2654 		int[] alignment = new int[1];
2655 		int offset = getOffsetAtPoint(columnX, y, caretLine, alignment);
2656 		setCaretOffset(offset, alignment[0]);
2657 	}
2658 	int oldColumnX = columnX;
2659 	int oldHScrollOffset = horizontalScrollOffset;
2660 	if (select) {
2661 		setMouseWordSelectionAnchor();
2662 		// select first and then scroll to reduce flash when key
2663 		// repeat scrolls lots of lines
2664 		doSelection(ST.COLUMN_NEXT);
2665 	}
2666 	showCaret();
2667 	int hScrollChange = oldHScrollOffset - horizontalScrollOffset;
2668 	columnX = oldColumnX + hScrollChange;
2669 }
2670 /**
2671  * Moves the caret to the end of the line.
2672  */
doLineEnd()2673 void doLineEnd() {
2674 	int caretLine = getCaretLine();
2675 	int lineOffset = content.getOffsetAtLine(caretLine);
2676 	int lineEndOffset;
2677 	if (isWordWrap()) {
2678 		TextLayout layout = renderer.getTextLayout(caretLine);
2679 		int offsetInLine = caretOffset - lineOffset;
2680 		int lineIndex = getVisualLineIndex(layout, offsetInLine);
2681 		int[] offsets = layout.getLineOffsets();
2682 		lineEndOffset = lineOffset + offsets[lineIndex + 1];
2683 		renderer.disposeTextLayout(layout);
2684 	} else {
2685 		int lineLength = content.getLine(caretLine).length();
2686 		lineEndOffset = lineOffset + lineLength;
2687 	}
2688 	setCaretOffset(lineEndOffset, PREVIOUS_OFFSET_TRAILING);
2689 	showCaret();
2690 }
2691 /**
2692  * Moves the caret to the beginning of the line.
2693  */
doLineStart()2694 void doLineStart() {
2695 	int caretLine = getCaretLine();
2696 	int lineOffset = content.getOffsetAtLine(caretLine);
2697 	if (isWordWrap()) {
2698 		TextLayout layout = renderer.getTextLayout(caretLine);
2699 		int offsetInLine = caretOffset - lineOffset;
2700 		int lineIndex = getVisualLineIndex(layout, offsetInLine);
2701 		int[] offsets = layout.getLineOffsets();
2702 		lineOffset += offsets[lineIndex];
2703 		renderer.disposeTextLayout(layout);
2704 	}
2705 	setCaretOffset(lineOffset, OFFSET_LEADING);
2706 	showCaret();
2707 }
2708 /**
2709  * Moves the caret one line up and to the same character offset relative
2710  * to the beginning of the line. Move the caret to the end of the new line
2711  * if the new line is shorter than the character offset. Moves the caret to
2712  * the beginning of the document if it is already on the first line.
2713  */
doLineUp(boolean select)2714 void doLineUp(boolean select) {
2715 	int caretLine = getCaretLine(), y = 0;
2716 	boolean firstLine = false;
2717 	if (isWordWrap()) {
2718 		int lineOffset = content.getOffsetAtLine(caretLine);
2719 		int offsetInLine = caretOffset - lineOffset;
2720 		TextLayout layout = renderer.getTextLayout(caretLine);
2721 		int lineIndex = getVisualLineIndex(layout, offsetInLine);
2722 		if (lineIndex == 0) {
2723 			firstLine = caretLine == 0;
2724 			if (!firstLine) {
2725 				caretLine--;
2726 				y = renderer.getLineHeight(caretLine) - 1;
2727 				y--; // bug 485722: workaround for fractional line heights
2728 			}
2729 		} else {
2730 			y = layout.getLineBounds(lineIndex - 1).y;
2731 			y++; // bug 485722: workaround for fractional line heights
2732 		}
2733 		renderer.disposeTextLayout(layout);
2734 	} else {
2735 		firstLine = caretLine == 0;
2736 		caretLine--;
2737 	}
2738 	if (firstLine) {
2739 		setCaretOffset(0, SWT.DEFAULT);
2740 	} else {
2741 		int[] alignment = new int[1];
2742 		int offset = getOffsetAtPoint(columnX, y, caretLine, alignment);
2743 		setCaretOffset(offset, alignment[0]);
2744 	}
2745 	int oldColumnX = columnX;
2746 	int oldHScrollOffset = horizontalScrollOffset;
2747 	if (select) setMouseWordSelectionAnchor();
2748 	showCaret();
2749 	if (select) doSelection(ST.COLUMN_PREVIOUS);
2750 	int hScrollChange = oldHScrollOffset - horizontalScrollOffset;
2751 	columnX = oldColumnX + hScrollChange;
2752 }
doMouseLinkCursor()2753 void doMouseLinkCursor() {
2754 	Display display = getDisplay();
2755 	Point point = display.getCursorLocation();
2756 	point = display.map(null, this, point);
2757 	doMouseLinkCursor(point.x, point.y);
2758 }
doMouseLinkCursor(int x, int y)2759 void doMouseLinkCursor(int x, int y) {
2760 	int offset = getOffsetAtPoint(x, y, null, true);
2761 	Display display = getDisplay();
2762 	Cursor newCursor = cursor;
2763 	if (renderer.hasLink(offset)) {
2764 		newCursor = display.getSystemCursor(SWT.CURSOR_HAND);
2765 	} else {
2766 		if (cursor == null) {
2767 			int type = blockSelection ? SWT.CURSOR_CROSS : SWT.CURSOR_IBEAM;
2768 			newCursor = display.getSystemCursor(type);
2769 		}
2770 	}
2771 	if (newCursor != getCursor()) super.setCursor(newCursor);
2772 }
2773 /**
2774  * Moves the caret to the specified location.
2775  *
2776  * @param x x location of the new caret position
2777  * @param y y location of the new caret position
2778  * @param select the location change is a selection operation.
2779  * 	include the line delimiter in the selection
2780  */
doMouseLocationChange(int x, int y, boolean select)2781 void doMouseLocationChange(int x, int y, boolean select) {
2782 	int line = getLineIndex(y);
2783 
2784 	updateCaretDirection = true;
2785 
2786 	if (blockSelection) {
2787 		x = Math.max(leftMargin, Math.min(x, clientAreaWidth - rightMargin));
2788 		y = Math.max(topMargin, Math.min(y, clientAreaHeight - bottomMargin));
2789 		if (doubleClickEnabled && clickCount > 1) {
2790 			boolean wordSelect = (clickCount & 1) == 0;
2791 			if (wordSelect) {
2792 				Point left = getPointAtOffset(doubleClickSelection.x);
2793 				int[] trailing = new int[1];
2794 				int offset = getOffsetAtPoint(x, y, trailing, true);
2795 				if (offset != -1) {
2796 					if (x > left.x) {
2797 						offset = getWordNext(offset + trailing[0], SWT.MOVEMENT_WORD_END);
2798 						setBlockSelectionOffset(doubleClickSelection.x, offset, true);
2799 					} else {
2800 						offset = getWordPrevious(offset + trailing[0], SWT.MOVEMENT_WORD_START);
2801 						setBlockSelectionOffset(doubleClickSelection.y, offset, true);
2802 					}
2803 				} else {
2804 					if (x > left.x) {
2805 						setBlockSelectionLocation(left.x, left.y, x, y, true);
2806 					} else {
2807 						Point right = getPointAtOffset(doubleClickSelection.y);
2808 						setBlockSelectionLocation(right.x, right.y, x, y, true);
2809 					}
2810 				}
2811 			} else {
2812 				setBlockSelectionLocation(blockXLocation, y, true);
2813 			}
2814 			return;
2815 		} else {
2816 			if (select) {
2817 				if (blockXLocation == -1) {
2818 					setBlockSelectionOffset(caretOffset, false);
2819 				}
2820 			} else {
2821 				clearBlockSelection(true, false);
2822 			}
2823 			int[] trailing = new int[1];
2824 			int offset = getOffsetAtPoint(x, y, trailing, true);
2825 			if (offset != -1) {
2826 				if (select) {
2827 					setBlockSelectionOffset(offset + trailing[0], true);
2828 					return;
2829 				}
2830 			} else {
2831 				if (isFixedLineHeight() && renderer.fixedPitch) {
2832 					int avg = renderer.averageCharWidth;
2833 					x = ((x + avg / 2 - leftMargin + horizontalScrollOffset) / avg * avg) + leftMargin - horizontalScrollOffset;
2834 				}
2835 				setBlockSelectionLocation(x, y, true);
2836 				return;
2837 			}
2838 		}
2839 	}
2840 
2841 	// allow caret to be placed below first line only if receiver is
2842 	// not in single line mode. fixes 4820.
2843 	if (line < 0 || (isSingleLine() && line > 0)) {
2844 		return;
2845 	}
2846 	int[] alignment = new int[1];
2847 	int newCaretOffset = getOffsetAtPoint(x, y, alignment);
2848 	int newCaretAlignemnt = alignment[0];
2849 
2850 	if (doubleClickEnabled && clickCount > 1) {
2851 		newCaretOffset = doMouseWordSelect(x, newCaretOffset, line);
2852 	}
2853 
2854 	int newCaretLine = content.getLineAtOffset(newCaretOffset);
2855 
2856 	// Is the mouse within the left client area border or on
2857 	// a different line? If not the autoscroll selection
2858 	// could be incorrectly reset. Fixes 1GKM3XS
2859 	boolean vchange = 0 <= y && y < clientAreaHeight || newCaretLine == 0 || newCaretLine == content.getLineCount() - 1;
2860 	boolean hchange = 0 <= x && x < clientAreaWidth || wordWrap || newCaretLine != content.getLineAtOffset(caretOffset);
2861 	if (vchange && hchange && (newCaretOffset != caretOffset || newCaretAlignemnt != caretAlignment)) {
2862 		setCaretOffset(newCaretOffset, newCaretAlignemnt);
2863 		if (select) doMouseSelection();
2864 		showCaret();
2865 	}
2866 	if (!select) {
2867 		setCaretOffset(newCaretOffset, newCaretAlignemnt);
2868 		clearSelection(true);
2869 	}
2870 }
2871 /**
2872  * Updates the selection based on the caret position
2873  */
2874 void doMouseSelection() {
2875 	if (caretOffset <= selection.x ||
2876 		(caretOffset > selection.x &&
2877 		 caretOffset < selection.y && selectionAnchor == selection.x)) {
2878 		doSelection(ST.COLUMN_PREVIOUS);
2879 	} else {
2880 		doSelection(ST.COLUMN_NEXT);
2881 	}
2882 }
2883 /**
2884  * Returns the offset of the word at the specified offset.
2885  * If the current selection extends from high index to low index
2886  * (i.e., right to left, or caret is at left border of selection on
2887  * non-bidi platforms) the start offset of the word preceding the
2888  * selection is returned. If the current selection extends from
2889  * low index to high index the end offset of the word following
2890  * the selection is returned.
2891  *
2892  * @param x mouse x location
2893  * @param newCaretOffset caret offset of the mouse cursor location
2894  * @param line line index of the mouse cursor location
2895  */
2896 int doMouseWordSelect(int x, int newCaretOffset, int line) {
2897 	// flip selection anchor based on word selection direction from
2898 	// base double click. Always do this here (and don't rely on doAutoScroll)
2899 	// because auto scroll only does not cover all possible mouse selections
2900 	// (e.g., mouse x < 0 && mouse y > caret line y)
2901 	if (newCaretOffset < selectionAnchor && selectionAnchor == selection.x) {
2902 		selectionAnchor = doubleClickSelection.y;
2903 	} else if (newCaretOffset > selectionAnchor && selectionAnchor == selection.y) {
2904 		selectionAnchor = doubleClickSelection.x;
2905 	}
2906 	if (0 <= x && x < clientAreaWidth) {
2907 		boolean wordSelect = (clickCount & 1) == 0;
2908 		if (caretOffset == selection.x) {
2909 			if (wordSelect) {
2910 				newCaretOffset = getWordPrevious(newCaretOffset, SWT.MOVEMENT_WORD_START);
2911 			} else {
2912 				newCaretOffset = content.getOffsetAtLine(line);
2913 			}
2914 		} else {
2915 			if (wordSelect) {
2916 				newCaretOffset = getWordNext(newCaretOffset, SWT.MOVEMENT_WORD_END);
2917 			} else {
2918 				int lineEnd = content.getCharCount();
2919 				if (line + 1 < content.getLineCount()) {
2920 					lineEnd = content.getOffsetAtLine(line + 1);
2921 				}
2922 				newCaretOffset = lineEnd;
2923 			}
2924 		}
2925 	}
2926 	return newCaretOffset;
2927 }
2928 /**
2929  * Scrolls one page down so that the last line (truncated or whole)
2930  * of the current page becomes the fully visible top line.
2931  * <p>
2932  * The caret is scrolled the same number of lines so that its location
2933  * relative to the top line remains the same. The exception is the end
2934  * of the text where a full page scroll is not possible. In this case
2935  * the caret is moved after the last character.
2936  * </p>
2937  *
2938  * @param select whether or not to select the page
2939  */
2940 void doPageDown(boolean select, int height) {
2941 	if (isSingleLine()) return;
2942 	int oldColumnX = columnX;
2943 	int oldHScrollOffset = horizontalScrollOffset;
2944 	if (isFixedLineHeight()) {
2945 		int lineCount = content.getLineCount();
2946 		int caretLine = getCaretLine();
2947 		if (caretLine < lineCount - 1) {
2948 			int lineHeight = renderer.getLineHeight();
2949 			int lines = (height == -1 ? clientAreaHeight : height) / lineHeight;
2950 			int scrollLines = Math.min(lineCount - caretLine - 1, lines);
2951 			// ensure that scrollLines never gets negative and at least one
2952 			// line is scrolled. fixes bug 5602.
2953 			scrollLines = Math.max(1, scrollLines);
2954 			int[] alignment = new int[1];
2955 			int offset = getOffsetAtPoint(columnX, getLinePixel(caretLine + scrollLines), alignment);
2956 			setCaretOffset(offset, alignment[0]);
2957 			if (select) {
2958 				doSelection(ST.COLUMN_NEXT);
2959 			}
2960 			// scroll one page down or to the bottom
2961 			int verticalMaximum = lineCount * getVerticalIncrement();
2962 			int pageSize = clientAreaHeight;
2963 			int verticalScrollOffset = getVerticalScrollOffset();
2964 			int scrollOffset = verticalScrollOffset + scrollLines * getVerticalIncrement();
2965 			if (scrollOffset + pageSize > verticalMaximum) {
2966 				scrollOffset = verticalMaximum - pageSize;
2967 			}
2968 			if (scrollOffset > verticalScrollOffset) {
2969 				scrollVertical(scrollOffset - verticalScrollOffset, true);
2970 			}
2971 		}
2972 	} else {
2973 		int lineCount = content.getLineCount();
2974 		int caretLine = getCaretLine();
2975 		int lineIndex, lineHeight;
2976 		if (height == -1) {
2977 			lineIndex = getPartialBottomIndex();
2978 			int topY = getLinePixel(lineIndex);
2979 			lineHeight = renderer.getLineHeight(lineIndex);
2980 			height = topY;
2981 			if (topY + lineHeight <= clientAreaHeight) {
2982 				height += lineHeight;
2983 			} else {
2984 				if (isWordWrap()) {
2985 					TextLayout layout = renderer.getTextLayout(lineIndex);
2986 					int y = clientAreaHeight - topY;
2987 					for (int i = 0; i < layout.getLineCount(); i++) {
2988 						Rectangle bounds = layout.getLineBounds(i);
2989 						if (bounds.contains(bounds.x, y)) {
2990 							height += bounds.y;
2991 							break;
2992 						}
2993 					}
2994 					renderer.disposeTextLayout(layout);
2995 				}
2996 			}
2997 		} else {
2998 			lineIndex = getLineIndex(height);
2999 			int topLineY = getLinePixel(lineIndex);
3000 			if (isWordWrap()) {
3001 				TextLayout layout = renderer.getTextLayout(lineIndex);
3002 				int y = height - topLineY;
3003 				for (int i = 0; i < layout.getLineCount(); i++) {
3004 					Rectangle bounds = layout.getLineBounds(i);
3005 					if (bounds.contains(bounds.x, y)) {
3006 						height = topLineY + bounds.y + bounds.height;
3007 						break;
3008 					}
3009 				}
3010 				renderer.disposeTextLayout(layout);
3011 			} else {
3012 				height = topLineY + renderer.getLineHeight(lineIndex);
3013 			}
3014 		}
3015 		int caretHeight = height;
3016 		if (isWordWrap()) {
3017 			TextLayout layout = renderer.getTextLayout(caretLine);
3018 			int offsetInLine = caretOffset - content.getOffsetAtLine(caretLine);
3019 			lineIndex = getVisualLineIndex(layout, offsetInLine);
3020 			caretHeight += layout.getLineBounds(lineIndex).y;
3021 			renderer.disposeTextLayout(layout);
3022 		}
3023 		lineIndex = caretLine;
3024 		lineHeight = renderer.getLineHeight(lineIndex);
3025 		while (caretHeight - lineHeight >= 0 && lineIndex < lineCount - 1) {
3026 			caretHeight -= lineHeight;
3027 			lineHeight = renderer.getLineHeight(++lineIndex);
3028 		}
3029 		int[] alignment = new int[1];
3030 		int offset = getOffsetAtPoint(columnX, caretHeight, lineIndex, alignment);
3031 		setCaretOffset(offset, alignment[0]);
3032 		if (select) doSelection(ST.COLUMN_NEXT);
3033 		height = getAvailableHeightBellow(height);
3034 		scrollVertical(height, true);
3035 		if (height == 0) setCaretLocation();
3036 	}
3037 	showCaret();
3038 	int hScrollChange = oldHScrollOffset - horizontalScrollOffset;
3039 	columnX = oldColumnX + hScrollChange;
3040 }
3041 /**
3042  * Moves the cursor to the end of the last fully visible line.
3043  */
3044 void doPageEnd() {
3045 	// go to end of line if in single line mode. fixes 5673
3046 	if (isSingleLine()) {
3047 		doLineEnd();
3048 	} else {
3049 		int bottomOffset;
3050 		if (isWordWrap()) {
3051 			int lineIndex = getPartialBottomIndex();
3052 			TextLayout layout = renderer.getTextLayout(lineIndex);
3053 			int y = (clientAreaHeight - bottomMargin) - getLinePixel(lineIndex);
3054 			int index = layout.getLineCount() - 1;
3055 			while (index >= 0) {
3056 				Rectangle bounds = layout.getLineBounds(index);
3057 				if (y >= bounds.y + bounds.height) break;
3058 				index--;
3059 			}
3060 			if (index == -1 && lineIndex > 0) {
3061 				bottomOffset = content.getOffsetAtLine(lineIndex - 1) + content.getLine(lineIndex - 1).length();
3062 			} else {
3063 				bottomOffset = content.getOffsetAtLine(lineIndex) + Math.max(0, layout.getLineOffsets()[index + 1] - 1);
3064 			}
3065 			renderer.disposeTextLayout(layout);
3066 		} else {
3067 			int lineIndex = getBottomIndex();
3068 			bottomOffset = content.getOffsetAtLine(lineIndex) + content.getLine(lineIndex).length();
3069 		}
3070 		if (caretOffset < bottomOffset) {
3071 			setCaretOffset(bottomOffset, OFFSET_LEADING);
3072 			showCaret();
3073 		}
3074 	}
3075 }
3076 /**
3077  * Moves the cursor to the beginning of the first fully visible line.
3078  */
3079 void doPageStart() {
3080 	int topOffset;
3081 	if (isWordWrap()) {
3082 		int y, lineIndex;
3083 		if (topIndexY > 0) {
3084 			lineIndex = topIndex - 1;
3085 			y = renderer.getLineHeight(lineIndex) - topIndexY;
3086 		} else {
3087 			lineIndex = topIndex;
3088 			y = -topIndexY;
3089 		}
3090 		TextLayout layout = renderer.getTextLayout(lineIndex);
3091 		int index = 0;
3092 		int lineCount = layout.getLineCount();
3093 		while (index < lineCount) {
3094 			Rectangle bounds = layout.getLineBounds(index);
3095 			if (y <= bounds.y) break;
3096 			index++;
3097 		}
3098 		if (index == lineCount) {
3099 			topOffset = content.getOffsetAtLine(lineIndex + 1);
3100 		} else {
3101 			topOffset = content.getOffsetAtLine(lineIndex) + layout.getLineOffsets()[index];
3102 		}
3103 		renderer.disposeTextLayout(layout);
3104 	} else {
3105 		topOffset = content.getOffsetAtLine(topIndex);
3106 	}
3107 	if (caretOffset > topOffset) {
3108 		setCaretOffset(topOffset, OFFSET_LEADING);
3109 		showCaret();
3110 	}
3111 }
3112 /**
3113  * Scrolls one page up so that the first line (truncated or whole)
3114  * of the current page becomes the fully visible last line.
3115  * The caret is scrolled the same number of lines so that its location
3116  * relative to the top line remains the same. The exception is the beginning
3117  * of the text where a full page scroll is not possible. In this case the
3118  * caret is moved in front of the first character.
3119  */
3120 void doPageUp(boolean select, int height) {
3121 	if (isSingleLine()) return;
3122 	int oldHScrollOffset = horizontalScrollOffset;
3123 	int oldColumnX = columnX;
3124 	if (isFixedLineHeight()) {
3125 		int caretLine = getCaretLine();
3126 		if (caretLine > 0) {
3127 			int lineHeight = renderer.getLineHeight();
3128 			int lines = (height == -1 ? clientAreaHeight : height) / lineHeight;
3129 			int scrollLines = Math.max(1, Math.min(caretLine, lines));
3130 			caretLine -= scrollLines;
3131 			int[] alignment = new int[1];
3132 			int offset = getOffsetAtPoint(columnX, getLinePixel(caretLine), alignment);
3133 			setCaretOffset(offset, alignment[0]);
3134 			if (select) {
3135 				doSelection(ST.COLUMN_PREVIOUS);
3136 			}
3137 			int verticalScrollOffset = getVerticalScrollOffset();
3138 			int scrollOffset = Math.max(0, verticalScrollOffset - scrollLines * getVerticalIncrement());
3139 			if (scrollOffset < verticalScrollOffset) {
3140 				scrollVertical(scrollOffset - verticalScrollOffset, true);
3141 			}
3142 		}
3143 	} else {
3144 		int caretLine = getCaretLine();
3145 		int lineHeight, lineIndex;
3146 		if (height == -1) {
3147 			if (topIndexY == 0) {
3148 				height = clientAreaHeight;
3149 			} else {
3150 				int y;
3151 				if (topIndex > 0) {
3152 					lineIndex = topIndex - 1;
3153 					lineHeight = renderer.getLineHeight(lineIndex);
3154 					height = clientAreaHeight - topIndexY;
3155 					y = lineHeight - topIndexY;
3156 				} else {
3157 					lineIndex = topIndex;
3158 					lineHeight = renderer.getLineHeight(lineIndex);
3159 					height = clientAreaHeight - (lineHeight + topIndexY);
3160 					y = -topIndexY;
3161 				}
3162 				if (isWordWrap()) {
3163 					TextLayout layout = renderer.getTextLayout(lineIndex);
3164 					for (int i = 0; i < layout.getLineCount(); i++) {
3165 						Rectangle bounds = layout.getLineBounds(i);
3166 						if (bounds.contains(bounds.x, y)) {
3167 							height += lineHeight - (bounds.y + bounds.height);
3168 							break;
3169 						}
3170 					}
3171 					renderer.disposeTextLayout(layout);
3172 				}
3173 			}
3174 		} else {
3175 			lineIndex = getLineIndex(clientAreaHeight - height);
3176 			int topLineY = getLinePixel(lineIndex);
3177 			if (isWordWrap()) {
3178 				TextLayout layout = renderer.getTextLayout(lineIndex);
3179 				int y = topLineY;
3180 				for (int i = 0; i < layout.getLineCount(); i++) {
3181 					Rectangle bounds = layout.getLineBounds(i);
3182 					if (bounds.contains(bounds.x, y)) {
3183 						height = clientAreaHeight - (topLineY + bounds.y);
3184 						break;
3185 					}
3186 				}
3187 				renderer.disposeTextLayout(layout);
3188 			} else {
3189 				height = clientAreaHeight - topLineY;
3190 			}
3191 		}
3192 		int caretHeight = height;
3193 		if (isWordWrap()) {
3194 			TextLayout layout = renderer.getTextLayout(caretLine);
3195 			int offsetInLine = caretOffset - content.getOffsetAtLine(caretLine);
3196 			lineIndex = getVisualLineIndex(layout, offsetInLine);
3197 			caretHeight += layout.getBounds().height - layout.getLineBounds(lineIndex).y;
3198 			renderer.disposeTextLayout(layout);
3199 		}
3200 		lineIndex = caretLine;
3201 		lineHeight = renderer.getLineHeight(lineIndex);
3202 		while (caretHeight - lineHeight >= 0 && lineIndex > 0) {
3203 			caretHeight -= lineHeight;
3204 			lineHeight = renderer.getLineHeight(--lineIndex);
3205 		}
3206 		lineHeight = renderer.getLineHeight(lineIndex);
3207 		int[] alignment = new int[1];
3208 		int offset = getOffsetAtPoint(columnX, lineHeight - caretHeight, lineIndex, alignment);
3209 		setCaretOffset(offset, alignment[0]);
3210 		if (select) doSelection(ST.COLUMN_PREVIOUS);
3211 		height = getAvailableHeightAbove(height);
3212 		scrollVertical(-height, true);
3213 		if (height == 0) setCaretLocation();
3214 	}
3215 	showCaret();
3216 	int hScrollChange = oldHScrollOffset - horizontalScrollOffset;
3217 	columnX = oldColumnX + hScrollChange;
3218 }
3219 /**
3220  * Updates the selection to extend to the current caret position.
3221  */
3222 void doSelection(int direction) {
3223 	int redrawStart = -1;
3224 	int redrawEnd = -1;
3225 	if (selectionAnchor == -1) {
3226 		selectionAnchor = selection.x;
3227 	}
3228 	if (direction == ST.COLUMN_PREVIOUS) {
3229 		if (caretOffset < selection.x) {
3230 			// grow selection
3231 			redrawEnd = selection.x;
3232 			redrawStart = selection.x = caretOffset;
3233 			// check if selection has reversed direction
3234 			if (selection.y != selectionAnchor) {
3235 				redrawEnd = selection.y;
3236 				selection.y = selectionAnchor;
3237 			}
3238 		// test whether selection actually changed. Fixes 1G71EO1
3239 		} else if (selectionAnchor == selection.x && caretOffset < selection.y) {
3240 			// caret moved towards selection anchor (left side of selection).
3241 			// shrink selection
3242 			redrawEnd = selection.y;
3243 			redrawStart = selection.y = caretOffset;
3244 		}
3245 	} else {
3246 		if (caretOffset > selection.y) {
3247 			// grow selection
3248 			redrawStart = selection.y;
3249 			redrawEnd = selection.y = caretOffset;
3250 			// check if selection has reversed direction
3251 			if (selection.x != selectionAnchor) {
3252 				redrawStart = selection.x;
3253 				selection.x = selectionAnchor;
3254 			}
3255 		// test whether selection actually changed. Fixes 1G71EO1
3256 		} else if (selectionAnchor == selection.y && caretOffset > selection.x) {
3257 			// caret moved towards selection anchor (right side of selection).
3258 			// shrink selection
3259 			redrawStart = selection.x;
3260 			redrawEnd = selection.x = caretOffset;
3261 		}
3262 	}
3263 	if (redrawStart != -1 && redrawEnd != -1) {
3264 		internalRedrawRange(redrawStart, redrawEnd - redrawStart);
3265 		sendSelectionEvent();
3266 	}
3267 	sendAccessibleTextCaretMoved();
3268 }
3269 /**
3270  * Moves the caret to the next character or to the beginning of the
3271  * next line if the cursor is at the end of a line.
3272  */
3273 void doSelectionCursorNext() {
3274 	int caretLine = getCaretLine();
3275 	int lineOffset = content.getOffsetAtLine(caretLine);
3276 	int offsetInLine = caretOffset - lineOffset;
3277 	int offset, alignment;
3278 	if (offsetInLine < content.getLine(caretLine).length()) {
3279 		TextLayout layout = renderer.getTextLayout(caretLine);
3280 		offsetInLine = layout.getNextOffset(offsetInLine, SWT.MOVEMENT_CLUSTER);
3281 		int lineStart = layout.getLineOffsets()[layout.getLineIndex(offsetInLine)];
3282 		renderer.disposeTextLayout(layout);
3283 		offset = offsetInLine + lineOffset;
3284 		alignment = offsetInLine == lineStart ? OFFSET_LEADING : PREVIOUS_OFFSET_TRAILING;
3285 		setCaretOffset(offset, alignment);
3286 		showCaret();
3287 	} else if (caretLine < content.getLineCount() - 1 && !isSingleLine()) {
3288 		caretLine++;
3289 		offset = content.getOffsetAtLine(caretLine);
3290 		alignment = PREVIOUS_OFFSET_TRAILING;
3291 		setCaretOffset(offset, alignment);
3292 		showCaret();
3293 	}
3294 }
3295 /**
3296  * Moves the caret to the previous character or to the end of the previous
3297  * line if the cursor is at the beginning of a line.
3298  */
3299 void doSelectionCursorPrevious() {
3300 	int caretLine = getCaretLine();
3301 	int lineOffset = content.getOffsetAtLine(caretLine);
3302 	int offsetInLine = caretOffset - lineOffset;
3303 	if (offsetInLine > 0) {
3304 		int offset = getClusterPrevious(caretOffset, caretLine);
3305 		setCaretOffset(offset, OFFSET_LEADING);
3306 		showCaret();
3307 	} else if (caretLine > 0) {
3308 		caretLine--;
3309 		lineOffset = content.getOffsetAtLine(caretLine);
3310 		int offset = lineOffset + content.getLine(caretLine).length();
3311 		setCaretOffset(offset, OFFSET_LEADING);
3312 		showCaret();
3313 	}
3314 }
3315 /**
3316  * Moves the caret one line down and to the same character offset relative
3317  * to the beginning of the line. Moves the caret to the end of the new line
3318  * if the new line is shorter than the character offset.
3319  * Moves the caret to the end of the text if the caret already is on the
3320  * last line.
3321  * Adjusts the selection according to the caret change. This can either add
3322  * to or subtract from the old selection, depending on the previous selection
3323  * direction.
3324  */
3325 void doSelectionLineDown() {
3326 	int oldColumnX = columnX = getPointAtOffset(caretOffset).x;
3327 	doLineDown(true);
3328 	columnX = oldColumnX;
3329 }
3330 /**
3331  * Moves the caret one line up and to the same character offset relative
3332  * to the beginning of the line. Moves the caret to the end of the new line
3333  * if the new line is shorter than the character offset.
3334  * Moves the caret to the beginning of the document if it is already on the
3335  * first line.
3336  * Adjusts the selection according to the caret change. This can either add
3337  * to or subtract from the old selection, depending on the previous selection
3338  * direction.
3339  */
3340 void doSelectionLineUp() {
3341 	int oldColumnX = columnX = getPointAtOffset(caretOffset).x;
3342 	doLineUp(true);
3343 	columnX = oldColumnX;
3344 }
3345 /**
3346  * Scrolls one page down so that the last line (truncated or whole)
3347  * of the current page becomes the fully visible top line.
3348  * <p>
3349  * The caret is scrolled the same number of lines so that its location
3350  * relative to the top line remains the same. The exception is the end
3351  * of the text where a full page scroll is not possible. In this case
3352  * the caret is moved after the last character.
3353  * </p><p>
3354  * Adjusts the selection according to the caret change. This can either add
3355  * to or subtract from the old selection, depending on the previous selection
3356  * direction.
3357  * </p>
3358  */
3359 void doSelectionPageDown(int pixels) {
3360 	int oldColumnX = columnX = getPointAtOffset(caretOffset).x;
3361 	doPageDown(true, pixels);
3362 	columnX = oldColumnX;
3363 }
3364 /**
3365  * Scrolls one page up so that the first line (truncated or whole)
3366  * of the current page becomes the fully visible last line.
3367  * <p>
3368  * The caret is scrolled the same number of lines so that its location
3369  * relative to the top line remains the same. The exception is the beginning
3370  * of the text where a full page scroll is not possible. In this case the
3371  * caret is moved in front of the first character.
3372  * </p><p>
3373  * Adjusts the selection according to the caret change. This can either add
3374  * to or subtract from the old selection, depending on the previous selection
3375  * direction.
3376  * </p>
3377  */
3378 void doSelectionPageUp(int pixels) {
3379 	int oldColumnX = columnX = getPointAtOffset(caretOffset).x;
3380 	doPageUp(true, pixels);
3381 	columnX = oldColumnX;
3382 }
3383 /**
3384  * Moves the caret to the end of the next word .
3385  */
3386 void doSelectionWordNext() {
3387 	int offset = getWordNext(caretOffset, SWT.MOVEMENT_WORD);
3388 	// don't change caret position if in single line mode and the cursor
3389 	// would be on a different line. fixes 5673
3390 	if (!isSingleLine() ||
3391 		content.getLineAtOffset(caretOffset) == content.getLineAtOffset(offset)) {
3392 		// Force symmetrical movement for word next and previous. Fixes 14536
3393 		setCaretOffset(offset, OFFSET_LEADING);
3394 		showCaret();
3395 	}
3396 }
3397 /**
3398  * Moves the caret to the start of the previous word.
3399  */
3400 void doSelectionWordPrevious() {
3401 	int offset = getWordPrevious(caretOffset, SWT.MOVEMENT_WORD);
3402 	setCaretOffset(offset, OFFSET_LEADING);
3403 	showCaret();
3404 }
3405 /**
3406  * Moves the caret one character to the left.  Do not go to the previous line.
3407  * When in a bidi locale and at a R2L character the caret is moved to the
3408  * beginning of the R2L segment (visually right) and then one character to the
3409  * left (visually left because it's now in a L2R segment).
3410  */
3411 void doVisualPrevious() {
3412 	int offset = getClusterPrevious(caretOffset, getCaretLine());
3413 	setCaretOffset(offset, SWT.DEFAULT);
3414 	showCaret();
3415 }
3416 /**
3417  * Moves the caret one character to the right.  Do not go to the next line.
3418  * When in a bidi locale and at a R2L character the caret is moved to the
3419  * end of the R2L segment (visually left) and then one character to the
3420  * right (visually right because it's now in a L2R segment).
3421  */
3422 void doVisualNext() {
3423 	int offset = getClusterNext(caretOffset, getCaretLine());
3424 	setCaretOffset(offset, SWT.DEFAULT);
3425 	showCaret();
3426 }
3427 /**
3428  * Moves the caret to the end of the next word.
3429  * If a selection exists, move the caret to the end of the selection
3430  * and remove the selection.
3431  */
3432 void doWordNext() {
3433 	if (selection.y - selection.x > 0) {
3434 		setCaretOffset(selection.y, SWT.DEFAULT);
3435 		showCaret();
3436 	} else {
3437 		doSelectionWordNext();
3438 	}
3439 }
3440 /**
3441  * Moves the caret to the start of the previous word.
3442  * If a selection exists, move the caret to the start of the selection
3443  * and remove the selection.
3444  */
3445 void doWordPrevious() {
3446 	if (selection.y - selection.x > 0) {
3447 		setCaretOffset(selection.x, SWT.DEFAULT);
3448 		showCaret();
3449 	} else {
3450 		doSelectionWordPrevious();
3451 	}
3452 }
3453 /**
3454  * Ends the autoscroll process.
3455  */
3456 void endAutoScroll() {
3457 	autoScrollDirection = SWT.NULL;
3458 }
3459 @Override
3460 public Color getBackground() {
3461 	checkWidget();
3462 	if (background == null) {
3463 		return getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
3464 	}
3465 	return background;
3466 }
3467 /**
3468  * Returns the baseline, in points.
3469  *
3470  * Note: this API should not be used if a StyleRange attribute causes lines to
3471  * have different heights (i.e. different fonts, rise, etc).
3472  *
3473  * @return baseline the baseline
3474  * @exception SWTException <ul>
3475  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3476  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3477  * </ul>
3478  * @since 3.0
3479  *
3480  * @see #getBaseline(int)
3481  */
3482 public int getBaseline() {
3483 	checkWidget();
3484 	return renderer.getBaseline();
3485 }
3486 /**
3487  * Returns the baseline at the given offset, in points.
3488  *
3489  * @param offset the offset
3490  *
3491  * @return baseline the baseline
3492  *
3493  * @exception SWTException <ul>
3494  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3495  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3496  * </ul>
3497  * @exception IllegalArgumentException <ul>
3498  *   <li>ERROR_INVALID_RANGE when the offset is outside the valid range (&lt; 0 or &gt; getCharCount())</li>
3499  * </ul>
3500  *
3501  * @since 3.2
3502  */
3503 public int getBaseline(int offset) {
3504 	checkWidget();
3505 	if (!(0 <= offset && offset <= content.getCharCount())) {
3506 		SWT.error(SWT.ERROR_INVALID_RANGE);
3507 	}
3508 	if (isFixedLineHeight()) {
3509 		return renderer.getBaseline();
3510 	}
3511 	int lineIndex = content.getLineAtOffset(offset);
3512 	int lineOffset = content.getOffsetAtLine(lineIndex);
3513 	TextLayout layout = renderer.getTextLayout(lineIndex);
3514 	int lineInParagraph = layout.getLineIndex(Math.min(offset - lineOffset, layout.getText().length()));
3515 	FontMetrics metrics = layout.getLineMetrics(lineInParagraph);
3516 	renderer.disposeTextLayout(layout);
3517 	return metrics.getAscent() + metrics.getLeading();
3518 }
3519 /**
3520  * Gets the BIDI coloring mode.  When true the BIDI text display
3521  * algorithm is applied to segments of text that are the same
3522  * color.
3523  *
3524  * @return the current coloring mode
3525  * @exception SWTException <ul>
3526  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3527  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3528  * </ul>
3529  *
3530  * @deprecated use BidiSegmentListener instead.
3531  */
3532 @Deprecated
3533 public boolean getBidiColoring() {
3534 	checkWidget();
3535 	return bidiColoring;
3536 }
3537 /**
3538  * Returns whether the widget is in block selection mode.
3539  *
3540  * @return true if widget is in block selection mode, false otherwise
3541  *
3542  * @exception SWTException <ul>
3543  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3544  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3545  * </ul>
3546  *
3547  * @since 3.5
3548  */
3549 public boolean getBlockSelection() {
3550 	checkWidget();
3551 	return blockSelection;
3552 }
3553 Rectangle getBlockSelectionPosition() {
3554 	int firstLine = getLineIndex(blockYAnchor - getVerticalScrollOffset());
3555 	int lastLine = getLineIndex(blockYLocation - getVerticalScrollOffset());
3556 	if (firstLine > lastLine) {
3557 		int temp = firstLine;
3558 		firstLine = lastLine;
3559 		lastLine = temp;
3560 	}
3561 	int left = blockXAnchor;
3562 	int right = blockXLocation;
3563 	if (left > right) {
3564 		left = blockXLocation;
3565 		right = blockXAnchor;
3566 	}
3567 	return new Rectangle (left - horizontalScrollOffset, firstLine, right - horizontalScrollOffset, lastLine);
3568 }
3569 /**
3570  * Returns the block selection bounds. The bounds is
3571  * relative to the upper left corner of the document.
3572  *
3573  * @return the block selection bounds
3574  *
3575  * @exception SWTException <ul>
3576  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3577  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3578  * </ul>
3579  *
3580  * @since 3.5
3581  */
3582 public Rectangle getBlockSelectionBounds() {
3583 	Rectangle rect;
3584 	if (blockSelection && blockXLocation != -1) {
3585 		rect = getBlockSelectionRectangle();
3586 	} else {
3587 		Point startPoint = getPointAtOffset(selection.x);
3588 		Point endPoint = getPointAtOffset(selection.y);
3589 		int height = getLineHeight(selection.y);
3590 		rect = new Rectangle(startPoint.x, startPoint.y, endPoint.x - startPoint.x, endPoint.y + height - startPoint.y);
3591 		if (selection.x == selection.y) {
3592 			rect.width = getCaretWidth();
3593 		}
3594 	}
3595 	rect.x += horizontalScrollOffset;
3596 	rect.y += getVerticalScrollOffset();
3597 	return rect;
3598 }
3599 Rectangle getBlockSelectionRectangle() {
3600 	Rectangle rect = getBlockSelectionPosition();
3601 	rect.y = getLinePixel(rect.y);
3602 	rect.width = rect.width - rect.x;
3603 	rect.height =  getLinePixel(rect.height + 1) - rect.y;
3604 	return rect;
3605 }
3606 String getBlockSelectionText(String delimiter) {
3607 	Rectangle rect = getBlockSelectionPosition();
3608 	int firstLine = rect.y;
3609 	int lastLine = rect.height;
3610 	int left = rect.x;
3611 	int right = rect.width;
3612 	StringBuilder buffer = new StringBuilder();
3613 	for (int lineIndex = firstLine; lineIndex <= lastLine; lineIndex++) {
3614 		int start = getOffsetAtPoint(left, 0, lineIndex, null);
3615 		int end = getOffsetAtPoint(right, 0, lineIndex, null);
3616 		if (start > end) {
3617 			int temp = start;
3618 			start = end;
3619 			end = temp;
3620 		}
3621 		String text = content.getTextRange(start, end - start);
3622 		buffer.append(text);
3623 		if (lineIndex < lastLine) buffer.append(delimiter);
3624 	}
3625 	return buffer.toString();
3626 }
3627 /**
3628  * Returns the index of the last fully visible line.
3629  *
3630  * @return index of the last fully visible line.
3631  */
3632 int getBottomIndex() {
3633 	int bottomIndex;
3634 	if (isFixedLineHeight()) {
3635 		int lineCount = 1;
3636 		int lineHeight = renderer.getLineHeight();
3637 		if (lineHeight != 0) {
3638 			// calculate the number of lines that are fully visible
3639 			int partialTopLineHeight = topIndex * lineHeight - getVerticalScrollOffset();
3640 			lineCount = (clientAreaHeight - partialTopLineHeight) / lineHeight;
3641 		}
3642 		bottomIndex = Math.min(content.getLineCount() - 1, topIndex + Math.max(0, lineCount - 1));
3643 	} else {
3644 		int clientAreaHeight = this.clientAreaHeight - bottomMargin;
3645 		bottomIndex = getLineIndex(clientAreaHeight);
3646 		if (bottomIndex > 0) {
3647 			int linePixel = getLinePixel(bottomIndex);
3648 			int lineHeight = renderer.getLineHeight(bottomIndex);
3649 			if (linePixel + lineHeight > clientAreaHeight) {
3650 				if (getLinePixel(bottomIndex - 1) >= topMargin) {
3651 					bottomIndex--;
3652 				}
3653 			}
3654 		}
3655 	}
3656 	return bottomIndex;
3657 }
3658 /**
3659  * Returns the bottom margin.
3660  *
3661  * @return the bottom margin.
3662  * @exception SWTException <ul>
3663  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3664  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3665  * </ul>
3666  *
3667  * @since 3.5
3668  */
3669 public int getBottomMargin() {
3670 	checkWidget();
3671 	return bottomMargin;
3672 }
3673 Rectangle getBoundsAtOffset(int offset) {
3674 	int lineIndex = content.getLineAtOffset(offset);
3675 	int lineOffset = content.getOffsetAtLine(lineIndex);
3676 	String line = content.getLine(lineIndex);
3677 	Rectangle bounds;
3678 	if (line.length() != 0) {
3679 		TextLayout layout = renderer.getTextLayout(lineIndex);
3680 		int offsetInLine = Math.min (layout.getText().length(), Math.max (0, offset - lineOffset));
3681 		bounds = layout.getBounds(offsetInLine, offsetInLine);
3682 		if (getListeners(ST.LineGetSegments).length > 0 && caretAlignment == PREVIOUS_OFFSET_TRAILING && offsetInLine != 0) {
3683 			offsetInLine = layout.getPreviousOffset(offsetInLine, SWT.MOVEMENT_CLUSTER);
3684 			Point point = layout.getLocation(offsetInLine, true);
3685 			bounds = new Rectangle (point.x, point.y, 0, bounds.height);
3686 		}
3687 		renderer.disposeTextLayout(layout);
3688 	} else {
3689 		bounds = new Rectangle (0, 0, 0, renderer.getLineHeight());
3690 	}
3691 	if (offset == caretOffset && !isWordWrap()) {
3692 		int lineEnd = lineOffset + line.length();
3693 		if (offset == lineEnd) {
3694 			bounds.width += getCaretWidth();
3695 		}
3696 	}
3697 	bounds.x += leftMargin - horizontalScrollOffset;
3698 	bounds.y += getLinePixel(lineIndex);
3699 	return bounds;
3700 }
3701 /**
3702  * Returns the caret position relative to the start of the text.
3703  *
3704  * @return the caret position relative to the start of the text.
3705  * @exception SWTException <ul>
3706  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3707  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3708  * </ul>
3709  */
3710 public int getCaretOffset() {
3711 	checkWidget();
3712 	return caretOffset;
3713 }
3714 /**
3715  * Returns the caret width.
3716  *
3717  * @return the caret width, 0 if caret is null.
3718  */
3719 int getCaretWidth() {
3720 	Caret caret = getCaret();
3721 	if (caret == null) return 0;
3722 	return caret.getSize().x;
3723 }
3724 Object getClipboardContent(int clipboardType) {
3725 	TextTransfer plainTextTransfer = TextTransfer.getInstance();
3726 	return clipboard.getContents(plainTextTransfer, clipboardType);
3727 }
3728 int getClusterNext(int offset, int lineIndex) {
3729 	int lineOffset = content.getOffsetAtLine(lineIndex);
3730 	TextLayout layout = renderer.getTextLayout(lineIndex);
3731 	offset -= lineOffset;
3732 	offset = layout.getNextOffset(offset, SWT.MOVEMENT_CLUSTER);
3733 	offset += lineOffset;
3734 	renderer.disposeTextLayout(layout);
3735 	return offset;
3736 }
3737 int getClusterPrevious(int offset, int lineIndex) {
3738 	int lineOffset = content.getOffsetAtLine(lineIndex);
3739 	TextLayout layout = renderer.getTextLayout(lineIndex);
3740 	offset -= lineOffset;
3741 	offset = layout.getPreviousOffset(offset, SWT.MOVEMENT_CLUSTER);
3742 	offset += lineOffset;
3743 	renderer.disposeTextLayout(layout);
3744 	return offset;
3745 }
3746 /**
3747  * Returns the content implementation that is used for text storage.
3748  *
3749  * @return content the user defined content implementation that is used for
3750  * text storage or the default content implementation if no user defined
3751  * content implementation has been set.
3752  * @exception SWTException <ul>
3753  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3754  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3755  * </ul>
3756  */
3757 public StyledTextContent getContent() {
3758 	checkWidget();
3759 	return content;
3760 }
3761 @Override
3762 public boolean getDragDetect () {
3763 	checkWidget ();
3764 	return dragDetect;
3765 }
3766 /**
3767  * Returns whether the widget implements double click mouse behavior.
3768  *
3769  * @return true if double clicking a word selects the word, false if double clicks
3770  * have the same effect as regular mouse clicks
3771  * @exception SWTException <ul>
3772  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3773  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3774  * </ul>
3775  */
3776 public boolean getDoubleClickEnabled() {
3777 	checkWidget();
3778 	return doubleClickEnabled;
3779 }
3780 /**
3781  * Returns whether the widget content can be edited.
3782  *
3783  * @return true if content can be edited, false otherwise
3784  * @exception SWTException <ul>
3785  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3786  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3787  * </ul>
3788  */
3789 public boolean getEditable() {
3790 	checkWidget();
3791 	return editable;
3792 }
3793 @Override
3794 public Color getForeground() {
3795 	checkWidget();
3796 	if (foreground == null) {
3797 		return getDisplay().getSystemColor(SWT.COLOR_LIST_FOREGROUND);
3798 	}
3799 	return foreground;
3800 }
3801 /**
3802  * Returns the horizontal scroll increment.
3803  *
3804  * @return horizontal scroll increment.
3805  */
3806 int getHorizontalIncrement() {
3807 	return renderer.averageCharWidth;
3808 }
3809 /**
3810  * Returns the horizontal scroll offset relative to the start of the line.
3811  *
3812  * @return horizontal scroll offset relative to the start of the line,
3813  * measured in character increments starting at 0, if &gt; 0 the content is scrolled
3814  * @exception SWTException <ul>
3815  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3816  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3817  * </ul>
3818  */
3819 public int getHorizontalIndex() {
3820 	checkWidget();
3821 	return horizontalScrollOffset / getHorizontalIncrement();
3822 }
3823 /**
3824  * Returns the horizontal scroll offset relative to the start of the line.
3825  *
3826  * @return the horizontal scroll offset relative to the start of the line,
3827  * measured in SWT logical point starting at 0, if &gt; 0 the content is scrolled.
3828  * @exception SWTException <ul>
3829  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3830  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3831  * </ul>
3832  */
3833 public int getHorizontalPixel() {
3834 	checkWidget();
3835 	return horizontalScrollOffset;
3836 }
3837 /**
3838  * Returns the line indentation of the widget.
3839  *
3840  * @return the line indentation
3841  *
3842  * @exception SWTException <ul>
3843  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3844  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3845  * </ul>
3846  *
3847  * @see #getLineIndent(int)
3848  *
3849  * @since 3.2
3850  */
3851 public int getIndent() {
3852 	checkWidget();
3853 	return indent;
3854 }
3855 /**
3856  * Returns whether the widget justifies lines.
3857  *
3858  * @return whether lines are justified
3859  *
3860  * @exception SWTException <ul>
3861  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3862  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3863  * </ul>
3864  *
3865  * @see #getLineJustify(int)
3866  *
3867  * @since 3.2
3868  */
3869 public boolean getJustify() {
3870 	checkWidget();
3871 	return justify;
3872 }
3873 /**
3874  * Returns the action assigned to the key.
3875  * Returns SWT.NULL if there is no action associated with the key.
3876  *
3877  * @param key a key code defined in SWT.java or a character.
3878  * 	Optionally ORd with a state mask.  Preferred state masks are one or more of
3879  *  SWT.MOD1, SWT.MOD2, SWT.MOD3, since these masks account for modifier platform
3880  *  differences.  However, there may be cases where using the specific state masks
3881  *  (i.e., SWT.CTRL, SWT.SHIFT, SWT.ALT, SWT.COMMAND) makes sense.
3882  * @return one of the predefined actions defined in ST.java or SWT.NULL
3883  * 	if there is no action associated with the key.
3884  * @exception SWTException <ul>
3885  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3886  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3887  * </ul>
3888  */
3889 public int getKeyBinding(int key) {
3890 	checkWidget();
3891 	Integer action = keyActionMap.get(key);
3892 	return action == null ? SWT.NULL : action.intValue();
3893 }
3894 /**
3895  * Gets the number of characters.
3896  *
3897  * @return number of characters in the widget
3898  * @exception SWTException <ul>
3899  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3900  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3901  * </ul>
3902  */
3903 public int getCharCount() {
3904 	checkWidget();
3905 	return content.getCharCount();
3906 }
3907 /**
3908  * Returns the line at the given line index without delimiters.
3909  * Index 0 is the first line of the content. When there are not
3910  * any lines, getLine(0) is a valid call that answers an empty string.
3911  * <p>
3912  *
3913  * @param lineIndex index of the line to return.
3914  * @return the line text without delimiters
3915  *
3916  * @exception SWTException <ul>
3917  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3918  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3919  * </ul>
3920  * @exception IllegalArgumentException <ul>
3921  *   <li>ERROR_INVALID_RANGE when the line index is outside the valid range (&lt; 0 or &gt;= getLineCount())</li>
3922  * </ul>
3923  * @since 3.4
3924  */
3925 public String getLine(int lineIndex) {
3926 	checkWidget();
3927 	if (lineIndex < 0 ||
3928 		(lineIndex > 0 && lineIndex >= content.getLineCount())) {
3929 		SWT.error(SWT.ERROR_INVALID_RANGE);
3930 	}
3931 	return content.getLine(lineIndex);
3932 }
3933 /**
3934  * Returns the alignment of the line at the given index.
3935  *
3936  * @param index the index of the line
3937  *
3938  * @return the line alignment
3939  *
3940  * @exception SWTException <ul>
3941  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3942  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3943  * </ul>
3944  * @exception IllegalArgumentException <ul>
3945  *    <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
3946  * </ul>
3947  *
3948  * @see #getAlignment()
3949  *
3950  * @since 3.2
3951  */
getLineAlignment(int index)3952 public int getLineAlignment(int index) {
3953 	checkWidget();
3954 	if (index < 0 || index > content.getLineCount()) {
3955 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3956 	}
3957 	return renderer.getLineAlignment(index, alignment);
3958 }
3959 /**
3960  * Returns the line at the specified offset in the text
3961  * where 0 &lt; offset &lt; getCharCount() so that getLineAtOffset(getCharCount())
3962  * returns the line of the insert location.
3963  *
3964  * @param offset offset relative to the start of the content.
3965  * 	0 &lt;= offset &lt;= getCharCount()
3966  * @return line at the specified offset in the text
3967  * @exception SWTException <ul>
3968  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3969  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3970  * </ul>
3971  * @exception IllegalArgumentException <ul>
3972  *   <li>ERROR_INVALID_RANGE when the offset is outside the valid range (&lt; 0 or &gt; getCharCount())</li>
3973  * </ul>
3974  */
getLineAtOffset(int offset)3975 public int getLineAtOffset(int offset) {
3976 	checkWidget();
3977 	if (offset < 0 || offset > getCharCount()) {
3978 		SWT.error(SWT.ERROR_INVALID_RANGE);
3979 	}
3980 	return content.getLineAtOffset(offset);
3981 }
3982 /**
3983  * Returns the background color of the line at the given index.
3984  * Returns null if a LineBackgroundListener has been set or if no background
3985  * color has been specified for the line. Should not be called if a
3986  * LineBackgroundListener has been set since the listener maintains the
3987  * line background colors.
3988  *
3989  * @param index the index of the line
3990  * @return the background color of the line at the given index.
3991  *
3992  * @exception SWTException <ul>
3993  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3994  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3995  * </ul>
3996  * @exception IllegalArgumentException <ul>
3997  *    <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
3998  * </ul>
3999  */
getLineBackground(int index)4000 public Color getLineBackground(int index) {
4001 	checkWidget();
4002 	if (index < 0 || index > content.getLineCount()) {
4003 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4004 	}
4005 	return isListening(ST.LineGetBackground) ? null : renderer.getLineBackground(index, null);
4006 }
4007 /**
4008  * Returns the bullet of the line at the given index.
4009  *
4010  * @param index the index of the line
4011  *
4012  * @return the line bullet
4013  *
4014  * @exception SWTException <ul>
4015  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4016  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4017  * </ul>
4018  * @exception IllegalArgumentException <ul>
4019  *    <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
4020  * </ul>
4021  *
4022  * @since 3.2
4023  */
getLineBullet(int index)4024 public Bullet getLineBullet(int index) {
4025 	checkWidget();
4026 	if (index < 0 || index > content.getLineCount()) {
4027 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4028 	}
4029 	return isListening(ST.LineGetStyle) ? null : renderer.getLineBullet(index, null);
4030 }
4031 /**
4032  * Returns the line background data for the given line or null if
4033  * there is none.
4034  *
4035  * @param lineOffset offset of the line start relative to the start
4036  * 	of the content.
4037  * @param line line to get line background data for
4038  * @return line background data for the given line.
4039  */
getLineBackgroundData(int lineOffset, String line)4040 StyledTextEvent getLineBackgroundData(int lineOffset, String line) {
4041 	return sendLineEvent(ST.LineGetBackground, lineOffset, line);
4042 }
4043 /**
4044  * Gets the number of text lines.
4045  *
4046  * @return the number of lines in the widget
4047  * @exception SWTException <ul>
4048  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4049  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4050  * </ul>
4051  */
getLineCount()4052 public int getLineCount() {
4053 	checkWidget();
4054 	return content.getLineCount();
4055 }
4056 /**
4057  * Returns the number of lines that can be completely displayed in the
4058  * widget client area.
4059  *
4060  * @return number of lines that can be completely displayed in the widget
4061  * 	client area.
4062  */
getLineCountWhole()4063 int getLineCountWhole() {
4064 	if (isFixedLineHeight()) {
4065 		int lineHeight = renderer.getLineHeight();
4066 		return lineHeight != 0 ? clientAreaHeight / lineHeight : 1;
4067 	}
4068 	return getBottomIndex() - topIndex + 1;
4069 }
4070 /**
4071  * Returns the line delimiter used for entering new lines by key down
4072  * or paste operation.
4073  *
4074  * @return line delimiter used for entering new lines by key down
4075  * or paste operation.
4076  * @exception SWTException <ul>
4077  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4078  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4079  * </ul>
4080  */
getLineDelimiter()4081 public String getLineDelimiter() {
4082 	checkWidget();
4083 	return content.getLineDelimiter();
4084 }
4085 /**
4086  * Returns the line height.
4087  * <p>
4088  * Note: this API should not be used if a StyleRange attribute causes lines to
4089  * have different heights (i.e. different fonts, rise, etc).
4090  * </p>
4091  *
4092  * @return line height in points.
4093  * @exception SWTException <ul>
4094  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4095  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4096  * </ul>
4097  * @see #getLineHeight(int)
4098  */
getLineHeight()4099 public int getLineHeight() {
4100 	checkWidget();
4101 	return renderer.getLineHeight();
4102 }
4103 /**
4104  * Returns the line height at the given offset.
4105  *
4106  * @param offset the offset
4107  *
4108  * @return line height in points
4109  *
4110  * @exception SWTException <ul>
4111  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4112  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4113  * </ul>
4114  * @exception IllegalArgumentException <ul>
4115  *   <li>ERROR_INVALID_RANGE when the offset is outside the valid range (&lt; 0 or &gt; getCharCount())</li>
4116  * </ul>
4117  *
4118  * @since 3.2
4119  */
getLineHeight(int offset)4120 public int getLineHeight(int offset) {
4121 	checkWidget();
4122 	if (!(0 <= offset && offset <= content.getCharCount())) {
4123 		SWT.error(SWT.ERROR_INVALID_RANGE);
4124 	}
4125 	if (isFixedLineHeight()) {
4126 		return renderer.getLineHeight();
4127 	}
4128 	int lineIndex = content.getLineAtOffset(offset);
4129 	int lineOffset = content.getOffsetAtLine(lineIndex);
4130 	TextLayout layout = renderer.getTextLayout(lineIndex);
4131 	int lineInParagraph = layout.getLineIndex(Math.min(offset - lineOffset, layout.getText().length()));
4132 	int height = layout.getLineBounds(lineInParagraph).height;
4133 	renderer.disposeTextLayout(layout);
4134 	return height;
4135 }
4136 /**
4137  * Returns the indentation of the line at the given index.
4138  *
4139  * @param index the index of the line
4140  *
4141  * @return the line indentation
4142  *
4143  * @exception SWTException <ul>
4144  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4145  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4146  * </ul>
4147  * @exception IllegalArgumentException <ul>
4148  *    <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
4149  * </ul>
4150  *
4151  * @see #getIndent()
4152  *
4153  * @since 3.2
4154  */
getLineIndent(int index)4155 public int getLineIndent(int index) {
4156 	checkWidget();
4157 	if (index < 0 || index > content.getLineCount()) {
4158 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4159 	}
4160 	return isListening(ST.LineGetStyle) ? 0 : renderer.getLineIndent(index, indent);
4161 }
4162 /**
4163  * Returns the vertical indentation of the line at the given index.
4164  *
4165  * @param index the index of the line
4166  *
4167  * @return the line vertical indentation
4168  *
4169  * @exception SWTException <ul>
4170  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4171  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4172  * </ul>
4173  * @exception IllegalArgumentException <ul>
4174  *    <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
4175  * </ul>
4176  *
4177  * @since 3.109
4178  */
getLineVerticalIndent(int index)4179 public int getLineVerticalIndent(int index) {
4180 	checkWidget();
4181 	if (index < 0 || index >= content.getLineCount()) {
4182 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4183 	}
4184 	return isListening(ST.LineGetStyle) ? 0 : renderer.getLineVerticalIndent(index);
4185 }
4186 /**
4187  * Returns whether the line at the given index is justified.
4188  *
4189  * @param index the index of the line
4190  *
4191  * @return whether the line is justified
4192  *
4193  * @exception SWTException <ul>
4194  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4195  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4196  * </ul>
4197  * @exception IllegalArgumentException <ul>
4198  *    <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
4199  * </ul>
4200  *
4201  * @see #getJustify()
4202  *
4203  * @since 3.2
4204  */
getLineJustify(int index)4205 public boolean getLineJustify(int index) {
4206 	checkWidget();
4207 	if (index < 0 || index > content.getLineCount()) {
4208 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4209 	}
4210 	return isListening(ST.LineGetStyle) ? false : renderer.getLineJustify(index, justify);
4211 }
4212 /**
4213  * Returns the line spacing of the widget.
4214  *
4215  * @return the line spacing
4216  *
4217  * @exception SWTException <ul>
4218  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4219  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4220  * </ul>
4221  *
4222  * @since 3.2
4223  */
getLineSpacing()4224 public int getLineSpacing() {
4225 	checkWidget();
4226 	return lineSpacing;
4227 }
4228 /**
4229  * Returns the line style data for the given line or null if there is
4230  * none.
4231  * <p>
4232  * If there is a LineStyleListener but it does not set any styles,
4233  * the StyledTextEvent.styles field will be initialized to an empty
4234  * array.
4235  * </p>
4236  *
4237  * @param lineOffset offset of the line start relative to the start of
4238  * 	the content.
4239  * @param line line to get line styles for
4240  * @return line style data for the given line. Styles may start before
4241  * 	line start and end after line end
4242  */
getLineStyleData(int lineOffset, String line)4243 StyledTextEvent getLineStyleData(int lineOffset, String line) {
4244 	return sendLineEvent(ST.LineGetStyle, lineOffset, line);
4245 }
4246 /**
4247  * Returns the top SWT logical point, relative to the client area, of a given line.
4248  * Clamps out of ranges index.
4249  *
4250  * @param lineIndex the line index, the max value is lineCount. If
4251  * lineIndex == lineCount it returns the bottom SWT logical point of the last line.
4252  * It means this function can be used to retrieve the bottom SWT logical point of any line.
4253  *
4254  * @return the top SWT logical point of a given line index
4255  *
4256  * @since 3.2
4257  */
getLinePixel(int lineIndex)4258 public int getLinePixel(int lineIndex) {
4259 	checkWidget();
4260 	int lineCount = content.getLineCount();
4261 	lineIndex = Math.max(0, Math.min(lineCount, lineIndex));
4262 	if (isFixedLineHeight()) {
4263 		int lineHeight = renderer.getLineHeight();
4264 		return lineIndex * lineHeight - getVerticalScrollOffset() + topMargin;
4265 	}
4266 	if (lineIndex == topIndex)
4267 		return topIndexY + topMargin;
4268 	int height = topIndexY;
4269 	if (lineIndex > topIndex) {
4270 		for (int i = topIndex; i < lineIndex; i++) {
4271 			height += renderer.getLineHeight(i);
4272 		}
4273 	} else {
4274 		for (int i = topIndex - 1; i >= lineIndex; i--) {
4275 			height -= renderer.getLineHeight(i);
4276 		}
4277 	}
4278 	return height + topMargin;
4279 }
4280 /**
4281  * Returns the line index for a y, relative to the client area.
4282  * The line index returned is always in the range 0..lineCount - 1.
4283  *
4284  * @param y the y-coordinate point
4285  *
4286  * @return the line index for a given y-coordinate point
4287  *
4288  * @since 3.2
4289  */
getLineIndex(int y)4290 public int getLineIndex(int y) {
4291 	checkWidget();
4292 	y -= topMargin;
4293 	if (isFixedLineHeight()) {
4294 		int lineHeight = renderer.getLineHeight();
4295 		int lineIndex = (y + getVerticalScrollOffset()) / lineHeight;
4296 		int lineCount = content.getLineCount();
4297 		lineIndex = Math.max(0, Math.min(lineCount - 1, lineIndex));
4298 		return lineIndex;
4299 	}
4300 	if (y == topIndexY) return topIndex;
4301 	int line = topIndex;
4302 	if (y < topIndexY) {
4303 		while (y < topIndexY && line > 0) {
4304 			y += renderer.getLineHeight(--line);
4305 		}
4306 	} else {
4307 		int lineCount = content.getLineCount();
4308 		int lineHeight = renderer.getLineHeight(line);
4309 		while (y - lineHeight >= topIndexY && line < lineCount - 1) {
4310 			y -= lineHeight;
4311 			lineHeight = renderer.getLineHeight(++line);
4312 		}
4313 	}
4314 	return line;
4315 }
4316 /**
4317  * Returns the tab stops of the line at the given <code>index</code>.
4318  *
4319  * @param index the index of the line
4320  *
4321  * @return the tab stops for the line
4322  *
4323  * @exception SWTException <ul>
4324  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4325  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4326  * </ul>
4327  * @exception IllegalArgumentException <ul>
4328  *    <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
4329  * </ul>
4330  *
4331  * @see #getTabStops()
4332  *
4333  * @since 3.6
4334  */
getLineTabStops(int index)4335 public int[] getLineTabStops(int index) {
4336 	checkWidget();
4337 	if (index < 0 || index > content.getLineCount()) {
4338 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4339 	}
4340 	if (isListening(ST.LineGetStyle)) return null;
4341 	int[] tabs = renderer.getLineTabStops(index, null);
4342 	if (tabs == null) tabs = this.tabs;
4343 	if (tabs == null) return new int [] {renderer.tabWidth};
4344 	int[] result = new int[tabs.length];
4345 	System.arraycopy(tabs, 0, result, 0, tabs.length);
4346 	return result;
4347 }
4348 /**
4349  * Returns the wrap indentation of the line at the given <code>index</code>.
4350  *
4351  * @param index the index of the line
4352  *
4353  * @return the wrap indentation
4354  *
4355  * @exception SWTException <ul>
4356  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4357  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4358  * </ul>
4359  * @exception IllegalArgumentException <ul>
4360  *    <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
4361  * </ul>
4362  *
4363  * @see #getWrapIndent()
4364  *
4365  * @since 3.6
4366  */
getLineWrapIndent(int index)4367 public int getLineWrapIndent(int index) {
4368 	checkWidget();
4369 	if (index < 0 || index > content.getLineCount()) {
4370 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4371 	}
4372 	return isListening(ST.LineGetStyle) ? 0 : renderer.getLineWrapIndent(index, wrapIndent);
4373 }
4374 /**
4375  * Returns the left margin.
4376  *
4377  * @return the left margin.
4378  * @exception SWTException <ul>
4379  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4380  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4381  * </ul>
4382  *
4383  * @since 3.5
4384  */
getLeftMargin()4385 public int getLeftMargin() {
4386 	checkWidget();
4387 	return leftMargin - alignmentMargin;
4388 }
4389 /**
4390  * Returns the x, y location of the upper left corner of the character
4391  * bounding box at the specified offset in the text. The point is
4392  * relative to the upper left corner of the widget client area.
4393  *
4394  * @param offset offset relative to the start of the content.
4395  * 	0 &lt;= offset &lt;= getCharCount()
4396  * @return x, y location of the upper left corner of the character
4397  * 	bounding box at the specified offset in the text.
4398  * @exception SWTException <ul>
4399  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4400  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4401  * </ul>
4402  * @exception IllegalArgumentException <ul>
4403  *   <li>ERROR_INVALID_RANGE when the offset is outside the valid range (&lt; 0 or &gt; getCharCount())</li>
4404  * </ul>
4405  */
getLocationAtOffset(int offset)4406 public Point getLocationAtOffset(int offset) {
4407 	checkWidget();
4408 	if (offset < 0 || offset > getCharCount()) {
4409 		SWT.error(SWT.ERROR_INVALID_RANGE);
4410 	}
4411 	return getPointAtOffset(offset);
4412 }
4413 /**
4414  * Returns <code>true</code> if the mouse navigator is enabled.
4415  * When mouse navigator is enabled, the user can navigate through the widget by pressing the middle button and moving the cursor
4416  *
4417  * @return the mouse navigator's enabled state
4418  *
4419  * @exception SWTException <ul>
4420  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4421  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4422  * </ul>
4423  *
4424  * @see #getEnabled
4425  * @since 3.110
4426  */
getMouseNavigatorEnabled()4427 public boolean getMouseNavigatorEnabled () {
4428 	checkWidget ();
4429 	return mouseNavigator != null;
4430 }
4431 /**
4432  * Returns the character offset of the first character of the given line.
4433  *
4434  * @param lineIndex index of the line, 0 based relative to the first
4435  * 	line in the content. 0 &lt;= lineIndex &lt; getLineCount(), except
4436  * 	lineIndex may always be 0
4437  * @return offset offset of the first character of the line, relative to
4438  * 	the beginning of the document. The first character of the document is
4439  *	at offset 0.
4440  *  When there are not any lines, getOffsetAtLine(0) is a valid call that
4441  * 	answers 0.
4442  * @exception SWTException <ul>
4443  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4444  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4445  * </ul>
4446  * @exception IllegalArgumentException <ul>
4447  *   <li>ERROR_INVALID_RANGE when the line index is outside the valid range (&lt; 0 or &gt;= getLineCount())</li>
4448  * </ul>
4449  * @since 2.0
4450  */
getOffsetAtLine(int lineIndex)4451 public int getOffsetAtLine(int lineIndex) {
4452 	checkWidget();
4453 	if (lineIndex < 0 ||
4454 		(lineIndex > 0 && lineIndex >= content.getLineCount())) {
4455 		SWT.error(SWT.ERROR_INVALID_RANGE);
4456 	}
4457 	return content.getOffsetAtLine(lineIndex);
4458 }
4459 /**
4460  * Returns the offset of the character at the given location relative
4461  * to the first character in the document.
4462  * <p>
4463  * The return value reflects the character offset that the caret will
4464  * be placed at if a mouse click occurred at the specified location.
4465  * If the x coordinate of the location is beyond the center of a character
4466  * the returned offset will be behind the character.
4467  * </p>
4468  *
4469  * @param point the origin of character bounding box relative to
4470  *  the origin of the widget client area.
4471  * @return offset of the character at the given location relative
4472  *  to the first character in the document.
4473  * @exception SWTException <ul>
4474  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4475  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4476  * </ul>
4477  * @exception IllegalArgumentException <ul>
4478  *   <li>ERROR_NULL_ARGUMENT when point is null</li>
4479  *   <li>ERROR_INVALID_ARGUMENT when there is no character at the specified location</li>
4480  * </ul>
4481  *
4482  * @deprecated Use {@link #getOffsetAtPoint(Point)} instead for better performance
4483  */
4484 @Deprecated
getOffsetAtLocation(Point point)4485 public int getOffsetAtLocation(Point point) {
4486 	checkWidget();
4487 	if (point == null) {
4488 		SWT.error(SWT.ERROR_NULL_ARGUMENT);
4489 	}
4490 	int[] trailing = new int[1];
4491 	int offset = getOffsetAtPoint(point.x, point.y, trailing, true);
4492 	if (offset == -1) {
4493 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4494 	}
4495 	return offset + trailing[0];
4496 }
4497 
4498 /**
4499  * Returns the offset of the character at the given point relative
4500  * to the first character in the document.
4501  * <p>
4502  * The return value reflects the character offset that the caret will
4503  * be placed at if a mouse click occurred at the specified point.
4504  * If the x coordinate of the point is beyond the center of a character
4505  * the returned offset will be behind the character.
4506  * </p>
4507  * Note: This method is functionally similar to {@link #getOffsetAtLocation(Point)} except that
4508  * it does not throw an exception when no character is found and thus performs faster.
4509  *
4510  * @param point the origin of character bounding box relative to
4511  *  the origin of the widget client area.
4512  * @return offset of the character at the given point relative
4513  *  to the first character in the document.
4514  * -1 when there is no character at the specified location.
4515  * @exception SWTException <ul>
4516  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4517  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4518  * </ul>
4519  * @exception IllegalArgumentException <ul>
4520  *   <li>ERROR_NULL_ARGUMENT when point is <code>null</code></li>
4521  * </ul>
4522  *
4523  * @since 3.107
4524  */
getOffsetAtPoint(Point point)4525 public int getOffsetAtPoint(Point point) {
4526 	checkWidget();
4527 	if (point == null) {
4528 		SWT.error(SWT.ERROR_NULL_ARGUMENT);
4529 	}
4530 	int[] trailing = new int[1];
4531 	int offset = getOffsetAtPoint(point.x, point.y, trailing, true);
4532 	return offset != -1 ? offset + trailing[0] : -1;
4533 }
4534 
getOffsetAtPoint(int x, int y, int[] alignment)4535 int getOffsetAtPoint(int x, int y, int[] alignment) {
4536 	int lineIndex = getLineIndex(y);
4537 	y -= getLinePixel(lineIndex);
4538 	return getOffsetAtPoint(x, y, lineIndex, alignment);
4539 }
getOffsetAtPoint(int x, int y, int lineIndex, int[] alignment)4540 int getOffsetAtPoint(int x, int y, int lineIndex, int[] alignment) {
4541 	TextLayout layout = renderer.getTextLayout(lineIndex);
4542 	x += horizontalScrollOffset - leftMargin;
4543 	int[] trailing = new int[1];
4544 	int offsetInLine = layout.getOffset(x, y, trailing);
4545 	if (alignment != null) alignment[0] = OFFSET_LEADING;
4546 	if (trailing[0] != 0) {
4547 		int lineInParagraph = layout.getLineIndex(offsetInLine + trailing[0]);
4548 		int lineStart = layout.getLineOffsets()[lineInParagraph];
4549 		if (offsetInLine + trailing[0] == lineStart) {
4550 			offsetInLine += trailing[0];
4551 			if (alignment != null) alignment[0] = PREVIOUS_OFFSET_TRAILING;
4552 		} else {
4553 			String line = content.getLine(lineIndex);
4554 			int level = 0;
4555 			if (alignment != null) {
4556 				int offset = offsetInLine;
4557 				while (offset > 0 && Character.isDigit(line.charAt(offset))) offset--;
4558 				if (offset == 0 && Character.isDigit(line.charAt(offset))) {
4559 					level = isMirrored() ? 1 : 0;
4560 				} else {
4561 					level = layout.getLevel(offset) & 0x1;
4562 				}
4563 			}
4564 			offsetInLine += trailing[0];
4565 			if (alignment != null) {
4566 				int trailingLevel = layout.getLevel(offsetInLine) & 0x1;
4567 				if (level != trailingLevel) {
4568 					alignment[0] = PREVIOUS_OFFSET_TRAILING;
4569 				} else {
4570 					alignment[0] = OFFSET_LEADING;
4571 				}
4572 			}
4573 		}
4574 	}
4575 	renderer.disposeTextLayout(layout);
4576 	return offsetInLine + content.getOffsetAtLine(lineIndex);
4577 }
getOffsetAtPoint(int x, int y, int[] trailing, boolean inTextOnly)4578 int getOffsetAtPoint(int x, int y, int[] trailing, boolean inTextOnly) {
4579 	if (inTextOnly && y + getVerticalScrollOffset() < 0 || x + horizontalScrollOffset < 0) {
4580 		return -1;
4581 	}
4582 	int bottomIndex = getPartialBottomIndex();
4583 	int height = getLinePixel(bottomIndex + 1);
4584 	if (inTextOnly && y > height) {
4585 		return -1;
4586 	}
4587 	int lineIndex = getLineIndex(y);
4588 	int lineOffset = content.getOffsetAtLine(lineIndex);
4589 	TextLayout layout = renderer.getTextLayout(lineIndex);
4590 	x += horizontalScrollOffset - leftMargin;
4591 	y -= getLinePixel(lineIndex);
4592 	int offset = layout.getOffset(x, y, trailing);
4593 	Rectangle rect = layout.getLineBounds(layout.getLineIndex(offset));
4594 	renderer.disposeTextLayout(layout);
4595 	if (inTextOnly && !(rect.x  <= x && x <=  rect.x + rect.width)) {
4596 		return -1;
4597 	}
4598 	return offset + lineOffset;
4599 }
4600 /**
4601  * Returns the orientation of the receiver.
4602  *
4603  * @return the orientation style
4604  *
4605  * @exception SWTException <ul>
4606  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4607  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4608  * </ul>
4609  *
4610  * @since 2.1.2
4611  */
4612 @Override
getOrientation()4613 public int getOrientation () {
4614 	return super.getOrientation ();
4615 }
4616 /**
4617  * Returns the index of the last partially visible line.
4618  *
4619  * @return index of the last partially visible line.
4620  */
getPartialBottomIndex()4621 int getPartialBottomIndex() {
4622 	if (isFixedLineHeight()) {
4623 		int lineHeight = renderer.getLineHeight();
4624 		int partialLineCount = Compatibility.ceil(clientAreaHeight, lineHeight);
4625 		return Math.max(0, Math.min(content.getLineCount(), topIndex + partialLineCount) - 1);
4626 	}
4627 	return getLineIndex(clientAreaHeight - bottomMargin);
4628 }
4629 /**
4630  * Returns the index of the first partially visible line.
4631  *
4632  * @return index of the first partially visible line.
4633  */
getPartialTopIndex()4634 int getPartialTopIndex() {
4635 	if (isFixedLineHeight()) {
4636 		int lineHeight = renderer.getLineHeight();
4637 		return getVerticalScrollOffset() / lineHeight;
4638 	}
4639 	return topIndexY <= 0 ? topIndex : topIndex - 1;
4640 }
4641 /**
4642  * Returns the content in the specified range using the platform line
4643  * delimiter to separate lines.
4644  *
4645  * @param writer the TextWriter to write line text into
4646  * @return the content in the specified range using the platform line
4647  * 	delimiter to separate lines as written by the specified TextWriter.
4648  */
getPlatformDelimitedText(TextWriter writer)4649 String getPlatformDelimitedText(TextWriter writer) {
4650 	int end = writer.getStart() + writer.getCharCount();
4651 	int startLine = content.getLineAtOffset(writer.getStart());
4652 	int endLine = content.getLineAtOffset(end);
4653 	String endLineText = content.getLine(endLine);
4654 	int endLineOffset = content.getOffsetAtLine(endLine);
4655 
4656 	for (int i = startLine; i <= endLine; i++) {
4657 		writer.writeLine(content.getLine(i), content.getOffsetAtLine(i));
4658 		if (i < endLine) {
4659 			writer.writeLineDelimiter(PlatformLineDelimiter);
4660 		}
4661 	}
4662 	if (end > endLineOffset + endLineText.length()) {
4663 		writer.writeLineDelimiter(PlatformLineDelimiter);
4664 	}
4665 	writer.close();
4666 	return writer.toString();
4667 }
4668 /**
4669  * Returns all the ranges of text that have an associated StyleRange.
4670  * Returns an empty array if a LineStyleListener has been set.
4671  * Should not be called if a LineStyleListener has been set since the
4672  * listener maintains the styles.
4673  * <p>
4674  * The ranges array contains start and length pairs.  Each pair refers to
4675  * the corresponding style in the styles array.  For example, the pair
4676  * that starts at ranges[n] with length ranges[n+1] uses the style
4677  * at styles[n/2] returned by <code>getStyleRanges(int, int, boolean)</code>.
4678  * </p>
4679  *
4680  * @return the ranges or an empty array if a LineStyleListener has been set.
4681  *
4682  * @exception SWTException <ul>
4683  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4684  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4685  * </ul>
4686  *
4687  * @since 3.2
4688  *
4689  * @see #getStyleRanges(boolean)
4690  */
getRanges()4691 public int[] getRanges() {
4692 	checkWidget();
4693 	if (!isListening(ST.LineGetStyle)) {
4694 		int[] ranges = renderer.getRanges(0, content.getCharCount());
4695 		if (ranges != null) return ranges;
4696 	}
4697 	return new int[0];
4698 }
4699 /**
4700  * Returns the ranges of text that have an associated StyleRange.
4701  * Returns an empty array if a LineStyleListener has been set.
4702  * Should not be called if a LineStyleListener has been set since the
4703  * listener maintains the styles.
4704  * <p>
4705  * The ranges array contains start and length pairs.  Each pair refers to
4706  * the corresponding style in the styles array.  For example, the pair
4707  * that starts at ranges[n] with length ranges[n+1] uses the style
4708  * at styles[n/2] returned by <code>getStyleRanges(int, int, boolean)</code>.
4709  * </p>
4710  *
4711  * @param start the start offset of the style ranges to return
4712  * @param length the number of style ranges to return
4713  *
4714  * @return the ranges or an empty array if a LineStyleListener has been set.
4715  *
4716  * @exception SWTException <ul>
4717  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4718  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4719  * </ul>
4720  * @exception IllegalArgumentException <ul>
4721  *   <li>ERROR_INVALID_RANGE if start or length are outside the widget content</li>
4722  * </ul>
4723  *
4724  * @since 3.2
4725  *
4726  * @see #getStyleRanges(int, int, boolean)
4727  */
getRanges(int start, int length)4728 public int[] getRanges(int start, int length) {
4729 	checkWidget();
4730 	int contentLength = getCharCount();
4731 	int end = start + length;
4732 	if (start > end || start < 0 || end > contentLength) {
4733 		SWT.error(SWT.ERROR_INVALID_RANGE);
4734 	}
4735 	if (!isListening(ST.LineGetStyle)) {
4736 		int[] ranges = renderer.getRanges(start, length);
4737 		if (ranges != null) return ranges;
4738 	}
4739 	return new int[0];
4740 }
4741 /**
4742  * Returns the right margin.
4743  *
4744  * @return the right margin.
4745  * @exception SWTException <ul>
4746  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4747  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4748  * </ul>
4749  *
4750  * @since 3.5
4751  */
getRightMargin()4752 public int getRightMargin() {
4753 	checkWidget();
4754 	return rightMargin;
4755 }
4756 /**
4757  * Returns the selection.
4758  * <p>
4759  * Text selections are specified in terms of caret positions.  In a text
4760  * widget that contains N characters, there are N+1 caret positions,
4761  * ranging from 0..N
4762  * </p>
4763  *
4764  * @return start and end of the selection, x is the offset of the first
4765  * 	selected character, y is the offset after the last selected character.
4766  *  The selection values returned are visual (i.e., x will always always be
4767  *  &lt;= y).  To determine if a selection is right-to-left (RtoL) vs. left-to-right
4768  *  (LtoR), compare the caretOffset to the start and end of the selection
4769  *  (e.g., caretOffset == start of selection implies that the selection is RtoL).
4770  * @see #getSelectionRange
4771  * @exception SWTException <ul>
4772  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4773  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4774  * </ul>
4775  */
getSelection()4776 public Point getSelection() {
4777 	checkWidget();
4778 	return new Point(selection.x, selection.y);
4779 }
4780 /**
4781  * Returns the selection.
4782  *
4783  * @return start and length of the selection, x is the offset of the
4784  * 	first selected character, relative to the first character of the
4785  * 	widget content. y is the length of the selection.
4786  *  The selection values returned are visual (i.e., length will always always be
4787  *  positive).  To determine if a selection is right-to-left (RtoL) vs. left-to-right
4788  *  (LtoR), compare the caretOffset to the start and end of the selection
4789  *  (e.g., caretOffset == start of selection implies that the selection is RtoL).
4790  * @exception SWTException <ul>
4791  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4792  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4793  * </ul>
4794  */
getSelectionRange()4795 public Point getSelectionRange() {
4796 	checkWidget();
4797 	return new Point(selection.x, selection.y - selection.x);
4798 }
4799 /**
4800  * Returns the ranges of text that are inside the block selection rectangle.
4801  * <p>
4802  * The ranges array contains start and length pairs. When the receiver is not
4803  * in block selection mode the return arrays contains the start and length of
4804  * the regular selection.
4805  *
4806  * @return the ranges array
4807  *
4808  * @exception SWTException <ul>
4809  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4810  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4811  * </ul>
4812  *
4813  * @since 3.5
4814  */
getSelectionRanges()4815 public int[] getSelectionRanges() {
4816 	checkWidget();
4817 	if (blockSelection && blockXLocation != -1) {
4818 		Rectangle rect = getBlockSelectionPosition();
4819 		int firstLine = rect.y;
4820 		int lastLine = rect.height;
4821 		int left = rect.x;
4822 		int right = rect.width;
4823 		int[] ranges = new int[(lastLine - firstLine + 1) * 2];
4824 		int index = 0;
4825 		for (int lineIndex = firstLine; lineIndex <= lastLine; lineIndex++) {
4826 			int start = getOffsetAtPoint(left, 0, lineIndex, null);
4827 			int end = getOffsetAtPoint(right, 0, lineIndex, null);
4828 			if (start > end) {
4829 				int temp = start;
4830 				start = end;
4831 				end = temp;
4832 			}
4833 			ranges[index++] = start;
4834 			ranges[index++] = end - start;
4835 		}
4836 		return ranges;
4837 	}
4838 	return new int[] {selection.x, selection.y - selection.x};
4839 }
4840 /**
4841  * Returns the receiver's selection background color.
4842  *
4843  * @return the selection background color
4844  *
4845  * @exception SWTException <ul>
4846  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4847  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4848  * </ul>
4849  * @since 2.1
4850  */
getSelectionBackground()4851 public Color getSelectionBackground() {
4852 	checkWidget();
4853 	if (selectionBackground == null) {
4854 		return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION);
4855 	}
4856 	return selectionBackground;
4857 }
4858 /**
4859  * Gets the number of selected characters.
4860  *
4861  * @return the number of selected characters.
4862  * @exception SWTException <ul>
4863  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4864  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4865  * </ul>
4866  */
getSelectionCount()4867 public int getSelectionCount() {
4868 	checkWidget();
4869 	if (blockSelection && blockXLocation != -1) {
4870 		return getBlockSelectionText(content.getLineDelimiter()).length();
4871 	}
4872 	return getSelectionRange().y;
4873 }
4874 /**
4875  * Returns the receiver's selection foreground color.
4876  *
4877  * @return the selection foreground color
4878  *
4879  * @exception SWTException <ul>
4880  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4881  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4882  * </ul>
4883  * @since 2.1
4884  */
getSelectionForeground()4885 public Color getSelectionForeground() {
4886 	checkWidget();
4887 	if (selectionForeground == null) {
4888 		return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT);
4889 	}
4890 	return selectionForeground;
4891 }
4892 /**
4893  * Returns the selected text.
4894  *
4895  * @return selected text, or an empty String if there is no selection.
4896  * @exception SWTException <ul>
4897  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4898  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4899  * </ul>
4900  */
getSelectionText()4901 public String getSelectionText() {
4902 	checkWidget();
4903 	if (blockSelection && blockXLocation != -1) {
4904 		return getBlockSelectionText(content.getLineDelimiter());
4905 	}
4906 	return content.getTextRange(selection.x, selection.y - selection.x);
4907 }
getBidiSegments(int lineOffset, String line)4908 StyledTextEvent getBidiSegments(int lineOffset, String line) {
4909 	if (!isListening(ST.LineGetSegments)) {
4910 		if (!bidiColoring) return null;
4911 		StyledTextEvent event = new StyledTextEvent(content);
4912 		event.segments = getBidiSegmentsCompatibility(line, lineOffset);
4913 		return event;
4914 	}
4915 	StyledTextEvent event = sendLineEvent(ST.LineGetSegments, lineOffset, line);
4916 	if (event == null || event.segments == null || event.segments.length == 0) return null;
4917 	int lineLength = line.length();
4918 	int[] segments = event.segments;
4919 	if (segments[0] > lineLength) {
4920 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4921 	}
4922 	char[] segmentsChars = event.segmentsChars;
4923 	boolean hasSegmentsChars = segmentsChars != null;
4924 	for (int i = 1; i < segments.length; i++) {
4925 		if ((hasSegmentsChars ? segments[i] < segments[i - 1] : segments[i] <= segments[i - 1]) || segments[i] > lineLength) {
4926 			SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4927 		}
4928 	}
4929 	if (hasSegmentsChars && !visualWrap) {
4930 		for (char segmentsChar : segmentsChars) {
4931 			if (segmentsChar == '\n' || segmentsChar == '\r') {
4932 				visualWrap = true;
4933 				break;
4934 			}
4935 		}
4936 	}
4937 	return event;
4938 }
4939 /**
4940  * @see #getBidiSegments
4941  * Supports deprecated setBidiColoring API. Remove when API is removed.
4942  */
getBidiSegmentsCompatibility(String line, int lineOffset)4943 int [] getBidiSegmentsCompatibility(String line, int lineOffset) {
4944 	int lineLength = line.length();
4945 	StyleRange [] styles = null;
4946 	StyledTextEvent event = getLineStyleData(lineOffset, line);
4947 	if (event != null) {
4948 		styles = event.styles;
4949 	} else {
4950 		styles = renderer.getStyleRanges(lineOffset, lineLength, true);
4951 	}
4952 	if (styles == null || styles.length == 0) {
4953 		return new int[] {0, lineLength};
4954 	}
4955 	int k=0, count = 1;
4956 	while (k < styles.length && styles[k].start == 0 && styles[k].length == lineLength) {
4957 		k++;
4958 	}
4959 	int[] offsets = new int[(styles.length - k) * 2 + 2];
4960 	for (int i = k; i < styles.length; i++) {
4961 		StyleRange style = styles[i];
4962 		int styleLineStart = Math.max(style.start - lineOffset, 0);
4963 		int styleLineEnd = Math.max(style.start + style.length - lineOffset, styleLineStart);
4964 		styleLineEnd = Math.min (styleLineEnd, line.length ());
4965 		if (i > 0 && count > 1 &&
4966 			((styleLineStart >= offsets[count-2] && styleLineStart <= offsets[count-1]) ||
4967 			 (styleLineEnd >= offsets[count-2] && styleLineEnd <= offsets[count-1])) &&
4968 			 style.similarTo(styles[i-1])) {
4969 			offsets[count-2] = Math.min(offsets[count-2], styleLineStart);
4970 			offsets[count-1] = Math.max(offsets[count-1], styleLineEnd);
4971 		} else {
4972 			if (styleLineStart > offsets[count - 1]) {
4973 				offsets[count] = styleLineStart;
4974 				count++;
4975 			}
4976 			offsets[count] = styleLineEnd;
4977 			count++;
4978 		}
4979 	}
4980 	// add offset for last non-colored segment in line, if any
4981 	if (lineLength > offsets[count-1]) {
4982 		offsets [count] = lineLength;
4983 		count++;
4984 	}
4985 	if (count == offsets.length) {
4986 		return offsets;
4987 	}
4988 	int [] result = new int [count];
4989 	System.arraycopy (offsets, 0, result, 0, count);
4990 	return result;
4991 }
4992 /**
4993  * Returns the style range at the given offset.
4994  * <p>
4995  * Returns null if a LineStyleListener has been set or if a style is not set
4996  * for the offset.
4997  * Should not be called if a LineStyleListener has been set since the
4998  * listener maintains the styles.
4999  * </p>
5000  *
5001  * @param offset the offset to return the style for.
5002  * 	0 &lt;= offset &lt; getCharCount() must be true.
5003  * @return a StyleRange with start == offset and length == 1, indicating
5004  * 	the style at the given offset. null if a LineStyleListener has been set
5005  * 	or if a style is not set for the given offset.
5006  * @exception SWTException <ul>
5007  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5008  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5009  * </ul>
5010  * @exception IllegalArgumentException <ul>
5011  *   <li>ERROR_INVALID_ARGUMENT when the offset is invalid</li>
5012  * </ul>
5013  */
getStyleRangeAtOffset(int offset)5014 public StyleRange getStyleRangeAtOffset(int offset) {
5015 	checkWidget();
5016 	if (offset < 0 || offset >= getCharCount()) {
5017 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
5018 	}
5019 	if (!isListening(ST.LineGetStyle)) {
5020 		StyleRange[] ranges = renderer.getStyleRanges(offset, 1, true);
5021 		if (ranges != null) return ranges[0];
5022 	}
5023 	return null;
5024 }
5025 /**
5026  * Returns the styles.
5027  * <p>
5028  * Returns an empty array if a LineStyleListener has been set.
5029  * Should not be called if a LineStyleListener has been set since the
5030  * listener maintains the styles.
5031  * </p><p>
5032  * Note: Because a StyleRange includes the start and length, the
5033  * same instance cannot occur multiple times in the array of styles.
5034  * If the same style attributes, such as font and color, occur in
5035  * multiple StyleRanges, <code>getStyleRanges(boolean)</code>
5036  * can be used to get the styles without the ranges.
5037  * </p>
5038  *
5039  * @return the styles or an empty array if a LineStyleListener has been set.
5040  *
5041  * @exception SWTException <ul>
5042  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5043  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5044  * </ul>
5045  *
5046  * @see #getStyleRanges(boolean)
5047  */
getStyleRanges()5048 public StyleRange[] getStyleRanges() {
5049 	checkWidget();
5050 	return getStyleRanges(0, content.getCharCount(), true);
5051 }
5052 /**
5053  * Returns the styles.
5054  * <p>
5055  * Returns an empty array if a LineStyleListener has been set.
5056  * Should not be called if a LineStyleListener has been set since the
5057  * listener maintains the styles.
5058  * </p><p>
5059  * Note: When <code>includeRanges</code> is true, the start and length
5060  * fields of each StyleRange will be valid, however the StyleRange
5061  * objects may need to be cloned. When <code>includeRanges</code> is
5062  * false, <code>getRanges(int, int)</code> can be used to get the
5063  * associated ranges.
5064  * </p>
5065  *
5066  * @param includeRanges whether the start and length field of the StyleRanges should be set.
5067  *
5068  * @return the styles or an empty array if a LineStyleListener has been set.
5069  *
5070  * @exception SWTException <ul>
5071  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5072  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5073  * </ul>
5074  *
5075  * @since 3.2
5076  *
5077  * @see #getRanges(int, int)
5078  * @see #setStyleRanges(int[], StyleRange[])
5079  */
getStyleRanges(boolean includeRanges)5080 public StyleRange[] getStyleRanges(boolean includeRanges) {
5081 	checkWidget();
5082 	return getStyleRanges(0, content.getCharCount(), includeRanges);
5083 }
5084 /**
5085  * Returns the styles for the given text range.
5086  * <p>
5087  * Returns an empty array if a LineStyleListener has been set.
5088  * Should not be called if a LineStyleListener has been set since the
5089  * listener maintains the styles.
5090  * </p><p>
5091  * Note: Because the StyleRange includes the start and length, the
5092  * same instance cannot occur multiple times in the array of styles.
5093  * If the same style attributes, such as font and color, occur in
5094  * multiple StyleRanges, <code>getStyleRanges(int, int, boolean)</code>
5095  * can be used to get the styles without the ranges.
5096  * </p>
5097  * @param start the start offset of the style ranges to return
5098  * @param length the number of style ranges to return
5099  *
5100  * @return the styles or an empty array if a LineStyleListener has
5101  *  been set.  The returned styles will reflect the given range.  The first
5102  *  returned <code>StyleRange</code> will have a starting offset &gt;= start
5103  *  and the last returned <code>StyleRange</code> will have an ending
5104  *  offset &lt;= start + length - 1
5105  *
5106  * @exception SWTException <ul>
5107  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5108  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5109  * </ul>
5110  * @exception IllegalArgumentException <ul>
5111  *   <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
5112  * </ul>
5113  *
5114  * @see #getStyleRanges(int, int, boolean)
5115  *
5116  * @since 3.0
5117  */
getStyleRanges(int start, int length)5118 public StyleRange[] getStyleRanges(int start, int length) {
5119 	checkWidget();
5120 	return getStyleRanges(start, length, true);
5121 }
5122 /**
5123  * Returns the styles for the given text range.
5124  * <p>
5125  * Returns an empty array if a LineStyleListener has been set.
5126  * Should not be called if a LineStyleListener has been set since the
5127  * listener maintains the styles.
5128  * </p><p>
5129  * Note: When <code>includeRanges</code> is true, the start and length
5130  * fields of each StyleRange will be valid, however the StyleRange
5131  * objects may need to be cloned. When <code>includeRanges</code> is
5132  * false, <code>getRanges(int, int)</code> can be used to get the
5133  * associated ranges.
5134  * </p>
5135  *
5136  * @param start the start offset of the style ranges to return
5137  * @param length the number of style ranges to return
5138  * @param includeRanges whether the start and length field of the StyleRanges should be set.
5139  *
5140  * @return the styles or an empty array if a LineStyleListener has
5141  *  been set.  The returned styles will reflect the given range.  The first
5142  *  returned <code>StyleRange</code> will have a starting offset &gt;= start
5143  *  and the last returned <code>StyleRange</code> will have an ending
5144  *  offset &gt;= start + length - 1
5145  *
5146  * @exception SWTException <ul>
5147  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5148  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5149  * </ul>
5150  * @exception IllegalArgumentException <ul>
5151  *   <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
5152  * </ul>
5153  *
5154  * @since 3.2
5155  *
5156  * @see #getRanges(int, int)
5157  * @see #setStyleRanges(int[], StyleRange[])
5158  */
getStyleRanges(int start, int length, boolean includeRanges)5159 public StyleRange[] getStyleRanges(int start, int length, boolean includeRanges) {
5160 	checkWidget();
5161 	int contentLength = getCharCount();
5162 	int end = start + length;
5163 	if (start > end || start < 0 || end > contentLength) {
5164 		SWT.error(SWT.ERROR_INVALID_RANGE);
5165 	}
5166 	if (!isListening(ST.LineGetStyle)) {
5167 		StyleRange[] ranges = renderer.getStyleRanges(start, length, includeRanges);
5168 		if (ranges != null) return ranges;
5169 	}
5170 	return new StyleRange[0];
5171 }
5172 /**
5173  * Returns the tab width measured in characters.
5174  *
5175  * @return tab width measured in characters
5176  * @exception SWTException <ul>
5177  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5178  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5179  * </ul>
5180  *
5181  * @see #getTabStops()
5182  */
getTabs()5183 public int getTabs() {
5184 	checkWidget();
5185 	return tabLength;
5186 }
5187 
5188 /**
5189  * Returns the tab list of the receiver.
5190  *
5191  * @return the tab list
5192  * @exception SWTException <ul>
5193  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5194  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5195  * </ul>
5196  *
5197  * @since 3.6
5198  */
getTabStops()5199 public int[] getTabStops() {
5200 	checkWidget();
5201 	if (tabs == null) return new int [] {renderer.tabWidth};
5202 	int[] result = new int[tabs.length];
5203 	System.arraycopy(tabs, 0, result, 0, tabs.length);
5204 	return result;
5205 }
5206 
5207 /**
5208  * Returns a copy of the widget content.
5209  *
5210  * @return copy of the widget content
5211  * @exception SWTException <ul>
5212  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5213  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5214  * </ul>
5215  */
getText()5216 public String getText() {
5217 	checkWidget();
5218 	return content.getTextRange(0, getCharCount());
5219 }
5220 /**
5221  * Returns the widget content between the two offsets.
5222  *
5223  * @param start offset of the first character in the returned String
5224  * @param end offset of the last character in the returned String
5225  * @return widget content starting at start and ending at end
5226  * @see #getTextRange(int,int)
5227  * @exception SWTException <ul>
5228  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5229  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5230  * </ul>
5231  * @exception IllegalArgumentException <ul>
5232  *   <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
5233  * </ul>
5234  */
getText(int start, int end)5235 public String getText(int start, int end) {
5236 	checkWidget();
5237 	int contentLength = getCharCount();
5238 	if (start < 0 || start >= contentLength || end < 0 || end >= contentLength || start > end) {
5239 		SWT.error(SWT.ERROR_INVALID_RANGE);
5240 	}
5241 	return content.getTextRange(start, end - start + 1);
5242 }
5243 /**
5244  * Returns the smallest bounding rectangle that includes the characters between two offsets.
5245  *
5246  * @param start offset of the first character included in the bounding box
5247  * @param end offset of the last character included in the bounding box
5248  * @return bounding box of the text between start and end
5249  * @exception SWTException <ul>
5250  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5251  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5252  * </ul>
5253  * @exception IllegalArgumentException <ul>
5254  *   <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
5255  * </ul>
5256  * @since 3.1
5257  */
getTextBounds(int start, int end)5258 public Rectangle getTextBounds(int start, int end) {
5259 	checkWidget();
5260 	int contentLength = getCharCount();
5261 	if (start < 0 || start >= contentLength || end < 0 || end >= contentLength || start > end) {
5262 		SWT.error(SWT.ERROR_INVALID_RANGE);
5263 	}
5264 	int lineStart = content.getLineAtOffset(start);
5265 	int lineEnd = content.getLineAtOffset(end);
5266 	Rectangle rect;
5267 	int y = getLinePixel(lineStart);
5268 	int height = 0;
5269 	int left = 0x7fffffff, right = 0;
5270 	for (int i = lineStart; i <= lineEnd; i++) {
5271 		int lineOffset = content.getOffsetAtLine(i);
5272 		TextLayout layout = renderer.getTextLayout(i);
5273 		int length = layout.getText().length();
5274 		if (length > 0) {
5275 			if (i == lineStart) {
5276 				if (i == lineEnd) {
5277 					rect = layout.getBounds(start - lineOffset, end - lineOffset);
5278 				} else {
5279 					rect = layout.getBounds(start - lineOffset, length);
5280 				}
5281 				y += rect.y;
5282 			} else if (i == lineEnd) {
5283 				rect = layout.getBounds(0, end - lineOffset);
5284 			} else {
5285 				rect = layout.getBounds();
5286 			}
5287 			left = Math.min(left, rect.x);
5288 			right = Math.max(right, rect.x + rect.width);
5289 			height += rect.height;
5290 		} else {
5291 			height += renderer.getLineHeight();
5292 		}
5293 		renderer.disposeTextLayout(layout);
5294 	}
5295 	rect = new Rectangle (left, y, right-left, height);
5296 	rect.x += leftMargin - horizontalScrollOffset;
5297 	return rect;
5298 }
5299 /**
5300  * Returns the widget content starting at start for length characters.
5301  *
5302  * @param start offset of the first character in the returned String
5303  * @param length number of characters to return
5304  * @return widget content starting at start and extending length characters.
5305  * @exception SWTException <ul>
5306  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5307  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5308  * </ul>
5309  * @exception IllegalArgumentException <ul>
5310  *   <li>ERROR_INVALID_RANGE when start and/or length are outside the widget content</li>
5311  * </ul>
5312  */
getTextRange(int start, int length)5313 public String getTextRange(int start, int length) {
5314 	checkWidget();
5315 	int contentLength = getCharCount();
5316 	int end = start + length;
5317 	if (start > end || start < 0 || end > contentLength) {
5318 		SWT.error(SWT.ERROR_INVALID_RANGE);
5319 	}
5320 	return content.getTextRange(start, length);
5321 }
5322 /**
5323  * Returns the maximum number of characters that the receiver is capable of holding.
5324  *
5325  * @return the text limit
5326  *
5327  * @exception SWTException <ul>
5328  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5329  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5330  * </ul>
5331  */
getTextLimit()5332 public int getTextLimit() {
5333 	checkWidget();
5334 	return textLimit;
5335 }
5336 /**
5337  * Gets the top index.
5338  * <p>
5339  * The top index is the index of the fully visible line that is currently
5340  * at the top of the widget or the topmost partially visible line if no line is fully visible.
5341  * The top index changes when the widget is scrolled. Indexing is zero based.
5342  * </p>
5343  *
5344  * @return the index of the top line
5345  * @exception SWTException <ul>
5346  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5347  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5348  * </ul>
5349  */
getTopIndex()5350 public int getTopIndex() {
5351 	checkWidget();
5352 	return topIndex;
5353 }
5354 /**
5355  * Returns the top margin.
5356  *
5357  * @return the top margin.
5358  * @exception SWTException <ul>
5359  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5360  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5361  * </ul>
5362  *
5363  * @since 3.5
5364  */
getTopMargin()5365 public int getTopMargin() {
5366 	checkWidget();
5367 	return topMargin;
5368 }
5369 /**
5370  * Gets the top SWT logical point.
5371  * <p>
5372  * The top point is the SWT logical point position of the line that is
5373  * currently at the top of the widget. The text widget can be scrolled by points
5374  * by dragging the scroll thumb so that a partial line may be displayed at the top
5375  * the widget.  The top point changes when the widget is scrolled.  The top point
5376  * does not include the widget trimming.
5377  * </p>
5378  *
5379  * @return SWT logical point position of the top line
5380  * @exception SWTException <ul>
5381  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5382  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5383  * </ul>
5384  */
getTopPixel()5385 public int getTopPixel() {
5386 	checkWidget();
5387 	return getVerticalScrollOffset();
5388 }
5389 /**
5390  * Returns the vertical scroll increment.
5391  *
5392  * @return vertical scroll increment.
5393  */
getVerticalIncrement()5394 int getVerticalIncrement() {
5395 	return renderer.getLineHeight();
5396 }
getVerticalScrollOffset()5397 int getVerticalScrollOffset() {
5398 	if (verticalScrollOffset == -1) {
5399 		renderer.calculate(0, topIndex);
5400 		int height = 0;
5401 		for (int i = 0; i < topIndex; i++) {
5402 			height += renderer.getCachedLineHeight(i);
5403 		}
5404 		height -= topIndexY;
5405 		verticalScrollOffset = height;
5406 	}
5407 	return verticalScrollOffset;
5408 }
getVisualLineIndex(TextLayout layout, int offsetInLine)5409 int getVisualLineIndex(TextLayout layout, int offsetInLine) {
5410 	int lineIndex = layout.getLineIndex(offsetInLine);
5411 	int[] offsets = layout.getLineOffsets();
5412 	Caret caret = getCaret();
5413 	if (caret != null && lineIndex != 0 && offsetInLine == offsets[lineIndex]) {
5414 		int lineY = layout.getLineBounds(lineIndex).y;
5415 		int caretY = caret.getLocation().y - getLinePixel(getCaretLine());
5416 		if (lineY > caretY) lineIndex--;
5417 		caretAlignment = OFFSET_LEADING;
5418 	}
5419 	return lineIndex;
5420 }
getCaretDirection()5421 int getCaretDirection() {
5422 	if (!isBidiCaret()) return SWT.DEFAULT;
5423 	if (ime.getCompositionOffset() != -1) return SWT.DEFAULT;
5424 	if (!updateCaretDirection && caretDirection != SWT.NULL) return caretDirection;
5425 	updateCaretDirection = false;
5426 	int caretLine = getCaretLine();
5427 	int lineOffset = content.getOffsetAtLine(caretLine);
5428 	String line = content.getLine(caretLine);
5429 	int offset = caretOffset - lineOffset;
5430 	int lineLength = line.length();
5431 	if (lineLength == 0) return isMirrored() ? SWT.RIGHT : SWT.LEFT;
5432 	if (caretAlignment == PREVIOUS_OFFSET_TRAILING && offset > 0) offset--;
5433 	if (offset == lineLength && offset > 0) offset--;
5434 	while (offset > 0 && Character.isDigit(line.charAt(offset))) offset--;
5435 	if (offset == 0 && Character.isDigit(line.charAt(offset))) {
5436 		return isMirrored() ? SWT.RIGHT : SWT.LEFT;
5437 	}
5438 	TextLayout layout = renderer.getTextLayout(caretLine);
5439 	int level = layout.getLevel(offset);
5440 	renderer.disposeTextLayout(layout);
5441 	return ((level & 1) != 0) ? SWT.RIGHT : SWT.LEFT;
5442 }
5443 /*
5444  * Returns the index of the line the caret is on.
5445  */
getCaretLine()5446 int getCaretLine() {
5447 	return content.getLineAtOffset(caretOffset);
5448 }
getWrapWidth()5449 int getWrapWidth () {
5450 	if (wordWrap && !isSingleLine()) {
5451 		int width = clientAreaWidth - leftMargin - rightMargin;
5452 		return width > 0 ? width : 1;
5453 	}
5454 	return -1;
5455 }
getWordNext(int offset, int movement)5456 int getWordNext (int offset, int movement) {
5457 	return getWordNext(offset, movement, false);
5458 }
getWordNext(int offset, int movement, boolean ignoreListener)5459 int getWordNext (int offset, int movement, boolean ignoreListener) {
5460 	int newOffset, lineOffset;
5461 	String lineText;
5462 	if (offset >= getCharCount()) {
5463 		newOffset = offset;
5464 		int lineIndex = content.getLineCount() - 1;
5465 		lineOffset = content.getOffsetAtLine(lineIndex);
5466 		lineText = content.getLine(lineIndex);
5467 	} else {
5468 		int lineIndex = content.getLineAtOffset(offset);
5469 		lineOffset = content.getOffsetAtLine(lineIndex);
5470 		lineText = content.getLine(lineIndex);
5471 		int lineLength = lineText.length();
5472 		if (offset >= lineOffset + lineLength) {
5473 			newOffset = content.getOffsetAtLine(lineIndex + 1);
5474 		} else {
5475 			TextLayout layout = renderer.getTextLayout(lineIndex);
5476 			newOffset = lineOffset + layout.getNextOffset(offset - lineOffset, movement);
5477 			renderer.disposeTextLayout(layout);
5478 		}
5479 	}
5480 	if (ignoreListener) return newOffset;
5481 	return sendWordBoundaryEvent(ST.WordNext, movement, offset, newOffset, lineText, lineOffset);
5482 }
getWordPrevious(int offset, int movement)5483 int getWordPrevious(int offset, int movement) {
5484 	return getWordPrevious(offset, movement, false);
5485 }
getWordPrevious(int offset, int movement, boolean ignoreListener)5486 int getWordPrevious(int offset, int movement, boolean ignoreListener) {
5487 	int newOffset, lineOffset;
5488 	String lineText;
5489 	if (offset <= 0) {
5490 		newOffset = 0;
5491 		int lineIndex = content.getLineAtOffset(newOffset);
5492 		lineOffset = content.getOffsetAtLine(lineIndex);
5493 		lineText = content.getLine(lineIndex);
5494 	} else {
5495 		int lineIndex = content.getLineAtOffset(offset);
5496 		lineOffset = content.getOffsetAtLine(lineIndex);
5497 		lineText = content.getLine(lineIndex);
5498 		if (offset == lineOffset) {
5499 			String nextLineText = content.getLine(lineIndex - 1);
5500 			int nextLineOffset = content.getOffsetAtLine(lineIndex - 1);
5501 			newOffset = nextLineOffset + nextLineText.length();
5502 		} else {
5503 			int layoutOffset = Math.min(offset - lineOffset, lineText.length());
5504 			TextLayout layout = renderer.getTextLayout(lineIndex);
5505 			newOffset = lineOffset + layout.getPreviousOffset(layoutOffset, movement);
5506 			renderer.disposeTextLayout(layout);
5507 		}
5508 	}
5509 	if (ignoreListener) return newOffset;
5510 	return sendWordBoundaryEvent(ST.WordPrevious, movement, offset, newOffset, lineText, lineOffset);
5511 }
5512 /**
5513  * Returns whether the widget wraps lines.
5514  *
5515  * @return true if widget wraps lines, false otherwise
5516  * @since 2.0
5517  */
getWordWrap()5518 public boolean getWordWrap() {
5519 	checkWidget();
5520 	return wordWrap;
5521 }
5522 /**
5523  * Returns the wrap indentation of the widget.
5524  *
5525  * @return the wrap indentation
5526  *
5527  * @exception SWTException <ul>
5528  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5529  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5530  * </ul>
5531  *
5532  * @see #getLineWrapIndent(int)
5533  *
5534  * @since 3.6
5535  */
getWrapIndent()5536 public int getWrapIndent() {
5537 	checkWidget();
5538 	return wrapIndent;
5539 }
5540 /**
5541  * Returns the location of the given offset.
5542  * <p>
5543  * <b>NOTE:</b> Does not return correct values for true italic fonts (vs. slanted fonts).
5544  * </p>
5545  *
5546  * @return location of the character at the given offset in the line.
5547  */
getPointAtOffset(int offset)5548 Point getPointAtOffset(int offset) {
5549 	int lineIndex = content.getLineAtOffset(offset);
5550 	String line = content.getLine(lineIndex);
5551 	int lineOffset = content.getOffsetAtLine(lineIndex);
5552 	int offsetInLine = Math.max (0, offset - lineOffset);
5553 	int lineLength = line.length();
5554 	if (lineIndex < content.getLineCount() - 1) {
5555 		int endLineOffset = content.getOffsetAtLine(lineIndex + 1) - 1;
5556 		if (lineLength < offsetInLine && offsetInLine <= endLineOffset) {
5557 			offsetInLine = lineLength;
5558 		}
5559 	}
5560 	Point point;
5561 	TextLayout layout = renderer.getTextLayout(lineIndex);
5562 	if (lineLength != 0  && offsetInLine <= lineLength) {
5563 		if (offsetInLine == lineLength) {
5564 			offsetInLine = layout.getPreviousOffset(offsetInLine, SWT.MOVEMENT_CLUSTER);
5565 			point = layout.getLocation(offsetInLine, true);
5566 		} else {
5567 			switch (caretAlignment) {
5568 				case OFFSET_LEADING:
5569 					point = layout.getLocation(offsetInLine, false);
5570 					break;
5571 				case PREVIOUS_OFFSET_TRAILING:
5572 				default:
5573 					boolean lineBegin = offsetInLine == 0;
5574 					// If word wrap is enabled, we should also consider offsets
5575 					// of wrapped line parts as line begin and do NOT go back.
5576 					// This prevents clients to jump one line higher than
5577 					// expected, see bug 488172.
5578 					// Respect caretAlignment at the caretOffset, unless there's
5579 					// a non-empty selection, see bug 488172 comment 6.
5580 					if (wordWrap && !lineBegin && (offset != caretOffset || selection.x != selection.y)) {
5581 						int[] offsets = layout.getLineOffsets();
5582 						for (int i : offsets) {
5583 							if (i == offsetInLine) {
5584 								lineBegin = true;
5585 								break;
5586 							}
5587 						}
5588 					}
5589 					if (lineBegin) {
5590 						point = layout.getLocation(offsetInLine, false);
5591 					} else {
5592 						offsetInLine = layout.getPreviousOffset(offsetInLine, SWT.MOVEMENT_CLUSTER);
5593 						point = layout.getLocation(offsetInLine, true);
5594 					}
5595 					break;
5596 			}
5597 		}
5598 	} else {
5599 		point = new Point(layout.getIndent(), layout.getVerticalIndent());
5600 	}
5601 	renderer.disposeTextLayout(layout);
5602 	point.x += leftMargin - horizontalScrollOffset;
5603 	point.y += getLinePixel(lineIndex);
5604 	return point;
5605 }
5606 /**
5607  * Inserts a string.  The old selection is replaced with the new text.
5608  *
5609  * @param string the string
5610  * @see #replaceTextRange(int,int,String)
5611  * @exception SWTException <ul>
5612  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5613  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5614  * </ul>
5615  * @exception IllegalArgumentException <ul>
5616  *    <li>ERROR_NULL_ARGUMENT when string is null</li>
5617  * </ul>
5618  */
insert(String string)5619 public void insert(String string) {
5620 	checkWidget();
5621 	if (string == null) {
5622 		SWT.error(SWT.ERROR_NULL_ARGUMENT);
5623 	}
5624 	if (blockSelection) {
5625 		insertBlockSelectionText(string, false);
5626 	} else {
5627 		Point sel = getSelectionRange();
5628 		replaceTextRange(sel.x, sel.y, string);
5629 	}
5630 }
insertBlockSelectionText(String text, boolean fillWithSpaces)5631 int insertBlockSelectionText(String text, boolean fillWithSpaces) {
5632 	int lineCount = 1;
5633 	for (int i = 0; i < text.length(); i++) {
5634 		char ch = text.charAt(i);
5635 		if (ch == '\n' || ch == '\r') {
5636 			lineCount++;
5637 			if (ch == '\r' && i + 1 < text.length() && text.charAt(i + 1) == '\n') {
5638 				i++;
5639 			}
5640 		}
5641 	}
5642 	String[] lines = new String[lineCount];
5643 	int start = 0;
5644 	lineCount = 0;
5645 	for (int i = 0; i < text.length(); i++) {
5646 		char ch = text.charAt(i);
5647 		if (ch == '\n' || ch == '\r') {
5648 			lines[lineCount++] = text.substring(start, i);
5649 			if (ch == '\r' && i + 1 < text.length() && text.charAt(i + 1) == '\n') {
5650 				i++;
5651 			}
5652 			start = i + 1;
5653 		}
5654 	}
5655 	lines[lineCount++] = text.substring(start);
5656 	if (fillWithSpaces) {
5657 		int maxLength = 0;
5658 		for (String line : lines) {
5659 			int length = line.length();
5660 			maxLength = Math.max(maxLength, length);
5661 		}
5662 		for (int i = 0; i < lines.length; i++) {
5663 			String line = lines[i];
5664 			int length = line.length();
5665 			if (length < maxLength) {
5666 				int numSpaces = maxLength - length;
5667 				StringBuilder buffer = new StringBuilder(length + numSpaces);
5668 				buffer.append(line);
5669 				for (int j = 0; j < numSpaces; j++) buffer.append(' ');
5670 				lines[i] = buffer.toString();
5671 			}
5672 		}
5673 	}
5674 	int firstLine, lastLine, left, right;
5675 	if (blockXLocation != -1) {
5676 		Rectangle rect = getBlockSelectionPosition();
5677 		firstLine = rect.y;
5678 		lastLine = rect.height;
5679 		left = rect.x;
5680 		right = rect.width;
5681 	} else {
5682 		firstLine = lastLine = getCaretLine();
5683 		left = right = getPointAtOffset(caretOffset).x;
5684 	}
5685 	start = caretOffset;
5686 	int caretLine = getCaretLine();
5687 	int index = 0, lineIndex = firstLine;
5688 	while (lineIndex <= lastLine) {
5689 		String string = index < lineCount ? lines[index++] : "";
5690 		int lineStart = sendTextEvent(left, right, lineIndex, string, fillWithSpaces);
5691 		if (lineIndex == caretLine) start = lineStart;
5692 		lineIndex++;
5693 	}
5694 	while (index < lineCount) {
5695 		int lineStart = sendTextEvent(left, left, lineIndex, lines[index++], fillWithSpaces);
5696 		if (lineIndex == caretLine) start = lineStart;
5697 		lineIndex++;
5698 	}
5699 	return start;
5700 }
5701 void insertBlockSelectionText(char key, int action) {
5702 	if (key == SWT.CR || key == SWT.LF) return;
5703 	Rectangle rect = getBlockSelectionPosition();
5704 	int firstLine = rect.y;
5705 	int lastLine = rect.height;
5706 	int left = rect.x;
5707 	int right = rect.width;
5708 	int[] trailing = new int[1];
5709 	int offset = 0, delta = 0;
5710 	String text = key != 0 ? new String(new char[] {key}) : "";
5711 	int length = text.length();
5712 	for (int lineIndex = firstLine; lineIndex <= lastLine; lineIndex++) {
5713 		String line = content.getLine(lineIndex);
5714 		int lineOffset = content.getOffsetAtLine(lineIndex);
5715 		int lineEndOffset = lineOffset + line.length();
5716 		int linePixel = getLinePixel(lineIndex);
5717 		int start = getOffsetAtPoint(left, linePixel, trailing, true);
5718 		boolean outOfLine = start == -1;
5719 		if (outOfLine) {
5720 			start = left < leftMargin ? lineOffset : lineEndOffset;
5721 		} else {
5722 			start += trailing[0];
5723 		}
5724 		int end = getOffsetAtPoint(right, linePixel, trailing, true);
5725 		if (end == -1) {
5726 			end = right < leftMargin ? lineOffset : lineEndOffset;
5727 		} else {
5728 			end += trailing[0];
5729 		}
5730 		if (start > end) {
5731 			int temp = start;
5732 			start = end;
5733 			end = temp;
5734 		}
5735 		if (start == end && !outOfLine) {
5736 			switch (action) {
5737 				case ST.DELETE_PREVIOUS:
5738 					if (start > lineOffset) start = getClusterPrevious(start, lineIndex);
5739 					break;
5740 				case ST.DELETE_NEXT:
5741 					if (end < lineEndOffset) end = getClusterNext(end, lineIndex);
5742 					break;
5743 			}
5744 		}
5745 		if (outOfLine) {
5746 			if (line.length() >= delta) {
5747 				delta = line.length();
5748 				offset = lineEndOffset + length;
5749 			}
5750 		} else {
5751 			offset = start + length;
5752 			delta = content.getCharCount();
5753 		}
5754 		Event event = new Event();
5755 		event.text = text;
5756 		event.start = start;
5757 		event.end = end;
5758 		sendKeyEvent(event);
5759 	}
5760 	int x = getPointAtOffset(offset).x;
5761 	int verticalScrollOffset = getVerticalScrollOffset();
5762 	setBlockSelectionLocation(x, blockYAnchor - verticalScrollOffset, x, blockYLocation - verticalScrollOffset, false);
5763 }
5764 /**
5765  * Creates content change listeners and set the default content model.
5766  */
5767 void installDefaultContent() {
5768 	textChangeListener = new TextChangeListener() {
5769 		@Override
5770 		public void textChanging(TextChangingEvent event) {
5771 			handleTextChanging(event);
5772 		}
5773 		@Override
5774 		public void textChanged(TextChangedEvent event) {
5775 			handleTextChanged(event);
5776 		}
5777 		@Override
5778 		public void textSet(TextChangedEvent event) {
5779 			handleTextSet(event);
5780 		}
5781 	};
5782 	content = new DefaultContent();
5783 	content.addTextChangeListener(textChangeListener);
5784 }
5785 /**
5786  * Adds event listeners
5787  */
5788 void installListeners() {
5789 	ScrollBar verticalBar = getVerticalBar();
5790 	ScrollBar horizontalBar = getHorizontalBar();
5791 
5792 	listener = event -> {
5793 		switch (event.type) {
5794 			case SWT.Dispose: handleDispose(event); break;
5795 			case SWT.KeyDown: handleKeyDown(event); break;
5796 			case SWT.KeyUp: handleKeyUp(event); break;
5797 			case SWT.MenuDetect: handleMenuDetect(event); break;
5798 			case SWT.MouseDown: handleMouseDown(event); break;
5799 			case SWT.MouseUp: handleMouseUp(event); break;
5800 			case SWT.MouseMove: handleMouseMove(event); break;
5801 			case SWT.Paint: handlePaint(event); break;
5802 			case SWT.Resize: handleResize(event); break;
5803 			case SWT.Traverse: handleTraverse(event); break;
5804 		}
5805 	};
5806 	addListener(SWT.Dispose, listener);
5807 	addListener(SWT.KeyDown, listener);
5808 	addListener(SWT.KeyUp, listener);
5809 	addListener(SWT.MenuDetect, listener);
5810 	addListener(SWT.MouseDown, listener);
5811 	addListener(SWT.MouseUp, listener);
5812 	addListener(SWT.MouseMove, listener);
5813 	addListener(SWT.Paint, listener);
5814 	addListener(SWT.Resize, listener);
5815 	addListener(SWT.Traverse, listener);
5816 	ime.addListener(SWT.ImeComposition, event -> {
5817 		if (!editable) {
5818 			event.doit = false;
5819 			event.start = 0;
5820 			event.end = 0;
5821 			event.text = "";
5822 			return;
5823 		}
5824 
5825 		switch (event.detail) {
5826 			case SWT.COMPOSITION_SELECTION: handleCompositionSelection(event); break;
5827 			case SWT.COMPOSITION_CHANGED: handleCompositionChanged(event); break;
5828 			case SWT.COMPOSITION_OFFSET: handleCompositionOffset(event); break;
5829 		}
5830 	});
5831 	if (verticalBar != null) {
5832 		verticalBar.addListener(SWT.Selection, this::handleVerticalScroll);
5833 	}
5834 	if (horizontalBar != null) {
5835 		horizontalBar.addListener(SWT.Selection, this::handleHorizontalScroll);
5836 	}
5837 }
5838 void internalRedrawRange(int start, int length) {
5839 	if (length <= 0) return;
5840 	int end = start + length;
5841 	int startLine = content.getLineAtOffset(start);
5842 	int endLine = content.getLineAtOffset(end);
5843 	int partialBottomIndex = getPartialBottomIndex();
5844 	int partialTopIndex = getPartialTopIndex();
5845 	if (startLine > partialBottomIndex || endLine < partialTopIndex) {
5846 		return;
5847 	}
5848 	if (partialTopIndex > startLine) {
5849 		startLine = partialTopIndex;
5850 		start = 0;
5851 	} else {
5852 		start -= content.getOffsetAtLine(startLine);
5853 	}
5854 	if (partialBottomIndex < endLine) {
5855 		endLine = partialBottomIndex + 1;
5856 		end = 0;
5857 	} else {
5858 		end -= content.getOffsetAtLine(endLine);
5859 	}
5860 
5861 	TextLayout layout = renderer.getTextLayout(startLine);
5862 	int lineX = leftMargin - horizontalScrollOffset, startLineY = getLinePixel(startLine);
5863 	int[] offsets = layout.getLineOffsets();
5864 	int startIndex = layout.getLineIndex(Math.min(start, layout.getText().length()));
5865 
5866 	/* Redraw end of line before start line if wrapped and start offset is first char */
5867 	if (isWordWrap() && startIndex > 0 && offsets[startIndex] == start) {
5868 		Rectangle rect = layout.getLineBounds(startIndex - 1);
5869 		rect.x = rect.width;
5870 		rect.width = clientAreaWidth - rightMargin - rect.x;
5871 		rect.x += lineX;
5872 		rect.y += startLineY;
5873 		super.redraw(rect.x, rect.y, rect.width, rect.height, false);
5874 	}
5875 
5876 	if (startLine == endLine) {
5877 		int endIndex = layout.getLineIndex(Math.min(end, layout.getText().length()));
5878 		if (startIndex == endIndex) {
5879 			/* Redraw rect between start and end offset if start and end offsets are in same wrapped line */
5880 			Rectangle rect = layout.getBounds(start, end - 1);
5881 			rect.x += lineX;
5882 			rect.y += startLineY;
5883 			super.redraw(rect.x, rect.y, rect.width, rect.height, false);
5884 			renderer.disposeTextLayout(layout);
5885 			return;
5886 		}
5887 	}
5888 
5889 	/* Redraw start line from the start offset to the end of client area */
5890 	Rectangle startRect = layout.getBounds(start, offsets[startIndex + 1] - 1);
5891 	if (startRect.height == 0) {
5892 		Rectangle bounds = layout.getLineBounds(startIndex);
5893 		startRect.x = bounds.width;
5894 		startRect.y = bounds.y;
5895 		startRect.height = bounds.height;
5896 	}
5897 	startRect.x += lineX;
5898 	startRect.y += startLineY;
5899 	startRect.width = clientAreaWidth - rightMargin - startRect.x;
5900 	super.redraw(startRect.x, startRect.y, startRect.width, startRect.height, false);
5901 
5902 	/* Redraw end line from the beginning of the line to the end offset */
5903 	if (startLine != endLine) {
5904 		renderer.disposeTextLayout(layout);
5905 		layout = renderer.getTextLayout(endLine);
5906 		offsets = layout.getLineOffsets();
5907 	}
5908 	int endIndex = layout.getLineIndex(Math.min(end, layout.getText().length()));
5909 	Rectangle endRect = layout.getBounds(offsets[endIndex], end - 1);
5910 	if (endRect.height == 0) {
5911 		Rectangle bounds = layout.getLineBounds(endIndex);
5912 		endRect.y = bounds.y;
5913 		endRect.height = bounds.height;
5914 	}
5915 	endRect.x += lineX;
5916 	endRect.y += getLinePixel(endLine);
5917 	super.redraw(endRect.x, endRect.y, endRect.width, endRect.height, false);
5918 	renderer.disposeTextLayout(layout);
5919 
5920 	/* Redraw all lines in between start and end line */
5921 	int y = startRect.y + startRect.height;
5922 	if (endRect.y > y) {
5923 		super.redraw(leftMargin, y, clientAreaWidth - rightMargin - leftMargin, endRect.y - y, false);
5924 	}
5925 }
5926 void handleCompositionOffset (Event event) {
5927 	int[] trailing = new int [1];
5928 	event.index = getOffsetAtPoint(event.x, event.y, trailing, true);
5929 	event.count = trailing[0];
5930 }
5931 void handleCompositionSelection (Event event) {
5932 	if (event.start != event.end) {
5933 		int charCount = getCharCount();
5934 		event.start = Math.max(0, Math.min(event.start, charCount));
5935 		event.end = Math.max(0, Math.min(event.end, charCount));
5936 		if (event.text != null) {
5937 			setSelection(event.start, event.end);
5938 		} else {
5939 			event.text = getTextRange(event.start, event.end - event.start);
5940 		}
5941 	} else {
5942 		event.start = selection.x;
5943 		event.end = selection.y;
5944 		event.text = getSelectionText();
5945 	}
5946 }
5947 void handleCompositionChanged(Event event) {
5948 	String text = event.text;
5949 	int start = event.start;
5950 	int end = event.end;
5951 	int charCount = content.getCharCount();
5952 	start = Math.min(start, charCount);
5953 	end = Math.min(end, charCount);
5954 	int length = text.length();
5955 	if (length == ime.getCommitCount()) {
5956 		content.replaceTextRange(start, end - start, "");
5957 		setCaretOffset(ime.getCompositionOffset(), SWT.DEFAULT);
5958 		caretWidth = 0;
5959 		caretDirection = SWT.NULL;
5960 	} else {
5961 		content.replaceTextRange(start, end - start, text);
5962 		int alignment = SWT.DEFAULT;
5963 		if (ime.getWideCaret()) {
5964 			start = ime.getCompositionOffset();
5965 			int lineIndex = getCaretLine();
5966 			int lineOffset = content.getOffsetAtLine(lineIndex);
5967 			TextLayout layout = renderer.getTextLayout(lineIndex);
5968 			caretWidth = layout.getBounds(start - lineOffset, start + length - 1 - lineOffset).width;
5969 			renderer.disposeTextLayout(layout);
5970 			alignment = OFFSET_LEADING;
5971 		}
5972 		setCaretOffset(ime.getCaretOffset(), alignment);
5973 	}
5974 	resetSelection();
5975 	showCaret();
5976 }
5977 /**
5978  * Frees resources.
5979  */
5980 void handleDispose(Event event) {
5981 	removeListener(SWT.Dispose, listener);
5982 	notifyListeners(SWT.Dispose, event);
5983 	event.type = SWT.None;
5984 
5985 	clipboard.dispose();
5986 	if (renderer != null) {
5987 		renderer.dispose();
5988 		renderer = null;
5989 	}
5990 	if (content != null) {
5991 		content.removeTextChangeListener(textChangeListener);
5992 		content = null;
5993 	}
5994 	if (defaultCaret != null) {
5995 		defaultCaret.dispose();
5996 		defaultCaret = null;
5997 	}
5998 	if (leftCaretBitmap != null) {
5999 		leftCaretBitmap.dispose();
6000 		leftCaretBitmap = null;
6001 	}
6002 	if (rightCaretBitmap != null) {
6003 		rightCaretBitmap.dispose();
6004 		rightCaretBitmap = null;
6005 	}
6006 	if (isBidiCaret()) {
6007 		BidiUtil.removeLanguageListener(this);
6008 	}
6009 	selectionBackground = null;
6010 	selectionForeground = null;
6011 	marginColor = null;
6012 	textChangeListener = null;
6013 	selection = null;
6014 	doubleClickSelection = null;
6015 	keyActionMap = null;
6016 	background = null;
6017 	foreground = null;
6018 	clipboard = null;
6019 	tabs = null;
6020 }
6021 /**
6022  * Scrolls the widget horizontally.
6023  */
6024 void handleHorizontalScroll(Event event) {
6025 	int scrollPixel = getHorizontalBar().getSelection() - horizontalScrollOffset;
6026 	scrollHorizontal(scrollPixel, false);
6027 }
6028 /**
6029  * If an action has been registered for the key stroke execute the action.
6030  * Otherwise, if a character has been entered treat it as new content.
6031  *
6032  * @param event keyboard event
6033  */
6034 void handleKey(Event event) {
6035 	int action;
6036 	caretAlignment = PREVIOUS_OFFSET_TRAILING;
6037 	if (event.keyCode != 0) {
6038 		// special key pressed (e.g., F1)
6039 		action = getKeyBinding(event.keyCode | event.stateMask);
6040 	} else {
6041 		// character key pressed
6042 		action = getKeyBinding(event.character | event.stateMask);
6043 		if (action == SWT.NULL) {
6044 			// see if we have a control character
6045 			if ((event.stateMask & SWT.CTRL) != 0 && event.character <= 31) {
6046 				// get the character from the CTRL+char sequence, the control
6047 				// key subtracts 64 from the value of the key that it modifies
6048 				int c = event.character + 64;
6049 				action = getKeyBinding(c | event.stateMask);
6050 			}
6051 		}
6052 	}
6053 	if (action == SWT.NULL) {
6054 		boolean ignore = false;
6055 
6056 		if (IS_MAC) {
6057 			// Ignore accelerator key combinations (we do not want to
6058 			// insert a character in the text in this instance).
6059 			ignore = (event.stateMask & (SWT.COMMAND | SWT.CTRL)) != 0;
6060 		} else {
6061 			// Ignore accelerator key combinations (we do not want to
6062 			// insert a character in the text in this instance). Don't
6063 			// ignore CTRL+ALT combinations since that is the Alt Gr
6064 			// key on some keyboards.  See bug 20953.
6065 			ignore = event.stateMask == SWT.ALT ||
6066 					event.stateMask == SWT.CTRL ||
6067 					event.stateMask == (SWT.ALT | SWT.SHIFT) ||
6068 					event.stateMask == (SWT.CTRL | SWT.SHIFT);
6069 		}
6070 		// -ignore anything below SPACE except for line delimiter keys and tab.
6071 		// -ignore DEL
6072 		if (!ignore && event.character > 31 && event.character != SWT.DEL ||
6073 			event.character == SWT.CR || event.character == SWT.LF ||
6074 			event.character == TAB) {
6075 			doContent(event.character);
6076 			update();
6077 		}
6078 	} else {
6079 		invokeAction(action);
6080 	}
6081 }
6082 /**
6083  * If a VerifyKey listener exists, verify that the key that was entered
6084  * should be processed.
6085  *
6086  * @param event keyboard event
6087  */
6088 void handleKeyDown(Event event) {
6089 	if (clipboardSelection == null) {
6090 		clipboardSelection = new Point(selection.x, selection.y);
6091 	}
6092 	newOrientation = SWT.NONE;
6093 	event.stateMask &= SWT.MODIFIER_MASK;
6094 
6095 	Event verifyEvent = new Event();
6096 	verifyEvent.character = event.character;
6097 	verifyEvent.keyCode = event.keyCode;
6098 	verifyEvent.keyLocation = event.keyLocation;
6099 	verifyEvent.stateMask = event.stateMask;
6100 	verifyEvent.doit = event.doit;
6101 	notifyListeners(ST.VerifyKey, verifyEvent);
6102 	if (verifyEvent.doit) {
6103 		if ((event.stateMask & SWT.MODIFIER_MASK) == SWT.CTRL && event.keyCode == SWT.SHIFT && isBidiCaret()) {
6104 			newOrientation = event.keyLocation == SWT.LEFT ? SWT.LEFT_TO_RIGHT : SWT.RIGHT_TO_LEFT;
6105 		}
6106 		handleKey(event);
6107 	}
6108 }
6109 /**
6110  * Update the Selection Clipboard.
6111  *
6112  * @param event keyboard event
6113  */
6114 void handleKeyUp(Event event) {
6115 	if (clipboardSelection != null) {
6116 		if (clipboardSelection.x != selection.x || clipboardSelection.y != selection.y) {
6117 			copySelection(DND.SELECTION_CLIPBOARD);
6118 		}
6119 	}
6120 	clipboardSelection = null;
6121 
6122 	if (newOrientation != SWT.NONE) {
6123 		if (newOrientation != getOrientation()) {
6124 			Event e = new Event();
6125 			e.doit = true;
6126 			notifyListeners(SWT.OrientationChange, e);
6127 			if (e.doit) {
6128 				setOrientation(newOrientation);
6129 			}
6130 		}
6131 		newOrientation = SWT.NONE;
6132 	}
6133 }
6134 /**
6135  * Update the event location for focus-based context menu triggers.
6136  *
6137  * @param event menu detect event
6138  */
6139 void handleMenuDetect(Event event) {
6140 	if (event.detail == SWT.MENU_KEYBOARD) {
6141 		Point point = getDisplay().map(this, null, getPointAtOffset(caretOffset));
6142 		event.x = point.x;
6143 		event.y = point.y + getLineHeight(caretOffset);
6144 	}
6145 }
6146 /**
6147  * Updates the caret location and selection if mouse button 1 has been
6148  * pressed.
6149  */
6150 void handleMouseDown(Event event) {
6151 	//force focus (object support)
6152 	forceFocus();
6153 
6154 	//drag detect
6155 	if (dragDetect && checkDragDetect(event)) return;
6156 
6157 	//paste clipboard selection
6158 	if (event.button == 2) {
6159 		// On GTK, if mouseNavigator is enabled we have to distinguish a short middle-click (to paste content) from
6160 		// a long middle-click (mouse navigation started)
6161 		if (IS_GTK && mouseNavigator != null) {
6162 			middleClickPressed = true;
6163 			getDisplay().timerExec(200, ()->{
6164 				boolean click = middleClickPressed;
6165 				middleClickPressed = false;
6166 				if (click && mouseNavigator !=null) {
6167 					mouseNavigator.onMouseDown(event);
6168 				} else {
6169 					pasteOnMiddleClick(event);
6170 				}
6171 			});
6172 			return;
6173 		} else {
6174 			pasteOnMiddleClick(event);
6175 		}
6176 	}
6177 
6178 	//set selection
6179 	if ((event.button != 1) || (IS_MAC && (event.stateMask & SWT.MOD4) != 0)) {
6180 		return;
6181 	}
6182 	clickCount = event.count;
6183 	if (clickCount == 1) {
6184 		boolean select = (event.stateMask & SWT.MOD2) != 0;
6185 		doMouseLocationChange(event.x, event.y, select);
6186 	} else {
6187 		if (doubleClickEnabled) {
6188 			boolean wordSelect = (clickCount & 1) == 0;
6189 			int offset = getOffsetAtPoint(event.x, event.y, null);
6190 			int lineIndex = content.getLineAtOffset(offset);
6191 			int lineOffset = content.getOffsetAtLine(lineIndex);
6192 			if (wordSelect) {
6193 				int min = blockSelection ? lineOffset : 0;
6194 				int max = blockSelection ? lineOffset + content.getLine(lineIndex).length() : content.getCharCount();
6195 				int start = Math.max(min, getWordPrevious(offset, SWT.MOVEMENT_WORD_START));
6196 				int end = Math.min(max, getWordNext(start, SWT.MOVEMENT_WORD_END));
6197 				setSelection(start, end - start, false, true);
6198 				sendSelectionEvent();
6199 			} else {
6200 				if (blockSelection) {
6201 					setBlockSelectionLocation(leftMargin, event.y, clientAreaWidth - rightMargin, event.y, true);
6202 				} else {
6203 					int lineEnd = content.getCharCount();
6204 					if (lineIndex + 1 < content.getLineCount()) {
6205 						lineEnd = content.getOffsetAtLine(lineIndex + 1);
6206 					}
6207 					setSelection(lineOffset, lineEnd - lineOffset, false, false);
6208 					sendSelectionEvent();
6209 				}
6210 			}
6211 			doubleClickSelection = new Point(selection.x, selection.y);
6212 			showCaret();
6213 		}
6214 	}
6215 }
6216 /**
6217  * Updates the caret location and selection if mouse button 1 is pressed
6218  * during the mouse move.
6219  */
6220 void handleMouseMove(Event event) {
6221 	if (clickCount > 0) {
6222 		update();
6223 		doAutoScroll(event);
6224 		doMouseLocationChange(event.x, event.y, true);
6225 	}
6226 	if (renderer.hasLinks) {
6227 		doMouseLinkCursor(event.x, event.y);
6228 	}
6229 }
6230 /**
6231  * Autoscrolling ends when the mouse button is released.
6232  */
6233 void handleMouseUp(Event event) {
6234 	middleClickPressed = false;
6235 	clickCount = 0;
6236 	endAutoScroll();
6237 	if (event.button == 1) {
6238 		copySelection(DND.SELECTION_CLIPBOARD);
6239 	}
6240 }
6241 /**
6242  * Renders the invalidated area specified in the paint event.
6243  *
6244  * @param event paint event
6245  */
6246 void handlePaint(Event event) {
6247 	if (event.width == 0 || event.height == 0) return;
6248 	if (clientAreaWidth == 0 || clientAreaHeight == 0) return;
6249 
6250 	int startLine = getLineIndex(event.y);
6251 	int y = getLinePixel(startLine);
6252 	int endY = event.y + event.height;
6253 	GC gc = event.gc;
6254 	Color background = getBackground();
6255 	Color foreground = getForeground();
6256 	if (endY > 0) {
6257 		int lineCount = isSingleLine() ? 1 : content.getLineCount();
6258 		int x = leftMargin - horizontalScrollOffset;
6259 		for (int i = startLine; y < endY && i < lineCount; i++) {
6260 			y += renderer.drawLine(i, x, y, gc, background, foreground);
6261 		}
6262 		if (y < endY) {
6263 			gc.setBackground(background);
6264 			drawBackground(gc, 0, y, clientAreaWidth, endY - y);
6265 		}
6266 	}
6267 	if (blockSelection && blockXLocation != -1) {
6268 		gc.setBackground(getSelectionBackground());
6269 		Rectangle rect = getBlockSelectionRectangle();
6270 		gc.drawRectangle(rect.x, rect.y, Math.max(1, rect.width - 1), Math.max(1, rect.height - 1));
6271 		gc.setAdvanced(true);
6272 		if (gc.getAdvanced()) {
6273 			gc.setAlpha(100);
6274 			gc.fillRectangle(rect);
6275 			gc.setAdvanced(false);
6276 		}
6277 	}
6278 
6279 	// fill the margin background
6280 	gc.setBackground(marginColor != null ? marginColor : background);
6281 	if (topMargin > 0) {
6282 		drawBackground(gc, 0, 0, clientAreaWidth, topMargin);
6283 	}
6284 	if (bottomMargin > 0) {
6285 		drawBackground(gc, 0, clientAreaHeight - bottomMargin, clientAreaWidth, bottomMargin);
6286 	}
6287 	if (leftMargin - alignmentMargin > 0) {
6288 		drawBackground(gc, 0, 0, leftMargin - alignmentMargin, clientAreaHeight);
6289 	}
6290 	if (rightMargin > 0) {
6291 		drawBackground(gc, clientAreaWidth - rightMargin, 0, rightMargin, clientAreaHeight);
6292 	}
6293 }
6294 /**
6295  * Recalculates the scroll bars. Rewraps all lines when in word
6296  * wrap mode.
6297  *
6298  * @param event resize event
6299  */
6300 void handleResize(Event event) {
6301 	int oldHeight = clientAreaHeight;
6302 	int oldWidth = clientAreaWidth;
6303 	Rectangle clientArea = getClientArea();
6304 	clientAreaHeight = clientArea.height;
6305 	clientAreaWidth = clientArea.width;
6306 	if (!alwaysShowScroll && ignoreResize != 0) return;
6307 
6308 	redrawMargins(oldHeight, oldWidth);
6309 	if (wordWrap) {
6310 		if (oldWidth != clientAreaWidth) {
6311 			renderer.reset(0, content.getLineCount());
6312 			verticalScrollOffset = -1;
6313 			renderer.calculateIdle();
6314 			super.redraw();
6315 		}
6316 		if (oldHeight != clientAreaHeight) {
6317 			if (oldHeight == 0) topIndexY = 0;
6318 			setScrollBars(true);
6319 		}
6320 		setCaretLocation();
6321 	} else  {
6322 		renderer.calculateClientArea();
6323 		setScrollBars(true);
6324 		claimRightFreeSpace();
6325 		// StyledText allows any value for horizontalScrollOffset when clientArea is zero
6326 		// in setHorizontalPixel() and setHorisontalOffset(). Fixes bug 168429.
6327 		if (clientAreaWidth != 0) {
6328 			ScrollBar horizontalBar = getHorizontalBar();
6329 			if (horizontalBar != null && horizontalBar.getVisible()) {
6330 				if (horizontalScrollOffset != horizontalBar.getSelection()) {
6331 					horizontalBar.setSelection(horizontalScrollOffset);
6332 					horizontalScrollOffset = horizontalBar.getSelection();
6333 				}
6334 			}
6335 		}
6336 	}
6337 	updateCaretVisibility();
6338 	claimBottomFreeSpace();
6339 	setAlignment();
6340 	//TODO FIX TOP INDEX DURING RESIZE
6341 //	if (oldHeight != clientAreaHeight || wordWrap) {
6342 //		calculateTopIndex(0);
6343 //	}
6344 }
6345 /**
6346  * Updates the caret position and selection and the scroll bars to reflect
6347  * the content change.
6348  */
6349 void handleTextChanged(TextChangedEvent event) {
6350 	int offset = ime.getCompositionOffset();
6351 	if (offset != -1 && lastTextChangeStart < offset) {
6352 		ime.setCompositionOffset(offset + lastTextChangeNewCharCount - lastTextChangeReplaceCharCount);
6353 	}
6354 	int firstLine = content.getLineAtOffset(lastTextChangeStart);
6355 	resetCache(firstLine, 0);
6356 	if (!isFixedLineHeight() && topIndex > firstLine) {
6357 		topIndex = firstLine;
6358 		if (topIndex < 0) {
6359 			// TODO: This logging is in place to determine why topIndex is getting set to negative values.
6360 			// It should be deleted once we fix the root cause of this issue. See bug 487254 for details.
6361 			System.err.println("StyledText: topIndex was " + topIndex
6362 					+ ", lastTextChangeStart = " + lastTextChangeStart
6363 					+ ", content.getClass() = " + content.getClass()
6364 					);
6365 			topIndex = 0;
6366 		}
6367 		topIndexY = 0;
6368 		super.redraw();
6369 	} else {
6370 		int lastLine = firstLine + lastTextChangeNewLineCount;
6371 		int firstLineTop = getLinePixel(firstLine);
6372 		int newLastLineBottom = getLinePixel(lastLine + 1);
6373 		if (lastLineBottom != newLastLineBottom) {
6374 			super.redraw();
6375 		} else {
6376 			super.redraw(0, firstLineTop, clientAreaWidth, newLastLineBottom - firstLineTop, false);
6377 			redrawLinesBullet(renderer.redrawLines);
6378 		}
6379 	}
6380 	renderer.redrawLines = null;
6381 	// update selection/caret location after styles have been changed.
6382 	// otherwise any text measuring could be incorrect
6383 	//
6384 	// also, this needs to be done after all scrolling. Otherwise,
6385 	// selection redraw would be flushed during scroll which is wrong.
6386 	// in some cases new text would be drawn in scroll source area even
6387 	// though the intent is to scroll it.
6388 	if (!(blockSelection && blockXLocation != -1)) {
6389 		updateSelection(lastTextChangeStart, lastTextChangeReplaceCharCount, lastTextChangeNewCharCount);
6390 	}
6391 	if (lastTextChangeReplaceLineCount > 0 || wordWrap || visualWrap) {
6392 		claimBottomFreeSpace();
6393 	}
6394 	if (lastTextChangeReplaceCharCount > 0) {
6395 		claimRightFreeSpace();
6396 	}
6397 
6398 	sendAccessibleTextChanged(lastTextChangeStart, lastTextChangeNewCharCount, 0);
6399 	lastCharCount += lastTextChangeNewCharCount;
6400 	lastCharCount -= lastTextChangeReplaceCharCount;
6401 	setAlignment();
6402 }
6403 /**
6404  * Updates the screen to reflect a pending content change.
6405  *
6406  * @param event .start the start offset of the change
6407  * @param event .newText text that is going to be inserted or empty String
6408  *	if no text will be inserted
6409  * @param event .replaceCharCount length of text that is going to be replaced
6410  * @param event .newCharCount length of text that is going to be inserted
6411  * @param event .replaceLineCount number of lines that are going to be replaced
6412  * @param event .newLineCount number of new lines that are going to be inserted
6413  */
6414 void handleTextChanging(TextChangingEvent event) {
6415 	if (event.replaceCharCount < 0) {
6416 		event.start += event.replaceCharCount;
6417 		event.replaceCharCount *= -1;
6418 	}
6419 	lastTextChangeStart = event.start;
6420 	lastTextChangeNewLineCount = event.newLineCount;
6421 	lastTextChangeNewCharCount = event.newCharCount;
6422 	lastTextChangeReplaceLineCount = event.replaceLineCount;
6423 	lastTextChangeReplaceCharCount = event.replaceCharCount;
6424 	int lineIndex = content.getLineAtOffset(event.start);
6425 	int srcY = getLinePixel(lineIndex + event.replaceLineCount + 1);
6426 	int destY = getLinePixel(lineIndex + 1) + event.newLineCount * renderer.getLineHeight();
6427 	lastLineBottom = destY;
6428 	if (srcY < 0 && destY < 0) {
6429 		lastLineBottom += srcY - destY;
6430 		verticalScrollOffset += destY - srcY;
6431 		calculateTopIndex(destY - srcY);
6432 		setScrollBars(true);
6433 	} else {
6434 		scrollText(srcY, destY);
6435 	}
6436 	sendAccessibleTextChanged(lastTextChangeStart, 0, lastTextChangeReplaceCharCount);
6437 	renderer.textChanging(event);
6438 
6439 	// Update the caret offset if it is greater than the length of the content.
6440 	// This is necessary since style range API may be called between the
6441 	// handleTextChanging and handleTextChanged events and this API sets the
6442 	// caretOffset.
6443 	int newEndOfText = content.getCharCount() - event.replaceCharCount + event.newCharCount;
6444 	if (caretOffset > newEndOfText) setCaretOffset(newEndOfText, SWT.DEFAULT);
6445 }
6446 /**
6447  * Called when the widget content is set programmatically, overwriting
6448  * the old content. Resets the caret position, selection and scroll offsets.
6449  * Recalculates the content width and scroll bars. Redraws the widget.
6450  *
6451  * @param event text change event.
6452  */
6453 void handleTextSet(TextChangedEvent event) {
6454 	reset();
6455 	int newCharCount = getCharCount();
6456 	sendAccessibleTextChanged(0, newCharCount, lastCharCount);
6457 	lastCharCount = newCharCount;
6458 	setAlignment();
6459 }
6460 /**
6461  * Called when a traversal key is pressed.
6462  * Allow tab next traversal to occur when the widget is in single
6463  * line mode or in multi line and non-editable mode .
6464  * When in editable multi line mode we want to prevent the tab
6465  * traversal and receive the tab key event instead.
6466  *
6467  * @param event the event
6468  */
6469 void handleTraverse(Event event) {
6470 	switch (event.detail) {
6471 		case SWT.TRAVERSE_ESCAPE:
6472 		case SWT.TRAVERSE_PAGE_NEXT:
6473 		case SWT.TRAVERSE_PAGE_PREVIOUS:
6474 			event.doit = true;
6475 			break;
6476 		case SWT.TRAVERSE_RETURN:
6477 		case SWT.TRAVERSE_TAB_NEXT:
6478 		case SWT.TRAVERSE_TAB_PREVIOUS:
6479 			if ((getStyle() & SWT.SINGLE) != 0) {
6480 				event.doit = true;
6481 			} else {
6482 				if (!editable || (event.stateMask & SWT.MODIFIER_MASK) != 0) {
6483 					event.doit = true;
6484 				}
6485 			}
6486 			break;
6487 	}
6488 }
6489 /**
6490  * Scrolls the widget vertically.
6491  */
6492 void handleVerticalScroll(Event event) {
6493 	int scrollPixel = getVerticalBar().getSelection() - getVerticalScrollOffset();
6494 	scrollVertical(scrollPixel, false);
6495 }
6496 /**
6497  * Add accessibility support for the widget.
6498  */
6499 void initializeAccessible() {
6500 	acc = getAccessible();
6501 
6502 	accAdapter = new AccessibleAdapter() {
6503 		@Override
6504 		public void getName (AccessibleEvent e) {
6505 			String name = null;
6506 			String text = getAssociatedLabel ();
6507 			if (text != null) {
6508 				name = stripMnemonic (text);
6509 			}
6510 			e.result = name;
6511 		}
6512 		@Override
6513 		public void getHelp(AccessibleEvent e) {
6514 			e.result = getToolTipText();
6515 		}
6516 		@Override
6517 		public void getKeyboardShortcut(AccessibleEvent e) {
6518 			String shortcut = null;
6519 			String text = getAssociatedLabel ();
6520 			if (text != null) {
6521 				char mnemonic = _findMnemonic (text);
6522 				if (mnemonic != '\0') {
6523 					shortcut = "Alt+"+mnemonic; //$NON-NLS-1$
6524 				}
6525 			}
6526 			e.result = shortcut;
6527 		}
6528 	};
6529 	acc.addAccessibleListener(accAdapter);
6530 
6531 	accTextExtendedAdapter = new AccessibleTextExtendedAdapter() {
6532 		@Override
6533 		public void getCaretOffset(AccessibleTextEvent e) {
6534 			e.offset = StyledText.this.getCaretOffset();
6535 		}
6536 		@Override
6537 		public void setCaretOffset(AccessibleTextEvent e) {
6538 			StyledText.this.setCaretOffset(e.offset);
6539 			e.result = ACC.OK;
6540 		}
6541 		@Override
6542 		public void getSelectionRange(AccessibleTextEvent e) {
6543 			Point selection = StyledText.this.getSelectionRange();
6544 			e.offset = selection.x;
6545 			e.length = selection.y;
6546 		}
6547 		@Override
6548 		public void addSelection(AccessibleTextEvent e) {
6549 			StyledText st = StyledText.this;
6550 			Point point = st.getSelection();
6551 			if (point.x == point.y) {
6552 				int end = e.end;
6553 				if (end == -1) end = st.getCharCount();
6554 				st.setSelection(e.start, end);
6555 				e.result = ACC.OK;
6556 			}
6557 		}
6558 		@Override
6559 		public void getSelection(AccessibleTextEvent e) {
6560 			StyledText st = StyledText.this;
6561 			if (st.blockSelection && st.blockXLocation != -1) {
6562 				Rectangle rect = st.getBlockSelectionPosition();
6563 				int lineIndex = rect.y + e.index;
6564 				int linePixel = st.getLinePixel(lineIndex);
6565 				e.ranges = getRanges(rect.x, linePixel, rect.width, linePixel);
6566 				if (e.ranges.length > 0) {
6567 					e.start = e.ranges[0];
6568 					e.end = e.ranges[e.ranges.length - 1];
6569 				}
6570 			} else {
6571 				if (e.index == 0) {
6572 					Point point = st.getSelection();
6573 					e.start = point.x;
6574 					e.end = point.y;
6575 					if (e.start > e.end) {
6576 						int temp = e.start;
6577 						e.start = e.end;
6578 						e.end = temp;
6579 					}
6580 				}
6581 			}
6582 		}
6583 		@Override
6584 		public void getSelectionCount(AccessibleTextEvent e) {
6585 			StyledText st = StyledText.this;
6586 			if (st.blockSelection && st.blockXLocation != -1) {
6587 				Rectangle rect = st.getBlockSelectionPosition();
6588 				e.count = rect.height - rect.y + 1;
6589 			} else {
6590 				Point point = st.getSelection();
6591 				e.count = point.x == point.y ? 0 : 1;
6592 			}
6593 		}
6594 		@Override
6595 		public void removeSelection(AccessibleTextEvent e) {
6596 			StyledText st = StyledText.this;
6597 			if (e.index == 0) {
6598 				if (st.blockSelection) {
6599 					st.clearBlockSelection(true, false);
6600 				} else {
6601 					st.clearSelection(false);
6602 				}
6603 				e.result = ACC.OK;
6604 			}
6605 		}
6606 		@Override
6607 		public void setSelection(AccessibleTextEvent e) {
6608 			if (e.index != 0) return;
6609 			StyledText st = StyledText.this;
6610 			Point point = st.getSelection();
6611 			if (point.x == point.y) return;
6612 			int end = e.end;
6613 			if (end == -1) end = st.getCharCount();
6614 			st.setSelection(e.start, end);
6615 			e.result = ACC.OK;
6616 		}
6617 		@Override
6618 		public void getCharacterCount(AccessibleTextEvent e) {
6619 			e.count = StyledText.this.getCharCount();
6620 		}
6621 		@Override
6622 		public void getOffsetAtPoint(AccessibleTextEvent e) {
6623 			StyledText st = StyledText.this;
6624 			Point point = new Point (e.x, e.y);
6625 			Display display = st.getDisplay();
6626 			point = display.map(null, st, point);
6627 			e.offset = st.getOffsetAtPoint(point.x, point.y, null, true);
6628 		}
6629 		@Override
6630 		public void getTextBounds(AccessibleTextEvent e) {
6631 			StyledText st = StyledText.this;
6632 			int start = e.start;
6633 			int end = e.end;
6634 			int contentLength = st.getCharCount();
6635 			start = Math.max(0, Math.min(start, contentLength));
6636 			end = Math.max(0, Math.min(end, contentLength));
6637 			if (start > end) {
6638 				int temp = start;
6639 				start = end;
6640 				end = temp;
6641 			}
6642 			int startLine = st.getLineAtOffset(start);
6643 			int endLine = st.getLineAtOffset(end);
6644 			Rectangle[] rects = new Rectangle[endLine - startLine + 1];
6645 			Rectangle bounds = null;
6646 			int index = 0;
6647 			Display display = st.getDisplay();
6648 			for (int lineIndex = startLine; lineIndex <= endLine; lineIndex++) {
6649 				Rectangle rect = new Rectangle(0, 0, 0, 0);
6650 				rect.y = st.getLinePixel(lineIndex);
6651 				rect.height = st.renderer.getLineHeight(lineIndex);
6652 				if (lineIndex == startLine) {
6653 					rect.x = st.getPointAtOffset(start).x;
6654 				} else {
6655 					rect.x = st.leftMargin - st.horizontalScrollOffset;
6656 				}
6657 				if (lineIndex == endLine) {
6658 					rect.width = st.getPointAtOffset(end).x - rect.x;
6659 				} else {
6660 					TextLayout layout = st.renderer.getTextLayout(lineIndex);
6661 					rect.width = layout.getBounds().width - rect.x;
6662 					st.renderer.disposeTextLayout(layout);
6663 				}
6664 				rects [index++] = rect = display.map(st, null, rect);
6665 				if (bounds == null) {
6666 					bounds = new Rectangle(rect.x, rect.y, rect.width, rect.height);
6667 				} else {
6668 					bounds.add(rect);
6669 				}
6670 			}
6671 			e.rectangles = rects;
6672 			if (bounds != null) {
6673 				e.x = bounds.x;
6674 				e.y = bounds.y;
6675 				e.width = bounds.width;
6676 				e.height = bounds.height;
6677 			}
6678 		}
6679 		int[] getRanges(int left, int top, int right, int bottom) {
6680 			StyledText st = StyledText.this;
6681 			int lineStart = st.getLineIndex(top);
6682 			int lineEnd = st.getLineIndex(bottom);
6683 			int count = lineEnd - lineStart + 1;
6684 			int[] ranges = new int [count * 2];
6685 			int index = 0;
6686 			for (int lineIndex = lineStart; lineIndex <= lineEnd; lineIndex++) {
6687 				String line = st.content.getLine(lineIndex);
6688 				int lineOffset = st.content.getOffsetAtLine(lineIndex);
6689 				int lineEndOffset = lineOffset + line.length();
6690 				int linePixel = st.getLinePixel(lineIndex);
6691 				int start = st.getOffsetAtPoint(left, linePixel, null, true);
6692 				if (start == -1) {
6693 					start = left < st.leftMargin ? lineOffset : lineEndOffset;
6694 				}
6695 				int[] trailing = new int[1];
6696 				int end = st.getOffsetAtPoint(right, linePixel, trailing, true);
6697 				if (end == -1) {
6698 					end = right < st.leftMargin ? lineOffset : lineEndOffset;
6699 				} else {
6700 					end += trailing[0];
6701 				}
6702 				if (start > end) {
6703 					int temp = start;
6704 					start = end;
6705 					end = temp;
6706 				}
6707 				ranges[index++] = start;
6708 				ranges[index++] = end;
6709 			}
6710 			return ranges;
6711 		}
6712 		@Override
6713 		public void getRanges(AccessibleTextEvent e) {
6714 			StyledText st = StyledText.this;
6715 			Point point = new Point (e.x, e.y);
6716 			Display display = st.getDisplay();
6717 			point = display.map(null, st, point);
6718 			e.ranges = getRanges(point.x, point.y, point.x + e.width, point.y + e.height);
6719 			if (e.ranges.length > 0) {
6720 				e.start = e.ranges[0];
6721 				e.end = e.ranges[e.ranges.length - 1];
6722 			}
6723 		}
6724 		@Override
6725 		public void getText(AccessibleTextEvent e) {
6726 			StyledText st = StyledText.this;
6727 			int start = e.start;
6728 			int end = e.end;
6729 			int contentLength = st.getCharCount();
6730 			if (end == -1) end = contentLength;
6731 			start = Math.max(0, Math.min(start, contentLength));
6732 			end = Math.max(0, Math.min(end, contentLength));
6733 			if (start > end) {
6734 				int temp = start;
6735 				start = end;
6736 				end = temp;
6737 			}
6738 			int count = e.count;
6739 			switch (e.type) {
6740 				case ACC.TEXT_BOUNDARY_ALL:
6741 					//nothing to do
6742 					break;
6743 				case ACC.TEXT_BOUNDARY_CHAR: {
6744 					int newCount = 0;
6745 					if (count > 0) {
6746 						while (count-- > 0) {
6747 							int newEnd = st.getWordNext(end, SWT.MOVEMENT_CLUSTER);
6748 							if (newEnd == contentLength) break;
6749 							if (newEnd == end) break;
6750 							end = newEnd;
6751 							newCount++;
6752 						}
6753 						start = end;
6754 						end = st.getWordNext(end, SWT.MOVEMENT_CLUSTER);
6755 					} else {
6756 						while (count++ < 0) {
6757 							int newStart = st.getWordPrevious(start, SWT.MOVEMENT_CLUSTER);
6758 							if (newStart == start) break;
6759 							start = newStart;
6760 							newCount--;
6761 						}
6762 						end = st.getWordNext(start, SWT.MOVEMENT_CLUSTER);
6763 					}
6764 					count = newCount;
6765 					break;
6766 				}
6767 				case ACC.TEXT_BOUNDARY_WORD: {
6768 					int newCount = 0;
6769 					if (count > 0) {
6770 						while (count-- > 0) {
6771 							int newEnd = st.getWordNext(end, SWT.MOVEMENT_WORD_START, true);
6772 							if (newEnd == end) break;
6773 							newCount++;
6774 							end = newEnd;
6775 						}
6776 						start = end;
6777 						end = st.getWordNext(start, SWT.MOVEMENT_WORD_END, true);
6778 					} else {
6779 						if (st.getWordPrevious(Math.min(start + 1, contentLength), SWT.MOVEMENT_WORD_START, true) == start) {
6780 							//start is a word start already
6781 							count++;
6782 						}
6783 						while (count <= 0) {
6784 							int newStart = st.getWordPrevious(start, SWT.MOVEMENT_WORD_START, true);
6785 							if (newStart == start) break;
6786 							count++;
6787 							start = newStart;
6788 							if (count != 0) newCount--;
6789 						}
6790 						if (count <= 0 && start == 0) {
6791 							end = start;
6792 						} else {
6793 							end = st.getWordNext(start, SWT.MOVEMENT_WORD_END, true);
6794 						}
6795 					}
6796 					count = newCount;
6797 					break;
6798 				}
6799 				case ACC.TEXT_BOUNDARY_LINE:
6800 					//TODO implement line
6801 				case ACC.TEXT_BOUNDARY_PARAGRAPH:
6802 				case ACC.TEXT_BOUNDARY_SENTENCE: {
6803 					int offset = count > 0 ? end : start;
6804 					int lineIndex = st.getLineAtOffset(offset) + count;
6805 					lineIndex = Math.max(0, Math.min(lineIndex, st.getLineCount() - 1));
6806 					start = st.getOffsetAtLine(lineIndex);
6807 					String line = st.getLine(lineIndex);
6808 					end = start + line.length();
6809 					count = lineIndex - st.getLineAtOffset(offset);
6810 					break;
6811 				}
6812 			}
6813 			e.start = start;
6814 			e.end = end;
6815 			e.count = count;
6816 			e.result = st.content.getTextRange(start, end - start);
6817 		}
6818 		@Override
6819 		public void getVisibleRanges(AccessibleTextEvent e) {
6820 			e.ranges = getRanges(leftMargin, topMargin, clientAreaWidth - rightMargin, clientAreaHeight - bottomMargin);
6821 			if (e.ranges.length > 0) {
6822 				e.start = e.ranges[0];
6823 				e.end = e.ranges[e.ranges.length - 1];
6824 			}
6825 		}
6826 		@Override
6827 		public void scrollText(AccessibleTextEvent e) {
6828 			StyledText st = StyledText.this;
6829 			int topPixel = getTopPixel(), horizontalPixel = st.getHorizontalPixel();
6830 			switch (e.type) {
6831 				case ACC.SCROLL_TYPE_ANYWHERE:
6832 				case ACC.SCROLL_TYPE_TOP_LEFT:
6833 				case ACC.SCROLL_TYPE_LEFT_EDGE:
6834 				case ACC.SCROLL_TYPE_TOP_EDGE: {
6835 					Rectangle rect = st.getBoundsAtOffset(e.start);
6836 					if (e.type != ACC.SCROLL_TYPE_TOP_EDGE) {
6837 						horizontalPixel = horizontalPixel + rect.x - st.leftMargin;
6838 					}
6839 					if (e.type != ACC.SCROLL_TYPE_LEFT_EDGE) {
6840 						topPixel = topPixel + rect.y - st.topMargin;
6841 					}
6842 					break;
6843 				}
6844 				case ACC.SCROLL_TYPE_BOTTOM_RIGHT:
6845 				case ACC.SCROLL_TYPE_BOTTOM_EDGE:
6846 				case ACC.SCROLL_TYPE_RIGHT_EDGE: {
6847 					Rectangle rect = st.getBoundsAtOffset(e.end - 1);
6848 					if (e.type != ACC.SCROLL_TYPE_BOTTOM_EDGE) {
6849 						horizontalPixel = horizontalPixel - st.clientAreaWidth + rect.x + rect.width + st.rightMargin;
6850 					}
6851 					if (e.type != ACC.SCROLL_TYPE_RIGHT_EDGE) {
6852 						topPixel = topPixel - st.clientAreaHeight + rect.y +rect.height + st.bottomMargin;
6853 					}
6854 					break;
6855 				}
6856 				case ACC.SCROLL_TYPE_POINT: {
6857 					Point point = new Point(e.x, e.y);
6858 					Display display = st.getDisplay();
6859 					point = display.map(null, st, point);
6860 					Rectangle rect = st.getBoundsAtOffset(e.start);
6861 					topPixel = topPixel - point.y + rect.y;
6862 					horizontalPixel = horizontalPixel - point.x + rect.x;
6863 					break;
6864 				}
6865 			}
6866 			st.setTopPixel(topPixel);
6867 			st.setHorizontalPixel(horizontalPixel);
6868 			e.result = ACC.OK;
6869 		}
6870 	};
6871 	acc.addAccessibleTextListener(accTextExtendedAdapter);
6872 
6873 	accEditableTextListener = new AccessibleEditableTextListener() {
6874 		@Override
6875 		public void setTextAttributes(AccessibleTextAttributeEvent e) {
6876 			// This method must be implemented by the application
6877 			e.result = ACC.OK;
6878 		}
6879 		@Override
6880 		public void replaceText(AccessibleEditableTextEvent e) {
6881 			StyledText st = StyledText.this;
6882 			st.replaceTextRange(e.start, e.end - e.start, e.string);
6883 			e.result = ACC.OK;
6884 		}
6885 		@Override
6886 		public void pasteText(AccessibleEditableTextEvent e) {
6887 			StyledText st = StyledText.this;
6888 			st.setSelection(e.start);
6889 			st.paste();
6890 			e.result = ACC.OK;
6891 		}
6892 		@Override
6893 		public void cutText(AccessibleEditableTextEvent e) {
6894 			StyledText st = StyledText.this;
6895 			st.setSelection(e.start, e.end);
6896 			st.cut();
6897 			e.result = ACC.OK;
6898 		}
6899 		@Override
6900 		public void copyText(AccessibleEditableTextEvent e) {
6901 			StyledText st = StyledText.this;
6902 			st.setSelection(e.start, e.end);
6903 			st.copy();
6904 			e.result = ACC.OK;
6905 		}
6906 	};
6907 	acc.addAccessibleEditableTextListener(accEditableTextListener);
6908 
6909 	accAttributeAdapter = new AccessibleAttributeAdapter() {
6910 		@Override
6911 		public void getAttributes(AccessibleAttributeEvent e) {
6912 			StyledText st = StyledText.this;
6913 			e.leftMargin = st.getLeftMargin();
6914 			e.topMargin = st.getTopMargin();
6915 			e.rightMargin = st.getRightMargin();
6916 			e.bottomMargin = st.getBottomMargin();
6917 			e.tabStops = st.getTabStops();
6918 			e.justify = st.getJustify();
6919 			e.alignment = st.getAlignment();
6920 			e.indent = st.getIndent();
6921 		}
6922 		@Override
6923 		public void getTextAttributes(AccessibleTextAttributeEvent e) {
6924 			StyledText st = StyledText.this;
6925 			int contentLength = st.getCharCount();
6926 			if (!isListening(ST.LineGetStyle) && st.renderer.styleCount == 0) {
6927 				e.start = 0;
6928 				e.end = contentLength;
6929 				e.textStyle = new TextStyle(st.getFont(), st.foreground, st.background);
6930 				return;
6931 			}
6932 			int offset = Math.max(0, Math.min(e.offset, contentLength - 1));
6933 			int lineIndex = st.getLineAtOffset(offset);
6934 			int lineOffset = st.getOffsetAtLine(lineIndex);
6935 			int lineCount = st.getLineCount();
6936 			offset = offset - lineOffset;
6937 
6938 			TextLayout layout = st.renderer.getTextLayout(lineIndex);
6939 			int lineLength = layout.getText().length();
6940 			if (lineLength > 0) {
6941 				e.textStyle = layout.getStyle(Math.max(0, Math.min(offset, lineLength - 1)));
6942 			}
6943 
6944 			// If no override info available, use defaults. Don't supply default colors, though.
6945 			if (e.textStyle == null) {
6946 				e.textStyle = new TextStyle(st.getFont(), st.foreground, st.background);
6947 			} else {
6948 				if (e.textStyle.foreground == null || e.textStyle.background == null || e.textStyle.font == null) {
6949 					TextStyle textStyle = new TextStyle(e.textStyle);
6950 					if (textStyle.foreground == null) textStyle.foreground = st.foreground;
6951 					if (textStyle.background == null) textStyle.background = st.background;
6952 					if (textStyle.font == null) textStyle.font = st.getFont();
6953 					e.textStyle = textStyle;
6954 				}
6955 			}
6956 
6957 			//offset at line delimiter case
6958 			if (offset >= lineLength) {
6959 				e.start = lineOffset + lineLength;
6960 				if (lineIndex + 1 < lineCount) {
6961 					e.end = st.getOffsetAtLine(lineIndex + 1);
6962 				} else  {
6963 					e.end = contentLength;
6964 				}
6965 				return;
6966 			}
6967 
6968 			int[] ranges = layout.getRanges();
6969 			st.renderer.disposeTextLayout(layout);
6970 			int index = 0;
6971 			int end = 0;
6972 			while (index < ranges.length) {
6973 				int styleStart = ranges[index++];
6974 				int styleEnd = ranges[index++];
6975 				if (styleStart <= offset && offset <= styleEnd) {
6976 					e.start = lineOffset + styleStart;
6977 					e.end = lineOffset + styleEnd + 1;
6978 					return;
6979 				}
6980 				if (styleStart > offset) {
6981 					e.start = lineOffset + end;
6982 					e.end = lineOffset + styleStart;
6983 					return;
6984 				}
6985 				end = styleEnd + 1;
6986 			}
6987 			if (index == ranges.length) {
6988 				e.start = lineOffset + end;
6989 				if (lineIndex + 1 < lineCount) {
6990 					e.end = st.getOffsetAtLine(lineIndex + 1);
6991 				} else  {
6992 					e.end = contentLength;
6993 				}
6994 			}
6995 		}
6996 	};
6997 	acc.addAccessibleAttributeListener(accAttributeAdapter);
6998 
6999 	accControlAdapter = new AccessibleControlAdapter() {
7000 		@Override
7001 		public void getRole(AccessibleControlEvent e) {
7002 			e.detail = ACC.ROLE_TEXT;
7003 		}
7004 		@Override
7005 		public void getState(AccessibleControlEvent e) {
7006 			int state = 0;
7007 			if (isEnabled()) state |= ACC.STATE_FOCUSABLE;
7008 			if (isFocusControl()) state |= ACC.STATE_FOCUSED;
7009 			if (!isVisible()) state |= ACC.STATE_INVISIBLE;
7010 			if (!getEditable()) state |= ACC.STATE_READONLY;
7011 			if (isSingleLine()) state |= ACC.STATE_SINGLELINE;
7012 			else state |= ACC.STATE_MULTILINE;
7013 			e.detail = state;
7014 		}
7015 		@Override
7016 		public void getValue(AccessibleControlEvent e) {
7017 			e.result = StyledText.this.getText();
7018 		}
7019 	};
7020 	acc.addAccessibleControlListener(accControlAdapter);
7021 
7022 	addListener(SWT.FocusIn, event -> acc.setFocus(ACC.CHILDID_SELF));
7023 }
7024 
7025 @Override
7026 public void dispose() {
7027 	/*
7028 	 * Note: It is valid to attempt to dispose a widget more than once.
7029 	 * Added check for this.
7030 	 */
7031 	if (!isDisposed()) {
7032 		acc.removeAccessibleControlListener(accControlAdapter);
7033 		acc.removeAccessibleAttributeListener(accAttributeAdapter);
7034 		acc.removeAccessibleEditableTextListener(accEditableTextListener);
7035 		acc.removeAccessibleTextListener(accTextExtendedAdapter);
7036 		acc.removeAccessibleListener(accAdapter);
7037 	}
7038 	super.dispose();
7039 }
7040 
7041 /*
7042  * Return the Label immediately preceding the receiver in the z-order,
7043  * or null if none.
7044  */
7045 String getAssociatedLabel () {
7046 	Control[] siblings = getParent ().getChildren ();
7047 	for (int i = 0; i < siblings.length; i++) {
7048 		if (siblings [i] == StyledText.this) {
7049 			if (i > 0) {
7050 				Control sibling = siblings [i-1];
7051 				if (sibling instanceof Label) return ((Label) sibling).getText();
7052 				if (sibling instanceof CLabel) return ((CLabel) sibling).getText();
7053 			}
7054 			break;
7055 		}
7056 	}
7057 	return null;
7058 }
7059 String stripMnemonic (String string) {
7060 	int index = 0;
7061 	int length = string.length ();
7062 	do {
7063 		while ((index < length) && (string.charAt (index) != '&')) index++;
7064 		if (++index >= length) return string;
7065 		if (string.charAt (index) != '&') {
7066 			return string.substring(0, index-1) + string.substring(index, length);
7067 		}
7068 		index++;
7069 	} while (index < length);
7070 	return string;
7071 }
7072 /*
7073  * Return the lowercase of the first non-'&' character following
7074  * an '&' character in the given string. If there are no '&'
7075  * characters in the given string, return '\0'.
7076  */
7077 char _findMnemonic (String string) {
7078 	if (string == null) return '\0';
7079 	int index = 0;
7080 	int length = string.length ();
7081 	do {
7082 		while (index < length && string.charAt (index) != '&') index++;
7083 		if (++index >= length) return '\0';
7084 		if (string.charAt (index) != '&') return Character.toLowerCase (string.charAt (index));
7085 		index++;
7086 	} while (index < length);
7087 	return '\0';
7088 }
7089 /**
7090  * Executes the action.
7091  *
7092  * @param action one of the actions defined in ST.java
7093  */
7094 public void invokeAction(int action) {
7095 	checkWidget();
7096 	if (blockSelection && invokeBlockAction(action)) return;
7097 	updateCaretDirection = true;
7098 	switch (action) {
7099 		// Navigation
7100 		case ST.LINE_UP:
7101 			doLineUp(false);
7102 			clearSelection(true);
7103 			break;
7104 		case ST.LINE_DOWN:
7105 			doLineDown(false);
7106 			clearSelection(true);
7107 			break;
7108 		case ST.LINE_START:
7109 			doLineStart();
7110 			clearSelection(true);
7111 			break;
7112 		case ST.LINE_END:
7113 			doLineEnd();
7114 			clearSelection(true);
7115 			break;
7116 		case ST.COLUMN_PREVIOUS:
7117 			doCursorPrevious();
7118 			clearSelection(true);
7119 			break;
7120 		case ST.COLUMN_NEXT:
7121 			doCursorNext();
7122 			clearSelection(true);
7123 			break;
7124 		case ST.PAGE_UP:
7125 			doPageUp(false, -1);
7126 			clearSelection(true);
7127 			break;
7128 		case ST.PAGE_DOWN:
7129 			doPageDown(false, -1);
7130 			clearSelection(true);
7131 			break;
7132 		case ST.WORD_PREVIOUS:
7133 			doWordPrevious();
7134 			clearSelection(true);
7135 			break;
7136 		case ST.WORD_NEXT:
7137 			doWordNext();
7138 			clearSelection(true);
7139 			break;
7140 		case ST.TEXT_START:
7141 			doContentStart();
7142 			clearSelection(true);
7143 			break;
7144 		case ST.TEXT_END:
7145 			doContentEnd();
7146 			clearSelection(true);
7147 			break;
7148 		case ST.WINDOW_START:
7149 			doPageStart();
7150 			clearSelection(true);
7151 			break;
7152 		case ST.WINDOW_END:
7153 			doPageEnd();
7154 			clearSelection(true);
7155 			break;
7156 		// Selection
7157 		case ST.SELECT_LINE_UP:
7158 			doSelectionLineUp();
7159 			break;
7160 		case ST.SELECT_ALL:
7161 			selectAll();
7162 			break;
7163 		case ST.SELECT_LINE_DOWN:
7164 			doSelectionLineDown();
7165 			break;
7166 		case ST.SELECT_LINE_START:
7167 			doLineStart();
7168 			doSelection(ST.COLUMN_PREVIOUS);
7169 			break;
7170 		case ST.SELECT_LINE_END:
7171 			doLineEnd();
7172 			doSelection(ST.COLUMN_NEXT);
7173 			break;
7174 		case ST.SELECT_COLUMN_PREVIOUS:
7175 			doSelectionCursorPrevious();
7176 			doSelection(ST.COLUMN_PREVIOUS);
7177 			break;
7178 		case ST.SELECT_COLUMN_NEXT:
7179 			doSelectionCursorNext();
7180 			doSelection(ST.COLUMN_NEXT);
7181 			break;
7182 		case ST.SELECT_PAGE_UP:
7183 			doSelectionPageUp(-1);
7184 			break;
7185 		case ST.SELECT_PAGE_DOWN:
7186 			doSelectionPageDown(-1);
7187 			break;
7188 		case ST.SELECT_WORD_PREVIOUS:
7189 			doSelectionWordPrevious();
7190 			doSelection(ST.COLUMN_PREVIOUS);
7191 			break;
7192 		case ST.SELECT_WORD_NEXT:
7193 			doSelectionWordNext();
7194 			doSelection(ST.COLUMN_NEXT);
7195 			break;
7196 		case ST.SELECT_TEXT_START:
7197 			doContentStart();
7198 			doSelection(ST.COLUMN_PREVIOUS);
7199 			break;
7200 		case ST.SELECT_TEXT_END:
7201 			doContentEnd();
7202 			doSelection(ST.COLUMN_NEXT);
7203 			break;
7204 		case ST.SELECT_WINDOW_START:
7205 			doPageStart();
7206 			doSelection(ST.COLUMN_PREVIOUS);
7207 			break;
7208 		case ST.SELECT_WINDOW_END:
7209 			doPageEnd();
7210 			doSelection(ST.COLUMN_NEXT);
7211 			break;
7212 		// Modification
7213 		case ST.CUT:
7214 			cut();
7215 			break;
7216 		case ST.COPY:
7217 			copy();
7218 			break;
7219 		case ST.PASTE:
7220 			paste();
7221 			break;
7222 		case ST.DELETE_PREVIOUS:
7223 			doBackspace();
7224 			break;
7225 		case ST.DELETE_NEXT:
7226 			doDelete();
7227 			break;
7228 		case ST.DELETE_WORD_PREVIOUS:
7229 			doDeleteWordPrevious();
7230 			break;
7231 		case ST.DELETE_WORD_NEXT:
7232 			doDeleteWordNext();
7233 			break;
7234 		// Miscellaneous
7235 		case ST.TOGGLE_OVERWRITE:
7236 			overwrite = !overwrite;		// toggle insert/overwrite mode
7237 			break;
7238 		case ST.TOGGLE_BLOCKSELECTION:
7239 			setBlockSelection(!blockSelection);
7240 			break;
7241 	}
7242 }
7243 /**
7244 * Returns true if an action should not be performed when block
7245 * selection in active
7246 */
7247 boolean invokeBlockAction(int action) {
7248 	switch (action) {
7249 		// Navigation
7250 		case ST.LINE_UP:
7251 		case ST.LINE_DOWN:
7252 		case ST.LINE_START:
7253 		case ST.LINE_END:
7254 		case ST.COLUMN_PREVIOUS:
7255 		case ST.COLUMN_NEXT:
7256 		case ST.PAGE_UP:
7257 		case ST.PAGE_DOWN:
7258 		case ST.WORD_PREVIOUS:
7259 		case ST.WORD_NEXT:
7260 		case ST.TEXT_START:
7261 		case ST.TEXT_END:
7262 		case ST.WINDOW_START:
7263 		case ST.WINDOW_END:
7264 			clearBlockSelection(false, false);
7265 			return false;
7266 		// Selection
7267 		case ST.SELECT_LINE_UP:
7268 			doBlockLineVertical(true);
7269 			return true;
7270 		case ST.SELECT_LINE_DOWN:
7271 			doBlockLineVertical(false);
7272 			return true;
7273 		case ST.SELECT_LINE_START:
7274 			doBlockLineHorizontal(false);
7275 			return true;
7276 		case ST.SELECT_LINE_END:
7277 			doBlockLineHorizontal(true);
7278 			return false;
7279 		case ST.SELECT_COLUMN_PREVIOUS:
7280 			doBlockColumn(false);
7281 			return true;
7282 		case ST.SELECT_COLUMN_NEXT:
7283 			doBlockColumn(true);
7284 			return true;
7285 		case ST.SELECT_WORD_PREVIOUS:
7286 			doBlockWord(false);
7287 			return true;
7288 		case ST.SELECT_WORD_NEXT:
7289 			doBlockWord(true);
7290 			return true;
7291 		case ST.SELECT_ALL:
7292 			return false;
7293 		case ST.SELECT_TEXT_START:
7294 			doBlockContentStartEnd(false);
7295 			break;
7296 		case ST.SELECT_TEXT_END:
7297 			doBlockContentStartEnd(true);
7298 			break;
7299 		case ST.SELECT_PAGE_UP:
7300 		case ST.SELECT_PAGE_DOWN:
7301 		case ST.SELECT_WINDOW_START:
7302 		case ST.SELECT_WINDOW_END:
7303 			//blocked actions
7304 			return true;
7305 		// Modification
7306 		case ST.CUT:
7307 		case ST.COPY:
7308 		case ST.PASTE:
7309 			return false;
7310 		case ST.DELETE_PREVIOUS:
7311 		case ST.DELETE_NEXT:
7312 			if (blockXLocation != -1) {
7313 				insertBlockSelectionText((char)0, action);
7314 				return true;
7315 			}
7316 			return false;
7317 		case ST.DELETE_WORD_PREVIOUS:
7318 		case ST.DELETE_WORD_NEXT:
7319 			//blocked actions
7320 			return blockXLocation != -1;
7321 	}
7322 	return false;
7323 }
7324 boolean isBidiCaret() {
7325 	return BidiUtil.isBidiPlatform();
7326 }
7327 boolean isFixedLineHeight() {
7328 	return !isWordWrap() && lineSpacing == 0 && renderer.lineSpacingProvider == null && !hasStyleWithVariableHeight && !hasVerticalIndent;
7329 }
7330 /**
7331  * Returns whether the given offset is inside a multi byte line delimiter.
7332  * Example:
7333  * "Line1\r\n" isLineDelimiter(5) == false but isLineDelimiter(6) == true
7334  *
7335  * @return true if the given offset is inside a multi byte line delimiter.
7336  * false if the given offset is before or after a line delimiter.
7337  */
7338 boolean isLineDelimiter(int offset) {
7339 	int line = content.getLineAtOffset(offset);
7340 	int lineOffset = content.getOffsetAtLine(line);
7341 	int offsetInLine = offset - lineOffset;
7342 	// offsetInLine will be greater than line length if the line
7343 	// delimiter is longer than one character and the offset is set
7344 	// in between parts of the line delimiter.
7345 	return offsetInLine > content.getLine(line).length();
7346 }
7347 /**
7348  * Returns whether the widget is mirrored (right oriented/right to left
7349  * writing order).
7350  *
7351  * @return isMirrored true=the widget is right oriented, false=the widget
7352  * 	is left oriented
7353  */
7354 boolean isMirrored() {
7355 	return (getStyle() & SWT.MIRRORED) != 0;
7356 }
7357 /**
7358  * Returns <code>true</code> if any text in the widget is selected,
7359  * and <code>false</code> otherwise.
7360  *
7361  * @return the text selection state
7362  * @exception SWTException <ul>
7363  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7364  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7365  * </ul>
7366  *
7367  * @since 3.103
7368  */
7369 public boolean isTextSelected() {
7370 	checkWidget();
7371 	if (blockSelection && blockXLocation != -1) {
7372 		Rectangle rect = getBlockSelectionPosition();
7373 		return !rect.isEmpty();
7374 	}
7375 	return selection.y != selection.x;
7376 }
7377 /**
7378  * Returns whether the widget can have only one line.
7379  *
7380  * @return true if widget can have only one line, false if widget can have
7381  * 	multiple lines
7382  */
7383 boolean isSingleLine() {
7384 	return (getStyle() & SWT.SINGLE) != 0;
7385 }
7386 
7387 /**
7388  * Sends the specified verify event, replace/insert text as defined by
7389  * the event and send a modify event.
7390  *
7391  * @param event	the text change event.
7392  *	<ul>
7393  *	<li>event.start - the replace start offset</li>
7394  * 	<li>event.end - the replace end offset</li>
7395  * 	<li>event.text - the new text</li>
7396  *	</ul>
7397  * @param updateCaret whether or not he caret should be set behind
7398  *	the new text
7399  */
7400 void modifyContent(Event event, boolean updateCaret) {
7401 	event.doit = true;
7402 	notifyListeners(SWT.Verify, event);
7403 	if (event.doit) {
7404 		StyledTextEvent styledTextEvent = null;
7405 		int replacedLength = event.end - event.start;
7406 		if (isListening(ST.ExtendedModify)) {
7407 			styledTextEvent = new StyledTextEvent(content);
7408 			styledTextEvent.start = event.start;
7409 			styledTextEvent.end = event.start + event.text.length();
7410 			styledTextEvent.text = content.getTextRange(event.start, replacedLength);
7411 		}
7412 		if (updateCaret) {
7413 			//Fix advancing flag for delete/backspace key on direction boundary
7414 			if (event.text.length() == 0) {
7415 				int lineIndex = content.getLineAtOffset(event.start);
7416 				int lineOffset = content.getOffsetAtLine(lineIndex);
7417 				TextLayout layout = renderer.getTextLayout(lineIndex);
7418 				int levelStart = layout.getLevel(event.start - lineOffset);
7419 				int lineIndexEnd = content.getLineAtOffset(event.end);
7420 				if (lineIndex != lineIndexEnd) {
7421 					renderer.disposeTextLayout(layout);
7422 					lineOffset = content.getOffsetAtLine(lineIndexEnd);
7423 					layout = renderer.getTextLayout(lineIndexEnd);
7424 				}
7425 				int levelEnd = layout.getLevel(event.end - lineOffset);
7426 				renderer.disposeTextLayout(layout);
7427 				if (levelStart != levelEnd) {
7428 					caretAlignment = PREVIOUS_OFFSET_TRAILING;
7429 				} else {
7430 					caretAlignment = OFFSET_LEADING;
7431 				}
7432 			}
7433 		}
7434 		content.replaceTextRange(event.start, replacedLength, event.text);
7435 		// set the caret position prior to sending the modify event.
7436 		// fixes 1GBB8NJ
7437 		if (updateCaret && !(blockSelection && blockXLocation != -1)) {
7438 			// always update the caret location. fixes 1G8FODP
7439 			setSelection(event.start + event.text.length(), 0, true, false);
7440 			showCaret();
7441 		}
7442 		notifyListeners(SWT.Modify, event);
7443 		if (isListening(ST.ExtendedModify)) {
7444 			notifyListeners(ST.ExtendedModify, styledTextEvent);
7445 		}
7446 	}
7447 }
7448 void paintObject(GC gc, int x, int y, int ascent, int descent, StyleRange style, Bullet bullet, int bulletIndex) {
7449 	if (isListening(ST.PaintObject)) {
7450 		StyledTextEvent event = new StyledTextEvent (content) ;
7451 		event.gc = gc;
7452 		event.x = x;
7453 		event.y = y;
7454 		event.ascent = ascent;
7455 		event.descent = descent;
7456 		event.style = style;
7457 		event.bullet = bullet;
7458 		event.bulletIndex = bulletIndex;
7459 		notifyListeners(ST.PaintObject, event);
7460 	}
7461 }
7462 /**
7463  * Replaces the selection with the text on the <code>DND.CLIPBOARD</code>
7464  * clipboard  or, if there is no selection,  inserts the text at the current
7465  * caret offset.   If the widget has the SWT.SINGLE style and the
7466  * clipboard text contains more than one line, only the first line without
7467  * line delimiters is  inserted in the widget.
7468  *
7469  * @exception SWTException <ul>
7470  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7471  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7472  * </ul>
7473  */
7474 public void paste(){
7475 	checkWidget();
7476 	String text = (String) getClipboardContent(DND.CLIPBOARD);
7477 	if (text != null && text.length() > 0) {
7478 		if (blockSelection) {
7479 			boolean fillWithSpaces = isFixedLineHeight() && renderer.fixedPitch;
7480 			int offset = insertBlockSelectionText(text, fillWithSpaces);
7481 			setCaretOffset(offset, SWT.DEFAULT);
7482 			clearBlockSelection(true, true);
7483 			setCaretLocation();
7484 			return;
7485 		}
7486 		Event event = new Event();
7487 		event.start = selection.x;
7488 		event.end = selection.y;
7489 		String delimitedText = getModelDelimitedText(text);
7490 		if (textLimit > 0) {
7491 			int uneditedTextLength = getCharCount() - (selection.y - selection.x);
7492 			if ((uneditedTextLength + delimitedText.length()) > textLimit) {
7493 				int endIndex = textLimit - uneditedTextLength;
7494 				delimitedText = delimitedText.substring(0, Math.max(endIndex, 0));
7495 			}
7496 		}
7497 		event.text = delimitedText;
7498 		sendKeyEvent(event);
7499 	}
7500 }
7501 private void pasteOnMiddleClick(Event event) {
7502 	String text = (String)getClipboardContent(DND.SELECTION_CLIPBOARD);
7503 	if (text != null && text.length() > 0) {
7504 		// position cursor
7505 		doMouseLocationChange(event.x, event.y, false);
7506 		// insert text
7507 		Event e = new Event();
7508 		e.start = selection.x;
7509 		e.end = selection.y;
7510 		e.text = getModelDelimitedText(text);
7511 		sendKeyEvent(e);
7512 	}
7513 }
7514 /**
7515  * Prints the widget's text to the default printer.
7516  *
7517  * @exception SWTException <ul>
7518  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7519  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7520  * </ul>
7521  */
7522 public void print() {
7523 	checkWidget();
7524 	Printer printer = new Printer();
7525 	StyledTextPrintOptions options = new StyledTextPrintOptions();
7526 	options.printTextForeground = true;
7527 	options.printTextBackground = true;
7528 	options.printTextFontStyle = true;
7529 	options.printLineBackground = true;
7530 	new Printing(this, printer, options).run();
7531 	printer.dispose();
7532 }
7533 /**
7534  * Returns a runnable that will print the widget's text
7535  * to the specified printer.
7536  * <p>
7537  * The runnable may be run in a non-UI thread.
7538  * </p>
7539  *
7540  * @param printer the printer to print to
7541  *
7542  * @return a <code>Runnable</code> for printing the receiver's text
7543  *
7544  * @exception SWTException <ul>
7545  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7546  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7547  * </ul>
7548  * @exception IllegalArgumentException <ul>
7549  *    <li>ERROR_NULL_ARGUMENT when printer is null</li>
7550  * </ul>
7551  */
7552 public Runnable print(Printer printer) {
7553 	checkWidget();
7554 	if (printer == null) {
7555 		SWT.error(SWT.ERROR_NULL_ARGUMENT);
7556 	}
7557 	StyledTextPrintOptions options = new StyledTextPrintOptions();
7558 	options.printTextForeground = true;
7559 	options.printTextBackground = true;
7560 	options.printTextFontStyle = true;
7561 	options.printLineBackground = true;
7562 	return print(printer, options);
7563 }
7564 /**
7565  * Returns a runnable that will print the widget's text
7566  * to the specified printer.
7567  * <p>
7568  * The runnable may be run in a non-UI thread.
7569  * </p>
7570  *
7571  * @param printer the printer to print to
7572  * @param options print options to use during printing
7573  *
7574  * @return a <code>Runnable</code> for printing the receiver's text
7575  *
7576  * @exception SWTException <ul>
7577  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7578  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7579  * </ul>
7580  * @exception IllegalArgumentException <ul>
7581  *    <li>ERROR_NULL_ARGUMENT when printer or options is null</li>
7582  * </ul>
7583  * @since 2.1
7584  */
7585 public Runnable print(Printer printer, StyledTextPrintOptions options) {
7586 	checkWidget();
7587 	if (printer == null || options == null) {
7588 		SWT.error(SWT.ERROR_NULL_ARGUMENT);
7589 	}
7590 	return new Printing(this, printer, options);
7591 }
7592 /**
7593  * Causes the entire bounds of the receiver to be marked
7594  * as needing to be redrawn. The next time a paint request
7595  * is processed, the control will be completely painted.
7596  * <p>
7597  * Recalculates the content width for all lines in the bounds.
7598  * When a <code>LineStyleListener</code> is used a redraw call
7599  * is the only notification to the widget that styles have changed
7600  * and that the content width may have changed.
7601  * </p>
7602  *
7603  * @exception SWTException <ul>
7604  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7605  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7606  * </ul>
7607  *
7608  * @see Control#update()
7609  */
7610 @Override
7611 public void redraw() {
7612 	super.redraw();
7613 	int itemCount = getPartialBottomIndex() - topIndex + 1;
7614 	renderer.reset(topIndex, itemCount);
7615 	renderer.calculate(topIndex, itemCount);
7616 	setScrollBars(false);
7617 	doMouseLinkCursor();
7618 }
7619 /**
7620  * Causes the rectangular area of the receiver specified by
7621  * the arguments to be marked as needing to be redrawn.
7622  * The next time a paint request is processed, that area of
7623  * the receiver will be painted. If the <code>all</code> flag
7624  * is <code>true</code>, any children of the receiver which
7625  * intersect with the specified area will also paint their
7626  * intersecting areas. If the <code>all</code> flag is
7627  * <code>false</code>, the children will not be painted.
7628  * <p>
7629  * Marks the content width of all lines in the specified rectangle
7630  * as unknown. Recalculates the content width of all visible lines.
7631  * When a <code>LineStyleListener</code> is used a redraw call
7632  * is the only notification to the widget that styles have changed
7633  * and that the content width may have changed.
7634  * </p>
7635  *
7636  * @param x the x coordinate of the area to draw
7637  * @param y the y coordinate of the area to draw
7638  * @param width the width of the area to draw
7639  * @param height the height of the area to draw
7640  * @param all <code>true</code> if children should redraw, and <code>false</code> otherwise
7641  *
7642  * @exception SWTException <ul>
7643  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7644  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7645  * </ul>
7646  *
7647  * @see Control#update()
7648  */
7649 @Override
7650 public void redraw(int x, int y, int width, int height, boolean all) {
7651 	super.redraw(x, y, width, height, all);
7652 	if (height > 0) {
7653 		int firstLine = getLineIndex(y);
7654 		int lastLine = getLineIndex(y + height);
7655 		resetCache(firstLine, lastLine - firstLine + 1);
7656 		doMouseLinkCursor();
7657 	}
7658 }
7659 void redrawLines(int startLine, int lineCount, boolean bottomChanged) {
7660 	// do nothing if redraw range is completely invisible
7661 	int endLine = startLine + lineCount - 1;
7662 	int partialBottomIndex = getPartialBottomIndex();
7663 	int partialTopIndex = getPartialTopIndex();
7664 	if (startLine > partialBottomIndex || endLine < partialTopIndex) {
7665 		return;
7666 	}
7667 	// only redraw visible lines
7668 	if (startLine < partialTopIndex) {
7669 		startLine = partialTopIndex;
7670 	}
7671 	if (endLine > partialBottomIndex) {
7672 		endLine = partialBottomIndex;
7673 	}
7674 	int redrawTop = getLinePixel(startLine);
7675 	int redrawBottom = getLinePixel(endLine + 1);
7676 	if (bottomChanged) redrawBottom = clientAreaHeight - bottomMargin;
7677 	int redrawWidth = clientAreaWidth - leftMargin - rightMargin;
7678 	super.redraw(leftMargin, redrawTop, redrawWidth, redrawBottom - redrawTop, true);
7679 }
7680 void redrawLinesBullet (int[] redrawLines) {
7681 	if (redrawLines == null) return;
7682 	int topIndex = getPartialTopIndex();
7683 	int bottomIndex = getPartialBottomIndex();
7684 	for (int redrawLine : redrawLines) {
7685 		int lineIndex = redrawLine;
7686 		if (!(topIndex <= lineIndex && lineIndex <= bottomIndex)) continue;
7687 		int width = -1;
7688 		Bullet bullet = renderer.getLineBullet(lineIndex, null);
7689 		if (bullet != null) {
7690 			StyleRange style = bullet.style;
7691 			GlyphMetrics metrics = style.metrics;
7692 			width = metrics.width;
7693 		}
7694 		if (width == -1) width = getClientArea().width;
7695 		int height = renderer.getLineHeight(lineIndex);
7696 		int y = getLinePixel(lineIndex);
7697 		super.redraw(0, y, width, height, false);
7698 	}
7699 }
7700 void redrawMargins(int oldHeight, int oldWidth) {
7701 	/* Redraw the old or new right/bottom margin if needed */
7702 	if (oldWidth != clientAreaWidth) {
7703 		if (rightMargin > 0) {
7704 			int x = (oldWidth < clientAreaWidth ? oldWidth : clientAreaWidth) - rightMargin;
7705 			super.redraw(x, 0, rightMargin, oldHeight, false);
7706 		}
7707 	}
7708 	if (oldHeight != clientAreaHeight) {
7709 		if (bottomMargin > 0) {
7710 			int y = (oldHeight < clientAreaHeight ? oldHeight : clientAreaHeight) - bottomMargin;
7711 			super.redraw(0, y, oldWidth, bottomMargin, false);
7712 		}
7713 	}
7714 }
7715 /**
7716  * Redraws the specified text range.
7717  *
7718  * @param start offset of the first character to redraw
7719  * @param length number of characters to redraw
7720  * @param clearBackground true if the background should be cleared as
7721  *  part of the redraw operation.  If true, the entire redraw range will
7722  *  be cleared before anything is redrawn.  If the redraw range includes
7723  *	the last character of a line (i.e., the entire line is redrawn) the
7724  * 	line is cleared all the way to the right border of the widget.
7725  * 	The redraw operation will be faster and smoother if clearBackground
7726  * 	is set to false.  Whether or not the flag can be set to false depends
7727  * 	on the type of change that has taken place.  If font styles or
7728  * 	background colors for the redraw range have changed, clearBackground
7729  * 	should be set to true.  If only foreground colors have changed for
7730  * 	the redraw range, clearBackground can be set to false.
7731  * @exception SWTException <ul>
7732  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7733  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7734  * </ul>
7735  * @exception IllegalArgumentException <ul>
7736  *   <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
7737  * </ul>
7738  */
7739 public void redrawRange(int start, int length, boolean clearBackground) {
7740 	checkWidget();
7741 	int end = start + length;
7742 	int contentLength = content.getCharCount();
7743 	if (start > end || start < 0 || end > contentLength) {
7744 		SWT.error(SWT.ERROR_INVALID_RANGE);
7745 	}
7746 	int firstLine = content.getLineAtOffset(start);
7747 	int lastLine = content.getLineAtOffset(end);
7748 	resetCache(firstLine, lastLine - firstLine + 1);
7749 	internalRedrawRange(start, length);
7750 	doMouseLinkCursor();
7751 }
7752 /**
7753  * Removes the specified bidirectional segment listener.
7754  *
7755  * @param listener the listener which should no longer be notified
7756  *
7757  * @exception SWTException <ul>
7758  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7759  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7760  * </ul>
7761  * @exception IllegalArgumentException <ul>
7762  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
7763  * </ul>
7764  *
7765  * @since 2.0
7766  */
7767 public void removeBidiSegmentListener(BidiSegmentListener listener) {
7768 	checkWidget();
7769 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7770 	removeListener(ST.LineGetSegments, listener);
7771 	resetCache(0, content.getLineCount());
7772 	setCaretLocation();
7773 	super.redraw();
7774 }
7775 /**
7776  * Removes the specified caret listener.
7777  *
7778  * @param listener the listener which should no longer be notified
7779  *
7780  * @exception SWTException <ul>
7781  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7782  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7783  * </ul>
7784  * @exception IllegalArgumentException <ul>
7785  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
7786  * </ul>
7787  *
7788  * @since 3.5
7789  */
7790 public void removeCaretListener(CaretListener listener) {
7791 	checkWidget();
7792 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7793 	removeListener(ST.CaretMoved, listener);
7794 }
7795 /**
7796  * Removes the specified extended modify listener.
7797  *
7798  * @param extendedModifyListener the listener which should no longer be notified
7799  *
7800  * @exception SWTException <ul>
7801  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7802  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7803  * </ul>
7804  * @exception IllegalArgumentException <ul>
7805  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
7806  * </ul>
7807  */
7808 public void removeExtendedModifyListener(ExtendedModifyListener extendedModifyListener) {
7809 	checkWidget();
7810 	if (extendedModifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7811 	removeListener(ST.ExtendedModify, extendedModifyListener);
7812 }
7813 /**
7814  * Removes the specified line background listener.
7815  *
7816  * @param listener the listener which should no longer be notified
7817  *
7818  * @exception SWTException <ul>
7819  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7820  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7821  * </ul>
7822  * @exception IllegalArgumentException <ul>
7823  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
7824  * </ul>
7825  */
7826 public void removeLineBackgroundListener(LineBackgroundListener listener) {
7827 	checkWidget();
7828 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7829 	removeListener(ST.LineGetBackground, listener);
7830 }
7831 /**
7832  * Removes the specified line style listener.
7833  *
7834  * @param listener the listener which should no longer be notified
7835  *
7836  * @exception SWTException <ul>
7837  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7838  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7839  * </ul>
7840  * @exception IllegalArgumentException <ul>
7841  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
7842  * </ul>
7843  */
7844 public void removeLineStyleListener(LineStyleListener listener) {
7845 	checkWidget();
7846 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7847 	removeListener(ST.LineGetStyle, listener);
7848 	setCaretLocation();
7849 }
7850 /**
7851  * Removes the specified modify listener.
7852  *
7853  * @param modifyListener the listener which should no longer be notified
7854  *
7855  * @exception SWTException <ul>
7856  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7857  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7858  * </ul>
7859  * @exception IllegalArgumentException <ul>
7860  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
7861  * </ul>
7862  */
7863 public void removeModifyListener(ModifyListener modifyListener) {
7864 	checkWidget();
7865 	if (modifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7866 	removeListener(SWT.Modify, modifyListener);
7867 }
7868 /**
7869  * Removes the specified listener.
7870  *
7871  * @param listener the listener which should no longer be notified
7872  *
7873  * @exception SWTException <ul>
7874  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7875  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7876  * </ul>
7877  * @exception IllegalArgumentException <ul>
7878  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
7879  * </ul>
7880  * @since 3.2
7881  */
7882 public void removePaintObjectListener(PaintObjectListener listener) {
7883 	checkWidget();
7884 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7885 	removeListener(ST.PaintObject, listener);
7886 }
7887 /**
7888  * Removes the listener from the collection of listeners who will
7889  * be notified when the user changes the receiver's selection.
7890  *
7891  * @param listener the listener which should no longer be notified
7892  *
7893  * @exception IllegalArgumentException <ul>
7894  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
7895  * </ul>
7896  * @exception SWTException <ul>
7897  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7898  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7899  * </ul>
7900  *
7901  * @see SelectionListener
7902  * @see #addSelectionListener
7903  */
7904 public void removeSelectionListener(SelectionListener listener) {
7905 	checkWidget();
7906 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7907 	removeListener(SWT.Selection, listener);
7908 }
7909 /**
7910  * Removes the specified verify listener.
7911  *
7912  * @param verifyListener the listener which should no longer be notified
7913  *
7914  * @exception SWTException <ul>
7915  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7916  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7917  * </ul>
7918  * @exception IllegalArgumentException <ul>
7919  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
7920  * </ul>
7921  */
7922 public void removeVerifyListener(VerifyListener verifyListener) {
7923 	checkWidget();
7924 	if (verifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7925 	removeListener(SWT.Verify, verifyListener);
7926 }
7927 /**
7928  * Removes the specified key verify listener.
7929  *
7930  * @param listener the listener which should no longer be notified
7931  *
7932  * @exception SWTException <ul>
7933  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7934  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7935  * </ul>
7936  * @exception IllegalArgumentException <ul>
7937  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
7938  * </ul>
7939  */
7940 public void removeVerifyKeyListener(VerifyKeyListener listener) {
7941 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7942 	removeListener(ST.VerifyKey, listener);
7943 }
7944 /**
7945  * Removes the specified word movement listener.
7946  *
7947  * @param listener the listener which should no longer be notified
7948  *
7949  * @exception SWTException <ul>
7950  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7951  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7952  * </ul>
7953  * @exception IllegalArgumentException <ul>
7954  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
7955  * </ul>
7956  *
7957  * @see MovementEvent
7958  * @see MovementListener
7959  * @see #addWordMovementListener
7960  *
7961  * @since 3.3
7962  */
7963 
7964 public void removeWordMovementListener(MovementListener listener) {
7965 	checkWidget();
7966 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7967 	removeListener(ST.WordNext, listener);
7968 	removeListener(ST.WordPrevious, listener);
7969 }
7970 /**
7971  * Replaces the styles in the given range with new styles.  This method
7972  * effectively deletes the styles in the given range and then adds the
7973  * the new styles.
7974  * <p>
7975  * Note: Because a StyleRange includes the start and length, the
7976  * same instance cannot occur multiple times in the array of styles.
7977  * If the same style attributes, such as font and color, occur in
7978  * multiple StyleRanges, <code>setStyleRanges(int, int, int[], StyleRange[])</code>
7979  * can be used to share styles and reduce memory usage.
7980  * </p><p>
7981  * Should not be called if a LineStyleListener has been set since the
7982  * listener maintains the styles.
7983  * </p>
7984  *
7985  * @param start offset of first character where styles will be deleted
7986  * @param length length of the range to delete styles in
7987  * @param ranges StyleRange objects containing the new style information.
7988  * The ranges should not overlap and should be within the specified start
7989  * and length. The style rendering is undefined if the ranges do overlap
7990  * or are ill-defined. Must not be null.
7991  * @exception SWTException <ul>
7992  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7993  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7994  * </ul>
7995  * @exception IllegalArgumentException <ul>
7996  *   <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 &lt;= offset &lt;= getCharCount())</li>
7997  *   <li>ERROR_NULL_ARGUMENT when ranges is null</li>
7998  * </ul>
7999  *
8000  * @since 2.0
8001  *
8002  * @see #setStyleRanges(int, int, int[], StyleRange[])
8003  */
8004 public void replaceStyleRanges(int start, int length, StyleRange[] ranges) {
8005 	checkWidget();
8006 	if (isListening(ST.LineGetStyle)) return;
8007 	if (ranges == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
8008 	setStyleRanges(start, length, null, ranges, false);
8009 }
8010 /**
8011  * Replaces the given text range with new text.
8012  * If the widget has the SWT.SINGLE style and "text" contains more than
8013  * one line, only the first line is rendered but the text is stored
8014  * unchanged. A subsequent call to getText will return the same text
8015  * that was set. Note that only a single line of text should be set when
8016  * the SWT.SINGLE style is used.
8017  * <p>
8018  * <b>NOTE:</b> During the replace operation the current selection is
8019  * changed as follows:
8020  * </p>
8021  * <ul>
8022  * <li>selection before replaced text: selection unchanged
8023  * <li>selection after replaced text: adjust the selection so that same text
8024  * remains selected
8025  * <li>selection intersects replaced text: selection is cleared and caret
8026  * is placed after inserted text
8027  * </ul>
8028  *
8029  * @param start offset of first character to replace
8030  * @param length number of characters to replace. Use 0 to insert text
8031  * @param text new text. May be empty to delete text.
8032  * @exception SWTException <ul>
8033  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8034  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8035  * </ul>
8036  * @exception IllegalArgumentException <ul>
8037  *   <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 &lt;= offset &lt;= getCharCount())</li>
8038  *   <li>ERROR_INVALID_ARGUMENT when either start or end is inside a multi byte line delimiter.
8039  * 		Splitting a line delimiter for example by inserting text in between the CR and LF and deleting part of a line delimiter is not supported</li>
8040  *   <li>ERROR_NULL_ARGUMENT when string is null</li>
8041  * </ul>
8042  */
8043 public void replaceTextRange(int start, int length, String text) {
8044 	checkWidget();
8045 	if (text == null) {
8046 		SWT.error(SWT.ERROR_NULL_ARGUMENT);
8047 	}
8048 	int contentLength = getCharCount();
8049 	int end = start + length;
8050 	if (start > end || start < 0 || end > contentLength) {
8051 		SWT.error(SWT.ERROR_INVALID_RANGE);
8052 	}
8053 	Event event = new Event();
8054 	event.start = start;
8055 	event.end = end;
8056 	event.text = text;
8057 	modifyContent(event, false);
8058 }
8059 /**
8060  * Resets the caret position, selection and scroll offsets. Recalculate
8061  * the content width and scroll bars. Redraw the widget.
8062  */
8063 void reset() {
8064 	ScrollBar verticalBar = getVerticalBar();
8065 	ScrollBar horizontalBar = getHorizontalBar();
8066 	setCaretOffset(0, SWT.DEFAULT);
8067 	topIndex = 0;
8068 	topIndexY = 0;
8069 	verticalScrollOffset = 0;
8070 	horizontalScrollOffset = 0;
8071 	resetSelection();
8072 	renderer.setContent(content);
8073 	if (verticalBar != null) {
8074 		verticalBar.setSelection(0);
8075 	}
8076 	if (horizontalBar != null) {
8077 		horizontalBar.setSelection(0);
8078 	}
8079 	resetCache(0, 0);
8080 	setCaretLocation();
8081 	super.redraw();
8082 }
8083 void resetBidiData() {
8084 	caretDirection = SWT.NULL;
8085 	resetCache(0, content.getLineCount());
8086 	setCaretLocation();
8087 	keyActionMap.clear();
8088 	createKeyBindings();
8089 	super.redraw();
8090 }
8091 void resetCache(SortedSet<Integer> lines) {
8092 	if (lines == null || lines.isEmpty()) return;
8093 	int maxLineIndex = renderer.maxWidthLineIndex;
8094 	renderer.reset(lines);
8095 	renderer.calculateClientArea();
8096 	if (0 <= maxLineIndex && maxLineIndex < content.getLineCount()) {
8097 		renderer.calculate(maxLineIndex, 1);
8098 	}
8099 	setScrollBars(true);
8100 	if (!isFixedLineHeight()) {
8101 		if (topIndex > lines.iterator().next()) {
8102 			verticalScrollOffset = -1;
8103 		}
8104 		renderer.calculateIdle();
8105 	}
8106 }
8107 void resetCache(int firstLine, int count) {
8108 	int maxLineIndex = renderer.maxWidthLineIndex;
8109 	renderer.reset(firstLine, count);
8110 	renderer.calculateClientArea();
8111 	if (0 <= maxLineIndex && maxLineIndex < content.getLineCount()) {
8112 		renderer.calculate(maxLineIndex, 1);
8113 	}
8114 	setScrollBars(true);
8115 	if (!isFixedLineHeight()) {
8116 		if (topIndex > firstLine) {
8117 			verticalScrollOffset = -1;
8118 		}
8119 		renderer.calculateIdle();
8120 	}
8121 }
8122 /**
8123  * Resets the selection.
8124  */
8125 void resetSelection() {
8126 	selection.x = selection.y = caretOffset;
8127 	selectionAnchor = -1;
8128 	sendAccessibleTextCaretMoved();
8129 }
8130 
8131 @Override
8132 public void scroll(int destX, int destY, int x, int y, int width, int height, boolean all) {
8133 	super.scroll(destX, destY, x, y, width, height, false);
8134 	if (all) {
8135 		int deltaX = destX - x, deltaY = destY - y;
8136 		for (Control child : getChildren()) {
8137 			Rectangle rect = child.getBounds();
8138 			child.setLocation(rect.x + deltaX, rect.y + deltaY);
8139 		}
8140 	}
8141 }
8142 
8143 /**
8144  * Scrolls the widget horizontally.
8145  *
8146  * @param pixels number of SWT logical points to scroll, &gt; 0 = scroll left,
8147  * 	&lt; 0 scroll right
8148  * @param adjustScrollBar
8149  * 	true= the scroll thumb will be moved to reflect the new scroll offset.
8150  * 	false = the scroll thumb will not be moved
8151  * @return
8152  *	true=the widget was scrolled
8153  *	false=the widget was not scrolled, the given offset is not valid.
8154  */
8155 boolean scrollHorizontal(int pixels, boolean adjustScrollBar) {
8156 	if (pixels == 0) return false;
8157 	if (wordWrap) return false;
8158 	ScrollBar horizontalBar = getHorizontalBar();
8159 	if (horizontalBar != null && adjustScrollBar) {
8160 		horizontalBar.setSelection(horizontalScrollOffset + pixels);
8161 	}
8162 	int scrollHeight = clientAreaHeight - topMargin - bottomMargin;
8163 	if (pixels > 0) {
8164 		int sourceX = leftMargin + pixels;
8165 		int scrollWidth = clientAreaWidth - sourceX - rightMargin;
8166 		if (scrollWidth > 0) {
8167 			scroll(leftMargin, topMargin, sourceX, topMargin, scrollWidth, scrollHeight, true);
8168 		}
8169 		if (sourceX > scrollWidth) {
8170 			super.redraw(leftMargin + scrollWidth, topMargin, pixels - scrollWidth, scrollHeight, true);
8171 		}
8172 	} else {
8173 		int destinationX = leftMargin - pixels;
8174 		int scrollWidth = clientAreaWidth - destinationX - rightMargin;
8175 		if (scrollWidth > 0) {
8176 			scroll(destinationX, topMargin, leftMargin, topMargin, scrollWidth, scrollHeight, true);
8177 		}
8178 		if (destinationX > scrollWidth) {
8179 			super.redraw(leftMargin + scrollWidth, topMargin, -pixels - scrollWidth, scrollHeight, true);
8180 		}
8181 	}
8182 	horizontalScrollOffset += pixels;
8183 	setCaretLocation();
8184 	return true;
8185 }
8186 /**
8187  * Scrolls the widget vertically.
8188  *
8189  * @param pixel the new vertical scroll offset
8190  * @param adjustScrollBar
8191  * 	true= the scroll thumb will be moved to reflect the new scroll offset.
8192  * 	false = the scroll thumb will not be moved
8193  * @return
8194  *	true=the widget was scrolled
8195  *	false=the widget was not scrolled
8196  */
8197 boolean scrollVertical(int pixels, boolean adjustScrollBar) {
8198 	if (pixels == 0) {
8199 		return false;
8200 	}
8201 	if (verticalScrollOffset != -1) {
8202 		ScrollBar verticalBar = getVerticalBar();
8203 		if (verticalBar != null && adjustScrollBar) {
8204 			verticalBar.setSelection(verticalScrollOffset + pixels);
8205 		}
8206 		int deltaY = 0;
8207 		if (pixels > 0) {
8208 			int sourceY = topMargin + pixels;
8209 			int scrollHeight = clientAreaHeight - sourceY - bottomMargin;
8210 			if (scrollHeight > 0) {
8211 				deltaY = -pixels;
8212 			}
8213 		} else {
8214 			int destinationY = topMargin - pixels;
8215 			int scrollHeight = clientAreaHeight - destinationY - bottomMargin;
8216 			if (scrollHeight > 0) {
8217 				deltaY = -pixels;
8218 			}
8219 		}
8220 		Control[] children = getChildren();
8221 		for (Control child : children) {
8222 			Rectangle rect = child.getBounds();
8223 			child.setLocation(rect.x, rect.y + deltaY);
8224 		}
8225 		verticalScrollOffset += pixels;
8226 		calculateTopIndex(pixels);
8227 		super.redraw();
8228 	} else {
8229 		calculateTopIndex(pixels);
8230 		super.redraw();
8231 	}
8232 	setCaretLocation();
8233 	return true;
8234 }
8235 void scrollText(int srcY, int destY) {
8236 	if (srcY == destY) return;
8237 	int deltaY = destY - srcY;
8238 	int scrollWidth = clientAreaWidth - leftMargin - rightMargin, scrollHeight;
8239 	if (deltaY > 0) {
8240 		scrollHeight = clientAreaHeight - srcY - bottomMargin;
8241 	} else {
8242 		scrollHeight = clientAreaHeight - destY - bottomMargin;
8243 	}
8244 	scroll(leftMargin, destY, leftMargin, srcY, scrollWidth, scrollHeight, true);
8245 	if ((0 < srcY + scrollHeight) && (topMargin > srcY)) {
8246 		super.redraw(leftMargin, deltaY, scrollWidth, topMargin, false);
8247 	}
8248 	if ((0 < destY + scrollHeight) && (topMargin > destY)) {
8249 		super.redraw(leftMargin, 0, scrollWidth, topMargin, false);
8250 	}
8251 	if ((clientAreaHeight - bottomMargin < srcY + scrollHeight) && (clientAreaHeight > srcY)) {
8252 		super.redraw(leftMargin, clientAreaHeight - bottomMargin + deltaY, scrollWidth, bottomMargin, false);
8253 	}
8254 	if ((clientAreaHeight - bottomMargin < destY + scrollHeight) && (clientAreaHeight > destY)) {
8255 		super.redraw(leftMargin, clientAreaHeight - bottomMargin, scrollWidth, bottomMargin, false);
8256 	}
8257 }
8258 void sendAccessibleTextCaretMoved() {
8259 	if (caretOffset != accCaretOffset) {
8260 		accCaretOffset = caretOffset;
8261 		getAccessible().textCaretMoved(caretOffset);
8262 	}
8263 }
8264 void sendAccessibleTextChanged(int start, int newCharCount, int replaceCharCount) {
8265 	Accessible accessible = getAccessible();
8266 	if (replaceCharCount != 0) {
8267 		accessible.textChanged(ACC.TEXT_DELETE, start, replaceCharCount);
8268 	}
8269 	if (newCharCount != 0) {
8270 		accessible.textChanged(ACC.TEXT_INSERT, start, newCharCount);
8271 	}
8272 }
8273 /**
8274  * Selects all the text.
8275  *
8276  * @exception SWTException <ul>
8277  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8278  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8279  * </ul>
8280  */
8281 public void selectAll() {
8282 	checkWidget();
8283 	if (blockSelection) {
8284 		renderer.calculate(0, content.getLineCount());
8285 		setScrollBars(false);
8286 		int verticalScrollOffset = getVerticalScrollOffset();
8287 		int left = leftMargin - horizontalScrollOffset;
8288 		int top = topMargin - verticalScrollOffset;
8289 		int right = renderer.getWidth() - rightMargin - horizontalScrollOffset;
8290 		int bottom = renderer.getHeight() - bottomMargin - verticalScrollOffset;
8291 		setBlockSelectionLocation(left, top, right, bottom, false);
8292 		return;
8293 	}
8294 	setSelection(0, Math.max(getCharCount(),0));
8295 }
8296 /**
8297  * Replaces/inserts text as defined by the event.
8298  *
8299  * @param event the text change event.
8300  *	<ul>
8301  *	<li>event.start - the replace start offset</li>
8302  * 	<li>event.end - the replace end offset</li>
8303  * 	<li>event.text - the new text</li>
8304  *	</ul>
8305  */
8306 void sendKeyEvent(Event event) {
8307 	if (editable) {
8308 		modifyContent(event, true);
8309 	}
8310 }
8311 /**
8312  * Returns a StyledTextEvent that can be used to request data such
8313  * as styles and background color for a line.
8314  * <p>
8315  * The specified line may be a visual (wrapped) line if in word
8316  * wrap mode. The returned object will always be for a logical
8317  * (unwrapped) line.
8318  * </p>
8319  *
8320  * @param lineOffset offset of the line. This may be the offset of
8321  * 	a visual line if the widget is in word wrap mode.
8322  * @param line line text. This may be the text of a visual line if
8323  * 	the widget is in word wrap mode.
8324  * @return StyledTextEvent that can be used to request line data
8325  * 	for the given line.
8326  */
8327 StyledTextEvent sendLineEvent(int eventType, int lineOffset, String line) {
8328 	StyledTextEvent event = null;
8329 	if (isListening(eventType)) {
8330 		event = new StyledTextEvent(content);
8331 		event.detail = lineOffset;
8332 		event.text = line;
8333 		event.alignment = alignment;
8334 		event.indent = indent;
8335 		event.wrapIndent = wrapIndent;
8336 		event.justify = justify;
8337 		notifyListeners(eventType, event);
8338 	}
8339 	return event;
8340 }
8341 /**
8342  * Sends the specified selection event.
8343  */
8344 void sendSelectionEvent() {
8345 	getAccessible().textSelectionChanged();
8346 	Event event = new Event();
8347 	event.x = selection.x;
8348 	event.y = selection.y;
8349 	notifyListeners(SWT.Selection, event);
8350 }
8351 int sendTextEvent(int left, int right, int lineIndex, String text, boolean fillWithSpaces) {
8352 	int lineWidth = 0, start, end;
8353 	StringBuilder buffer = new StringBuilder();
8354 	if (lineIndex < content.getLineCount()) {
8355 		int[] trailing = new int[1];
8356 		start = getOffsetAtPoint(left, getLinePixel(lineIndex), trailing, true);
8357 		if (start == -1) {
8358 			int lineOffset = content.getOffsetAtLine(lineIndex);
8359 			int lineLegth = content.getLine(lineIndex).length();
8360 			start = end = lineOffset + lineLegth;
8361 			if (fillWithSpaces) {
8362 				TextLayout layout = renderer.getTextLayout(lineIndex);
8363 				lineWidth = layout.getBounds().width;
8364 				renderer.disposeTextLayout(layout);
8365 			}
8366 		} else {
8367 			start += trailing[0];
8368 			end = left == right ? start : getOffsetAtPoint(right, 0, lineIndex, null);
8369 			fillWithSpaces = false;
8370 		}
8371 	} else {
8372 		start = end = content.getCharCount();
8373 		buffer.append(content.getLineDelimiter());
8374 	}
8375 	if (start > end) {
8376 		int temp = start;
8377 		start = end;
8378 		end = temp;
8379 	}
8380 	if (fillWithSpaces) {
8381 		int spacesWidth = left - lineWidth + horizontalScrollOffset - leftMargin;
8382 		int spacesCount = spacesWidth / renderer.averageCharWidth;
8383 		for (int i = 0; i < spacesCount; i++) {
8384 			buffer.append(' ');
8385 		}
8386 	}
8387 	buffer.append(text);
8388 	Event event = new Event();
8389 	event.start = start;
8390 	event.end = end;
8391 	event.text = buffer.toString();
8392 	sendKeyEvent(event);
8393 	return event.start + event.text.length();
8394 }
8395 int sendWordBoundaryEvent(int eventType, int movement, int offset, int newOffset, String lineText, int lineOffset) {
8396 	if (isListening(eventType)) {
8397 		StyledTextEvent event = new StyledTextEvent(content);
8398 		event.detail = lineOffset;
8399 		event.text = lineText;
8400 		event.count = movement;
8401 		event.start = offset;
8402 		event.end = newOffset;
8403 		notifyListeners(eventType, event);
8404 		offset = event.end;
8405 		if (offset != newOffset) {
8406 			int length = getCharCount();
8407 			if (offset < 0) {
8408 				offset = 0;
8409 			} else if (offset > length) {
8410 				offset = length;
8411 			} else {
8412 				if (isLineDelimiter(offset)) {
8413 					SWT.error(SWT.ERROR_INVALID_ARGUMENT);
8414 				}
8415 			}
8416 		}
8417 		return offset;
8418 	}
8419 	return newOffset;
8420 }
8421 void setAlignment() {
8422 	if ((getStyle() & SWT.SINGLE) == 0) return;
8423 	int alignment = renderer.getLineAlignment(0, this.alignment);
8424 	int newAlignmentMargin = 0;
8425 	if (alignment != SWT.LEFT) {
8426 		renderer.calculate(0, 1);
8427 		int width = renderer.getWidth() - alignmentMargin;
8428 		newAlignmentMargin = clientAreaWidth - width;
8429 		if (newAlignmentMargin < 0) newAlignmentMargin = 0;
8430 		if (alignment == SWT.CENTER) newAlignmentMargin /= 2;
8431 	}
8432 	if (alignmentMargin != newAlignmentMargin) {
8433 		leftMargin -= alignmentMargin;
8434 		leftMargin += newAlignmentMargin;
8435 		alignmentMargin = newAlignmentMargin;
8436 		resetCache(0, 1);
8437 		setCaretLocation();
8438 		super.redraw();
8439 	}
8440 }
8441 /**
8442  * Sets the alignment of the widget. The argument should be one of <code>SWT.LEFT</code>,
8443  * <code>SWT.CENTER</code> or <code>SWT.RIGHT</code>. The alignment applies for all lines.
8444  * <p>
8445  * Note that if <code>SWT.MULTI</code> is set, then <code>SWT.WRAP</code> must also be set
8446  * in order to stabilize the right edge before setting alignment.
8447  * </p>
8448  *
8449  * @param alignment the new alignment
8450  *
8451  * @exception SWTException <ul>
8452  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8453  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8454  * </ul>
8455  *
8456  * @see #setLineAlignment(int, int, int)
8457  *
8458  * @since 3.2
8459  */
8460 public void setAlignment(int alignment) {
8461 	checkWidget();
8462 	alignment &= (SWT.LEFT | SWT.RIGHT | SWT.CENTER);
8463 	if (alignment == 0 || this.alignment == alignment) return;
8464 	this.alignment = alignment;
8465 	resetCache(0, content.getLineCount());
8466 	setCaretLocation();
8467 	setAlignment();
8468 	super.redraw();
8469 }
8470 /**
8471  * Set the Always Show Scrollbars flag.  True if the scrollbars are
8472  * always shown even if they are not required (default value).  False if the scrollbars are only
8473  * visible when some part of the content needs to be scrolled to be seen.
8474  * The H_SCROLL and V_SCROLL style bits are also required to enable scrollbars in the
8475  * horizontal and vertical directions.
8476  *
8477  * @param show true to show the scrollbars even when not required, false to show scrollbars only when required
8478  *
8479  * @exception SWTException <ul>
8480  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8481  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8482  * </ul>
8483  *
8484  * @since 3.8
8485  */
8486 public void setAlwaysShowScrollBars(boolean show) {
8487 	checkWidget();
8488 	if (show == alwaysShowScroll) return;
8489 	alwaysShowScroll = show;
8490 	setScrollBars(true);
8491 }
8492 /**
8493  * @see Control#setBackground(Color)
8494  */
8495 @Override
8496 public void setBackground(Color color) {
8497 	checkWidget();
8498 	boolean backgroundDisabled = false;
8499 	if (!this.enabled && color == null) {
8500 		if (background != null) {
8501 			Color disabledBg = getDisplay().getSystemColor(SWT.COLOR_TEXT_DISABLED_BACKGROUND);
8502 			if (background.equals(disabledBg)) {
8503 				return;
8504 			} else {
8505 				color = new Color (disabledBg.getRGBA());
8506 				backgroundDisabled = true;
8507 			}
8508 		}
8509 	}
8510 	customBackground = color != null && !this.insideSetEnableCall && !backgroundDisabled;
8511 	background = color;
8512 	super.setBackground(color);
8513 	resetCache(0, content.getLineCount());
8514 	setCaretLocation();
8515 	super.redraw();
8516 }
8517 /**
8518  * Sets the block selection mode.
8519  *
8520  * @param blockSelection true=enable block selection, false=disable block selection
8521  *
8522  * @since 3.5
8523  */
8524 public void setBlockSelection(boolean blockSelection) {
8525 	checkWidget();
8526 	if ((getStyle() & SWT.SINGLE) != 0) return;
8527 	if (blockSelection == this.blockSelection) return;
8528 	if (wordWrap) return;
8529 	this.blockSelection = blockSelection;
8530 	if (cursor == null) {
8531 		Display display = getDisplay();
8532 		int type = blockSelection ? SWT.CURSOR_CROSS : SWT.CURSOR_IBEAM;
8533 		super.setCursor(display.getSystemCursor(type));
8534 	}
8535 	if (blockSelection) {
8536 		int start = selection.x;
8537 		int end = selection.y;
8538 		if (start != end) {
8539 			setBlockSelectionOffset(start, end, false);
8540 		}
8541 	} else {
8542 		clearBlockSelection(false, false);
8543 	}
8544 }
8545 /**
8546  * Sets the block selection bounds. The bounds is
8547  * relative to the upper left corner of the document.
8548  *
8549  * @param rect the new bounds for the block selection
8550  *
8551  * @see #setBlockSelectionBounds(int, int, int, int)
8552  * @exception SWTException <ul>
8553  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8554  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8555  * </ul>
8556  * @exception IllegalArgumentException <ul>
8557  *   <li>ERROR_NULL_ARGUMENT when point is null</li>
8558  * </ul>
8559  *
8560  * @since 3.5
8561  */
8562 public void setBlockSelectionBounds(Rectangle rect) {
8563 	checkWidget();
8564 	if (rect == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
8565 	setBlockSelectionBounds(rect.x, rect.y, rect.width, rect.height);
8566 }
8567 /**
8568  * Sets the block selection bounds. The bounds is
8569  * relative to the upper left corner of the document.
8570  *
8571  * @param x the new x coordinate for the block selection
8572  * @param y the new y coordinate for the block selection
8573  * @param width the new width for the block selection
8574  * @param height the new height for the block selection
8575  *
8576  * @exception SWTException <ul>
8577  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8578  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8579  * </ul>
8580  *
8581  * @since 3.5
8582  */
8583 public void setBlockSelectionBounds(int x, int y, int width, int height) {
8584 	checkWidget();
8585 	int verticalScrollOffset = getVerticalScrollOffset();
8586 	if (!blockSelection) {
8587 		x -= horizontalScrollOffset;
8588 		y -= verticalScrollOffset;
8589 		int start = getOffsetAtPoint(x, y, null);
8590 		int end = getOffsetAtPoint(x+width-1, y+height-1, null);
8591 		setSelection(start, end - start, false, false);
8592 		setCaretLocation();
8593 		return;
8594 	}
8595 	int minY = topMargin;
8596 	int minX = leftMargin;
8597 	int maxY = renderer.getHeight() - bottomMargin;
8598 	int maxX = Math.max(clientAreaWidth, renderer.getWidth()) - rightMargin;
8599 	int anchorX = Math.max(minX, Math.min(maxX, x)) - horizontalScrollOffset;
8600 	int anchorY = Math.max(minY, Math.min(maxY, y)) - verticalScrollOffset;
8601 	int locationX = Math.max(minX, Math.min(maxX, x + width)) - horizontalScrollOffset;
8602 	int locationY = Math.max(minY, Math.min(maxY, y + height - 1)) - verticalScrollOffset;
8603 	if (isFixedLineHeight() && renderer.fixedPitch) {
8604 		int avg = renderer.averageCharWidth;
8605 		anchorX = ((anchorX - leftMargin + horizontalScrollOffset) / avg * avg) + leftMargin - horizontalScrollOffset;
8606 		locationX = ((locationX + avg / 2 - leftMargin + horizontalScrollOffset) / avg * avg) + leftMargin - horizontalScrollOffset;
8607 	}
8608 	setBlockSelectionLocation(anchorX, anchorY, locationX, locationY, false);
8609 }
8610 void setBlockSelectionLocation (int x, int y, boolean sendEvent) {
8611 	int verticalScrollOffset = getVerticalScrollOffset();
8612 	blockXLocation = x + horizontalScrollOffset;
8613 	blockYLocation = y + verticalScrollOffset;
8614 	int[] alignment = new int[1];
8615 	int offset = getOffsetAtPoint(x, y, alignment);
8616 	setCaretOffset(offset, alignment[0]);
8617 	if (blockXAnchor == -1) {
8618 		blockXAnchor = blockXLocation;
8619 		blockYAnchor = blockYLocation;
8620 		selectionAnchor = caretOffset;
8621 	}
8622 	doBlockSelection(sendEvent);
8623 }
8624 void setBlockSelectionLocation (int anchorX, int anchorY, int x, int y, boolean sendEvent) {
8625 	int verticalScrollOffset = getVerticalScrollOffset();
8626 	blockXAnchor = anchorX + horizontalScrollOffset;
8627 	blockYAnchor = anchorY + verticalScrollOffset;
8628 	selectionAnchor = getOffsetAtPoint(anchorX, anchorY, null);
8629 	setBlockSelectionLocation(x, y, sendEvent);
8630 }
8631 void setBlockSelectionOffset (int offset, boolean sendEvent) {
8632 	Point point = getPointAtOffset(offset);
8633 	int verticalScrollOffset = getVerticalScrollOffset();
8634 	blockXLocation = point.x + horizontalScrollOffset;
8635 	blockYLocation = point.y + verticalScrollOffset;
8636 	setCaretOffset(offset, SWT.DEFAULT);
8637 	if (blockXAnchor == -1) {
8638 		blockXAnchor = blockXLocation;
8639 		blockYAnchor = blockYLocation;
8640 		selectionAnchor = caretOffset;
8641 	}
8642 	doBlockSelection(sendEvent);
8643 }
8644 void setBlockSelectionOffset (int anchorOffset, int offset, boolean sendEvent) {
8645 	int verticalScrollOffset = getVerticalScrollOffset();
8646 	Point anchorPoint = getPointAtOffset(anchorOffset);
8647 	blockXAnchor = anchorPoint.x + horizontalScrollOffset;
8648 	blockYAnchor = anchorPoint.y + verticalScrollOffset;
8649 	selectionAnchor = anchorOffset;
8650 	setBlockSelectionOffset(offset, sendEvent);
8651 }
8652 /**
8653  * Sets the receiver's caret.  Set the caret's height and location.
8654  *
8655  * @param caret the new caret for the receiver
8656  *
8657  * @exception SWTException <ul>
8658  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8659  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8660  * </ul>
8661  */
8662 @Override
8663 public void setCaret(Caret caret) {
8664 	checkWidget ();
8665 	super.setCaret(caret);
8666 	caretDirection = SWT.NULL;
8667 	if (caret != null) {
8668 		setCaretLocation();
8669 	}
8670 }
8671 /**
8672  * Sets the BIDI coloring mode.  When true the BIDI text display
8673  * algorithm is applied to segments of text that are the same
8674  * color.
8675  *
8676  * @param mode the new coloring mode
8677  * @exception SWTException <ul>
8678  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8679  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8680  * </ul>
8681  *
8682  * @deprecated use BidiSegmentListener instead.
8683  */
8684 @Deprecated
8685 public void setBidiColoring(boolean mode) {
8686 	checkWidget();
8687 	bidiColoring = mode;
8688 }
8689 /**
8690  * Sets the bottom margin.
8691  *
8692  * @param bottomMargin the bottom margin.
8693  * @exception SWTException <ul>
8694  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8695  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8696  * </ul>
8697  *
8698  * @since 3.5
8699  */
8700 public void setBottomMargin (int bottomMargin) {
8701 	checkWidget();
8702 	setMargins(getLeftMargin(), topMargin, rightMargin, bottomMargin);
8703 }
8704 /**
8705  * Moves the Caret to the current caret offset.
8706  */
8707 void setCaretLocation() {
8708 	Point newCaretPos = getPointAtOffset(caretOffset);
8709 	setCaretLocation(newCaretPos, getCaretDirection());
8710 }
8711 void setCaretLocation(final Point location, int direction) {
8712 	Caret caret = getCaret();
8713 	if (caret != null) {
8714 		final boolean isDefaultCaret = caret == defaultCaret;
8715 		final StyleRange styleAtOffset = content.getCharCount() > 0 ?
8716 			(caretOffset < content.getCharCount() ?
8717 				getStyleRangeAtOffset(caretOffset) :
8718 				getStyleRangeAtOffset(content.getCharCount() - 1)) : // caret after last char: use last char style
8719 			null;
8720 
8721 		int graphicalLineHeight = getLineHeight(caretOffset);
8722 		int caretHeight = getLineHeight();
8723 
8724 		if (styleAtOffset != null && styleAtOffset.isVariableHeight()) {
8725 			if (isDefaultCaret) {
8726 				direction = SWT.DEFAULT;
8727 				caretHeight = graphicalLineHeight;
8728 			} else {
8729 				caretHeight = caret.getSize().y;
8730 			}
8731 		}
8732 		if (caretHeight < graphicalLineHeight) {
8733 			location.y += (graphicalLineHeight - caretHeight);
8734 		}
8735 
8736 		int imageDirection = direction;
8737 		if (isMirrored()) {
8738 			if (imageDirection == SWT.LEFT) {
8739 				imageDirection = SWT.RIGHT;
8740 			} else if (imageDirection == SWT.RIGHT) {
8741 				imageDirection = SWT.LEFT;
8742 			}
8743 		}
8744 		if (isDefaultCaret && imageDirection == SWT.RIGHT) {
8745 			location.x -= (caret.getSize().x - 1);
8746 		}
8747 		if (isDefaultCaret) {
8748 			caret.setBounds(location.x, location.y, caretWidth, caretHeight);
8749 		} else {
8750 			caret.setLocation(location);
8751 		}
8752 		if (direction != caretDirection) {
8753 			caretDirection = direction;
8754 			if (isDefaultCaret) {
8755 				if (imageDirection == SWT.DEFAULT) {
8756 					defaultCaret.setImage(null);
8757 				} else if (imageDirection == SWT.LEFT) {
8758 					defaultCaret.setImage(leftCaretBitmap);
8759 				} else if (imageDirection == SWT.RIGHT) {
8760 					defaultCaret.setImage(rightCaretBitmap);
8761 				}
8762 			}
8763 			if (caretDirection == SWT.LEFT) {
8764 				BidiUtil.setKeyboardLanguage(BidiUtil.KEYBOARD_NON_BIDI);
8765 			} else if (caretDirection == SWT.RIGHT) {
8766 				BidiUtil.setKeyboardLanguage(BidiUtil.KEYBOARD_BIDI);
8767 			}
8768 		}
8769 		updateCaretVisibility();
8770 	}
8771 	columnX = location.x;
8772 }
8773 /**
8774  * Sets the caret offset.
8775  *
8776  * @param offset caret offset, relative to the first character in the text.
8777  * @exception SWTException <ul>
8778  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8779  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8780  * </ul>
8781  * @exception IllegalArgumentException <ul>
8782  *   <li>ERROR_INVALID_ARGUMENT when the offset is inside a multi byte line
8783  *   delimiter (and thus neither clearly in front of or after the line delimiter)
8784  * </ul>
8785  */
8786 public void setCaretOffset(int offset) {
8787 	checkWidget();
8788 	int length = getCharCount();
8789 	if (length > 0 && offset != caretOffset) {
8790 		if (offset < 0) {
8791 			offset = 0;
8792 		} else if (offset > length) {
8793 			offset = length;
8794 		} else {
8795 			if (isLineDelimiter(offset)) {
8796 				// offset is inside a multi byte line delimiter. This is an
8797 				// illegal operation and an exception is thrown. Fixes 1GDKK3R
8798 				SWT.error(SWT.ERROR_INVALID_ARGUMENT);
8799 			}
8800 		}
8801 		setCaretOffset(offset, PREVIOUS_OFFSET_TRAILING);
8802 		// clear the selection if the caret is moved.
8803 		// don't notify listeners about the selection change.
8804 		if (blockSelection) {
8805 			clearBlockSelection(true, false);
8806 		} else {
8807 			clearSelection(false);
8808 		}
8809 	}
8810 	setCaretLocation();
8811 }
8812 void setCaretOffset(int offset, int alignment) {
8813 	if (caretOffset != offset) {
8814 		caretOffset = offset;
8815 		if (isListening(ST.CaretMoved)) {
8816 			StyledTextEvent event = new StyledTextEvent(content);
8817 			event.end = caretOffset;
8818 			notifyListeners(ST.CaretMoved, event);
8819 		}
8820 	}
8821 	if (alignment != SWT.DEFAULT) {
8822 		caretAlignment = alignment;
8823 	}
8824 }
8825 /**
8826  * Copies the specified text range to the clipboard.  The text will be placed
8827  * in the clipboard in plain text format and RTF format.
8828  *
8829  * @param start start index of the text
8830  * @param length length of text to place in clipboard
8831  *
8832  * @exception SWTError
8833  * @see org.eclipse.swt.dnd.Clipboard#setContents
8834  */
8835 void setClipboardContent(int start, int length, int clipboardType) throws SWTError {
8836 	if (clipboardType == DND.SELECTION_CLIPBOARD && !IS_GTK) return;
8837 	TextTransfer plainTextTransfer = TextTransfer.getInstance();
8838 	TextWriter plainTextWriter = new TextWriter(start, length);
8839 	String plainText = getPlatformDelimitedText(plainTextWriter);
8840 	Object[] data;
8841 	Transfer[] types;
8842 	if (clipboardType == DND.SELECTION_CLIPBOARD) {
8843 		data = new Object[]{plainText};
8844 		types = new Transfer[]{plainTextTransfer};
8845 	} else {
8846 		RTFTransfer rtfTransfer = RTFTransfer.getInstance();
8847 		RTFWriter rtfWriter = new RTFWriter(start, length);
8848 		String rtfText = getPlatformDelimitedText(rtfWriter);
8849 		data = new Object[]{rtfText, plainText};
8850 		types = new Transfer[]{rtfTransfer, plainTextTransfer};
8851 	}
8852 	clipboard.setContents(data, types, clipboardType);
8853 }
8854 /**
8855  * Sets the content implementation to use for text storage.
8856  *
8857  * @param newContent StyledTextContent implementation to use for text storage.
8858  * @exception SWTException <ul>
8859  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8860  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8861  * </ul>
8862  * @exception IllegalArgumentException <ul>
8863  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
8864  * </ul>
8865  */
8866 public void setContent(StyledTextContent newContent) {
8867 	checkWidget();
8868 	if (newContent == null) {
8869 		SWT.error(SWT.ERROR_NULL_ARGUMENT);
8870 	}
8871 	if (content != null) {
8872 		content.removeTextChangeListener(textChangeListener);
8873 	}
8874 	content = newContent;
8875 	content.addTextChangeListener(textChangeListener);
8876 	reset();
8877 }
8878 /**
8879  * Sets the receiver's cursor to the cursor specified by the
8880  * argument.  Overridden to handle the null case since the
8881  * StyledText widget uses an ibeam as its default cursor.
8882  *
8883  * @see Control#setCursor(Cursor)
8884  */
8885 @Override
8886 public void setCursor (Cursor cursor) {
8887 	checkWidget();
8888 	if (cursor != null && cursor.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
8889 	this.cursor = cursor;
8890 	if (cursor == null) {
8891 		Display display = getDisplay();
8892 		int type = blockSelection ? SWT.CURSOR_CROSS : SWT.CURSOR_IBEAM;
8893 		super.setCursor(display.getSystemCursor(type));
8894 	} else {
8895 		super.setCursor(cursor);
8896 	}
8897 }
8898 /**
8899  * Sets whether the widget implements double click mouse behavior.
8900  *
8901  * @param enable if true double clicking a word selects the word, if false
8902  * 	double clicks have the same effect as regular mouse clicks.
8903  * @exception SWTException <ul>
8904  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8905  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8906  * </ul>
8907  */
8908 public void setDoubleClickEnabled(boolean enable) {
8909 	checkWidget();
8910 	doubleClickEnabled = enable;
8911 }
8912 @Override
8913 public void setDragDetect (boolean dragDetect) {
8914 	checkWidget ();
8915 	this.dragDetect = dragDetect;
8916 }
8917 /**
8918  * Sets whether the widget content can be edited.
8919  *
8920  * @param editable if true content can be edited, if false content can not be
8921  * 	edited
8922  * @exception SWTException <ul>
8923  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8924  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8925  * </ul>
8926  */
8927 public void setEditable(boolean editable) {
8928 	checkWidget();
8929 	this.editable = editable;
8930 }
8931 @Override
8932 public void setEnabled(boolean enabled) {
8933 	super.setEnabled(enabled);
8934 	Display display = getDisplay();
8935 	this.enabled = enabled;
8936 	this.insideSetEnableCall = true;
8937 	try {
8938 		if (enabled && editable) {
8939 			if (!customBackground) setBackground(display.getSystemColor(SWT.COLOR_LIST_BACKGROUND));
8940 			if (!customForeground) setForeground(display.getSystemColor(SWT.COLOR_LIST_FOREGROUND));
8941 		} else if(!enabled) {
8942 			if (!customBackground) setBackground(display.getSystemColor(SWT.COLOR_TEXT_DISABLED_BACKGROUND));
8943 			if (!customForeground) setForeground(display.getSystemColor(SWT.COLOR_WIDGET_DISABLED_FOREGROUND));
8944 		} else if(!editable) {
8945 			if (!customBackground) setBackground(display.getSystemColor(SWT.COLOR_TEXT_DISABLED_BACKGROUND));
8946 			if (!customForeground) setForeground(display.getSystemColor(SWT.COLOR_LIST_FOREGROUND));
8947 		}
8948 	}
8949 	finally {
8950 		this.insideSetEnableCall = false;
8951 	}
8952 }
8953 /**
8954  * Sets a new font to render text with.
8955  * <p>
8956  * <b>NOTE:</b> Italic fonts are not supported unless they have no overhang
8957  * and the same baseline as regular fonts.
8958  * </p>
8959  *
8960  * @param font new font
8961  * @exception SWTException <ul>
8962  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8963  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8964  * </ul>
8965  */
8966 @Override
8967 public void setFont(Font font) {
8968 	checkWidget();
8969 	int oldLineHeight = renderer.getLineHeight();
8970 	super.setFont(font);
8971 	renderer.setFont(getFont(), tabLength);
8972 	// keep the same top line visible. fixes 5815
8973 	if (isFixedLineHeight()) {
8974 		int lineHeight = renderer.getLineHeight();
8975 		if (lineHeight != oldLineHeight) {
8976 			int vscroll = (getVerticalScrollOffset() * lineHeight / oldLineHeight) - getVerticalScrollOffset();
8977 			scrollVertical(vscroll, true);
8978 		}
8979 	}
8980 	resetCache(0, content.getLineCount());
8981 	claimBottomFreeSpace();
8982 	calculateScrollBars();
8983 	if (isBidiCaret()) createCaretBitmaps();
8984 	caretDirection = SWT.NULL;
8985 	setCaretLocation();
8986 	super.redraw();
8987 }
8988 @Override
8989 public void setForeground(Color color) {
8990 	checkWidget();
8991 	boolean foregroundDisabled = false;
8992 	if (!this.enabled && color == null) {
8993 		if (foreground != null) {
8994 			Color disabledFg = getDisplay().getSystemColor(SWT.COLOR_WIDGET_DISABLED_FOREGROUND);
8995 			if (foreground.equals(disabledFg)) {
8996 				return;
8997 			} else {
8998 				color = new Color (disabledFg.getRGBA());
8999 				foregroundDisabled = true;
9000 			}
9001 		}
9002 	}
9003 	customForeground = color != null && !this.insideSetEnableCall && !foregroundDisabled;
9004 	foreground = color;
9005 	super.setForeground(color);
9006 	resetCache(0, content.getLineCount());
9007 	setCaretLocation();
9008 	super.redraw();
9009 }
9010 /**
9011  * Sets the horizontal scroll offset relative to the start of the line.
9012  * Do nothing if there is no text set.
9013  * <p>
9014  * <b>NOTE:</b> The horizontal index is reset to 0 when new text is set in the
9015  * widget.
9016  * </p>
9017  *
9018  * @param offset horizontal scroll offset relative to the start
9019  * 	of the line, measured in character increments starting at 0, if
9020  * 	equal to 0 the content is not scrolled, if &gt; 0 = the content is scrolled.
9021  * @exception SWTException <ul>
9022  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9023  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9024  * </ul>
9025  */
9026 public void setHorizontalIndex(int offset) {
9027 	checkWidget();
9028 	if (getCharCount() == 0) {
9029 		return;
9030 	}
9031 	if (offset < 0) {
9032 		offset = 0;
9033 	}
9034 	offset *= getHorizontalIncrement();
9035 	// allow any value if client area width is unknown or 0.
9036 	// offset will be checked in resize handler.
9037 	// don't use isVisible since width is known even if widget
9038 	// is temporarily invisible
9039 	if (clientAreaWidth > 0) {
9040 		int width = renderer.getWidth();
9041 		// prevent scrolling if the content fits in the client area.
9042 		// align end of longest line with right border of client area
9043 		// if offset is out of range.
9044 		if (offset > width - clientAreaWidth) {
9045 			offset = Math.max(0, width - clientAreaWidth);
9046 		}
9047 	}
9048 	scrollHorizontal(offset - horizontalScrollOffset, true);
9049 }
9050 /**
9051  * Sets the horizontal SWT logical point offset relative to the start of the line.
9052  * Do nothing if there is no text set.
9053  * <p>
9054  * <b>NOTE:</b> The horizontal SWT logical point offset is reset to 0 when new text
9055  * is set in the widget.
9056  * </p>
9057  *
9058  * @param pixel horizontal SWT logical point offset relative to the start
9059  * 	of the line.
9060  * @exception SWTException <ul>
9061  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9062  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9063  * </ul>
9064  * @since 2.0
9065  */
9066 public void setHorizontalPixel(int pixel) {
9067 	checkWidget();
9068 	if (getCharCount() == 0) {
9069 		return;
9070 	}
9071 	if (pixel < 0) {
9072 		pixel = 0;
9073 	}
9074 	// allow any value if client area width is unknown or 0.
9075 	// offset will be checked in resize handler.
9076 	// don't use isVisible since width is known even if widget
9077 	// is temporarily invisible
9078 	if (clientAreaWidth > 0) {
9079 		int width = renderer.getWidth();
9080 		// prevent scrolling if the content fits in the client area.
9081 		// align end of longest line with right border of client area
9082 		// if offset is out of range.
9083 		if (pixel > width - clientAreaWidth) {
9084 			pixel = Math.max(0, width - clientAreaWidth);
9085 		}
9086 	}
9087 	scrollHorizontal(pixel - horizontalScrollOffset, true);
9088 }
9089 /**
9090  * Sets the line indentation of the widget.
9091  * <p>
9092  * It is the amount of blank space, in points, at the beginning of each line.
9093  * When a line wraps in several lines only the first one is indented.
9094  * </p>
9095  *
9096  * @param indent the new indent
9097  *
9098  * @exception SWTException <ul>
9099  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9100  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9101  * </ul>
9102  *
9103  * @see #setLineIndent(int, int, int)
9104  *
9105  * @since 3.2
9106  */
9107 public void setIndent(int indent) {
9108 	checkWidget();
9109 	if (this.indent == indent || indent < 0) return;
9110 	this.indent = indent;
9111 	resetCache(0, content.getLineCount());
9112 	setCaretLocation();
9113 	super.redraw();
9114 }
9115 /**
9116  * Sets whether the widget should justify lines.
9117  *
9118  * @param justify whether lines should be justified
9119  *
9120  * @exception SWTException <ul>
9121  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9122  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9123  * </ul>
9124  *
9125  * @see #setLineJustify(int, int, boolean)
9126  *
9127  * @since 3.2
9128  */
9129 public void setJustify(boolean justify) {
9130 	checkWidget();
9131 	if (this.justify == justify) return;
9132 	this.justify = justify;
9133 	resetCache(0, content.getLineCount());
9134 	setCaretLocation();
9135 	super.redraw();
9136 }
9137 /**
9138  * Maps a key to an action.
9139  * <p>
9140  * One action can be associated with N keys. However, each key can only
9141  * have one action (key:action is N:1 relation).
9142  * </p>
9143  *
9144  * @param key a key code defined in SWT.java or a character.
9145  * 	Optionally ORd with a state mask.  Preferred state masks are one or more of
9146  *  SWT.MOD1, SWT.MOD2, SWT.MOD3, since these masks account for modifier platform
9147  *  differences.  However, there may be cases where using the specific state masks
9148  *  (i.e., SWT.CTRL, SWT.SHIFT, SWT.ALT, SWT.COMMAND) makes sense.
9149  * @param action one of the predefined actions defined in ST.java.
9150  * 	Use SWT.NULL to remove a key binding.
9151  * @exception SWTException <ul>
9152  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9153  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9154  * </ul>
9155  */
9156 public void setKeyBinding(int key, int action) {
9157 	checkWidget();
9158 	int modifierValue = key & SWT.MODIFIER_MASK;
9159 	int keyInt = key & SWT.KEY_MASK;
9160 	char keyChar = (char)keyInt;
9161 	/**
9162 	 * Bug 440535: Make sure the key getting mapped to letter is in defiened
9163 	 * character range and filter out incorrect int to char typecasting. For
9164 	 * Example: SWT.KEYPAD_CR int gets wrongly type-cast to char letter 'p'
9165 	 */
9166 	if (Character.isDefined(keyInt) && Character.isLetter(keyChar)) {
9167 		// make the keybinding case insensitive by adding it
9168 		// in its upper and lower case form
9169 		char ch = Character.toUpperCase(keyChar);
9170 		int newKey = ch | modifierValue;
9171 		if (action == SWT.NULL) {
9172 			keyActionMap.remove(newKey);
9173 		} else {
9174 			keyActionMap.put(newKey, action);
9175 		}
9176 		ch = Character.toLowerCase(keyChar);
9177 		newKey = ch | modifierValue;
9178 		if (action == SWT.NULL) {
9179 			keyActionMap.remove(newKey);
9180 		} else {
9181 			keyActionMap.put(newKey, action);
9182 		}
9183 	} else {
9184 		if (action == SWT.NULL) {
9185 			keyActionMap.remove(key);
9186 		} else {
9187 			keyActionMap.put(key, action);
9188 		}
9189 	}
9190 }
9191 /**
9192  * Sets the left margin.
9193  *
9194  * @param leftMargin the left margin.
9195  * @exception SWTException <ul>
9196  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9197  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9198  * </ul>
9199  *
9200  * @since 3.5
9201  */
9202 public void setLeftMargin (int leftMargin) {
9203 	checkWidget();
9204 	setMargins(leftMargin, topMargin, rightMargin, bottomMargin);
9205 }
9206 /**
9207  * Sets the alignment of the specified lines. The argument should be one of <code>SWT.LEFT</code>,
9208  * <code>SWT.CENTER</code> or <code>SWT.RIGHT</code>.
9209  * <p>
9210  * Note that if <code>SWT.MULTI</code> is set, then <code>SWT.WRAP</code> must also be set
9211  * in order to stabilize the right edge before setting alignment.
9212  * </p><p>
9213  * Should not be called if a LineStyleListener has been set since the listener
9214  * maintains the line attributes.
9215  * </p><p>
9216  * All line attributes are maintained relative to the line text, not the
9217  * line index that is specified in this method call.
9218  * During text changes, when entire lines are inserted or removed, the line
9219  * attributes that are associated with the lines after the change
9220  * will "move" with their respective text. An entire line is defined as
9221  * extending from the first character on a line to the last and including the
9222  * line delimiter.
9223  * </p>
9224  * When two lines are joined by deleting a line delimiter, the top line
9225  * attributes take precedence and the attributes of the bottom line are deleted.
9226  * For all other text changes line attributes will remain unchanged.
9227  *
9228  * @param startLine first line the alignment is applied to, 0 based
9229  * @param lineCount number of lines the alignment applies to.
9230  * @param alignment line alignment
9231  *
9232  * @exception SWTException <ul>
9233  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9234  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9235  * </ul>
9236  * @exception IllegalArgumentException <ul>
9237  *   <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
9238  * </ul>
9239  * @see #setAlignment(int)
9240  * @since 3.2
9241  */
9242 public void setLineAlignment(int startLine, int lineCount, int alignment) {
9243 	checkWidget();
9244 	if (isListening(ST.LineGetStyle)) return;
9245 	if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
9246 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9247 	}
9248 
9249 	renderer.setLineAlignment(startLine, lineCount, alignment);
9250 	resetCache(startLine, lineCount);
9251 	redrawLines(startLine, lineCount, false);
9252 	int caretLine = getCaretLine();
9253 	if (startLine <= caretLine && caretLine < startLine + lineCount) {
9254 		setCaretLocation();
9255 	}
9256 	setAlignment();
9257 }
9258 /**
9259  * Sets the background color of the specified lines.
9260  * <p>
9261  * The background color is drawn for the width of the widget. All
9262  * line background colors are discarded when setText is called.
9263  * The text background color if defined in a StyleRange overlays the
9264  * line background color.
9265  * </p><p>
9266  * Should not be called if a LineBackgroundListener has been set since the
9267  * listener maintains the line backgrounds.
9268  * </p><p>
9269  * All line attributes are maintained relative to the line text, not the
9270  * line index that is specified in this method call.
9271  * During text changes, when entire lines are inserted or removed, the line
9272  * attributes that are associated with the lines after the change
9273  * will "move" with their respective text. An entire line is defined as
9274  * extending from the first character on a line to the last and including the
9275  * line delimiter.
9276  * </p><p>
9277  * When two lines are joined by deleting a line delimiter, the top line
9278  * attributes take precedence and the attributes of the bottom line are deleted.
9279  * For all other text changes line attributes will remain unchanged.
9280  * </p>
9281  *
9282  * @param startLine first line the color is applied to, 0 based
9283  * @param lineCount number of lines the color applies to.
9284  * @param background line background color
9285  * @exception SWTException <ul>
9286  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9287  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9288  * </ul>
9289  * @exception IllegalArgumentException <ul>
9290  *   <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
9291  * </ul>
9292  */
9293 public void setLineBackground(int startLine, int lineCount, Color background) {
9294 	checkWidget();
9295 	if (isListening(ST.LineGetBackground)) return;
9296 	if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
9297 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9298 	}
9299 	if (background != null) {
9300 		renderer.setLineBackground(startLine, lineCount, background);
9301 	} else {
9302 		renderer.clearLineBackground(startLine, lineCount);
9303 	}
9304 	redrawLines(startLine, lineCount, false);
9305 }
9306 /**
9307  * Sets the bullet of the specified lines.
9308  * <p>
9309  * Should not be called if a LineStyleListener has been set since the listener
9310  * maintains the line attributes.
9311  * </p><p>
9312  * All line attributes are maintained relative to the line text, not the
9313  * line index that is specified in this method call.
9314  * During text changes, when entire lines are inserted or removed, the line
9315  * attributes that are associated with the lines after the change
9316  * will "move" with their respective text. An entire line is defined as
9317  * extending from the first character on a line to the last and including the
9318  * line delimiter.
9319  * </p><p>
9320  * When two lines are joined by deleting a line delimiter, the top line
9321  * attributes take precedence and the attributes of the bottom line are deleted.
9322  * For all other text changes line attributes will remain unchanged.
9323  * </p>
9324  *
9325  * @param startLine first line the bullet is applied to, 0 based
9326  * @param lineCount number of lines the bullet applies to.
9327  * @param bullet line bullet
9328  *
9329  * @exception SWTException <ul>
9330  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9331  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9332  * </ul>
9333  * @exception IllegalArgumentException <ul>
9334  *   <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
9335  * </ul>
9336  * @since 3.2
9337  */
9338 public void setLineBullet(int startLine, int lineCount, Bullet bullet) {
9339 	checkWidget();
9340 	if (isListening(ST.LineGetStyle)) return;
9341 	if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
9342 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9343 	}
9344 	int oldBottom = getLinePixel(startLine + lineCount);
9345 	renderer.setLineBullet(startLine, lineCount, bullet);
9346 	resetCache(startLine, lineCount);
9347 	int newBottom = getLinePixel(startLine + lineCount);
9348 	redrawLines(startLine, lineCount, oldBottom != newBottom);
9349 	int caretLine = getCaretLine();
9350 	if (startLine <= caretLine && caretLine < startLine + lineCount) {
9351 		setCaretLocation();
9352 	}
9353 }
9354 /**
9355  * Returns true if StyledText is in word wrap mode and false otherwise.
9356  *
9357  * @return true if StyledText is in word wrap mode and false otherwise.
9358  */
9359 boolean isWordWrap() {
9360 	return wordWrap || visualWrap;
9361 }
9362 /**
9363  * Sets the indent of the specified lines.
9364  * <p>
9365  * Should not be called if a LineStyleListener has been set since the listener
9366  * maintains the line attributes.
9367  * </p><p>
9368  * All line attributes are maintained relative to the line text, not the
9369  * line index that is specified in this method call.
9370  * During text changes, when entire lines are inserted or removed, the line
9371  * attributes that are associated with the lines after the change
9372  * will "move" with their respective text. An entire line is defined as
9373  * extending from the first character on a line to the last and including the
9374  * line delimiter.
9375  * </p><p>
9376  * When two lines are joined by deleting a line delimiter, the top line
9377  * attributes take precedence and the attributes of the bottom line are deleted.
9378  * For all other text changes line attributes will remain unchanged.
9379  * </p>
9380  *
9381  * @param startLine first line the indent is applied to, 0 based
9382  * @param lineCount number of lines the indent applies to.
9383  * @param indent line indent
9384  *
9385  * @exception SWTException <ul>
9386  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9387  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9388  * </ul>
9389  * @exception IllegalArgumentException <ul>
9390  *   <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
9391  * </ul>
9392  * @see #setIndent(int)
9393  * @since 3.2
9394  */
9395 public void setLineIndent(int startLine, int lineCount, int indent) {
9396 	checkWidget();
9397 	if (isListening(ST.LineGetStyle)) return;
9398 	if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
9399 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9400 	}
9401 	int oldBottom = getLinePixel(startLine + lineCount);
9402 	renderer.setLineIndent(startLine, lineCount, indent);
9403 	resetCache(startLine, lineCount);
9404 	int newBottom = getLinePixel(startLine + lineCount);
9405 	redrawLines(startLine, lineCount, oldBottom != newBottom);
9406 	int caretLine = getCaretLine();
9407 	if (startLine <= caretLine && caretLine < startLine + lineCount) {
9408 		setCaretLocation();
9409 	}
9410 }
9411 
9412 /**
9413  * Sets the vertical indent of the specified lines.
9414  * <p>
9415  * Should not be called if a LineStyleListener has been set since the listener
9416  * maintains the line attributes.
9417  * </p><p>
9418  * All line attributes are maintained relative to the line text, not the
9419  * line index that is specified in this method call.
9420  * During text changes, when entire lines are inserted or removed, the line
9421  * attributes that are associated with the lines after the change
9422  * will "move" with their respective text. An entire line is defined as
9423  * extending from the first character on a line to the last and including the
9424  * line delimiter.
9425  * </p><p>
9426  * When two lines are joined by deleting a line delimiter, the top line
9427  * attributes take precedence and the attributes of the bottom line are deleted.
9428  * For all other text changes line attributes will remain unchanged.
9429  * </p><p>
9430  * Setting both line spacing and vertical indent on a line would result in the
9431  * spacing and indent add up for the line.
9432  * </p>
9433  *
9434  * @param lineIndex line index the vertical indent is applied to, 0 based
9435  * @param verticalLineIndent vertical line indent
9436  *
9437  * @exception SWTException <ul>
9438  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9439  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9440  * </ul>
9441  * @exception IllegalArgumentException <ul>
9442  *   <li>ERROR_INVALID_ARGUMENT when the specified line index is invalid</li>
9443  * </ul>
9444  * @since 3.109
9445  */
9446 public void setLineVerticalIndent(int lineIndex, int verticalLineIndent) {
9447 	checkWidget();
9448 	if (isListening(ST.LineGetStyle)) return;
9449 	if (lineIndex < 0 || lineIndex >= content.getLineCount()) {
9450 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9451 	}
9452 	int previousVerticalIndent = renderer.getLineVerticalIndent(lineIndex);
9453 	if (verticalLineIndent == previousVerticalIndent) {
9454 		return;
9455 	}
9456 	int initialTopPixel = getTopPixel();
9457 	int initialTopIndex = getPartialTopIndex();
9458 	int initialBottomIndex = getPartialBottomIndex();
9459 	int verticalIndentDiff = verticalLineIndent - previousVerticalIndent;
9460 	renderer.setLineVerticalIndent(lineIndex, verticalLineIndent);
9461 	this.hasVerticalIndent = verticalLineIndent != 0 || renderer.hasVerticalIndent();
9462 	ScrollBar verticalScrollbar = getVerticalBar();
9463 	if (lineIndex < initialTopIndex) {
9464 		verticalScrollOffset += verticalIndentDiff; // just change value, don't actually scroll/redraw
9465 		if (verticalScrollbar != null) {
9466 			verticalScrollbar.setSelection(verticalScrollOffset);
9467 			verticalScrollbar.setMaximum(verticalScrollbar.getMaximum() + verticalIndentDiff);
9468 		}
9469 	} else if (lineIndex > initialBottomIndex) {
9470 		if (verticalScrollbar != null) {
9471 			verticalScrollbar.setMaximum(verticalScrollbar.getMaximum() + verticalIndentDiff);
9472 		}
9473 	} else {
9474 		resetCache(lineIndex, 1);
9475 		if (getCaretLine() >= initialTopIndex && getCaretLine() <= initialBottomIndex) { // caret line with caret mustn't move
9476 			if (getCaretLine() < lineIndex) {
9477 				redrawLines(lineIndex, getPartialBottomIndex() - lineIndex + 1, true);
9478 			} else {
9479 				setTopPixel(initialTopPixel + verticalIndentDiff);
9480 			}
9481 		} else { // move as few lines as possible
9482 			if (lineIndex - getTopIndex() < getBottomIndex() - lineIndex) {
9483 				setTopPixel(initialTopPixel + verticalIndentDiff);
9484 			} else {
9485 				// redraw below
9486 				redrawLines(lineIndex, getPartialBottomIndex() - lineIndex + 1, true);
9487 			}
9488 		}
9489 		setScrollBars(true);
9490 	}
9491 }
9492 
9493 /**
9494  * Sets the justify of the specified lines.
9495  * <p>
9496  * Should not be called if a LineStyleListener has been set since the listener
9497  * maintains the line attributes.
9498  * </p><p>
9499  * All line attributes are maintained relative to the line text, not the
9500  * line index that is specified in this method call.
9501  * During text changes, when entire lines are inserted or removed, the line
9502  * attributes that are associated with the lines after the change
9503  * will "move" with their respective text. An entire line is defined as
9504  * extending from the first character on a line to the last and including the
9505  * line delimiter.
9506  * </p><p>
9507  * When two lines are joined by deleting a line delimiter, the top line
9508  * attributes take precedence and the attributes of the bottom line are deleted.
9509  * For all other text changes line attributes will remain unchanged.
9510  * </p>
9511  *
9512  * @param startLine first line the justify is applied to, 0 based
9513  * @param lineCount number of lines the justify applies to.
9514  * @param justify true if lines should be justified
9515  *
9516  * @exception SWTException <ul>
9517  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9518  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9519  * </ul>
9520  * @exception IllegalArgumentException <ul>
9521  *   <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
9522  * </ul>
9523  * @see #setJustify(boolean)
9524  * @since 3.2
9525  */
9526 public void setLineJustify(int startLine, int lineCount, boolean justify) {
9527 	checkWidget();
9528 	if (isListening(ST.LineGetStyle)) return;
9529 	if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
9530 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9531 	}
9532 
9533 	renderer.setLineJustify(startLine, lineCount, justify);
9534 	resetCache(startLine, lineCount);
9535 	redrawLines(startLine, lineCount, false);
9536 	int caretLine = getCaretLine();
9537 	if (startLine <= caretLine && caretLine < startLine + lineCount) {
9538 		setCaretLocation();
9539 	}
9540 }
9541 /**
9542  * Sets the line spacing of the widget. The line spacing applies for all lines.
9543  * In the case of #setLineSpacingProvider(StyledTextLineSpacingProvider) is customized,
9544  * the line spacing are applied only for the lines which are not managed by {@link StyledTextLineSpacingProvider}.
9545  *
9546  * @param lineSpacing the line spacing
9547  * @exception SWTException <ul>
9548  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9549  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9550  * </ul>
9551  * @see #setLineSpacingProvider(StyledTextLineSpacingProvider)
9552  * @since 3.2
9553  */
9554 public void setLineSpacing(int lineSpacing) {
9555 	checkWidget();
9556 	if (this.lineSpacing == lineSpacing || lineSpacing < 0) return;
9557 	this.lineSpacing = lineSpacing;
9558 	resetCache(0, content.getLineCount());
9559 	setCaretLocation();
9560 	super.redraw();
9561 }
9562 /**
9563  * Sets the line spacing provider of the widget. The line spacing applies for some lines with customized spacing
9564  * or reset the customized spacing if the argument is null.
9565  *
9566  * @param lineSpacingProvider the line spacing provider (or null)
9567  * @exception SWTException <ul>
9568  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9569  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9570  * </ul>
9571  * @see #setLineSpacingProvider(StyledTextLineSpacingProvider)
9572  * @since 3.107
9573  */
9574 public void setLineSpacingProvider(StyledTextLineSpacingProvider lineSpacingProvider) {
9575 	checkWidget();
9576 	boolean wasFixedLineHeight = isFixedLineHeight();
9577 	if (renderer.getLineSpacingProvider() == null && lineSpacingProvider == null
9578 			|| (renderer.getLineSpacingProvider() != null
9579 					&& renderer.getLineSpacingProvider().equals(lineSpacingProvider)))
9580 		return;
9581 	renderer.setLineSpacingProvider(lineSpacingProvider);
9582 	// reset lines cache if needed
9583 	if (lineSpacingProvider == null) {
9584 		if (!wasFixedLineHeight) {
9585 			resetCache(0, content.getLineCount());
9586 		}
9587 	} else {
9588 		if (wasFixedLineHeight) {
9589 			int firstLine = -1;
9590 			for (int i = 0; i < content.getLineCount(); i++) {
9591 				Integer lineSpacing = lineSpacingProvider.getLineSpacing(i);
9592 				if (lineSpacing != null && lineSpacing > 0) {
9593 					// there is a custom line spacing, set StyledText as variable line height mode
9594 					// reset only the line size
9595 					renderer.reset(i, 1);
9596 					if (firstLine == -1) {
9597 						firstLine = i;
9598 					}
9599 				}
9600 			}
9601 			if (firstLine != -1) {
9602 				// call reset cache for the first line which have changed to recompute scrollbars
9603 				resetCache(firstLine, 0);
9604 			}
9605 		}
9606 	}
9607 	setCaretLocation();
9608 	super.redraw();
9609 }
9610 /**
9611  * Sets the tab stops of the specified lines.
9612  * <p>
9613  * Should not be called if a <code>LineStyleListener</code> has been set since the listener
9614  * maintains the line attributes.
9615  * </p><p>
9616  * All line attributes are maintained relative to the line text, not the
9617  * line index that is specified in this method call.
9618  * During text changes, when entire lines are inserted or removed, the line
9619  * attributes that are associated with the lines after the change
9620  * will "move" with their respective text. An entire line is defined as
9621  * extending from the first character on a line to the last and including the
9622  * line delimiter.
9623  * </p><p>
9624  * When two lines are joined by deleting a line delimiter, the top line
9625  * attributes take precedence and the attributes of the bottom line are deleted.
9626  * For all other text changes line attributes will remain unchanged.
9627  * </p>
9628  *
9629  * @param startLine first line the justify is applied to, 0 based
9630  * @param lineCount number of lines the justify applies to.
9631  * @param tabStops tab stops
9632  *
9633  * @exception SWTException <ul>
9634  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9635  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9636  * </ul>
9637  * @exception IllegalArgumentException <ul>
9638  *   <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
9639  * </ul>
9640  * @see #setTabStops(int[])
9641  * @since 3.6
9642  */
9643 public void setLineTabStops(int startLine, int lineCount, int[] tabStops) {
9644 	checkWidget();
9645 	if (isListening(ST.LineGetStyle)) return;
9646 	if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
9647 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9648 	}
9649 	if (tabStops != null) {
9650 		int pos = 0;
9651 		int[] newTabs = new int[tabStops.length];
9652 		for (int i = 0; i < tabStops.length; i++) {
9653 			if (tabStops[i] < pos) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9654 			newTabs[i] = pos = tabStops[i];
9655 		}
9656 		renderer.setLineTabStops(startLine, lineCount, newTabs);
9657 	} else {
9658 		renderer.setLineTabStops(startLine, lineCount, null);
9659 	}
9660 	resetCache(startLine, lineCount);
9661 	redrawLines(startLine, lineCount, false);
9662 	int caretLine = getCaretLine();
9663 	if (startLine <= caretLine && caretLine < startLine + lineCount) {
9664 		setCaretLocation();
9665 	}
9666 }
9667 /**
9668  * Sets the wrap indent of the specified lines.
9669  * <p>
9670  * Should not be called if a <code>LineStyleListener</code> has been set since the listener
9671  * maintains the line attributes.
9672  * </p><p>
9673  * All line attributes are maintained relative to the line text, not the
9674  * line index that is specified in this method call.
9675  * During text changes, when entire lines are inserted or removed, the line
9676  * attributes that are associated with the lines after the change
9677  * will "move" with their respective text. An entire line is defined as
9678  * extending from the first character on a line to the last and including the
9679  * line delimiter.
9680  * </p><p>
9681  * When two lines are joined by deleting a line delimiter, the top line
9682  * attributes take precedence and the attributes of the bottom line are deleted.
9683  * For all other text changes line attributes will remain unchanged.
9684  * </p>
9685  *
9686  * @param startLine first line the wrap indent is applied to, 0 based
9687  * @param lineCount number of lines the wrap indent applies to.
9688  * @param wrapIndent line wrap indent
9689  *
9690  * @exception SWTException <ul>
9691  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9692  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9693  * </ul>
9694  * @exception IllegalArgumentException <ul>
9695  *   <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
9696  * </ul>
9697  * @see #setWrapIndent(int)
9698  * @since 3.6
9699  */
9700 public void setLineWrapIndent(int startLine, int lineCount, int wrapIndent) {
9701 	checkWidget();
9702 	if (isListening(ST.LineGetStyle)) return;
9703 	if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
9704 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9705 	}
9706 	int oldBottom = getLinePixel(startLine + lineCount);
9707 	renderer.setLineWrapIndent(startLine, lineCount, wrapIndent);
9708 	resetCache(startLine, lineCount);
9709 	int newBottom = getLinePixel(startLine + lineCount);
9710 	redrawLines(startLine, lineCount, oldBottom != newBottom);
9711 	int caretLine = getCaretLine();
9712 	if (startLine <= caretLine && caretLine < startLine + lineCount) {
9713 		setCaretLocation();
9714 	}
9715 }
9716 
9717 /**
9718  * Sets the color of the margins.
9719  *
9720  * @param color the new color (or null)
9721  * @exception IllegalArgumentException <ul>
9722  *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
9723  * </ul>
9724  * @exception SWTException <ul>
9725  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9726  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9727  * </ul>
9728  *
9729  * @since 3.5
9730  */
9731 public void setMarginColor(Color color) {
9732 	checkWidget();
9733 	if (color != null && color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9734 	marginColor = color;
9735 	super.redraw();
9736 }
9737 /**
9738  * Sets the margins.
9739  *
9740  * @param leftMargin the left margin.
9741  * @param topMargin the top margin.
9742  * @param rightMargin the right margin.
9743  * @param bottomMargin the bottom margin.
9744  * @exception SWTException <ul>
9745  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9746  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9747  * </ul>
9748  *
9749  * @since 3.5
9750  */
9751 public void setMargins (int leftMargin, int topMargin, int rightMargin, int bottomMargin) {
9752 	checkWidget();
9753 	this.leftMargin = Math.max(0, leftMargin) + alignmentMargin;
9754 	this.topMargin = Math.max(0, topMargin);
9755 	this.rightMargin = Math.max(0, rightMargin);
9756 	this.bottomMargin = Math.max(0, bottomMargin);
9757 	resetCache(0, content.getLineCount());
9758 	setScrollBars(true);
9759 	setCaretLocation();
9760 	setAlignment();
9761 	super.redraw();
9762 }
9763 /**
9764  * Sets the enabled state of the mouse navigator. When the mouse navigator is enabled, the user can navigate through the widget
9765  * by pressing the middle button and moving the cursor.
9766  *
9767  * @param enabled if true, the mouse navigator is enabled, if false the mouse navigator is deactivated
9768  * @exception SWTException <ul>
9769  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9770  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9771  * </ul>
9772  * @since 3.110
9773  */
9774 public void setMouseNavigatorEnabled(boolean enabled) {
9775 	checkWidget();
9776 	if ((enabled && mouseNavigator != null) || (!enabled && mouseNavigator == null)) {
9777 		return;
9778 	}
9779 	if (enabled) {
9780 		mouseNavigator = new MouseNavigator(this);
9781 	} else {
9782 		mouseNavigator.dispose();
9783 		mouseNavigator = null;
9784 	}
9785 }
9786 /**
9787  * Flips selection anchor based on word selection direction.
9788  */
9789 void setMouseWordSelectionAnchor() {
9790 	if (doubleClickEnabled && clickCount > 1) {
9791 		if (caretOffset < doubleClickSelection.x) {
9792 			selectionAnchor = doubleClickSelection.y;
9793 		} else if (caretOffset > doubleClickSelection.y) {
9794 			selectionAnchor = doubleClickSelection.x;
9795 		}
9796 	}
9797 }
9798 /**
9799  * Sets the orientation of the receiver, which must be one
9800  * of the constants <code>SWT.LEFT_TO_RIGHT</code> or <code>SWT.RIGHT_TO_LEFT</code>.
9801  *
9802  * @param orientation new orientation style
9803  *
9804  * @exception SWTException <ul>
9805  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9806  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9807  * </ul>
9808  *
9809  * @since 2.1.2
9810  */
9811 @Override
9812 public void setOrientation(int orientation) {
9813 	int oldOrientation = getOrientation();
9814 	super.setOrientation(orientation);
9815 	int newOrientation = getOrientation();
9816 	if (oldOrientation != newOrientation) {
9817 		resetBidiData();
9818 	}
9819 }
9820 /**
9821  * Sets the right margin.
9822  *
9823  * @param rightMargin the right margin.
9824  * @exception SWTException <ul>
9825  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9826  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9827  * </ul>
9828  *
9829  * @since 3.5
9830  */
9831 public void setRightMargin (int rightMargin) {
9832 	checkWidget();
9833 	setMargins(getLeftMargin(), topMargin, rightMargin, bottomMargin);
9834 }
9835 void setScrollBar(ScrollBar bar, int clientArea, int maximum, int margin) {
9836 	int inactive = 1;
9837 	if (clientArea < maximum) {
9838 		bar.setMaximum(maximum - margin);
9839 		bar.setThumb(clientArea - margin);
9840 		bar.setPageIncrement(clientArea - margin);
9841 		if (!alwaysShowScroll) bar.setVisible(true);
9842 	} else if (bar.getThumb() != inactive || bar.getMaximum() != inactive) {
9843 		bar.setValues(bar.getSelection(), bar.getMinimum(), inactive, inactive, bar.getIncrement(), inactive);
9844 	}
9845 }
9846 /**
9847  * Adjusts the maximum and the page size of the scroll bars to
9848  * reflect content width/length changes.
9849  *
9850  * @param vertical indicates if the vertical scrollbar also needs to be set
9851  */
9852 void setScrollBars(boolean vertical) {
9853 	ignoreResize++;
9854 	if (!isFixedLineHeight() || !alwaysShowScroll) vertical = true;
9855 	ScrollBar verticalBar = vertical ? getVerticalBar() : null;
9856 	ScrollBar horizontalBar = getHorizontalBar();
9857 	int oldHeight = clientAreaHeight;
9858 	int oldWidth = clientAreaWidth;
9859 	if (!alwaysShowScroll) {
9860 		if (verticalBar != null) verticalBar.setVisible(false);
9861 		if (horizontalBar != null) horizontalBar.setVisible(false);
9862 	}
9863 	if (verticalBar != null) {
9864 		setScrollBar(verticalBar, clientAreaHeight, renderer.getHeight(), topMargin + bottomMargin);
9865 	}
9866 	if (horizontalBar != null && !wordWrap) {
9867 		setScrollBar(horizontalBar, clientAreaWidth, renderer.getWidth(), leftMargin + rightMargin);
9868 		if (!alwaysShowScroll && horizontalBar.getVisible() && verticalBar != null) {
9869 			setScrollBar(verticalBar, clientAreaHeight, renderer.getHeight(), topMargin + bottomMargin);
9870 			if (verticalBar.getVisible()) {
9871 				setScrollBar(horizontalBar, clientAreaWidth, renderer.getWidth(), leftMargin + rightMargin);
9872 			}
9873 		}
9874 	}
9875 	if (!alwaysShowScroll) {
9876 		redrawMargins(oldHeight, oldWidth);
9877 	}
9878 	ignoreResize--;
9879 }
9880 /**
9881  * Sets the selection to the given position and scrolls it into view.  Equivalent to setSelection(start,start).
9882  *
9883  * @param start new caret position
9884  * @see #setSelection(int,int)
9885  * @exception SWTException <ul>
9886  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9887  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9888  * </ul>
9889  * @exception IllegalArgumentException <ul>
9890  *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
9891  * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
9892  * </ul>
9893  */
9894 public void setSelection(int start) {
9895 	// checkWidget test done in setSelectionRange
9896 	setSelection(start, start);
9897 }
9898 /**
9899  * Sets the selection and scrolls it into view.
9900  * <p>
9901  * Indexing is zero based.  Text selections are specified in terms of
9902  * caret positions.  In a text widget that contains N characters, there are
9903  * N+1 caret positions, ranging from 0..N
9904  * </p>
9905  *
9906  * @param point x=selection start offset, y=selection end offset
9907  * 	The caret will be placed at the selection start when x &gt; y.
9908  * @see #setSelection(int,int)
9909  * @exception SWTException <ul>
9910  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9911  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9912  * </ul>
9913  * @exception IllegalArgumentException <ul>
9914  *   <li>ERROR_NULL_ARGUMENT when point is null</li>
9915  *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
9916  * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
9917  * </ul>
9918  */
9919 public void setSelection(Point point) {
9920 	checkWidget();
9921 	if (point == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
9922 	setSelection(point.x, point.y);
9923 }
9924 /**
9925  * Sets the receiver's selection background color to the color specified
9926  * by the argument, or to the default system color for the control
9927  * if the argument is null.
9928  *
9929  * @param color the new color (or null)
9930  *
9931  * @exception IllegalArgumentException <ul>
9932  *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
9933  * </ul>
9934  * @exception SWTException <ul>
9935  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9936  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9937  * </ul>
9938  * @since 2.1
9939  */
9940 public void setSelectionBackground (Color color) {
9941 	checkWidget ();
9942 	if (color != null) {
9943 		if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9944 	}
9945 	selectionBackground = color;
9946 	resetCache(0, content.getLineCount());
9947 	setCaretLocation();
9948 	super.redraw();
9949 }
9950 /**
9951  * Sets the receiver's selection foreground color to the color specified
9952  * by the argument, or to the default system color for the control
9953  * if the argument is null.
9954  * <p>
9955  * Note that this is a <em>HINT</em>. Some platforms do not allow the application
9956  * to change the selection foreground color.
9957  * </p>
9958  * @param color the new color (or null)
9959  *
9960  * @exception IllegalArgumentException <ul>
9961  *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
9962  * </ul>
9963  * @exception SWTException <ul>
9964  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9965  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9966  * </ul>
9967  * @since 2.1
9968  */
9969 public void setSelectionForeground (Color color) {
9970 	checkWidget ();
9971 	if (color != null) {
9972 		if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9973 	}
9974 	selectionForeground = color;
9975 	resetCache(0, content.getLineCount());
9976 	setCaretLocation();
9977 	super.redraw();
9978 }
9979 /**
9980  * Sets the selection and scrolls it into view.
9981  * <p>
9982  * Indexing is zero based.  Text selections are specified in terms of
9983  * caret positions.  In a text widget that contains N characters, there are
9984  * N+1 caret positions, ranging from 0..N
9985  * </p>
9986  *
9987  * @param start selection start offset. The caret will be placed at the
9988  * 	selection start when start &gt; end.
9989  * @param end selection end offset
9990  * @see #setSelectionRange(int,int)
9991  * @exception SWTException <ul>
9992  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9993  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9994  * </ul>
9995  * @exception IllegalArgumentException <ul>
9996  *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
9997  * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
9998  * </ul>
9999  */
10000 public void setSelection(int start, int end) {
10001 	setSelectionRange(start, end - start);
10002 	showSelection();
10003 }
10004 /**
10005  * Sets the selection.
10006  * <p>
10007  * The new selection may not be visible. Call showSelection to scroll
10008  * the selection into view.
10009  * </p>
10010  *
10011  * @param start offset of the first selected character, start &gt;= 0 must be true.
10012  * @param length number of characters to select, 0 &lt;= start + length
10013  * 	&lt;= getCharCount() must be true.
10014  * 	A negative length places the caret at the selection start.
10015  * @param sendEvent a Selection event is sent when set to true and when
10016  * 	the selection is reset.
10017  */
10018 void setSelection(int start, int length, boolean sendEvent, boolean doBlock) {
10019 	int end = start + length;
10020 	if (start > end) {
10021 		int temp = end;
10022 		end = start;
10023 		start = temp;
10024 	}
10025 	// is the selection range different or is the selection direction
10026 	// different?
10027 	if (selection.x != start || selection.y != end ||
10028 		(length > 0 && selectionAnchor != selection.x) ||
10029 		(length < 0 && selectionAnchor != selection.y)) {
10030 		if (blockSelection && doBlock) {
10031 			if (length < 0) {
10032 				setBlockSelectionOffset(end, start, sendEvent);
10033 			} else {
10034 				setBlockSelectionOffset(start, end, sendEvent);
10035 			}
10036 		} else {
10037 			int oldStart = selection.x;
10038 			int oldLength = selection.y - selection.x;
10039 			int charCount = content.getCharCount();
10040 			// called internally to remove selection after text is removed
10041 			// therefore make sure redraw range is valid.
10042 			int redrawX = Math.min(selection.x, charCount);
10043 			int redrawY = Math.min(selection.y, charCount);
10044 			if (length < 0) {
10045 				selectionAnchor = selection.y = end;
10046 				selection.x = start;
10047 				setCaretOffset(start, PREVIOUS_OFFSET_TRAILING);
10048 			} else {
10049 				selectionAnchor = selection.x = start;
10050 				selection.y = end;
10051 				setCaretOffset(end, PREVIOUS_OFFSET_TRAILING);
10052 			}
10053 			redrawX = Math.min(redrawX, selection.x);
10054 			redrawY = Math.max(redrawY, selection.y);
10055 			if (redrawY - redrawX > 0) {
10056 				internalRedrawRange(redrawX, redrawY - redrawX);
10057 			}
10058 			if (sendEvent && (oldLength != end - start || (oldLength != 0 && oldStart != start))) {
10059 				sendSelectionEvent();
10060 			}
10061 			sendAccessibleTextCaretMoved();
10062 		}
10063 	}
10064 }
10065 /**
10066  * Sets the selection.
10067  * <p>
10068  * The new selection may not be visible. Call showSelection to scroll the selection
10069  * into view. A negative length places the caret at the visual start of the selection.
10070  * </p>
10071  *
10072  * @param start offset of the first selected character
10073  * @param length number of characters to select
10074  *
10075  * @exception SWTException <ul>
10076  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10077  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10078  * </ul>
10079  * @exception IllegalArgumentException <ul>
10080  *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
10081  * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
10082  * </ul>
10083  */
10084 public void setSelectionRange(int start, int length) {
10085 	checkWidget();
10086 	int contentLength = getCharCount();
10087 	start = Math.max(0, Math.min (start, contentLength));
10088 	int end = start + length;
10089 	if (end < 0) {
10090 		length = -start;
10091 	} else {
10092 		if (end > contentLength) length = contentLength - start;
10093 	}
10094 	if (isLineDelimiter(start) || isLineDelimiter(start + length)) {
10095 		// the start offset or end offset of the selection range is inside a
10096 		// multi byte line delimiter. This is an illegal operation and an exception
10097 		// is thrown. Fixes 1GDKK3R
10098 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
10099 	}
10100 	setSelection(start, length, false, true);
10101 	setCaretLocation();
10102 }
10103 /**
10104  * Adds the specified style.
10105  * <p>
10106  * The new style overwrites existing styles for the specified range.
10107  * Existing style ranges are adjusted if they partially overlap with
10108  * the new style. To clear an individual style, call setStyleRange
10109  * with a StyleRange that has null attributes.
10110  * </p><p>
10111  * Should not be called if a LineStyleListener has been set since the
10112  * listener maintains the styles.
10113  * </p>
10114  *
10115  * @param range StyleRange object containing the style information.
10116  * Overwrites the old style in the given range. May be null to delete
10117  * all styles.
10118  * @exception SWTException <ul>
10119  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10120  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10121  * </ul>
10122  * @exception IllegalArgumentException <ul>
10123  *   <li>ERROR_INVALID_RANGE when the style range is outside the valid range (&gt; getCharCount())</li>
10124  * </ul>
10125  */
10126 public void setStyleRange(StyleRange range) {
10127 	checkWidget();
10128 	if (isListening(ST.LineGetStyle)) return;
10129 	if (range != null) {
10130 		if (range.isUnstyled()) {
10131 			setStyleRanges(range.start, range.length, null, null, false);
10132 		} else {
10133 			setStyleRanges(range.start, 0, null, new StyleRange[]{range}, false);
10134 		}
10135 	} else {
10136 		setStyleRanges(0, 0, null, null, true);
10137 	}
10138 }
10139 /**
10140  * Clears the styles in the range specified by <code>start</code> and
10141  * <code>length</code> and adds the new styles.
10142  * <p>
10143  * The ranges array contains start and length pairs.  Each pair refers to
10144  * the corresponding style in the styles array.  For example, the pair
10145  * that starts at ranges[n] with length ranges[n+1] uses the style
10146  * at styles[n/2].  The range fields within each StyleRange are ignored.
10147  * If ranges or styles is null, the specified range is cleared.
10148  * </p><p>
10149  * Note: It is expected that the same instance of a StyleRange will occur
10150  * multiple times within the styles array, reducing memory usage.
10151  * </p><p>
10152  * Should not be called if a LineStyleListener has been set since the
10153  * listener maintains the styles.
10154  * </p>
10155  *
10156  * @param start offset of first character where styles will be deleted
10157  * @param length length of the range to delete styles in
10158  * @param ranges the array of ranges.  The ranges must not overlap and must be in order.
10159  * @param styles the array of StyleRanges.  The range fields within the StyleRange are unused.
10160  *
10161  * @exception SWTException <ul>
10162  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10163  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10164  * </ul>
10165  * @exception IllegalArgumentException <ul>
10166  *    <li>ERROR_NULL_ARGUMENT when an element in the styles array is null</li>
10167  *    <li>ERROR_INVALID_RANGE when the number of ranges and style do not match (ranges.length * 2 == styles.length)</li>
10168  *    <li>ERROR_INVALID_RANGE when a range is outside the valid range (&gt; getCharCount() or less than zero)</li>
10169  *    <li>ERROR_INVALID_RANGE when a range overlaps</li>
10170  * </ul>
10171  *
10172  * @since 3.2
10173  */
10174 public void setStyleRanges(int start, int length, int[] ranges, StyleRange[] styles) {
10175 	checkWidget();
10176 	if (isListening(ST.LineGetStyle)) return;
10177 	if (ranges == null || styles == null) {
10178 		setStyleRanges(start, length, null, null, false);
10179 	} else {
10180 		setStyleRanges(start, length, ranges, styles, false);
10181 	}
10182 }
10183 /**
10184  * Sets styles to be used for rendering the widget content.
10185  * <p>
10186  * All styles in the widget will be replaced with the given set of ranges and styles.
10187  * The ranges array contains start and length pairs.  Each pair refers to
10188  * the corresponding style in the styles array.  For example, the pair
10189  * that starts at ranges[n] with length ranges[n+1] uses the style
10190  * at styles[n/2].  The range fields within each StyleRange are ignored.
10191  * If either argument is null, the styles are cleared.
10192  * </p><p>
10193  * Note: It is expected that the same instance of a StyleRange will occur
10194  * multiple times within the styles array, reducing memory usage.
10195  * </p><p>
10196  * Should not be called if a LineStyleListener has been set since the
10197  * listener maintains the styles.
10198  * </p>
10199  *
10200  * @param ranges the array of ranges.  The ranges must not overlap and must be in order.
10201  * @param styles the array of StyleRanges.  The range fields within the StyleRange are unused.
10202  *
10203  * @exception SWTException <ul>
10204  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10205  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10206  * </ul>
10207  * @exception IllegalArgumentException <ul>
10208  *    <li>ERROR_NULL_ARGUMENT when an element in the styles array is null</li>
10209  *    <li>ERROR_INVALID_RANGE when the number of ranges and style do not match (ranges.length * 2 == styles.length)</li>
10210  *    <li>ERROR_INVALID_RANGE when a range is outside the valid range (&gt; getCharCount() or less than zero)</li>
10211  *    <li>ERROR_INVALID_RANGE when a range overlaps</li>
10212  * </ul>
10213  *
10214  * @since 3.2
10215  */
10216 public void setStyleRanges(int[] ranges, StyleRange[] styles) {
10217 	checkWidget();
10218 	if (isListening(ST.LineGetStyle)) return;
10219 	if (ranges == null || styles == null) {
10220 		setStyleRanges(0, 0, null, null, true);
10221 	} else {
10222 		setStyleRanges(0, 0, ranges, styles, true);
10223 	}
10224 }
10225 void setStyleRanges(int start, int length, int[] ranges, StyleRange[] styles, boolean reset) {
10226 	int charCount = content.getCharCount();
10227 	if (reset) {
10228 		start = 0;
10229 		length = charCount;
10230 	}
10231 	int[] formerRanges = getRanges(start, length);
10232 	StyleRange[] formerStyles = getStyleRanges(start, length);
10233 	int end = start + length;
10234 	final boolean wasFixedLineHeight = isFixedLineHeight();
10235 	if (start > end || start < 0) {
10236 		SWT.error(SWT.ERROR_INVALID_RANGE);
10237 	}
10238 	if (styles != null) {
10239 		if (end > charCount) {
10240 			SWT.error(SWT.ERROR_INVALID_RANGE);
10241 		}
10242 		if (ranges != null) {
10243 			if (ranges.length != styles.length << 1) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
10244 		}
10245 		int lastOffset = 0;
10246 		for (int i = 0; i < styles.length; i ++) {
10247 			if (styles[i] == null) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
10248 			int rangeStart, rangeLength;
10249 			if (ranges != null) {
10250 				rangeStart = ranges[i << 1];
10251 				rangeLength = ranges[(i << 1) + 1];
10252 			} else {
10253 				rangeStart = styles[i].start;
10254 				rangeLength = styles[i].length;
10255 			}
10256 			if (rangeLength < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
10257 			if (!(0 <= rangeStart && rangeStart + rangeLength <= charCount)) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
10258 			if (lastOffset > rangeStart) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
10259 			hasStyleWithVariableHeight |= styles[i].isVariableHeight();
10260 			lastOffset = rangeStart + rangeLength;
10261 		}
10262 	}
10263 	int rangeStart = start, rangeEnd = end;
10264 	if (styles != null && styles.length > 0) {
10265 		if (ranges != null) {
10266 			rangeStart = ranges[0];
10267 			rangeEnd = ranges[ranges.length - 2] + ranges[ranges.length - 1];
10268 		} else {
10269 			rangeStart = styles[0].start;
10270 			rangeEnd = styles[styles.length - 1].start + styles[styles.length - 1].length;
10271 		}
10272 	}
10273 
10274 	// This needs to happen before new styles are applied
10275 	int expectedBottom = 0;
10276 	if (!isFixedLineHeight() && !reset) {
10277 		int lineEnd = content.getLineAtOffset(Math.max(end, rangeEnd));
10278 		int partialTopIndex = getPartialTopIndex();
10279 		int partialBottomIndex = getPartialBottomIndex();
10280 		if (partialTopIndex <= lineEnd && lineEnd <= partialBottomIndex) {
10281 			expectedBottom = getLinePixel(lineEnd + 1);
10282 		}
10283 	}
10284 	if (reset) {
10285 		renderer.setStyleRanges(null, null);
10286 	} else {
10287 		renderer.updateRanges(start, length, length);
10288 	}
10289 	if (styles != null && styles.length > 0) {
10290 		renderer.setStyleRanges(ranges, styles);
10291 	}
10292 
10293 	// re-evaluate variable height with all styles (including new ones)
10294 	hasStyleWithVariableHeight = false;
10295 	for (StyleRange style : getStyleRanges(false)) {
10296 		hasStyleWithVariableHeight = style.isVariableHeight();
10297 		if (hasStyleWithVariableHeight) break;
10298 	}
10299 
10300 	SortedSet<Integer> modifiedLines = computeModifiedLines(formerRanges, formerStyles, ranges, styles);
10301 	resetCache(modifiedLines);
10302 	if (reset) {
10303 		super.redraw();
10304 	} else {
10305 		int lineStart = content.getLineAtOffset(Math.min(start, rangeStart));
10306 		int lineEnd = content.getLineAtOffset(Math.max(end, rangeEnd));
10307 		int partialTopIndex = getPartialTopIndex();
10308 		int partialBottomIndex = getPartialBottomIndex();
10309 		if (!(lineStart > partialBottomIndex || lineEnd < partialTopIndex)) {
10310 			int top = 0;
10311 			int bottom = clientAreaHeight;
10312 			if (partialTopIndex <= lineStart && lineStart <= partialBottomIndex) {
10313 				top = Math.max(0, getLinePixel(lineStart));
10314 			}
10315 			if (partialTopIndex <= lineEnd && lineEnd <= partialBottomIndex) {
10316 				bottom = getLinePixel(lineEnd + 1);
10317 			}
10318 			if (!(wasFixedLineHeight && isFixedLineHeight()) && bottom != expectedBottom) {
10319 				bottom = clientAreaHeight;
10320 			}
10321 			super.redraw(0, top, clientAreaWidth, bottom - top, false);
10322 		}
10323 	}
10324 	int oldColumnX = columnX;
10325 	setCaretLocation();
10326 	columnX = oldColumnX;
10327 	doMouseLinkCursor();
10328 }
10329 
10330 /**
10331  *
10332  * @param referenceRanges former ranges, sorted by order and without overlapping, typically returned {@link #getRanges(int, int)}
10333  * @param referenceStyles
10334  * @param newRanges former ranges, sorted by order and without overlapping
10335  * @param newStyles
10336  * @return
10337  */
10338 private SortedSet<Integer> computeModifiedLines(int[] referenceRanges, StyleRange[] referenceStyles, int[] newRanges, StyleRange[] newStyles) {
10339 	if (referenceStyles == null) {
10340 		referenceStyles = new StyleRange[0];
10341 	}
10342 	if (referenceRanges == null) {
10343 		referenceRanges = createRanges(referenceStyles);
10344 	}
10345 	if (newStyles == null) {
10346 		newStyles = new StyleRange[0];
10347 	}
10348 	if (newRanges == null) {
10349 		newRanges = createRanges(newStyles);
10350 	}
10351 	if (referenceRanges.length != 2 * referenceStyles.length) {
10352 		throw new IllegalArgumentException();
10353 	}
10354 	if (newRanges.length != 2 * newStyles.length) {
10355 		throw new IllegalArgumentException();
10356 	}
10357 	SortedSet<Integer> res = new TreeSet<>();
10358 	int referenceRangeIndex = 0;
10359 	int newRangeIndex = 0;
10360 	StyleRange defaultStyle = new StyleRange();
10361 	defaultStyle.foreground = this.foreground;
10362 	defaultStyle.background = this.background;
10363 	defaultStyle.font = getFont();
10364 	int currentOffset = referenceRanges.length > 0 ? referenceRanges[0] : Integer.MAX_VALUE;
10365 	if (newRanges.length > 0) {
10366 		currentOffset = Math.min(currentOffset, newRanges[0]);
10367 	}
10368 	while (currentOffset < content.getCharCount() && (referenceRangeIndex < referenceStyles.length || newRangeIndex < newRanges.length)) {
10369 		int nextMilestoneOffset = Integer.MAX_VALUE; // next new range start/end after current offset
10370 
10371 		while (referenceRangeIndex < referenceStyles.length && endRangeOffset(referenceRanges, referenceRangeIndex) <= currentOffset) {
10372 			referenceRangeIndex++;
10373 		}
10374 		StyleRange referenceStyleAtCurrentOffset = defaultStyle;
10375 		if (isInRange(referenceRanges, referenceRangeIndex, currentOffset)) { // has styling
10376 			referenceStyleAtCurrentOffset = referenceStyles[referenceRangeIndex];
10377 			nextMilestoneOffset =  endRangeOffset(referenceRanges, referenceRangeIndex);
10378 		} else if (referenceRangeIndex < referenceStyles.length) { // no range, default styling
10379 			nextMilestoneOffset = referenceRanges[2 * referenceRangeIndex]; // beginning of next range
10380 		}
10381 
10382 		while (newRangeIndex < newStyles.length && endRangeOffset(newRanges, newRangeIndex) <= currentOffset) {
10383 			newRangeIndex++;
10384 		}
10385 		StyleRange newStyleAtCurrentOffset = defaultStyle;
10386 		if (isInRange(newRanges, newRangeIndex, currentOffset)) {
10387 			newStyleAtCurrentOffset = newStyles[newRangeIndex];
10388 			nextMilestoneOffset = Math.min(nextMilestoneOffset, endRangeOffset(newRanges, newRangeIndex));
10389 		} else if (newRangeIndex < newStyles.length) {
10390 			nextMilestoneOffset = Math.min(nextMilestoneOffset, newRanges[2 * newRangeIndex]);
10391 		}
10392 
10393 		if (!referenceStyleAtCurrentOffset.similarTo(newStyleAtCurrentOffset)) {
10394 			int fromLine = getLineAtOffset(currentOffset);
10395 			int toLine = getLineAtOffset(nextMilestoneOffset - 1);
10396 			for (int line = fromLine; line <= toLine; line++) {
10397 				res.add(line);
10398 			}
10399 			currentOffset = toLine + 1 < getLineCount() ? getOffsetAtLine(toLine + 1) : content.getCharCount();
10400 		} else {
10401 			currentOffset = nextMilestoneOffset;
10402 		}
10403 	}
10404 	return res;
10405 }
10406 private int[] createRanges(StyleRange[] referenceStyles) {
10407 	int[] referenceRanges;
10408 	referenceRanges = new int[2 * referenceStyles.length];
10409 	for (int i = 0; i < referenceStyles.length; i++) {
10410 		referenceRanges[2 * i] = referenceStyles[i].start;
10411 		referenceRanges[2 * i + 1] = referenceStyles[i].length;
10412 	}
10413 	return referenceRanges;
10414 }
10415 
10416 private boolean isInRange(int[] ranges, int styleIndex, int offset) {
10417 	if (ranges == null || ranges.length == 0 || styleIndex < 0 || 2 * styleIndex + 1 > ranges.length) {
10418 		return false;
10419 	}
10420 	int start = ranges[2 * styleIndex];
10421 	int length = ranges[2 * styleIndex + 1];
10422 	return offset >= start && offset < start + length;
10423 }
10424 
10425 /**
10426  * The offset on which the range ends (excluded)
10427  * @param ranges
10428  * @param styleIndex
10429  * @return
10430  */
10431 private int endRangeOffset(int[] ranges, int styleIndex) {
10432 	if (styleIndex < 0 || 2 * styleIndex > ranges.length) {
10433 		throw new IllegalArgumentException();
10434 	}
10435 	int start = ranges[2 * styleIndex];
10436 	int length = ranges[2 * styleIndex + 1];
10437 	return start + length;
10438 }
10439 
10440 /**
10441  * Sets styles to be used for rendering the widget content. All styles
10442  * in the widget will be replaced with the given set of styles.
10443  * <p>
10444  * Note: Because a StyleRange includes the start and length, the
10445  * same instance cannot occur multiple times in the array of styles.
10446  * If the same style attributes, such as font and color, occur in
10447  * multiple StyleRanges, <code>setStyleRanges(int[], StyleRange[])</code>
10448  * can be used to share styles and reduce memory usage.
10449  * </p><p>
10450  * Should not be called if a LineStyleListener has been set since the
10451  * listener maintains the styles.
10452  * </p>
10453  *
10454  * @param ranges StyleRange objects containing the style information.
10455  * The ranges should not overlap. The style rendering is undefined if
10456  * the ranges do overlap. Must not be null. The styles need to be in order.
10457  * @exception SWTException <ul>
10458  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10459  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10460  * </ul>
10461  * @exception IllegalArgumentException <ul>
10462  *    <li>ERROR_NULL_ARGUMENT when the list of ranges is null</li>
10463  *    <li>ERROR_INVALID_RANGE when the last of the style ranges is outside the valid range (&gt; getCharCount())</li>
10464  * </ul>
10465  *
10466  * @see #setStyleRanges(int[], StyleRange[])
10467  */
10468 public void setStyleRanges(StyleRange[] ranges) {
10469 	checkWidget();
10470 	if (isListening(ST.LineGetStyle)) return;
10471 	if (ranges == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
10472 	setStyleRanges(0, 0, null, ranges, true);
10473 }
10474 /**
10475  * Sets the tab width.
10476  *
10477  * @param tabs tab width measured in characters.
10478  * @exception SWTException <ul>
10479  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10480  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10481  * </ul>
10482  *
10483  * @see #setTabStops(int[])
10484  */
10485 public void setTabs(int tabs) {
10486 	checkWidget();
10487 	tabLength = tabs;
10488 	renderer.setFont(null, tabs);
10489 	resetCache(0, content.getLineCount());
10490 	setCaretLocation();
10491 	super.redraw();
10492 }
10493 
10494 /**
10495  * Sets the receiver's tab list. Each value in the tab list specifies
10496  * the space in points from the origin of the document to the respective
10497  * tab stop.  The last tab stop width is repeated continuously.
10498  *
10499  * @param tabs the new tab list (or null)
10500  *
10501  * @exception SWTException <ul>
10502  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10503  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10504  * </ul>
10505  * @exception IllegalArgumentException <ul>
10506  *    <li>ERROR_INVALID_ARGUMENT - if a tab stop is negative or less than the previous stop in the list</li>
10507  * </ul>
10508  *
10509  * @see StyledText#getTabStops()
10510  *
10511  * @since 3.6
10512  */
10513 public void setTabStops(int [] tabs) {
10514 	checkWidget();
10515 	if (tabs != null) {
10516 		int pos = 0;
10517 		int[] newTabs = new int[tabs.length];
10518 		for (int i = 0; i < tabs.length; i++) {
10519 			if (tabs[i] < pos) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
10520 			newTabs[i] = pos = tabs[i];
10521 		}
10522 		this.tabs = newTabs;
10523 	} else {
10524 		this.tabs = null;
10525 	}
10526 	resetCache(0, content.getLineCount());
10527 	setCaretLocation();
10528 	super.redraw();
10529 }
10530 
10531 /**
10532  * Sets the widget content.
10533  * If the widget has the SWT.SINGLE style and "text" contains more than
10534  * one line, only the first line is rendered but the text is stored
10535  * unchanged. A subsequent call to getText will return the same text
10536  * that was set.
10537  * <p>
10538  * <b>Note:</b> Only a single line of text should be set when the SWT.SINGLE
10539  * style is used.
10540  * </p>
10541  *
10542  * @param text new widget content. Replaces existing content. Line styles
10543  * 	that were set using StyledText API are discarded.  The
10544  * 	current selection is also discarded.
10545  * @exception SWTException <ul>
10546  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10547  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10548  * </ul>
10549  * @exception IllegalArgumentException <ul>
10550  *    <li>ERROR_NULL_ARGUMENT when string is null</li>
10551  * </ul>
10552  */
10553 public void setText(String text) {
10554 	checkWidget();
10555 	if (text == null) {
10556 		SWT.error(SWT.ERROR_NULL_ARGUMENT);
10557 	}
10558 	Event event = new Event();
10559 	event.start = 0;
10560 	event.end = getCharCount();
10561 	event.text = text;
10562 	event.doit = true;
10563 	notifyListeners(SWT.Verify, event);
10564 	if (event.doit) {
10565 		StyledTextEvent styledTextEvent = null;
10566 		if (isListening(ST.ExtendedModify)) {
10567 			styledTextEvent = new StyledTextEvent(content);
10568 			styledTextEvent.start = event.start;
10569 			styledTextEvent.end = event.start + event.text.length();
10570 			styledTextEvent.text = content.getTextRange(event.start, event.end - event.start);
10571 		}
10572 		content.setText(event.text);
10573 		notifyListeners(SWT.Modify, event);
10574 		if (styledTextEvent != null) {
10575 			notifyListeners(ST.ExtendedModify, styledTextEvent);
10576 		}
10577 	}
10578 }
10579 
10580 /**
10581  * Sets the base text direction (a.k.a. "paragraph direction") of the receiver,
10582  * which must be one of the constants <code>SWT.LEFT_TO_RIGHT</code> or
10583  * <code>SWT.RIGHT_TO_LEFT</code>.
10584  * <p>
10585  * <code>setOrientation</code> would override this value with the text direction
10586  * that is consistent with the new orientation.
10587  * </p>
10588  * <p>
10589  * <b>Warning</b>: This API is currently only implemented on Windows.
10590  * It doesn't set the base text direction on GTK and Cocoa.
10591  * </p>
10592  *
10593  * @param textDirection the base text direction style
10594  *
10595  * @exception SWTException <ul>
10596  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10597  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10598  * </ul>
10599  *
10600  * @see SWT#FLIP_TEXT_DIRECTION
10601  */
10602 @Override
10603 public void setTextDirection(int textDirection) {
10604 	checkWidget();
10605 	int oldStyle = getStyle();
10606 	super.setTextDirection(textDirection);
10607 	if (isAutoDirection () || oldStyle != getStyle()) {
10608 		resetBidiData();
10609 	}
10610 }
10611 
10612 /**
10613  * Sets the text limit to the specified number of characters.
10614  * <p>
10615  * The text limit specifies the amount of text that
10616  * the user can type into the widget.
10617  * </p>
10618  *
10619  * @param limit the new text limit.
10620  * @exception SWTException <ul>
10621  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10622  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10623  * </ul>
10624  * @exception IllegalArgumentException <ul>
10625  *   <li>ERROR_CANNOT_BE_ZERO when limit is 0</li>
10626  * </ul>
10627  */
10628 public void setTextLimit(int limit) {
10629 	checkWidget();
10630 	if (limit == 0) {
10631 		SWT.error(SWT.ERROR_CANNOT_BE_ZERO);
10632 	}
10633 	textLimit = limit;
10634 }
10635 /**
10636  * Sets the top index. Do nothing if there is no text set.
10637  * <p>
10638  * The top index is the index of the line that is currently at the top
10639  * of the widget. The top index changes when the widget is scrolled.
10640  * Indexing starts from zero.
10641  * Note: The top index is reset to 0 when new text is set in the widget.
10642  * </p>
10643  *
10644  * @param topIndex new top index. Must be between 0 and
10645  * 	getLineCount() - fully visible lines per page. If no lines are fully
10646  * 	visible the maximum value is getLineCount() - 1. An out of range
10647  * 	index will be adjusted accordingly.
10648  * @exception SWTException <ul>
10649  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10650  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10651  * </ul>
10652  */
10653 public void setTopIndex(int topIndex) {
10654 	checkWidget();
10655 	if (getCharCount() == 0) {
10656 		return;
10657 	}
10658 	int lineCount = content.getLineCount(), pixel;
10659 	if (isFixedLineHeight()) {
10660 		int pageSize = Math.max(1, Math.min(lineCount, getLineCountWhole()));
10661 		if (topIndex < 0) {
10662 			topIndex = 0;
10663 		} else if (topIndex > lineCount - pageSize) {
10664 			topIndex = lineCount - pageSize;
10665 		}
10666 		pixel = getLinePixel(topIndex);
10667 	} else {
10668 		topIndex = Math.max(0, Math.min(lineCount - 1, topIndex));
10669 		pixel = getLinePixel(topIndex);
10670 		if (pixel > 0) {
10671 			pixel = getAvailableHeightBellow(pixel);
10672 		} else {
10673 			pixel = getAvailableHeightAbove(pixel);
10674 		}
10675 	}
10676 	scrollVertical(pixel, true);
10677 }
10678 /**
10679  * Sets the top margin.
10680  *
10681  * @param topMargin the top margin.
10682  * @exception SWTException <ul>
10683  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10684  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10685  * </ul>
10686  *
10687  * @since 3.5
10688  */
10689 public void setTopMargin (int topMargin) {
10690 	checkWidget();
10691 	setMargins(getLeftMargin(), topMargin, rightMargin, bottomMargin);
10692 }
10693 /**
10694  * Sets the top SWT logical point offset. Do nothing if there is no text set.
10695  * <p>
10696  * The top point offset is the vertical SWT logical point offset of the widget. The
10697  * widget is scrolled so that the given SWT logical point position is at the top.
10698  * The top index is adjusted to the corresponding top line.
10699  * Note: The top point is reset to 0 when new text is set in the widget.
10700  * </p>
10701  *
10702  * @param pixel new top point offset. Must be between 0 and
10703  * 	(getLineCount() - visible lines per page) / getLineHeight()). An out
10704  * 	of range offset will be adjusted accordingly.
10705  * @exception SWTException <ul>
10706  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10707  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10708  * </ul>
10709  * @since 2.0
10710  */
10711 public void setTopPixel(int pixel) {
10712 	checkWidget();
10713 	if (getCharCount() == 0) {
10714 		return;
10715 	}
10716 	if (pixel < 0) pixel = 0;
10717 	int lineCount = content.getLineCount();
10718 	int height = clientAreaHeight - topMargin - bottomMargin;
10719 	int verticalOffset = getVerticalScrollOffset();
10720 	if (isFixedLineHeight()) {
10721 		int maxTopPixel = Math.max(0, lineCount * getVerticalIncrement() - height);
10722 		if (pixel > maxTopPixel) pixel = maxTopPixel;
10723 		pixel -= verticalOffset;
10724 	} else {
10725 		pixel -= verticalOffset;
10726 		if (pixel > 0) {
10727 			pixel = getAvailableHeightBellow(pixel);
10728 		}
10729 	}
10730 	scrollVertical(pixel, true);
10731 }
10732 /**
10733  * Sets whether the widget wraps lines.
10734  * <p>
10735  * This overrides the creation style bit SWT.WRAP.
10736  * </p>
10737  *
10738  * @param wrap true=widget wraps lines, false=widget does not wrap lines
10739  * @since 2.0
10740  */
10741 public void setWordWrap(boolean wrap) {
10742 	checkWidget();
10743 	if ((getStyle() & SWT.SINGLE) != 0) return;
10744 	if (wordWrap == wrap) return;
10745 	if (wordWrap && blockSelection) setBlockSelection(false);
10746 	wordWrap = wrap;
10747 	resetCache(0, content.getLineCount());
10748 	horizontalScrollOffset = 0;
10749 	ScrollBar horizontalBar = getHorizontalBar();
10750 	if (horizontalBar != null) {
10751 		horizontalBar.setVisible(!wordWrap);
10752 	}
10753 	setScrollBars(true);
10754 	setCaretLocation();
10755 	super.redraw();
10756 }
10757 /**
10758  * Sets the wrap line indentation of the widget.
10759  * <p>
10760  * It is the amount of blank space, in points, at the beginning of each wrapped line.
10761  * When a line wraps in several lines all the lines but the first one is indented
10762  * by this amount.
10763  * </p>
10764  *
10765  * @param wrapIndent the new wrap indent
10766  *
10767  * @exception SWTException <ul>
10768  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10769  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10770  * </ul>
10771  *
10772  * @see #setLineWrapIndent(int, int, int)
10773  *
10774  * @since 3.6
10775  */
10776 public void setWrapIndent(int wrapIndent) {
10777 	checkWidget();
10778 	if (this.wrapIndent == wrapIndent || wrapIndent < 0) return;
10779 	this.wrapIndent = wrapIndent;
10780 	resetCache(0, content.getLineCount());
10781 	setCaretLocation();
10782 	super.redraw();
10783 }
10784 boolean showLocation(Rectangle rect, boolean scrollPage) {
10785 	boolean scrolled = false;
10786 	if (rect.y < topMargin) {
10787 		scrolled = scrollVertical(rect.y - topMargin, true);
10788 	} else if (rect.y + rect.height > clientAreaHeight - bottomMargin) {
10789 		if (clientAreaHeight - topMargin - bottomMargin <= 0) {
10790 			scrolled = scrollVertical(rect.y - topMargin, true);
10791 		} else {
10792 			scrolled = scrollVertical(rect.y + rect.height - (clientAreaHeight - bottomMargin), true);
10793 		}
10794 	}
10795 	int width = clientAreaWidth - rightMargin - leftMargin;
10796 	if (width > 0) {
10797 		int minScroll = scrollPage ? width / 4 : 0;
10798 		if (rect.x < leftMargin) {
10799 			int scrollWidth = Math.max(leftMargin - rect.x, minScroll);
10800 			int maxScroll = horizontalScrollOffset;
10801 			scrolled = scrollHorizontal(-Math.min(maxScroll, scrollWidth), true);
10802 		} else if (rect.x + rect.width > (clientAreaWidth - rightMargin)) {
10803 			int scrollWidth =  Math.max(rect.x + rect.width - (clientAreaWidth - rightMargin), minScroll);
10804 			int maxScroll = renderer.getWidth() - horizontalScrollOffset - clientAreaWidth;
10805 			scrolled = scrollHorizontal(Math.min(maxScroll, scrollWidth), true);
10806 		}
10807 	}
10808 	return scrolled;
10809 }
10810 /**
10811  * Sets the caret location and scrolls the caret offset into view.
10812  */
10813 void showCaret() {
10814 	Rectangle bounds = getBoundsAtOffset(caretOffset);
10815 	if (!showLocation(bounds, true)) {
10816 		setCaretLocation();
10817 	}
10818 }
10819 /**
10820  * Scrolls the selection into view.
10821  * <p>
10822  * The end of the selection will be scrolled into view.
10823  * Note that if a right-to-left selection exists, the end of the selection is
10824  * the visual beginning of the selection (i.e., where the caret is located).
10825  * </p>
10826  *
10827  * @exception SWTException <ul>
10828  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10829  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10830  * </ul>
10831  */
10832 public void showSelection() {
10833 	checkWidget();
10834 	// is selection from right-to-left?
10835 	boolean rightToLeft = caretOffset == selection.x;
10836 	int startOffset, endOffset;
10837 	if (rightToLeft) {
10838 		startOffset = selection.y;
10839 		endOffset = selection.x;
10840 	} else {
10841 		startOffset = selection.x;
10842 		endOffset = selection.y;
10843 	}
10844 
10845 	Rectangle startBounds = getBoundsAtOffset(startOffset);
10846 	Rectangle endBounds = getBoundsAtOffset(endOffset);
10847 
10848 	// can the selection be fully displayed within the widget's visible width?
10849 	int w = clientAreaWidth - leftMargin - rightMargin;
10850 	boolean selectionFits = rightToLeft ? startBounds.x - endBounds.x <= w : endBounds.x - startBounds.x <= w;
10851 	if (selectionFits) {
10852 		// show as much of the selection as possible by first showing
10853 		// the start of the selection
10854 		if (showLocation(startBounds, false)) {
10855 			// endX value could change if showing startX caused a scroll to occur
10856 			endBounds = getBoundsAtOffset(endOffset);
10857 		}
10858 		// the character at endOffset is not part of the selection
10859 		endBounds.width = endOffset == caretOffset ? getCaretWidth() : 0;
10860 		showLocation(endBounds, false);
10861 	} else {
10862 		// just show the end of the selection since the selection start
10863 		// will not be visible
10864 		showLocation(endBounds, true);
10865 	}
10866 }
10867 void updateCaretVisibility() {
10868 	Caret caret = getCaret();
10869 	if (caret != null) {
10870 		if (blockSelection && blockXLocation != -1) {
10871 			caret.setVisible(false);
10872 		} else {
10873 			Point location = caret.getLocation();
10874 			Point size = caret.getSize();
10875 			boolean visible =
10876 				topMargin <= location.y + size.y && location.y <= clientAreaHeight - bottomMargin &&
10877 				leftMargin <= location.x + size.x && location.x <= clientAreaWidth - rightMargin;
10878 			caret.setVisible(visible);
10879 		}
10880 	}
10881 }
10882 /**
10883  * Updates the selection and caret position depending on the text change.
10884  * <p>
10885  * If the selection intersects with the replaced text, the selection is
10886  * reset and the caret moved to the end of the new text.
10887  * If the selection is behind the replaced text it is moved so that the
10888  * same text remains selected.  If the selection is before the replaced text
10889  * it is left unchanged.
10890  * </p>
10891  *
10892  * @param startOffset offset of the text change
10893  * @param replacedLength length of text being replaced
10894  * @param newLength length of new text
10895  */
10896 void updateSelection(int startOffset, int replacedLength, int newLength) {
10897 	if (selection.y <= startOffset) {
10898 		// selection ends before text change
10899 		if (isWordWrap()) setCaretLocation();
10900 		return;
10901 	}
10902 	if (selection.x < startOffset) {
10903 		// clear selection fragment before text change
10904 		internalRedrawRange(selection.x, startOffset - selection.x);
10905 	}
10906 	if (selection.y > startOffset + replacedLength && selection.x < startOffset + replacedLength) {
10907 		// clear selection fragment after text change.
10908 		// do this only when the selection is actually affected by the
10909 		// change. Selection is only affected if it intersects the change (1GDY217).
10910 		int netNewLength = newLength - replacedLength;
10911 		int redrawStart = startOffset + newLength;
10912 		internalRedrawRange(redrawStart, selection.y + netNewLength - redrawStart);
10913 	}
10914 	if (selection.y > startOffset && selection.x < startOffset + replacedLength) {
10915 		// selection intersects replaced text. set caret behind text change
10916 		setSelection(startOffset + newLength, 0, true, false);
10917 	} else {
10918 		// move selection to keep same text selected
10919 		setSelection(selection.x + newLength - replacedLength, selection.y - selection.x, true, false);
10920 	}
10921 	setCaretLocation();
10922 }
10923 }
10924