1 /*******************************************************************************
2 * Copyright (c) 2000, 2018 IBM Corporation and others.
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/
9 * SPDX-License-Identifier: EPL-2.0
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;
22 import java.util.List;
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.*;
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:
37 * <li>foreground color
38 * <li>background color
39 * <li>font style (bold, italic, bold-italic, regular)
44 * In addition to text style attributes, the background color of a line may
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>
54 * <li>getStyleRangeAtOffset(int)
55 * <li>getStyleRanges()
56 * <li>replaceStyleRanges(int,int,StyleRange[])
57 * <li>setStyleRange(StyleRange)
58 * <li>setStyleRanges(StyleRange[])
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>
69 * <li>getLineBackground(int)
70 * <li>setLineBackground(int,int,Color)
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.
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
82 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
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.
90 public class StyledText extends Canvas {
91 static final char TAB = '\t';
92 static final String PlatformLineDelimiter = System.getProperty("line.separator");
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;
101 static final String STYLEDTEXT_KEY = "org.eclipse.swt.internal.cocoa.styledtext"; //$NON-NLS-1$
103 Color selectionBackground; // selection background color
104 Color selectionForeground; // selection foreground color
105 StyledTextContent content; // native content (default or user specified)
106 StyledTextRenderer renderer;
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
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
124 int columnX; // keep track of the horizontal caret position when changing lines/pages. Fixes bug 5935
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;
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;
166 Caret defaultCaret = null;
167 boolean updateCaretDirection = true;
168 boolean dragDetect = true;
173 int indent, wrapIndent;
176 int newOrientation = SWT.NONE;
179 AccessibleControlAdapter accControlAdapter;
180 AccessibleAttributeAdapter accAttributeAdapter;
181 AccessibleEditableTextListener accEditableTextListener;
182 AccessibleTextExtendedAdapter accTextExtendedAdapter;
183 AccessibleAdapter accAdapter;
184 MouseNavigator mouseNavigator;
185 boolean middleClickPressed;
188 boolean blockSelection;
189 int blockXAnchor = -1, blockYAnchor = -1;
190 int blockXLocation = -1, blockYLocation = -1;
192 final static boolean IS_MAC, IS_GTK;
194 String platform = SWT.getPlatform();
195 IS_MAC = "cocoa".equals(platform);
196 IS_GTK = "gtk".equals(platform);
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.
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
211 StyledTextRenderer printerRenderer;
212 StyledTextPrintOptions printOptions;
213 Rectangle clientArea;
216 Map<Resource, Resource> resources;
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
232 * Creates an instance of <code>Printing</code>.
233 * Copies the widget content and rendering data that needs
234 * to be requested from listeners.
236 * @param parent StyledText widget to print.
237 * @param printer printer device to print on.
238 * @param printOptions print options
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();
246 endPage = Integer.MAX_VALUE;
247 PrinterData data = printer.getPrinterData();
249 if (scope == PrinterData.PAGE_RANGE) {
250 startPage = data.startPage;
251 endPage = data.endPage;
252 if (endPage < startPage) {
257 } else if (scope == PrinterData.SELECTION) {
258 selection = styledText.getSelectionRange();
260 printerRenderer = new StyledTextRenderer(printer, null);
261 printerRenderer.setContent(copyContent(styledText.getContent()));
262 cacheLineData(styledText);
265 * Caches all line data that needs to be requested from a listener.
267 * @param printerContent <code>StyledTextContent</code> to request
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);
285 event = styledText.getBidiSegments(lineOffset, line);
287 printerRenderer.setLineSegments(i, 1, event.segments);
288 printerRenderer.setLineSegmentChars(i, 1, event.segmentsChars);
290 event = styledText.getLineStyleData(lineOffset, line);
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);
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);
309 if (printOptions.printLineBackground) {
310 Color printerColor = (Color)resources.get(color);
311 if (printerColor == null) {
312 printerColor = new Color (printer, color.getRGB());
313 resources.put(color, printerColor);
315 printerRenderer.setLineBackground(i, 1, printerColor);
317 printerRenderer.setLineBackground(i, 1, null);
320 int indent = printerRenderer.getLineIndent(i, 0);
322 printerRenderer.setLineIndent(i, 1, indent * printerDPI.x / screenDPI.x);
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);
335 style.font = printerFont;
337 Color color = style.foreground;
339 Color printerColor = (Color)resources.get(color);
340 if (printOptions.printTextForeground) {
341 if (printerColor == null) {
342 printerColor = new Color (printer, color.getRGB());
343 resources.put(color, printerColor);
345 style.foreground = printerColor;
347 style.foreground = null;
350 color = style.background;
352 Color printerColor = (Color)resources.get(color);
353 if (printOptions.printTextBackground) {
354 if (printerColor == null) {
355 printerColor = new Color (printer, color.getRGB());
356 resources.put(color, printerColor);
358 style.background = printerColor;
360 style.background = null;
363 if (!printOptions.printTextFontStyle) {
364 style.fontStyle = SWT.NORMAL;
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;
374 lineSpacing = styledText.lineSpacing * printerDPI.y / screenDPI.y;
375 if (printOptions.printLineNumbers) {
376 printMargin = 3 * printerDPI.x / screenDPI.x;
380 * Copies the text of the specified <code>StyledTextContent</code>.
382 * @param original the <code>StyledTextContent</code> to copy.
384 StyledTextContent copyContent(StyledTextContent original) {
385 StyledTextContent printerContent = new DefaultContent();
386 int insertOffset = 0;
387 for (int i = 0; i < original.getLineCount(); i++) {
389 if (i < original.getLineCount() - 1) {
390 insertEndOffset = original.getOffsetAtLine(i + 1);
392 insertEndOffset = original.getCharCount();
394 printerContent.replaceTextRange(insertOffset, 0, original.getTextRange(insertOffset, insertEndOffset - insertOffset));
395 insertOffset = insertEndOffset;
397 return printerContent;
400 * Disposes of the resources and the <code>PrintRenderer</code>.
407 if (resources != null) {
408 for (Resource resource : resources.values()) {
413 if (printerFont != null) {
414 printerFont.dispose();
417 if (printerRenderer != null) {
418 printerRenderer.dispose();
419 printerRenderer = null;
423 Rectangle trim = printer.computeTrim(0, 0, 0, 0);
424 Point dpi = printer.getDPI();
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);
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;
444 if (printOptions.footer != null) {
445 clientArea.height -= lineHeight * 2;
449 StyledTextContent content = printerRenderer.content;
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);
460 endLine = startLine - 1;
465 * Prints the lines in the specified page range.
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);
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);
494 StringBuilder buffer = new StringBuilder("0");
495 while ((count /= 10) > 0) buffer.append("0");
496 printLayout.setText(buffer.toString());
497 numberingWidth = printLayout.getBounds().width;
499 numberingWidth += printMargin;
500 if (numberingWidth > width) numberingWidth = width;
501 paintX += numberingWidth;
502 width -= numberingWidth;
504 for (int i = startLine; i <= endLine && page <= endPage; i++) {
505 if (paintY == clientArea.y) {
507 printDecoration(page, true, printLayout);
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;
517 int lineCount = layout.getLineCount();
518 while (paragraphBottom > pageBottom && lineCount > 0) {
520 paragraphBottom -= layout.getLineBounds(lineCount).height + layout.getSpacing();
522 if (lineCount == 0) {
523 //the whole paragraph goes to the next page
524 printDecoration(page, false, printLayout);
527 if (page <= endPage) {
529 printDecoration(page, true, printLayout);
530 paintY = clientArea.y;
531 printLine(paintX, paintY, gc, foreground, lineBackground, layout, printLayout, i);
532 paintY += layout.getBounds().height;
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);
543 if (page <= endPage) {
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;
555 printerRenderer.disposeTextLayout(layout);
557 if (page <= endPage && paintY > clientArea.y) {
558 // close partial page
559 printDecoration(page, false, printLayout);
562 if (printLayout != null) printLayout.dispose();
565 * Print header or footer decorations.
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
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);
577 if (segmentIndex == -1) {
578 segment = text.substring(lastSegmentIndex);
579 printDecorationSegment(segment, i, page, header, layout);
582 segment = text.substring(lastSegmentIndex, segmentIndex);
583 printDecorationSegment(segment, i, page, header, layout);
584 lastSegmentIndex = segmentIndex + StyledTextPrintOptions.SEPARATOR.length();
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.
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
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();
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;
620 drawY = clientArea.y - segmentHeight * 2;
622 drawY = clientArea.y + clientArea.height + segmentHeight;
624 layout.draw(gc, drawX, drawY);
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);
633 // int lineCount = layout.getLineCount();
634 // for (int i = 0; i < lineCount; i++) {
635 // Rectangle rect = layout.getLineBounds(i);
637 // rect.y += paintY + layout.getSpacing();
638 // rect.width = width;//layout bounds
639 // gc.fillRectangle(rect);
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]);
651 printLayout.setText("");
654 printLayout.setText(String.valueOf(index));
656 int paintX = x - printMargin - printLayout.getBounds().width;
657 printLayout.draw(gc, paintX, y);
658 printLayout.setAscent(-1);
659 printLayout.setDescent(-1);
661 gc.setForeground(foreground);
662 layout.draw(gc, x, y);
665 * Starts a print job and prints the pages specified in the constructor.
669 String jobName = printOptions.jobName;
670 if (jobName == null) {
671 jobName = "Printing";
673 if (printer.startJob(jobName)) {
682 * The <code>RTFWriter</code> class is used to write widget content as
683 * rich text. The implementation complies with the RTF specification
686 * toString() is guaranteed to return a valid RTF string only after
687 * close() has been called.
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.
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;
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
707 * @param start start offset of content to write, 0 based from
708 * beginning of document
709 * @param length length of content to write
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());
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.
725 public void close() {
733 * Returns the index of the specified color in the RTF color table.
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.
740 int getColorIndex(Color color, int defaultIndex) {
741 if (color == null) return defaultIndex;
742 int index = colorTable.indexOf(color);
744 index = colorTable.size();
745 colorTable.add(color);
750 * Returns the index of the specified color in the RTF color table.
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.
757 int getFontIndex(Font font) {
758 int index = fontTable.indexOf(font);
760 index = fontTable.size();
766 * Appends the specified segment of "string" to the RTF data.
767 * Copy from <code>start</code> up to, but excluding, <code>end</code>.
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
774 void write(String string, int start, int end) {
775 for (int index = start; index < end; index++) {
776 char ch = string.charAt(index);
778 // write the sub string from the last escaped character
779 // to the current one. Fixes bug 21698.
781 write(string.substring(start, index));
784 write(Integer.toString((short) ch));
785 write('?'); // ANSI representation (1 byte long, \\uc1)
787 } else if (ch == '}' || ch == '{' || ch == '\\') {
788 // write the sub string from the last escaped character
789 // to the current one. Fixes bug 21698.
791 write(string.substring(start, index));
798 // write from the last escaped character to the end.
801 write(string.substring(start, end));
805 * Writes the RTF header including font table and color table.
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");
819 header.append("\\uc1\\deff0{\\fonttbl{\\f0\\fnil ");
820 header.append(fontData.getName());
822 for (int i = 1; i < fontTable.size(); i++) {
823 header.append("\\f");
826 FontData fd = fontTable.get(i).getFontData()[0];
827 header.append(fd.getName());
830 header.append("}}\n{\\colortbl");
831 for (int i = 0; i < colorTable.size(); i++) {
832 Color color = colorTable.get(i);
833 header.append("\\red");
834 header.append(color.getRed());
835 header.append("\\green");
836 header.append(color.getGreen());
837 header.append("\\blue");
838 header.append(color.getBlue());
841 // some RTF readers ignore the deff0 font tag. Explicitly
842 // set the font for the whole document to work around this.
843 header.append("}\n{\\f0\\fs");
844 // font size is specified in half points
845 header.append(fontData.getHeight() * 2);
847 write(header.toString(), 0);
850 * Appends the specified line text to the RTF data. Lines will be formatted
851 * using the styles queried from the LineStyleListener, if set, or those set
852 * directly in the widget.
854 * @param line line text to write as RTF. Must not contain line breaks
855 * Line breaks should be written using writeLineDelimiter()
856 * @param lineOffset offset of the line. 0 based from the start of the
857 * widget document. Any text occurring before the start offset or after the
858 * end offset specified during object creation is ignored.
859 * @exception SWTException <ul>
860 * <li>ERROR_IO when the writer is closed.</li>
864 public void writeLine(String line, int lineOffset) {
866 SWT.error(SWT.ERROR_IO);
868 int lineIndex = content.getLineAtOffset(lineOffset);
869 int lineAlignment, lineIndent;
873 StyledTextEvent event = getLineStyleData(lineOffset, line);
875 lineAlignment = event.alignment;
876 lineIndent = event.indent;
877 lineJustify = event.justify;
878 ranges = event.ranges;
879 styles = event.styles;
881 lineAlignment = renderer.getLineAlignment(lineIndex, alignment);
882 lineIndent = renderer.getLineIndent(lineIndex, indent);
883 lineJustify = renderer.getLineJustify(lineIndex, justify);
884 ranges = renderer.getRanges(lineOffset, line.length());
885 styles = renderer.getStyleRanges(lineOffset, line.length(), false);
887 if (styles == null) styles = new StyleRange[0];
888 Color lineBackground = renderer.getLineBackground(lineIndex, null);
889 event = getLineBackgroundData(lineOffset, line);
890 if (event != null && event.lineBackground != null) lineBackground = event.lineBackground;
891 writeStyledLine(line, lineOffset, ranges, styles, lineBackground, lineIndent, lineAlignment, lineJustify);
894 * Appends the specified line delimiter to the RTF data.
896 * @param lineDelimiter line delimiter to write as RTF.
897 * @exception SWTException <ul>
898 * <li>ERROR_IO when the writer is closed.</li>
902 public void writeLineDelimiter(String lineDelimiter) {
904 SWT.error(SWT.ERROR_IO);
906 write(lineDelimiter, 0, lineDelimiter.length());
910 * Appends the specified line text to the RTF data.
912 * Use the colors and font styles specified in "styles" and "lineBackground".
913 * Formatting is written to reflect the text rendering by the text widget.
914 * Style background colors take precedence over the line background color.
915 * Background colors are written using the \chshdng0\chcbpat tag (vs. the \cb tag).
918 * @param line line text to write as RTF. Must not contain line breaks
919 * Line breaks should be written using writeLineDelimiter()
920 * @param lineOffset offset of the line. 0 based from the start of the
921 * widget document. Any text occurring before the start offset or after the
922 * end offset specified during object creation is ignored.
923 * @param styles styles to use for formatting. Must not be null.
924 * @param lineBackground line background color to use for formatting.
927 void writeStyledLine(String line, int lineOffset, int ranges[], StyleRange[] styles, Color lineBackground, int indent, int alignment, boolean justify) {
928 int lineLength = line.length();
929 int startOffset = getStart();
930 int writeOffset = startOffset - lineOffset;
931 if (writeOffset >= lineLength) return;
932 int lineIndex = Math.max(0, writeOffset);
937 case SWT.LEFT: write("\\ql"); break;
938 case SWT.CENTER: write("\\qc"); break;
939 case SWT.RIGHT: write("\\qr"); break;
941 if (justify) write("\\qj");
944 if (lineBackground != null) {
945 write("{\\chshdng0\\chcbpat");
946 write(getColorIndex(lineBackground, DEFAULT_BACKGROUND));
949 int endOffset = startOffset + super.getCharCount();
950 int lineEndOffset = Math.min(lineLength, endOffset - lineOffset);
951 for (int i = 0; i < styles.length; i++) {
952 StyleRange style = styles[i];
954 if (ranges != null) {
955 start = ranges[i << 1] - lineOffset;
956 end = start + ranges[(i << 1) + 1];
958 start = style.start - lineOffset;
959 end = start + style.length;
961 // skip over partial first line
962 if (end < writeOffset) {
965 // style starts beyond line end or RTF write end
966 if (start >= lineEndOffset) {
969 // write any unstyled text
970 if (lineIndex < start) {
971 // copy to start of style
972 // style starting beyond end of write range or end of line
973 // is guarded against above.
974 write(line, lineIndex, start);
979 write(getColorIndex(style.foreground, DEFAULT_FOREGROUND));
980 int colorIndex = getColorIndex(style.background, DEFAULT_BACKGROUND);
981 if (colorIndex != DEFAULT_BACKGROUND) {
982 write("\\chshdng0\\chcbpat");
985 int fontStyle = style.fontStyle;
986 Font font = style.font;
988 int fontIndex = getFontIndex(font);
991 FontData fontData = font.getFontData()[0];
993 write(fontData.getHeight() * 2);
994 fontStyle = fontData.getStyle();
996 if ((fontStyle & SWT.BOLD) != 0) {
999 if ((fontStyle & SWT.ITALIC) != 0) {
1002 if (style.underline) {
1005 if (style.strikeout) {
1009 // copy to end of style or end of write range or end of line
1010 int copyEnd = Math.min(end, lineEndOffset);
1011 // guard against invalid styles and let style processing continue
1012 copyEnd = Math.max(copyEnd, lineIndex);
1013 write(line, lineIndex, copyEnd);
1014 if ((fontStyle & SWT.BOLD) != 0) {
1017 if ((style.fontStyle & SWT.ITALIC) != 0) {
1020 if (style.underline) {
1023 if (style.strikeout) {
1027 lineIndex = copyEnd;
1029 // write unstyled text at the end of the line
1030 if (lineIndex < lineEndOffset) {
1031 write(line, lineIndex, lineEndOffset);
1033 if (lineBackground != null) write("}");
1037 * The <code>TextWriter</code> class is used to write widget content to
1038 * a string. Whole and partial lines and line breaks can be written. To write
1039 * partial lines, specify the start and length of the desired segment
1040 * during object creation.
1042 * <b>NOTE:</b> <code>toString()</code> is guaranteed to return a valid string only after close()
1047 private StringBuilder buffer;
1048 private int startOffset; // offset of first character that will be written
1049 private int endOffset; // offset of last character that will be written.
1050 // 0 based from the beginning of the widget text.
1051 private boolean isClosed = false;
1054 * Creates a writer that writes content starting at offset "start"
1055 * in the document. <code>start</code> and <code>length</code> can be set to specify partial lines.
1057 * @param start start offset of content to write, 0 based from beginning of document
1058 * @param length length of content to write
1060 public TextWriter(int start, int length) {
1061 buffer = new StringBuilder(length);
1062 startOffset = start;
1063 endOffset = start + length;
1066 * Closes the writer. Once closed no more content can be written.
1067 * <b>NOTE:</b> <code>toString()</code> is not guaranteed to return a valid string unless
1068 * the writer is closed.
1070 public void close() {
1076 * Returns the number of characters to write.
1077 * @return the integer number of characters to write
1079 public int getCharCount() {
1080 return endOffset - startOffset;
1083 * Returns the offset where writing starts. 0 based from the start of
1084 * the widget text. Used to write partial lines.
1085 * @return the integer offset where writing starts
1087 public int getStart() {
1091 * Returns whether the writer is closed.
1092 * @return a boolean specifying whether or not the writer is closed
1094 public boolean isClosed() {
1098 * Returns the string. <code>close()</code> must be called before <code>toString()</code>
1099 * is guaranteed to return a valid string.
1101 * @return the string
1104 public String toString() {
1105 return buffer.toString();
1108 * Appends the given string to the data.
1110 void write(String string) {
1111 buffer.append(string);
1114 * Inserts the given string to the data at the specified offset.
1116 * Do nothing if "offset" is < 0 or > getCharCount()
1119 * @param string text to insert
1120 * @param offset offset in the existing data to insert "string" at.
1122 void write(String string, int offset) {
1123 if (offset < 0 || offset > buffer.length()) {
1126 buffer.insert(offset, string);
1129 * Appends the given int to the data.
1135 * Appends the given character to the data.
1137 void write(char i) {
1141 * Appends the specified line text to the data.
1143 * @param line line text to write. Must not contain line breaks
1144 * Line breaks should be written using writeLineDelimiter()
1145 * @param lineOffset offset of the line. 0 based from the start of the
1146 * widget document. Any text occurring before the start offset or after the
1147 * end offset specified during object creation is ignored.
1148 * @exception SWTException <ul>
1149 * <li>ERROR_IO when the writer is closed.</li>
1152 public void writeLine(String line, int lineOffset) {
1154 SWT.error(SWT.ERROR_IO);
1156 int writeOffset = startOffset - lineOffset;
1157 int lineLength = line.length();
1159 if (writeOffset >= lineLength) {
1160 return; // whole line is outside write range
1161 } else if (writeOffset > 0) {
1162 lineIndex = writeOffset; // line starts before write start
1166 int copyEnd = Math.min(lineLength, endOffset - lineOffset);
1167 if (lineIndex < copyEnd) {
1168 write(line.substring(lineIndex, copyEnd));
1172 * Appends the specified line delimiter to the data.
1174 * @param lineDelimiter line delimiter to write
1175 * @exception SWTException <ul>
1176 * <li>ERROR_IO when the writer is closed.</li>
1179 public void writeLineDelimiter(String lineDelimiter) {
1181 SWT.error(SWT.ERROR_IO);
1183 write(lineDelimiter);
1188 * Constructs a new instance of this class given its parent
1189 * and a style value describing its behavior and appearance.
1191 * The style value is either one of the style constants defined in
1192 * class <code>SWT</code> which is applicable to instances of this
1193 * class, or must be built by <em>bitwise OR</em>'ing together
1194 * (that is, using the <code>int</code> "|" operator) two or more
1195 * of those <code>SWT</code> style constants. The class description
1196 * lists the style constants that are applicable to the class.
1197 * Style bits are also inherited from superclasses.
1200 * @param parent a widget which will be the parent of the new instance (cannot be null)
1201 * @param style the style of widget to construct
1203 * @exception IllegalArgumentException <ul>
1204 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
1206 * @exception SWTException <ul>
1207 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
1210 * @see SWT#FULL_SELECTION
1212 * @see SWT#READ_ONLY
1217 public StyledText(Composite parent, int style) {
1218 super(parent, checkStyle(style));
1219 // set the fg in the OS to ensure that these are the same as StyledText, necessary
1220 // for ensuring that the bg/fg the IME box uses is the same as what StyledText uses
1221 super.setForeground(getForeground());
1222 super.setDragDetect(false);
1223 Display display = getDisplay();
1224 if ((style & SWT.READ_ONLY) != 0) {
1227 leftMargin = rightMargin = isBidiCaret() ? BIDI_CARET_WIDTH - 1: 0;
1228 if ((style & SWT.SINGLE) != 0 && (style & SWT.BORDER) != 0) {
1229 leftMargin = topMargin = rightMargin = bottomMargin = 2;
1231 alignment = style & (SWT.LEFT | SWT.RIGHT | SWT.CENTER);
1232 if (alignment == 0) alignment = SWT.LEFT;
1233 clipboard = new Clipboard(display);
1234 installDefaultContent();
1235 renderer = new StyledTextRenderer(getDisplay(), this);
1236 renderer.setContent(content);
1237 renderer.setFont(getFont(), tabLength);
1238 ime = new IME(this, SWT.NONE);
1239 defaultCaret = new Caret(this, SWT.NONE);
1240 if ((style & SWT.WRAP) != 0) {
1243 if (isBidiCaret()) {
1244 createCaretBitmaps();
1245 Runnable runnable = () -> {
1246 int direction = BidiUtil.getKeyboardLanguage() == BidiUtil.KEYBOARD_BIDI ? SWT.RIGHT : SWT.LEFT;
1247 if (direction == caretDirection) return;
1248 if (getCaret() != defaultCaret) return;
1249 Point newCaretPos = getPointAtOffset(caretOffset);
1250 setCaretLocation(newCaretPos, direction);
1252 BidiUtil.addLanguageListener(this, runnable);
1254 setCaret(defaultCaret);
1255 calculateScrollBars();
1256 createKeyBindings();
1257 super.setCursor(display.getSystemCursor(SWT.CURSOR_IBEAM));
1259 initializeAccessible();
1260 setData("DEFAULT_DROP_TARGET_EFFECT", new StyledTextDropTargetEffect(this));
1261 if (IS_MAC) setData(STYLEDTEXT_KEY);
1264 * Adds an extended modify listener. An ExtendedModify event is sent by the
1265 * widget when the widget text has changed.
1267 * @param extendedModifyListener the listener
1268 * @exception SWTException <ul>
1269 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1270 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1272 * @exception IllegalArgumentException <ul>
1273 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
1276 public void addExtendedModifyListener(ExtendedModifyListener extendedModifyListener) {
1278 if (extendedModifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1279 StyledTextListener typedListener = new StyledTextListener(extendedModifyListener);
1280 addListener(ST.ExtendedModify, typedListener);
1283 * Adds a bidirectional segment listener.
1285 * A BidiSegmentEvent is sent
1286 * whenever a line of text is measured or rendered. You can
1287 * specify text ranges in the line that should be treated as if they
1288 * had a different direction than the surrounding text.
1289 * This may be used when adjacent segments of right-to-left text should
1290 * not be reordered relative to each other.
1291 * E.g., multiple Java string literals in a right-to-left language
1292 * should generally remain in logical order to each other, that is, the
1293 * way they are stored.
1296 * @param listener the listener
1297 * @exception SWTException <ul>
1298 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1299 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1301 * @exception IllegalArgumentException <ul>
1302 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
1304 * @see BidiSegmentEvent
1307 public void addBidiSegmentListener(BidiSegmentListener listener) {
1309 if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1310 addListener(ST.LineGetSegments, new StyledTextListener(listener));
1311 resetCache(0, content.getLineCount());
1316 * Adds a caret listener. CaretEvent is sent when the caret offset changes.
1318 * @param listener the listener
1319 * @exception SWTException <ul>
1320 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1321 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1323 * @exception IllegalArgumentException <ul>
1324 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
1329 public void addCaretListener(CaretListener listener) {
1331 if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1332 addListener(ST.CaretMoved, new StyledTextListener(listener));
1335 * Adds a line background listener. A LineGetBackground event is sent by the
1336 * widget to determine the background color for a line.
1338 * @param listener the listener
1339 * @exception SWTException <ul>
1340 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1341 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1343 * @exception IllegalArgumentException <ul>
1344 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
1347 public void addLineBackgroundListener(LineBackgroundListener listener) {
1349 if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1350 if (!isListening(ST.LineGetBackground)) {
1351 renderer.clearLineBackground(0, content.getLineCount());
1353 addListener(ST.LineGetBackground, new StyledTextListener(listener));
1356 * Adds a line style listener. A LineGetStyle event is sent by the widget to
1357 * determine the styles for a line.
1359 * @param listener the listener
1360 * @exception SWTException <ul>
1361 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1362 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1364 * @exception IllegalArgumentException <ul>
1365 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
1368 public void addLineStyleListener(LineStyleListener listener) {
1370 if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1371 if (!isListening(ST.LineGetStyle)) {
1372 setStyleRanges(0, 0, null, null, true);
1373 renderer.clearLineStyle(0, content.getLineCount());
1375 addListener(ST.LineGetStyle, new StyledTextListener(listener));
1379 * Adds a modify listener. A Modify event is sent by the widget when the widget text
1382 * @param modifyListener the listener
1383 * @exception SWTException <ul>
1384 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1385 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1387 * @exception IllegalArgumentException <ul>
1388 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
1391 public void addModifyListener(ModifyListener modifyListener) {
1393 if (modifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1394 addListener(SWT.Modify, new TypedListener(modifyListener));
1397 * Adds a paint object listener. A paint object event is sent by the widget when an object
1398 * needs to be drawn.
1400 * @param listener the listener
1401 * @exception SWTException <ul>
1402 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1403 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1405 * @exception IllegalArgumentException <ul>
1406 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
1411 * @see PaintObjectListener
1412 * @see PaintObjectEvent
1414 public void addPaintObjectListener(PaintObjectListener listener) {
1416 if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1417 addListener(ST.PaintObject, new StyledTextListener(listener));
1420 * Adds a selection listener. A Selection event is sent by the widget when the
1421 * user changes the selection.
1423 * When <code>widgetSelected</code> is called, the event x and y fields contain
1424 * the start and end caret indices of the selection. The selection values returned are visual
1425 * (i.e., x will always always be <= y).
1426 * No event is sent when the caret is moved while the selection length is 0.
1428 * <code>widgetDefaultSelected</code> is not called for StyledTexts.
1431 * @param listener the listener which should be notified when the user changes the receiver's selection
1433 * @exception IllegalArgumentException <ul>
1434 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
1436 * @exception SWTException <ul>
1437 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1438 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1441 * @see SelectionListener
1442 * @see #removeSelectionListener
1443 * @see SelectionEvent
1445 public void addSelectionListener(SelectionListener listener) {
1447 if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1448 addListener(SWT.Selection, new TypedListener(listener));
1451 * Adds a verify key listener. A VerifyKey event is sent by the widget when a key
1452 * is pressed. The widget ignores the key press if the listener sets the doit field
1453 * of the event to false.
1455 * @param listener the listener
1456 * @exception SWTException <ul>
1457 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1458 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1460 * @exception IllegalArgumentException <ul>
1461 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
1464 public void addVerifyKeyListener(VerifyKeyListener listener) {
1466 if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1467 addListener(ST.VerifyKey, new StyledTextListener(listener));
1470 * Adds a verify listener. A Verify event is sent by the widget when the widget text
1471 * is about to change. The listener can set the event text and the doit field to
1472 * change the text that is set in the widget or to force the widget to ignore the
1475 * @param verifyListener the listener
1476 * @exception SWTException <ul>
1477 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1478 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1480 * @exception IllegalArgumentException <ul>
1481 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
1484 public void addVerifyListener(VerifyListener verifyListener) {
1486 if (verifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1487 addListener(SWT.Verify, new TypedListener(verifyListener));
1490 * Adds a word movement listener. A movement event is sent when the boundary
1491 * of a word is needed. For example, this occurs during word next and word
1494 * @param movementListener the listener
1495 * @exception SWTException <ul>
1496 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1497 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1499 * @exception IllegalArgumentException <ul>
1500 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
1503 * @see MovementEvent
1504 * @see MovementListener
1505 * @see #removeWordMovementListener
1509 public void addWordMovementListener(MovementListener movementListener) {
1511 if (movementListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1512 addListener(ST.WordNext, new StyledTextListener(movementListener));
1513 addListener(ST.WordPrevious, new StyledTextListener(movementListener));
1516 * Appends a string to the text at the end of the widget.
1518 * @param string the string to be appended
1519 * @see #replaceTextRange(int,int,String)
1520 * @exception SWTException <ul>
1521 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1522 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1524 * @exception IllegalArgumentException <ul>
1525 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
1528 public void append(String string) {
1530 if (string == null) {
1531 SWT.error(SWT.ERROR_NULL_ARGUMENT);
1533 int lastChar = Math.max(getCharCount(), 0);
1534 replaceTextRange(lastChar, 0, string);
1537 * Calculates the scroll bars
1539 void calculateScrollBars() {
1540 ScrollBar horizontalBar = getHorizontalBar();
1541 ScrollBar verticalBar = getVerticalBar();
1542 setScrollBars(true);
1543 if (verticalBar != null) {
1544 verticalBar.setIncrement(getVerticalIncrement());
1546 if (horizontalBar != null) {
1547 horizontalBar.setIncrement(getHorizontalIncrement());
1551 * Calculates the top index based on the current vertical scroll offset.
1552 * The top index is the index of the topmost fully visible line or the
1553 * topmost partially visible line if no line is fully visible.
1554 * The top index starts at 0.
1556 void calculateTopIndex(int delta) {
1557 int oldDelta = delta;
1558 int oldTopIndex = topIndex;
1559 int oldTopIndexY = topIndexY;
1560 if (isFixedLineHeight()) {
1561 int verticalIncrement = getVerticalIncrement();
1562 if (verticalIncrement == 0) {
1565 topIndex = Compatibility.ceil(getVerticalScrollOffset(), verticalIncrement);
1566 // Set top index to partially visible top line if no line is fully
1567 // visible but at least some of the widget client area is visible.
1570 if (clientAreaHeight > 0) {
1571 int bottomPixel = getVerticalScrollOffset() + clientAreaHeight;
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;
1580 } else if (topIndex >= content.getLineCount()) {
1581 topIndex = content.getLineCount() - 1;
1587 int lineIndex = topIndex;
1588 int lineCount = content.getLineCount();
1589 while (lineIndex < lineCount) {
1590 if (delta <= 0) break;
1591 delta -= renderer.getLineHeight(lineIndex++);
1593 if (lineIndex < lineCount && -delta + renderer.getLineHeight(lineIndex) <= clientAreaHeight - topMargin - bottomMargin) {
1594 topIndex = lineIndex;
1597 topIndex = lineIndex - 1;
1598 topIndexY = -renderer.getLineHeight(topIndex) - delta;
1602 int lineIndex = topIndex;
1603 while (lineIndex > 0) {
1604 int lineHeight = renderer.getLineHeight(lineIndex - 1);
1605 if (delta + lineHeight > 0) break;
1606 delta += lineHeight;
1609 if (lineIndex == 0 || -delta + renderer.getLineHeight(lineIndex) <= clientAreaHeight - topMargin - bottomMargin) {
1610 topIndex = lineIndex;
1611 topIndexY = - delta;
1613 topIndex = lineIndex - 1;
1614 topIndexY = - renderer.getLineHeight(topIndex) - delta;
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());
1633 if (topIndex != oldTopIndex || oldTopIndexY != topIndexY) {
1634 int width = renderer.getWidth();
1635 renderer.calculateClientArea();
1636 if (width != renderer.getWidth()) {
1637 setScrollBars(false);
1642 * Hides the scroll bars if widget is created in single line mode.
1644 static int checkStyle(int style) {
1645 if ((style & SWT.SINGLE) != 0) {
1646 style &= ~(SWT.H_SCROLL | SWT.V_SCROLL | SWT.WRAP | SWT.MULTI);
1649 if ((style & SWT.WRAP) != 0) {
1650 style &= ~SWT.H_SCROLL;
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;
1658 * Scrolls down the text to use new space made available by a resize or by
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);
1669 int bottomIndex = getPartialBottomIndex();
1670 int height = getLinePixel(bottomIndex + 1);
1671 if (clientAreaHeight > height) {
1672 scrollVertical(-getAvailableHeightAbove(clientAreaHeight - height), true);
1677 * Scrolls text to the right to use new space made available by a resize.
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);
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();
1695 if (sendEvent) sendSelectionEvent();
1698 * Removes the widget selection.
1700 * @param sendEvent a Selection event is sent when set to true and when the selection is actually reset.
1702 void clearSelection(boolean sendEvent) {
1703 int selectionStart = selection.x;
1704 int selectionEnd = selection.y;
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);
1717 sendSelectionEvent();
1722 public Point computeSize (int wHint, int hHint, boolean changed) {
1724 int lineCount = (getStyle() & SWT.SINGLE) != 0 ? 1 : content.getLineCount();
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;
1741 if (isFixedLineHeight()) {
1742 height = lineCount * renderer.getLineHeight();
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);
1756 * Copies the selected text to the <code>DND.CLIPBOARD</code> clipboard.
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
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>
1769 public void copy() {
1771 copySelection(DND.CLIPBOARD);
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.
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.
1785 * @param clipboardType indicates the type of clipboard
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>
1794 public void copy(int clipboardType) {
1796 copySelection(clipboardType);
1798 boolean copySelection(int type) {
1799 if (type != DND.CLIPBOARD && type != DND.SELECTION_CLIPBOARD) return false;
1801 if (blockSelection && blockXLocation != -1) {
1802 String text = getBlockSelectionText(PlatformLineDelimiter);
1803 if (text.length() > 0) {
1805 TextTransfer plainTextTransfer = TextTransfer.getInstance();
1806 Object[] data = new Object[]{text};
1807 Transfer[] types = new Transfer[]{plainTextTransfer};
1808 clipboard.setContents(data, types, type);
1812 int length = selection.y - selection.x;
1814 setClipboardContent(selection.x, length, type);
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) {
1829 * Returns the alignment of the widget.
1831 * @return the alignment
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>
1838 * @see #getLineAlignment(int)
1842 public int getAlignment() {
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.
1853 * @return the Always Show Scrollbars flag value
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>
1862 public boolean getAlwaysShowScrollBars() {
1864 return alwaysShowScroll;
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--);
1874 while (height > maxHeight && lineIndex >= 0) {
1875 maxHeight += renderer.getLineHeight(lineIndex--);
1878 return Math.min(height, maxHeight);
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);
1889 int lineIndex = partialBottomIndex + 1;
1890 int lineCount = content.getLineCount();
1891 while (height > availableHeight && lineIndex < lineCount) {
1892 availableHeight += renderer.getLineHeight(lineIndex++);
1894 return Math.min(height, availableHeight);
1897 * Returns the color of the margins.
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>
1907 public Color getMarginColor() {
1909 return marginColor != null ? marginColor : getBackground();
1912 * Returns a string that uses only the line delimiter specified by the
1913 * StyledTextContent implementation.
1915 * Returns only the first line if the widget has the SWT.SINGLE style.
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.
1925 String getModelDelimitedText(String text) {
1926 int length = text.length();
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);
1939 if (lfIndex != -1) {
1940 lfIndex = text.indexOf(SWT.LF, i);
1942 if (lfIndex == -1 && crIndex == -1) { // no more line breaks?
1944 } else if ((crIndex < lfIndex && crIndex != -1) || lfIndex == -1) {
1945 convertedText.append(text.substring(i, crIndex));
1946 if (lfIndex == crIndex + 1) { // CR/LF combination?
1951 } else { // LF occurs before CR!
1952 convertedText.append(text.substring(i, lfIndex));
1955 if (isSingleLine()) {
1958 convertedText.append(delimiter);
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));
1965 return convertedText.toString();
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);
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);
1987 * Creates default key bindings.
1989 void createKeyBindings() {
1990 int nextKey = isMirrored() ? SWT.ARROW_LEFT : SWT.ARROW_RIGHT;
1991 int previousKey = isMirrored() ? SWT.ARROW_RIGHT : SWT.ARROW_LEFT;
1994 setKeyBinding(SWT.ARROW_UP, ST.LINE_UP);
1995 setKeyBinding(SWT.ARROW_DOWN, ST.LINE_DOWN);
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);
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);
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);
2021 setKeyBinding(SWT.ARROW_UP | SWT.MOD2, ST.SELECT_LINE_UP);
2022 setKeyBinding(SWT.ARROW_DOWN | SWT.MOD2, ST.SELECT_LINE_DOWN);
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);
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);
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);
2049 setKeyBinding('X' | SWT.MOD1, ST.CUT);
2050 setKeyBinding('C' | SWT.MOD1, ST.COPY);
2051 setKeyBinding('V' | SWT.MOD1, ST.PASTE);
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);
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);
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);
2069 setKeyBinding(SWT.INSERT, ST.TOGGLE_OVERWRITE);
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).
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);
2083 leftCaretBitmap.dispose();
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);
2096 if (rightCaretBitmap != null) {
2097 if (defaultCaret != null && rightCaretBitmap.equals(defaultCaret.getImage())) {
2098 defaultCaret.setImage(null);
2100 rightCaretBitmap.dispose();
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);
2113 * Moves the selected text to the clipboard. The text will be put in the
2114 * clipboard in plain text format and RTF format.
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>
2123 // Abort cut operation if copy to clipboard fails.
2125 if (copySelection(DND.CLIPBOARD)) {
2126 if (blockSelection && blockXLocation != -1) {
2127 insertBlockSelectionText((char)0, SWT.NULL);
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.
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));
2153 * Initiates autoscrolling.
2155 * @param direction SWT.UP, SWT.DOWN, SWT.COLUMN_NEXT, SWT.COLUMN_PREVIOUS
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) {
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() {
2172 /* Bug 437357 - NPE in StyledText.getCaretLine
2173 * StyledText.content is null at times, probably because the
2174 * widget itself has been disposed.
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);
2183 setBlockSelectionLocation(blockXLocation - horizontalScrollOffset, y + pixels, true);
2184 scrollVertical(pixels, true);
2187 doSelectionPageUp(autoScrollDistance);
2189 display.timerExec(V_SCROLL_RATE, this);
2193 autoScrollDirection = direction;
2194 display.timerExec(V_SCROLL_RATE, timer);
2195 } else if (direction == SWT.DOWN) {
2196 timer = new Runnable() {
2199 /* Bug 437357 - NPE in StyledText.getCaretLine
2200 * StyledText.content is null at times, probably because the
2201 * widget itself has been disposed.
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));
2211 setBlockSelectionLocation(blockXLocation - horizontalScrollOffset, y + pixels, true);
2212 scrollVertical(pixels, true);
2215 doSelectionPageDown(autoScrollDistance);
2217 display.timerExec(V_SCROLL_RATE, this);
2221 autoScrollDirection = direction;
2222 display.timerExec(V_SCROLL_RATE, timer);
2223 } else if (direction == ST.COLUMN_NEXT) {
2224 timer = new Runnable() {
2227 /* Bug 437357 - NPE in StyledText.getCaretLine
2228 * StyledText.content is null at times, probably because the
2229 * widget itself has been disposed.
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));
2238 setBlockSelectionLocation(x + pixels, blockYLocation - getVerticalScrollOffset(), true);
2239 scrollHorizontal(pixels, true);
2243 setMouseWordSelectionAnchor();
2246 display.timerExec(H_SCROLL_RATE, this);
2250 autoScrollDirection = direction;
2251 display.timerExec(H_SCROLL_RATE, timer);
2252 } else if (direction == ST.COLUMN_PREVIOUS) {
2253 timer = new Runnable() {
2256 /* Bug 437357 - NPE in StyledText.getCaretLine
2257 * StyledText.content is null at times, probably because the
2258 * widget itself has been disposed.
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);
2266 setBlockSelectionLocation(x + pixels, blockYLocation - getVerticalScrollOffset(), true);
2267 scrollHorizontal(pixels, true);
2271 setMouseWordSelectionAnchor();
2274 display.timerExec(H_SCROLL_RATE, this);
2278 autoScrollDirection = direction;
2279 display.timerExec(H_SCROLL_RATE, timer);
2283 * Deletes the previous character. Delete the selected text if any.
2284 * Move the caret in front of the deleted text.
2286 void doBackspace() {
2287 Event event = new Event();
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;
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;
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;
2316 sendKeyEvent(event);
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);
2326 offset += trailing[0];
2327 int lineIndex = content.getLineAtOffset(offset);
2330 newOffset = getClusterNext(offset, lineIndex);
2332 newOffset = getClusterPrevious(offset, lineIndex);
2334 offset = newOffset != offset ? newOffset : -1;
2337 setBlockSelectionOffset(offset, true);
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);
2348 void doBlockContentStartEnd(boolean end) {
2349 if (blockXLocation == -1) setBlockSelectionOffset(caretOffset, false);
2350 int offset = end ? content.getCharCount() : 0;
2351 setBlockSelectionOffset(offset, true);
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);
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;
2368 if (offset < lineOffset + lineLength) {
2369 newOffset = getWordNext(offset, SWT.MOVEMENT_WORD);
2372 if (offset > lineOffset) {
2373 newOffset = getWordPrevious(offset, SWT.MOVEMENT_WORD);
2376 offset = newOffset != offset ? newOffset : -1;
2379 setBlockSelectionOffset(offset, true);
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);
2390 void doBlockLineVertical(boolean up) {
2391 if (blockXLocation == -1) setBlockSelectionOffset(caretOffset, false);
2392 int y = blockYLocation - getVerticalScrollOffset();
2393 int lineIndex = getLineIndex(y);
2395 if (lineIndex > 0) {
2396 y = getLinePixel(lineIndex - 1);
2397 setBlockSelectionLocation(blockXLocation - horizontalScrollOffset, y, true);
2398 if (y < topMargin) {
2399 scrollVertical(y - topMargin, true);
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;
2409 scrollVertical(y - bottom, true);
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);
2425 offset += trailing[0];
2426 int newOffset = offset;
2428 if (offset < lineOffset + lineLength) {
2429 newOffset = lineOffset + lineLength;
2432 if (offset > lineOffset) {
2433 newOffset = lineOffset;
2436 offset = newOffset != offset ? newOffset : -1;
2438 if (!end) offset = lineOffset + lineLength;
2441 setBlockSelectionOffset(offset, true);
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);
2451 void doBlockSelection(boolean sendEvent) {
2452 if (caretOffset > selectionAnchor) {
2453 selection.x = selectionAnchor;
2454 selection.y = caretOffset;
2456 selection.x = caretOffset;
2457 selection.y = selectionAnchor;
2459 updateCaretVisibility();
2463 sendSelectionEvent();
2465 sendAccessibleTextCaretMoved();
2468 * Replaces the selection with the character or insert the character at the
2469 * current caret position if no selection exists.
2471 * If a carriage return was typed replace it with the line break character
2472 * used by the widget on this platform.
2475 * @param key the character typed by the user
2477 void doContent(char key) {
2478 if (blockSelection && blockXLocation != -1) {
2479 insertBlockSelectionText(key, SWT.NULL);
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();
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
2501 if (event.end < lineOffset + line.length()) {
2504 event.text = new String(new char[] {key});
2506 event.text = new String(new char[] {key});
2508 if (event.text != null) {
2509 if (textLimit > 0 && content.getCharCount() - (event.end - event.start) >= textLimit) {
2512 sendKeyEvent(event);
2516 * Moves the caret after the last character of the widget content.
2518 void doContentEnd() {
2519 // place caret at end of first line if receiver is in single
2520 // line mode. fixes 4820.
2521 if (isSingleLine()) {
2524 int length = content.getCharCount();
2525 setCaretOffset(length, SWT.DEFAULT);
2530 * Moves the caret in front of the first character of the widget content.
2532 void doContentStart() {
2533 setCaretOffset(0, SWT.DEFAULT);
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.
2541 * @see #doSelectionCursorPrevious
2543 void doCursorPrevious() {
2544 if (selection.y - selection.x > 0) {
2545 setCaretOffset(selection.x, OFFSET_LEADING);
2548 doSelectionCursorPrevious();
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.
2556 * @see #doSelectionCursorNext
2558 void doCursorNext() {
2559 if (selection.y - selection.x > 0) {
2560 setCaretOffset(selection.y, PREVIOUS_OFFSET_TRAILING);
2563 doSelectionCursorNext();
2567 * Deletes the next character. Delete the selected text if any.
2570 Event event = new Event();
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);
2584 event.start = caretOffset;
2585 event.end = getClusterNext(caretOffset, line);
2587 sendKeyEvent(event);
2591 * Deletes the next word.
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
2599 Event event = new Event();
2601 event.start = caretOffset;
2602 event.end = getWordNext(caretOffset, SWT.MOVEMENT_WORD);
2603 sendKeyEvent(event);
2607 * Deletes the previous word.
2609 void doDeleteWordPrevious() {
2610 if (selection.x != selection.y) {
2611 // if a selection exists, treat as if
2612 // only the backspace key was pressed
2615 Event event = new Event();
2617 event.start = getWordPrevious(caretOffset, SWT.MOVEMENT_WORD);
2618 event.end = caretOffset;
2619 sendKeyEvent(event);
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.
2628 void doLineDown(boolean select) {
2629 int caretLine = getCaretLine();
2630 int lineCount = content.getLineCount();
2632 boolean lastLine = false;
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;
2643 y = layout.getLineBounds(lineIndex + 1).y;
2644 y++; // bug 485722: workaround for fractional line heights
2646 renderer.disposeTextLayout(layout);
2648 lastLine = caretLine == lineCount - 1;
2652 setCaretOffset(content.getCharCount(), SWT.DEFAULT);
2654 int[] alignment = new int[1];
2655 int offset = getOffsetAtPoint(columnX, y, caretLine, alignment);
2656 setCaretOffset(offset, alignment[0]);
2658 int oldColumnX = columnX;
2659 int oldHScrollOffset = horizontalScrollOffset;
2661 setMouseWordSelectionAnchor();
2662 // select first and then scroll to reduce flash when key
2663 // repeat scrolls lots of lines
2664 doSelection(ST.COLUMN_NEXT);
2667 int hScrollChange = oldHScrollOffset - horizontalScrollOffset;
2668 columnX = oldColumnX + hScrollChange;
2671 * Moves the caret to the end of the line.
2674 int caretLine = getCaretLine();
2675 int lineOffset = content.getOffsetAtLine(caretLine);
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);
2685 int lineLength = content.getLine(caretLine).length();
2686 lineEndOffset = lineOffset + lineLength;
2688 setCaretOffset(lineEndOffset, PREVIOUS_OFFSET_TRAILING);
2692 * Moves the caret to the beginning of the line.
2694 void doLineStart() {
2695 int caretLine = getCaretLine();
2696 int lineOffset = content.getOffsetAtLine(caretLine);
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);
2705 setCaretOffset(lineOffset, OFFSET_LEADING);
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.
2714 void doLineUp(boolean select) {
2715 int caretLine = getCaretLine(), y = 0;
2716 boolean firstLine = false;
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;
2726 y = renderer.getLineHeight(caretLine) - 1;
2727 y--; // bug 485722: workaround for fractional line heights
2730 y = layout.getLineBounds(lineIndex - 1).y;
2731 y++; // bug 485722: workaround for fractional line heights
2733 renderer.disposeTextLayout(layout);
2735 firstLine = caretLine == 0;
2739 setCaretOffset(0, SWT.DEFAULT);
2741 int[] alignment = new int[1];
2742 int offset = getOffsetAtPoint(columnX, y, caretLine, alignment);
2743 setCaretOffset(offset, alignment[0]);
2745 int oldColumnX = columnX;
2746 int oldHScrollOffset = horizontalScrollOffset;
2747 if (select) setMouseWordSelectionAnchor();
2749 if (select) doSelection(ST.COLUMN_PREVIOUS);
2750 int hScrollChange = oldHScrollOffset - horizontalScrollOffset;
2751 columnX = oldColumnX + hScrollChange;
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);
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);
2766 if (cursor == null) {
2767 int type = blockSelection ? SWT.CURSOR_CROSS : SWT.CURSOR_IBEAM;
2768 newCursor = display.getSystemCursor(type);
2771 if (newCursor != getCursor()) super.setCursor(newCursor);
2774 * Moves the caret to the specified location.
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
2781 void doMouseLocationChange(int x, int y, boolean select) {
2782 int line = getLineIndex(y);
2784 updateCaretDirection = true;
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;
2792 Point left = getPointAtOffset(doubleClickSelection.x);
2793 int[] trailing = new int[1];
2794 int offset = getOffsetAtPoint(x, y, trailing, true);
2797 offset = getWordNext(offset + trailing[0], SWT.MOVEMENT_WORD_END);
2798 setBlockSelectionOffset(doubleClickSelection.x, offset, true);
2800 offset = getWordPrevious(offset + trailing[0], SWT.MOVEMENT_WORD_START);
2801 setBlockSelectionOffset(doubleClickSelection.y, offset, true);
2805 setBlockSelectionLocation(left.x, left.y, x, y, true);
2807 Point right = getPointAtOffset(doubleClickSelection.y);
2808 setBlockSelectionLocation(right.x, right.y, x, y, true);
2812 setBlockSelectionLocation(blockXLocation, y, true);
2817 if (blockXLocation == -1) {
2818 setBlockSelectionOffset(caretOffset, false);
2821 clearBlockSelection(true, false);
2823 int[] trailing = new int[1];
2824 int offset = getOffsetAtPoint(x, y, trailing, true);
2827 setBlockSelectionOffset(offset + trailing[0], true);
2831 if (isFixedLineHeight() && renderer.fixedPitch) {
2832 int avg = renderer.averageCharWidth;
2833 x = ((x + avg / 2 - leftMargin + horizontalScrollOffset) / avg * avg) + leftMargin - horizontalScrollOffset;
2835 setBlockSelectionLocation(x, y, true);
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)) {
2846 int[] alignment = new int[1];
2847 int newCaretOffset = getOffsetAtPoint(x, y, alignment);
2848 int newCaretAlignemnt = alignment[0];
2850 if (doubleClickEnabled && clickCount > 1) {
2851 newCaretOffset = doMouseWordSelect(x, newCaretOffset, line);
2854 int newCaretLine = content.getLineAtOffset(newCaretOffset);
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();
2867 setCaretOffset(newCaretOffset, newCaretAlignemnt);
2868 clearSelection(true);
2872 * Updates the selection based on the caret position
2874 void doMouseSelection() {
2875 if (caretOffset <= selection.x ||
2876 (caretOffset > selection.x &&
2877 caretOffset < selection.y && selectionAnchor == selection.x)) {
2878 doSelection(ST.COLUMN_PREVIOUS);
2880 doSelection(ST.COLUMN_NEXT);
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.
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
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;
2906 if (0 <= x && x < clientAreaWidth) {
2907 boolean wordSelect = (clickCount & 1) == 0;
2908 if (caretOffset == selection.x) {
2910 newCaretOffset = getWordPrevious(newCaretOffset, SWT.MOVEMENT_WORD_START);
2912 newCaretOffset = content.getOffsetAtLine(line);
2916 newCaretOffset = getWordNext(newCaretOffset, SWT.MOVEMENT_WORD_END);
2918 int lineEnd = content.getCharCount();
2919 if (line + 1 < content.getLineCount()) {
2920 lineEnd = content.getOffsetAtLine(line + 1);
2922 newCaretOffset = lineEnd;
2926 return newCaretOffset;
2929 * Scrolls one page down so that the last line (truncated or whole)
2930 * of the current page becomes the fully visible top line.
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.
2938 * @param select whether or not to select the page
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]);
2958 doSelection(ST.COLUMN_NEXT);
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;
2968 if (scrollOffset > verticalScrollOffset) {
2969 scrollVertical(scrollOffset - verticalScrollOffset, true);
2973 int lineCount = content.getLineCount();
2974 int caretLine = getCaretLine();
2975 int lineIndex, lineHeight;
2977 lineIndex = getPartialBottomIndex();
2978 int topY = getLinePixel(lineIndex);
2979 lineHeight = renderer.getLineHeight(lineIndex);
2981 if (topY + lineHeight <= clientAreaHeight) {
2982 height += lineHeight;
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)) {
2994 renderer.disposeTextLayout(layout);
2998 lineIndex = getLineIndex(height);
2999 int topLineY = getLinePixel(lineIndex);
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;
3010 renderer.disposeTextLayout(layout);
3012 height = topLineY + renderer.getLineHeight(lineIndex);
3015 int caretHeight = height;
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);
3023 lineIndex = caretLine;
3024 lineHeight = renderer.getLineHeight(lineIndex);
3025 while (caretHeight - lineHeight >= 0 && lineIndex < lineCount - 1) {
3026 caretHeight -= lineHeight;
3027 lineHeight = renderer.getLineHeight(++lineIndex);
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();
3038 int hScrollChange = oldHScrollOffset - horizontalScrollOffset;
3039 columnX = oldColumnX + hScrollChange;
3042 * Moves the cursor to the end of the last fully visible line.
3045 // go to end of line if in single line mode. fixes 5673
3046 if (isSingleLine()) {
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;
3060 if (index == -1 && lineIndex > 0) {
3061 bottomOffset = content.getOffsetAtLine(lineIndex - 1) + content.getLine(lineIndex - 1).length();
3063 bottomOffset = content.getOffsetAtLine(lineIndex) + Math.max(0, layout.getLineOffsets()[index + 1] - 1);
3065 renderer.disposeTextLayout(layout);
3067 int lineIndex = getBottomIndex();
3068 bottomOffset = content.getOffsetAtLine(lineIndex) + content.getLine(lineIndex).length();
3070 if (caretOffset < bottomOffset) {
3071 setCaretOffset(bottomOffset, OFFSET_LEADING);
3077 * Moves the cursor to the beginning of the first fully visible line.
3079 void doPageStart() {
3083 if (topIndexY > 0) {
3084 lineIndex = topIndex - 1;
3085 y = renderer.getLineHeight(lineIndex) - topIndexY;
3087 lineIndex = topIndex;
3090 TextLayout layout = renderer.getTextLayout(lineIndex);
3092 int lineCount = layout.getLineCount();
3093 while (index < lineCount) {
3094 Rectangle bounds = layout.getLineBounds(index);
3095 if (y <= bounds.y) break;
3098 if (index == lineCount) {
3099 topOffset = content.getOffsetAtLine(lineIndex + 1);
3101 topOffset = content.getOffsetAtLine(lineIndex) + layout.getLineOffsets()[index];
3103 renderer.disposeTextLayout(layout);
3105 topOffset = content.getOffsetAtLine(topIndex);
3107 if (caretOffset > topOffset) {
3108 setCaretOffset(topOffset, OFFSET_LEADING);
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.
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]);
3135 doSelection(ST.COLUMN_PREVIOUS);
3137 int verticalScrollOffset = getVerticalScrollOffset();
3138 int scrollOffset = Math.max(0, verticalScrollOffset - scrollLines * getVerticalIncrement());
3139 if (scrollOffset < verticalScrollOffset) {
3140 scrollVertical(scrollOffset - verticalScrollOffset, true);
3144 int caretLine = getCaretLine();
3145 int lineHeight, lineIndex;
3147 if (topIndexY == 0) {
3148 height = clientAreaHeight;
3152 lineIndex = topIndex - 1;
3153 lineHeight = renderer.getLineHeight(lineIndex);
3154 height = clientAreaHeight - topIndexY;
3155 y = lineHeight - topIndexY;
3157 lineIndex = topIndex;
3158 lineHeight = renderer.getLineHeight(lineIndex);
3159 height = clientAreaHeight - (lineHeight + topIndexY);
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);
3171 renderer.disposeTextLayout(layout);
3175 lineIndex = getLineIndex(clientAreaHeight - height);
3176 int topLineY = getLinePixel(lineIndex);
3178 TextLayout layout = renderer.getTextLayout(lineIndex);
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);
3187 renderer.disposeTextLayout(layout);
3189 height = clientAreaHeight - topLineY;
3192 int caretHeight = height;
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);
3200 lineIndex = caretLine;
3201 lineHeight = renderer.getLineHeight(lineIndex);
3202 while (caretHeight - lineHeight >= 0 && lineIndex > 0) {
3203 caretHeight -= lineHeight;
3204 lineHeight = renderer.getLineHeight(--lineIndex);
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();
3216 int hScrollChange = oldHScrollOffset - horizontalScrollOffset;
3217 columnX = oldColumnX + hScrollChange;
3220 * Updates the selection to extend to the current caret position.
3222 void doSelection(int direction) {
3223 int redrawStart = -1;
3225 if (selectionAnchor == -1) {
3226 selectionAnchor = selection.x;
3228 if (direction == ST.COLUMN_PREVIOUS) {
3229 if (caretOffset < selection.x) {
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;
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).
3242 redrawEnd = selection.y;
3243 redrawStart = selection.y = caretOffset;
3246 if (caretOffset > selection.y) {
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;
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).
3259 redrawStart = selection.x;
3260 redrawEnd = selection.x = caretOffset;
3263 if (redrawStart != -1 && redrawEnd != -1) {
3264 internalRedrawRange(redrawStart, redrawEnd - redrawStart);
3265 sendSelectionEvent();
3267 sendAccessibleTextCaretMoved();
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.
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);
3287 } else if (caretLine < content.getLineCount() - 1 && !isSingleLine()) {
3289 offset = content.getOffsetAtLine(caretLine);
3290 alignment = PREVIOUS_OFFSET_TRAILING;
3291 setCaretOffset(offset, alignment);
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.
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);
3307 } else if (caretLine > 0) {
3309 lineOffset = content.getOffsetAtLine(caretLine);
3310 int offset = lineOffset + content.getLine(caretLine).length();
3311 setCaretOffset(offset, OFFSET_LEADING);
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
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
3325 void doSelectionLineDown() {
3326 int oldColumnX = columnX = getPointAtOffset(caretOffset).x;
3328 columnX = oldColumnX;
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
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
3340 void doSelectionLineUp() {
3341 int oldColumnX = columnX = getPointAtOffset(caretOffset).x;
3343 columnX = oldColumnX;
3346 * Scrolls one page down so that the last line (truncated or whole)
3347 * of the current page becomes the fully visible top line.
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.
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
3359 void doSelectionPageDown(int pixels) {
3360 int oldColumnX = columnX = getPointAtOffset(caretOffset).x;
3361 doPageDown(true, pixels);
3362 columnX = oldColumnX;
3365 * Scrolls one page up so that the first line (truncated or whole)
3366 * of the current page becomes the fully visible last line.
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.
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
3378 void doSelectionPageUp(int pixels) {
3379 int oldColumnX = columnX = getPointAtOffset(caretOffset).x;
3380 doPageUp(true, pixels);
3381 columnX = oldColumnX;
3384 * Moves the caret to the end of the next word .
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);
3398 * Moves the caret to the start of the previous word.
3400 void doSelectionWordPrevious() {
3401 int offset = getWordPrevious(caretOffset, SWT.MOVEMENT_WORD);
3402 setCaretOffset(offset, OFFSET_LEADING);
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).
3411 void doVisualPrevious() {
3412 int offset = getClusterPrevious(caretOffset, getCaretLine());
3413 setCaretOffset(offset, SWT.DEFAULT);
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).
3422 void doVisualNext() {
3423 int offset = getClusterNext(caretOffset, getCaretLine());
3424 setCaretOffset(offset, SWT.DEFAULT);
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.
3433 if (selection.y - selection.x > 0) {
3434 setCaretOffset(selection.y, SWT.DEFAULT);
3437 doSelectionWordNext();
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.
3445 void doWordPrevious() {
3446 if (selection.y - selection.x > 0) {
3447 setCaretOffset(selection.x, SWT.DEFAULT);
3450 doSelectionWordPrevious();
3454 * Ends the autoscroll process.
3456 void endAutoScroll() {
3457 autoScrollDirection = SWT.NULL;
3460 public Color getBackground() {
3462 if (background == null) {
3463 return getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
3468 * Returns the baseline, in points.
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).
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>
3480 * @see #getBaseline(int)
3482 public int getBaseline() {
3484 return renderer.getBaseline();
3487 * Returns the baseline at the given offset, in points.
3489 * @param offset the offset
3491 * @return baseline the baseline
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>
3497 * @exception IllegalArgumentException <ul>
3498 * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
3503 public int getBaseline(int offset) {
3505 if (!(0 <= offset && offset <= content.getCharCount())) {
3506 SWT.error(SWT.ERROR_INVALID_RANGE);
3508 if (isFixedLineHeight()) {
3509 return renderer.getBaseline();
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();
3520 * Gets the BIDI coloring mode. When true the BIDI text display
3521 * algorithm is applied to segments of text that are the same
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>
3530 * @deprecated use BidiSegmentListener instead.
3533 public boolean getBidiColoring() {
3535 return bidiColoring;
3538 * Returns whether the widget is in block selection mode.
3540 * @return true if widget is in block selection mode, false otherwise
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>
3549 public boolean getBlockSelection() {
3551 return blockSelection;
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;
3561 int left = blockXAnchor;
3562 int right = blockXLocation;
3564 left = blockXLocation;
3565 right = blockXAnchor;
3567 return new Rectangle (left - horizontalScrollOffset, firstLine, right - horizontalScrollOffset, lastLine);
3570 * Returns the block selection bounds. The bounds is
3571 * relative to the upper left corner of the document.
3573 * @return the block selection bounds
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>
3582 public Rectangle getBlockSelectionBounds() {
3584 if (blockSelection && blockXLocation != -1) {
3585 rect = getBlockSelectionRectangle();
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();
3595 rect.x += horizontalScrollOffset;
3596 rect.y += getVerticalScrollOffset();
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;
3606 String getBlockSelectionText(String delimiter) {
3607 Rectangle rect = getBlockSelectionPosition();
3608 int firstLine = rect.y;
3609 int lastLine = rect.height;
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);
3621 String text = content.getTextRange(start, end - start);
3622 buffer.append(text);
3623 if (lineIndex < lastLine) buffer.append(delimiter);
3625 return buffer.toString();
3628 * Returns the index of the last fully visible line.
3630 * @return index of the last fully visible line.
3632 int getBottomIndex() {
3634 if (isFixedLineHeight()) {
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;
3642 bottomIndex = Math.min(content.getLineCount() - 1, topIndex + Math.max(0, lineCount - 1));
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) {
3659 * Returns the bottom margin.
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>
3669 public int getBottomMargin() {
3671 return bottomMargin;
3673 Rectangle getBoundsAtOffset(int offset) {
3674 int lineIndex = content.getLineAtOffset(offset);
3675 int lineOffset = content.getOffsetAtLine(lineIndex);
3676 String line = content.getLine(lineIndex);
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);
3687 renderer.disposeTextLayout(layout);
3689 bounds = new Rectangle (0, 0, 0, renderer.getLineHeight());
3691 if (offset == caretOffset && !isWordWrap()) {
3692 int lineEnd = lineOffset + line.length();
3693 if (offset == lineEnd) {
3694 bounds.width += getCaretWidth();
3697 bounds.x += leftMargin - horizontalScrollOffset;
3698 bounds.y += getLinePixel(lineIndex);
3702 * Returns the caret position relative to the start of the text.
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>
3710 public int getCaretOffset() {
3715 * Returns the caret width.
3717 * @return the caret width, 0 if caret is null.
3719 int getCaretWidth() {
3720 Caret caret = getCaret();
3721 if (caret == null) return 0;
3722 return caret.getSize().x;
3724 Object getClipboardContent(int clipboardType) {
3725 TextTransfer plainTextTransfer = TextTransfer.getInstance();
3726 return clipboard.getContents(plainTextTransfer, clipboardType);
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);
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);
3747 * Returns the content implementation that is used for text storage.
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>
3757 public StyledTextContent getContent() {
3762 public boolean getDragDetect () {
3767 * Returns whether the widget implements double click mouse behavior.
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>
3776 public boolean getDoubleClickEnabled() {
3778 return doubleClickEnabled;
3781 * Returns whether the widget content can be edited.
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>
3789 public boolean getEditable() {
3794 public Color getForeground() {
3796 if (foreground == null) {
3797 return getDisplay().getSystemColor(SWT.COLOR_LIST_FOREGROUND);
3802 * Returns the horizontal scroll increment.
3804 * @return horizontal scroll increment.
3806 int getHorizontalIncrement() {
3807 return renderer.averageCharWidth;
3810 * Returns the horizontal scroll offset relative to the start of the line.
3812 * @return horizontal scroll offset relative to the start of the line,
3813 * measured in character increments starting at 0, if > 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>
3819 public int getHorizontalIndex() {
3821 return horizontalScrollOffset / getHorizontalIncrement();
3824 * Returns the horizontal scroll offset relative to the start of the line.
3826 * @return the horizontal scroll offset relative to the start of the line,
3827 * measured in SWT logical point starting at 0, if > 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>
3833 public int getHorizontalPixel() {
3835 return horizontalScrollOffset;
3838 * Returns the line indentation of the widget.
3840 * @return the line indentation
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>
3847 * @see #getLineIndent(int)
3851 public int getIndent() {
3856 * Returns whether the widget justifies lines.
3858 * @return whether lines are justified
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>
3865 * @see #getLineJustify(int)
3869 public boolean getJustify() {
3874 * Returns the action assigned to the key.
3875 * Returns SWT.NULL if there is no action associated with the key.
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>
3889 public int getKeyBinding(int key) {
3891 Integer action = keyActionMap.get(key);
3892 return action == null ? SWT.NULL : action.intValue();
3895 * Gets the number of characters.
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>
3903 public int getCharCount() {
3905 return content.getCharCount();
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.
3913 * @param lineIndex index of the line to return.
3914 * @return the line text without delimiters
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>
3920 * @exception IllegalArgumentException <ul>
3921 * <li>ERROR_INVALID_RANGE when the line index is outside the valid range (< 0 or >= getLineCount())</li>
3925 public String getLine(int lineIndex) {
3927 if (lineIndex < 0 ||
3928 (lineIndex > 0 && lineIndex >= content.getLineCount())) {
3929 SWT.error(SWT.ERROR_INVALID_RANGE);
3931 return content.getLine(lineIndex);
3934 * Returns the alignment of the line at the given index.
3936 * @param index the index of the line
3938 * @return the line alignment
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>
3944 * @exception IllegalArgumentException <ul>
3945 * <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
3948 * @see #getAlignment()
3952 public int getLineAlignment(int index) {
3954 if (index < 0 || index > content.getLineCount()) {
3955 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3957 return renderer.getLineAlignment(index, alignment);
3960 * Returns the line at the specified offset in the text
3961 * where 0 < offset < getCharCount() so that getLineAtOffset(getCharCount())
3962 * returns the line of the insert location.
3964 * @param offset offset relative to the start of the content.
3965 * 0 <= offset <= 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>
3971 * @exception IllegalArgumentException <ul>
3972 * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
3975 public int getLineAtOffset(int offset) {
3977 if (offset < 0 || offset > getCharCount()) {
3978 SWT.error(SWT.ERROR_INVALID_RANGE);
3980 return content.getLineAtOffset(offset);
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.
3989 * @param index the index of the line
3990 * @return the background color of the line at the given index.
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>
3996 * @exception IllegalArgumentException <ul>
3997 * <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
4000 public Color getLineBackground(int index) {
4002 if (index < 0 || index > content.getLineCount()) {
4003 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4005 return isListening(ST.LineGetBackground) ? null : renderer.getLineBackground(index, null);
4008 * Returns the bullet of the line at the given index.
4010 * @param index the index of the line
4012 * @return the line bullet
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>
4018 * @exception IllegalArgumentException <ul>
4019 * <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
4024 public Bullet getLineBullet(int index) {
4026 if (index < 0 || index > content.getLineCount()) {
4027 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4029 return isListening(ST.LineGetStyle) ? null : renderer.getLineBullet(index, null);
4032 * Returns the line background data for the given line or null if
4035 * @param lineOffset offset of the line start relative to the start
4037 * @param line line to get line background data for
4038 * @return line background data for the given line.
4040 StyledTextEvent getLineBackgroundData(int lineOffset, String line) {
4041 return sendLineEvent(ST.LineGetBackground, lineOffset, line);
4044 * Gets the number of text lines.
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>
4052 public int getLineCount() {
4054 return content.getLineCount();
4057 * Returns the number of lines that can be completely displayed in the
4058 * widget client area.
4060 * @return number of lines that can be completely displayed in the widget
4063 int getLineCountWhole() {
4064 if (isFixedLineHeight()) {
4065 int lineHeight = renderer.getLineHeight();
4066 return lineHeight != 0 ? clientAreaHeight / lineHeight : 1;
4068 return getBottomIndex() - topIndex + 1;
4071 * Returns the line delimiter used for entering new lines by key down
4072 * or paste operation.
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>
4081 public String getLineDelimiter() {
4083 return content.getLineDelimiter();
4086 * Returns the line height.
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).
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>
4097 * @see #getLineHeight(int)
4099 public int getLineHeight() {
4101 return renderer.getLineHeight();
4104 * Returns the line height at the given offset.
4106 * @param offset the offset
4108 * @return line height in points
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>
4114 * @exception IllegalArgumentException <ul>
4115 * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
4120 public int getLineHeight(int offset) {
4122 if (!(0 <= offset && offset <= content.getCharCount())) {
4123 SWT.error(SWT.ERROR_INVALID_RANGE);
4125 if (isFixedLineHeight()) {
4126 return renderer.getLineHeight();
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);
4137 * Returns the indentation of the line at the given index.
4139 * @param index the index of the line
4141 * @return the line indentation
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>
4147 * @exception IllegalArgumentException <ul>
4148 * <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
4155 public int getLineIndent(int index) {
4157 if (index < 0 || index > content.getLineCount()) {
4158 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4160 return isListening(ST.LineGetStyle) ? 0 : renderer.getLineIndent(index, indent);
4163 * Returns the vertical indentation of the line at the given index.
4165 * @param index the index of the line
4167 * @return the line vertical indentation
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>
4173 * @exception IllegalArgumentException <ul>
4174 * <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
4179 public int getLineVerticalIndent(int index) {
4181 if (index < 0 || index >= content.getLineCount()) {
4182 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4184 return isListening(ST.LineGetStyle) ? 0 : renderer.getLineVerticalIndent(index);
4187 * Returns whether the line at the given index is justified.
4189 * @param index the index of the line
4191 * @return whether the line is justified
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>
4197 * @exception IllegalArgumentException <ul>
4198 * <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
4201 * @see #getJustify()
4205 public boolean getLineJustify(int index) {
4207 if (index < 0 || index > content.getLineCount()) {
4208 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4210 return isListening(ST.LineGetStyle) ? false : renderer.getLineJustify(index, justify);
4213 * Returns the line spacing of the widget.
4215 * @return the line spacing
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>
4224 public int getLineSpacing() {
4229 * Returns the line style data for the given line or null if there is
4232 * If there is a LineStyleListener but it does not set any styles,
4233 * the StyledTextEvent.styles field will be initialized to an empty
4237 * @param lineOffset offset of the line start relative to the start of
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
4243 StyledTextEvent getLineStyleData(int lineOffset, String line) {
4244 return sendLineEvent(ST.LineGetStyle, lineOffset, line);
4247 * Returns the top SWT logical point, relative to the client area, of a given line.
4248 * Clamps out of ranges index.
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.
4254 * @return the top SWT logical point of a given line index
4258 public int getLinePixel(int lineIndex) {
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;
4266 if (lineIndex == topIndex) return topIndexY + topMargin;
4267 int height = topIndexY;
4268 if (lineIndex > topIndex) {
4269 for (int i = topIndex; i < lineIndex; i++) {
4270 height += renderer.getLineHeight(i);
4273 for (int i = topIndex - 1; i >= lineIndex; i--) {
4274 height -= renderer.getLineHeight(i);
4277 return height + topMargin;
4280 * Returns the line index for a y, relative to the client area.
4281 * The line index returned is always in the range 0..lineCount - 1.
4283 * @param y the y-coordinate point
4285 * @return the line index for a given y-coordinate point
4289 public int getLineIndex(int y) {
4292 if (isFixedLineHeight()) {
4293 int lineHeight = renderer.getLineHeight();
4294 int lineIndex = (y + getVerticalScrollOffset()) / lineHeight;
4295 int lineCount = content.getLineCount();
4296 lineIndex = Math.max(0, Math.min(lineCount - 1, lineIndex));
4299 if (y == topIndexY) return topIndex;
4300 int line = topIndex;
4301 if (y < topIndexY) {
4302 while (y < topIndexY && line > 0) {
4303 y += renderer.getLineHeight(--line);
4306 int lineCount = content.getLineCount();
4307 int lineHeight = renderer.getLineHeight(line);
4308 while (y - lineHeight >= topIndexY && line < lineCount - 1) {
4310 lineHeight = renderer.getLineHeight(++line);
4316 * Returns the tab stops of the line at the given <code>index</code>.
4318 * @param index the index of the line
4320 * @return the tab stops for the line
4322 * @exception SWTException <ul>
4323 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4324 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4326 * @exception IllegalArgumentException <ul>
4327 * <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
4330 * @see #getTabStops()
4334 public int[] getLineTabStops(int index) {
4336 if (index < 0 || index > content.getLineCount()) {
4337 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4339 if (isListening(ST.LineGetStyle)) return null;
4340 int[] tabs = renderer.getLineTabStops(index, null);
4341 if (tabs == null) tabs = this.tabs;
4342 if (tabs == null) return new int [] {renderer.tabWidth};
4343 int[] result = new int[tabs.length];
4344 System.arraycopy(tabs, 0, result, 0, tabs.length);
4348 * Returns the wrap indentation of the line at the given <code>index</code>.
4350 * @param index the index of the line
4352 * @return the wrap indentation
4354 * @exception SWTException <ul>
4355 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4356 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4358 * @exception IllegalArgumentException <ul>
4359 * <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
4362 * @see #getWrapIndent()
4366 public int getLineWrapIndent(int index) {
4368 if (index < 0 || index > content.getLineCount()) {
4369 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4371 return isListening(ST.LineGetStyle) ? 0 : renderer.getLineWrapIndent(index, wrapIndent);
4374 * Returns the left margin.
4376 * @return the left margin.
4377 * @exception SWTException <ul>
4378 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4379 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4384 public int getLeftMargin() {
4386 return leftMargin - alignmentMargin;
4389 * Returns the x, y location of the upper left corner of the character
4390 * bounding box at the specified offset in the text. The point is
4391 * relative to the upper left corner of the widget client area.
4393 * @param offset offset relative to the start of the content.
4394 * 0 <= offset <= getCharCount()
4395 * @return x, y location of the upper left corner of the character
4396 * bounding box at the specified offset in the text.
4397 * @exception SWTException <ul>
4398 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4399 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4401 * @exception IllegalArgumentException <ul>
4402 * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
4405 public Point getLocationAtOffset(int offset) {
4407 if (offset < 0 || offset > getCharCount()) {
4408 SWT.error(SWT.ERROR_INVALID_RANGE);
4410 return getPointAtOffset(offset);
4413 * Returns <code>true</code> if the mouse navigator is enabled.
4414 * When mouse navigator is enabled, the user can navigate through the widget by pressing the middle button and moving the cursor
4416 * @return the mouse navigator's enabled state
4418 * @exception SWTException <ul>
4419 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4420 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4426 public boolean getMouseNavigatorEnabled () {
4428 return mouseNavigator != null;
4431 * Returns the character offset of the first character of the given line.
4433 * @param lineIndex index of the line, 0 based relative to the first
4434 * line in the content. 0 <= lineIndex < getLineCount(), except
4435 * lineIndex may always be 0
4436 * @return offset offset of the first character of the line, relative to
4437 * the beginning of the document. The first character of the document is
4439 * When there are not any lines, getOffsetAtLine(0) is a valid call that
4441 * @exception SWTException <ul>
4442 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4443 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4445 * @exception IllegalArgumentException <ul>
4446 * <li>ERROR_INVALID_RANGE when the line index is outside the valid range (< 0 or >= getLineCount())</li>
4450 public int getOffsetAtLine(int lineIndex) {
4452 if (lineIndex < 0 ||
4453 (lineIndex > 0 && lineIndex >= content.getLineCount())) {
4454 SWT.error(SWT.ERROR_INVALID_RANGE);
4456 return content.getOffsetAtLine(lineIndex);
4459 * Returns the offset of the character at the given location relative
4460 * to the first character in the document.
4462 * The return value reflects the character offset that the caret will
4463 * be placed at if a mouse click occurred at the specified location.
4464 * If the x coordinate of the location is beyond the center of a character
4465 * the returned offset will be behind the character.
4468 * @param point the origin of character bounding box relative to
4469 * the origin of the widget client area.
4470 * @return offset of the character at the given location relative
4471 * to the first character in the document.
4472 * @exception SWTException <ul>
4473 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4474 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4476 * @exception IllegalArgumentException <ul>
4477 * <li>ERROR_NULL_ARGUMENT when point is null</li>
4478 * <li>ERROR_INVALID_ARGUMENT when there is no character at the specified location</li>
4481 * @deprecated Use {@link #getOffsetAtPoint(Point)} instead for better performance
4484 public int getOffsetAtLocation(Point point) {
4486 if (point == null) {
4487 SWT.error(SWT.ERROR_NULL_ARGUMENT);
4489 int[] trailing = new int[1];
4490 int offset = getOffsetAtPoint(point.x, point.y, trailing, true);
4492 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4494 return offset + trailing[0];
4498 * Returns the offset of the character at the given point relative
4499 * to the first character in the document.
4501 * The return value reflects the character offset that the caret will
4502 * be placed at if a mouse click occurred at the specified point.
4503 * If the x coordinate of the point is beyond the center of a character
4504 * the returned offset will be behind the character.
4506 * Note: This method is functionally similar to {@link #getOffsetAtLocation(Point)} except that
4507 * it does not throw an exception when no character is found and thus performs faster.
4509 * @param point the origin of character bounding box relative to
4510 * the origin of the widget client area.
4511 * @return offset of the character at the given point relative
4512 * to the first character in the document.
4513 * -1 when there is no character at the specified location.
4514 * @exception SWTException <ul>
4515 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4516 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4518 * @exception IllegalArgumentException <ul>
4519 * <li>ERROR_NULL_ARGUMENT when point is <code>null</code></li>
4524 public int getOffsetAtPoint(Point point) {
4526 if (point == null) {
4527 SWT.error(SWT.ERROR_NULL_ARGUMENT);
4529 int[] trailing = new int[1];
4530 int offset = getOffsetAtPoint(point.x, point.y, trailing, true);
4531 return offset != -1 ? offset + trailing[0] : -1;
4534 int getOffsetAtPoint(int x, int y, int[] alignment) {
4535 int lineIndex = getLineIndex(y);
4536 y -= getLinePixel(lineIndex);
4537 return getOffsetAtPoint(x, y, lineIndex, alignment);
4539 int getOffsetAtPoint(int x, int y, int lineIndex, int[] alignment) {
4540 TextLayout layout = renderer.getTextLayout(lineIndex);
4541 x += horizontalScrollOffset - leftMargin;
4542 int[] trailing = new int[1];
4543 int offsetInLine = layout.getOffset(x, y, trailing);
4544 if (alignment != null) alignment[0] = OFFSET_LEADING;
4545 if (trailing[0] != 0) {
4546 int lineInParagraph = layout.getLineIndex(offsetInLine + trailing[0]);
4547 int lineStart = layout.getLineOffsets()[lineInParagraph];
4548 if (offsetInLine + trailing[0] == lineStart) {
4549 offsetInLine += trailing[0];
4550 if (alignment != null) alignment[0] = PREVIOUS_OFFSET_TRAILING;
4552 String line = content.getLine(lineIndex);
4554 if (alignment != null) {
4555 int offset = offsetInLine;
4556 while (offset > 0 && Character.isDigit(line.charAt(offset))) offset--;
4557 if (offset == 0 && Character.isDigit(line.charAt(offset))) {
4558 level = isMirrored() ? 1 : 0;
4560 level = layout.getLevel(offset) & 0x1;
4563 offsetInLine += trailing[0];
4564 if (alignment != null) {
4565 int trailingLevel = layout.getLevel(offsetInLine) & 0x1;
4566 if (level != trailingLevel) {
4567 alignment[0] = PREVIOUS_OFFSET_TRAILING;
4569 alignment[0] = OFFSET_LEADING;
4574 renderer.disposeTextLayout(layout);
4575 return offsetInLine + content.getOffsetAtLine(lineIndex);
4577 int getOffsetAtPoint(int x, int y, int[] trailing, boolean inTextOnly) {
4578 if (inTextOnly && y + getVerticalScrollOffset() < 0 || x + horizontalScrollOffset < 0) {
4581 int bottomIndex = getPartialBottomIndex();
4582 int height = getLinePixel(bottomIndex + 1);
4583 if (inTextOnly && y > height) {
4586 int lineIndex = getLineIndex(y);
4587 int lineOffset = content.getOffsetAtLine(lineIndex);
4588 TextLayout layout = renderer.getTextLayout(lineIndex);
4589 x += horizontalScrollOffset - leftMargin;
4590 y -= getLinePixel(lineIndex);
4591 int offset = layout.getOffset(x, y, trailing);
4592 Rectangle rect = layout.getLineBounds(layout.getLineIndex(offset));
4593 renderer.disposeTextLayout(layout);
4594 if (inTextOnly && !(rect.x <= x && x <= rect.x + rect.width)) {
4597 return offset + lineOffset;
4600 * Returns the orientation of the receiver.
4602 * @return the orientation style
4604 * @exception SWTException <ul>
4605 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4606 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4612 public int getOrientation () {
4613 return super.getOrientation ();
4616 * Returns the index of the last partially visible line.
4618 * @return index of the last partially visible line.
4620 int getPartialBottomIndex() {
4621 if (isFixedLineHeight()) {
4622 int lineHeight = renderer.getLineHeight();
4623 int partialLineCount = Compatibility.ceil(clientAreaHeight, lineHeight);
4624 return Math.max(0, Math.min(content.getLineCount(), topIndex + partialLineCount) - 1);
4626 return getLineIndex(clientAreaHeight - bottomMargin);
4629 * Returns the index of the first partially visible line.
4631 * @return index of the first partially visible line.
4633 int getPartialTopIndex() {
4634 if (isFixedLineHeight()) {
4635 int lineHeight = renderer.getLineHeight();
4636 return getVerticalScrollOffset() / lineHeight;
4638 return topIndexY <= 0 ? topIndex : topIndex - 1;
4641 * Returns the content in the specified range using the platform line
4642 * delimiter to separate lines.
4644 * @param writer the TextWriter to write line text into
4645 * @return the content in the specified range using the platform line
4646 * delimiter to separate lines as written by the specified TextWriter.
4648 String getPlatformDelimitedText(TextWriter writer) {
4649 int end = writer.getStart() + writer.getCharCount();
4650 int startLine = content.getLineAtOffset(writer.getStart());
4651 int endLine = content.getLineAtOffset(end);
4652 String endLineText = content.getLine(endLine);
4653 int endLineOffset = content.getOffsetAtLine(endLine);
4655 for (int i = startLine; i <= endLine; i++) {
4656 writer.writeLine(content.getLine(i), content.getOffsetAtLine(i));
4658 writer.writeLineDelimiter(PlatformLineDelimiter);
4661 if (end > endLineOffset + endLineText.length()) {
4662 writer.writeLineDelimiter(PlatformLineDelimiter);
4665 return writer.toString();
4668 * Returns all the ranges of text that have an associated StyleRange.
4669 * Returns an empty array if a LineStyleListener has been set.
4670 * Should not be called if a LineStyleListener has been set since the
4671 * listener maintains the styles.
4673 * The ranges array contains start and length pairs. Each pair refers to
4674 * the corresponding style in the styles array. For example, the pair
4675 * that starts at ranges[n] with length ranges[n+1] uses the style
4676 * at styles[n/2] returned by <code>getStyleRanges(int, int, boolean)</code>.
4679 * @return the ranges or an empty array if a LineStyleListener has been set.
4681 * @exception SWTException <ul>
4682 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4683 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4688 * @see #getStyleRanges(boolean)
4690 public int[] getRanges() {
4692 if (!isListening(ST.LineGetStyle)) {
4693 int[] ranges = renderer.getRanges(0, content.getCharCount());
4694 if (ranges != null) return ranges;
4699 * Returns the ranges of text that have an associated StyleRange.
4700 * Returns an empty array if a LineStyleListener has been set.
4701 * Should not be called if a LineStyleListener has been set since the
4702 * listener maintains the styles.
4704 * The ranges array contains start and length pairs. Each pair refers to
4705 * the corresponding style in the styles array. For example, the pair
4706 * that starts at ranges[n] with length ranges[n+1] uses the style
4707 * at styles[n/2] returned by <code>getStyleRanges(int, int, boolean)</code>.
4710 * @param start the start offset of the style ranges to return
4711 * @param length the number of style ranges to return
4713 * @return the ranges or an empty array if a LineStyleListener has been set.
4715 * @exception SWTException <ul>
4716 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4717 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4719 * @exception IllegalArgumentException <ul>
4720 * <li>ERROR_INVALID_RANGE if start or length are outside the widget content</li>
4725 * @see #getStyleRanges(int, int, boolean)
4727 public int[] getRanges(int start, int length) {
4729 int contentLength = getCharCount();
4730 int end = start + length;
4731 if (start > end || start < 0 || end > contentLength) {
4732 SWT.error(SWT.ERROR_INVALID_RANGE);
4734 if (!isListening(ST.LineGetStyle)) {
4735 int[] ranges = renderer.getRanges(start, length);
4736 if (ranges != null) return ranges;
4741 * Returns the right margin.
4743 * @return the right margin.
4744 * @exception SWTException <ul>
4745 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4746 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4751 public int getRightMargin() {
4756 * Returns the selection.
4758 * Text selections are specified in terms of caret positions. In a text
4759 * widget that contains N characters, there are N+1 caret positions,
4763 * @return start and end of the selection, x is the offset of the first
4764 * selected character, y is the offset after the last selected character.
4765 * The selection values returned are visual (i.e., x will always always be
4766 * <= y). To determine if a selection is right-to-left (RtoL) vs. left-to-right
4767 * (LtoR), compare the caretOffset to the start and end of the selection
4768 * (e.g., caretOffset == start of selection implies that the selection is RtoL).
4769 * @see #getSelectionRange
4770 * @exception SWTException <ul>
4771 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4772 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4775 public Point getSelection() {
4777 return new Point(selection.x, selection.y);
4780 * Returns the selection.
4782 * @return start and length of the selection, x is the offset of the
4783 * first selected character, relative to the first character of the
4784 * widget content. y is the length of the selection.
4785 * The selection values returned are visual (i.e., length will always always be
4786 * positive). To determine if a selection is right-to-left (RtoL) vs. left-to-right
4787 * (LtoR), compare the caretOffset to the start and end of the selection
4788 * (e.g., caretOffset == start of selection implies that the selection is RtoL).
4789 * @exception SWTException <ul>
4790 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4791 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4794 public Point getSelectionRange() {
4796 return new Point(selection.x, selection.y - selection.x);
4799 * Returns the ranges of text that are inside the block selection rectangle.
4801 * The ranges array contains start and length pairs. When the receiver is not
4802 * in block selection mode the return arrays contains the start and length of
4803 * the regular selection.
4805 * @return the ranges array
4807 * @exception SWTException <ul>
4808 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4809 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4814 public int[] getSelectionRanges() {
4816 if (blockSelection && blockXLocation != -1) {
4817 Rectangle rect = getBlockSelectionPosition();
4818 int firstLine = rect.y;
4819 int lastLine = rect.height;
4821 int right = rect.width;
4822 int[] ranges = new int[(lastLine - firstLine + 1) * 2];
4824 for (int lineIndex = firstLine; lineIndex <= lastLine; lineIndex++) {
4825 int start = getOffsetAtPoint(left, 0, lineIndex, null);
4826 int end = getOffsetAtPoint(right, 0, lineIndex, null);
4832 ranges[index++] = start;
4833 ranges[index++] = end - start;
4837 return new int[] {selection.x, selection.y - selection.x};
4840 * Returns the receiver's selection background color.
4842 * @return the selection background color
4844 * @exception SWTException <ul>
4845 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4846 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4850 public Color getSelectionBackground() {
4852 if (selectionBackground == null) {
4853 return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION);
4855 return selectionBackground;
4858 * Gets the number of selected characters.
4860 * @return the number of selected characters.
4861 * @exception SWTException <ul>
4862 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4863 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4866 public int getSelectionCount() {
4868 if (blockSelection && blockXLocation != -1) {
4869 return getBlockSelectionText(content.getLineDelimiter()).length();
4871 return getSelectionRange().y;
4874 * Returns the receiver's selection foreground color.
4876 * @return the selection foreground color
4878 * @exception SWTException <ul>
4879 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4880 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4884 public Color getSelectionForeground() {
4886 if (selectionForeground == null) {
4887 return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT);
4889 return selectionForeground;
4892 * Returns the selected text.
4894 * @return selected text, or an empty String if there is no selection.
4895 * @exception SWTException <ul>
4896 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4897 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4900 public String getSelectionText() {
4902 if (blockSelection && blockXLocation != -1) {
4903 return getBlockSelectionText(content.getLineDelimiter());
4905 return content.getTextRange(selection.x, selection.y - selection.x);
4907 StyledTextEvent getBidiSegments(int lineOffset, String line) {
4908 if (!isListening(ST.LineGetSegments)) {
4909 if (!bidiColoring) return null;
4910 StyledTextEvent event = new StyledTextEvent(content);
4911 event.segments = getBidiSegmentsCompatibility(line, lineOffset);
4914 StyledTextEvent event = sendLineEvent(ST.LineGetSegments, lineOffset, line);
4915 if (event == null || event.segments == null || event.segments.length == 0) return null;
4916 int lineLength = line.length();
4917 int[] segments = event.segments;
4918 if (segments[0] > lineLength) {
4919 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4921 char[] segmentsChars = event.segmentsChars;
4922 boolean hasSegmentsChars = segmentsChars != null;
4923 for (int i = 1; i < segments.length; i++) {
4924 if ((hasSegmentsChars ? segments[i] < segments[i - 1] : segments[i] <= segments[i - 1]) || segments[i] > lineLength) {
4925 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4928 if (hasSegmentsChars && !visualWrap) {
4929 for (int i= 0; i < segmentsChars.length; i++) {
4930 if (segmentsChars[i] == '\n' || segmentsChars[i] == '\r') {
4939 * @see #getBidiSegments
4940 * Supports deprecated setBidiColoring API. Remove when API is removed.
4942 int [] getBidiSegmentsCompatibility(String line, int lineOffset) {
4943 int lineLength = line.length();
4944 StyleRange [] styles = null;
4945 StyledTextEvent event = getLineStyleData(lineOffset, line);
4946 if (event != null) {
4947 styles = event.styles;
4949 styles = renderer.getStyleRanges(lineOffset, lineLength, true);
4951 if (styles == null || styles.length == 0) {
4952 return new int[] {0, lineLength};
4955 while (k < styles.length && styles[k].start == 0 && styles[k].length == lineLength) {
4958 int[] offsets = new int[(styles.length - k) * 2 + 2];
4959 for (int i = k; i < styles.length; i++) {
4960 StyleRange style = styles[i];
4961 int styleLineStart = Math.max(style.start - lineOffset, 0);
4962 int styleLineEnd = Math.max(style.start + style.length - lineOffset, styleLineStart);
4963 styleLineEnd = Math.min (styleLineEnd, line.length ());
4964 if (i > 0 && count > 1 &&
4965 ((styleLineStart >= offsets[count-2] && styleLineStart <= offsets[count-1]) ||
4966 (styleLineEnd >= offsets[count-2] && styleLineEnd <= offsets[count-1])) &&
4967 style.similarTo(styles[i-1])) {
4968 offsets[count-2] = Math.min(offsets[count-2], styleLineStart);
4969 offsets[count-1] = Math.max(offsets[count-1], styleLineEnd);
4971 if (styleLineStart > offsets[count - 1]) {
4972 offsets[count] = styleLineStart;
4975 offsets[count] = styleLineEnd;
4979 // add offset for last non-colored segment in line, if any
4980 if (lineLength > offsets[count-1]) {
4981 offsets [count] = lineLength;
4984 if (count == offsets.length) {
4987 int [] result = new int [count];
4988 System.arraycopy (offsets, 0, result, 0, count);
4992 * Returns the style range at the given offset.
4994 * Returns null if a LineStyleListener has been set or if a style is not set
4996 * Should not be called if a LineStyleListener has been set since the
4997 * listener maintains the styles.
5000 * @param offset the offset to return the style for.
5001 * 0 <= offset < getCharCount() must be true.
5002 * @return a StyleRange with start == offset and length == 1, indicating
5003 * the style at the given offset. null if a LineStyleListener has been set
5004 * or if a style is not set for the given offset.
5005 * @exception SWTException <ul>
5006 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5007 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5009 * @exception IllegalArgumentException <ul>
5010 * <li>ERROR_INVALID_ARGUMENT when the offset is invalid</li>
5013 public StyleRange getStyleRangeAtOffset(int offset) {
5015 if (offset < 0 || offset >= getCharCount()) {
5016 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
5018 if (!isListening(ST.LineGetStyle)) {
5019 StyleRange[] ranges = renderer.getStyleRanges(offset, 1, true);
5020 if (ranges != null) return ranges[0];
5025 * Returns the styles.
5027 * Returns an empty array if a LineStyleListener has been set.
5028 * Should not be called if a LineStyleListener has been set since the
5029 * listener maintains the styles.
5031 * Note: Because a StyleRange includes the start and length, the
5032 * same instance cannot occur multiple times in the array of styles.
5033 * If the same style attributes, such as font and color, occur in
5034 * multiple StyleRanges, <code>getStyleRanges(boolean)</code>
5035 * can be used to get the styles without the ranges.
5038 * @return the styles or an empty array if a LineStyleListener has been set.
5040 * @exception SWTException <ul>
5041 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5042 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5045 * @see #getStyleRanges(boolean)
5047 public StyleRange[] getStyleRanges() {
5049 return getStyleRanges(0, content.getCharCount(), true);
5052 * Returns the styles.
5054 * Returns an empty array if a LineStyleListener has been set.
5055 * Should not be called if a LineStyleListener has been set since the
5056 * listener maintains the styles.
5058 * Note: When <code>includeRanges</code> is true, the start and length
5059 * fields of each StyleRange will be valid, however the StyleRange
5060 * objects may need to be cloned. When <code>includeRanges</code> is
5061 * false, <code>getRanges(int, int)</code> can be used to get the
5062 * associated ranges.
5065 * @param includeRanges whether the start and length field of the StyleRanges should be set.
5067 * @return the styles or an empty array if a LineStyleListener has been set.
5069 * @exception SWTException <ul>
5070 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5071 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5076 * @see #getRanges(int, int)
5077 * @see #setStyleRanges(int[], StyleRange[])
5079 public StyleRange[] getStyleRanges(boolean includeRanges) {
5081 return getStyleRanges(0, content.getCharCount(), includeRanges);
5084 * Returns the styles for the given text range.
5086 * Returns an empty array if a LineStyleListener has been set.
5087 * Should not be called if a LineStyleListener has been set since the
5088 * listener maintains the styles.
5090 * Note: Because the StyleRange includes the start and length, the
5091 * same instance cannot occur multiple times in the array of styles.
5092 * If the same style attributes, such as font and color, occur in
5093 * multiple StyleRanges, <code>getStyleRanges(int, int, boolean)</code>
5094 * can be used to get the styles without the ranges.
5096 * @param start the start offset of the style ranges to return
5097 * @param length the number of style ranges to return
5099 * @return the styles or an empty array if a LineStyleListener has
5100 * been set. The returned styles will reflect the given range. The first
5101 * returned <code>StyleRange</code> will have a starting offset >= start
5102 * and the last returned <code>StyleRange</code> will have an ending
5103 * offset <= start + length - 1
5105 * @exception SWTException <ul>
5106 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5107 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5109 * @exception IllegalArgumentException <ul>
5110 * <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
5113 * @see #getStyleRanges(int, int, boolean)
5117 public StyleRange[] getStyleRanges(int start, int length) {
5119 return getStyleRanges(start, length, true);
5122 * Returns the styles for the given text range.
5124 * Returns an empty array if a LineStyleListener has been set.
5125 * Should not be called if a LineStyleListener has been set since the
5126 * listener maintains the styles.
5128 * Note: When <code>includeRanges</code> is true, the start and length
5129 * fields of each StyleRange will be valid, however the StyleRange
5130 * objects may need to be cloned. When <code>includeRanges</code> is
5131 * false, <code>getRanges(int, int)</code> can be used to get the
5132 * associated ranges.
5135 * @param start the start offset of the style ranges to return
5136 * @param length the number of style ranges to return
5137 * @param includeRanges whether the start and length field of the StyleRanges should be set.
5139 * @return the styles or an empty array if a LineStyleListener has
5140 * been set. The returned styles will reflect the given range. The first
5141 * returned <code>StyleRange</code> will have a starting offset >= start
5142 * and the last returned <code>StyleRange</code> will have an ending
5143 * offset >= start + length - 1
5145 * @exception SWTException <ul>
5146 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5147 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5149 * @exception IllegalArgumentException <ul>
5150 * <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
5155 * @see #getRanges(int, int)
5156 * @see #setStyleRanges(int[], StyleRange[])
5158 public StyleRange[] getStyleRanges(int start, int length, boolean includeRanges) {
5160 int contentLength = getCharCount();
5161 int end = start + length;
5162 if (start > end || start < 0 || end > contentLength) {
5163 SWT.error(SWT.ERROR_INVALID_RANGE);
5165 if (!isListening(ST.LineGetStyle)) {
5166 StyleRange[] ranges = renderer.getStyleRanges(start, length, includeRanges);
5167 if (ranges != null) return ranges;
5169 return new StyleRange[0];
5172 * Returns the tab width measured in characters.
5174 * @return tab width measured in characters
5175 * @exception SWTException <ul>
5176 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5177 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5180 * @see #getTabStops()
5182 public int getTabs() {
5188 * Returns the tab list of the receiver.
5190 * @return the tab list
5191 * @exception SWTException <ul>
5192 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5193 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5198 public int[] getTabStops() {
5200 if (tabs == null) return new int [] {renderer.tabWidth};
5201 int[] result = new int[tabs.length];
5202 System.arraycopy(tabs, 0, result, 0, tabs.length);
5207 * Returns a copy of the widget content.
5209 * @return copy of the widget content
5210 * @exception SWTException <ul>
5211 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5212 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5215 public String getText() {
5217 return content.getTextRange(0, getCharCount());
5220 * Returns the widget content between the two offsets.
5222 * @param start offset of the first character in the returned String
5223 * @param end offset of the last character in the returned String
5224 * @return widget content starting at start and ending at end
5225 * @see #getTextRange(int,int)
5226 * @exception SWTException <ul>
5227 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5228 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5230 * @exception IllegalArgumentException <ul>
5231 * <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
5234 public String getText(int start, int end) {
5236 int contentLength = getCharCount();
5237 if (start < 0 || start >= contentLength || end < 0 || end >= contentLength || start > end) {
5238 SWT.error(SWT.ERROR_INVALID_RANGE);
5240 return content.getTextRange(start, end - start + 1);
5243 * Returns the smallest bounding rectangle that includes the characters between two offsets.
5245 * @param start offset of the first character included in the bounding box
5246 * @param end offset of the last character included in the bounding box
5247 * @return bounding box of the text between start and end
5248 * @exception SWTException <ul>
5249 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5250 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5252 * @exception IllegalArgumentException <ul>
5253 * <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
5257 public Rectangle getTextBounds(int start, int end) {
5259 int contentLength = getCharCount();
5260 if (start < 0 || start >= contentLength || end < 0 || end >= contentLength || start > end) {
5261 SWT.error(SWT.ERROR_INVALID_RANGE);
5263 int lineStart = content.getLineAtOffset(start);
5264 int lineEnd = content.getLineAtOffset(end);
5266 int y = getLinePixel(lineStart);
5268 int left = 0x7fffffff, right = 0;
5269 for (int i = lineStart; i <= lineEnd; i++) {
5270 int lineOffset = content.getOffsetAtLine(i);
5271 TextLayout layout = renderer.getTextLayout(i);
5272 int length = layout.getText().length();
5274 if (i == lineStart) {
5276 rect = layout.getBounds(start - lineOffset, end - lineOffset);
5278 rect = layout.getBounds(start - lineOffset, length);
5281 } else if (i == lineEnd) {
5282 rect = layout.getBounds(0, end - lineOffset);
5284 rect = layout.getBounds();
5286 left = Math.min(left, rect.x);
5287 right = Math.max(right, rect.x + rect.width);
5288 height += rect.height;
5290 height += renderer.getLineHeight();
5292 renderer.disposeTextLayout(layout);
5294 rect = new Rectangle (left, y, right-left, height);
5295 rect.x += leftMargin - horizontalScrollOffset;
5299 * Returns the widget content starting at start for length characters.
5301 * @param start offset of the first character in the returned String
5302 * @param length number of characters to return
5303 * @return widget content starting at start and extending length characters.
5304 * @exception SWTException <ul>
5305 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5306 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5308 * @exception IllegalArgumentException <ul>
5309 * <li>ERROR_INVALID_RANGE when start and/or length are outside the widget content</li>
5312 public String getTextRange(int start, int length) {
5314 int contentLength = getCharCount();
5315 int end = start + length;
5316 if (start > end || start < 0 || end > contentLength) {
5317 SWT.error(SWT.ERROR_INVALID_RANGE);
5319 return content.getTextRange(start, length);
5322 * Returns the maximum number of characters that the receiver is capable of holding.
5324 * @return the text limit
5326 * @exception SWTException <ul>
5327 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5328 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5331 public int getTextLimit() {
5336 * Gets the top index.
5338 * The top index is the index of the fully visible line that is currently
5339 * at the top of the widget or the topmost partially visible line if no line is fully visible.
5340 * The top index changes when the widget is scrolled. Indexing is zero based.
5343 * @return the index of the top line
5344 * @exception SWTException <ul>
5345 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5346 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5349 public int getTopIndex() {
5354 * Returns the top margin.
5356 * @return the top margin.
5357 * @exception SWTException <ul>
5358 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5359 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5364 public int getTopMargin() {
5369 * Gets the top SWT logical point.
5371 * The top point is the SWT logical point position of the line that is
5372 * currently at the top of the widget. The text widget can be scrolled by points
5373 * by dragging the scroll thumb so that a partial line may be displayed at the top
5374 * the widget. The top point changes when the widget is scrolled. The top point
5375 * does not include the widget trimming.
5378 * @return SWT logical point position of the top line
5379 * @exception SWTException <ul>
5380 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5381 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5384 public int getTopPixel() {
5386 return getVerticalScrollOffset();
5389 * Returns the vertical scroll increment.
5391 * @return vertical scroll increment.
5393 int getVerticalIncrement() {
5394 return renderer.getLineHeight();
5396 int getVerticalScrollOffset() {
5397 if (verticalScrollOffset == -1) {
5398 renderer.calculate(0, topIndex);
5400 for (int i = 0; i < topIndex; i++) {
5401 height += renderer.getLineHeight(i);
5403 height -= topIndexY;
5404 verticalScrollOffset = height;
5406 return verticalScrollOffset;
5408 int getVisualLineIndex(TextLayout layout, int offsetInLine) {
5409 int lineIndex = layout.getLineIndex(offsetInLine);
5410 int[] offsets = layout.getLineOffsets();
5411 Caret caret = getCaret();
5412 if (caret != null && lineIndex != 0 && offsetInLine == offsets[lineIndex]) {
5413 int lineY = layout.getLineBounds(lineIndex).y;
5414 int caretY = caret.getLocation().y - getLinePixel(getCaretLine());
5415 if (lineY > caretY) lineIndex--;
5416 caretAlignment = OFFSET_LEADING;
5420 int getCaretDirection() {
5421 if (!isBidiCaret()) return SWT.DEFAULT;
5422 if (ime.getCompositionOffset() != -1) return SWT.DEFAULT;
5423 if (!updateCaretDirection && caretDirection != SWT.NULL) return caretDirection;
5424 updateCaretDirection = false;
5425 int caretLine = getCaretLine();
5426 int lineOffset = content.getOffsetAtLine(caretLine);
5427 String line = content.getLine(caretLine);
5428 int offset = caretOffset - lineOffset;
5429 int lineLength = line.length();
5430 if (lineLength == 0) return isMirrored() ? SWT.RIGHT : SWT.LEFT;
5431 if (caretAlignment == PREVIOUS_OFFSET_TRAILING && offset > 0) offset--;
5432 if (offset == lineLength && offset > 0) offset--;
5433 while (offset > 0 && Character.isDigit(line.charAt(offset))) offset--;
5434 if (offset == 0 && Character.isDigit(line.charAt(offset))) {
5435 return isMirrored() ? SWT.RIGHT : SWT.LEFT;
5437 TextLayout layout = renderer.getTextLayout(caretLine);
5438 int level = layout.getLevel(offset);
5439 renderer.disposeTextLayout(layout);
5440 return ((level & 1) != 0) ? SWT.RIGHT : SWT.LEFT;
5443 * Returns the index of the line the caret is on.
5445 int getCaretLine() {
5446 return content.getLineAtOffset(caretOffset);
5448 int getWrapWidth () {
5449 if (wordWrap && !isSingleLine()) {
5450 int width = clientAreaWidth - leftMargin - rightMargin;
5451 return width > 0 ? width : 1;
5455 int getWordNext (int offset, int movement) {
5456 return getWordNext(offset, movement, false);
5458 int getWordNext (int offset, int movement, boolean ignoreListener) {
5459 int newOffset, lineOffset;
5461 if (offset >= getCharCount()) {
5463 int lineIndex = content.getLineCount() - 1;
5464 lineOffset = content.getOffsetAtLine(lineIndex);
5465 lineText = content.getLine(lineIndex);
5467 int lineIndex = content.getLineAtOffset(offset);
5468 lineOffset = content.getOffsetAtLine(lineIndex);
5469 lineText = content.getLine(lineIndex);
5470 int lineLength = lineText.length();
5471 if (offset >= lineOffset + lineLength) {
5472 newOffset = content.getOffsetAtLine(lineIndex + 1);
5474 TextLayout layout = renderer.getTextLayout(lineIndex);
5475 newOffset = lineOffset + layout.getNextOffset(offset - lineOffset, movement);
5476 renderer.disposeTextLayout(layout);
5479 if (ignoreListener) return newOffset;
5480 return sendWordBoundaryEvent(ST.WordNext, movement, offset, newOffset, lineText, lineOffset);
5482 int getWordPrevious(int offset, int movement) {
5483 return getWordPrevious(offset, movement, false);
5485 int getWordPrevious(int offset, int movement, boolean ignoreListener) {
5486 int newOffset, lineOffset;
5490 int lineIndex = content.getLineAtOffset(newOffset);
5491 lineOffset = content.getOffsetAtLine(lineIndex);
5492 lineText = content.getLine(lineIndex);
5494 int lineIndex = content.getLineAtOffset(offset);
5495 lineOffset = content.getOffsetAtLine(lineIndex);
5496 lineText = content.getLine(lineIndex);
5497 if (offset == lineOffset) {
5498 String nextLineText = content.getLine(lineIndex - 1);
5499 int nextLineOffset = content.getOffsetAtLine(lineIndex - 1);
5500 newOffset = nextLineOffset + nextLineText.length();
5502 int layoutOffset = Math.min(offset - lineOffset, lineText.length());
5503 TextLayout layout = renderer.getTextLayout(lineIndex);
5504 newOffset = lineOffset + layout.getPreviousOffset(layoutOffset, movement);
5505 renderer.disposeTextLayout(layout);
5508 if (ignoreListener) return newOffset;
5509 return sendWordBoundaryEvent(ST.WordPrevious, movement, offset, newOffset, lineText, lineOffset);
5512 * Returns whether the widget wraps lines.
5514 * @return true if widget wraps lines, false otherwise
5517 public boolean getWordWrap() {
5522 * Returns the wrap indentation of the widget.
5524 * @return the wrap indentation
5526 * @exception SWTException <ul>
5527 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5528 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5531 * @see #getLineWrapIndent(int)
5535 public int getWrapIndent() {
5540 * Returns the location of the given offset.
5542 * <b>NOTE:</b> Does not return correct values for true italic fonts (vs. slanted fonts).
5545 * @return location of the character at the given offset in the line.
5547 Point getPointAtOffset(int offset) {
5548 int lineIndex = content.getLineAtOffset(offset);
5549 String line = content.getLine(lineIndex);
5550 int lineOffset = content.getOffsetAtLine(lineIndex);
5551 int offsetInLine = Math.max (0, offset - lineOffset);
5552 int lineLength = line.length();
5553 if (lineIndex < content.getLineCount() - 1) {
5554 int endLineOffset = content.getOffsetAtLine(lineIndex + 1) - 1;
5555 if (lineLength < offsetInLine && offsetInLine <= endLineOffset) {
5556 offsetInLine = lineLength;
5560 TextLayout layout = renderer.getTextLayout(lineIndex);
5561 if (lineLength != 0 && offsetInLine <= lineLength) {
5562 if (offsetInLine == lineLength) {
5563 offsetInLine = layout.getPreviousOffset(offsetInLine, SWT.MOVEMENT_CLUSTER);
5564 point = layout.getLocation(offsetInLine, true);
5566 switch (caretAlignment) {
5567 case OFFSET_LEADING:
5568 point = layout.getLocation(offsetInLine, false);
5570 case PREVIOUS_OFFSET_TRAILING:
5572 boolean lineBegin = offsetInLine == 0;
5573 // If word wrap is enabled, we should also consider offsets
5574 // of wrapped line parts as line begin and do NOT go back.
5575 // This prevents clients to jump one line higher than
5576 // expected, see bug 488172.
5577 // Respect caretAlignment at the caretOffset, unless there's
5578 // a non-empty selection, see bug 488172 comment 6.
5579 if (wordWrap && !lineBegin && (offset != caretOffset || selection.x != selection.y)) {
5580 int[] offsets = layout.getLineOffsets();
5581 for (int i : offsets) {
5582 if (i == offsetInLine) {
5589 point = layout.getLocation(offsetInLine, false);
5591 offsetInLine = layout.getPreviousOffset(offsetInLine, SWT.MOVEMENT_CLUSTER);
5592 point = layout.getLocation(offsetInLine, true);
5598 point = new Point(layout.getIndent(), layout.getVerticalIndent());
5600 renderer.disposeTextLayout(layout);
5601 point.x += leftMargin - horizontalScrollOffset;
5602 point.y += getLinePixel(lineIndex);
5606 * Inserts a string. The old selection is replaced with the new text.
5608 * @param string the string
5609 * @see #replaceTextRange(int,int,String)
5610 * @exception SWTException <ul>
5611 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5612 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5614 * @exception IllegalArgumentException <ul>
5615 * <li>ERROR_NULL_ARGUMENT when string is null</li>
5618 public void insert(String string) {
5620 if (string == null) {
5621 SWT.error(SWT.ERROR_NULL_ARGUMENT);
5623 if (blockSelection) {
5624 insertBlockSelectionText(string, false);
5626 Point sel = getSelectionRange();
5627 replaceTextRange(sel.x, sel.y, string);
5630 int insertBlockSelectionText(String text, boolean fillWithSpaces) {
5632 for (int i = 0; i < text.length(); i++) {
5633 char ch = text.charAt(i);
5634 if (ch == '\n' || ch == '\r') {
5636 if (ch == '\r' && i + 1 < text.length() && text.charAt(i + 1) == '\n') {
5641 String[] lines = new String[lineCount];
5644 for (int i = 0; i < text.length(); i++) {
5645 char ch = text.charAt(i);
5646 if (ch == '\n' || ch == '\r') {
5647 lines[lineCount++] = text.substring(start, i);
5648 if (ch == '\r' && i + 1 < text.length() && text.charAt(i + 1) == '\n') {
5654 lines[lineCount++] = text.substring(start);
5655 if (fillWithSpaces) {
5657 for (int i = 0; i < lines.length; i++) {
5658 int length = lines[i].length();
5659 maxLength = Math.max(maxLength, length);
5661 for (int i = 0; i < lines.length; i++) {
5662 String line = lines[i];
5663 int length = line.length();
5664 if (length < maxLength) {
5665 int numSpaces = maxLength - length;
5666 StringBuilder buffer = new StringBuilder(length + numSpaces);
5667 buffer.append(line);
5668 for (int j = 0; j < numSpaces; j++) buffer.append(' ');
5669 lines[i] = buffer.toString();
5673 int firstLine, lastLine, left, right;
5674 if (blockXLocation != -1) {
5675 Rectangle rect = getBlockSelectionPosition();
5677 lastLine = rect.height;
5681 firstLine = lastLine = getCaretLine();
5682 left = right = getPointAtOffset(caretOffset).x;
5684 start = caretOffset;
5685 int caretLine = getCaretLine();
5686 int index = 0, lineIndex = firstLine;
5687 while (lineIndex <= lastLine) {
5688 String string = index < lineCount ? lines[index++] : "";
5689 int lineStart = sendTextEvent(left, right, lineIndex, string, fillWithSpaces);
5690 if (lineIndex == caretLine) start = lineStart;
5693 while (index < lineCount) {
5694 int lineStart = sendTextEvent(left, left, lineIndex, lines[index++], fillWithSpaces);
5695 if (lineIndex == caretLine) start = lineStart;
5700 void insertBlockSelectionText(char key, int action) {
5701 if (key == SWT.CR || key == SWT.LF) return;
5702 Rectangle rect = getBlockSelectionPosition();
5703 int firstLine = rect.y;
5704 int lastLine = rect.height;
5706 int right = rect.width;
5707 int[] trailing = new int[1];
5708 int offset = 0, delta = 0;
5709 String text = key != 0 ? new String(new char[] {key}) : "";
5710 int length = text.length();
5711 for (int lineIndex = firstLine; lineIndex <= lastLine; lineIndex++) {
5712 String line = content.getLine(lineIndex);
5713 int lineOffset = content.getOffsetAtLine(lineIndex);
5714 int lineEndOffset = lineOffset + line.length();
5715 int linePixel = getLinePixel(lineIndex);
5716 int start = getOffsetAtPoint(left, linePixel, trailing, true);
5717 boolean outOfLine = start == -1;
5719 start = left < leftMargin ? lineOffset : lineEndOffset;
5721 start += trailing[0];
5723 int end = getOffsetAtPoint(right, linePixel, trailing, true);
5725 end = right < leftMargin ? lineOffset : lineEndOffset;
5734 if (start == end && !outOfLine) {
5736 case ST.DELETE_PREVIOUS:
5737 if (start > lineOffset) start = getClusterPrevious(start, lineIndex);
5739 case ST.DELETE_NEXT:
5740 if (end < lineEndOffset) end = getClusterNext(end, lineIndex);
5745 if (line.length() >= delta) {
5746 delta = line.length();
5747 offset = lineEndOffset + length;
5750 offset = start + length;
5751 delta = content.getCharCount();
5753 Event event = new Event();
5755 event.start = start;
5757 sendKeyEvent(event);
5759 int x = getPointAtOffset(offset).x;
5760 int verticalScrollOffset = getVerticalScrollOffset();
5761 setBlockSelectionLocation(x, blockYAnchor - verticalScrollOffset, x, blockYLocation - verticalScrollOffset, false);
5764 * Creates content change listeners and set the default content model.
5766 void installDefaultContent() {
5767 textChangeListener = new TextChangeListener() {
5769 public void textChanging(TextChangingEvent event) {
5770 handleTextChanging(event);
5773 public void textChanged(TextChangedEvent event) {
5774 handleTextChanged(event);
5777 public void textSet(TextChangedEvent event) {
5778 handleTextSet(event);
5781 content = new DefaultContent();
5782 content.addTextChangeListener(textChangeListener);
5785 * Adds event listeners
5787 void installListeners() {
5788 ScrollBar verticalBar = getVerticalBar();
5789 ScrollBar horizontalBar = getHorizontalBar();
5791 listener = event -> {
5792 switch (event.type) {
5793 case SWT.Dispose: handleDispose(event); break;
5794 case SWT.KeyDown: handleKeyDown(event); break;
5795 case SWT.KeyUp: handleKeyUp(event); break;
5796 case SWT.MenuDetect: handleMenuDetect(event); break;
5797 case SWT.MouseDown: handleMouseDown(event); break;
5798 case SWT.MouseUp: handleMouseUp(event); break;
5799 case SWT.MouseMove: handleMouseMove(event); break;
5800 case SWT.Paint: handlePaint(event); break;
5801 case SWT.Resize: handleResize(event); break;
5802 case SWT.Traverse: handleTraverse(event); break;
5805 addListener(SWT.Dispose, listener);
5806 addListener(SWT.KeyDown, listener);
5807 addListener(SWT.KeyUp, listener);
5808 addListener(SWT.MenuDetect, listener);
5809 addListener(SWT.MouseDown, listener);
5810 addListener(SWT.MouseUp, listener);
5811 addListener(SWT.MouseMove, listener);
5812 addListener(SWT.Paint, listener);
5813 addListener(SWT.Resize, listener);
5814 addListener(SWT.Traverse, listener);
5815 ime.addListener(SWT.ImeComposition, event -> {
5824 switch (event.detail) {
5825 case SWT.COMPOSITION_SELECTION: handleCompositionSelection(event); break;
5826 case SWT.COMPOSITION_CHANGED: handleCompositionChanged(event); break;
5827 case SWT.COMPOSITION_OFFSET: handleCompositionOffset(event); break;
5830 if (verticalBar != null) {
5831 verticalBar.addListener(SWT.Selection, event -> handleVerticalScroll(event));
5833 if (horizontalBar != null) {
5834 horizontalBar.addListener(SWT.Selection, event -> handleHorizontalScroll(event));
5837 void internalRedrawRange(int start, int length) {
5838 if (length <= 0) return;
5839 int end = start + length;
5840 int startLine = content.getLineAtOffset(start);
5841 int endLine = content.getLineAtOffset(end);
5842 int partialBottomIndex = getPartialBottomIndex();
5843 int partialTopIndex = getPartialTopIndex();
5844 if (startLine > partialBottomIndex || endLine < partialTopIndex) {
5847 if (partialTopIndex > startLine) {
5848 startLine = partialTopIndex;
5851 start -= content.getOffsetAtLine(startLine);
5853 if (partialBottomIndex < endLine) {
5854 endLine = partialBottomIndex + 1;
5857 end -= content.getOffsetAtLine(endLine);
5860 TextLayout layout = renderer.getTextLayout(startLine);
5861 int lineX = leftMargin - horizontalScrollOffset, startLineY = getLinePixel(startLine);
5862 int[] offsets = layout.getLineOffsets();
5863 int startIndex = layout.getLineIndex(Math.min(start, layout.getText().length()));
5865 /* Redraw end of line before start line if wrapped and start offset is first char */
5866 if (isWordWrap() && startIndex > 0 && offsets[startIndex] == start) {
5867 Rectangle rect = layout.getLineBounds(startIndex - 1);
5868 rect.x = rect.width;
5869 rect.width = clientAreaWidth - rightMargin - rect.x;
5871 rect.y += startLineY;
5872 super.redraw(rect.x, rect.y, rect.width, rect.height, false);
5875 if (startLine == endLine) {
5876 int endIndex = layout.getLineIndex(Math.min(end, layout.getText().length()));
5877 if (startIndex == endIndex) {
5878 /* Redraw rect between start and end offset if start and end offsets are in same wrapped line */
5879 Rectangle rect = layout.getBounds(start, end - 1);
5881 rect.y += startLineY;
5882 super.redraw(rect.x, rect.y, rect.width, rect.height, false);
5883 renderer.disposeTextLayout(layout);
5888 /* Redraw start line from the start offset to the end of client area */
5889 Rectangle startRect = layout.getBounds(start, offsets[startIndex + 1] - 1);
5890 if (startRect.height == 0) {
5891 Rectangle bounds = layout.getLineBounds(startIndex);
5892 startRect.x = bounds.width;
5893 startRect.y = bounds.y;
5894 startRect.height = bounds.height;
5896 startRect.x += lineX;
5897 startRect.y += startLineY;
5898 startRect.width = clientAreaWidth - rightMargin - startRect.x;
5899 super.redraw(startRect.x, startRect.y, startRect.width, startRect.height, false);
5901 /* Redraw end line from the beginning of the line to the end offset */
5902 if (startLine != endLine) {
5903 renderer.disposeTextLayout(layout);
5904 layout = renderer.getTextLayout(endLine);
5905 offsets = layout.getLineOffsets();
5907 int endIndex = layout.getLineIndex(Math.min(end, layout.getText().length()));
5908 Rectangle endRect = layout.getBounds(offsets[endIndex], end - 1);
5909 if (endRect.height == 0) {
5910 Rectangle bounds = layout.getLineBounds(endIndex);
5911 endRect.y = bounds.y;
5912 endRect.height = bounds.height;
5915 endRect.y += getLinePixel(endLine);
5916 super.redraw(endRect.x, endRect.y, endRect.width, endRect.height, false);
5917 renderer.disposeTextLayout(layout);
5919 /* Redraw all lines in between start and end line */
5920 int y = startRect.y + startRect.height;
5921 if (endRect.y > y) {
5922 super.redraw(leftMargin, y, clientAreaWidth - rightMargin - leftMargin, endRect.y - y, false);
5925 void handleCompositionOffset (Event event) {
5926 int[] trailing = new int [1];
5927 event.index = getOffsetAtPoint(event.x, event.y, trailing, true);
5928 event.count = trailing[0];
5930 void handleCompositionSelection (Event event) {
5931 if (event.start != event.end) {
5932 int charCount = getCharCount();
5933 event.start = Math.max(0, Math.min(event.start, charCount));
5934 event.end = Math.max(0, Math.min(event.end, charCount));
5935 if (event.text != null) {
5936 setSelection(event.start, event.end);
5938 event.text = getTextRange(event.start, event.end - event.start);
5941 event.start = selection.x;
5942 event.end = selection.y;
5943 event.text = getSelectionText();
5946 void handleCompositionChanged(Event event) {
5947 String text = event.text;
5948 int start = event.start;
5949 int end = event.end;
5950 int charCount = content.getCharCount();
5951 start = Math.min(start, charCount);
5952 end = Math.min(end, charCount);
5953 int length = text.length();
5954 if (length == ime.getCommitCount()) {
5955 content.replaceTextRange(start, end - start, "");
5956 setCaretOffset(ime.getCompositionOffset(), SWT.DEFAULT);
5958 caretDirection = SWT.NULL;
5960 content.replaceTextRange(start, end - start, text);
5961 int alignment = SWT.DEFAULT;
5962 if (ime.getWideCaret()) {
5963 start = ime.getCompositionOffset();
5964 int lineIndex = getCaretLine();
5965 int lineOffset = content.getOffsetAtLine(lineIndex);
5966 TextLayout layout = renderer.getTextLayout(lineIndex);
5967 caretWidth = layout.getBounds(start - lineOffset, start + length - 1 - lineOffset).width;
5968 renderer.disposeTextLayout(layout);
5969 alignment = OFFSET_LEADING;
5971 setCaretOffset(ime.getCaretOffset(), alignment);
5979 void handleDispose(Event event) {
5980 removeListener(SWT.Dispose, listener);
5981 notifyListeners(SWT.Dispose, event);
5982 event.type = SWT.None;
5984 clipboard.dispose();
5985 if (renderer != null) {
5989 if (content != null) {
5990 content.removeTextChangeListener(textChangeListener);
5993 if (defaultCaret != null) {
5994 defaultCaret.dispose();
5995 defaultCaret = null;
5997 if (leftCaretBitmap != null) {
5998 leftCaretBitmap.dispose();
5999 leftCaretBitmap = null;
6001 if (rightCaretBitmap != null) {
6002 rightCaretBitmap.dispose();
6003 rightCaretBitmap = null;
6005 if (isBidiCaret()) {
6006 BidiUtil.removeLanguageListener(this);
6008 selectionBackground = null;
6009 selectionForeground = null;
6011 textChangeListener = null;
6013 doubleClickSelection = null;
6014 keyActionMap = null;
6021 * Scrolls the widget horizontally.
6023 void handleHorizontalScroll(Event event) {
6024 int scrollPixel = getHorizontalBar().getSelection() - horizontalScrollOffset;
6025 scrollHorizontal(scrollPixel, false);
6028 * If an action has been registered for the key stroke execute the action.
6029 * Otherwise, if a character has been entered treat it as new content.
6031 * @param event keyboard event
6033 void handleKey(Event event) {
6035 caretAlignment = PREVIOUS_OFFSET_TRAILING;
6036 if (event.keyCode != 0) {
6037 // special key pressed (e.g., F1)
6038 action = getKeyBinding(event.keyCode | event.stateMask);
6040 // character key pressed
6041 action = getKeyBinding(event.character | event.stateMask);
6042 if (action == SWT.NULL) {
6043 // see if we have a control character
6044 if ((event.stateMask & SWT.CTRL) != 0 && event.character <= 31) {
6045 // get the character from the CTRL+char sequence, the control
6046 // key subtracts 64 from the value of the key that it modifies
6047 int c = event.character + 64;
6048 action = getKeyBinding(c | event.stateMask);
6052 if (action == SWT.NULL) {
6053 boolean ignore = false;
6056 // Ignore accelerator key combinations (we do not want to
6057 // insert a character in the text in this instance).
6058 ignore = (event.stateMask & (SWT.COMMAND | SWT.CTRL)) != 0;
6060 // Ignore accelerator key combinations (we do not want to
6061 // insert a character in the text in this instance). Don't
6062 // ignore CTRL+ALT combinations since that is the Alt Gr
6063 // key on some keyboards. See bug 20953.
6064 ignore = event.stateMask == SWT.ALT ||
6065 event.stateMask == SWT.CTRL ||
6066 event.stateMask == (SWT.ALT | SWT.SHIFT) ||
6067 event.stateMask == (SWT.CTRL | SWT.SHIFT);
6069 // -ignore anything below SPACE except for line delimiter keys and tab.
6071 if (!ignore && event.character > 31 && event.character != SWT.DEL ||
6072 event.character == SWT.CR || event.character == SWT.LF ||
6073 event.character == TAB) {
6074 doContent(event.character);
6078 invokeAction(action);
6082 * If a VerifyKey listener exists, verify that the key that was entered
6083 * should be processed.
6085 * @param event keyboard event
6087 void handleKeyDown(Event event) {
6088 if (clipboardSelection == null) {
6089 clipboardSelection = new Point(selection.x, selection.y);
6091 newOrientation = SWT.NONE;
6092 event.stateMask &= SWT.MODIFIER_MASK;
6094 Event verifyEvent = new Event();
6095 verifyEvent.character = event.character;
6096 verifyEvent.keyCode = event.keyCode;
6097 verifyEvent.keyLocation = event.keyLocation;
6098 verifyEvent.stateMask = event.stateMask;
6099 verifyEvent.doit = event.doit;
6100 notifyListeners(ST.VerifyKey, verifyEvent);
6101 if (verifyEvent.doit) {
6102 if ((event.stateMask & SWT.MODIFIER_MASK) == SWT.CTRL && event.keyCode == SWT.SHIFT && isBidiCaret()) {
6103 newOrientation = event.keyLocation == SWT.LEFT ? SWT.LEFT_TO_RIGHT : SWT.RIGHT_TO_LEFT;
6109 * Update the Selection Clipboard.
6111 * @param event keyboard event
6113 void handleKeyUp(Event event) {
6114 if (clipboardSelection != null) {
6115 if (clipboardSelection.x != selection.x || clipboardSelection.y != selection.y) {
6116 copySelection(DND.SELECTION_CLIPBOARD);
6119 clipboardSelection = null;
6121 if (newOrientation != SWT.NONE) {
6122 if (newOrientation != getOrientation()) {
6123 Event e = new Event();
6125 notifyListeners(SWT.OrientationChange, e);
6127 setOrientation(newOrientation);
6130 newOrientation = SWT.NONE;
6134 * Update the event location for focus-based context menu triggers.
6136 * @param event menu detect event
6138 void handleMenuDetect(Event event) {
6139 if (event.detail == SWT.MENU_KEYBOARD) {
6140 Point point = getDisplay().map(this, null, getPointAtOffset(caretOffset));
6142 event.y = point.y + getLineHeight(caretOffset);
6146 * Updates the caret location and selection if mouse button 1 has been
6149 void handleMouseDown(Event event) {
6150 //force focus (object support)
6154 if (dragDetect && checkDragDetect(event)) return;
6156 //paste clipboard selection
6157 if (event.button == 2) {
6158 // On GTK, if mouseNavigator is enabled we have to distinguish a short middle-click (to paste content) from
6159 // a long middle-click (mouse navigation started)
6160 if (IS_GTK && mouseNavigator != null) {
6161 middleClickPressed = true;
6162 getDisplay().timerExec(200, ()->{
6163 boolean click = middleClickPressed;
6164 middleClickPressed = false;
6165 if (click && mouseNavigator !=null) {
6166 mouseNavigator.onMouseDown(event);
6168 pasteOnMiddleClick(event);
6173 pasteOnMiddleClick(event);
6178 if ((event.button != 1) || (IS_MAC && (event.stateMask & SWT.MOD4) != 0)) {
6181 clickCount = event.count;
6182 if (clickCount == 1) {
6183 boolean select = (event.stateMask & SWT.MOD2) != 0;
6184 doMouseLocationChange(event.x, event.y, select);
6186 if (doubleClickEnabled) {
6187 boolean wordSelect = (clickCount & 1) == 0;
6188 int offset = getOffsetAtPoint(event.x, event.y, null);
6189 int lineIndex = content.getLineAtOffset(offset);
6190 int lineOffset = content.getOffsetAtLine(lineIndex);
6192 int min = blockSelection ? lineOffset : 0;
6193 int max = blockSelection ? lineOffset + content.getLine(lineIndex).length() : content.getCharCount();
6194 int start = Math.max(min, getWordPrevious(offset, SWT.MOVEMENT_WORD_START));
6195 int end = Math.min(max, getWordNext(start, SWT.MOVEMENT_WORD_END));
6196 setSelection(start, end - start, false, true);
6197 sendSelectionEvent();
6199 if (blockSelection) {
6200 setBlockSelectionLocation(leftMargin, event.y, clientAreaWidth - rightMargin, event.y, true);
6202 int lineEnd = content.getCharCount();
6203 if (lineIndex + 1 < content.getLineCount()) {
6204 lineEnd = content.getOffsetAtLine(lineIndex + 1);
6206 setSelection(lineOffset, lineEnd - lineOffset, false, false);
6207 sendSelectionEvent();
6210 doubleClickSelection = new Point(selection.x, selection.y);
6216 * Updates the caret location and selection if mouse button 1 is pressed
6217 * during the mouse move.
6219 void handleMouseMove(Event event) {
6220 if (clickCount > 0) {
6222 doAutoScroll(event);
6223 doMouseLocationChange(event.x, event.y, true);
6225 if (renderer.hasLinks) {
6226 doMouseLinkCursor(event.x, event.y);
6230 * Autoscrolling ends when the mouse button is released.
6232 void handleMouseUp(Event event) {
6233 middleClickPressed = false;
6236 if (event.button == 1) {
6237 copySelection(DND.SELECTION_CLIPBOARD);
6241 * Renders the invalidated area specified in the paint event.
6243 * @param event paint event
6245 void handlePaint(Event event) {
6246 if (event.width == 0 || event.height == 0) return;
6247 if (clientAreaWidth == 0 || clientAreaHeight == 0) return;
6249 int startLine = getLineIndex(event.y);
6250 int y = getLinePixel(startLine);
6251 int endY = event.y + event.height;
6253 Color background = getBackground();
6254 Color foreground = getForeground();
6256 int lineCount = isSingleLine() ? 1 : content.getLineCount();
6257 int x = leftMargin - horizontalScrollOffset;
6258 for (int i = startLine; y < endY && i < lineCount; i++) {
6259 y += renderer.drawLine(i, x, y, gc, background, foreground);
6262 gc.setBackground(background);
6263 drawBackground(gc, 0, y, clientAreaWidth, endY - y);
6266 if (blockSelection && blockXLocation != -1) {
6267 gc.setBackground(getSelectionBackground());
6268 Rectangle rect = getBlockSelectionRectangle();
6269 gc.drawRectangle(rect.x, rect.y, Math.max(1, rect.width - 1), Math.max(1, rect.height - 1));
6270 gc.setAdvanced(true);
6271 if (gc.getAdvanced()) {
6273 gc.fillRectangle(rect);
6274 gc.setAdvanced(false);
6278 // fill the margin background
6279 gc.setBackground(marginColor != null ? marginColor : background);
6280 if (topMargin > 0) {
6281 drawBackground(gc, 0, 0, clientAreaWidth, topMargin);
6283 if (bottomMargin > 0) {
6284 drawBackground(gc, 0, clientAreaHeight - bottomMargin, clientAreaWidth, bottomMargin);
6286 if (leftMargin - alignmentMargin > 0) {
6287 drawBackground(gc, 0, 0, leftMargin - alignmentMargin, clientAreaHeight);
6289 if (rightMargin > 0) {
6290 drawBackground(gc, clientAreaWidth - rightMargin, 0, rightMargin, clientAreaHeight);
6294 * Recalculates the scroll bars. Rewraps all lines when in word
6297 * @param event resize event
6299 void handleResize(Event event) {
6300 int oldHeight = clientAreaHeight;
6301 int oldWidth = clientAreaWidth;
6302 Rectangle clientArea = getClientArea();
6303 clientAreaHeight = clientArea.height;
6304 clientAreaWidth = clientArea.width;
6305 if (!alwaysShowScroll && ignoreResize != 0) return;
6307 redrawMargins(oldHeight, oldWidth);
6309 if (oldWidth != clientAreaWidth) {
6310 renderer.reset(0, content.getLineCount());
6311 verticalScrollOffset = -1;
6312 renderer.calculateIdle();
6315 if (oldHeight != clientAreaHeight) {
6316 if (oldHeight == 0) topIndexY = 0;
6317 setScrollBars(true);
6321 renderer.calculateClientArea();
6322 setScrollBars(true);
6323 claimRightFreeSpace();
6324 // StyledText allows any value for horizontalScrollOffset when clientArea is zero
6325 // in setHorizontalPixel() and setHorisontalOffset(). Fixes bug 168429.
6326 if (clientAreaWidth != 0) {
6327 ScrollBar horizontalBar = getHorizontalBar();
6328 if (horizontalBar != null && horizontalBar.getVisible()) {
6329 if (horizontalScrollOffset != horizontalBar.getSelection()) {
6330 horizontalBar.setSelection(horizontalScrollOffset);
6331 horizontalScrollOffset = horizontalBar.getSelection();
6336 updateCaretVisibility();
6337 claimBottomFreeSpace();
6339 //TODO FIX TOP INDEX DURING RESIZE
6340 // if (oldHeight != clientAreaHeight || wordWrap) {
6341 // calculateTopIndex(0);
6345 * Updates the caret position and selection and the scroll bars to reflect
6346 * the content change.
6348 void handleTextChanged(TextChangedEvent event) {
6349 int offset = ime.getCompositionOffset();
6350 if (offset != -1 && lastTextChangeStart < offset) {
6351 ime.setCompositionOffset(offset + lastTextChangeNewCharCount - lastTextChangeReplaceCharCount);
6353 int firstLine = content.getLineAtOffset(lastTextChangeStart);
6354 resetCache(firstLine, 0);
6355 if (!isFixedLineHeight() && topIndex > firstLine) {
6356 topIndex = firstLine;
6358 // TODO: This logging is in place to determine why topIndex is getting set to negative values.
6359 // It should be deleted once we fix the root cause of this issue. See bug 487254 for details.
6360 System.err.println("StyledText: topIndex was " + topIndex
6361 + ", lastTextChangeStart = " + lastTextChangeStart
6362 + ", content.getClass() = " + content.getClass()
6369 int lastLine = firstLine + lastTextChangeNewLineCount;
6370 int firstLineTop = getLinePixel(firstLine);
6371 int newLastLineBottom = getLinePixel(lastLine + 1);
6372 if (lastLineBottom != newLastLineBottom) {
6375 super.redraw(0, firstLineTop, clientAreaWidth, newLastLineBottom - firstLineTop, false);
6376 redrawLinesBullet(renderer.redrawLines);
6379 renderer.redrawLines = null;
6380 // update selection/caret location after styles have been changed.
6381 // otherwise any text measuring could be incorrect
6383 // also, this needs to be done after all scrolling. Otherwise,
6384 // selection redraw would be flushed during scroll which is wrong.
6385 // in some cases new text would be drawn in scroll source area even
6386 // though the intent is to scroll it.
6387 if (!(blockSelection && blockXLocation != -1)) {
6388 updateSelection(lastTextChangeStart, lastTextChangeReplaceCharCount, lastTextChangeNewCharCount);
6390 if (lastTextChangeReplaceLineCount > 0 || wordWrap || visualWrap) {
6391 claimBottomFreeSpace();
6393 if (lastTextChangeReplaceCharCount > 0) {
6394 claimRightFreeSpace();
6397 sendAccessibleTextChanged(lastTextChangeStart, lastTextChangeNewCharCount, 0);
6398 lastCharCount += lastTextChangeNewCharCount;
6399 lastCharCount -= lastTextChangeReplaceCharCount;
6403 * Updates the screen to reflect a pending content change.
6405 * @param event .start the start offset of the change
6406 * @param event .newText text that is going to be inserted or empty String
6407 * if no text will be inserted
6408 * @param event .replaceCharCount length of text that is going to be replaced
6409 * @param event .newCharCount length of text that is going to be inserted
6410 * @param event .replaceLineCount number of lines that are going to be replaced
6411 * @param event .newLineCount number of new lines that are going to be inserted
6413 void handleTextChanging(TextChangingEvent event) {
6414 if (event.replaceCharCount < 0) {
6415 event.start += event.replaceCharCount;
6416 event.replaceCharCount *= -1;
6418 lastTextChangeStart = event.start;
6419 lastTextChangeNewLineCount = event.newLineCount;
6420 lastTextChangeNewCharCount = event.newCharCount;
6421 lastTextChangeReplaceLineCount = event.replaceLineCount;
6422 lastTextChangeReplaceCharCount = event.replaceCharCount;
6423 int lineIndex = content.getLineAtOffset(event.start);
6424 int srcY = getLinePixel(lineIndex + event.replaceLineCount + 1);
6425 int destY = getLinePixel(lineIndex + 1) + event.newLineCount * renderer.getLineHeight();
6426 lastLineBottom = destY;
6427 if (srcY < 0 && destY < 0) {
6428 lastLineBottom += srcY - destY;
6429 verticalScrollOffset += destY - srcY;
6430 calculateTopIndex(destY - srcY);
6431 setScrollBars(true);
6433 scrollText(srcY, destY);
6435 sendAccessibleTextChanged(lastTextChangeStart, 0, lastTextChangeReplaceCharCount);
6436 renderer.textChanging(event);
6438 // Update the caret offset if it is greater than the length of the content.
6439 // This is necessary since style range API may be called between the
6440 // handleTextChanging and handleTextChanged events and this API sets the
6442 int newEndOfText = content.getCharCount() - event.replaceCharCount + event.newCharCount;
6443 if (caretOffset > newEndOfText) setCaretOffset(newEndOfText, SWT.DEFAULT);
6446 * Called when the widget content is set programmatically, overwriting
6447 * the old content. Resets the caret position, selection and scroll offsets.
6448 * Recalculates the content width and scroll bars. Redraws the widget.
6450 * @param event text change event.
6452 void handleTextSet(TextChangedEvent event) {
6454 int newCharCount = getCharCount();
6455 sendAccessibleTextChanged(0, newCharCount, lastCharCount);
6456 lastCharCount = newCharCount;
6460 * Called when a traversal key is pressed.
6461 * Allow tab next traversal to occur when the widget is in single
6462 * line mode or in multi line and non-editable mode .
6463 * When in editable multi line mode we want to prevent the tab
6464 * traversal and receive the tab key event instead.
6466 * @param event the event
6468 void handleTraverse(Event event) {
6469 switch (event.detail) {
6470 case SWT.TRAVERSE_ESCAPE:
6471 case SWT.TRAVERSE_PAGE_NEXT:
6472 case SWT.TRAVERSE_PAGE_PREVIOUS:
6475 case SWT.TRAVERSE_RETURN:
6476 case SWT.TRAVERSE_TAB_NEXT:
6477 case SWT.TRAVERSE_TAB_PREVIOUS:
6478 if ((getStyle() & SWT.SINGLE) != 0) {
6481 if (!editable || (event.stateMask & SWT.MODIFIER_MASK) != 0) {
6489 * Scrolls the widget vertically.
6491 void handleVerticalScroll(Event event) {
6492 int scrollPixel = getVerticalBar().getSelection() - getVerticalScrollOffset();
6493 scrollVertical(scrollPixel, false);
6496 * Add accessibility support for the widget.
6498 void initializeAccessible() {
6499 acc = getAccessible();
6501 accAdapter = new AccessibleAdapter() {
6503 public void getName (AccessibleEvent e) {
6505 String text = getAssociatedLabel ();
6507 name = stripMnemonic (text);
6512 public void getHelp(AccessibleEvent e) {
6513 e.result = getToolTipText();
6516 public void getKeyboardShortcut(AccessibleEvent e) {
6517 String shortcut = null;
6518 String text = getAssociatedLabel ();
6520 char mnemonic = _findMnemonic (text);
6521 if (mnemonic != '\0') {
6522 shortcut = "Alt+"+mnemonic; //$NON-NLS-1$
6525 e.result = shortcut;
6528 acc.addAccessibleListener(accAdapter);
6530 accTextExtendedAdapter = new AccessibleTextExtendedAdapter() {
6532 public void getCaretOffset(AccessibleTextEvent e) {
6533 e.offset = StyledText.this.getCaretOffset();
6536 public void setCaretOffset(AccessibleTextEvent e) {
6537 StyledText.this.setCaretOffset(e.offset);
6541 public void getSelectionRange(AccessibleTextEvent e) {
6542 Point selection = StyledText.this.getSelectionRange();
6543 e.offset = selection.x;
6544 e.length = selection.y;
6547 public void addSelection(AccessibleTextEvent e) {
6548 StyledText st = StyledText.this;
6549 Point point = st.getSelection();
6550 if (point.x == point.y) {
6552 if (end == -1) end = st.getCharCount();
6553 st.setSelection(e.start, end);
6558 public void getSelection(AccessibleTextEvent e) {
6559 StyledText st = StyledText.this;
6560 if (st.blockSelection && st.blockXLocation != -1) {
6561 Rectangle rect = st.getBlockSelectionPosition();
6562 int lineIndex = rect.y + e.index;
6563 int linePixel = st.getLinePixel(lineIndex);
6564 e.ranges = getRanges(rect.x, linePixel, rect.width, linePixel);
6565 if (e.ranges.length > 0) {
6566 e.start = e.ranges[0];
6567 e.end = e.ranges[e.ranges.length - 1];
6571 Point point = st.getSelection();
6574 if (e.start > e.end) {
6583 public void getSelectionCount(AccessibleTextEvent e) {
6584 StyledText st = StyledText.this;
6585 if (st.blockSelection && st.blockXLocation != -1) {
6586 Rectangle rect = st.getBlockSelectionPosition();
6587 e.count = rect.height - rect.y + 1;
6589 Point point = st.getSelection();
6590 e.count = point.x == point.y ? 0 : 1;
6594 public void removeSelection(AccessibleTextEvent e) {
6595 StyledText st = StyledText.this;
6597 if (st.blockSelection) {
6598 st.clearBlockSelection(true, false);
6600 st.clearSelection(false);
6606 public void setSelection(AccessibleTextEvent e) {
6607 if (e.index != 0) return;
6608 StyledText st = StyledText.this;
6609 Point point = st.getSelection();
6610 if (point.x == point.y) return;
6612 if (end == -1) end = st.getCharCount();
6613 st.setSelection(e.start, end);
6617 public void getCharacterCount(AccessibleTextEvent e) {
6618 e.count = StyledText.this.getCharCount();
6621 public void getOffsetAtPoint(AccessibleTextEvent e) {
6622 StyledText st = StyledText.this;
6623 Point point = new Point (e.x, e.y);
6624 Display display = st.getDisplay();
6625 point = display.map(null, st, point);
6626 e.offset = st.getOffsetAtPoint(point.x, point.y, null, true);
6629 public void getTextBounds(AccessibleTextEvent e) {
6630 StyledText st = StyledText.this;
6631 int start = e.start;
6633 int contentLength = st.getCharCount();
6634 start = Math.max(0, Math.min(start, contentLength));
6635 end = Math.max(0, Math.min(end, contentLength));
6641 int startLine = st.getLineAtOffset(start);
6642 int endLine = st.getLineAtOffset(end);
6643 Rectangle[] rects = new Rectangle[endLine - startLine + 1];
6644 Rectangle bounds = null;
6646 Display display = st.getDisplay();
6647 for (int lineIndex = startLine; lineIndex <= endLine; lineIndex++) {
6648 Rectangle rect = new Rectangle(0, 0, 0, 0);
6649 rect.y = st.getLinePixel(lineIndex);
6650 rect.height = st.renderer.getLineHeight(lineIndex);
6651 if (lineIndex == startLine) {
6652 rect.x = st.getPointAtOffset(start).x;
6654 rect.x = st.leftMargin - st.horizontalScrollOffset;
6656 if (lineIndex == endLine) {
6657 rect.width = st.getPointAtOffset(end).x - rect.x;
6659 TextLayout layout = st.renderer.getTextLayout(lineIndex);
6660 rect.width = layout.getBounds().width - rect.x;
6661 st.renderer.disposeTextLayout(layout);
6663 rects [index++] = rect = display.map(st, null, rect);
6664 if (bounds == null) {
6665 bounds = new Rectangle(rect.x, rect.y, rect.width, rect.height);
6670 e.rectangles = rects;
6671 if (bounds != null) {
6674 e.width = bounds.width;
6675 e.height = bounds.height;
6678 int[] getRanges(int left, int top, int right, int bottom) {
6679 StyledText st = StyledText.this;
6680 int lineStart = st.getLineIndex(top);
6681 int lineEnd = st.getLineIndex(bottom);
6682 int count = lineEnd - lineStart + 1;
6683 int[] ranges = new int [count * 2];
6685 for (int lineIndex = lineStart; lineIndex <= lineEnd; lineIndex++) {
6686 String line = st.content.getLine(lineIndex);
6687 int lineOffset = st.content.getOffsetAtLine(lineIndex);
6688 int lineEndOffset = lineOffset + line.length();
6689 int linePixel = st.getLinePixel(lineIndex);
6690 int start = st.getOffsetAtPoint(left, linePixel, null, true);
6692 start = left < st.leftMargin ? lineOffset : lineEndOffset;
6694 int[] trailing = new int[1];
6695 int end = st.getOffsetAtPoint(right, linePixel, trailing, true);
6697 end = right < st.leftMargin ? lineOffset : lineEndOffset;
6706 ranges[index++] = start;
6707 ranges[index++] = end;
6712 public void getRanges(AccessibleTextEvent e) {
6713 StyledText st = StyledText.this;
6714 Point point = new Point (e.x, e.y);
6715 Display display = st.getDisplay();
6716 point = display.map(null, st, point);
6717 e.ranges = getRanges(point.x, point.y, point.x + e.width, point.y + e.height);
6718 if (e.ranges.length > 0) {
6719 e.start = e.ranges[0];
6720 e.end = e.ranges[e.ranges.length - 1];
6724 public void getText(AccessibleTextEvent e) {
6725 StyledText st = StyledText.this;
6726 int start = e.start;
6728 int contentLength = st.getCharCount();
6729 if (end == -1) end = contentLength;
6730 start = Math.max(0, Math.min(start, contentLength));
6731 end = Math.max(0, Math.min(end, contentLength));
6737 int count = e.count;
6739 case ACC.TEXT_BOUNDARY_ALL:
6742 case ACC.TEXT_BOUNDARY_CHAR: {
6745 while (count-- > 0) {
6746 int newEnd = st.getWordNext(end, SWT.MOVEMENT_CLUSTER);
6747 if (newEnd == contentLength) break;
6748 if (newEnd == end) break;
6753 end = st.getWordNext(end, SWT.MOVEMENT_CLUSTER);
6755 while (count++ < 0) {
6756 int newStart = st.getWordPrevious(start, SWT.MOVEMENT_CLUSTER);
6757 if (newStart == start) break;
6761 end = st.getWordNext(start, SWT.MOVEMENT_CLUSTER);
6766 case ACC.TEXT_BOUNDARY_WORD: {
6769 while (count-- > 0) {
6770 int newEnd = st.getWordNext(end, SWT.MOVEMENT_WORD_START, true);
6771 if (newEnd == end) break;
6776 end = st.getWordNext(start, SWT.MOVEMENT_WORD_END, true);
6778 if (st.getWordPrevious(Math.min(start + 1, contentLength), SWT.MOVEMENT_WORD_START, true) == start) {
6779 //start is a word start already
6782 while (count <= 0) {
6783 int newStart = st.getWordPrevious(start, SWT.MOVEMENT_WORD_START, true);
6784 if (newStart == start) break;
6787 if (count != 0) newCount--;
6789 if (count <= 0 && start == 0) {
6792 end = st.getWordNext(start, SWT.MOVEMENT_WORD_END, true);
6798 case ACC.TEXT_BOUNDARY_LINE:
6799 //TODO implement line
6800 case ACC.TEXT_BOUNDARY_PARAGRAPH:
6801 case ACC.TEXT_BOUNDARY_SENTENCE: {
6802 int offset = count > 0 ? end : start;
6803 int lineIndex = st.getLineAtOffset(offset) + count;
6804 lineIndex = Math.max(0, Math.min(lineIndex, st.getLineCount() - 1));
6805 start = st.getOffsetAtLine(lineIndex);
6806 String line = st.getLine(lineIndex);
6807 end = start + line.length();
6808 count = lineIndex - st.getLineAtOffset(offset);
6815 e.result = st.content.getTextRange(start, end - start);
6818 public void getVisibleRanges(AccessibleTextEvent e) {
6819 e.ranges = getRanges(leftMargin, topMargin, clientAreaWidth - rightMargin, clientAreaHeight - bottomMargin);
6820 if (e.ranges.length > 0) {
6821 e.start = e.ranges[0];
6822 e.end = e.ranges[e.ranges.length - 1];
6826 public void scrollText(AccessibleTextEvent e) {
6827 StyledText st = StyledText.this;
6828 int topPixel = getTopPixel(), horizontalPixel = st.getHorizontalPixel();
6830 case ACC.SCROLL_TYPE_ANYWHERE:
6831 case ACC.SCROLL_TYPE_TOP_LEFT:
6832 case ACC.SCROLL_TYPE_LEFT_EDGE:
6833 case ACC.SCROLL_TYPE_TOP_EDGE: {
6834 Rectangle rect = st.getBoundsAtOffset(e.start);
6835 if (e.type != ACC.SCROLL_TYPE_TOP_EDGE) {
6836 horizontalPixel = horizontalPixel + rect.x - st.leftMargin;
6838 if (e.type != ACC.SCROLL_TYPE_LEFT_EDGE) {
6839 topPixel = topPixel + rect.y - st.topMargin;
6843 case ACC.SCROLL_TYPE_BOTTOM_RIGHT:
6844 case ACC.SCROLL_TYPE_BOTTOM_EDGE:
6845 case ACC.SCROLL_TYPE_RIGHT_EDGE: {
6846 Rectangle rect = st.getBoundsAtOffset(e.end - 1);
6847 if (e.type != ACC.SCROLL_TYPE_BOTTOM_EDGE) {
6848 horizontalPixel = horizontalPixel - st.clientAreaWidth + rect.x + rect.width + st.rightMargin;
6850 if (e.type != ACC.SCROLL_TYPE_RIGHT_EDGE) {
6851 topPixel = topPixel - st.clientAreaHeight + rect.y +rect.height + st.bottomMargin;
6855 case ACC.SCROLL_TYPE_POINT: {
6856 Point point = new Point(e.x, e.y);
6857 Display display = st.getDisplay();
6858 point = display.map(null, st, point);
6859 Rectangle rect = st.getBoundsAtOffset(e.start);
6860 topPixel = topPixel - point.y + rect.y;
6861 horizontalPixel = horizontalPixel - point.x + rect.x;
6865 st.setTopPixel(topPixel);
6866 st.setHorizontalPixel(horizontalPixel);
6870 acc.addAccessibleTextListener(accTextExtendedAdapter);
6872 accEditableTextListener = new AccessibleEditableTextListener() {
6874 public void setTextAttributes(AccessibleTextAttributeEvent e) {
6875 // This method must be implemented by the application
6879 public void replaceText(AccessibleEditableTextEvent e) {
6880 StyledText st = StyledText.this;
6881 st.replaceTextRange(e.start, e.end - e.start, e.string);
6885 public void pasteText(AccessibleEditableTextEvent e) {
6886 StyledText st = StyledText.this;
6887 st.setSelection(e.start);
6892 public void cutText(AccessibleEditableTextEvent e) {
6893 StyledText st = StyledText.this;
6894 st.setSelection(e.start, e.end);
6899 public void copyText(AccessibleEditableTextEvent e) {
6900 StyledText st = StyledText.this;
6901 st.setSelection(e.start, e.end);
6906 acc.addAccessibleEditableTextListener(accEditableTextListener);
6908 accAttributeAdapter = new AccessibleAttributeAdapter() {
6910 public void getAttributes(AccessibleAttributeEvent e) {
6911 StyledText st = StyledText.this;
6912 e.leftMargin = st.getLeftMargin();
6913 e.topMargin = st.getTopMargin();
6914 e.rightMargin = st.getRightMargin();
6915 e.bottomMargin = st.getBottomMargin();
6916 e.tabStops = st.getTabStops();
6917 e.justify = st.getJustify();
6918 e.alignment = st.getAlignment();
6919 e.indent = st.getIndent();
6922 public void getTextAttributes(AccessibleTextAttributeEvent e) {
6923 StyledText st = StyledText.this;
6924 int contentLength = st.getCharCount();
6925 if (!isListening(ST.LineGetStyle) && st.renderer.styleCount == 0) {
6927 e.end = contentLength;
6928 e.textStyle = new TextStyle(st.getFont(), st.foreground, st.background);
6931 int offset = Math.max(0, Math.min(e.offset, contentLength - 1));
6932 int lineIndex = st.getLineAtOffset(offset);
6933 int lineOffset = st.getOffsetAtLine(lineIndex);
6934 int lineCount = st.getLineCount();
6935 offset = offset - lineOffset;
6937 TextLayout layout = st.renderer.getTextLayout(lineIndex);
6938 int lineLength = layout.getText().length();
6939 if (lineLength > 0) {
6940 e.textStyle = layout.getStyle(Math.max(0, Math.min(offset, lineLength - 1)));
6943 // If no override info available, use defaults. Don't supply default colors, though.
6944 if (e.textStyle == null) {
6945 e.textStyle = new TextStyle(st.getFont(), st.foreground, st.background);
6947 if (e.textStyle.foreground == null || e.textStyle.background == null || e.textStyle.font == null) {
6948 TextStyle textStyle = new TextStyle(e.textStyle);
6949 if (textStyle.foreground == null) textStyle.foreground = st.foreground;
6950 if (textStyle.background == null) textStyle.background = st.background;
6951 if (textStyle.font == null) textStyle.font = st.getFont();
6952 e.textStyle = textStyle;
6956 //offset at line delimiter case
6957 if (offset >= lineLength) {
6958 e.start = lineOffset + lineLength;
6959 if (lineIndex + 1 < lineCount) {
6960 e.end = st.getOffsetAtLine(lineIndex + 1);
6962 e.end = contentLength;
6967 int[] ranges = layout.getRanges();
6968 st.renderer.disposeTextLayout(layout);
6971 while (index < ranges.length) {
6972 int styleStart = ranges[index++];
6973 int styleEnd = ranges[index++];
6974 if (styleStart <= offset && offset <= styleEnd) {
6975 e.start = lineOffset + styleStart;
6976 e.end = lineOffset + styleEnd + 1;
6979 if (styleStart > offset) {
6980 e.start = lineOffset + end;
6981 e.end = lineOffset + styleStart;
6986 if (index == ranges.length) {
6987 e.start = lineOffset + end;
6988 if (lineIndex + 1 < lineCount) {
6989 e.end = st.getOffsetAtLine(lineIndex + 1);
6991 e.end = contentLength;
6996 acc.addAccessibleAttributeListener(accAttributeAdapter);
6998 accControlAdapter = new AccessibleControlAdapter() {
7000 public void getRole(AccessibleControlEvent e) {
7001 e.detail = ACC.ROLE_TEXT;
7004 public void getState(AccessibleControlEvent e) {
7006 if (isEnabled()) state |= ACC.STATE_FOCUSABLE;
7007 if (isFocusControl()) state |= ACC.STATE_FOCUSED;
7008 if (!isVisible()) state |= ACC.STATE_INVISIBLE;
7009 if (!getEditable()) state |= ACC.STATE_READONLY;
7010 if (isSingleLine()) state |= ACC.STATE_SINGLELINE;
7011 else state |= ACC.STATE_MULTILINE;
7015 public void getValue(AccessibleControlEvent e) {
7016 e.result = StyledText.this.getText();
7019 acc.addAccessibleControlListener(accControlAdapter);
7021 addListener(SWT.FocusIn, event -> acc.setFocus(ACC.CHILDID_SELF));
7025 public void dispose() {
7027 * Note: It is valid to attempt to dispose a widget more than once.
7028 * Added check for this.
7030 if (!isDisposed()) {
7031 acc.removeAccessibleControlListener(accControlAdapter);
7032 acc.removeAccessibleAttributeListener(accAttributeAdapter);
7033 acc.removeAccessibleEditableTextListener(accEditableTextListener);
7034 acc.removeAccessibleTextListener(accTextExtendedAdapter);
7035 acc.removeAccessibleListener(accAdapter);
7041 * Return the Label immediately preceding the receiver in the z-order,
7044 String getAssociatedLabel () {
7045 Control[] siblings = getParent ().getChildren ();
7046 for (int i = 0; i < siblings.length; i++) {
7047 if (siblings [i] == StyledText.this) {
7049 Control sibling = siblings [i-1];
7050 if (sibling instanceof Label) return ((Label) sibling).getText();
7051 if (sibling instanceof CLabel) return ((CLabel) sibling).getText();
7058 String stripMnemonic (String string) {
7060 int length = string.length ();
7062 while ((index < length) && (string.charAt (index) != '&')) index++;
7063 if (++index >= length) return string;
7064 if (string.charAt (index) != '&') {
7065 return string.substring(0, index-1) + string.substring(index, length);
7068 } while (index < length);
7072 * Return the lowercase of the first non-'&' character following
7073 * an '&' character in the given string. If there are no '&'
7074 * characters in the given string, return '\0'.
7076 char _findMnemonic (String string) {
7077 if (string == null) return '\0';
7079 int length = string.length ();
7081 while (index < length && string.charAt (index) != '&') index++;
7082 if (++index >= length) return '\0';
7083 if (string.charAt (index) != '&') return Character.toLowerCase (string.charAt (index));
7085 } while (index < length);
7089 * Executes the action.
7091 * @param action one of the actions defined in ST.java
7093 public void invokeAction(int action) {
7095 if (blockSelection && invokeBlockAction(action)) return;
7096 updateCaretDirection = true;
7101 clearSelection(true);
7105 clearSelection(true);
7109 clearSelection(true);
7113 clearSelection(true);
7115 case ST.COLUMN_PREVIOUS:
7117 clearSelection(true);
7119 case ST.COLUMN_NEXT:
7121 clearSelection(true);
7124 doPageUp(false, -1);
7125 clearSelection(true);
7128 doPageDown(false, -1);
7129 clearSelection(true);
7131 case ST.WORD_PREVIOUS:
7133 clearSelection(true);
7137 clearSelection(true);
7141 clearSelection(true);
7145 clearSelection(true);
7147 case ST.WINDOW_START:
7149 clearSelection(true);
7153 clearSelection(true);
7156 case ST.SELECT_LINE_UP:
7157 doSelectionLineUp();
7162 case ST.SELECT_LINE_DOWN:
7163 doSelectionLineDown();
7165 case ST.SELECT_LINE_START:
7167 doSelection(ST.COLUMN_PREVIOUS);
7169 case ST.SELECT_LINE_END:
7171 doSelection(ST.COLUMN_NEXT);
7173 case ST.SELECT_COLUMN_PREVIOUS:
7174 doSelectionCursorPrevious();
7175 doSelection(ST.COLUMN_PREVIOUS);
7177 case ST.SELECT_COLUMN_NEXT:
7178 doSelectionCursorNext();
7179 doSelection(ST.COLUMN_NEXT);
7181 case ST.SELECT_PAGE_UP:
7182 doSelectionPageUp(-1);
7184 case ST.SELECT_PAGE_DOWN:
7185 doSelectionPageDown(-1);
7187 case ST.SELECT_WORD_PREVIOUS:
7188 doSelectionWordPrevious();
7189 doSelection(ST.COLUMN_PREVIOUS);
7191 case ST.SELECT_WORD_NEXT:
7192 doSelectionWordNext();
7193 doSelection(ST.COLUMN_NEXT);
7195 case ST.SELECT_TEXT_START:
7197 doSelection(ST.COLUMN_PREVIOUS);
7199 case ST.SELECT_TEXT_END:
7201 doSelection(ST.COLUMN_NEXT);
7203 case ST.SELECT_WINDOW_START:
7205 doSelection(ST.COLUMN_PREVIOUS);
7207 case ST.SELECT_WINDOW_END:
7209 doSelection(ST.COLUMN_NEXT);
7221 case ST.DELETE_PREVIOUS:
7224 case ST.DELETE_NEXT:
7227 case ST.DELETE_WORD_PREVIOUS:
7228 doDeleteWordPrevious();
7230 case ST.DELETE_WORD_NEXT:
7234 case ST.TOGGLE_OVERWRITE:
7235 overwrite = !overwrite; // toggle insert/overwrite mode
7237 case ST.TOGGLE_BLOCKSELECTION:
7238 setBlockSelection(!blockSelection);
7243 * Returns true if an action should not be performed when block
7244 * selection in active
7246 boolean invokeBlockAction(int action) {
7253 case ST.COLUMN_PREVIOUS:
7254 case ST.COLUMN_NEXT:
7257 case ST.WORD_PREVIOUS:
7261 case ST.WINDOW_START:
7263 clearBlockSelection(false, false);
7266 case ST.SELECT_LINE_UP:
7267 doBlockLineVertical(true);
7269 case ST.SELECT_LINE_DOWN:
7270 doBlockLineVertical(false);
7272 case ST.SELECT_LINE_START:
7273 doBlockLineHorizontal(false);
7275 case ST.SELECT_LINE_END:
7276 doBlockLineHorizontal(true);
7278 case ST.SELECT_COLUMN_PREVIOUS:
7279 doBlockColumn(false);
7281 case ST.SELECT_COLUMN_NEXT:
7282 doBlockColumn(true);
7284 case ST.SELECT_WORD_PREVIOUS:
7287 case ST.SELECT_WORD_NEXT:
7292 case ST.SELECT_TEXT_START:
7293 doBlockContentStartEnd(false);
7295 case ST.SELECT_TEXT_END:
7296 doBlockContentStartEnd(true);
7298 case ST.SELECT_PAGE_UP:
7299 case ST.SELECT_PAGE_DOWN:
7300 case ST.SELECT_WINDOW_START:
7301 case ST.SELECT_WINDOW_END:
7309 case ST.DELETE_PREVIOUS:
7310 case ST.DELETE_NEXT:
7311 if (blockXLocation != -1) {
7312 insertBlockSelectionText((char)0, action);
7316 case ST.DELETE_WORD_PREVIOUS:
7317 case ST.DELETE_WORD_NEXT:
7319 return blockXLocation != -1;
7323 boolean isBidiCaret() {
7324 return BidiUtil.isBidiPlatform();
7326 boolean isFixedLineHeight() {
7327 return !isWordWrap() && lineSpacing == 0 && renderer.lineSpacingProvider == null && !hasStyleWithVariableHeight && !hasVerticalIndent;
7330 * Returns whether the given offset is inside a multi byte line delimiter.
7332 * "Line1\r\n" isLineDelimiter(5) == false but isLineDelimiter(6) == true
7334 * @return true if the given offset is inside a multi byte line delimiter.
7335 * false if the given offset is before or after a line delimiter.
7337 boolean isLineDelimiter(int offset) {
7338 int line = content.getLineAtOffset(offset);
7339 int lineOffset = content.getOffsetAtLine(line);
7340 int offsetInLine = offset - lineOffset;
7341 // offsetInLine will be greater than line length if the line
7342 // delimiter is longer than one character and the offset is set
7343 // in between parts of the line delimiter.
7344 return offsetInLine > content.getLine(line).length();
7347 * Returns whether the widget is mirrored (right oriented/right to left
7350 * @return isMirrored true=the widget is right oriented, false=the widget
7353 boolean isMirrored() {
7354 return (getStyle() & SWT.MIRRORED) != 0;
7357 * Returns <code>true</code> if any text in the widget is selected,
7358 * and <code>false</code> otherwise.
7360 * @return the text selection state
7361 * @exception SWTException <ul>
7362 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7363 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7368 public boolean isTextSelected() {
7370 if (blockSelection && blockXLocation != -1) {
7371 Rectangle rect = getBlockSelectionPosition();
7372 return !rect.isEmpty();
7374 return selection.y != selection.x;
7377 * Returns whether the widget can have only one line.
7379 * @return true if widget can have only one line, false if widget can have
7382 boolean isSingleLine() {
7383 return (getStyle() & SWT.SINGLE) != 0;
7387 * Sends the specified verify event, replace/insert text as defined by
7388 * the event and send a modify event.
7390 * @param event the text change event.
7392 * <li>event.start - the replace start offset</li>
7393 * <li>event.end - the replace end offset</li>
7394 * <li>event.text - the new text</li>
7396 * @param updateCaret whether or not he caret should be set behind
7399 void modifyContent(Event event, boolean updateCaret) {
7401 notifyListeners(SWT.Verify, event);
7403 StyledTextEvent styledTextEvent = null;
7404 int replacedLength = event.end - event.start;
7405 if (isListening(ST.ExtendedModify)) {
7406 styledTextEvent = new StyledTextEvent(content);
7407 styledTextEvent.start = event.start;
7408 styledTextEvent.end = event.start + event.text.length();
7409 styledTextEvent.text = content.getTextRange(event.start, replacedLength);
7412 //Fix advancing flag for delete/backspace key on direction boundary
7413 if (event.text.length() == 0) {
7414 int lineIndex = content.getLineAtOffset(event.start);
7415 int lineOffset = content.getOffsetAtLine(lineIndex);
7416 TextLayout layout = renderer.getTextLayout(lineIndex);
7417 int levelStart = layout.getLevel(event.start - lineOffset);
7418 int lineIndexEnd = content.getLineAtOffset(event.end);
7419 if (lineIndex != lineIndexEnd) {
7420 renderer.disposeTextLayout(layout);
7421 lineOffset = content.getOffsetAtLine(lineIndexEnd);
7422 layout = renderer.getTextLayout(lineIndexEnd);
7424 int levelEnd = layout.getLevel(event.end - lineOffset);
7425 renderer.disposeTextLayout(layout);
7426 if (levelStart != levelEnd) {
7427 caretAlignment = PREVIOUS_OFFSET_TRAILING;
7429 caretAlignment = OFFSET_LEADING;
7433 content.replaceTextRange(event.start, replacedLength, event.text);
7434 // set the caret position prior to sending the modify event.
7436 if (updateCaret && !(blockSelection && blockXLocation != -1)) {
7437 // always update the caret location. fixes 1G8FODP
7438 setSelection(event.start + event.text.length(), 0, true, false);
7441 notifyListeners(SWT.Modify, event);
7442 if (isListening(ST.ExtendedModify)) {
7443 notifyListeners(ST.ExtendedModify, styledTextEvent);
7447 void paintObject(GC gc, int x, int y, int ascent, int descent, StyleRange style, Bullet bullet, int bulletIndex) {
7448 if (isListening(ST.PaintObject)) {
7449 StyledTextEvent event = new StyledTextEvent (content) ;
7453 event.ascent = ascent;
7454 event.descent = descent;
7455 event.style = style;
7456 event.bullet = bullet;
7457 event.bulletIndex = bulletIndex;
7458 notifyListeners(ST.PaintObject, event);
7462 * Replaces the selection with the text on the <code>DND.CLIPBOARD</code>
7463 * clipboard or, if there is no selection, inserts the text at the current
7464 * caret offset. If the widget has the SWT.SINGLE style and the
7465 * clipboard text contains more than one line, only the first line without
7466 * line delimiters is inserted in the widget.
7468 * @exception SWTException <ul>
7469 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7470 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7473 public void paste(){
7475 String text = (String) getClipboardContent(DND.CLIPBOARD);
7476 if (text != null && text.length() > 0) {
7477 if (blockSelection) {
7478 boolean fillWithSpaces = isFixedLineHeight() && renderer.fixedPitch;
7479 int offset = insertBlockSelectionText(text, fillWithSpaces);
7480 setCaretOffset(offset, SWT.DEFAULT);
7481 clearBlockSelection(true, true);
7485 Event event = new Event();
7486 event.start = selection.x;
7487 event.end = selection.y;
7488 String delimitedText = getModelDelimitedText(text);
7489 if (textLimit > 0) {
7490 int uneditedTextLength = getCharCount() - (selection.y - selection.x);
7491 if ((uneditedTextLength + delimitedText.length()) > textLimit) {
7492 int endIndex = textLimit - uneditedTextLength;
7493 delimitedText = delimitedText.substring(0, Math.max(endIndex, 0));
7496 event.text = delimitedText;
7497 sendKeyEvent(event);
7500 private void pasteOnMiddleClick(Event event) {
7501 String text = (String)getClipboardContent(DND.SELECTION_CLIPBOARD);
7502 if (text != null && text.length() > 0) {
7504 doMouseLocationChange(event.x, event.y, false);
7506 Event e = new Event();
7507 e.start = selection.x;
7508 e.end = selection.y;
7509 e.text = getModelDelimitedText(text);
7514 * Prints the widget's text to the default printer.
7516 * @exception SWTException <ul>
7517 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7518 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7521 public void print() {
7523 Printer printer = new Printer();
7524 StyledTextPrintOptions options = new StyledTextPrintOptions();
7525 options.printTextForeground = true;
7526 options.printTextBackground = true;
7527 options.printTextFontStyle = true;
7528 options.printLineBackground = true;
7529 new Printing(this, printer, options).run();
7533 * Returns a runnable that will print the widget's text
7534 * to the specified printer.
7536 * The runnable may be run in a non-UI thread.
7539 * @param printer the printer to print to
7541 * @return a <code>Runnable</code> for printing the receiver's text
7543 * @exception SWTException <ul>
7544 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7545 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7547 * @exception IllegalArgumentException <ul>
7548 * <li>ERROR_NULL_ARGUMENT when printer is null</li>
7551 public Runnable print(Printer printer) {
7553 if (printer == null) {
7554 SWT.error(SWT.ERROR_NULL_ARGUMENT);
7556 StyledTextPrintOptions options = new StyledTextPrintOptions();
7557 options.printTextForeground = true;
7558 options.printTextBackground = true;
7559 options.printTextFontStyle = true;
7560 options.printLineBackground = true;
7561 return print(printer, options);
7564 * Returns a runnable that will print the widget's text
7565 * to the specified printer.
7567 * The runnable may be run in a non-UI thread.
7570 * @param printer the printer to print to
7571 * @param options print options to use during printing
7573 * @return a <code>Runnable</code> for printing the receiver's text
7575 * @exception SWTException <ul>
7576 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7577 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7579 * @exception IllegalArgumentException <ul>
7580 * <li>ERROR_NULL_ARGUMENT when printer or options is null</li>
7584 public Runnable print(Printer printer, StyledTextPrintOptions options) {
7586 if (printer == null || options == null) {
7587 SWT.error(SWT.ERROR_NULL_ARGUMENT);
7589 return new Printing(this, printer, options);
7592 * Causes the entire bounds of the receiver to be marked
7593 * as needing to be redrawn. The next time a paint request
7594 * is processed, the control will be completely painted.
7596 * Recalculates the content width for all lines in the bounds.
7597 * When a <code>LineStyleListener</code> is used a redraw call
7598 * is the only notification to the widget that styles have changed
7599 * and that the content width may have changed.
7602 * @exception SWTException <ul>
7603 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7604 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7607 * @see Control#update()
7610 public void redraw() {
7612 int itemCount = getPartialBottomIndex() - topIndex + 1;
7613 renderer.reset(topIndex, itemCount);
7614 renderer.calculate(topIndex, itemCount);
7615 setScrollBars(false);
7616 doMouseLinkCursor();
7619 * Causes the rectangular area of the receiver specified by
7620 * the arguments to be marked as needing to be redrawn.
7621 * The next time a paint request is processed, that area of
7622 * the receiver will be painted. If the <code>all</code> flag
7623 * is <code>true</code>, any children of the receiver which
7624 * intersect with the specified area will also paint their
7625 * intersecting areas. If the <code>all</code> flag is
7626 * <code>false</code>, the children will not be painted.
7628 * Marks the content width of all lines in the specified rectangle
7629 * as unknown. Recalculates the content width of all visible lines.
7630 * When a <code>LineStyleListener</code> is used a redraw call
7631 * is the only notification to the widget that styles have changed
7632 * and that the content width may have changed.
7635 * @param x the x coordinate of the area to draw
7636 * @param y the y coordinate of the area to draw
7637 * @param width the width of the area to draw
7638 * @param height the height of the area to draw
7639 * @param all <code>true</code> if children should redraw, and <code>false</code> otherwise
7641 * @exception SWTException <ul>
7642 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7643 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7646 * @see Control#update()
7649 public void redraw(int x, int y, int width, int height, boolean all) {
7650 super.redraw(x, y, width, height, all);
7652 int firstLine = getLineIndex(y);
7653 int lastLine = getLineIndex(y + height);
7654 resetCache(firstLine, lastLine - firstLine + 1);
7655 doMouseLinkCursor();
7658 void redrawLines(int startLine, int lineCount, boolean bottomChanged) {
7659 // do nothing if redraw range is completely invisible
7660 int endLine = startLine + lineCount - 1;
7661 int partialBottomIndex = getPartialBottomIndex();
7662 int partialTopIndex = getPartialTopIndex();
7663 if (startLine > partialBottomIndex || endLine < partialTopIndex) {
7666 // only redraw visible lines
7667 if (startLine < partialTopIndex) {
7668 startLine = partialTopIndex;
7670 if (endLine > partialBottomIndex) {
7671 endLine = partialBottomIndex;
7673 int redrawTop = getLinePixel(startLine);
7674 int redrawBottom = getLinePixel(endLine + 1);
7675 if (bottomChanged) redrawBottom = clientAreaHeight - bottomMargin;
7676 int redrawWidth = clientAreaWidth - leftMargin - rightMargin;
7677 super.redraw(leftMargin, redrawTop, redrawWidth, redrawBottom - redrawTop, true);
7679 void redrawLinesBullet (int[] redrawLines) {
7680 if (redrawLines == null) return;
7681 int topIndex = getPartialTopIndex();
7682 int bottomIndex = getPartialBottomIndex();
7683 for (int i = 0; i < redrawLines.length; i++) {
7684 int lineIndex = redrawLines[i];
7685 if (!(topIndex <= lineIndex && lineIndex <= bottomIndex)) continue;
7687 Bullet bullet = renderer.getLineBullet(lineIndex, null);
7688 if (bullet != null) {
7689 StyleRange style = bullet.style;
7690 GlyphMetrics metrics = style.metrics;
7691 width = metrics.width;
7693 if (width == -1) width = getClientArea().width;
7694 int height = renderer.getLineHeight(lineIndex);
7695 int y = getLinePixel(lineIndex);
7696 super.redraw(0, y, width, height, false);
7699 void redrawMargins(int oldHeight, int oldWidth) {
7700 /* Redraw the old or new right/bottom margin if needed */
7701 if (oldWidth != clientAreaWidth) {
7702 if (rightMargin > 0) {
7703 int x = (oldWidth < clientAreaWidth ? oldWidth : clientAreaWidth) - rightMargin;
7704 super.redraw(x, 0, rightMargin, oldHeight, false);
7707 if (oldHeight != clientAreaHeight) {
7708 if (bottomMargin > 0) {
7709 int y = (oldHeight < clientAreaHeight ? oldHeight : clientAreaHeight) - bottomMargin;
7710 super.redraw(0, y, oldWidth, bottomMargin, false);
7715 * Redraws the specified text range.
7717 * @param start offset of the first character to redraw
7718 * @param length number of characters to redraw
7719 * @param clearBackground true if the background should be cleared as
7720 * part of the redraw operation. If true, the entire redraw range will
7721 * be cleared before anything is redrawn. If the redraw range includes
7722 * the last character of a line (i.e., the entire line is redrawn) the
7723 * line is cleared all the way to the right border of the widget.
7724 * The redraw operation will be faster and smoother if clearBackground
7725 * is set to false. Whether or not the flag can be set to false depends
7726 * on the type of change that has taken place. If font styles or
7727 * background colors for the redraw range have changed, clearBackground
7728 * should be set to true. If only foreground colors have changed for
7729 * the redraw range, clearBackground can be set to false.
7730 * @exception SWTException <ul>
7731 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7732 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7734 * @exception IllegalArgumentException <ul>
7735 * <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
7738 public void redrawRange(int start, int length, boolean clearBackground) {
7740 int end = start + length;
7741 int contentLength = content.getCharCount();
7742 if (start > end || start < 0 || end > contentLength) {
7743 SWT.error(SWT.ERROR_INVALID_RANGE);
7745 int firstLine = content.getLineAtOffset(start);
7746 int lastLine = content.getLineAtOffset(end);
7747 resetCache(firstLine, lastLine - firstLine + 1);
7748 internalRedrawRange(start, length);
7749 doMouseLinkCursor();
7752 * Removes the specified bidirectional segment listener.
7754 * @param listener the listener which should no longer be notified
7756 * @exception SWTException <ul>
7757 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7758 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7760 * @exception IllegalArgumentException <ul>
7761 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
7766 public void removeBidiSegmentListener(BidiSegmentListener listener) {
7768 if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7769 removeListener(ST.LineGetSegments, listener);
7770 resetCache(0, content.getLineCount());
7775 * Removes the specified caret listener.
7777 * @param listener the listener which should no longer be notified
7779 * @exception SWTException <ul>
7780 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7781 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7783 * @exception IllegalArgumentException <ul>
7784 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
7789 public void removeCaretListener(CaretListener listener) {
7791 if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7792 removeListener(ST.CaretMoved, listener);
7795 * Removes the specified extended modify listener.
7797 * @param extendedModifyListener the listener which should no longer be notified
7799 * @exception SWTException <ul>
7800 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7801 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7803 * @exception IllegalArgumentException <ul>
7804 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
7807 public void removeExtendedModifyListener(ExtendedModifyListener extendedModifyListener) {
7809 if (extendedModifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7810 removeListener(ST.ExtendedModify, extendedModifyListener);
7813 * Removes the specified line background listener.
7815 * @param listener the listener which should no longer be notified
7817 * @exception SWTException <ul>
7818 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7819 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7821 * @exception IllegalArgumentException <ul>
7822 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
7825 public void removeLineBackgroundListener(LineBackgroundListener listener) {
7827 if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7828 removeListener(ST.LineGetBackground, listener);
7831 * Removes the specified line style listener.
7833 * @param listener the listener which should no longer be notified
7835 * @exception SWTException <ul>
7836 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7837 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7839 * @exception IllegalArgumentException <ul>
7840 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
7843 public void removeLineStyleListener(LineStyleListener listener) {
7845 if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7846 removeListener(ST.LineGetStyle, listener);
7850 * Removes the specified modify listener.
7852 * @param modifyListener the listener which should no longer be notified
7854 * @exception SWTException <ul>
7855 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7856 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7858 * @exception IllegalArgumentException <ul>
7859 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
7862 public void removeModifyListener(ModifyListener modifyListener) {
7864 if (modifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7865 removeListener(SWT.Modify, modifyListener);
7868 * Removes the specified listener.
7870 * @param listener the listener which should no longer be notified
7872 * @exception SWTException <ul>
7873 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7874 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7876 * @exception IllegalArgumentException <ul>
7877 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
7881 public void removePaintObjectListener(PaintObjectListener listener) {
7883 if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7884 removeListener(ST.PaintObject, listener);
7887 * Removes the listener from the collection of listeners who will
7888 * be notified when the user changes the receiver's selection.
7890 * @param listener the listener which should no longer be notified
7892 * @exception IllegalArgumentException <ul>
7893 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
7895 * @exception SWTException <ul>
7896 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7897 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7900 * @see SelectionListener
7901 * @see #addSelectionListener
7903 public void removeSelectionListener(SelectionListener listener) {
7905 if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7906 removeListener(SWT.Selection, listener);
7909 * Removes the specified verify listener.
7911 * @param verifyListener the listener which should no longer be notified
7913 * @exception SWTException <ul>
7914 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7915 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7917 * @exception IllegalArgumentException <ul>
7918 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
7921 public void removeVerifyListener(VerifyListener verifyListener) {
7923 if (verifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7924 removeListener(SWT.Verify, verifyListener);
7927 * Removes the specified key verify listener.
7929 * @param listener the listener which should no longer be notified
7931 * @exception SWTException <ul>
7932 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7933 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7935 * @exception IllegalArgumentException <ul>
7936 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
7939 public void removeVerifyKeyListener(VerifyKeyListener listener) {
7940 if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7941 removeListener(ST.VerifyKey, listener);
7944 * Removes the specified word movement listener.
7946 * @param listener the listener which should no longer be notified
7948 * @exception SWTException <ul>
7949 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7950 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7952 * @exception IllegalArgumentException <ul>
7953 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
7956 * @see MovementEvent
7957 * @see MovementListener
7958 * @see #addWordMovementListener
7963 public void removeWordMovementListener(MovementListener listener) {
7965 if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7966 removeListener(ST.WordNext, listener);
7967 removeListener(ST.WordPrevious, listener);
7970 * Replaces the styles in the given range with new styles. This method
7971 * effectively deletes the styles in the given range and then adds the
7974 * Note: Because a StyleRange includes the start and length, the
7975 * same instance cannot occur multiple times in the array of styles.
7976 * If the same style attributes, such as font and color, occur in
7977 * multiple StyleRanges, <code>setStyleRanges(int, int, int[], StyleRange[])</code>
7978 * can be used to share styles and reduce memory usage.
7980 * Should not be called if a LineStyleListener has been set since the
7981 * listener maintains the styles.
7984 * @param start offset of first character where styles will be deleted
7985 * @param length length of the range to delete styles in
7986 * @param ranges StyleRange objects containing the new style information.
7987 * The ranges should not overlap and should be within the specified start
7988 * and length. The style rendering is undefined if the ranges do overlap
7989 * or are ill-defined. Must not be null.
7990 * @exception SWTException <ul>
7991 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7992 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7994 * @exception IllegalArgumentException <ul>
7995 * <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 <= offset <= getCharCount())</li>
7996 * <li>ERROR_NULL_ARGUMENT when ranges is null</li>
8001 * @see #setStyleRanges(int, int, int[], StyleRange[])
8003 public void replaceStyleRanges(int start, int length, StyleRange[] ranges) {
8005 if (isListening(ST.LineGetStyle)) return;
8006 if (ranges == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
8007 setStyleRanges(start, length, null, ranges, false);
8010 * Replaces the given text range with new text.
8011 * If the widget has the SWT.SINGLE style and "text" contains more than
8012 * one line, only the first line is rendered but the text is stored
8013 * unchanged. A subsequent call to getText will return the same text
8014 * that was set. Note that only a single line of text should be set when
8015 * the SWT.SINGLE style is used.
8017 * <b>NOTE:</b> During the replace operation the current selection is
8018 * changed as follows:
8021 * <li>selection before replaced text: selection unchanged
8022 * <li>selection after replaced text: adjust the selection so that same text
8024 * <li>selection intersects replaced text: selection is cleared and caret
8025 * is placed after inserted text
8028 * @param start offset of first character to replace
8029 * @param length number of characters to replace. Use 0 to insert text
8030 * @param text new text. May be empty to delete text.
8031 * @exception SWTException <ul>
8032 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8033 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8035 * @exception IllegalArgumentException <ul>
8036 * <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 <= offset <= getCharCount())</li>
8037 * <li>ERROR_INVALID_ARGUMENT when either start or end is inside a multi byte line delimiter.
8038 * 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>
8039 * <li>ERROR_NULL_ARGUMENT when string is null</li>
8042 public void replaceTextRange(int start, int length, String text) {
8045 SWT.error(SWT.ERROR_NULL_ARGUMENT);
8047 int contentLength = getCharCount();
8048 int end = start + length;
8049 if (start > end || start < 0 || end > contentLength) {
8050 SWT.error(SWT.ERROR_INVALID_RANGE);
8052 Event event = new Event();
8053 event.start = start;
8056 modifyContent(event, false);
8059 * Resets the caret position, selection and scroll offsets. Recalculate
8060 * the content width and scroll bars. Redraw the widget.
8063 ScrollBar verticalBar = getVerticalBar();
8064 ScrollBar horizontalBar = getHorizontalBar();
8065 setCaretOffset(0, SWT.DEFAULT);
8068 verticalScrollOffset = 0;
8069 horizontalScrollOffset = 0;
8071 renderer.setContent(content);
8072 if (verticalBar != null) {
8073 verticalBar.setSelection(0);
8075 if (horizontalBar != null) {
8076 horizontalBar.setSelection(0);
8082 void resetBidiData() {
8083 caretDirection = SWT.NULL;
8084 resetCache(0, content.getLineCount());
8086 keyActionMap.clear();
8087 createKeyBindings();
8090 void resetCache(SortedSet<Integer> lines) {
8091 if (lines == null || lines.isEmpty()) return;
8092 int maxLineIndex = renderer.maxWidthLineIndex;
8093 renderer.reset(lines);
8094 renderer.calculateClientArea();
8095 if (0 <= maxLineIndex && maxLineIndex < content.getLineCount()) {
8096 renderer.calculate(maxLineIndex, 1);
8098 setScrollBars(true);
8099 if (!isFixedLineHeight()) {
8100 if (topIndex > lines.iterator().next()) {
8101 verticalScrollOffset = -1;
8103 renderer.calculateIdle();
8106 void resetCache(int firstLine, int count) {
8107 int maxLineIndex = renderer.maxWidthLineIndex;
8108 renderer.reset(firstLine, count);
8109 renderer.calculateClientArea();
8110 if (0 <= maxLineIndex && maxLineIndex < content.getLineCount()) {
8111 renderer.calculate(maxLineIndex, 1);
8113 setScrollBars(true);
8114 if (!isFixedLineHeight()) {
8115 if (topIndex > firstLine) {
8116 verticalScrollOffset = -1;
8118 renderer.calculateIdle();
8122 * Resets the selection.
8124 void resetSelection() {
8125 selection.x = selection.y = caretOffset;
8126 selectionAnchor = -1;
8127 sendAccessibleTextCaretMoved();
8131 public void scroll(int destX, int destY, int x, int y, int width, int height, boolean all) {
8132 super.scroll(destX, destY, x, y, width, height, false);
8134 int deltaX = destX - x, deltaY = destY - y;
8135 Control[] children = getChildren();
8136 for (int i=0; i<children.length; i++) {
8137 Control child = children[i];
8138 Rectangle rect = child.getBounds();
8139 child.setLocation(rect.x + deltaX, rect.y + deltaY);
8145 * Scrolls the widget horizontally.
8147 * @param pixels number of SWT logical points to scroll, > 0 = scroll left,
8148 * < 0 scroll right
8149 * @param adjustScrollBar
8150 * true= the scroll thumb will be moved to reflect the new scroll offset.
8151 * false = the scroll thumb will not be moved
8153 * true=the widget was scrolled
8154 * false=the widget was not scrolled, the given offset is not valid.
8156 boolean scrollHorizontal(int pixels, boolean adjustScrollBar) {
8157 if (pixels == 0) return false;
8158 if (wordWrap) return false;
8159 ScrollBar horizontalBar = getHorizontalBar();
8160 if (horizontalBar != null && adjustScrollBar) {
8161 horizontalBar.setSelection(horizontalScrollOffset + pixels);
8163 int scrollHeight = clientAreaHeight - topMargin - bottomMargin;
8165 int sourceX = leftMargin + pixels;
8166 int scrollWidth = clientAreaWidth - sourceX - rightMargin;
8167 if (scrollWidth > 0) {
8168 scroll(leftMargin, topMargin, sourceX, topMargin, scrollWidth, scrollHeight, true);
8170 if (sourceX > scrollWidth) {
8171 super.redraw(leftMargin + scrollWidth, topMargin, pixels - scrollWidth, scrollHeight, true);
8174 int destinationX = leftMargin - pixels;
8175 int scrollWidth = clientAreaWidth - destinationX - rightMargin;
8176 if (scrollWidth > 0) {
8177 scroll(destinationX, topMargin, leftMargin, topMargin, scrollWidth, scrollHeight, true);
8179 if (destinationX > scrollWidth) {
8180 super.redraw(leftMargin + scrollWidth, topMargin, -pixels - scrollWidth, scrollHeight, true);
8183 horizontalScrollOffset += pixels;
8188 * Scrolls the widget vertically.
8190 * @param pixel the new vertical scroll offset
8191 * @param adjustScrollBar
8192 * true= the scroll thumb will be moved to reflect the new scroll offset.
8193 * false = the scroll thumb will not be moved
8195 * true=the widget was scrolled
8196 * false=the widget was not scrolled
8198 boolean scrollVertical(int pixels, boolean adjustScrollBar) {
8202 if (verticalScrollOffset != -1) {
8203 ScrollBar verticalBar = getVerticalBar();
8204 if (verticalBar != null && adjustScrollBar) {
8205 verticalBar.setSelection(verticalScrollOffset + pixels);
8209 int sourceY = topMargin + pixels;
8210 int scrollHeight = clientAreaHeight - sourceY - bottomMargin;
8211 if (scrollHeight > 0) {
8215 int destinationY = topMargin - pixels;
8216 int scrollHeight = clientAreaHeight - destinationY - bottomMargin;
8217 if (scrollHeight > 0) {
8221 Control[] children = getChildren();
8222 for (int i=0; i<children.length; i++) {
8223 Control child = children[i];
8224 Rectangle rect = child.getBounds();
8225 child.setLocation(rect.x, rect.y + deltaY);
8227 verticalScrollOffset += pixels;
8228 calculateTopIndex(pixels);
8231 calculateTopIndex(pixels);
8237 void scrollText(int srcY, int destY) {
8238 if (srcY == destY) return;
8239 int deltaY = destY - srcY;
8240 int scrollWidth = clientAreaWidth - leftMargin - rightMargin, scrollHeight;
8242 scrollHeight = clientAreaHeight - srcY - bottomMargin;
8244 scrollHeight = clientAreaHeight - destY - bottomMargin;
8246 scroll(leftMargin, destY, leftMargin, srcY, scrollWidth, scrollHeight, true);
8247 if ((0 < srcY + scrollHeight) && (topMargin > srcY)) {
8248 super.redraw(leftMargin, deltaY, scrollWidth, topMargin, false);
8250 if ((0 < destY + scrollHeight) && (topMargin > destY)) {
8251 super.redraw(leftMargin, 0, scrollWidth, topMargin, false);
8253 if ((clientAreaHeight - bottomMargin < srcY + scrollHeight) && (clientAreaHeight > srcY)) {
8254 super.redraw(leftMargin, clientAreaHeight - bottomMargin + deltaY, scrollWidth, bottomMargin, false);
8256 if ((clientAreaHeight - bottomMargin < destY + scrollHeight) && (clientAreaHeight > destY)) {
8257 super.redraw(leftMargin, clientAreaHeight - bottomMargin, scrollWidth, bottomMargin, false);
8260 void sendAccessibleTextCaretMoved() {
8261 if (caretOffset != accCaretOffset) {
8262 accCaretOffset = caretOffset;
8263 getAccessible().textCaretMoved(caretOffset);
8266 void sendAccessibleTextChanged(int start, int newCharCount, int replaceCharCount) {
8267 Accessible accessible = getAccessible();
8268 if (replaceCharCount != 0) {
8269 accessible.textChanged(ACC.TEXT_DELETE, start, replaceCharCount);
8271 if (newCharCount != 0) {
8272 accessible.textChanged(ACC.TEXT_INSERT, start, newCharCount);
8276 * Selects all the text.
8278 * @exception SWTException <ul>
8279 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8280 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8283 public void selectAll() {
8285 if (blockSelection) {
8286 renderer.calculate(0, content.getLineCount());
8287 setScrollBars(false);
8288 int verticalScrollOffset = getVerticalScrollOffset();
8289 int left = leftMargin - horizontalScrollOffset;
8290 int top = topMargin - verticalScrollOffset;
8291 int right = renderer.getWidth() - rightMargin - horizontalScrollOffset;
8292 int bottom = renderer.getHeight() - bottomMargin - verticalScrollOffset;
8293 setBlockSelectionLocation(left, top, right, bottom, false);
8296 setSelection(0, Math.max(getCharCount(),0));
8299 * Replaces/inserts text as defined by the event.
8301 * @param event the text change event.
8303 * <li>event.start - the replace start offset</li>
8304 * <li>event.end - the replace end offset</li>
8305 * <li>event.text - the new text</li>
8308 void sendKeyEvent(Event event) {
8310 modifyContent(event, true);
8314 * Returns a StyledTextEvent that can be used to request data such
8315 * as styles and background color for a line.
8317 * The specified line may be a visual (wrapped) line if in word
8318 * wrap mode. The returned object will always be for a logical
8322 * @param lineOffset offset of the line. This may be the offset of
8323 * a visual line if the widget is in word wrap mode.
8324 * @param line line text. This may be the text of a visual line if
8325 * the widget is in word wrap mode.
8326 * @return StyledTextEvent that can be used to request line data
8327 * for the given line.
8329 StyledTextEvent sendLineEvent(int eventType, int lineOffset, String line) {
8330 StyledTextEvent event = null;
8331 if (isListening(eventType)) {
8332 event = new StyledTextEvent(content);
8333 event.detail = lineOffset;
8335 event.alignment = alignment;
8336 event.indent = indent;
8337 event.wrapIndent = wrapIndent;
8338 event.justify = justify;
8339 notifyListeners(eventType, event);
8344 * Sends the specified selection event.
8346 void sendSelectionEvent() {
8347 getAccessible().textSelectionChanged();
8348 Event event = new Event();
8349 event.x = selection.x;
8350 event.y = selection.y;
8351 notifyListeners(SWT.Selection, event);
8353 int sendTextEvent(int left, int right, int lineIndex, String text, boolean fillWithSpaces) {
8354 int lineWidth = 0, start, end;
8355 StringBuilder buffer = new StringBuilder();
8356 if (lineIndex < content.getLineCount()) {
8357 int[] trailing = new int[1];
8358 start = getOffsetAtPoint(left, getLinePixel(lineIndex), trailing, true);
8360 int lineOffset = content.getOffsetAtLine(lineIndex);
8361 int lineLegth = content.getLine(lineIndex).length();
8362 start = end = lineOffset + lineLegth;
8363 if (fillWithSpaces) {
8364 TextLayout layout = renderer.getTextLayout(lineIndex);
8365 lineWidth = layout.getBounds().width;
8366 renderer.disposeTextLayout(layout);
8369 start += trailing[0];
8370 end = left == right ? start : getOffsetAtPoint(right, 0, lineIndex, null);
8371 fillWithSpaces = false;
8374 start = end = content.getCharCount();
8375 buffer.append(content.getLineDelimiter());
8382 if (fillWithSpaces) {
8383 int spacesWidth = left - lineWidth + horizontalScrollOffset - leftMargin;
8384 int spacesCount = spacesWidth / renderer.averageCharWidth;
8385 for (int i = 0; i < spacesCount; i++) {
8389 buffer.append(text);
8390 Event event = new Event();
8391 event.start = start;
8393 event.text = buffer.toString();
8394 sendKeyEvent(event);
8395 return event.start + event.text.length();
8397 int sendWordBoundaryEvent(int eventType, int movement, int offset, int newOffset, String lineText, int lineOffset) {
8398 if (isListening(eventType)) {
8399 StyledTextEvent event = new StyledTextEvent(content);
8400 event.detail = lineOffset;
8401 event.text = lineText;
8402 event.count = movement;
8403 event.start = offset;
8404 event.end = newOffset;
8405 notifyListeners(eventType, event);
8407 if (offset != newOffset) {
8408 int length = getCharCount();
8411 } else if (offset > length) {
8414 if (isLineDelimiter(offset)) {
8415 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
8423 void setAlignment() {
8424 if ((getStyle() & SWT.SINGLE) == 0) return;
8425 int alignment = renderer.getLineAlignment(0, this.alignment);
8426 int newAlignmentMargin = 0;
8427 if (alignment != SWT.LEFT) {
8428 renderer.calculate(0, 1);
8429 int width = renderer.getWidth() - alignmentMargin;
8430 newAlignmentMargin = clientAreaWidth - width;
8431 if (newAlignmentMargin < 0) newAlignmentMargin = 0;
8432 if (alignment == SWT.CENTER) newAlignmentMargin /= 2;
8434 if (alignmentMargin != newAlignmentMargin) {
8435 leftMargin -= alignmentMargin;
8436 leftMargin += newAlignmentMargin;
8437 alignmentMargin = newAlignmentMargin;
8444 * Sets the alignment of the widget. The argument should be one of <code>SWT.LEFT</code>,
8445 * <code>SWT.CENTER</code> or <code>SWT.RIGHT</code>. The alignment applies for all lines.
8447 * Note that if <code>SWT.MULTI</code> is set, then <code>SWT.WRAP</code> must also be set
8448 * in order to stabilize the right edge before setting alignment.
8451 * @param alignment the new alignment
8453 * @exception SWTException <ul>
8454 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8455 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8458 * @see #setLineAlignment(int, int, int)
8462 public void setAlignment(int alignment) {
8464 alignment &= (SWT.LEFT | SWT.RIGHT | SWT.CENTER);
8465 if (alignment == 0 || this.alignment == alignment) return;
8466 this.alignment = alignment;
8467 resetCache(0, content.getLineCount());
8473 * Set the Always Show Scrollbars flag. True if the scrollbars are
8474 * always shown even if they are not required (default value). False if the scrollbars are only
8475 * visible when some part of the content needs to be scrolled to be seen.
8476 * The H_SCROLL and V_SCROLL style bits are also required to enable scrollbars in the
8477 * horizontal and vertical directions.
8479 * @param show true to show the scrollbars even when not required, false to show scrollbars only when required
8481 * @exception SWTException <ul>
8482 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8483 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8488 public void setAlwaysShowScrollBars(boolean show) {
8490 if (show == alwaysShowScroll) return;
8491 alwaysShowScroll = show;
8492 setScrollBars(true);
8495 * @see Control#setBackground(Color)
8498 public void setBackground(Color color) {
8500 boolean backgroundDisabled = false;
8501 if (!this.enabled && color == null) {
8502 if (background != null) {
8503 Color disabledBg = getDisplay().getSystemColor(SWT.COLOR_TEXT_DISABLED_BACKGROUND);
8504 if (background.equals(disabledBg)) {
8507 color = new Color (getDisplay(), disabledBg.getRGBA());
8508 backgroundDisabled = true;
8512 customBackground = color != null && !this.insideSetEnableCall && !backgroundDisabled;
8514 super.setBackground(color);
8515 resetCache(0, content.getLineCount());
8520 * Sets the block selection mode.
8522 * @param blockSelection true=enable block selection, false=disable block selection
8526 public void setBlockSelection(boolean blockSelection) {
8528 if ((getStyle() & SWT.SINGLE) != 0) return;
8529 if (blockSelection == this.blockSelection) return;
8530 if (wordWrap) return;
8531 this.blockSelection = blockSelection;
8532 if (cursor == null) {
8533 Display display = getDisplay();
8534 int type = blockSelection ? SWT.CURSOR_CROSS : SWT.CURSOR_IBEAM;
8535 super.setCursor(display.getSystemCursor(type));
8537 if (blockSelection) {
8538 int start = selection.x;
8539 int end = selection.y;
8541 setBlockSelectionOffset(start, end, false);
8544 clearBlockSelection(false, false);
8548 * Sets the block selection bounds. The bounds is
8549 * relative to the upper left corner of the document.
8551 * @param rect the new bounds for the block selection
8553 * @see #setBlockSelectionBounds(int, int, int, int)
8554 * @exception SWTException <ul>
8555 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8556 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8558 * @exception IllegalArgumentException <ul>
8559 * <li>ERROR_NULL_ARGUMENT when point is null</li>
8564 public void setBlockSelectionBounds(Rectangle rect) {
8566 if (rect == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
8567 setBlockSelectionBounds(rect.x, rect.y, rect.width, rect.height);
8570 * Sets the block selection bounds. The bounds is
8571 * relative to the upper left corner of the document.
8573 * @param x the new x coordinate for the block selection
8574 * @param y the new y coordinate for the block selection
8575 * @param width the new width for the block selection
8576 * @param height the new height for the block selection
8578 * @exception SWTException <ul>
8579 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8580 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8585 public void setBlockSelectionBounds(int x, int y, int width, int height) {
8587 int verticalScrollOffset = getVerticalScrollOffset();
8588 if (!blockSelection) {
8589 x -= horizontalScrollOffset;
8590 y -= verticalScrollOffset;
8591 int start = getOffsetAtPoint(x, y, null);
8592 int end = getOffsetAtPoint(x+width-1, y+height-1, null);
8593 setSelection(start, end - start, false, false);
8597 int minY = topMargin;
8598 int minX = leftMargin;
8599 int maxY = renderer.getHeight() - bottomMargin;
8600 int maxX = Math.max(clientAreaWidth, renderer.getWidth()) - rightMargin;
8601 int anchorX = Math.max(minX, Math.min(maxX, x)) - horizontalScrollOffset;
8602 int anchorY = Math.max(minY, Math.min(maxY, y)) - verticalScrollOffset;
8603 int locationX = Math.max(minX, Math.min(maxX, x + width)) - horizontalScrollOffset;
8604 int locationY = Math.max(minY, Math.min(maxY, y + height - 1)) - verticalScrollOffset;
8605 if (isFixedLineHeight() && renderer.fixedPitch) {
8606 int avg = renderer.averageCharWidth;
8607 anchorX = ((anchorX - leftMargin + horizontalScrollOffset) / avg * avg) + leftMargin - horizontalScrollOffset;
8608 locationX = ((locationX + avg / 2 - leftMargin + horizontalScrollOffset) / avg * avg) + leftMargin - horizontalScrollOffset;
8610 setBlockSelectionLocation(anchorX, anchorY, locationX, locationY, false);
8612 void setBlockSelectionLocation (int x, int y, boolean sendEvent) {
8613 int verticalScrollOffset = getVerticalScrollOffset();
8614 blockXLocation = x + horizontalScrollOffset;
8615 blockYLocation = y + verticalScrollOffset;
8616 int[] alignment = new int[1];
8617 int offset = getOffsetAtPoint(x, y, alignment);
8618 setCaretOffset(offset, alignment[0]);
8619 if (blockXAnchor == -1) {
8620 blockXAnchor = blockXLocation;
8621 blockYAnchor = blockYLocation;
8622 selectionAnchor = caretOffset;
8624 doBlockSelection(sendEvent);
8626 void setBlockSelectionLocation (int anchorX, int anchorY, int x, int y, boolean sendEvent) {
8627 int verticalScrollOffset = getVerticalScrollOffset();
8628 blockXAnchor = anchorX + horizontalScrollOffset;
8629 blockYAnchor = anchorY + verticalScrollOffset;
8630 selectionAnchor = getOffsetAtPoint(anchorX, anchorY, null);
8631 setBlockSelectionLocation(x, y, sendEvent);
8633 void setBlockSelectionOffset (int offset, boolean sendEvent) {
8634 Point point = getPointAtOffset(offset);
8635 int verticalScrollOffset = getVerticalScrollOffset();
8636 blockXLocation = point.x + horizontalScrollOffset;
8637 blockYLocation = point.y + verticalScrollOffset;
8638 setCaretOffset(offset, SWT.DEFAULT);
8639 if (blockXAnchor == -1) {
8640 blockXAnchor = blockXLocation;
8641 blockYAnchor = blockYLocation;
8642 selectionAnchor = caretOffset;
8644 doBlockSelection(sendEvent);
8646 void setBlockSelectionOffset (int anchorOffset, int offset, boolean sendEvent) {
8647 int verticalScrollOffset = getVerticalScrollOffset();
8648 Point anchorPoint = getPointAtOffset(anchorOffset);
8649 blockXAnchor = anchorPoint.x + horizontalScrollOffset;
8650 blockYAnchor = anchorPoint.y + verticalScrollOffset;
8651 selectionAnchor = anchorOffset;
8652 setBlockSelectionOffset(offset, sendEvent);
8655 * Sets the receiver's caret. Set the caret's height and location.
8657 * @param caret the new caret for the receiver
8659 * @exception SWTException <ul>
8660 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8661 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8665 public void setCaret(Caret caret) {
8667 super.setCaret(caret);
8668 caretDirection = SWT.NULL;
8669 if (caret != null) {
8674 * Sets the BIDI coloring mode. When true the BIDI text display
8675 * algorithm is applied to segments of text that are the same
8678 * @param mode the new coloring mode
8679 * @exception SWTException <ul>
8680 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8681 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8684 * @deprecated use BidiSegmentListener instead.
8687 public void setBidiColoring(boolean mode) {
8689 bidiColoring = mode;
8692 * Sets the bottom margin.
8694 * @param bottomMargin the bottom margin.
8695 * @exception SWTException <ul>
8696 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8697 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8702 public void setBottomMargin (int bottomMargin) {
8704 setMargins(getLeftMargin(), topMargin, rightMargin, bottomMargin);
8707 * Moves the Caret to the current caret offset.
8709 void setCaretLocation() {
8710 Point newCaretPos = getPointAtOffset(caretOffset);
8711 setCaretLocation(newCaretPos, getCaretDirection());
8713 void setCaretLocation(final Point location, int direction) {
8714 Caret caret = getCaret();
8715 if (caret != null) {
8716 final boolean isDefaultCaret = caret == defaultCaret;
8717 final StyleRange styleAtOffset = content.getCharCount() > 0 ?
8718 (caretOffset < content.getCharCount() ?
8719 getStyleRangeAtOffset(caretOffset) :
8720 getStyleRangeAtOffset(content.getCharCount() - 1)) : // caret after last char: use last char style
8722 final int caretLine = getCaretLine();
8724 int graphicalLineHeight = getLineHeight();
8725 final int lineStartOffset = getOffsetAtLine(caretLine);
8726 int graphicalLineFirstOffset = lineStartOffset;
8727 final int lineEndOffset = lineStartOffset + getLine(caretLine).length();
8728 int graphicalLineLastOffset = lineEndOffset;
8729 if (caretLine < getLineCount() && renderer.getLineHeight(caretLine) != getLineHeight()) { // word wrap, metrics, styles...
8730 graphicalLineHeight = getLineHeight(caretOffset);
8731 final Rectangle characterBounds = getBoundsAtOffset(caretOffset);
8732 graphicalLineFirstOffset = getOffsetAtPoint(new Point(leftMargin, characterBounds.y));
8733 graphicalLineLastOffset = getOffsetAtPoint(new Point(leftMargin, characterBounds.y + graphicalLineHeight)) - 1;
8734 if (graphicalLineLastOffset < graphicalLineFirstOffset) {
8735 graphicalLineLastOffset = getCharCount();
8739 int caretHeight = getLineHeight();
8740 boolean isTextAlignedAtBottom = true;
8741 if (graphicalLineFirstOffset >= 0) {
8742 for (StyleRange style : getStyleRanges(graphicalLineFirstOffset, graphicalLineLastOffset - graphicalLineFirstOffset)) {
8743 isTextAlignedAtBottom &= (
8744 (style.font == null || Objects.equals(style.font, getFont())) &&
8746 (style.metrics == null || style.metrics.descent <= 0)
8750 if (!isTextAlignedAtBottom || (styleAtOffset != null && styleAtOffset.isVariableHeight())) {
8751 if (isDefaultCaret) {
8752 direction = SWT.DEFAULT;
8753 caretHeight = graphicalLineHeight;
8755 caretHeight = caret.getSize().y;
8758 if (isTextAlignedAtBottom && caretHeight < graphicalLineHeight) {
8759 location.y += (graphicalLineHeight - caretHeight);
8762 int imageDirection = direction;
8764 if (imageDirection == SWT.LEFT) {
8765 imageDirection = SWT.RIGHT;
8766 } else if (imageDirection == SWT.RIGHT) {
8767 imageDirection = SWT.LEFT;
8770 if (isDefaultCaret && imageDirection == SWT.RIGHT) {
8771 location.x -= (caret.getSize().x - 1);
8773 if (isDefaultCaret) {
8774 caret.setBounds(location.x, location.y, caretWidth, caretHeight);
8776 caret.setLocation(location);
8778 if (direction != caretDirection) {
8779 caretDirection = direction;
8780 if (isDefaultCaret) {
8781 if (imageDirection == SWT.DEFAULT) {
8782 defaultCaret.setImage(null);
8783 } else if (imageDirection == SWT.LEFT) {
8784 defaultCaret.setImage(leftCaretBitmap);
8785 } else if (imageDirection == SWT.RIGHT) {
8786 defaultCaret.setImage(rightCaretBitmap);
8789 if (caretDirection == SWT.LEFT) {
8790 BidiUtil.setKeyboardLanguage(BidiUtil.KEYBOARD_NON_BIDI);
8791 } else if (caretDirection == SWT.RIGHT) {
8792 BidiUtil.setKeyboardLanguage(BidiUtil.KEYBOARD_BIDI);
8795 updateCaretVisibility();
8797 columnX = location.x;
8800 * Sets the caret offset.
8802 * @param offset caret offset, relative to the first character in the text.
8803 * @exception SWTException <ul>
8804 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8805 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8807 * @exception IllegalArgumentException <ul>
8808 * <li>ERROR_INVALID_ARGUMENT when the offset is inside a multi byte line
8809 * delimiter (and thus neither clearly in front of or after the line delimiter)
8812 public void setCaretOffset(int offset) {
8814 int length = getCharCount();
8815 if (length > 0 && offset != caretOffset) {
8818 } else if (offset > length) {
8821 if (isLineDelimiter(offset)) {
8822 // offset is inside a multi byte line delimiter. This is an
8823 // illegal operation and an exception is thrown. Fixes 1GDKK3R
8824 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
8827 setCaretOffset(offset, PREVIOUS_OFFSET_TRAILING);
8828 // clear the selection if the caret is moved.
8829 // don't notify listeners about the selection change.
8830 if (blockSelection) {
8831 clearBlockSelection(true, false);
8833 clearSelection(false);
8838 void setCaretOffset(int offset, int alignment) {
8839 if (caretOffset != offset) {
8840 caretOffset = offset;
8841 if (isListening(ST.CaretMoved)) {
8842 StyledTextEvent event = new StyledTextEvent(content);
8843 event.end = caretOffset;
8844 notifyListeners(ST.CaretMoved, event);
8847 if (alignment != SWT.DEFAULT) {
8848 caretAlignment = alignment;
8852 * Copies the specified text range to the clipboard. The text will be placed
8853 * in the clipboard in plain text format and RTF format.
8855 * @param start start index of the text
8856 * @param length length of text to place in clipboard
8858 * @exception SWTError
8859 * @see org.eclipse.swt.dnd.Clipboard#setContents
8861 void setClipboardContent(int start, int length, int clipboardType) throws SWTError {
8862 if (clipboardType == DND.SELECTION_CLIPBOARD && !IS_GTK) return;
8863 TextTransfer plainTextTransfer = TextTransfer.getInstance();
8864 TextWriter plainTextWriter = new TextWriter(start, length);
8865 String plainText = getPlatformDelimitedText(plainTextWriter);
8868 if (clipboardType == DND.SELECTION_CLIPBOARD) {
8869 data = new Object[]{plainText};
8870 types = new Transfer[]{plainTextTransfer};
8872 RTFTransfer rtfTransfer = RTFTransfer.getInstance();
8873 RTFWriter rtfWriter = new RTFWriter(start, length);
8874 String rtfText = getPlatformDelimitedText(rtfWriter);
8875 data = new Object[]{rtfText, plainText};
8876 types = new Transfer[]{rtfTransfer, plainTextTransfer};
8878 clipboard.setContents(data, types, clipboardType);
8881 * Sets the content implementation to use for text storage.
8883 * @param newContent StyledTextContent implementation to use for text storage.
8884 * @exception SWTException <ul>
8885 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8886 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8888 * @exception IllegalArgumentException <ul>
8889 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
8892 public void setContent(StyledTextContent newContent) {
8894 if (newContent == null) {
8895 SWT.error(SWT.ERROR_NULL_ARGUMENT);
8897 if (content != null) {
8898 content.removeTextChangeListener(textChangeListener);
8900 content = newContent;
8901 content.addTextChangeListener(textChangeListener);
8905 * Sets the receiver's cursor to the cursor specified by the
8906 * argument. Overridden to handle the null case since the
8907 * StyledText widget uses an ibeam as its default cursor.
8909 * @see Control#setCursor(Cursor)
8912 public void setCursor (Cursor cursor) {
8914 if (cursor != null && cursor.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
8915 this.cursor = cursor;
8916 if (cursor == null) {
8917 Display display = getDisplay();
8918 int type = blockSelection ? SWT.CURSOR_CROSS : SWT.CURSOR_IBEAM;
8919 super.setCursor(display.getSystemCursor(type));
8921 super.setCursor(cursor);
8925 * Sets whether the widget implements double click mouse behavior.
8927 * @param enable if true double clicking a word selects the word, if false
8928 * double clicks have the same effect as regular mouse clicks.
8929 * @exception SWTException <ul>
8930 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8931 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8934 public void setDoubleClickEnabled(boolean enable) {
8936 doubleClickEnabled = enable;
8939 public void setDragDetect (boolean dragDetect) {
8941 this.dragDetect = dragDetect;
8944 * Sets whether the widget content can be edited.
8946 * @param editable if true content can be edited, if false content can not be
8948 * @exception SWTException <ul>
8949 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8950 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8953 public void setEditable(boolean editable) {
8955 this.editable = editable;
8958 public void setEnabled(boolean enabled) {
8959 super.setEnabled(enabled);
8960 Display display = getDisplay();
8961 this.enabled = enabled;
8962 this.insideSetEnableCall = true;
8965 if (!customBackground) setBackground(display.getSystemColor(SWT.COLOR_LIST_BACKGROUND));
8966 if (!customForeground) setForeground(display.getSystemColor(SWT.COLOR_LIST_FOREGROUND));
8968 if (!customBackground) setBackground(display.getSystemColor(SWT.COLOR_TEXT_DISABLED_BACKGROUND));
8969 if (!customForeground) setForeground(display.getSystemColor(SWT.COLOR_WIDGET_DISABLED_FOREGROUND));
8973 this.insideSetEnableCall = false;
8977 * Sets a new font to render text with.
8979 * <b>NOTE:</b> Italic fonts are not supported unless they have no overhang
8980 * and the same baseline as regular fonts.
8983 * @param font new font
8984 * @exception SWTException <ul>
8985 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8986 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8990 public void setFont(Font font) {
8992 int oldLineHeight = renderer.getLineHeight();
8993 super.setFont(font);
8994 renderer.setFont(getFont(), tabLength);
8995 // keep the same top line visible. fixes 5815
8996 if (isFixedLineHeight()) {
8997 int lineHeight = renderer.getLineHeight();
8998 if (lineHeight != oldLineHeight) {
8999 int vscroll = (getVerticalScrollOffset() * lineHeight / oldLineHeight) - getVerticalScrollOffset();
9000 scrollVertical(vscroll, true);
9003 resetCache(0, content.getLineCount());
9004 claimBottomFreeSpace();
9005 calculateScrollBars();
9006 if (isBidiCaret()) createCaretBitmaps();
9007 caretDirection = SWT.NULL;
9012 public void setForeground(Color color) {
9014 boolean foregroundDisabled = false;
9015 if (!this.enabled && color == null) {
9016 if (foreground != null) {
9017 Color disabledFg = getDisplay().getSystemColor(SWT.COLOR_WIDGET_DISABLED_FOREGROUND);
9018 if (foreground.equals(disabledFg)) {
9021 color = new Color (getDisplay(), disabledFg.getRGBA());
9022 foregroundDisabled = true;
9026 customForeground = color != null && !this.insideSetEnableCall && !foregroundDisabled;
9028 super.setForeground(color);
9029 resetCache(0, content.getLineCount());
9034 * Sets the horizontal scroll offset relative to the start of the line.
9035 * Do nothing if there is no text set.
9037 * <b>NOTE:</b> The horizontal index is reset to 0 when new text is set in the
9041 * @param offset horizontal scroll offset relative to the start
9042 * of the line, measured in character increments starting at 0, if
9043 * equal to 0 the content is not scrolled, if > 0 = the content is scrolled.
9044 * @exception SWTException <ul>
9045 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9046 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9049 public void setHorizontalIndex(int offset) {
9051 if (getCharCount() == 0) {
9057 offset *= getHorizontalIncrement();
9058 // allow any value if client area width is unknown or 0.
9059 // offset will be checked in resize handler.
9060 // don't use isVisible since width is known even if widget
9061 // is temporarily invisible
9062 if (clientAreaWidth > 0) {
9063 int width = renderer.getWidth();
9064 // prevent scrolling if the content fits in the client area.
9065 // align end of longest line with right border of client area
9066 // if offset is out of range.
9067 if (offset > width - clientAreaWidth) {
9068 offset = Math.max(0, width - clientAreaWidth);
9071 scrollHorizontal(offset - horizontalScrollOffset, true);
9074 * Sets the horizontal SWT logical point offset relative to the start of the line.
9075 * Do nothing if there is no text set.
9077 * <b>NOTE:</b> The horizontal SWT logical point offset is reset to 0 when new text
9078 * is set in the widget.
9081 * @param pixel horizontal SWT logical point offset relative to the start
9083 * @exception SWTException <ul>
9084 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9085 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9089 public void setHorizontalPixel(int pixel) {
9091 if (getCharCount() == 0) {
9097 // allow any value if client area width is unknown or 0.
9098 // offset will be checked in resize handler.
9099 // don't use isVisible since width is known even if widget
9100 // is temporarily invisible
9101 if (clientAreaWidth > 0) {
9102 int width = renderer.getWidth();
9103 // prevent scrolling if the content fits in the client area.
9104 // align end of longest line with right border of client area
9105 // if offset is out of range.
9106 if (pixel > width - clientAreaWidth) {
9107 pixel = Math.max(0, width - clientAreaWidth);
9110 scrollHorizontal(pixel - horizontalScrollOffset, true);
9113 * Sets the line indentation of the widget.
9115 * It is the amount of blank space, in points, at the beginning of each line.
9116 * When a line wraps in several lines only the first one is indented.
9119 * @param indent the new indent
9121 * @exception SWTException <ul>
9122 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9123 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9126 * @see #setLineIndent(int, int, int)
9130 public void setIndent(int indent) {
9132 if (this.indent == indent || indent < 0) return;
9133 this.indent = indent;
9134 resetCache(0, content.getLineCount());
9139 * Sets whether the widget should justify lines.
9141 * @param justify whether lines should be justified
9143 * @exception SWTException <ul>
9144 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9145 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9148 * @see #setLineJustify(int, int, boolean)
9152 public void setJustify(boolean justify) {
9154 if (this.justify == justify) return;
9155 this.justify = justify;
9156 resetCache(0, content.getLineCount());
9161 * Maps a key to an action.
9163 * One action can be associated with N keys. However, each key can only
9164 * have one action (key:action is N:1 relation).
9167 * @param key a key code defined in SWT.java or a character.
9168 * Optionally ORd with a state mask. Preferred state masks are one or more of
9169 * SWT.MOD1, SWT.MOD2, SWT.MOD3, since these masks account for modifier platform
9170 * differences. However, there may be cases where using the specific state masks
9171 * (i.e., SWT.CTRL, SWT.SHIFT, SWT.ALT, SWT.COMMAND) makes sense.
9172 * @param action one of the predefined actions defined in ST.java.
9173 * Use SWT.NULL to remove a key binding.
9174 * @exception SWTException <ul>
9175 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9176 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9179 public void setKeyBinding(int key, int action) {
9181 int modifierValue = key & SWT.MODIFIER_MASK;
9182 int keyInt = key & SWT.KEY_MASK;
9183 char keyChar = (char)keyInt;
9185 * Bug 440535: Make sure the key getting mapped to letter is in defiened
9186 * character range and filter out incorrect int to char typecasting. For
9187 * Example: SWT.KEYPAD_CR int gets wrongly type-cast to char letter 'p'
9189 if (Character.isDefined(keyInt) && Character.isLetter(keyChar)) {
9190 // make the keybinding case insensitive by adding it
9191 // in its upper and lower case form
9192 char ch = Character.toUpperCase(keyChar);
9193 int newKey = ch | modifierValue;
9194 if (action == SWT.NULL) {
9195 keyActionMap.remove(newKey);
9197 keyActionMap.put(newKey, action);
9199 ch = Character.toLowerCase(keyChar);
9200 newKey = ch | modifierValue;
9201 if (action == SWT.NULL) {
9202 keyActionMap.remove(newKey);
9204 keyActionMap.put(newKey, action);
9207 if (action == SWT.NULL) {
9208 keyActionMap.remove(key);
9210 keyActionMap.put(key, action);
9215 * Sets the left margin.
9217 * @param leftMargin the left margin.
9218 * @exception SWTException <ul>
9219 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9220 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9225 public void setLeftMargin (int leftMargin) {
9227 setMargins(leftMargin, topMargin, rightMargin, bottomMargin);
9230 * Sets the alignment of the specified lines. The argument should be one of <code>SWT.LEFT</code>,
9231 * <code>SWT.CENTER</code> or <code>SWT.RIGHT</code>.
9233 * Note that if <code>SWT.MULTI</code> is set, then <code>SWT.WRAP</code> must also be set
9234 * in order to stabilize the right edge before setting alignment.
9236 * Should not be called if a LineStyleListener has been set since the listener
9237 * maintains the line attributes.
9239 * All line attributes are maintained relative to the line text, not the
9240 * line index that is specified in this method call.
9241 * During text changes, when entire lines are inserted or removed, the line
9242 * attributes that are associated with the lines after the change
9243 * will "move" with their respective text. An entire line is defined as
9244 * extending from the first character on a line to the last and including the
9247 * When two lines are joined by deleting a line delimiter, the top line
9248 * attributes take precedence and the attributes of the bottom line are deleted.
9249 * For all other text changes line attributes will remain unchanged.
9251 * @param startLine first line the alignment is applied to, 0 based
9252 * @param lineCount number of lines the alignment applies to.
9253 * @param alignment line alignment
9255 * @exception SWTException <ul>
9256 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9257 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9259 * @exception IllegalArgumentException <ul>
9260 * <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
9262 * @see #setAlignment(int)
9265 public void setLineAlignment(int startLine, int lineCount, int alignment) {
9267 if (isListening(ST.LineGetStyle)) return;
9268 if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
9269 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9272 renderer.setLineAlignment(startLine, lineCount, alignment);
9273 resetCache(startLine, lineCount);
9274 redrawLines(startLine, lineCount, false);
9275 int caretLine = getCaretLine();
9276 if (startLine <= caretLine && caretLine < startLine + lineCount) {
9282 * Sets the background color of the specified lines.
9284 * The background color is drawn for the width of the widget. All
9285 * line background colors are discarded when setText is called.
9286 * The text background color if defined in a StyleRange overlays the
9287 * line background color.
9289 * Should not be called if a LineBackgroundListener has been set since the
9290 * listener maintains the line backgrounds.
9292 * All line attributes are maintained relative to the line text, not the
9293 * line index that is specified in this method call.
9294 * During text changes, when entire lines are inserted or removed, the line
9295 * attributes that are associated with the lines after the change
9296 * will "move" with their respective text. An entire line is defined as
9297 * extending from the first character on a line to the last and including the
9300 * When two lines are joined by deleting a line delimiter, the top line
9301 * attributes take precedence and the attributes of the bottom line are deleted.
9302 * For all other text changes line attributes will remain unchanged.
9305 * @param startLine first line the color is applied to, 0 based
9306 * @param lineCount number of lines the color applies to.
9307 * @param background line background color
9308 * @exception SWTException <ul>
9309 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9310 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9312 * @exception IllegalArgumentException <ul>
9313 * <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
9316 public void setLineBackground(int startLine, int lineCount, Color background) {
9318 if (isListening(ST.LineGetBackground)) return;
9319 if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
9320 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9322 if (background != null) {
9323 renderer.setLineBackground(startLine, lineCount, background);
9325 renderer.clearLineBackground(startLine, lineCount);
9327 redrawLines(startLine, lineCount, false);
9330 * Sets the bullet of the specified lines.
9332 * Should not be called if a LineStyleListener has been set since the listener
9333 * maintains the line attributes.
9335 * All line attributes are maintained relative to the line text, not the
9336 * line index that is specified in this method call.
9337 * During text changes, when entire lines are inserted or removed, the line
9338 * attributes that are associated with the lines after the change
9339 * will "move" with their respective text. An entire line is defined as
9340 * extending from the first character on a line to the last and including the
9343 * When two lines are joined by deleting a line delimiter, the top line
9344 * attributes take precedence and the attributes of the bottom line are deleted.
9345 * For all other text changes line attributes will remain unchanged.
9348 * @param startLine first line the bullet is applied to, 0 based
9349 * @param lineCount number of lines the bullet applies to.
9350 * @param bullet line bullet
9352 * @exception SWTException <ul>
9353 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9354 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9356 * @exception IllegalArgumentException <ul>
9357 * <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
9361 public void setLineBullet(int startLine, int lineCount, Bullet bullet) {
9363 if (isListening(ST.LineGetStyle)) return;
9364 if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
9365 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9367 int oldBottom = getLinePixel(startLine + lineCount);
9368 renderer.setLineBullet(startLine, lineCount, bullet);
9369 resetCache(startLine, lineCount);
9370 int newBottom = getLinePixel(startLine + lineCount);
9371 redrawLines(startLine, lineCount, oldBottom != newBottom);
9372 int caretLine = getCaretLine();
9373 if (startLine <= caretLine && caretLine < startLine + lineCount) {
9378 * Returns true if StyledText is in word wrap mode and false otherwise.
9380 * @return true if StyledText is in word wrap mode and false otherwise.
9382 boolean isWordWrap() {
9383 return wordWrap || visualWrap;
9386 * Sets the indent of the specified lines.
9388 * Should not be called if a LineStyleListener has been set since the listener
9389 * maintains the line attributes.
9391 * All line attributes are maintained relative to the line text, not the
9392 * line index that is specified in this method call.
9393 * During text changes, when entire lines are inserted or removed, the line
9394 * attributes that are associated with the lines after the change
9395 * will "move" with their respective text. An entire line is defined as
9396 * extending from the first character on a line to the last and including the
9399 * When two lines are joined by deleting a line delimiter, the top line
9400 * attributes take precedence and the attributes of the bottom line are deleted.
9401 * For all other text changes line attributes will remain unchanged.
9404 * @param startLine first line the indent is applied to, 0 based
9405 * @param lineCount number of lines the indent applies to.
9406 * @param indent line indent
9408 * @exception SWTException <ul>
9409 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9410 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9412 * @exception IllegalArgumentException <ul>
9413 * <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
9415 * @see #setIndent(int)
9418 public void setLineIndent(int startLine, int lineCount, int indent) {
9420 if (isListening(ST.LineGetStyle)) return;
9421 if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
9422 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9424 int oldBottom = getLinePixel(startLine + lineCount);
9425 renderer.setLineIndent(startLine, lineCount, indent);
9426 resetCache(startLine, lineCount);
9427 int newBottom = getLinePixel(startLine + lineCount);
9428 redrawLines(startLine, lineCount, oldBottom != newBottom);
9429 int caretLine = getCaretLine();
9430 if (startLine <= caretLine && caretLine < startLine + lineCount) {
9436 * Sets the vertical indent of the specified lines.
9438 * Should not be called if a LineStyleListener has been set since the listener
9439 * maintains the line attributes.
9441 * All line attributes are maintained relative to the line text, not the
9442 * line index that is specified in this method call.
9443 * During text changes, when entire lines are inserted or removed, the line
9444 * attributes that are associated with the lines after the change
9445 * will "move" with their respective text. An entire line is defined as
9446 * extending from the first character on a line to the last and including the
9449 * When two lines are joined by deleting a line delimiter, the top line
9450 * attributes take precedence and the attributes of the bottom line are deleted.
9451 * For all other text changes line attributes will remain unchanged.
9453 * Setting both line spacing and vertical indent on a line would result in the
9454 * spacing and indent add up for the line.
9457 * @param lineIndex line index the vertical indent is applied to, 0 based
9458 * @param verticalLineIndent vertical line indent
9460 * @exception SWTException <ul>
9461 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9462 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9464 * @exception IllegalArgumentException <ul>
9465 * <li>ERROR_INVALID_ARGUMENT when the specified line index is invalid</li>
9469 public void setLineVerticalIndent(int lineIndex, int verticalLineIndent) {
9471 if (isListening(ST.LineGetStyle)) return;
9472 if (lineIndex < 0 || lineIndex >= content.getLineCount()) {
9473 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9475 if (verticalLineIndent == renderer.getLineVerticalIndent(lineIndex)) {
9478 int oldBottom = getLinePixel(lineIndex + 1);
9479 if (oldBottom <= getClientArea().height) {
9480 verticalScrollOffset = -1;
9482 renderer.setLineVerticalIndent(lineIndex, verticalLineIndent);
9483 hasVerticalIndent = verticalLineIndent != 0 || renderer.hasVerticalIndent();
9484 resetCache(lineIndex, 1);
9485 int newBottom = getLinePixel(lineIndex + 1);
9486 redrawLines(lineIndex, 1, oldBottom != newBottom);
9487 int caretLine = getCaretLine();
9488 if (lineIndex <= caretLine && caretLine < lineIndex + 1) {
9494 * Sets the justify of the specified lines.
9496 * Should not be called if a LineStyleListener has been set since the listener
9497 * maintains the line attributes.
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
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.
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
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>
9520 * @exception IllegalArgumentException <ul>
9521 * <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
9523 * @see #setJustify(boolean)
9526 public void setLineJustify(int startLine, int lineCount, boolean justify) {
9528 if (isListening(ST.LineGetStyle)) return;
9529 if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
9530 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
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) {
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}.
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>
9551 * @see #setLineSpacingProvider(StyledTextLineSpacingProvider)
9554 public void setLineSpacing(int lineSpacing) {
9556 if (this.lineSpacing == lineSpacing || lineSpacing < 0) return;
9557 this.lineSpacing = lineSpacing;
9558 resetCache(0, content.getLineCount());
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.
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>
9571 * @see #setLineSpacingProvider(StyledTextLineSpacingProvider)
9574 public void setLineSpacingProvider(StyledTextLineSpacingProvider lineSpacingProvider) {
9576 boolean wasFixedLineHeight = isFixedLineHeight();
9577 if (renderer.getLineSpacingProvider() == null && lineSpacingProvider == null
9578 || (renderer.getLineSpacingProvider() != null
9579 && renderer.getLineSpacingProvider().equals(lineSpacingProvider)))
9581 renderer.setLineSpacingProvider(lineSpacingProvider);
9582 // reset lines cache if needed
9583 if (lineSpacingProvider == null) {
9584 if (!wasFixedLineHeight) {
9585 resetCache(0, content.getLineCount());
9588 if (wasFixedLineHeight) {
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) {
9601 if (firstLine != -1) {
9602 // call reset cache for the first line which have changed to recompute scrollbars
9603 resetCache(firstLine, 0);
9611 * Sets the tab stops of the specified lines.
9613 * Should not be called if a <code>LineStyleListener</code> has been set since the listener
9614 * maintains the line attributes.
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
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.
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
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>
9637 * @exception IllegalArgumentException <ul>
9638 * <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
9640 * @see #setTabStops(int[])
9643 public void setLineTabStops(int startLine, int lineCount, int[] tabStops) {
9645 if (isListening(ST.LineGetStyle)) return;
9646 if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
9647 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9649 if (tabStops != null) {
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];
9656 renderer.setLineTabStops(startLine, lineCount, newTabs);
9658 renderer.setLineTabStops(startLine, lineCount, null);
9660 resetCache(startLine, lineCount);
9661 redrawLines(startLine, lineCount, false);
9662 int caretLine = getCaretLine();
9663 if (startLine <= caretLine && caretLine < startLine + lineCount) {
9668 * Sets the wrap indent of the specified lines.
9670 * Should not be called if a <code>LineStyleListener</code> has been set since the listener
9671 * maintains the line attributes.
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
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.
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
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>
9694 * @exception IllegalArgumentException <ul>
9695 * <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
9697 * @see #setWrapIndent(int)
9700 public void setLineWrapIndent(int startLine, int lineCount, int wrapIndent) {
9702 if (isListening(ST.LineGetStyle)) return;
9703 if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
9704 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
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) {
9718 * Sets the color of the margins.
9720 * @param color the new color (or null)
9721 * @exception IllegalArgumentException <ul>
9722 * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
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>
9731 public void setMarginColor(Color color) {
9733 if (color != null && color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9734 marginColor = color;
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>
9751 public void setMargins (int leftMargin, int topMargin, int rightMargin, int bottomMargin) {
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);
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.
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>
9774 public void setMouseNavigatorEnabled(boolean enabled) {
9776 if ((enabled && mouseNavigator != null) || (!enabled && mouseNavigator == null)) {
9780 mouseNavigator = new MouseNavigator(this);
9782 mouseNavigator.dispose();
9783 mouseNavigator = null;
9787 * Flips selection anchor based on word selection direction.
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;
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>.
9802 * @param orientation new orientation style
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>
9812 public void setOrientation(int orientation) {
9813 int oldOrientation = getOrientation();
9814 super.setOrientation(orientation);
9815 int newOrientation = getOrientation();
9816 if (oldOrientation != newOrientation) {
9821 * Sets the right margin.
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>
9831 public void setRightMargin (int rightMargin) {
9833 setMargins(getLeftMargin(), topMargin, rightMargin, bottomMargin);
9835 void setScrollBar(ScrollBar bar, int clientArea, int maximum, int margin) {
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);
9847 * Adjusts the maximum and the page size of the scroll bars to
9848 * reflect content width/length changes.
9850 * @param vertical indicates if the vertical scrollbar also needs to be set
9852 void setScrollBars(boolean vertical) {
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);
9863 if (verticalBar != null) {
9864 setScrollBar(verticalBar, clientAreaHeight, renderer.getHeight(), topMargin + bottomMargin);
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);
9875 if (!alwaysShowScroll) {
9876 redrawMargins(oldHeight, oldWidth);
9881 * Sets the selection to the given position and scrolls it into view. Equivalent to setSelection(start,start).
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>
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)
9894 public void setSelection(int start) {
9895 // checkWidget test done in setSelectionRange
9896 setSelection(start, start);
9899 * Sets the selection and scrolls it into view.
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
9906 * @param point x=selection start offset, y=selection end offset
9907 * The caret will be placed at the selection start when x > 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>
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)
9919 public void setSelection(Point point) {
9921 if (point == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
9922 setSelection(point.x, point.y);
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.
9929 * @param color the new color (or null)
9931 * @exception IllegalArgumentException <ul>
9932 * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
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>
9940 public void setSelectionBackground (Color color) {
9942 if (color != null) {
9943 if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9945 selectionBackground = color;
9946 resetCache(0, content.getLineCount());
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.
9955 * Note that this is a <em>HINT</em>. Some platforms do not allow the application
9956 * to change the selection foreground color.
9958 * @param color the new color (or null)
9960 * @exception IllegalArgumentException <ul>
9961 * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
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>
9969 public void setSelectionForeground (Color color) {
9971 if (color != null) {
9972 if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9974 selectionForeground = color;
9975 resetCache(0, content.getLineCount());
9980 * Sets the selection and scrolls it into view.
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
9987 * @param start selection start offset. The caret will be placed at the
9988 * selection start when start > 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>
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)
10000 public void setSelection(int start, int end) {
10001 setSelectionRange(start, end - start);
10005 * Sets the selection.
10007 * The new selection may not be visible. Call showSelection to scroll
10008 * the selection into view.
10011 * @param start offset of the first selected character, start >= 0 must be true.
10012 * @param length number of characters to select, 0 <= start + length
10013 * <= 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.
10018 void setSelection(int start, int length, boolean sendEvent, boolean doBlock) {
10019 int end = start + length;
10025 // is the selection range different or is the selection direction
10027 if (selection.x != start || selection.y != end ||
10028 (length > 0 && selectionAnchor != selection.x) ||
10029 (length < 0 && selectionAnchor != selection.y)) {
10030 if (blockSelection && doBlock) {
10032 setBlockSelectionOffset(end, start, sendEvent);
10034 setBlockSelectionOffset(start, end, sendEvent);
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);
10045 selectionAnchor = selection.y = end;
10046 selection.x = start;
10047 setCaretOffset(start, PREVIOUS_OFFSET_TRAILING);
10049 selectionAnchor = selection.x = start;
10051 setCaretOffset(end, PREVIOUS_OFFSET_TRAILING);
10053 redrawX = Math.min(redrawX, selection.x);
10054 redrawY = Math.max(redrawY, selection.y);
10055 if (redrawY - redrawX > 0) {
10056 internalRedrawRange(redrawX, redrawY - redrawX);
10058 if (sendEvent && (oldLength != end - start || (oldLength != 0 && oldStart != start))) {
10059 sendSelectionEvent();
10061 sendAccessibleTextCaretMoved();
10066 * Sets the selection.
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.
10072 * @param start offset of the first selected character
10073 * @param length number of characters to select
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>
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)
10084 public void setSelectionRange(int start, int length) {
10086 int contentLength = getCharCount();
10087 start = Math.max(0, Math.min (start, contentLength));
10088 int end = start + length;
10092 if (end > contentLength) length = contentLength - start;
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);
10100 setSelection(start, length, false, true);
10101 setCaretLocation();
10104 * Adds the specified style.
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.
10111 * Should not be called if a LineStyleListener has been set since the
10112 * listener maintains the styles.
10115 * @param range StyleRange object containing the style information.
10116 * Overwrites the old style in the given range. May be null to delete
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>
10122 * @exception IllegalArgumentException <ul>
10123 * <li>ERROR_INVALID_RANGE when the style range is outside the valid range (> getCharCount())</li>
10126 public void setStyleRange(StyleRange range) {
10128 if (isListening(ST.LineGetStyle)) return;
10129 if (range != null) {
10130 if (range.isUnstyled()) {
10131 setStyleRanges(range.start, range.length, null, null, false);
10133 setStyleRanges(range.start, 0, null, new StyleRange[]{range}, false);
10136 setStyleRanges(0, 0, null, null, true);
10140 * Clears the styles in the range specified by <code>start</code> and
10141 * <code>length</code> and adds the new styles.
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.
10149 * Note: It is expected that the same instance of a StyleRange will occur
10150 * multiple times within the styles array, reducing memory usage.
10152 * Should not be called if a LineStyleListener has been set since the
10153 * listener maintains the styles.
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.
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>
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 (> getCharCount() or less than zero)</li>
10169 * <li>ERROR_INVALID_RANGE when a range overlaps</li>
10174 public void setStyleRanges(int start, int length, int[] ranges, StyleRange[] styles) {
10176 if (isListening(ST.LineGetStyle)) return;
10177 if (ranges == null || styles == null) {
10178 setStyleRanges(start, length, null, null, false);
10180 setStyleRanges(start, length, ranges, styles, false);
10184 * Sets styles to be used for rendering the widget content.
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.
10193 * Note: It is expected that the same instance of a StyleRange will occur
10194 * multiple times within the styles array, reducing memory usage.
10196 * Should not be called if a LineStyleListener has been set since the
10197 * listener maintains the styles.
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.
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>
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 (> getCharCount() or less than zero)</li>
10211 * <li>ERROR_INVALID_RANGE when a range overlaps</li>
10216 public void setStyleRanges(int[] ranges, StyleRange[] styles) {
10218 if (isListening(ST.LineGetStyle)) return;
10219 if (ranges == null || styles == null) {
10220 setStyleRanges(0, 0, null, null, true);
10222 setStyleRanges(0, 0, ranges, styles, true);
10225 void setStyleRanges(int start, int length, int[] ranges, StyleRange[] styles, boolean reset) {
10226 int charCount = content.getCharCount();
10229 length = charCount;
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);
10238 if (styles != null) {
10239 if (end > charCount) {
10240 SWT.error(SWT.ERROR_INVALID_RANGE);
10242 if (ranges != null) {
10243 if (ranges.length != styles.length << 1) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
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];
10253 rangeStart = styles[i].start;
10254 rangeLength = styles[i].length;
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;
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];
10269 rangeStart = styles[0].start;
10270 rangeEnd = styles[styles.length - 1].start + styles[styles.length - 1].length;
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);
10285 renderer.setStyleRanges(null, null);
10287 renderer.updateRanges(start, length, length);
10289 if (styles != null && styles.length > 0) {
10290 renderer.setStyleRanges(ranges, styles);
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;
10300 SortedSet<Integer> modifiedLines = computeModifiedLines(formerRanges, formerStyles, ranges, styles);
10301 resetCache(modifiedLines);
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)) {
10311 int bottom = clientAreaHeight;
10312 if (partialTopIndex <= lineStart && lineStart <= partialBottomIndex) {
10313 top = Math.max(0, getLinePixel(lineStart));
10315 if (partialTopIndex <= lineEnd && lineEnd <= partialBottomIndex) {
10316 bottom = getLinePixel(lineEnd + 1);
10318 if (!(wasFixedLineHeight && isFixedLineHeight()) && bottom != expectedBottom) {
10319 bottom = clientAreaHeight;
10321 super.redraw(0, top, clientAreaWidth, bottom - top, false);
10324 int oldColumnX = columnX;
10325 setCaretLocation();
10326 columnX = oldColumnX;
10327 doMouseLinkCursor();
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
10338 private SortedSet<Integer> computeModifiedLines(int[] referenceRanges, StyleRange[] referenceStyles, int[] newRanges, StyleRange[] newStyles) {
10339 if (referenceStyles == null) {
10340 referenceStyles = new StyleRange[0];
10342 if (referenceRanges == null) {
10343 referenceRanges = createRanges(referenceStyles);
10345 if (newStyles == null) {
10346 newStyles = new StyleRange[0];
10348 if (newRanges == null) {
10349 newRanges = createRanges(newStyles);
10351 if (referenceRanges.length != 2 * referenceStyles.length) {
10352 throw new IllegalArgumentException();
10354 if (newRanges.length != 2 * newStyles.length) {
10355 throw new IllegalArgumentException();
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]);
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
10371 while (referenceRangeIndex < referenceStyles.length && endRangeOffset(referenceRanges, referenceRangeIndex) <= currentOffset) {
10372 referenceRangeIndex++;
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
10382 while (newRangeIndex < newStyles.length && endRangeOffset(newRanges, newRangeIndex) <= currentOffset) {
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]);
10393 if (!referenceStyleAtCurrentOffset.similarTo(newStyleAtCurrentOffset)) {
10394 int fromLine = getLineAtOffset(currentOffset);
10395 int toLine = getLineAtOffset(nextMilestoneOffset - 1);
10396 for (int line = fromLine; line <= toLine; line++) {
10399 currentOffset = toLine + 1 < getLineCount() ? getOffsetAtLine(toLine + 1) : content.getCharCount();
10401 currentOffset = nextMilestoneOffset;
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;
10413 return referenceRanges;
10416 private boolean isInRange(int[] ranges, int styleIndex, int offset) {
10417 if (ranges == null || ranges.length == 0 || styleIndex < 0 || 2 * styleIndex + 1 > ranges.length) {
10420 int start = ranges[2 * styleIndex];
10421 int length = ranges[2 * styleIndex + 1];
10422 return offset >= start && offset < start + length;
10426 * The offset on which the range ends (excluded)
10428 * @param styleIndex
10431 private int endRangeOffset(int[] ranges, int styleIndex) {
10432 if (styleIndex < 0 || 2 * styleIndex > ranges.length) {
10433 throw new IllegalArgumentException();
10435 int start = ranges[2 * styleIndex];
10436 int length = ranges[2 * styleIndex + 1];
10437 return start + length;
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.
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.
10450 * Should not be called if a LineStyleListener has been set since the
10451 * listener maintains the styles.
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>
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 (> getCharCount())</li>
10466 * @see #setStyleRanges(int[], StyleRange[])
10468 public void setStyleRanges(StyleRange[] ranges) {
10470 if (isListening(ST.LineGetStyle)) return;
10471 if (ranges == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
10472 setStyleRanges(0, 0, null, ranges, true);
10475 * Sets the tab width.
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>
10483 * @see #setTabStops(int[])
10485 public void setTabs(int tabs) {
10488 renderer.setFont(null, tabs);
10489 resetCache(0, content.getLineCount());
10490 setCaretLocation();
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.
10499 * @param tabs the new tab list (or null)
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>
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>
10509 * @see StyledText#getTabStops()
10513 public void setTabStops(int [] tabs) {
10515 if (tabs != null) {
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];
10522 this.tabs = newTabs;
10526 resetCache(0, content.getLineCount());
10527 setCaretLocation();
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
10538 * <b>Note:</b> Only a single line of text should be set when the SWT.SINGLE
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>
10549 * @exception IllegalArgumentException <ul>
10550 * <li>ERROR_NULL_ARGUMENT when string is null</li>
10553 public void setText(String text) {
10555 if (text == null) {
10556 SWT.error(SWT.ERROR_NULL_ARGUMENT);
10558 Event event = new Event();
10560 event.end = getCharCount();
10563 notifyListeners(SWT.Verify, event);
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);
10572 content.setText(event.text);
10573 notifyListeners(SWT.Modify, event);
10574 if (styledTextEvent != null) {
10575 notifyListeners(ST.ExtendedModify, styledTextEvent);
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>.
10585 * <code>setOrientation</code> would override this value with the text direction
10586 * that is consistent with the new orientation.
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.
10593 * @param textDirection the base text direction style
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>
10600 * @see SWT#FLIP_TEXT_DIRECTION
10603 public void setTextDirection(int textDirection) {
10605 int oldStyle = getStyle();
10606 super.setTextDirection(textDirection);
10607 if (isAutoDirection () || oldStyle != getStyle()) {
10613 * Sets the text limit to the specified number of characters.
10615 * The text limit specifies the amount of text that
10616 * the user can type into the widget.
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>
10624 * @exception IllegalArgumentException <ul>
10625 * <li>ERROR_CANNOT_BE_ZERO when limit is 0</li>
10628 public void setTextLimit(int limit) {
10631 SWT.error(SWT.ERROR_CANNOT_BE_ZERO);
10636 * Sets the top index. Do nothing if there is no text set.
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.
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>
10653 public void setTopIndex(int topIndex) {
10655 if (getCharCount() == 0) {
10658 int lineCount = content.getLineCount(), pixel;
10659 if (isFixedLineHeight()) {
10660 int pageSize = Math.max(1, Math.min(lineCount, getLineCountWhole()));
10661 if (topIndex < 0) {
10663 } else if (topIndex > lineCount - pageSize) {
10664 topIndex = lineCount - pageSize;
10666 pixel = getLinePixel(topIndex);
10668 topIndex = Math.max(0, Math.min(lineCount - 1, topIndex));
10669 pixel = getLinePixel(topIndex);
10671 pixel = getAvailableHeightBellow(pixel);
10673 pixel = getAvailableHeightAbove(pixel);
10676 scrollVertical(pixel, true);
10679 * Sets the top margin.
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>
10689 public void setTopMargin (int topMargin) {
10691 setMargins(getLeftMargin(), topMargin, rightMargin, bottomMargin);
10694 * Sets the top SWT logical point offset. Do nothing if there is no text set.
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.
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>
10711 public void setTopPixel(int pixel) {
10713 if (getCharCount() == 0) {
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;
10725 pixel -= verticalOffset;
10727 pixel = getAvailableHeightBellow(pixel);
10730 scrollVertical(pixel, true);
10733 * Sets whether the widget wraps lines.
10735 * This overrides the creation style bit SWT.WRAP.
10738 * @param wrap true=widget wraps lines, false=widget does not wrap lines
10741 public void setWordWrap(boolean wrap) {
10743 if ((getStyle() & SWT.SINGLE) != 0) return;
10744 if (wordWrap == wrap) return;
10745 if (wordWrap && blockSelection) setBlockSelection(false);
10747 resetCache(0, content.getLineCount());
10748 horizontalScrollOffset = 0;
10749 ScrollBar horizontalBar = getHorizontalBar();
10750 if (horizontalBar != null) {
10751 horizontalBar.setVisible(!wordWrap);
10753 setScrollBars(true);
10754 setCaretLocation();
10758 * Sets the wrap line indentation of the widget.
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
10765 * @param wrapIndent the new wrap indent
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>
10772 * @see #setLineWrapIndent(int, int, int)
10776 public void setWrapIndent(int wrapIndent) {
10778 if (this.wrapIndent == wrapIndent || wrapIndent < 0) return;
10779 this.wrapIndent = wrapIndent;
10780 resetCache(0, content.getLineCount());
10781 setCaretLocation();
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);
10792 scrolled = scrollVertical(rect.y + rect.height - (clientAreaHeight - bottomMargin), true);
10795 int width = clientAreaWidth - rightMargin - leftMargin;
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);
10811 * Sets the caret location and scrolls the caret offset into view.
10814 Rectangle bounds = getBoundsAtOffset(caretOffset);
10815 if (!showLocation(bounds, true)) {
10816 setCaretLocation();
10820 * Scrolls the selection into view.
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).
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>
10832 public void showSelection() {
10834 // is selection from right-to-left?
10835 boolean rightToLeft = caretOffset == selection.x;
10836 int startOffset, endOffset;
10838 startOffset = selection.y;
10839 endOffset = selection.x;
10841 startOffset = selection.x;
10842 endOffset = selection.y;
10845 Rectangle startBounds = getBoundsAtOffset(startOffset);
10846 Rectangle endBounds = getBoundsAtOffset(endOffset);
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);
10858 // the character at endOffset is not part of the selection
10859 endBounds.width = endOffset == caretOffset ? getCaretWidth() : 0;
10860 showLocation(endBounds, false);
10862 // just show the end of the selection since the selection start
10863 // will not be visible
10864 showLocation(endBounds, true);
10867 void updateCaretVisibility() {
10868 Caret caret = getCaret();
10869 if (caret != null) {
10870 if (blockSelection && blockXLocation != -1) {
10871 caret.setVisible(false);
10873 Point location = caret.getLocation();
10874 Point size = caret.getSize();
10876 topMargin <= location.y + size.y && location.y <= clientAreaHeight - bottomMargin &&
10877 leftMargin <= location.x + size.x && location.x <= clientAreaWidth - rightMargin;
10878 caret.setVisible(visible);
10883 * Updates the selection and caret position depending on the text change.
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.
10892 * @param startOffset offset of the text change
10893 * @param replacedLength length of text being replaced
10894 * @param newLength length of new text
10896 void updateSelection(int startOffset, int replacedLength, int newLength) {
10897 if (selection.y <= startOffset) {
10898 // selection ends before text change
10899 if (isWordWrap()) setCaretLocation();
10902 if (selection.x < startOffset) {
10903 // clear selection fragment before text change
10904 internalRedrawRange(selection.x, startOffset - selection.x);
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);
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);
10918 // move selection to keep same text selected
10919 setSelection(selection.x + newLength - replacedLength, selection.y - selection.x, true, false);
10921 setCaretLocation();