KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > swt > custom > StyledText


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.swt.custom;
12
13
14 import java.util.*;
15
16 import org.eclipse.swt.*;
17 import org.eclipse.swt.accessibility.*;
18 import org.eclipse.swt.dnd.*;
19 import org.eclipse.swt.events.*;
20 import org.eclipse.swt.graphics.*;
21 import org.eclipse.swt.internal.*;
22 import org.eclipse.swt.printing.*;
23 import org.eclipse.swt.widgets.*;
24
25 /**
26  * A StyledText is an editable user interface object that displays lines
27  * of text. The following style attributes can be defined for the text:
28  * <ul>
29  * <li>foreground color
30  * <li>background color
31  * <li>font style (bold, italic, bold-italic, regular)
32  * <li>underline
33  * <li>strikeout
34  * </ul>
35  * <p>
36  * In addition to text style attributes, the background color of a line may
37  * be specified.
38  * </p><p>
39  * There are two ways to use this widget when specifying text style information.
40  * You may use the API that is defined for StyledText or you may define your own
41  * LineStyleListener. If you define your own listener, you will be responsible
42  * for maintaining the text style information for the widget. IMPORTANT: You may
43  * not define your own listener and use the StyledText API. The following
44  * StyledText API is not supported if you have defined a LineStyleListener:
45  * <ul>
46  * <li>getStyleRangeAtOffset(int)
47  * <li>getStyleRanges()
48  * <li>replaceStyleRanges(int,int,StyleRange[])
49  * <li>setStyleRange(StyleRange)
50  * <li>setStyleRanges(StyleRange[])
51  * </ul>
52  * </p><p>
53  * There are two ways to use this widget when specifying line background colors.
54  * You may use the API that is defined for StyledText or you may define your own
55  * LineBackgroundListener. If you define your own listener, you will be responsible
56  * for maintaining the line background color information for the widget.
57  * IMPORTANT: You may not define your own listener and use the StyledText API.
58  * The following StyledText API is not supported if you have defined a
59  * LineBackgroundListener:
60  * <ul>
61  * <li>getLineBackground(int)
62  * <li>setLineBackground(int,int,Color)
63  * </ul>
64  * </p><p>
65  * The content implementation for this widget may also be user-defined. To do so,
66  * you must implement the StyledTextContent interface and use the StyledText API
67  * setContent(StyledTextContent) to initialize the widget.
68  * </p><p>
69  * <dl>
70  * <dt><b>Styles:</b><dd>FULL_SELECTION, MULTI, READ_ONLY, SINGLE, WRAP
71  * <dt><b>Events:</b><dd>ExtendedModify, LineGetBackground, LineGetSegments, LineGetStyle, Modify, Selection, Verify, VerifyKey
72  * </dl>
73  * </p><p>
74  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
75  * </p>
76  */

77 public class StyledText extends Canvas {
78     static final char TAB = '\t';
79     static final String JavaDoc PlatformLineDelimiter = System.getProperty("line.separator");
80     static final int BIDI_CARET_WIDTH = 3;
81     static final int DEFAULT_WIDTH = 64;
82     static final int DEFAULT_HEIGHT = 64;
83     static final int V_SCROLL_RATE = 50;
84     static final int H_SCROLL_RATE = 10;
85     
86     static final int ExtendedModify = 3000;
87     static final int LineGetBackground = 3001;
88     static final int LineGetStyle = 3002;
89     static final int TextChanging = 3003;
90     static final int TextSet = 3004;
91     static final int VerifyKey = 3005;
92     static final int TextChanged = 3006;
93     static final int LineGetSegments = 3007;
94     static final int PaintObject = 3008;
95     static final int WordNext = 3009;
96     static final int WordPrevious = 3010;
97     
98     static final int PREVIOUS_OFFSET_TRAILING = 0;
99     static final int OFFSET_LEADING = 1;
100     
101     Color selectionBackground; // selection background color
102
Color selectionForeground; // selection foreground color
103
StyledTextContent content; // native content (default or user specified)
104
StyledTextRenderer renderer;
105     Listener listener;
106     TextChangeListener textChangeListener; // listener for TextChanging, TextChanged and TextSet events from StyledTextContent
107
int verticalScrollOffset = 0; // pixel based
108
int horizontalScrollOffset = 0; // pixel based
109
int topIndex = 0; // top visible line
110
int topIndexY;
111     int clientAreaHeight = 0; // the client area height. Needed to calculate content width for new visible lines during Resize callback
112
int clientAreaWidth = 0; // the client area width. Needed during Resize callback to determine if line wrap needs to be recalculated
113
int tabLength = 4; // number of characters in a tab
114
int leftMargin;
115     int topMargin;
116     int rightMargin;
117     int bottomMargin;
118     int columnX; // keep track of the horizontal caret position when changing lines/pages. Fixes bug 5935
119
int caretOffset = 0;
120     int caretAlignment;
121     Point selection = new Point(0, 0); // x and y are start and end caret offsets of selection
122
Point clipboardSelection; // x and y are start and end caret offsets of previous selection
123
int selectionAnchor; // position of selection anchor. 0 based offset from beginning of text
124
Point doubleClickSelection; // selection after last mouse double click
125
boolean editable = true;
126     boolean wordWrap = false;
127     boolean doubleClickEnabled = true; // see getDoubleClickEnabled
128
boolean overwrite = false; // insert/overwrite edit mode
129
int textLimit = -1; // limits the number of characters the user can type in the widget. Unlimited by default.
130
Hashtable keyActionMap = new Hashtable();
131     Color background = null; // workaround for bug 4791
132
Color foreground = null; //
133
Clipboard clipboard;
134     int clickCount;
135     int autoScrollDirection = SWT.NULL; // the direction of autoscrolling (up, down, right, left)
136
int autoScrollDistance = 0;
137     int lastTextChangeStart; // cache data of the
138
int lastTextChangeNewLineCount; // last text changing
139
int lastTextChangeNewCharCount; // event for use in the
140
int lastTextChangeReplaceLineCount; // text changed handler
141
int lastTextChangeReplaceCharCount;
142     int lastLineBottom; // the bottom pixel of the last line been replaced
143
boolean isMirrored;
144     boolean bidiColoring = false; // apply the BIDI algorithm on text segments of the same color
145
Image leftCaretBitmap = null;
146     Image rightCaretBitmap = null;
147     int caretDirection = SWT.NULL;
148     Caret defaultCaret = null;
149     boolean updateCaretDirection = true;
150     boolean fixedLineHeight;
151     boolean dragDetect = true;
152     
153     int alignment;
154     boolean justify;
155     int indent;
156     int lineSpacing;
157
158     final static boolean IS_CARBON, IS_GTK, IS_MOTIF;
159     static {
160         String JavaDoc platform = SWT.getPlatform();
161         IS_CARBON = "carbon".equals(platform);
162         IS_GTK = "gtk".equals(platform);
163         IS_MOTIF = "motif".equals(platform);
164     }
165
166     /**
167      * The Printing class implements printing of a range of text.
168      * An instance of <code>Printing</code> is returned in the
169      * StyledText#print(Printer) API. The run() method may be
170      * invoked from any thread.
171      */

172     static class Printing implements Runnable JavaDoc {
173         final static int LEFT = 0; // left aligned header/footer segment
174
final static int CENTER = 1; // centered header/footer segment
175
final static int RIGHT = 2; // right aligned header/footer segment
176

177         Printer printer;
178         StyledTextRenderer printerRenderer;
179         StyledTextPrintOptions printOptions;
180         Rectangle clientArea;
181         FontData fontData;
182         Font printerFont;
183         Hashtable resources;
184         int tabLength;
185         GC gc; // printer GC
186
int pageWidth; // width of a printer page in pixels
187
int startPage; // first page to print
188
int endPage; // last page to print
189
int startLine; // first (wrapped) line to print
190
int endLine; // last (wrapped) line to print
191
boolean singleLine; // widget single line mode
192
Point selection = null; // selected text
193
boolean mirrored; // indicates the printing gc should be mirrored
194
int lineSpacing;
195         int printMargin;
196
197     /**
198      * Creates an instance of <code>Printing</code>.
199      * Copies the widget content and rendering data that needs
200      * to be requested from listeners.
201      * </p>
202      * @param parent StyledText widget to print.
203      * @param printer printer device to print on.
204      * @param printOptions print options
205      */

206     Printing(StyledText styledText, Printer printer, StyledTextPrintOptions printOptions) {
207         this.printer = printer;
208         this.printOptions = printOptions;
209         this.mirrored = (styledText.getStyle() & SWT.MIRRORED) != 0;
210         singleLine = styledText.isSingleLine();
211         startPage = 1;
212         endPage = Integer.MAX_VALUE;
213         PrinterData data = printer.getPrinterData();
214         if (data.scope == PrinterData.PAGE_RANGE) {
215             startPage = data.startPage;
216             endPage = data.endPage;
217             if (endPage < startPage) {
218                 int temp = endPage;
219                 endPage = startPage;
220                 startPage = temp;
221             }
222         } else if (data.scope == PrinterData.SELECTION) {
223             selection = styledText.getSelectionRange();
224         }
225         printerRenderer = new StyledTextRenderer(printer, null);
226         printerRenderer.setContent(copyContent(styledText.getContent()));
227         cacheLineData(styledText);
228     }
229     /**
230      * Caches all line data that needs to be requested from a listener.
231      * </p>
232      * @param printerContent <code>StyledTextContent</code> to request
233      * line data for.
234      */

235     void cacheLineData(StyledText styledText) {
236         StyledTextRenderer renderer = styledText.renderer;
237         renderer.copyInto(printerRenderer);
238         fontData = styledText.getFont().getFontData()[0];
239         tabLength = styledText.tabLength;
240         int lineCount = printerRenderer.lineCount;
241         if (styledText.isListening(LineGetBackground) || (styledText.isBidi() && styledText.isListening(LineGetSegments)) || styledText.isListening(LineGetStyle)) {
242             StyledTextContent content = printerRenderer.content;
243             for (int i = 0; i < lineCount; i++) {
244                 String JavaDoc line = content.getLine(i);
245                 int lineOffset = content.getOffsetAtLine(i);
246                 StyledTextEvent event = styledText.getLineBackgroundData(lineOffset, line);
247                 if (event != null && event.lineBackground != null) {
248                     printerRenderer.setLineBackground(i, 1, event.lineBackground);
249                 }
250                 if (styledText.isBidi()) {
251                     int[] segments = styledText.getBidiSegments(lineOffset, line);
252                     printerRenderer.setLineSegments(i, 1, segments);
253                 }
254                 event = styledText.getLineStyleData(lineOffset, line);
255                 if (event != null) {
256                     printerRenderer.setLineIndent(i, 1, event.indent);
257                     printerRenderer.setLineAlignment(i, 1, event.alignment);
258                     printerRenderer.setLineJustify(i, 1, event.justify);
259                     printerRenderer.setLineBullet(i, 1, event.bullet);
260                     StyleRange[] styles = event.styles;
261                     if (styles != null && styles.length > 0) {
262                         printerRenderer.setStyleRanges(event.ranges, styles);
263                     }
264                 }
265             }
266         }
267         Point screenDPI = styledText.getDisplay().getDPI();
268         Point printerDPI = printer.getDPI();
269         resources = new Hashtable ();
270         for (int i = 0; i < lineCount; i++) {
271             Color color = printerRenderer.getLineBackground(i, null);
272             if (color != null) {
273                 if (printOptions.printLineBackground) {
274                     Color printerColor = (Color)resources.get(color);
275                     if (printerColor == null) {
276                         printerColor = new Color (printer, color.getRGB());
277                         resources.put(color, printerColor);
278                     }
279                     printerRenderer.setLineBackground(i, 1, printerColor);
280                 } else {
281                     printerRenderer.setLineBackground(i, 1, null);
282                 }
283             }
284             int indent = printerRenderer.getLineIndent(i, 0);
285             if (indent != 0) {
286                 printerRenderer.setLineIndent(i, 1, indent * printerDPI.x / screenDPI.x);
287             }
288         }
289         StyleRange[] styles = printerRenderer.styles;
290         for (int i = 0; i < printerRenderer.styleCount; i++) {
291             StyleRange style = styles[i];
292             Font font = style.font;
293             if (style.font != null) {
294                 Font printerFont = (Font)resources.get(font);
295                 if (printerFont == null) {
296                     printerFont = new Font (printer, font.getFontData());
297                     resources.put(font, printerFont);
298                 }
299                 style.font = printerFont;
300             }
301             Color color = style.foreground;
302             if (color != null) {
303                 Color printerColor = (Color)resources.get(color);
304                 if (printOptions.printTextForeground) {
305                     if (printerColor == null) {
306                         printerColor = new Color (printer, color.getRGB());
307                         resources.put(color, printerColor);
308                     }
309                     style.foreground = printerColor;
310                 } else {
311                     style.foreground = null;
312                 }
313             }
314             color = style.background;
315             if (color != null) {
316                 Color printerColor = (Color)resources.get(color);
317                 if (printOptions.printTextBackground) {
318                     if (printerColor == null) {
319                         printerColor = new Color (printer, color.getRGB());
320                         resources.put(color, printerColor);
321                     }
322                     style.background = printerColor;
323                 } else {
324                     style.background = null;
325                 }
326             }
327             if (!printOptions.printTextFontStyle) {
328                 style.fontStyle = SWT.NORMAL;
329             }
330             style.rise = style.rise * printerDPI.y / screenDPI.y;
331             GlyphMetrics metrics = style.metrics;
332             if (metrics != null) {
333                 metrics.ascent = metrics.ascent * printerDPI.y / screenDPI.y;
334                 metrics.descent = metrics.descent * printerDPI.y / screenDPI.y;
335                 metrics.width = metrics.width * printerDPI.x / screenDPI.x;
336             }
337         }
338         lineSpacing = styledText.lineSpacing * printerDPI.y / screenDPI.y;
339         if (printOptions.printLineNumbers) {
340             printMargin = 3 * printerDPI.x / screenDPI.x;
341         }
342     }
343     /**
344      * Copies the text of the specified <code>StyledTextContent</code>.
345      * </p>
346      * @param original the <code>StyledTextContent</code> to copy.
347      */

348     StyledTextContent copyContent(StyledTextContent original) {
349         StyledTextContent printerContent = new DefaultContent();
350         int insertOffset = 0;
351         for (int i = 0; i < original.getLineCount(); i++) {
352             int insertEndOffset;
353             if (i < original.getLineCount() - 1) {
354                 insertEndOffset = original.getOffsetAtLine(i + 1);
355             } else {
356                 insertEndOffset = original.getCharCount();
357             }
358             printerContent.replaceTextRange(insertOffset, 0, original.getTextRange(insertOffset, insertEndOffset - insertOffset));
359             insertOffset = insertEndOffset;
360         }
361         return printerContent;
362     }
363     /**
364      * Disposes of the resources and the <code>PrintRenderer</code>.
365      */

366     void dispose() {
367         if (gc != null) {
368             gc.dispose();
369             gc = null;
370         }
371         if (resources != null) {
372             Enumeration enumeration = resources.elements();
373             while (enumeration.hasMoreElements()) {
374                 Resource resource = (Resource) enumeration.nextElement();
375                 resource.dispose();
376             }
377             resources = null;
378         }
379         if (printerFont != null) {
380             printerFont.dispose();
381             printerFont = null;
382         }
383         if (printerRenderer != null) {
384             printerRenderer.dispose();
385             printerRenderer = null;
386         }
387     }
388     void init() {
389         Rectangle trim = printer.computeTrim(0, 0, 0, 0);
390         Point dpi = printer.getDPI();
391         
392         printerFont = new Font(printer, fontData.getName(), fontData.getHeight(), SWT.NORMAL);
393         clientArea = printer.getClientArea();
394         pageWidth = clientArea.width;
395         // one inch margin around text
396
clientArea.x = dpi.x + trim.x;
397         clientArea.y = dpi.y + trim.y;
398         clientArea.width -= (clientArea.x + trim.width);
399         clientArea.height -= (clientArea.y + trim.height);
400
401         int style = mirrored ? SWT.RIGHT_TO_LEFT : SWT.LEFT_TO_RIGHT;
402         gc = new GC(printer, style);
403         gc.setFont(printerFont);
404         printerRenderer.setFont(printerFont, tabLength);
405         int lineHeight = printerRenderer.getLineHeight();
406         if (printOptions.header != null) {
407             clientArea.y += lineHeight * 2;
408             clientArea.height -= lineHeight * 2;
409         }
410         if (printOptions.footer != null) {
411             clientArea.height -= lineHeight * 2;
412         }
413         
414         // TODO not wrapped
415
StyledTextContent content = printerRenderer.content;
416         startLine = 0;
417         endLine = singleLine ? 0 : content.getLineCount() - 1;
418         PrinterData data = printer.getPrinterData();
419         if (data.scope == PrinterData.PAGE_RANGE) {
420             int pageSize = clientArea.height / lineHeight;//WRONG
421
startLine = (startPage - 1) * pageSize;
422         } else if (data.scope == PrinterData.SELECTION) {
423             startLine = content.getLineAtOffset(selection.x);
424             if (selection.y > 0) {
425                 endLine = content.getLineAtOffset(selection.x + selection.y - 1);
426             } else {
427                 endLine = startLine - 1;
428             }
429         }
430     }
431     /**
432      * Prints the lines in the specified page range.
433      */

434     void print() {
435         Color background = gc.getBackground();
436         Color foreground = gc.getForeground();
437         int paintY = clientArea.y;
438         int paintX = clientArea.x;
439         int width = clientArea.width;
440         int page = startPage;
441         int pageBottom = clientArea.y + clientArea.height;
442         int orientation = gc.getStyle() & (SWT.RIGHT_TO_LEFT | SWT.LEFT_TO_RIGHT);
443         TextLayout printLayout = null;
444         if (printOptions.printLineNumbers || printOptions.header != null || printOptions.footer != null) {
445             printLayout = new TextLayout(printer);
446             printLayout.setFont(printerFont);
447         }
448         if (printOptions.printLineNumbers) {
449             int count = endLine - startLine + 1;
450             StringBuffer JavaDoc buffer = new StringBuffer JavaDoc("0");
451             while ((count /= 10) > 0) buffer.append("0");
452             printLayout.setText(buffer.toString());
453             int numberingWidth = printLayout.getBounds().width + printMargin;
454             if (numberingWidth > width) numberingWidth = width;
455             paintX += numberingWidth;
456             width -= numberingWidth;
457         }
458         for (int i = startLine; i <= endLine && page <= endPage; i++) {
459             if (paintY == clientArea.y) {
460                 printer.startPage();
461                 printDecoration(page, true, printLayout);
462             }
463             TextLayout layout = printerRenderer.getTextLayout(i, orientation, width, lineSpacing);
464             Color lineBackground = printerRenderer.getLineBackground(i, background);
465             int paragraphBottom = paintY + layout.getBounds().height;
466             if (paragraphBottom <= pageBottom) {
467                 //normal case, the whole paragraph fits in the current page
468
printLine(paintX, paintY, gc, foreground, lineBackground, layout, printLayout, i);
469                 paintY = paragraphBottom;
470             } else {
471                 int lineCount = layout.getLineCount();
472                 while (paragraphBottom > pageBottom && lineCount > 0) {
473                     lineCount--;
474                     paragraphBottom -= layout.getLineBounds(lineCount).height + layout.getSpacing();
475                 }
476                 if (lineCount == 0) {
477                     //the whole paragraph goes to the next page
478
printDecoration(page, false, printLayout);
479                     printer.endPage();
480                     page++;
481                     if (page <= endPage) {
482                         printer.startPage();
483                         printDecoration(page, true, printLayout);
484                         paintY = clientArea.y;
485                         printLine(paintX, paintY, gc, foreground, lineBackground, layout, printLayout, i);
486                         paintY += layout.getBounds().height;
487                     }
488                 } else {
489                     //draw paragraph top in the current page and paragraph bottom in the next
490
int height = paragraphBottom - paintY;
491                     gc.setClipping(paintX, paintY, width, height);
492                     printLine(paintX, paintY, gc, foreground, lineBackground, layout, printLayout, i);
493                     printDecoration(page, false, printLayout);
494                     printer.endPage();
495                     page++;
496                     if (page <= endPage) {
497                         printer.startPage();
498                         printDecoration(page, true, printLayout);
499                         paintY = clientArea.y - height;
500                         int layoutHeight = layout.getBounds().height;
501                         gc.setClipping(paintX, clientArea.y, width, layoutHeight - height);
502                         printLine(paintX, paintY, gc, foreground, lineBackground, layout, printLayout, i);
503                         paintY += layoutHeight;
504                     }
505                     gc.setClipping((Rectangle)null);
506                 }
507             }
508             printerRenderer.disposeTextLayout(layout);
509         }
510         if (paintY > clientArea.y) {
511             // close partial page
512
printDecoration(page, false, printLayout);
513             printer.endPage();
514         }
515         if (printLayout != null) printLayout.dispose();
516     }
517     /**
518      * Print header or footer decorations.
519      *
520      * @param page page number to print, if specified in the StyledTextPrintOptions header or footer.
521      * @param header true = print the header, false = print the footer
522      */

523     void printDecoration(int page, boolean header, TextLayout layout) {
524         String JavaDoc text = header ? printOptions.header : printOptions.footer;
525         if (text == null) return;
526         int lastSegmentIndex = 0;
527         for (int i = 0; i < 3; i++) {
528             int segmentIndex = text.indexOf(StyledTextPrintOptions.SEPARATOR, lastSegmentIndex);
529             String JavaDoc segment;
530             if (segmentIndex == -1) {
531                 segment = text.substring(lastSegmentIndex);
532                 printDecorationSegment(segment, i, page, header, layout);
533                 break;
534             } else {
535                 segment = text.substring(lastSegmentIndex, segmentIndex);
536                 printDecorationSegment(segment, i, page, header, layout);
537                 lastSegmentIndex = segmentIndex + StyledTextPrintOptions.SEPARATOR.length();
538             }
539         }
540     }
541     /**
542      * Print one segment of a header or footer decoration.
543      * Headers and footers have three different segments.
544      * One each for left aligned, centered, and right aligned text.
545      *
546      * @param segment decoration segment to print
547      * @param alignment alignment of the segment. 0=left, 1=center, 2=right
548      * @param page page number to print, if specified in the decoration segment.
549      * @param header true = print the header, false = print the footer
550      */

551     void printDecorationSegment(String JavaDoc segment, int alignment, int page, boolean header, TextLayout layout) {
552         int pageIndex = segment.indexOf(StyledTextPrintOptions.PAGE_TAG);
553         if (pageIndex != -1) {
554             int pageTagLength = StyledTextPrintOptions.PAGE_TAG.length();
555             StringBuffer JavaDoc buffer = new StringBuffer JavaDoc(segment.substring (0, pageIndex));
556             buffer.append (page);
557             buffer.append (segment.substring(pageIndex + pageTagLength));
558             segment = buffer.toString();
559         }
560         if (segment.length() > 0) {
561             layout.setText(segment);
562             int segmentWidth = layout.getBounds().width;
563             int segmentHeight = printerRenderer.getLineHeight();
564             int drawX = 0, drawY;
565             if (alignment == LEFT) {
566                 drawX = clientArea.x;
567             } else if (alignment == CENTER) {
568                 drawX = (pageWidth - segmentWidth) / 2;
569             } else if (alignment == RIGHT) {
570                 drawX = clientArea.x + clientArea.width - segmentWidth;
571             }
572             if (header) {
573                 drawY = clientArea.y - segmentHeight * 2;
574             } else {
575                 drawY = clientArea.y + clientArea.height + segmentHeight;
576             }
577             layout.draw(gc, drawX, drawY);
578         }
579     }
580     void printLine(int x, int y, GC gc, Color foreground, Color background, TextLayout layout, TextLayout printLayout, int index) {
581         if (background != null) {
582             Rectangle rect = layout.getBounds();
583             gc.setBackground(background);
584             gc.fillRectangle(x, y, rect.width, rect.height);
585             
586 // int lineCount = layout.getLineCount();
587
// for (int i = 0; i < lineCount; i++) {
588
// Rectangle rect = layout.getLineBounds(i);
589
// rect.x += paintX;
590
// rect.y += paintY + layout.getSpacing();
591
// rect.width = width;//layout bounds
592
// gc.fillRectangle(rect);
593
// }
594
}
595         if (printOptions.printLineNumbers) {
596             FontMetrics metrics = layout.getLineMetrics(0);
597             printLayout.setAscent(metrics.getAscent() + metrics.getDescent());
598             printLayout.setDescent(metrics.getDescent());
599             printLayout.setText(String.valueOf(index));
600             int paintX = x - printMargin - printLayout.getBounds().width;
601             printLayout.draw(gc, paintX, y);
602             printLayout.setAscent(-1);
603             printLayout.setDescent(-1);
604         }
605         gc.setForeground(foreground);
606         layout.draw(gc, x, y);
607     }
608     /**
609      * Starts a print job and prints the pages specified in the constructor.
610      */

611     public void run() {
612         String JavaDoc jobName = printOptions.jobName;
613         if (jobName == null) {
614             jobName = "Printing";
615         }
616         if (printer.startJob(jobName)) {
617             init();
618             print();
619             dispose();
620             printer.endJob();
621         }
622     }
623     }
624     /**
625      * The <code>RTFWriter</code> class is used to write widget content as
626      * rich text. The implementation complies with the RTF specification
627      * version 1.5.
628      * <p>
629      * toString() is guaranteed to return a valid RTF string only after
630      * close() has been called.
631      * </p><p>
632      * Whole and partial lines and line breaks can be written. Lines will be
633      * formatted using the styles queried from the LineStyleListener, if
634      * set, or those set directly in the widget. All styles are applied to
635      * the RTF stream like they are rendered by the widget. In addition, the
636      * widget font name and size is used for the whole text.
637      * </p>
638      */

639     class RTFWriter extends TextWriter {
640         static final int DEFAULT_FOREGROUND = 0;
641         static final int DEFAULT_BACKGROUND = 1;
642         Vector colorTable, fontTable;
643         boolean WriteUnicode;
644         
645     /**
646      * Creates a RTF writer that writes content starting at offset "start"
647      * in the document. <code>start</code> and <code>length</code>can be set to specify partial
648      * lines.
649      *
650      * @param start start offset of content to write, 0 based from
651      * beginning of document
652      * @param length length of content to write
653      */

654     public RTFWriter(int start, int length) {
655         super(start, length);
656         colorTable = new Vector();
657         fontTable = new Vector();
658         colorTable.addElement(getForeground());
659         colorTable.addElement(getBackground());
660         fontTable.addElement(getFont());
661         setUnicode();
662     }
663     /**
664      * Closes the RTF writer. Once closed no more content can be written.
665      * <b>NOTE:</b> <code>toString()</code> does not return a valid RTF string until
666      * <code>close()</code> has been called.
667      */

668     public void close() {
669         if (!isClosed()) {
670             writeHeader();
671             write("\n}}\0");
672             super.close();
673         }
674     }
675     /**
676      * Returns the index of the specified color in the RTF color table.
677      *
678      * @param color the color
679      * @param defaultIndex return value if color is null
680      * @return the index of the specified color in the RTF color table
681      * or "defaultIndex" if "color" is null.
682      */

683     int getColorIndex(Color color, int defaultIndex) {
684         if (color == null) return defaultIndex;
685         int index = colorTable.indexOf(color);
686         if (index == -1) {
687             index = colorTable.size();
688             colorTable.addElement(color);
689         }
690         return index;
691     }
692     /**
693      * Returns the index of the specified color in the RTF color table.
694      *
695      * @param color the color
696      * @param defaultIndex return value if color is null
697      * @return the index of the specified color in the RTF color table
698      * or "defaultIndex" if "color" is null.
699      */

700     int getFontIndex(Font font) {
701         int index = fontTable.indexOf(font);
702         if (index == -1) {
703             index = fontTable.size();
704             fontTable.addElement(font);
705         }
706         return index;
707     }
708     /**
709      * Determines if Unicode RTF should be written.
710      * Don't write Unicode RTF on Windows 95/98/ME or NT.
711      */

712     void setUnicode() {
713         final String JavaDoc Win95 = "windows 95";
714         final String JavaDoc Win98 = "windows 98";
715         final String JavaDoc WinME = "windows me";
716         final String JavaDoc WinNT = "windows nt";
717         String JavaDoc osName = System.getProperty("os.name").toLowerCase();
718         String JavaDoc osVersion = System.getProperty("os.version");
719         int majorVersion = 0;
720         
721         if (osName.startsWith(WinNT) && osVersion != null) {
722             int majorIndex = osVersion.indexOf('.');
723             if (majorIndex != -1) {
724                 osVersion = osVersion.substring(0, majorIndex);
725                 try {
726                     majorVersion = Integer.parseInt(osVersion);
727                 } catch (NumberFormatException JavaDoc exception) {
728                     // ignore exception. version number remains unknown.
729
// will write without Unicode
730
}
731             }
732         }
733         WriteUnicode = !osName.startsWith(Win95) &&
734                         !osName.startsWith(Win98) &&
735                         !osName.startsWith(WinME) &&
736                         (!osName.startsWith(WinNT) || majorVersion > 4);
737     }
738     /**
739      * Appends the specified segment of "string" to the RTF data.
740      * Copy from <code>start</code> up to, but excluding, <code>end</code>.
741      *
742      * @param string string to copy a segment from. Must not contain
743      * line breaks. Line breaks should be written using writeLineDelimiter()
744      * @param start start offset of segment. 0 based.
745      * @param end end offset of segment
746      */

747     void write(String JavaDoc string, int start, int end) {
748         for (int index = start; index < end; index++) {
749             char ch = string.charAt(index);
750             if (ch > 0xFF && WriteUnicode) {
751                 // write the sub string from the last escaped character
752
// to the current one. Fixes bug 21698.
753
if (index > start) {
754                     write(string.substring(start, index));
755                 }
756                 write("\\u");
757                 write(Integer.toString((short) ch));
758                 write(' '); // control word delimiter
759
start = index + 1;
760             } else if (ch == '}' || ch == '{' || ch == '\\') {
761                 // write the sub string from the last escaped character
762
// to the current one. Fixes bug 21698.
763
if (index > start) {
764                     write(string.substring(start, index));
765                 }
766                 write('\\');
767                 write(ch);
768                 start = index + 1;
769             }
770         }
771         // write from the last escaped character to the end.
772
// Fixes bug 21698.
773
if (start < end) {
774             write(string.substring(start, end));
775         }
776     }
777     /**
778      * Writes the RTF header including font table and color table.
779      */

780     void writeHeader() {
781         StringBuffer JavaDoc header = new StringBuffer JavaDoc();
782         FontData fontData = getFont().getFontData()[0];
783         header.append("{\\rtf1\\ansi");
784         // specify code page, necessary for copy to work in bidi
785
// systems that don't support Unicode RTF.
786
String JavaDoc cpg = System.getProperty("file.encoding").toLowerCase();
787         if (cpg.startsWith("cp") || cpg.startsWith("ms")) {
788             cpg = cpg.substring(2, cpg.length());
789             header.append("\\ansicpg");
790             header.append(cpg);
791         }
792         header.append("\\uc0\\deff0{\\fonttbl{\\f0\\fnil ");
793         header.append(fontData.getName());
794         header.append(";");
795         for (int i = 1; i < fontTable.size(); i++) {
796             header.append("\\f");
797             header.append(i);
798             header.append(" ");
799             FontData fd = ((Font)fontTable.elementAt(i)).getFontData()[0];
800             header.append(fd.getName());
801             header.append(";");
802         }
803         header.append("}}\n{\\colortbl");
804         for (int i = 0; i < colorTable.size(); i++) {
805             Color color = (Color) colorTable.elementAt(i);
806             header.append("\\red");
807             header.append(color.getRed());
808             header.append("\\green");
809             header.append(color.getGreen());
810             header.append("\\blue");
811             header.append(color.getBlue());
812             header.append(";");
813         }
814         // some RTF readers ignore the deff0 font tag. Explicitly
815
// set the font for the whole document to work around this.
816
header.append("}\n{\\f0\\fs");
817         // font size is specified in half points
818
header.append(fontData.getHeight() * 2);
819         header.append(" ");
820         write(header.toString(), 0);
821     }
822     /**
823      * Appends the specified line text to the RTF data. Lines will be formatted
824      * using the styles queried from the LineStyleListener, if set, or those set
825      * directly in the widget.
826      *
827      * @param line line text to write as RTF. Must not contain line breaks
828      * Line breaks should be written using writeLineDelimiter()
829      * @param lineOffset offset of the line. 0 based from the start of the
830      * widget document. Any text occurring before the start offset or after the
831      * end offset specified during object creation is ignored.
832      * @exception SWTException <ul>
833      * <li>ERROR_IO when the writer is closed.</li>
834      * </ul>
835      */

836     public void writeLine(String JavaDoc line, int lineOffset) {
837         if (isClosed()) {
838             SWT.error(SWT.ERROR_IO);
839         }
840         int lineIndex = content.getLineAtOffset(lineOffset);
841         int lineAlignment, lineIndent;
842         boolean lineJustify;
843         int[] ranges;
844         StyleRange[] styles;
845         StyledTextEvent event = getLineStyleData(lineOffset, line);
846         if (event != null) {
847             lineAlignment = event.alignment;
848             lineIndent = event.indent;
849             lineJustify = event.justify;
850             ranges = event.ranges;
851             styles = event.styles;
852         } else {
853             lineAlignment = renderer.getLineAlignment(lineIndex, alignment);
854             lineIndent = renderer.getLineIndent(lineIndex, indent);
855             lineJustify = renderer.getLineJustify(lineIndex, justify);
856             ranges = renderer.getRanges(lineOffset, line.length());
857             styles = renderer.getStyleRanges(lineOffset, line.length(), false);
858         }
859         if (styles == null) styles = new StyleRange[0];
860         Color lineBackground = renderer.getLineBackground(lineIndex, null);
861         event = getLineBackgroundData(lineOffset, line);
862         if (event != null && event.lineBackground != null) lineBackground = event.lineBackground;
863         writeStyledLine(line, lineOffset, ranges, styles, lineBackground, lineIndent, lineAlignment, lineJustify);
864     }
865     /**
866      * Appends the specified line delimiter to the RTF data.
867      *
868      * @param lineDelimiter line delimiter to write as RTF.
869      * @exception SWTException <ul>
870      * <li>ERROR_IO when the writer is closed.</li>
871      * </ul>
872      */

873     public void writeLineDelimiter(String JavaDoc lineDelimiter) {
874         if (isClosed()) {
875             SWT.error(SWT.ERROR_IO);
876         }
877         write(lineDelimiter, 0, lineDelimiter.length());
878         write("\\par ");
879     }
880     /**
881      * Appends the specified line text to the RTF data.
882      * <p>
883      * Use the colors and font styles specified in "styles" and "lineBackground".
884      * Formatting is written to reflect the text rendering by the text widget.
885      * Style background colors take precedence over the line background color.
886      * Background colors are written using the \highlight tag (vs. the \cb tag).
887      * </p>
888      *
889      * @param line line text to write as RTF. Must not contain line breaks
890      * Line breaks should be written using writeLineDelimiter()
891      * @param lineOffset offset of the line. 0 based from the start of the
892      * widget document. Any text occurring before the start offset or after the
893      * end offset specified during object creation is ignored.
894      * @param styles styles to use for formatting. Must not be null.
895      * @param lineBackground line background color to use for formatting.
896      * May be null.
897      */

898     void writeStyledLine(String JavaDoc line, int lineOffset, int ranges[], StyleRange[] styles, Color lineBackground, int indent, int alignment, boolean justify) {
899         int lineLength = line.length();
900         int startOffset = getStart();
901         int writeOffset = startOffset - lineOffset;
902         if (writeOffset >= lineLength) return;
903         int lineIndex = Math.max(0, writeOffset);
904             
905         write("\\fi");
906         write(indent);
907         switch (alignment) {
908             case SWT.LEFT: write("\\ql"); break;
909             case SWT.CENTER: write("\\qc"); break;
910             case SWT.RIGHT: write("\\qr"); break;
911         }
912         if (justify) write("\\qj");
913         write(" ");
914         
915         if (lineBackground != null) {
916             write("{\\highlight");
917             write(getColorIndex(lineBackground, DEFAULT_BACKGROUND));
918             write(" ");
919         }
920         int endOffset = startOffset + super.getCharCount();
921         int lineEndOffset = Math.min(lineLength, endOffset - lineOffset);
922         for (int i = 0; i < styles.length; i++) {
923             StyleRange style = styles[i];
924             int start, end;
925             if (ranges != null) {
926                 start = ranges[i << 1] - lineOffset;
927                 end = start + ranges[(i << 1) + 1];
928             } else {
929                 start = style.start - lineOffset;
930                 end = start + style.length;
931             }
932             // skip over partial first line
933
if (end < writeOffset) {
934                 continue;
935             }
936             // style starts beyond line end or RTF write end
937
if (start >= lineEndOffset) {
938                 break;
939             }
940             // write any unstyled text
941
if (lineIndex < start) {
942                 // copy to start of style
943
// style starting beyond end of write range or end of line
944
// is guarded against above.
945
write(line, lineIndex, start);
946                 lineIndex = start;
947             }
948             // write styled text
949
write("{\\cf");
950             write(getColorIndex(style.foreground, DEFAULT_FOREGROUND));
951             int colorIndex = getColorIndex(style.background, DEFAULT_BACKGROUND);
952             if (colorIndex != DEFAULT_BACKGROUND) {
953                 write("\\highlight");
954                 write(colorIndex);
955             }
956             Font font = style.font;
957             if (font != null) {
958                 int fontIndex = getFontIndex(font);
959                 write("\\f");
960                 write(fontIndex);
961                 FontData fontData = font.getFontData()[0];
962                 write("\\fs");
963                 write(fontData.getHeight() * 2);
964             } else {
965                 if ((style.fontStyle & SWT.BOLD) != 0) {
966                     write("\\b");
967                 }
968                 if ((style.fontStyle & SWT.ITALIC) != 0) {
969                     write("\\i");
970                 }
971             }
972             if (style.underline) {
973                 write("\\ul");
974             }
975             if (style.strikeout) {
976                 write("\\strike");
977             }
978             write(" ");
979             // copy to end of style or end of write range or end of line
980
int copyEnd = Math.min(end, lineEndOffset);
981             // guard against invalid styles and let style processing continue
982
copyEnd = Math.max(copyEnd, lineIndex);
983             write(line, lineIndex, copyEnd);
984             if (font == null) {
985                 if ((style.fontStyle & SWT.BOLD) != 0) {
986                     write("\\b0");
987                 }
988                 if ((style.fontStyle & SWT.ITALIC) != 0) {
989                     write("\\i0");
990                 }
991             }
992             if (style.underline) {
993                 write("\\ul0");
994             }
995             if (style.strikeout) {
996                 write("\\strike0");
997             }
998             write("}");
999             lineIndex = copyEnd;
1000        }
1001        // write unstyled text at the end of the line
1002
if (lineIndex < lineEndOffset) {
1003            write(line, lineIndex, lineEndOffset);
1004        }
1005        if (lineBackground != null) write("}");
1006    }
1007    }
1008    /**
1009     * The <code>TextWriter</code> class is used to write widget content to
1010     * a string. Whole and partial lines and line breaks can be written. To write
1011     * partial lines, specify the start and length of the desired segment
1012     * during object creation.
1013     * <p>
1014     * </b>NOTE:</b> <code>toString()</code> is guaranteed to return a valid string only after close()
1015     * has been called.
1016     * </p>
1017     */

1018    class TextWriter {
1019        private StringBuffer JavaDoc buffer;
1020        private int startOffset; // offset of first character that will be written
1021
private int endOffset; // offset of last character that will be written.
1022
// 0 based from the beginning of the widget text.
1023
private boolean isClosed = false;
1024    
1025    /**
1026     * Creates a writer that writes content starting at offset "start"
1027     * in the document. <code>start</code> and <code>length</code> can be set to specify partial lines.
1028     *
1029     * @param start start offset of content to write, 0 based from beginning of document
1030     * @param length length of content to write
1031     */

1032    public TextWriter(int start, int length) {
1033        buffer = new StringBuffer JavaDoc(length);
1034        startOffset = start;
1035        endOffset = start + length;
1036    }
1037    /**
1038     * Closes the writer. Once closed no more content can be written.
1039     * <b>NOTE:</b> <code>toString()</code> is not guaranteed to return a valid string unless
1040     * the writer is closed.
1041     */

1042    public void close() {
1043        if (!isClosed) {
1044            isClosed = true;
1045        }
1046    }
1047    /**
1048     * Returns the number of characters to write.
1049     * @return the integer number of characters to write
1050     */

1051    public int getCharCount() {
1052        return endOffset - startOffset;
1053    }
1054    /**
1055     * Returns the offset where writing starts. 0 based from the start of
1056     * the widget text. Used to write partial lines.
1057     * @return the integer offset where writing starts
1058     */

1059    public int getStart() {
1060        return startOffset;
1061    }
1062    /**
1063     * Returns whether the writer is closed.
1064     * @return a boolean specifying whether or not the writer is closed
1065     */

1066    public boolean isClosed() {
1067        return isClosed;
1068    }
1069    /**
1070     * Returns the string. <code>close()</code> must be called before <code>toString()</code>
1071     * is guaranteed to return a valid string.
1072     *
1073     * @return the string
1074     */

1075    public String JavaDoc toString() {
1076        return buffer.toString();
1077    }
1078    /**
1079     * Appends the given string to the data.
1080     */

1081    void write(String JavaDoc string) {
1082        buffer.append(string);
1083    }
1084    /**
1085     * Inserts the given string to the data at the specified offset.
1086     * <p>
1087     * Do nothing if "offset" is < 0 or > getCharCount()
1088     * </p>
1089     *
1090     * @param string text to insert
1091     * @param offset offset in the existing data to insert "string" at.
1092     */

1093    void write(String JavaDoc string, int offset) {
1094        if (offset < 0 || offset > buffer.length()) {
1095            return;
1096        }
1097        buffer.insert(offset, string);
1098    }
1099    /**
1100     * Appends the given int to the data.
1101     */

1102    void write(int i) {
1103        buffer.append(i);
1104    }
1105    /**
1106     * Appends the given character to the data.
1107     */

1108    void write(char i) {
1109        buffer.append(i);
1110    }
1111    /**
1112     * Appends the specified line text to the data.
1113     *
1114     * @param line line text to write. Must not contain line breaks
1115     * Line breaks should be written using writeLineDelimiter()
1116     * @param lineOffset offset of the line. 0 based from the start of the
1117     * widget document. Any text occurring before the start offset or after the
1118     * end offset specified during object creation is ignored.
1119     * @exception SWTException <ul>
1120     * <li>ERROR_IO when the writer is closed.</li>
1121     * </ul>
1122     */

1123    public void writeLine(String JavaDoc line, int lineOffset) {
1124        if (isClosed) {
1125            SWT.error(SWT.ERROR_IO);
1126        }
1127        int writeOffset = startOffset - lineOffset;
1128        int lineLength = line.length();
1129        int lineIndex;
1130        if (writeOffset >= lineLength) {
1131            return; // whole line is outside write range
1132
} else if (writeOffset > 0) {
1133            lineIndex = writeOffset; // line starts before write start
1134
} else {
1135            lineIndex = 0;
1136        }
1137        int copyEnd = Math.min(lineLength, endOffset - lineOffset);
1138        if (lineIndex < copyEnd) {
1139            write(line.substring(lineIndex, copyEnd));
1140        }
1141    }
1142    /**
1143     * Appends the specified line delimiter to the data.
1144     *
1145     * @param lineDelimiter line delimiter to write
1146     * @exception SWTException <ul>
1147     * <li>ERROR_IO when the writer is closed.</li>
1148     * </ul>
1149     */

1150    public void writeLineDelimiter(String JavaDoc lineDelimiter) {
1151        if (isClosed) {
1152            SWT.error(SWT.ERROR_IO);
1153        }
1154        write(lineDelimiter);
1155    }
1156    }
1157
1158/**
1159 * Constructs a new instance of this class given its parent
1160 * and a style value describing its behavior and appearance.
1161 * <p>
1162 * The style value is either one of the style constants defined in
1163 * class <code>SWT</code> which is applicable to instances of this
1164 * class, or must be built by <em>bitwise OR</em>'ing together
1165 * (that is, using the <code>int</code> "|" operator) two or more
1166 * of those <code>SWT</code> style constants. The class description
1167 * lists the style constants that are applicable to the class.
1168 * Style bits are also inherited from superclasses.
1169 * </p>
1170 *
1171 * @param parent a widget which will be the parent of the new instance (cannot be null)
1172 * @param style the style of widget to construct
1173 *
1174 * @exception IllegalArgumentException <ul>
1175 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
1176 * </ul>
1177 * @exception SWTException <ul>
1178 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
1179 * </ul>
1180 *
1181 * @see SWT#FULL_SELECTION
1182 * @see SWT#MULTI
1183 * @see SWT#READ_ONLY
1184 * @see SWT#SINGLE
1185 * @see SWT#WRAP
1186 * @see #getStyle
1187 */

1188public StyledText(Composite parent, int style) {
1189    super(parent, checkStyle(style));
1190    // set the fg in the OS to ensure that these are the same as StyledText, necessary
1191
// for ensuring that the bg/fg the IME box uses is the same as what StyledText uses
1192
super.setForeground(getForeground());
1193    super.setDragDetect(false);
1194    Display display = getDisplay();
1195    isMirrored = (super.getStyle() & SWT.MIRRORED) != 0;
1196    fixedLineHeight = true;
1197    if ((style & SWT.READ_ONLY) != 0) {
1198        setEditable(false);
1199    }
1200    leftMargin = rightMargin = isBidiCaret() ? BIDI_CARET_WIDTH - 1: 0;
1201    if ((style & SWT.SINGLE) != 0 && (style & SWT.BORDER) != 0) {
1202        leftMargin = topMargin = rightMargin = bottomMargin = 2;
1203    }
1204    alignment = style & (SWT.LEFT | SWT.RIGHT | SWT.CENTER);
1205    if (alignment == 0) alignment = SWT.LEFT;
1206    clipboard = new Clipboard(display);
1207    installDefaultContent();
1208    renderer = new StyledTextRenderer(getDisplay(), this);
1209    renderer.setContent(content);
1210    renderer.setFont(getFont(), tabLength);
1211    defaultCaret = new Caret(this, SWT.NULL);
1212    if ((style & SWT.WRAP) != 0) {
1213        setWordWrap(true);
1214    }
1215    if (isBidiCaret()) {
1216        createCaretBitmaps();
1217        Runnable JavaDoc runnable = new Runnable JavaDoc() {
1218            public void run() {
1219                int direction = BidiUtil.getKeyboardLanguage() == BidiUtil.KEYBOARD_BIDI ? SWT.RIGHT : SWT.LEFT;
1220                if (direction == caretDirection) return;
1221                if (getCaret() != defaultCaret) return;
1222                Point newCaretPos = getPointAtOffset(caretOffset);
1223                setCaretLocation(newCaretPos, direction);
1224            }
1225        };
1226        BidiUtil.addLanguageListener(handle, runnable);
1227    }
1228    setCaret(defaultCaret);
1229    calculateScrollBars();
1230    createKeyBindings();
1231    setCursor(display.getSystemCursor(SWT.CURSOR_IBEAM));
1232    installListeners();
1233    initializeAccessible();
1234    setData("DEFAULT_DROP_TARGET_EFFECT", new StyledTextDropTargetEffect(this));
1235}
1236/**
1237 * Adds an extended modify listener. An ExtendedModify event is sent by the
1238 * widget when the widget text has changed.
1239 *
1240 * @param extendedModifyListener the listener
1241 * @exception SWTException <ul>
1242 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1243 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1244 * </ul>
1245 * @exception IllegalArgumentException <ul>
1246 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
1247 * </ul>
1248 */

1249public void addExtendedModifyListener(ExtendedModifyListener extendedModifyListener) {
1250    checkWidget();
1251    if (extendedModifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1252    StyledTextListener typedListener = new StyledTextListener(extendedModifyListener);
1253    addListener(ExtendedModify, typedListener);
1254}
1255/**
1256 * Adds a bidirectional segment listener.
1257 * <p>
1258 * A BidiSegmentEvent is sent
1259 * whenever a line of text is measured or rendered. The user can
1260 * specify text ranges in the line that should be treated as if they
1261 * had a different direction than the surrounding text.
1262 * This may be used when adjacent segments of right-to-left text should
1263 * not be reordered relative to each other.
1264 * E.g., Multiple Java string literals in a right-to-left language
1265 * should generally remain in logical order to each other, that is, the
1266 * way they are stored.
1267 * </p>
1268 *
1269 * @param listener the listener
1270 * @exception SWTException <ul>
1271 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1272 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1273 * </ul>
1274 * @exception IllegalArgumentException <ul>
1275 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
1276 * </ul>
1277 * @see BidiSegmentEvent
1278 * @since 2.0
1279 */

1280public void addBidiSegmentListener(BidiSegmentListener listener) {
1281    checkWidget();
1282    if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1283    addListener(LineGetSegments, new StyledTextListener(listener));
1284}
1285/**
1286 * Adds a line background listener. A LineGetBackground event is sent by the
1287 * widget to determine the background color for a line.
1288 *
1289 * @param listener the listener
1290 * @exception SWTException <ul>
1291 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1292 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1293 * </ul>
1294 * @exception IllegalArgumentException <ul>
1295 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
1296 * </ul>
1297 */

1298public void addLineBackgroundListener(LineBackgroundListener listener) {
1299    checkWidget();
1300    if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1301    if (!isListening(LineGetBackground)) {
1302        renderer.clearLineBackground(0, content.getLineCount());
1303    }
1304    addListener(LineGetBackground, new StyledTextListener(listener));
1305}
1306/**
1307 * Adds a line style listener. A LineGetStyle event is sent by the widget to
1308 * determine the styles for a line.
1309 *
1310 * @param listener the listener
1311 * @exception SWTException <ul>
1312 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1313 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1314 * </ul>
1315 * @exception IllegalArgumentException <ul>
1316 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
1317 * </ul>
1318 */

1319public void addLineStyleListener(LineStyleListener listener) {
1320    checkWidget();
1321    if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1322    if (!isListening(LineGetStyle)) {
1323        setStyleRanges(0, 0, null, null, true);
1324        renderer.clearLineStyle(0, content.getLineCount());
1325    }
1326    addListener(LineGetStyle, new StyledTextListener(listener));
1327}
1328/**
1329 * Adds a modify listener. A Modify event is sent by the widget when the widget text
1330 * has changed.
1331 *
1332 * @param modifyListener the listener
1333 * @exception SWTException <ul>
1334 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1335 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1336 * </ul>
1337 * @exception IllegalArgumentException <ul>
1338 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
1339 * </ul>
1340 */

1341public void addModifyListener(ModifyListener modifyListener) {
1342    checkWidget();
1343    if (modifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1344    addListener(SWT.Modify, new TypedListener(modifyListener));
1345}
1346/**
1347 * Adds a paint object listener. A paint object event is sent by the widget when an object
1348 * needs to be drawn.
1349 *
1350 * @param listener the listener
1351 * @exception SWTException <ul>
1352 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1353 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1354 * </ul>
1355 * @exception IllegalArgumentException <ul>
1356 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
1357 * </ul>
1358 *
1359 * @since 3.2
1360 *
1361 * @see PaintObjectListener
1362 * @see PaintObjectEvent
1363 */

1364public void addPaintObjectListener(PaintObjectListener listener) {
1365    checkWidget();
1366    if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1367    addListener(PaintObject, new StyledTextListener(listener));
1368}
1369/**
1370 * Adds a selection listener. A Selection event is sent by the widget when the
1371 * user changes the selection.
1372 * <p>
1373 * When <code>widgetSelected</code> is called, the event x and y fields contain
1374 * the start and end caret indices of the selection.
1375 * <code>widgetDefaultSelected</code> is not called for StyledTexts.
1376 * </p>
1377 *
1378 * @param listener the listener which should be notified when the user changes the receiver's selection
1379
1380 * @exception IllegalArgumentException <ul>
1381 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
1382 * </ul>
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>
1386 * </ul>
1387 *
1388 * @see SelectionListener
1389 * @see #removeSelectionListener
1390 * @see SelectionEvent
1391 */

1392public void addSelectionListener(SelectionListener listener) {
1393    checkWidget();
1394    if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1395    addListener(SWT.Selection, new TypedListener(listener));
1396}
1397/**
1398 * Adds a verify key listener. A VerifyKey event is sent by the widget when a key
1399 * is pressed. The widget ignores the key press if the listener sets the doit field
1400 * of the event to false.
1401 *
1402 * @param listener the listener
1403 * @exception SWTException <ul>
1404 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1405 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1406 * </ul>
1407 * @exception IllegalArgumentException <ul>
1408 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
1409 * </ul>
1410 */

1411public void addVerifyKeyListener(VerifyKeyListener listener) {
1412    checkWidget();
1413    if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1414    addListener(VerifyKey, new StyledTextListener(listener));
1415}
1416/**
1417 * Adds a verify listener. A Verify event is sent by the widget when the widget text
1418 * is about to change. The listener can set the event text and the doit field to
1419 * change the text that is set in the widget or to force the widget to ignore the
1420 * text change.
1421 *
1422 * @param verifyListener the listener
1423 * @exception SWTException <ul>
1424 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1425 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1426 * </ul>
1427 * @exception IllegalArgumentException <ul>
1428 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
1429 * </ul>
1430 */

1431public void addVerifyListener(VerifyListener verifyListener) {
1432    checkWidget();
1433    if (verifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1434    addListener(SWT.Verify, new TypedListener(verifyListener));
1435}
1436/**
1437 * Adds a word movement listener. A movement event is sent when the boundary
1438 * of a word is needed. For example, this occurs during word next and word
1439 * previous actions.
1440 *
1441 * @param movementListener the listener
1442 * @exception SWTException <ul>
1443 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1444 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1445 * </ul>
1446 * @exception IllegalArgumentException <ul>
1447 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
1448 * </ul>
1449 *
1450 * @see MovementEvent
1451 * @see MovementListener
1452 * @see #removeWordMovementListener
1453 *
1454 * @since 3.3
1455 */

1456public void addWordMovementListener(MovementListener movementListener) {
1457    checkWidget();
1458    if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1459    addListener(WordNext, new StyledTextListener(movementListener));
1460    addListener(WordPrevious, new StyledTextListener(movementListener));
1461}
1462/**
1463 * Appends a string to the text at the end of the widget.
1464 *
1465 * @param string the string to be appended
1466 * @see #replaceTextRange(int,int,String)
1467 * @exception SWTException <ul>
1468 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1469 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1470 * </ul>
1471 * @exception IllegalArgumentException <ul>
1472 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
1473 * </ul>
1474 */

1475public void append(String JavaDoc string) {
1476    checkWidget();
1477    if (string == null) {
1478        SWT.error(SWT.ERROR_NULL_ARGUMENT);
1479    }
1480    int lastChar = Math.max(getCharCount(), 0);
1481    replaceTextRange(lastChar, 0, string);
1482}
1483/**
1484 * Calculates the scroll bars
1485 */

1486void calculateScrollBars() {
1487    ScrollBar horizontalBar = getHorizontalBar();
1488    ScrollBar verticalBar = getVerticalBar();
1489    setScrollBars(true);
1490    if (verticalBar != null) {
1491        verticalBar.setIncrement(getVerticalIncrement());
1492    }
1493    if (horizontalBar != null) {
1494        horizontalBar.setIncrement(getHorizontalIncrement());
1495    }
1496}
1497/**
1498 * Calculates the top index based on the current vertical scroll offset.
1499 * The top index is the index of the topmost fully visible line or the
1500 * topmost partially visible line if no line is fully visible.
1501 * The top index starts at 0.
1502 */

1503void calculateTopIndex(int delta) {
1504    int oldTopIndex = topIndex;
1505    int oldTopIndexY = topIndexY;
1506    if (isFixedLineHeight()) {
1507        int verticalIncrement = getVerticalIncrement();
1508        if (verticalIncrement == 0) {
1509            return;
1510        }
1511        topIndex = Compatibility.ceil(getVerticalScrollOffset(), verticalIncrement);
1512        // Set top index to partially visible top line if no line is fully
1513
// visible but at least some of the widget client area is visible.
1514
// Fixes bug 15088.
1515
if (topIndex > 0) {
1516            if (clientAreaHeight > 0) {
1517                int bottomPixel = getVerticalScrollOffset() + clientAreaHeight;
1518                int fullLineTopPixel = topIndex * verticalIncrement;
1519                int fullLineVisibleHeight = bottomPixel - fullLineTopPixel;
1520                // set top index to partially visible line if no line fully fits in
1521
// client area or if space is available but not used (the latter should
1522
// never happen because we use claimBottomFreeSpace)
1523
if (fullLineVisibleHeight < verticalIncrement) {
1524                    topIndex--;
1525                }
1526            } else if (topIndex >= content.getLineCount()) {
1527                topIndex = content.getLineCount() - 1;
1528            }
1529        }
1530    } else {
1531        if (delta >= 0) {
1532            delta -= topIndexY;
1533            int lineIndex = topIndex;
1534            int lineCount = content.getLineCount();
1535            while (lineIndex < lineCount) {
1536                if (delta <= 0) break;
1537                delta -= renderer.getLineHeight(lineIndex++);
1538            }
1539            if (lineIndex < lineCount && -delta + renderer.getLineHeight(lineIndex) <= clientAreaHeight - topMargin - bottomMargin) {
1540                topIndex = lineIndex;
1541                topIndexY = -delta;
1542            } else {
1543                topIndex = lineIndex - 1;
1544                topIndexY = -renderer.getLineHeight(topIndex) - delta;
1545            }
1546        } else {
1547            delta -= topIndexY;
1548            int lineIndex = topIndex;
1549            while (lineIndex > 0) {
1550                int lineHeight = renderer.getLineHeight(lineIndex - 1);
1551                if (delta + lineHeight > 0) break;
1552                delta += lineHeight;
1553                lineIndex--;
1554            }
1555            if (lineIndex == 0 || -delta + renderer.getLineHeight(lineIndex) <= clientAreaHeight - topMargin - bottomMargin) {
1556                topIndex = lineIndex;
1557                topIndexY = - delta;
1558            } else {
1559                topIndex = lineIndex - 1;
1560                topIndexY = - renderer.getLineHeight(topIndex) - delta;
1561            }
1562        }
1563    }
1564    if (topIndex != oldTopIndex || oldTopIndexY != topIndexY) {
1565        renderer.calculateClientArea();
1566        setScrollBars(false);
1567    }
1568}
1569/**
1570 * Hides the scroll bars if widget is created in single line mode.
1571 */

1572static int checkStyle(int style) {
1573    if ((style & SWT.SINGLE) != 0) {
1574        style &= ~(SWT.H_SCROLL | SWT.V_SCROLL | SWT.WRAP | SWT.MULTI);
1575    } else {
1576        style |= SWT.MULTI;
1577        if ((style & SWT.WRAP) != 0) {
1578            style &= ~SWT.H_SCROLL;
1579        }
1580    }
1581    style |= SWT.NO_REDRAW_RESIZE | SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND;
1582    return style;
1583}
1584/**
1585 * Scrolls down the text to use new space made available by a resize or by
1586 * deleted lines.
1587 */

1588void claimBottomFreeSpace() {
1589    int clientAreaHeight = this.clientAreaHeight - topMargin - bottomMargin;
1590    if (isFixedLineHeight()) {
1591        int lineHeight = renderer.getLineHeight();
1592        int newVerticalOffset = Math.max(0, content.getLineCount() * lineHeight - clientAreaHeight);
1593        if (newVerticalOffset < getVerticalScrollOffset()) {
1594            scrollVertical(newVerticalOffset - getVerticalScrollOffset(), true);
1595        }
1596    } else {
1597        int bottomIndex = getPartialBottomIndex();
1598        int height = getLinePixel(bottomIndex + 1);
1599        if (clientAreaHeight > height) {
1600            scrollVertical(-getAvailableHeightAbove(clientAreaHeight - height), true);
1601        }
1602    }
1603}
1604/**
1605 * Scrolls text to the right to use new space made available by a resize.
1606 */

1607void claimRightFreeSpace() {
1608    int newHorizontalOffset = Math.max(0, renderer.getWidth() - (clientAreaWidth - leftMargin - rightMargin));
1609    if (newHorizontalOffset < horizontalScrollOffset) {
1610        // item is no longer drawn past the right border of the client area
1611
// align the right end of the item with the right border of the
1612
// client area (window is scrolled right).
1613
scrollHorizontal(newHorizontalOffset - horizontalScrollOffset, true);
1614    }
1615}
1616/**
1617 * Removes the widget selection.
1618 *
1619 * @param sendEvent a Selection event is sent when set to true and when the selection is actually reset.
1620 */

1621void clearSelection(boolean sendEvent) {
1622    int selectionStart = selection.x;
1623    int selectionEnd = selection.y;
1624    resetSelection();
1625    // redraw old selection, if any
1626
if (selectionEnd - selectionStart > 0) {
1627        int length = content.getCharCount();
1628        // called internally to remove selection after text is removed
1629
// therefore make sure redraw range is valid.
1630
int redrawStart = Math.min(selectionStart, length);
1631        int redrawEnd = Math.min(selectionEnd, length);
1632        if (redrawEnd - redrawStart > 0) {
1633            internalRedrawRange(redrawStart, redrawEnd - redrawStart);
1634        }
1635        if (sendEvent) {
1636            sendSelectionEvent();
1637        }
1638    }
1639}
1640public Point computeSize (int wHint, int hHint, boolean changed) {
1641    checkWidget();
1642    int lineCount = (getStyle() & SWT.SINGLE) != 0 ? 1 : content.getLineCount();
1643    int width = 0;
1644    int height = 0;
1645    if (wHint == SWT.DEFAULT || hHint == SWT.DEFAULT) {
1646        Display display = getDisplay();
1647        int maxHeight = display.getClientArea().height;
1648        for (int lineIndex = 0; lineIndex < lineCount; lineIndex++) {
1649            TextLayout layout = renderer.getTextLayout(lineIndex);
1650            if (wordWrap) layout.setWidth(wHint == 0 ? 1 : wHint);
1651            Rectangle rect = layout.getBounds();
1652            height += rect.height;
1653            width = Math.max(width, rect.width);
1654            renderer.disposeTextLayout(layout);
1655            if (isFixedLineHeight() && height > maxHeight) break;
1656        }
1657        if (isFixedLineHeight()) {
1658            height = lineCount * renderer.getLineHeight();
1659        }
1660    }
1661    // Use default values if no text is defined.
1662
if (width == 0) width = DEFAULT_WIDTH;
1663    if (height == 0) height = DEFAULT_HEIGHT;
1664    if (wHint != SWT.DEFAULT) width = wHint;
1665    if (hHint != SWT.DEFAULT) height = hHint;
1666    int wTrim = leftMargin + rightMargin + getCaretWidth();
1667    int hTrim = topMargin + bottomMargin;
1668    Rectangle rect = computeTrim(0, 0, width + wTrim, height + hTrim);
1669    return new Point (rect.width, rect.height);
1670}
1671/**
1672 * Copies the selected text to the <code>DND.CLIPBOARD</code> clipboard.
1673 * <p>
1674 * The text will be put on the clipboard in plain text format and RTF format.
1675 * The <code>DND.CLIPBOARD</code> clipboard is used for data that is
1676 * transferred by keyboard accelerator (such as Ctrl+C/Ctrl+V) or
1677 * by menu action.
1678 * </p>
1679 *
1680 * @exception SWTException <ul>
1681 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1682 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1683 * </ul>
1684 */

1685public void copy() {
1686    checkWidget();
1687    copy(DND.CLIPBOARD);
1688}
1689/**
1690 * Copies the selected text to the specified clipboard. The text will be put in the
1691 * clipboard in plain text format and RTF format.
1692 * <p>
1693 * The clipboardType is one of the clipboard constants defined in class
1694 * <code>DND</code>. The <code>DND.CLIPBOARD</code> clipboard is
1695 * used for data that is transferred by keyboard accelerator (such as Ctrl+C/Ctrl+V)
1696 * or by menu action. The <code>DND.SELECTION_CLIPBOARD</code>
1697 * clipboard is used for data that is transferred by selecting text and pasting
1698 * with the middle mouse button.
1699 * </p>
1700 *
1701 * @param clipboardType indicates the type of clipboard
1702 *
1703 * @exception SWTException <ul>
1704 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1705 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1706 * </ul>
1707 *
1708 * @since 3.1
1709 */

1710public void copy(int clipboardType) {
1711    checkWidget();
1712    if (clipboardType != DND.CLIPBOARD && clipboardType != DND.SELECTION_CLIPBOARD) return;
1713    int length = selection.y - selection.x;
1714    if (length > 0) {
1715        try {
1716            setClipboardContent(selection.x, length, clipboardType);
1717        } catch (SWTError error) {
1718            // Copy to clipboard failed. This happens when another application
1719
// is accessing the clipboard while we copy. Ignore the error.
1720
// Fixes 1GDQAVN
1721
// Rethrow all other errors. Fixes bug 17578.
1722
if (error.code != DND.ERROR_CANNOT_SET_CLIPBOARD) {
1723                throw error;
1724            }
1725        }
1726    }
1727}
1728/**
1729 * Returns the alignment of the widget.
1730 *
1731 * @return the alignment
1732 *
1733 * @exception SWTException <ul>
1734 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1735 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1736 * </ul>
1737 *
1738 * @see #getLineAlignment(int)
1739 *
1740 * @since 3.2
1741 */

1742public int getAlignment() {
1743    checkWidget();
1744    return alignment;
1745}
1746int getAvailableHeightAbove(int height) {
1747    int maxHeight = verticalScrollOffset;
1748    if (maxHeight == -1) {
1749        int lineIndex = topIndex - 1;
1750        maxHeight = -topIndexY;
1751        if (topIndexY > 0) {
1752            maxHeight += renderer.getLineHeight(lineIndex--);
1753        }
1754        while (height > maxHeight && lineIndex >= 0) {
1755            maxHeight += renderer.getLineHeight(lineIndex--);
1756        }
1757    }
1758    return Math.min(height, maxHeight);
1759}
1760int getAvailableHeightBellow(int height) {
1761    int partialBottomIndex = getPartialBottomIndex();
1762    int topY = getLinePixel(partialBottomIndex);
1763    int lineHeight = renderer.getLineHeight(partialBottomIndex);
1764    int availableHeight = 0;
1765    int clientAreaHeight = this.clientAreaHeight - topMargin - bottomMargin;
1766    if (topY + lineHeight > clientAreaHeight) {
1767        availableHeight = lineHeight - (clientAreaHeight - topY);
1768    }
1769    int lineIndex = partialBottomIndex + 1;
1770    int lineCount = content.getLineCount();
1771    while (height > availableHeight && lineIndex < lineCount) {
1772        availableHeight += renderer.getLineHeight(lineIndex++);
1773    }
1774    return Math.min(height, availableHeight);
1775}
1776/**
1777 * Returns a string that uses only the line delimiter specified by the
1778 * StyledTextContent implementation.
1779 * <p>
1780 * Returns only the first line if the widget has the SWT.SINGLE style.
1781 * </p>
1782 *
1783 * @param text the text that may have line delimiters that don't
1784 * match the model line delimiter. Possible line delimiters
1785 * are CR ('\r'), LF ('\n'), CR/LF ("\r\n")
1786 * @return the converted text that only uses the line delimiter
1787 * specified by the model. Returns only the first line if the widget
1788 * has the SWT.SINGLE style.
1789 */

1790String JavaDoc getModelDelimitedText(String JavaDoc text) {
1791    int length = text.length();
1792    if (length == 0) {
1793        return text;
1794    }
1795    int crIndex = 0;
1796    int lfIndex = 0;
1797    int i = 0;
1798    StringBuffer JavaDoc convertedText = new StringBuffer JavaDoc(length);
1799    String JavaDoc delimiter = getLineDelimiter();
1800    while (i < length) {
1801        if (crIndex != -1) {
1802            crIndex = text.indexOf(SWT.CR, i);
1803        }
1804        if (lfIndex != -1) {
1805            lfIndex = text.indexOf(SWT.LF, i);
1806        }
1807        if (lfIndex == -1 && crIndex == -1) { // no more line breaks?
1808
break;
1809        } else if ((crIndex < lfIndex && crIndex != -1) || lfIndex == -1) {
1810            convertedText.append(text.substring(i, crIndex));
1811            if (lfIndex == crIndex + 1) { // CR/LF combination?
1812
i = lfIndex + 1;
1813            } else {
1814                i = crIndex + 1;
1815            }
1816        } else { // LF occurs before CR!
1817
convertedText.append(text.substring(i, lfIndex));
1818            i = lfIndex + 1;
1819        }
1820        if (isSingleLine()) {
1821            break;
1822        }
1823        convertedText.append(delimiter);
1824    }
1825    // copy remaining text if any and if not in single line mode or no
1826
// text copied thus far (because there only is one line)
1827
if (i < length && (!isSingleLine() || convertedText.length() == 0)) {
1828        convertedText.append(text.substring(i));
1829    }
1830    return convertedText.toString();
1831}
1832boolean checkDragDetect(Event event) {
1833    if (!isListening(SWT.DragDetect)) return false;
1834    if (IS_MOTIF) {
1835        if (event.button != 2) return false;
1836    } else {
1837        if (event.button != 1) return false;
1838    }
1839    if (selection.x == selection.y) return false;
1840    int offset = getOffsetAtPoint(event.x, event.y);
1841    if (offset > selection.x && offset < selection.y) {
1842        return dragDetect(event);
1843    }
1844    return false;
1845}
1846/**
1847 * Creates default key bindings.
1848 */

1849void createKeyBindings() {
1850    int nextKey = isMirrored() ? SWT.ARROW_LEFT : SWT.ARROW_RIGHT;
1851    int previousKey = isMirrored() ? SWT.ARROW_RIGHT : SWT.ARROW_LEFT;
1852    
1853    // Navigation
1854
setKeyBinding(SWT.ARROW_UP, ST.LINE_UP);
1855    setKeyBinding(SWT.ARROW_DOWN, ST.LINE_DOWN);
1856    if (IS_CARBON) {
1857        setKeyBinding(previousKey | SWT.MOD1, ST.LINE_START);
1858        setKeyBinding(nextKey | SWT.MOD1, ST.LINE_END);
1859        setKeyBinding(SWT.HOME, ST.TEXT_START);
1860        setKeyBinding(SWT.END, ST.TEXT_END);
1861        setKeyBinding(SWT.ARROW_UP | SWT.MOD1, ST.TEXT_START);
1862        setKeyBinding(SWT.ARROW_DOWN | SWT.MOD1, ST.TEXT_END);
1863        setKeyBinding(nextKey | SWT.MOD3, ST.WORD_NEXT);
1864        setKeyBinding(previousKey | SWT.MOD3, ST.WORD_PREVIOUS);
1865    } else {
1866        setKeyBinding(SWT.HOME, ST.LINE_START);
1867        setKeyBinding(SWT.END, ST.LINE_END);
1868        setKeyBinding(SWT.HOME | SWT.MOD1, ST.TEXT_START);
1869        setKeyBinding(SWT.END | SWT.MOD1, ST.TEXT_END);
1870        setKeyBinding(nextKey | SWT.MOD1, ST.WORD_NEXT);
1871        setKeyBinding(previousKey | SWT.MOD1, ST.WORD_PREVIOUS);
1872    }
1873    setKeyBinding(SWT.PAGE_UP, ST.PAGE_UP);
1874    setKeyBinding(SWT.PAGE_DOWN, ST.PAGE_DOWN);
1875    setKeyBinding(SWT.PAGE_UP | SWT.MOD1, ST.WINDOW_START);
1876    setKeyBinding(SWT.PAGE_DOWN | SWT.MOD1, ST.WINDOW_END);
1877    setKeyBinding(nextKey, ST.COLUMN_NEXT);
1878    setKeyBinding(previousKey, ST.COLUMN_PREVIOUS);
1879    
1880    // Selection
1881
setKeyBinding(SWT.ARROW_UP | SWT.MOD2, ST.SELECT_LINE_UP);
1882    setKeyBinding(SWT.ARROW_DOWN | SWT.MOD2, ST.SELECT_LINE_DOWN);
1883    if (IS_CARBON) {
1884        setKeyBinding(previousKey | SWT.MOD1 | SWT.MOD2, ST.SELECT_LINE_START);
1885        setKeyBinding(nextKey | SWT.MOD1 | SWT.MOD2, ST.SELECT_LINE_END);
1886        setKeyBinding(SWT.HOME | SWT.MOD2, ST.SELECT_TEXT_START);
1887        setKeyBinding(SWT.END | SWT.MOD2, ST.SELECT_TEXT_END);
1888        setKeyBinding(SWT.ARROW_UP | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_START);
1889        setKeyBinding(SWT.ARROW_DOWN | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_END);
1890        setKeyBinding(nextKey | SWT.MOD2 | SWT.MOD3, ST.SELECT_WORD_NEXT);
1891        setKeyBinding(previousKey | SWT.MOD2 | SWT.MOD3, ST.SELECT_WORD_PREVIOUS);
1892    } else {
1893        setKeyBinding(SWT.HOME | SWT.MOD2, ST.SELECT_LINE_START);
1894        setKeyBinding(SWT.END | SWT.MOD2, ST.SELECT_LINE_END);
1895        setKeyBinding(SWT.HOME | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_START);
1896        setKeyBinding(SWT.END | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_END);
1897        setKeyBinding(nextKey | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_NEXT);
1898        setKeyBinding(previousKey | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_PREVIOUS);
1899    }
1900    setKeyBinding(SWT.PAGE_UP | SWT.MOD2, ST.SELECT_PAGE_UP);
1901    setKeyBinding(SWT.PAGE_DOWN | SWT.MOD2, ST.SELECT_PAGE_DOWN);
1902    setKeyBinding(SWT.PAGE_UP | SWT.MOD1 | SWT.MOD2, ST.SELECT_WINDOW_START);
1903    setKeyBinding(SWT.PAGE_DOWN | SWT.MOD1 | SWT.MOD2, ST.SELECT_WINDOW_END);
1904    setKeyBinding(nextKey | SWT.MOD2, ST.SELECT_COLUMN_NEXT);
1905    setKeyBinding(previousKey | SWT.MOD2, ST.SELECT_COLUMN_PREVIOUS);
1906                
1907    // Modification
1908
// Cut, Copy, Paste
1909
setKeyBinding('X' | SWT.MOD1, ST.CUT);
1910    setKeyBinding('C' | SWT.MOD1, ST.COPY);
1911    setKeyBinding('V' | SWT.MOD1, ST.PASTE);
1912    if (IS_CARBON) {
1913        setKeyBinding(SWT.DEL | SWT.MOD2, ST.DELETE_NEXT);
1914        setKeyBinding(SWT.BS | SWT.MOD3, ST.DELETE_WORD_PREVIOUS);
1915        setKeyBinding(SWT.DEL | SWT.MOD3, ST.DELETE_WORD_NEXT);
1916    } else {
1917        // Cut, Copy, Paste Wordstar style
1918
setKeyBinding(SWT.DEL | SWT.MOD2, ST.CUT);
1919        setKeyBinding(SWT.INSERT | SWT.MOD1, ST.COPY);
1920        setKeyBinding(SWT.INSERT | SWT.MOD2, ST.PASTE);
1921    }
1922    setKeyBinding(SWT.BS | SWT.MOD2, ST.DELETE_PREVIOUS);
1923    setKeyBinding(SWT.BS, ST.DELETE_PREVIOUS);
1924    setKeyBinding(SWT.DEL, ST.DELETE_NEXT);
1925    setKeyBinding(SWT.BS | SWT.MOD1, ST.DELETE_WORD_PREVIOUS);
1926    setKeyBinding(SWT.DEL | SWT.MOD1, ST.DELETE_WORD_NEXT);
1927    
1928    // Miscellaneous
1929
setKeyBinding(SWT.INSERT, ST.TOGGLE_OVERWRITE);
1930}
1931/**
1932 * Create the bitmaps to use for the caret in bidi mode. This
1933 * method only needs to be called upon widget creation and when the
1934 * font changes (the caret bitmap height needs to match font height).
1935 */

1936void createCaretBitmaps() {
1937    int caretWidth = BIDI_CARET_WIDTH;
1938    Display display = getDisplay();
1939    if (leftCaretBitmap != null) {
1940        if (defaultCaret != null && leftCaretBitmap.equals(defaultCaret.getImage())) {
1941            defaultCaret.setImage(null);
1942        }
1943        leftCaretBitmap.dispose();
1944    }
1945    int lineHeight = renderer.getLineHeight();
1946    leftCaretBitmap = new Image(display, caretWidth, lineHeight);
1947    GC gc = new GC (leftCaretBitmap);
1948    gc.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
1949    gc.fillRectangle(0, 0, caretWidth, lineHeight);
1950    gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
1951    gc.drawLine(0,0,0,lineHeight);
1952    gc.drawLine(0,0,caretWidth-1,0);
1953    gc.drawLine(0,1,1,1);
1954    gc.dispose();
1955    
1956    if (rightCaretBitmap != null) {
1957        if (defaultCaret != null && rightCaretBitmap.equals(defaultCaret.getImage())) {
1958            defaultCaret.setImage(null);
1959        }
1960        rightCaretBitmap.dispose();
1961    }
1962    rightCaretBitmap = new Image(display, caretWidth, lineHeight);
1963    gc = new GC (rightCaretBitmap);
1964    gc.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
1965    gc.fillRectangle(0, 0, caretWidth, lineHeight);
1966    gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
1967    gc.drawLine(caretWidth-1,0,caretWidth-1,lineHeight);
1968    gc.drawLine(0,0,caretWidth-1,0);
1969    gc.drawLine(caretWidth-1,1,1,1);
1970    gc.dispose();
1971}
1972/**
1973 * Moves the selected text to the clipboard. The text will be put in the
1974 * clipboard in plain text format and RTF format.
1975 *
1976 * @exception SWTException <ul>
1977 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1978 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1979 * </ul>
1980 */

1981public void cut(){
1982    checkWidget();
1983    int length = selection.y - selection.x;
1984    if (length > 0) {
1985        try {
1986            setClipboardContent(selection.x, length, DND.CLIPBOARD);
1987        } catch (SWTError error) {
1988            // Copy to clipboard failed. This happens when another application
1989
// is accessing the clipboard while we copy. Ignore the error.
1990
// Fixes 1GDQAVN
1991
// Rethrow all other errors. Fixes bug 17578.
1992
if (error.code != DND.ERROR_CANNOT_SET_CLIPBOARD) {
1993                throw error;
1994            }
1995            // Abort cut operation if copy to clipboard fails.
1996
// Fixes bug 21030.
1997
return;
1998        }
1999        doDelete();
2000    }
2001}
2002/**
2003 * A mouse move event has occurred. See if we should start autoscrolling. If
2004 * the move position is outside of the client area, initiate autoscrolling.
2005 * Otherwise, we've moved back into the widget so end autoscrolling.
2006 */

2007void doAutoScroll(Event event) {
2008    if (event.y > clientAreaHeight) {
2009        doAutoScroll(SWT.DOWN, event.y - clientAreaHeight);
2010    } else if (event.y < 0) {
2011        doAutoScroll(SWT.UP, -event.y);
2012    } else if (event.x < leftMargin && !wordWrap) {
2013        doAutoScroll(ST.COLUMN_PREVIOUS, leftMargin - event.x);
2014    } else if (event.x > clientAreaWidth - leftMargin - rightMargin && !wordWrap) {
2015        doAutoScroll(ST.COLUMN_NEXT, event.x - (clientAreaWidth - leftMargin - rightMargin));
2016    } else {
2017        endAutoScroll();
2018    }
2019}
2020/**
2021 * Initiates autoscrolling.
2022 *
2023 * @param direction SWT.UP, SWT.DOWN, SWT.COLUMN_NEXT, SWT.COLUMN_PREVIOUS
2024 */

2025void doAutoScroll(int direction, int distance) {
2026    autoScrollDistance = distance;
2027    // If we're already autoscrolling in the given direction do nothing
2028
if (autoScrollDirection == direction) {
2029        return;
2030    }
2031    
2032    Runnable JavaDoc timer = null;
2033    final Display display = getDisplay();
2034    // Set a timer that will simulate the user pressing and holding
2035
// down a cursor key (i.e., arrowUp, arrowDown).
2036
if (direction == SWT.UP) {
2037        timer = new Runnable JavaDoc() {
2038            public void run() {
2039                if (autoScrollDirection == SWT.UP) {
2040                    doSelectionPageUp(autoScrollDistance);
2041                    display.timerExec(V_SCROLL_RATE, this);
2042                }
2043            }
2044        };
2045        autoScrollDirection = direction;
2046        display.timerExec(V_SCROLL_RATE, timer);
2047    } else if (direction == SWT.DOWN) {
2048        timer = new Runnable JavaDoc() {
2049            public void run() {
2050                if (autoScrollDirection == SWT.DOWN) {
2051                    doSelectionPageDown(autoScrollDistance);
2052                    display.timerExec(V_SCROLL_RATE, this);
2053                }
2054            }
2055        };
2056        autoScrollDirection = direction;
2057        display.timerExec(V_SCROLL_RATE, timer);
2058    } else if (direction == ST.COLUMN_NEXT) {
2059        timer = new Runnable JavaDoc() {
2060            public void run() {
2061                if (autoScrollDirection == ST.COLUMN_NEXT) {
2062                    doVisualNext();
2063                    setMouseWordSelectionAnchor();
2064                    doMouseSelection();
2065                    display.timerExec(H_SCROLL_RATE, this);
2066                }
2067            }
2068        };
2069        autoScrollDirection = direction;
2070        display.timerExec(H_SCROLL_RATE, timer);
2071    } else if (direction == ST.COLUMN_PREVIOUS) {
2072        timer = new Runnable JavaDoc() {
2073            public void run() {
2074                if (autoScrollDirection == ST.COLUMN_PREVIOUS) {
2075                    doVisualPrevious();
2076                    setMouseWordSelectionAnchor();
2077                    doMouseSelection();
2078                    display.timerExec(H_SCROLL_RATE, this);
2079                }
2080            }
2081        };
2082        autoScrollDirection = direction;
2083        display.timerExec(H_SCROLL_RATE, timer);
2084    }
2085}
2086/**
2087 * Deletes the previous character. Delete the selected text if any.
2088 * Move the caret in front of the deleted text.
2089 */

2090void doBackspace() {
2091    Event event = new Event();
2092    event.text = "";
2093    if (selection.x != selection.y) {
2094        event.start = selection.x;
2095        event.end = selection.y;
2096        sendKeyEvent(event);
2097    } else if (caretOffset > 0) {
2098        int lineIndex = content.getLineAtOffset(caretOffset);
2099        int lineOffset = content.getOffsetAtLine(lineIndex);
2100        if (caretOffset == lineOffset) {
2101            lineOffset = content.getOffsetAtLine(lineIndex - 1);
2102            event.start = lineOffset + content.getLine(lineIndex - 1).length();
2103            event.end = caretOffset;
2104        } else {
2105            TextLayout layout = renderer.getTextLayout(lineIndex);
2106            int start = layout.getPreviousOffset(caretOffset - lineOffset, SWT.MOVEMENT_CHAR);
2107            renderer.disposeTextLayout(layout);
2108            event.start = start + lineOffset;
2109            event.end = caretOffset;
2110        }
2111        sendKeyEvent(event);
2112    }
2113}
2114/**
2115 * Replaces the selection with the character or insert the character at the
2116 * current caret position if no selection exists.
2117 * <p>
2118 * If a carriage return was typed replace it with the line break character
2119 * used by the widget on this platform.
2120 * </p>
2121 *
2122 * @param key the character typed by the user
2123 */

2124void doContent(char key) {
2125    if (textLimit > 0 &&
2126        content.getCharCount() - (selection.y - selection.x) >= textLimit) {
2127        return;
2128    }
2129    Event event = new Event();
2130    event.start = selection.x;
2131    event.end = selection.y;
2132    // replace a CR line break with the widget line break
2133
// CR does not make sense on Windows since most (all?) applications
2134
// don't recognize CR as a line break.
2135
if (key == SWT.CR || key == SWT.LF) {
2136        if (!isSingleLine()) {
2137            event.text = getLineDelimiter();
2138        }
2139    } else if (selection.x == selection.y && overwrite && key != TAB) {
2140        // no selection and overwrite mode is on and the typed key is not a
2141
// tab character (tabs are always inserted without overwriting)?
2142
int lineIndex = content.getLineAtOffset(event.end);
2143        int lineOffset = content.getOffsetAtLine(lineIndex);
2144        String JavaDoc line = content.getLine(lineIndex);
2145        // replace character at caret offset if the caret is not at the
2146
// end of the line
2147
if (event.end < lineOffset + line.length()) {
2148            event.end++;
2149        }
2150        event.text = new String JavaDoc(new char[] {key});
2151    } else {
2152        event.text = new String JavaDoc(new char[] {key});
2153    }
2154    if (event.text != null) {
2155        sendKeyEvent(event);
2156    }
2157}
2158/**
2159 * Moves the caret after the last character of the widget content.
2160 */

2161void doContentEnd() {
2162    // place caret at end of first line if receiver is in single
2163
// line mode. fixes 4820.
2164
if (isSingleLine()) {
2165        doLineEnd();
2166    } else {
2167        int length = content.getCharCount();
2168        if (caretOffset < length) {
2169            caretOffset = length;
2170            showCaret();
2171        }
2172    }
2173}
2174/**
2175 * Moves the caret in front of the first character of the widget content.
2176 */

2177void doContentStart() {
2178    if (caretOffset > 0) {
2179        caretOffset = 0;
2180        showCaret();
2181    }
2182}
2183/**
2184 * Moves the caret to the start of the selection if a selection exists.
2185 * Otherwise, if no selection exists move the cursor according to the
2186 * cursor selection rules.
2187 *
2188 * @see #doSelectionCursorPrevious
2189 */

2190void doCursorPrevious() {
2191    if (selection.y - selection.x > 0) {
2192        caretOffset = selection.x;
2193        caretAlignment = OFFSET_LEADING;
2194        showCaret();
2195    } else {
2196        doSelectionCursorPrevious();
2197    }
2198}
2199/**
2200 * Moves the caret to the end of the selection if a selection exists.
2201 * Otherwise, if no selection exists move the cursor according to the
2202 * cursor selection rules.
2203 *
2204 * @see #doSelectionCursorNext
2205 */

2206void doCursorNext() {
2207    if (selection.y - selection.x > 0) {
2208        caretOffset = selection.y;
2209        caretAlignment = PREVIOUS_OFFSET_TRAILING;
2210        showCaret();
2211    } else {
2212        doSelectionCursorNext();
2213    }
2214}
2215/**
2216 * Deletes the next character. Delete the selected text if any.
2217 */

2218void doDelete() {
2219    Event event = new Event();
2220    event.text = "";
2221    if (selection.x != selection.y) {
2222        event.start = selection.x;
2223        event.end = selection.y;
2224        sendKeyEvent(event);
2225    } else if (caretOffset < content.getCharCount()) {
2226        int line = content.getLineAtOffset(caretOffset);
2227        int lineOffset = content.getOffsetAtLine(line);
2228        int lineLength = content.getLine(line).length();
2229        if (caretOffset == lineOffset + lineLength) {
2230            event.start = caretOffset;
2231            event.end = content.getOffsetAtLine(line + 1);
2232        } else {
2233            event.start = caretOffset;
2234            event.end = getClusterNext(caretOffset, line);
2235        }
2236        sendKeyEvent(event);
2237    }
2238}
2239/**
2240 * Deletes the next word.
2241 */

2242void doDeleteWordNext() {
2243    if (selection.x != selection.y) {
2244        // if a selection exists, treat the as if
2245
// only the delete key was pressed
2246
doDelete();
2247    } else {
2248        Event event = new Event();
2249        event.text = "";
2250        event.start = caretOffset;
2251        event.end = getWordNext(caretOffset, SWT.MOVEMENT_WORD);
2252        sendKeyEvent(event);
2253    }
2254}
2255/**
2256 * Deletes the previous word.
2257 */

2258void doDeleteWordPrevious() {
2259    if (selection.x != selection.y) {
2260        // if a selection exists, treat as if
2261
// only the backspace key was pressed
2262
doBackspace();
2263    } else {
2264        Event event = new Event();
2265        event.text = "";
2266        event.start = getWordPrevious(caretOffset, SWT.MOVEMENT_WORD);
2267        event.end = caretOffset;
2268        sendKeyEvent(event);
2269    }
2270}
2271/**
2272 * Moves the caret one line down and to the same character offset relative
2273 * to the beginning of the line. Move the caret to the end of the new line
2274 * if the new line is shorter than the character offset.
2275 */

2276void doLineDown(boolean select) {
2277    int caretLine = getCaretLine();
2278    int lineCount = content.getLineCount();
2279    int y = 0;
2280    boolean lastLine = false;
2281    if (wordWrap) {
2282        int lineOffset = content.getOffsetAtLine(caretLine);
2283        int offsetInLine = caretOffset - lineOffset;
2284        TextLayout layout = renderer.getTextLayout(caretLine);
2285        int lineIndex = getVisualLineIndex(layout, offsetInLine);
2286        int layoutLineCount = layout.getLineCount();
2287        if (lineIndex == layoutLineCount - 1) {
2288            lastLine = caretLine == lineCount - 1;
2289            caretLine++;
2290        } else {
2291            y = layout.getLineBounds(lineIndex + 1).y;
2292        }
2293        renderer.disposeTextLayout(layout);
2294    } else {
2295        lastLine = caretLine == lineCount - 1;
2296        caretLine++;
2297    }
2298    if (lastLine) {
2299        if (select) caretOffset = content.getCharCount();
2300    } else {
2301        caretOffset = getOffsetAtPoint(columnX, y, caretLine);
2302    }
2303    int oldColumnX = columnX;
2304    int oldHScrollOffset = horizontalScrollOffset;
2305    if (select) {
2306        setMouseWordSelectionAnchor();
2307        // select first and then scroll to reduce flash when key
2308
// repeat scrolls lots of lines
2309
doSelection(ST.COLUMN_NEXT);
2310    }
2311    showCaret();
2312    int hScrollChange = oldHScrollOffset - horizontalScrollOffset;
2313    columnX = oldColumnX + hScrollChange;
2314}
2315/**
2316 * Moves the caret to the end of the line.
2317 */

2318void doLineEnd() {
2319    int caretLine = getCaretLine();
2320    int lineOffset = content.getOffsetAtLine(caretLine);
2321    int lineEndOffset;
2322    if (wordWrap) {
2323        TextLayout layout = renderer.getTextLayout(caretLine);
2324        int offsetInLine = caretOffset - lineOffset;
2325        int lineIndex = getVisualLineIndex(layout, offsetInLine);
2326        int[] offsets = layout.getLineOffsets();
2327        lineEndOffset = lineOffset + offsets[lineIndex + 1];
2328        renderer.disposeTextLayout(layout);
2329    } else {
2330        int lineLength = content.getLine(caretLine).length();
2331        lineEndOffset = lineOffset + lineLength;
2332    }
2333    if (caretOffset < lineEndOffset) {
2334        caretOffset = lineEndOffset;
2335        caretAlignment = PREVIOUS_OFFSET_TRAILING;
2336        showCaret();
2337    }
2338}
2339/**
2340 * Moves the caret to the beginning of the line.
2341 */

2342void doLineStart() {
2343    int caretLine = getCaretLine();
2344    int lineOffset = content.getOffsetAtLine(caretLine);
2345    if (wordWrap) {
2346        TextLayout layout = renderer.getTextLayout(caretLine);
2347        int offsetInLine = caretOffset - lineOffset;
2348        int lineIndex = getVisualLineIndex(layout, offsetInLine);
2349        int[] offsets = layout.getLineOffsets();
2350        lineOffset += offsets[lineIndex];
2351        renderer.disposeTextLayout(layout);
2352    }
2353    if (caretOffset > lineOffset) {
2354        caretOffset = lineOffset;
2355        caretAlignment = OFFSET_LEADING;
2356        showCaret();
2357    }
2358}
2359/**
2360 * Moves the caret one line up and to the same character offset relative
2361 * to the beginning of the line. Move the caret to the end of the new line
2362 * if the new line is shorter than the character offset.
2363 */

2364void doLineUp(boolean select) {
2365    int caretLine = getCaretLine(), y = 0;
2366    boolean firstLine = false;
2367    if (wordWrap) {
2368        int lineOffset = content.getOffsetAtLine(caretLine);
2369        int offsetInLine = caretOffset - lineOffset;
2370        TextLayout layout = renderer.getTextLayout(caretLine);
2371        int lineIndex = getVisualLineIndex(layout, offsetInLine);
2372        if (lineIndex == 0) {
2373            firstLine = caretLine == 0;
2374            if (!firstLine) {
2375                caretLine--;
2376                y = renderer.getLineHeight(caretLine) - 1;
2377            }
2378        } else {
2379            y = layout.getLineBounds(lineIndex - 1).y;
2380        }
2381        renderer.disposeTextLayout(layout);
2382    } else {
2383        firstLine = caretLine == 0;
2384        caretLine--;
2385    }
2386    if (firstLine) {
2387        if (select) caretOffset = 0;
2388    } else {
2389        caretOffset = getOffsetAtPoint(columnX, y, caretLine);
2390    }
2391    int oldColumnX = columnX;
2392    int oldHScrollOffset = horizontalScrollOffset;
2393    if (select) setMouseWordSelectionAnchor();
2394    showCaret();
2395    if (select) doSelection(ST.COLUMN_PREVIOUS);
2396    int hScrollChange = oldHScrollOffset - horizontalScrollOffset;
2397    columnX = oldColumnX + hScrollChange;
2398}
2399/**
2400 * Moves the caret to the specified location.
2401 *
2402 * @param x x location of the new caret position
2403 * @param y y location of the new caret position
2404 * @param select the location change is a selection operation.
2405 * include the line delimiter in the selection
2406 */

2407void doMouseLocationChange(int x, int y, boolean select) {
2408    int line = getLineIndex(y);
2409
2410    updateCaretDirection = true;
2411    // allow caret to be placed below first line only if receiver is
2412
// not in single line mode. fixes 4820.
2413
if (line < 0 || (isSingleLine() && line > 0)) {
2414        return;
2415    }
2416    int oldCaretAlignment = caretAlignment;
2417    int newCaretOffset = getOffsetAtPoint(x, y);
2418    
2419    if (clickCount > 1) {
2420        // double click word select the previous/next word. fixes bug 15610
2421
newCaretOffset = doMouseWordSelect(x, newCaretOffset, line);
2422    }
2423    
2424    int newCaretLine = content.getLineAtOffset(newCaretOffset);
2425    
2426    // Is the mouse within the left client area border or on
2427
// a different line? If not the autoscroll selection
2428
// could be incorrectly reset. Fixes 1GKM3XS
2429
if (0 <= y && y < clientAreaHeight &&
2430        (0 <= x && x < clientAreaWidth || wordWrap ||
2431        newCaretLine != content.getLineAtOffset(caretOffset))) {
2432        if (newCaretOffset != caretOffset || caretAlignment != oldCaretAlignment) {
2433            caretOffset = newCaretOffset;
2434            if (select) doMouseSelection();
2435            showCaret();
2436        }
2437    }
2438    if (!select) {
2439        caretOffset = newCaretOffset;
2440        clearSelection(true);
2441    }
2442}
2443/**
2444 * Updates the selection based on the caret position
2445 */

2446void doMouseSelection() {
2447    if (caretOffset <= selection.x ||
2448        (caretOffset > selection.x &&
2449         caretOffset < selection.y && selectionAnchor == selection.x)) {
2450        doSelection(ST.COLUMN_PREVIOUS);
2451    } else {
2452        doSelection(ST.COLUMN_NEXT);
2453    }
2454}
2455/**
2456 * Returns the offset of the word at the specified offset.
2457 * If the current selection extends from high index to low index
2458 * (i.e., right to left, or caret is at left border of selection on
2459 * non-bidi platforms) the start offset of the word preceding the
2460 * selection is returned. If the current selection extends from
2461 * low index to high index the end offset of the word following
2462 * the selection is returned.
2463 *
2464 * @param x mouse x location
2465 * @param newCaretOffset caret offset of the mouse cursor location
2466 * @param line line index of the mouse cursor location
2467 */

2468int doMouseWordSelect(int x, int newCaretOffset, int line) {
2469    // flip selection anchor based on word selection direction from
2470
// base double click. Always do this here (and don't rely on doAutoScroll)
2471
// because auto scroll only does not cover all possible mouse selections
2472
// (e.g., mouse x < 0 && mouse y > caret line y)
2473
if (newCaretOffset < selectionAnchor && selectionAnchor == selection.x) {
2474        selectionAnchor = doubleClickSelection.y;
2475    } else if (newCaretOffset > selectionAnchor && selectionAnchor == selection.y) {
2476        selectionAnchor = doubleClickSelection.x;
2477    }
2478    if (0 <= x && x < clientAreaWidth) {
2479        boolean wordSelect = (clickCount & 1) == 0;
2480        if (caretOffset == selection.x) {
2481            if (wordSelect) {
2482                newCaretOffset = getWordPrevious(newCaretOffset, SWT.MOVEMENT_WORD_START);
2483            } else {
2484                newCaretOffset = content.getOffsetAtLine(line);
2485            }
2486        } else {
2487            if (wordSelect) {
2488                newCaretOffset = getWordNext(newCaretOffset, SWT.MOVEMENT_WORD_END);
2489            } else {
2490                int lineEnd = content.getCharCount();
2491                if (line + 1 < content.getLineCount()) {
2492                    lineEnd = content.getOffsetAtLine(line + 1);
2493                }
2494                newCaretOffset = lineEnd;
2495            }
2496        }
2497    }
2498    return newCaretOffset;
2499}
2500/**
2501 * Scrolls one page down so that the last line (truncated or whole)
2502 * of the current page becomes the fully visible top line.
2503 * <p>
2504 * The caret is scrolled the same number of lines so that its location
2505 * relative to the top line remains the same. The exception is the end
2506 * of the text where a full page scroll is not possible. In this case
2507 * the caret is moved after the last character.
2508 * </p>
2509 *
2510 * @param select whether or not to select the page
2511 */

2512void doPageDown(boolean select, int height) {
2513    if (isSingleLine()) return;
2514    int oldColumnX = columnX;
2515    int oldHScrollOffset = horizontalScrollOffset;
2516    if (isFixedLineHeight()) {
2517        int lineCount = content.getLineCount();
2518        int caretLine = getCaretLine();
2519        if (caretLine < lineCount - 1) {
2520            int lineHeight = renderer.getLineHeight();
2521            int lines = (height == -1 ? clientAreaHeight : height) / lineHeight;
2522            int scrollLines = Math.min(lineCount - caretLine - 1, lines);
2523            // ensure that scrollLines never gets negative and at least one
2524
// line is scrolled. fixes bug 5602.
2525
scrollLines = Math.max(1, scrollLines);
2526            caretOffset = getOffsetAtPoint(columnX, getLinePixel(caretLine + scrollLines));
2527            if (select) {
2528                doSelection(ST.COLUMN_NEXT);
2529            }
2530            // scroll one page down or to the bottom
2531
int verticalMaximum = lineCount * getVerticalIncrement();
2532            int pageSize = clientAreaHeight;
2533            int verticalScrollOffset = getVerticalScrollOffset();
2534            int scrollOffset = verticalScrollOffset + scrollLines * getVerticalIncrement();
2535            if (scrollOffset + pageSize > verticalMaximum) {
2536                scrollOffset = verticalMaximum - pageSize;
2537            }
2538            if (scrollOffset > verticalScrollOffset) {
2539                scrollVertical(scrollOffset - verticalScrollOffset, true);
2540            }
2541        }
2542    } else {
2543        int lineCount = content.getLineCount();
2544        int caretLine = getCaretLine();
2545        int lineIndex, lineHeight;
2546        if (height == -1) {
2547            lineIndex = getPartialBottomIndex();
2548            int topY = getLinePixel(lineIndex);
2549            lineHeight = renderer.getLineHeight(lineIndex);
2550            height = topY;
2551            if (topY + lineHeight <= clientAreaHeight) {
2552                height += lineHeight;
2553            } else {
2554                if (wordWrap) {
2555                    TextLayout layout = renderer.getTextLayout(lineIndex);
2556                    int y = clientAreaHeight - topY;
2557                    for (int i = 0; i < layout.getLineCount(); i++) {
2558                        Rectangle bounds = layout.getLineBounds(i);
2559                        if (bounds.contains(bounds.x, y)) {
2560                            height += bounds.y;
2561                            break;
2562                        }
2563                    }
2564                    renderer.disposeTextLayout(layout);
2565                }
2566            }
2567        } else {
2568            lineIndex = getLineIndex(height);
2569            int topLineY = getLinePixel(lineIndex);
2570            if (wordWrap) {
2571                TextLayout layout = renderer.getTextLayout(lineIndex);
2572                int y = height - topLineY;
2573                for (int i = 0; i < layout.getLineCount(); i++) {
2574                    Rectangle bounds = layout.getLineBounds(i);
2575                    if (bounds.contains(bounds.x, y)) {
2576                        height = topLineY + bounds.y + bounds.height;
2577                        break;
2578                    }
2579                }
2580                renderer.disposeTextLayout(layout);
2581            } else {
2582                height = topLineY + renderer.getLineHeight(lineIndex);
2583            }
2584        }
2585        int caretHeight = height;
2586        if (wordWrap) {
2587            TextLayout layout = renderer.getTextLayout(caretLine);
2588            int offsetInLine = caretOffset - content.getOffsetAtLine(caretLine);
2589            lineIndex = getVisualLineIndex(layout, offsetInLine);
2590            caretHeight += layout.getLineBounds(lineIndex).y;
2591            renderer.disposeTextLayout(layout);
2592        }
2593        lineIndex = caretLine;
2594        lineHeight = renderer.getLineHeight(lineIndex);
2595        while (caretHeight - lineHeight >= 0 && lineIndex < lineCount - 1) {
2596            caretHeight -= lineHeight;
2597            lineHeight = renderer.getLineHeight(++lineIndex);
2598        }
2599        caretOffset = getOffsetAtPoint(columnX, caretHeight, lineIndex);
2600        if (select) doSelection(ST.COLUMN_NEXT);
2601        height = getAvailableHeightBellow(height);
2602        scrollVertical(height, true);
2603        if (height == 0) setCaretLocation();
2604    }
2605    showCaret();
2606    int hScrollChange = oldHScrollOffset - horizontalScrollOffset;
2607    columnX = oldColumnX + hScrollChange;
2608}
2609/**
2610 * Moves the cursor to the end of the last fully visible line.
2611 */

2612void doPageEnd() {
2613    // go to end of line if in single line mode. fixes 5673
2614
if (isSingleLine()) {
2615        doLineEnd();
2616    } else {
2617        int bottomOffset;
2618        if (wordWrap) {
2619            int lineIndex = getPartialBottomIndex();
2620            TextLayout layout = renderer.getTextLayout(lineIndex);
2621            int y = (clientAreaHeight - bottomMargin) - getLinePixel(lineIndex);
2622            int index = layout.getLineCount() - 1;
2623            while (index >= 0) {
2624                Rectangle bounds = layout.getLineBounds(index);
2625                if (y >= bounds.y + bounds.height) break;
2626                index--;
2627            }
2628            if (index == -1 && lineIndex > 0) {
2629                bottomOffset = content.getOffsetAtLine(lineIndex - 1) + content.getLine(lineIndex - 1).length();
2630            } else {
2631                bottomOffset = content.getOffsetAtLine(lineIndex) + Math.max(0, layout.getLineOffsets()[index + 1] - 1);
2632            }
2633            renderer.disposeTextLayout(layout);
2634        } else {
2635            int lineIndex = getBottomIndex();
2636            bottomOffset = content.getOffsetAtLine(lineIndex) + content.getLine(lineIndex).length();
2637        }
2638        if (caretOffset < bottomOffset) {
2639            caretOffset = bottomOffset;
2640            caretAlignment = OFFSET_LEADING;
2641            showCaret();
2642        }
2643    }
2644}
2645/**
2646 * Moves the cursor to the beginning of the first fully visible line.
2647 */

2648void doPageStart() {
2649    int topOffset;
2650    if (wordWrap) {
2651        int y, lineIndex;
2652        if (topIndexY > 0) {
2653            lineIndex = topIndex - 1;
2654            y = renderer.getLineHeight(lineIndex) - topIndexY;
2655        } else {
2656            lineIndex = topIndex;
2657            y = -topIndexY;
2658        }
2659        TextLayout layout = renderer.getTextLayout(lineIndex);
2660        int index = 0;
2661        int lineCount = layout.getLineCount();
2662        while (index < lineCount) {
2663            Rectangle bounds = layout.getLineBounds(index);
2664            if (y <= bounds.y) break;
2665            index++;
2666        }
2667        if (index == lineCount) {
2668            topOffset = content.getOffsetAtLine(lineIndex + 1);
2669        } else {
2670            topOffset = content.getOffsetAtLine(lineIndex) + layout.getLineOffsets()[index];
2671        }
2672        renderer.disposeTextLayout(layout);
2673    } else {
2674        topOffset = content.getOffsetAtLine(topIndex);
2675    }
2676    if (caretOffset > topOffset) {
2677        caretOffset = topOffset;
2678        caretAlignment = OFFSET_LEADING;
2679        showCaret();
2680    }
2681}
2682/**
2683 * Scrolls one page up so that the first line (truncated or whole)
2684 * of the current page becomes the fully visible last line.
2685 * The caret is scrolled the same number of lines so that its location
2686 * relative to the top line remains the same. The exception is the beginning
2687 * of the text where a full page scroll is not possible. In this case the
2688 * caret is moved in front of the first character.
2689 */

2690void doPageUp(boolean select, int height) {
2691    if (isSingleLine()) return;
2692    int oldHScrollOffset = horizontalScrollOffset;
2693    int oldColumnX = columnX;
2694    if (isFixedLineHeight()) {
2695        int caretLine = getCaretLine();
2696        if (caretLine > 0) {
2697            int lineHeight = renderer.getLineHeight();
2698            int lines = (height == -1 ? clientAreaHeight : height) / lineHeight;
2699            int scrollLines = Math.max(1, Math.min(caretLine, lines));
2700            caretLine -= scrollLines;
2701            caretOffset = getOffsetAtPoint(columnX, getLinePixel(caretLine));
2702            if (select) {
2703                doSelection(ST.COLUMN_PREVIOUS);
2704            }
2705            int verticalScrollOffset = getVerticalScrollOffset();
2706            int scrollOffset = Math.max(0, verticalScrollOffset - scrollLines * getVerticalIncrement());
2707            if (scrollOffset < verticalScrollOffset) {
2708                scrollVertical(scrollOffset - verticalScrollOffset, true);
2709            }
2710        }
2711    } else {
2712        int caretLine = getCaretLine();
2713        int lineHeight, lineIndex;
2714        if (height == -1) {
2715            if (topIndexY == 0) {
2716                height = clientAreaHeight;
2717            } else {
2718                int y;
2719                if (topIndex > 0) {
2720                    lineIndex = topIndex - 1;
2721                    lineHeight = renderer.getLineHeight(lineIndex);
2722                    height = clientAreaHeight - topIndexY;
2723                    y = lineHeight - topIndexY;
2724                } else {
2725                    lineIndex = topIndex;
2726                    lineHeight = renderer.getLineHeight(lineIndex);
2727                    height = clientAreaHeight - (lineHeight + topIndexY);
2728                    y = -topIndexY;
2729                }
2730                if (wordWrap) {
2731                    TextLayout layout = renderer.getTextLayout(lineIndex);
2732                    for (int i = 0; i < layout.getLineCount(); i++) {
2733                        Rectangle bounds = layout.getLineBounds(i);
2734                        if (bounds.contains(bounds.x, y)) {
2735                            height += lineHeight - (bounds.y + bounds.height);
2736                            break;
2737                        }
2738                    }
2739                    renderer.disposeTextLayout(layout);
2740                }
2741            }
2742        } else {
2743            lineIndex = getLineIndex(clientAreaHeight - height);
2744            int topLineY = getLinePixel(lineIndex);
2745            if (wordWrap) {
2746                TextLayout layout = renderer.getTextLayout(lineIndex);
2747                int y = topLineY;
2748                for (int i = 0; i < layout.getLineCount(); i++) {
2749                    Rectangle bounds = layout.getLineBounds(i);
2750                    if (bounds.contains(bounds.x, y)) {
2751                        height = clientAreaHeight - (topLineY + bounds.y);
2752                        break;
2753                    }
2754                }
2755                renderer.disposeTextLayout(layout);
2756            } else {
2757                height = clientAreaHeight - topLineY;
2758            }
2759        }
2760        int caretHeight = height;
2761        if (wordWrap) {
2762            TextLayout layout = renderer.getTextLayout(caretLine);
2763            int offsetInLine = caretOffset - content.getOffsetAtLine(caretLine);
2764            lineIndex = getVisualLineIndex(layout, offsetInLine);
2765            caretHeight += layout.getBounds().height - layout.getLineBounds(lineIndex).y;
2766            renderer.disposeTextLayout(layout);
2767        }
2768        lineIndex = caretLine;
2769        lineHeight = renderer.getLineHeight(lineIndex);
2770        while (caretHeight - lineHeight >= 0 && lineIndex > 0) {
2771            caretHeight -= lineHeight;
2772            lineHeight = renderer.getLineHeight(--lineIndex);
2773        }
2774        lineHeight = renderer.getLineHeight(lineIndex);
2775        caretOffset = getOffsetAtPoint(columnX, lineHeight - caretHeight, lineIndex);
2776        if (select) doSelection(ST.COLUMN_PREVIOUS);
2777        height = getAvailableHeightAbove(height);
2778        scrollVertical(-height, true);
2779        if (height == 0) setCaretLocation();
2780    }
2781    showCaret();
2782    int hScrollChange = oldHScrollOffset - horizontalScrollOffset;
2783    columnX = oldColumnX + hScrollChange;
2784}
2785/**
2786 * Updates the selection to extend to the current caret position.
2787 */

2788void doSelection(int direction) {
2789    int redrawStart = -1;
2790    int redrawEnd = -1;
2791    if (selectionAnchor == -1) {
2792        selectionAnchor = selection.x;
2793    }
2794    if (direction == ST.COLUMN_PREVIOUS) {
2795        if (caretOffset < selection.x) {
2796            // grow selection
2797
redrawEnd = selection.x;
2798            redrawStart = selection.x = caretOffset;
2799            // check if selection has reversed direction
2800
if (selection.y != selectionAnchor) {
2801                redrawEnd = selection.y;
2802                selection.y = selectionAnchor;
2803            }
2804        // test whether selection actually changed. Fixes 1G71EO1
2805
} else if (selectionAnchor == selection.x && caretOffset < selection.y) {
2806            // caret moved towards selection anchor (left side of selection).
2807
// shrink selection
2808
redrawEnd = selection.y;
2809            redrawStart = selection.y = caretOffset;
2810        }
2811    } else {
2812        if (caretOffset > selection.y) {
2813            // grow selection
2814
redrawStart = selection.y;
2815            redrawEnd = selection.y = caretOffset;
2816            // check if selection has reversed direction
2817
if (selection.x != selectionAnchor) {
2818                redrawStart = selection.x;
2819                selection.x = selectionAnchor;
2820            }
2821        // test whether selection actually changed. Fixes 1G71EO1
2822
} else if (selectionAnchor == selection.y && caretOffset > selection.x) {
2823            // caret moved towards selection anchor (right side of selection).
2824
// shrink selection
2825
redrawStart = selection.x;
2826            redrawEnd = selection.x = caretOffset;
2827        }
2828    }
2829    if (redrawStart != -1 && redrawEnd != -1) {
2830        internalRedrawRange(redrawStart, redrawEnd - redrawStart);
2831        sendSelectionEvent();
2832    }
2833}
2834/**
2835 * Moves the caret to the next character or to the beginning of the
2836 * next line if the cursor is at the end of a line.
2837 */

2838void doSelectionCursorNext() {
2839    int caretLine = getCaretLine();
2840    int lineOffset = content.getOffsetAtLine(caretLine);
2841    int offsetInLine = caretOffset - lineOffset;
2842    if (offsetInLine < content.getLine(caretLine).length()) {
2843        TextLayout layout = renderer.getTextLayout(caretLine);
2844        offsetInLine = layout.getNextOffset(offsetInLine, SWT.MOVEMENT_CLUSTER);
2845        int lineStart = layout.getLineOffsets()[layout.getLineIndex(offsetInLine)];
2846        renderer.disposeTextLayout(layout);
2847        caretOffset = offsetInLine + lineOffset;
2848        caretAlignment = offsetInLine == lineStart ? OFFSET_LEADING : PREVIOUS_OFFSET_TRAILING;
2849        showCaret();
2850    } else if (caretLine < content.getLineCount() - 1 && !isSingleLine()) {
2851        caretLine++;
2852        caretOffset = content.getOffsetAtLine(caretLine);
2853        caretAlignment = PREVIOUS_OFFSET_TRAILING;
2854        showCaret();
2855    }
2856}
2857/**
2858 * Moves the caret to the previous character or to the end of the previous
2859 * line if the cursor is at the beginning of a line.
2860 */

2861void doSelectionCursorPrevious() {
2862    int caretLine = getCaretLine();
2863    int lineOffset = content.getOffsetAtLine(caretLine);
2864    int offsetInLine = caretOffset - lineOffset;
2865    caretAlignment = OFFSET_LEADING;
2866    if (offsetInLine > 0) {
2867        caretOffset = getClusterPrevious(caretOffset, caretLine);
2868        showCaret();
2869    } else if (caretLine > 0) {
2870        caretLine--;
2871        lineOffset = content.getOffsetAtLine(caretLine);
2872        caretOffset = lineOffset + content.getLine(caretLine).length();
2873        showCaret();
2874    }
2875}
2876/**
2877 * Moves the caret one line down and to the same character offset relative
2878 * to the beginning of the line. Moves the caret to the end of the new line
2879 * if the new line is shorter than the character offset.
2880 * Moves the caret to the end of the text if the caret already is on the
2881 * last line.
2882 * Adjusts the selection according to the caret change. This can either add
2883 * to or subtract from the old selection, depending on the previous selection
2884 * direction.
2885 */

2886void doSelectionLineDown() {
2887    int oldColumnX = columnX = getPointAtOffset(caretOffset).x;
2888    doLineDown(true);
2889    columnX = oldColumnX;
2890}
2891/**
2892 * Moves the caret one line up and to the same character offset relative
2893 * to the beginning of the line. Moves the caret to the end of the new line
2894 * if the new line is shorter than the character offset.
2895 * Moves the caret to the beginning of the document if it is already on the
2896 * first line.
2897 * Adjusts the selection according to the caret change. This can either add
2898 * to or subtract from the old selection, depending on the previous selection
2899 * direction.
2900 */

2901void doSelectionLineUp() {
2902    int oldColumnX = columnX = getPointAtOffset(caretOffset).x;
2903    doLineUp(true);
2904    columnX = oldColumnX;
2905}
2906/**
2907 * Scrolls one page down so that the last line (truncated or whole)
2908 * of the current page becomes the fully visible top line.
2909 * <p>
2910 * The caret is scrolled the same number of lines so that its location
2911 * relative to the top line remains the same. The exception is the end
2912 * of the text where a full page scroll is not possible. In this case
2913 * the caret is moved after the last character.
2914 * <p></p>
2915 * Adjusts the selection according to the caret change. This can either add
2916 * to or subtract from the old selection, depending on the previous selection
2917 * direction.
2918 * </p>
2919 */

2920void doSelectionPageDown(int pixels) {
2921    int oldColumnX = columnX = getPointAtOffset(caretOffset).x;
2922    doPageDown(true, pixels);
2923    columnX = oldColumnX;
2924}
2925/**
2926 * Scrolls one page up so that the first line (truncated or whole)
2927 * of the current page becomes the fully visible last line.
2928 * <p>
2929 * The caret is scrolled the same number of lines so that its location
2930 * relative to the top line remains the same. The exception is the beginning
2931 * of the text where a full page scroll is not possible. In this case the
2932 * caret is moved in front of the first character.
2933 * </p><p>
2934 * Adjusts the selection according to the caret change. This can either add
2935 * to or subtract from the old selection, depending on the previous selection
2936 * direction.
2937 * </p>
2938 */

2939void doSelectionPageUp(int pixels) {
2940    int oldColumnX = columnX = getPointAtOffset(caretOffset).x;
2941    doPageUp(true, pixels);
2942    columnX = oldColumnX;
2943}
2944/**
2945 * Moves the caret to the end of the next word .
2946 */

2947void doSelectionWordNext() {
2948    int newCaretOffset = getWordNext(caretOffset, SWT.MOVEMENT_WORD);
2949    // Force symmetrical movement for word next and previous. Fixes 14536
2950
caretAlignment = OFFSET_LEADING;
2951    // don't change caret position if in single line mode and the cursor
2952
// would be on a different line. fixes 5673
2953
if (!isSingleLine() ||
2954        content.getLineAtOffset(caretOffset) == content.getLineAtOffset(newCaretOffset)) {
2955        caretOffset = newCaretOffset;
2956        showCaret();
2957    }
2958}
2959/**
2960 * Moves the caret to the start of the previous word.
2961 */

2962void doSelectionWordPrevious() {
2963    caretAlignment = OFFSET_LEADING;
2964    caretOffset = getWordPrevious(caretOffset, SWT.MOVEMENT_WORD);
2965    int caretLine = content.getLineAtOffset(caretOffset);
2966    // word previous always comes from bottom line. when
2967
// wrapping lines, stay on bottom line when on line boundary
2968
if (wordWrap && caretLine < content.getLineCount() - 1 &&
2969        caretOffset == content.getOffsetAtLine(caretLine + 1)) {
2970        caretLine++;
2971    }
2972    showCaret();
2973}
2974/**
2975 * Moves the caret one character to the left. Do not go to the previous line.
2976 * When in a bidi locale and at a R2L character the caret is moved to the
2977 * beginning of the R2L segment (visually right) and then one character to the
2978 * left (visually left because it's now in a L2R segment).
2979 */

2980void doVisualPrevious() {
2981    caretOffset = getClusterPrevious(caretOffset, getCaretLine());
2982    showCaret();
2983}
2984/**
2985 * Moves the caret one character to the right. Do not go to the next line.
2986 * When in a bidi locale and at a R2L character the caret is moved to the
2987 * end of the R2L segment (visually left) and then one character to the
2988 * right (visually right because it's now in a L2R segment).
2989 */

2990void doVisualNext() {
2991    caretOffset = getClusterNext(caretOffset, getCaretLine());
2992    showCaret();
2993}
2994/**
2995 * Moves the caret to the end of the next word.
2996 * If a selection exists, move the caret to the end of the selection
2997 * and remove the selection.
2998 */

2999void doWordNext() {
3000    if (selection.y - selection.x > 0) {
3001        caretOffset = selection.y;
3002        showCaret();
3003    } else {
3004        doSelectionWordNext();
3005    }
3006}
3007/**
3008 * Moves the caret to the start of the previous word.
3009 * If a selection exists, move the caret to the start of the selection
3010 * and remove the selection.
3011 */

3012void doWordPrevious() {
3013    if (selection.y - selection.x > 0) {
3014        caretOffset = selection.x;
3015        showCaret();
3016    } else {
3017        doSelectionWordPrevious();
3018    }
3019}
3020/**
3021 * Ends the autoscroll process.
3022 */

3023void endAutoScroll() {
3024    autoScrollDirection = SWT.NULL;
3025}
3026public Color getBackground() {
3027    checkWidget();
3028    if (background == null) {
3029        return getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
3030    }
3031    return background;
3032}
3033/**
3034 * Returns the baseline, in pixels.
3035 *
3036 * Note: this API should not be used if a StyleRange attribute causes lines to
3037 * have different heights (i.e. different fonts, rise, etc).
3038 *
3039 * @return baseline the baseline
3040 * @exception SWTException <ul>
3041 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3042 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3043 * </ul>
3044 * @since 3.0
3045 *
3046 * @see #getBaseline(int)
3047 */

3048public int getBaseline() {
3049    checkWidget();
3050    return renderer.getBaseline();
3051}
3052/**
3053 * Returns the baseline at the given offset, in pixels.
3054 *
3055 * @param offset the offset
3056 *
3057 * @return baseline the baseline
3058 *
3059 * @exception SWTException <ul>
3060 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3061 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3062 * </ul>
3063 * @exception IllegalArgumentException <ul>
3064 * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
3065 * </ul>
3066 *
3067 * @since 3.2
3068 */

3069public int getBaseline(int offset) {
3070    checkWidget();
3071    if (!(0 <= offset && offset <= content.getCharCount())) {
3072        SWT.error(SWT.ERROR_INVALID_RANGE);
3073    }
3074    if (isFixedLineHeight()) {
3075        return renderer.getBaseline();
3076    }
3077    int lineIndex = content.getLineAtOffset(offset);
3078    int lineOffset = content.getOffsetAtLine(lineIndex);
3079    TextLayout layout = renderer.getTextLayout(lineIndex);
3080    int lineInParagraph = layout.getLineIndex(Math.min(offset - lineOffset, layout.getText().length()));
3081    FontMetrics metrics = layout.getLineMetrics(lineInParagraph);
3082    renderer.disposeTextLayout(layout);
3083    return metrics.getAscent() + metrics.getLeading();
3084}
3085/**
3086 * Gets the BIDI coloring mode. When true the BIDI text display
3087 * algorithm is applied to segments of text that are the same
3088 * color.
3089 *
3090 * @return the current coloring mode
3091 * @exception SWTException <ul>
3092 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3093 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3094 * </ul>
3095 *
3096 * @deprecated use BidiSegmentListener instead.
3097 */

3098public boolean getBidiColoring() {
3099    checkWidget();
3100    return bidiColoring;
3101}
3102/**
3103 * Returns the index of the last fully visible line.
3104 *
3105 * @return index of the last fully visible line.
3106 */

3107int getBottomIndex() {
3108    int bottomIndex;
3109    if (isFixedLineHeight()) {
3110        int lineCount = 1;
3111        int lineHeight = renderer.getLineHeight();
3112        if (lineHeight != 0) {
3113            // calculate the number of lines that are fully visible
3114
int partialTopLineHeight = topIndex * lineHeight - getVerticalScrollOffset();
3115            lineCount = (clientAreaHeight - partialTopLineHeight) / lineHeight;
3116        }
3117        bottomIndex = Math.min(content.getLineCount() - 1, topIndex + Math.max(0, lineCount - 1));
3118    } else {
3119        int clientAreaHeight = this.clientAreaHeight - bottomMargin;
3120        bottomIndex = getLineIndex(clientAreaHeight);
3121        if (bottomIndex > 0) {
3122            int linePixel = getLinePixel(bottomIndex);
3123            int lineHeight = renderer.getLineHeight(bottomIndex);
3124            if (linePixel + lineHeight > clientAreaHeight) {
3125                if (getLinePixel(bottomIndex - 1) >= topMargin) {
3126                    bottomIndex--;
3127                }
3128            }
3129        }
3130    }
3131    return bottomIndex;
3132}
3133Rectangle getBoundsAtOffset(int offset) {
3134    int lineIndex = content.getLineAtOffset(offset);
3135    String JavaDoc line = content.getLine(lineIndex);
3136    Rectangle bounds;
3137    if (line.length() != 0) {
3138        int offsetInLine = offset - content.getOffsetAtLine(lineIndex);
3139        TextLayout layout = renderer.getTextLayout(lineIndex);
3140        bounds = layout.getBounds(offsetInLine, offsetInLine);
3141        renderer.disposeTextLayout(layout);
3142    } else {
3143        bounds = new Rectangle (0, 0, 0, renderer.getLineHeight());
3144    }
3145    bounds.x += leftMargin - horizontalScrollOffset;
3146    bounds.y += getLinePixel(lineIndex);
3147    return bounds;
3148}
3149
3150/**
3151 * Returns the caret position relative to the start of the text.
3152 *
3153 * @return the caret position relative to the start of the text.
3154 * @exception SWTException <ul>
3155 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3156 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3157 * </ul>
3158 */

3159public int getCaretOffset() {
3160    checkWidget();
3161    return caretOffset;
3162}
3163/**
3164 * Returns the caret width.
3165 *
3166 * @return the caret width, 0 if caret is null.
3167 */

3168int getCaretWidth() {
3169    Caret caret = getCaret();
3170    if (caret == null) return 0;
3171    return caret.getSize().x;
3172}
3173Object JavaDoc getClipboardContent(int clipboardType) {
3174    TextTransfer plainTextTransfer = TextTransfer.getInstance();
3175    return clipboard.getContents(plainTextTransfer, clipboardType);
3176}
3177int getClusterNext(int offset, int lineIndex) {
3178    int lineOffset = content.getOffsetAtLine(lineIndex);
3179    TextLayout layout = renderer.getTextLayout(lineIndex);
3180    offset -= lineOffset;
3181    offset = layout.getNextOffset(offset, SWT.MOVEMENT_CLUSTER);
3182    offset += lineOffset;
3183    renderer.disposeTextLayout(layout);
3184    return offset;
3185}
3186int getClusterPrevious(int offset, int lineIndex) {
3187    int lineOffset = content.getOffsetAtLine(lineIndex);
3188    TextLayout layout = renderer.getTextLayout(lineIndex);
3189    offset -= lineOffset;
3190    offset = layout.getPreviousOffset(offset, SWT.MOVEMENT_CLUSTER);
3191    offset += lineOffset;
3192    renderer.disposeTextLayout(layout);
3193    return offset;
3194}
3195/**
3196 * Returns the content implementation that is used for text storage
3197 * or null if no user defined content implementation has been set.
3198 *
3199 * @return content implementation that is used for text storage or null
3200 * if no user defined content implementation has been set.
3201 * @exception SWTException <ul>
3202 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3203 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3204 * </ul>
3205 */

3206public StyledTextContent getContent() {
3207    checkWidget();
3208    return content;
3209}
3210public boolean getDragDetect () {
3211    checkWidget ();
3212    return dragDetect;
3213}
3214/**
3215 * Returns whether the widget implements double click mouse behavior.
3216 *
3217 * @return true if double clicking a word selects the word, false if double clicks
3218 * have the same effect as regular mouse clicks
3219 * @exception SWTException <ul>
3220 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3221 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3222 * </ul>
3223 */

3224public boolean getDoubleClickEnabled() {
3225    checkWidget();
3226    return doubleClickEnabled;
3227}
3228/**
3229 * Returns whether the widget content can be edited.
3230 *
3231 * @return true if content can be edited, false otherwise
3232 * @exception SWTException <ul>
3233 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3234 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3235 * </ul>
3236 */

3237public boolean getEditable() {
3238    checkWidget();
3239    return editable;
3240}
3241public Color getForeground() {
3242    checkWidget();
3243    if (foreground == null) {
3244        return getDisplay().getSystemColor(SWT.COLOR_LIST_FOREGROUND);
3245    }
3246    return foreground;
3247}
3248/**
3249 * Returns the horizontal scroll increment.
3250 *
3251 * @return horizontal scroll increment.
3252 */

3253int getHorizontalIncrement() {
3254    return renderer.averageCharWidth;
3255}
3256/**
3257 * Returns the horizontal scroll offset relative to the start of the line.
3258 *
3259 * @return horizontal scroll offset relative to the start of the line,
3260 * measured in character increments starting at 0, if > 0 the content is scrolled
3261 * @exception SWTException <ul>
3262 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3263 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3264 * </ul>
3265 */

3266public int getHorizontalIndex() {
3267    checkWidget();
3268    return horizontalScrollOffset / getHorizontalIncrement();
3269}
3270/**
3271 * Returns the horizontal scroll offset relative to the start of the line.
3272 *
3273 * @return the horizontal scroll offset relative to the start of the line,
3274 * measured in pixel starting at 0, if > 0 the content is scrolled.
3275 * @exception SWTException <ul>
3276 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3277 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3278 * </ul>
3279 */

3280public int getHorizontalPixel() {
3281    checkWidget();
3282    return horizontalScrollOffset;
3283}
3284/**
3285 * Returns the line indentation of the widget.
3286 *
3287 * @return the line indentation
3288 *
3289 * @exception SWTException <ul>
3290 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3291 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3292 * </ul>
3293 *
3294 * @see #getLineIndent(int)
3295 *
3296 * @since 3.2
3297 */

3298public int getIndent() {
3299    checkWidget();
3300    return indent;
3301}
3302/**
3303 * Returns whether the widget justifies lines.
3304 *
3305 * @return whether lines are justified
3306 *
3307 * @exception SWTException <ul>
3308 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3309 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3310 * </ul>
3311 *
3312 * @see #getLineJustify(int)
3313 *
3314 * @since 3.2
3315 */

3316public boolean getJustify() {
3317    checkWidget();
3318    return justify;
3319}
3320/**
3321 * Returns the action assigned to the key.
3322 * Returns SWT.NULL if there is no action associated with the key.
3323 *
3324 * @param key a key code defined in SWT.java or a character.
3325 * Optionally ORd with a state mask. Preferred state masks are one or more of
3326 * SWT.MOD1, SWT.MOD2, SWT.MOD3, since these masks account for modifier platform
3327 * differences. However, there may be cases where using the specific state masks
3328 * (i.e., SWT.CTRL, SWT.SHIFT, SWT.ALT, SWT.COMMAND) makes sense.
3329 * @return one of the predefined actions defined in ST.java or SWT.NULL
3330 * if there is no action associated with the key.
3331 * @exception SWTException <ul>
3332 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3333 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3334 * </ul>
3335 */

3336public int getKeyBinding(int key) {
3337    checkWidget();
3338    Integer JavaDoc action = (Integer JavaDoc) keyActionMap.get(new Integer JavaDoc(key));
3339    return action == null ? SWT.NULL : action.intValue();
3340}
3341/**
3342 * Gets the number of characters.
3343 *
3344 * @return number of characters in the widget
3345 * @exception SWTException <ul>
3346 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3347 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3348 * </ul>
3349 */

3350public int getCharCount() {
3351    checkWidget();
3352    return content.getCharCount();
3353}
3354/**
3355 * Returns the alignment of the line at the given index.
3356 *
3357 * @param index the index of the line
3358 *
3359 * @return the line alignment
3360 *
3361 * @exception SWTException <ul>
3362 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3363 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3364 * </ul>
3365 * @exception IllegalArgumentException <ul>
3366 * <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
3367 * </ul>
3368 *
3369 * @see #getAlignment()
3370 *
3371 * @since 3.2
3372 */

3373public int getLineAlignment(int index) {
3374    checkWidget();
3375    if (index < 0 || index > content.getLineCount()) {
3376        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3377    }
3378    return renderer.getLineAlignment(index, alignment);
3379}
3380/**
3381 * Returns the line at the specified offset in the text
3382 * where 0 &lt; offset &lt; getCharCount() so that getLineAtOffset(getCharCount())
3383 * returns the line of the insert location.
3384 *
3385 * @param offset offset relative to the start of the content.
3386 * 0 <= offset <= getCharCount()
3387 * @return line at the specified offset in the text
3388 * @exception SWTException <ul>
3389 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3390 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3391 * </ul>
3392 * @exception IllegalArgumentException <ul>
3393 * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
3394 * </ul>
3395 */

3396public int getLineAtOffset(int offset) {
3397    checkWidget();
3398    if (offset < 0 || offset > getCharCount()) {
3399        SWT.error(SWT.ERROR_INVALID_RANGE);
3400    }
3401    return content.getLineAtOffset(offset);
3402}
3403/**
3404 * Returns the background color of the line at the given index.
3405 * Returns null if a LineBackgroundListener has been set or if no background
3406 * color has been specified for the line. Should not be called if a
3407 * LineBackgroundListener has been set since the listener maintains the
3408 * line background colors.
3409 *
3410 * @param index the index of the line
3411 * @return the background color of the line at the given index.
3412 *
3413 * @exception SWTException <ul>
3414 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3415 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3416 * </ul>
3417 * @exception IllegalArgumentException <ul>
3418 * <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
3419 * </ul>
3420 */

3421public Color getLineBackground(int index) {
3422    checkWidget();
3423    if (index < 0 || index > content.getLineCount()) {
3424        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3425    }
3426    return isListening(LineGetBackground) ? null : renderer.getLineBackground(index, null);
3427}
3428/**
3429 * Returns the bullet of the line at the given index.
3430 *
3431 * @param index the index of the line
3432 *
3433 * @return the line bullet
3434 *
3435 * @exception SWTException <ul>
3436 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3437 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3438 * </ul>
3439 * @exception IllegalArgumentException <ul>
3440 * <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
3441 * </ul>
3442 *
3443 * @since 3.2
3444 */

3445public Bullet getLineBullet(int index) {
3446    checkWidget();
3447    if (index < 0 || index > content.getLineCount()) {
3448        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3449    }
3450    return isListening(LineGetStyle) ? null : renderer.getLineBullet(index, null);
3451}
3452/**
3453 * Returns the line background data for the given line or null if
3454 * there is none.
3455 *
3456 * @param lineOffset offset of the line start relative to the start
3457 * of the content.
3458 * @param line line to get line background data for
3459 * @return line background data for the given line.
3460 */

3461StyledTextEvent getLineBackgroundData(int lineOffset, String JavaDoc line) {
3462    return sendLineEvent(LineGetBackground, lineOffset, line);
3463}
3464/**
3465 * Gets the number of text lines.
3466 *
3467 * @return the number of lines in the widget
3468 * @exception SWTException <ul>
3469 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3470 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3471 * </ul>
3472 */

3473public int getLineCount() {
3474    checkWidget();
3475    return content.getLineCount();
3476}
3477/**
3478 * Returns the number of lines that can be completely displayed in the
3479 * widget client area.
3480 *
3481 * @return number of lines that can be completely displayed in the widget
3482 * client area.
3483 */

3484int getLineCountWhole() {
3485    if (isFixedLineHeight()) {
3486        int lineHeight = renderer.getLineHeight();
3487        return lineHeight != 0 ? clientAreaHeight / lineHeight : 1;
3488    }
3489    return getBottomIndex() - topIndex + 1;
3490}
3491/**
3492 * Returns the line delimiter used for entering new lines by key down
3493 * or paste operation.
3494 *
3495 * @return line delimiter used for entering new lines by key down
3496 * or paste operation.
3497 * @exception SWTException <ul>
3498 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3499 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3500 * </ul>
3501 */

3502public String JavaDoc getLineDelimiter() {
3503    checkWidget();
3504    return content.getLineDelimiter();
3505}
3506/**
3507 * Returns the line height.
3508 * <p>
3509 * Note: this API should not be used if a StyleRange attribute causes lines to
3510 * have different heights (i.e. different fonts, rise, etc).
3511 * </p>
3512 *
3513 * @return line height in pixel.
3514 * @exception SWTException <ul>
3515 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3516 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3517 * </ul>
3518 * @see #getLineHeight(int)
3519 */

3520public int getLineHeight() {
3521    checkWidget();
3522    return renderer.getLineHeight();
3523}
3524/**
3525 * Returns the line height at the given offset.
3526 *
3527 * @param offset the offset
3528 *
3529 * @return line height in pixels
3530 *
3531 * @exception SWTException <ul>
3532 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3533 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3534 * </ul>
3535 * @exception IllegalArgumentException <ul>
3536 * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
3537 * </ul>
3538 *
3539 * @since 3.2
3540 */

3541public int getLineHeight(int offset) {
3542    checkWidget();
3543    if (!(0 <= offset && offset <= content.getCharCount())) {
3544        SWT.error(SWT.ERROR_INVALID_RANGE);
3545    }
3546    if (isFixedLineHeight()) {
3547        return renderer.getLineHeight();
3548    }
3549    int lineIndex = content.getLineAtOffset(offset);
3550    int lineOffset = content.getOffsetAtLine(lineIndex);
3551    TextLayout layout = renderer.getTextLayout(lineIndex);
3552    int lineInParagraph = layout.getLineIndex(Math.min(offset - lineOffset, layout.getText().length()));
3553    int height = layout.getLineBounds(lineInParagraph).height;
3554    renderer.disposeTextLayout(layout);
3555    return height;
3556}
3557/**
3558 * Returns the indentation of the line at the given index.
3559 *
3560 * @param index the index of the line
3561 *
3562 * @return the line indentation
3563 *
3564 * @exception SWTException <ul>
3565 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3566 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3567 * </ul>
3568 * @exception IllegalArgumentException <ul>
3569 * <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
3570 * </ul>
3571 *
3572 * @see #getIndent()
3573 *
3574 * @since 3.2
3575 */

3576public int getLineIndent(int index) {
3577    checkWidget();
3578    if (index < 0 || index > content.getLineCount()) {
3579        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3580    }
3581    return isListening(LineGetStyle) ? 0 : renderer.getLineIndent(index, indent);
3582}
3583/**
3584 * Returns whether the line at the given index is justified.
3585 *
3586 * @param index the index of the line
3587 *
3588 * @return whether the line is justified
3589 *
3590 * @exception SWTException <ul>
3591 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3592 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3593 * </ul>
3594 * @exception IllegalArgumentException <ul>
3595 * <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
3596 * </ul>
3597 *
3598 * @see #getJustify()
3599 *
3600 * @since 3.2
3601 */

3602public boolean getLineJustify(int index) {
3603    checkWidget();
3604    if (index < 0 || index > content.getLineCount()) {
3605        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3606    }
3607    return isListening(LineGetStyle) ? false : renderer.getLineJustify(index, justify);
3608}
3609/**
3610 * Returns the line spacing of the widget.
3611 *
3612 * @return the line spacing
3613 *
3614 * @exception SWTException <ul>
3615 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3616 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3617 * </ul>
3618 *
3619 * @since 3.2
3620 */

3621public int getLineSpacing() {
3622    checkWidget();
3623    return lineSpacing;
3624}
3625/**
3626 * Returns the line style data for the given line or null if there is
3627 * none.
3628 * <p>
3629 * If there is a LineStyleListener but it does not set any styles,
3630 * the StyledTextEvent.styles field will be initialized to an empty
3631 * array.
3632 * </p>
3633 *
3634 * @param lineOffset offset of the line start relative to the start of
3635 * the content.
3636 * @param line line to get line styles for
3637 * @return line style data for the given line. Styles may start before
3638 * line start and end after line end
3639 */

3640StyledTextEvent getLineStyleData(int lineOffset, String JavaDoc line) {
3641    return sendLineEvent(LineGetStyle, lineOffset, line);
3642}
3643/**
3644 * Returns the top pixel, relative to the client area, of a given line.
3645 * Clamps out of ranges index.
3646 *
3647 * @param lineIndex the line index, the max value is lineCount. If
3648 * lineIndex == lineCount it returns the bottom pixel of the last line.
3649 * It means this function can be used to retrieve the bottom pixel of any line.
3650 *
3651 * @since 3.2
3652 */

3653public int getLinePixel(int lineIndex) {
3654    checkWidget();
3655    int lineCount = content.getLineCount();
3656    lineIndex = Math.max(0, Math.min(lineCount, lineIndex));
3657    if (isFixedLineHeight()) {
3658        int lineHeight = renderer.getLineHeight();
3659        return lineIndex * lineHeight - getVerticalScrollOffset() + topMargin;
3660    }
3661    if (lineIndex == topIndex) return topIndexY + topMargin;
3662    int height = topIndexY;
3663    if (lineIndex > topIndex) {
3664        for (int i = topIndex; i < lineIndex; i++) {
3665            height += renderer.getLineHeight(i);
3666        }
3667    } else {
3668        for (int i = topIndex - 1; i >= lineIndex; i--) {
3669            height -= renderer.getLineHeight(i);
3670        }
3671    }
3672    return height + topMargin;
3673}
3674/**
3675 * Returns the line index for a y, relative to the client area.
3676 * The line index returned is always in the range 0..lineCount - 1.
3677 *
3678 * @since 3.2
3679 */

3680public int getLineIndex(int y) {
3681    checkWidget();
3682    y -= topMargin;
3683    if (isFixedLineHeight()) {
3684        int lineHeight = renderer.getLineHeight();
3685        int lineIndex = (y + getVerticalScrollOffset()) / lineHeight;
3686        int lineCount = content.getLineCount();
3687        lineIndex = Math.max(0, Math.min(lineCount - 1, lineIndex));
3688        return lineIndex;
3689    }
3690    if (y == topIndexY) return topIndex;
3691    int line = topIndex;
3692    if (y < topIndexY) {
3693        while (y < topIndexY && line > 0) {
3694            y += renderer.getLineHeight(--line);
3695        }
3696    } else {
3697        int lineCount = content.getLineCount();
3698        int lineHeight = renderer.getLineHeight(line);
3699        while (y - lineHeight >= topIndexY && line < lineCount - 1) {
3700            y -= lineHeight;
3701            lineHeight = renderer.getLineHeight(++line);
3702        }
3703    }
3704    return line;
3705}
3706/**
3707 * Returns the x, y location of the upper left corner of the character
3708 * bounding box at the specified offset in the text. The point is
3709 * relative to the upper left corner of the widget client area.
3710 *
3711 * @param offset offset relative to the start of the content.
3712 * 0 <= offset <= getCharCount()
3713 * @return x, y location of the upper left corner of the character
3714 * bounding box at the specified offset in the text.
3715 * @exception SWTException <ul>
3716 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3717 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3718 * </ul>
3719 * @exception IllegalArgumentException <ul>
3720 * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
3721 * </ul>
3722 */

3723public Point getLocationAtOffset(int offset) {
3724    checkWidget();
3725    if (offset < 0 || offset > getCharCount()) {
3726        SWT.error(SWT.ERROR_INVALID_RANGE);
3727    }
3728    return getPointAtOffset(offset);
3729}
3730/**
3731 * Returns the character offset of the first character of the given line.
3732 *
3733 * @param lineIndex index of the line, 0 based relative to the first
3734 * line in the content. 0 <= lineIndex < getLineCount(), except
3735 * lineIndex may always be 0
3736 * @return offset offset of the first character of the line, relative to
3737 * the beginning of the document. The first character of the document is
3738 * at offset 0.
3739 * When there are not any lines, getOffsetAtLine(0) is a valid call that
3740 * answers 0.
3741 * @exception SWTException <ul>
3742 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3743 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3744 * </ul>
3745 * @exception IllegalArgumentException <ul>
3746 * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
3747 * </ul>
3748 * @since 2.0
3749 */

3750public int getOffsetAtLine(int lineIndex) {
3751    checkWidget();
3752    if (lineIndex < 0 ||
3753        (lineIndex > 0 && lineIndex >= content.getLineCount())) {
3754        SWT.error(SWT.ERROR_INVALID_RANGE);
3755    }
3756    return content.getOffsetAtLine(lineIndex);
3757}
3758/**
3759 * Returns the offset of the character at the given location relative
3760 * to the first character in the document.
3761 * <p>
3762 * The return value reflects the character offset that the caret will
3763 * be placed at if a mouse click occurred at the specified location.
3764 * If the x coordinate of the location is beyond the center of a character
3765 * the returned offset will be behind the character.
3766 * </p>
3767 *
3768 * @param point the origin of character bounding box relative to
3769 * the origin of the widget client area.
3770 * @return offset of the character at the given location relative
3771 * to the first character in the document.
3772 * @exception SWTException <ul>
3773 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3774 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3775 * </ul>
3776 * @exception IllegalArgumentException <ul>
3777 * <li>ERROR_NULL_ARGUMENT when point is null</li>
3778 * <li>ERROR_INVALID_ARGUMENT when there is no character at the specified location</li>
3779 * </ul>
3780 */

3781public int getOffsetAtLocation(Point point) {
3782    checkWidget();
3783    if (point == null) {
3784        SWT.error(SWT.ERROR_NULL_ARGUMENT);
3785    }
3786    // is y above first line or is x before first column?
3787
if (point.y + getVerticalScrollOffset() < 0 || point.x + horizontalScrollOffset < 0) {
3788        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3789    }
3790    int bottomIndex = getLineIndex(clientAreaHeight);
3791    int height = getLinePixel(bottomIndex) + renderer.getLineHeight(bottomIndex);
3792    if (point.y > height) {
3793        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3794    }
3795    int lineIndex = getLineIndex(point.y);
3796    int lineOffset = content.getOffsetAtLine(lineIndex);
3797    TextLayout layout = renderer.getTextLayout(lineIndex);
3798    int[] trailing = new int[1];
3799    int x = point.x + horizontalScrollOffset - leftMargin ;
3800    int y = point.y - getLinePixel(lineIndex);
3801    int offsetInLine = layout.getOffset(x, y, trailing);
3802    String JavaDoc line = content.getLine(lineIndex);
3803    if (offsetInLine != line.length() - 1) {
3804        offsetInLine = Math.min(line.length(), offsetInLine + trailing[0]);
3805    }
3806    Rectangle rect = layout.getLineBounds(layout.getLineIndex(offsetInLine));
3807    renderer.disposeTextLayout(layout);
3808    if (x > rect.x + rect.width) {
3809        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3810    }
3811    return lineOffset + offsetInLine;
3812}
3813int getOffsetAtPoint(int x, int y) {
3814    int lineIndex = getLineIndex(y);
3815    y -= getLinePixel(lineIndex);
3816    return getOffsetAtPoint(x, y, lineIndex);
3817}
3818/**
3819 * Returns the offset at the specified x location in the specified line.
3820 *
3821 * @param x x location of the mouse location
3822 * @param line line the mouse location is in
3823 * @return the offset at the specified x location in the specified line,
3824 * relative to the beginning of the document
3825 */

3826int getOffsetAtPoint(int x, int y, int lineIndex) {
3827    TextLayout layout = renderer.getTextLayout(lineIndex);
3828    x += horizontalScrollOffset - leftMargin;
3829    int[] trailing = new int[1];
3830    int offsetInLine = layout.getOffset(x, y, trailing);
3831    caretAlignment = OFFSET_LEADING;
3832    if (trailing[0] != 0) {
3833        int lineInParagraph = layout.getLineIndex(offsetInLine + trailing[0]);
3834        //TODO handle bidi text
3835
int lineStart = layout.getLineOffsets()[lineInParagraph];
3836        if (offsetInLine + trailing[0] == lineStart) {
3837            offsetInLine += trailing[0];
3838            caretAlignment = PREVIOUS_OFFSET_TRAILING;
3839        } else {
3840            String JavaDoc line = content.getLine(lineIndex);
3841            int level;
3842            int offset = offsetInLine;
3843            while (offset > 0 && Character.isDigit(line.charAt(offset))) offset--;
3844            if (offset == 0 && Character.isDigit(line.charAt(offset))) {
3845                level = isMirrored() ? 1 : 0;
3846            } else {
3847                level = layout.getLevel(offset) & 0x1;
3848            }
3849            offsetInLine += trailing[0];
3850            int trailingLevel = layout.getLevel(offsetInLine) & 0x1;
3851            if ((level ^ trailingLevel) != 0) {
3852                caretAlignment = PREVIOUS_OFFSET_TRAILING;
3853            } else {
3854                caretAlignment = OFFSET_LEADING;
3855            }
3856        }
3857    }
3858    renderer.disposeTextLayout(layout);
3859    return offsetInLine + content.getOffsetAtLine(lineIndex);
3860}
3861/**
3862 * Returns the orientation of the receiver.
3863 *
3864 * @return the orientation style
3865 *
3866 * @exception SWTException <ul>
3867 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3868 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3869 * </ul>
3870 *
3871 * @since 2.1.2
3872 */

3873public int getOrientation () {
3874    checkWidget();
3875    return isMirrored() ? SWT.RIGHT_TO_LEFT : SWT.LEFT_TO_RIGHT;
3876}
3877/**
3878 * Returns the index of the last partially visible line.
3879 *
3880 * @return index of the last partially visible line.
3881 */

3882int getPartialBottomIndex() {
3883    if (isFixedLineHeight()) {
3884        int lineHeight = renderer.getLineHeight();
3885        int partialLineCount = Compatibility.ceil(clientAreaHeight, lineHeight);
3886        return Math.min(content.getLineCount(), topIndex + partialLineCount) - 1;
3887    }
3888    return getLineIndex(clientAreaHeight - bottomMargin);
3889}
3890/**
3891 * Returns the index of the first partially visible line.
3892 *
3893 * @return index of the first partially visible line.
3894 */

3895int getPartialTopIndex() {
3896    if (isFixedLineHeight()) {
3897        int lineHeight = renderer.getLineHeight();
3898        return getVerticalScrollOffset() / lineHeight;
3899    }
3900    return topIndexY <= 0 ? topIndex : topIndex - 1;
3901}
3902/**
3903 * Returns the content in the specified range using the platform line
3904 * delimiter to separate lines.
3905 *
3906 * @param writer the TextWriter to write line text into
3907 * @return the content in the specified range using the platform line
3908 * delimiter to separate lines as written by the specified TextWriter.
3909 */

3910String JavaDoc getPlatformDelimitedText(TextWriter writer) {
3911    int end = writer.getStart() + writer.getCharCount();
3912    int startLine = content.getLineAtOffset(writer.getStart());
3913    int endLine = content.getLineAtOffset(end);
3914    String JavaDoc endLineText = content.getLine(endLine);
3915    int endLineOffset = content.getOffsetAtLine(endLine);
3916    
3917    for (int i = startLine; i <= endLine; i++) {
3918        writer.writeLine(content.getLine(i), content.getOffsetAtLine(i));
3919        if (i < endLine) {
3920            writer.writeLineDelimiter(PlatformLineDelimiter);
3921        }
3922    }
3923    if (end > endLineOffset + endLineText.length()) {
3924        writer.writeLineDelimiter(PlatformLineDelimiter);
3925    }
3926    writer.close();
3927    return writer.toString();
3928}
3929/**
3930 * Returns all the ranges of text that have an associated StyleRange.
3931 * Returns an empty array if a LineStyleListener has been set.
3932 * Should not be called if a LineStyleListener has been set since the
3933 * listener maintains the styles.
3934 * <p>
3935 * The ranges array contains start and length pairs. Each pair refers to
3936 * the corresponding style in the styles array. For example, the pair
3937 * that starts at ranges[n] with length ranges[n+1] uses the style
3938 * at styles[n/2] returned by <code>getStyleRanges(int, int, boolean)</code>.
3939 * </p>
3940 *
3941 * @return the ranges or an empty array if a LineStyleListener has been set.
3942 *
3943 * @exception SWTException <ul>
3944 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3945 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3946 * </ul>
3947 *
3948 * @since 3.2
3949 *
3950 * @see #getStyleRanges(boolean)
3951 */

3952public int[] getRanges() {
3953    checkWidget();
3954    if (!isListening(LineGetStyle)) {
3955        int[] ranges = renderer.getRanges(0, content.getCharCount());
3956        if (ranges != null) return ranges;
3957    }
3958    return new int[0];
3959}
3960/**
3961 * Returns the ranges of text that have an associated StyleRange.
3962 * Returns an empty array if a LineStyleListener has been set.
3963 * Should not be called if a LineStyleListener has been set since the
3964 * listener maintains the styles.
3965 * <p>
3966 * The ranges array contains start and length pairs. Each pair refers to
3967 * the corresponding style in the styles array. For example, the pair
3968 * that starts at ranges[n] with length ranges[n+1] uses the style
3969 * at styles[n/2] returned by <code>getStyleRanges(int, int, boolean)</code>.
3970 * </p>
3971 *
3972 * @param start the start offset of the style ranges to return
3973 * @param length the number of style ranges to return
3974 *
3975 * @return the ranges or an empty array if a LineStyleListener has been set.
3976 *
3977 * @exception SWTException <ul>
3978 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3979 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3980 * </ul>
3981 * @exception IllegalArgumentException <ul>
3982 * <li>ERROR_INVALID_RANGE if start or length are outside the widget content</li>
3983 * </ul>
3984 *
3985 * @since 3.2
3986 *
3987 * @see #getStyleRanges(int, int, boolean)
3988 */

3989public int[] getRanges(int start, int length) {
3990    checkWidget();
3991    int contentLength = getCharCount();
3992    int end = start + length;
3993    if (start > end || start < 0 || end > contentLength) {
3994        SWT.error(SWT.ERROR_INVALID_RANGE);
3995    }
3996    if (!isListening(LineGetStyle)) {
3997        int[] ranges = renderer.getRanges(start, length);
3998        if (ranges != null) return ranges;
3999    }
4000    return new int[0];
4001}
4002/**
4003 * Returns the selection.
4004 * <p>
4005 * Text selections are specified in terms of caret positions. In a text
4006 * widget that contains N characters, there are N+1 caret positions,
4007 * ranging from 0..N
4008 * </p>
4009 *
4010 * @return start and end of the selection, x is the offset of the first
4011 * selected character, y is the offset after the last selected character.
4012 * The selection values returned are visual (i.e., x will always always be
4013 * <= y). To determine if a selection is right-to-left (RtoL) vs. left-to-right
4014 * (LtoR), compare the caretOffset to the start and end of the selection
4015 * (e.g., caretOffset == start of selection implies that the selection is RtoL).
4016 * @see #getSelectionRange
4017 * @exception SWTException <ul>
4018 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4019 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4020 * </ul>
4021 */

4022public Point getSelection() {
4023    checkWidget();
4024    return new Point(selection.x, selection.y);
4025}
4026/**
4027 * Returns the selection.
4028 *
4029 * @return start and length of the selection, x is the offset of the
4030 * first selected character, relative to the first character of the
4031 * widget content. y is the length of the selection.
4032 * The selection values returned are visual (i.e., length will always always be
4033 * positive). To determine if a selection is right-to-left (RtoL) vs. left-to-right
4034 * (LtoR), compare the caretOffset to the start and end of the selection
4035 * (e.g., caretOffset == start of selection implies that the selection is RtoL).
4036 * @exception SWTException <ul>
4037 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4038 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4039 * </ul>
4040 */

4041public Point getSelectionRange() {
4042    checkWidget();
4043    return new Point(selection.x, selection.y - selection.x);
4044}
4045/**
4046 * Returns the receiver's selection background color.
4047 *
4048 * @return the selection background color
4049 *
4050 * @exception SWTException <ul>
4051 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4052 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4053 * </ul>
4054 * @since 2.1
4055 */

4056public Color getSelectionBackground() {
4057    checkWidget();
4058    if (selectionBackground == null) {
4059        return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION);
4060    }
4061    return selectionBackground;
4062}
4063/**
4064 * Gets the number of selected characters.
4065 *
4066 * @return the number of selected characters.
4067 * @exception SWTException <ul>
4068 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4069 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4070 * </ul>
4071 */

4072public int getSelectionCount() {
4073    checkWidget();
4074    return getSelectionRange().y;
4075}
4076/**
4077 * Returns the receiver's selection foreground color.
4078 *
4079 * @return the selection foreground color
4080 *
4081 * @exception SWTException <ul>
4082 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4083 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4084 * </ul>
4085 * @since 2.1
4086 */

4087public Color getSelectionForeground() {
4088    checkWidget();
4089    if (selectionForeground == null) {
4090        return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT);
4091    }
4092    return selectionForeground;
4093}
4094/**
4095 * Returns the selected text.
4096 *
4097 * @return selected text, or an empty String if there is no selection.
4098 * @exception SWTException <ul>
4099 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4100 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4101 * </ul>
4102 */

4103public String JavaDoc getSelectionText() {
4104    checkWidget();
4105    return content.getTextRange(selection.x, selection.y - selection.x);
4106}
4107public int getStyle() {
4108    int style = super.getStyle();
4109    style &= ~(SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT | SWT.MIRRORED);
4110    if (isMirrored()) {
4111        style |= SWT.RIGHT_TO_LEFT | SWT.MIRRORED;
4112    } else {
4113        style |= SWT.LEFT_TO_RIGHT;
4114    }
4115    return style;
4116}
4117
4118/**
4119 * Returns the text segments that should be treated as if they
4120 * had a different direction than the surrounding text.
4121 *
4122 * @param lineOffset offset of the first character in the line.
4123 * 0 based from the beginning of the document.
4124 * @param line text of the line to specify bidi segments for
4125 * @return text segments that should be treated as if they had a
4126 * different direction than the surrounding text. Only the start
4127 * index of a segment is specified, relative to the start of the
4128 * line. Always starts with 0 and ends with the line length.
4129 * @exception IllegalArgumentException <ul>
4130 * <li>ERROR_INVALID_ARGUMENT - if the segment indices returned
4131 * by the listener do not start with 0, are not in ascending order,
4132 * exceed the line length or have duplicates</li>
4133 * </ul>
4134 */

4135int [] getBidiSegments(int lineOffset, String JavaDoc line) {
4136    if (!isBidi()) return null;
4137    if (!isListening(LineGetSegments)) {
4138        return getBidiSegmentsCompatibility(line, lineOffset);
4139    }
4140    StyledTextEvent event = sendLineEvent(LineGetSegments, lineOffset, line);
4141    int lineLength = line.length();
4142    int[] segments;
4143    if (event == null || event.segments == null || event.segments.length == 0) {
4144        segments = new int[] {0, lineLength};
4145    } else {
4146        int segmentCount = event.segments.length;
4147        
4148        // test segment index consistency
4149
if (event.segments[0] != 0) {
4150            SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4151        }
4152        for (int i = 1; i < segmentCount; i++) {
4153            if (event.segments[i] <= event.segments[i - 1] || event.segments[i] > lineLength) {
4154                SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4155            }
4156        }
4157        // ensure that last segment index is line end offset
4158
if (event.segments[segmentCount - 1] != lineLength) {
4159            segments = new int[segmentCount + 1];
4160            System.arraycopy(event.segments, 0, segments, 0, segmentCount);
4161            segments[segmentCount] = lineLength;
4162        } else {
4163            segments = event.segments;
4164        }
4165    }
4166    return segments;
4167}
4168/**
4169 * @see #getBidiSegments
4170 * Supports deprecated setBidiColoring API. Remove when API is removed.
4171 */

4172int [] getBidiSegmentsCompatibility(String JavaDoc line, int lineOffset) {
4173    int lineLength = line.length();
4174    if (!bidiColoring) {
4175        return new int[] {0, lineLength};
4176    }
4177    StyleRange [] styles = null;
4178    StyledTextEvent event = getLineStyleData(lineOffset, line);
4179    if (event != null) {
4180        styles = event.styles;
4181    } else {
4182        styles = renderer.getStyleRanges(lineOffset, lineLength, true);
4183    }
4184    if (styles == null || styles.length == 0) {
4185        return new int[] {0, lineLength};
4186    }
4187    int k=0, count = 1;
4188    while (k < styles.length && styles[k].start == 0 && styles[k].length == lineLength) {
4189        k++;
4190    }
4191    int[] offsets = new int[(styles.length - k) * 2 + 2];
4192    for (int i = k; i < styles.length; i++) {
4193        StyleRange style = styles[i];
4194        int styleLineStart = Math.max(style.start - lineOffset, 0);
4195        int styleLineEnd = Math.max(style.start + style.length - lineOffset, styleLineStart);
4196        styleLineEnd = Math.min (styleLineEnd, line.length ());
4197        if (i > 0 && count > 1 &&
4198            ((styleLineStart >= offsets[count-2] && styleLineStart <= offsets[count-1]) ||
4199             (styleLineEnd >= offsets[count-2] && styleLineEnd <= offsets[count-1])) &&
4200             style.similarTo(styles[i-1])) {
4201            offsets[count-2] = Math.min(offsets[count-2], styleLineStart);
4202            offsets[count-1] = Math.max(offsets[count-1], styleLineEnd);
4203        } else {
4204            if (styleLineStart > offsets[count - 1]) {
4205                offsets[count] = styleLineStart;
4206                count++;
4207            }
4208            offsets[count] = styleLineEnd;
4209            count++;
4210        }
4211    }
4212    // add offset for last non-colored segment in line, if any
4213
if (lineLength > offsets[count-1]) {
4214        offsets [count] = lineLength;
4215        count++;
4216    }
4217    if (count == offsets.length) {
4218        return offsets;
4219    }
4220    int [] result = new int [count];
4221    System.arraycopy (offsets, 0, result, 0, count);
4222    return result;
4223}
4224/**
4225 * Returns the style range at the given offset.
4226 * <p>
4227 * Returns null if a LineStyleListener has been set or if a style is not set
4228 * for the offset.
4229 * Should not be called if a LineStyleListener has been set since the
4230 * listener maintains the styles.
4231 * </p>
4232 *
4233 * @param offset the offset to return the style for.
4234 * 0 <= offset < getCharCount() must be true.
4235 * @return a StyleRange with start == offset and length == 1, indicating
4236 * the style at the given offset. null if a LineStyleListener has been set
4237 * or if a style is not set for the given offset.
4238 * @exception SWTException <ul>
4239 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4240 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4241 * </ul>
4242 * @exception IllegalArgumentException <ul>
4243 * <li>ERROR_INVALID_ARGUMENT when the offset is invalid</li>
4244 * </ul>
4245 */

4246public StyleRange getStyleRangeAtOffset(int offset) {
4247    checkWidget();
4248    if (offset < 0 || offset >= getCharCount()) {
4249        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4250    }
4251    if (!isListening(LineGetStyle)) {
4252        StyleRange[] ranges = renderer.getStyleRanges(offset, 1, true);
4253        if (ranges != null) return ranges[0];
4254    }
4255    return null;
4256}
4257/**
4258 * Returns the styles.
4259 * <p>
4260 * Returns an empty array if a LineStyleListener has been set.
4261 * Should not be called if a LineStyleListener has been set since the
4262 * listener maintains the styles.
4263 * <p></p>
4264 * Note: Because a StyleRange includes the start and length, the
4265 * same instance cannot occur multiple times in the array of styles.
4266 * If the same style attributes, such as font and color, occur in
4267 * multiple StyleRanges, <code>getStyleRanges(boolean)</code>
4268 * can be used to get the styles without the ranges.
4269 * </p>
4270 *
4271 * @return the styles or an empty array if a LineStyleListener has been set.
4272 *
4273 * @exception SWTException <ul>
4274 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4275 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4276 * </ul>
4277 *
4278 * @see #getStyleRanges(boolean)
4279 */

4280public StyleRange[] getStyleRanges() {
4281    checkWidget();
4282    return getStyleRanges(0, content.getCharCount(), true);
4283}
4284/**
4285 * Returns the styles.
4286 * <p>
4287 * Returns an empty array if a LineStyleListener has been set.
4288 * Should not be called if a LineStyleListener has been set since the
4289 * listener maintains the styles.
4290 * </p><p>
4291 * Note: When <code>includeRanges</code> is true, the start and length
4292 * fields of each StyleRange will be valid, however the StyleRange
4293 * objects may need to be cloned. When <code>includeRanges</code> is
4294 * false, <code>getRanges(int, int)</code> can be used to get the
4295 * associated ranges.
4296 * </p>
4297 *
4298 * @param includeRanges whether the start and length field of the StyleRanges should be set.
4299 *
4300 * @return the styles or an empty array if a LineStyleListener has been set.
4301 *
4302 * @exception SWTException <ul>
4303 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4304 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4305 * </ul>
4306 *
4307 * @since 3.2
4308 *
4309 * @see #getRanges(int, int)
4310 * @see #setStyleRanges(int[], StyleRange[])
4311 */

4312public StyleRange[] getStyleRanges(boolean includeRanges) {
4313    checkWidget();
4314    return getStyleRanges(0, content.getCharCount(), includeRanges);
4315}
4316/**
4317 * Returns the styles for the given text range.
4318 * <p>
4319 * Returns an empty array if a LineStyleListener has been set.
4320 * Should not be called if a LineStyleListener has been set since the
4321 * listener maintains the styles.
4322 * </p><p>
4323 * Note: Because the StyleRange includes the start and length, the
4324 * same instance cannot occur multiple times in the array of styles.
4325 * If the same style attributes, such as font and color, occur in
4326 * multiple StyleRanges, <code>getStyleRanges(int, int, boolean)</code>
4327 * can be used to get the styles without the ranges.
4328 * </p>
4329 * @param start the start offset of the style ranges to return
4330 * @param length the number of style ranges to return
4331 *
4332 * @return the styles or an empty array if a LineStyleListener has
4333 * been set. The returned styles will reflect the given range. The first
4334 * returned <code>StyleRange</code> will have a starting offset >= start
4335 * and the last returned <code>StyleRange</code> will have an ending
4336 * offset <= start + length - 1
4337 *
4338 * @exception SWTException <ul>
4339 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4340 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4341 * </ul>
4342 * @exception IllegalArgumentException <ul>
4343 * <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
4344 * </ul>
4345 *
4346 * @see #getStyleRanges(int, int, boolean)
4347 *
4348 * @since 3.0
4349 */

4350public StyleRange[] getStyleRanges(int start, int length) {
4351    checkWidget();
4352    return getStyleRanges(start, length, true);
4353}
4354/**
4355 * Returns the styles for the given text range.
4356 * <p>
4357 * Returns an empty array if a LineStyleListener has been set.
4358 * Should not be called if a LineStyleListener has been set since the
4359 * listener maintains the styles.
4360 * </p><p>
4361 * Note: When <code>includeRanges</code> is true, the start and length
4362 * fields of each StyleRange will be valid, however the StyleRange
4363 * objects may need to be cloned. When <code>includeRanges</code> is
4364 * false, <code>getRanges(int, int)</code> can be used to get the
4365 * associated ranges.
4366 * </p>
4367 *
4368 * @param start the start offset of the style ranges to return
4369 * @param length the number of style ranges to return
4370 * @param includeRanges whether the start and length field of the StyleRanges should be set.
4371 *
4372 * @return the styles or an empty array if a LineStyleListener has
4373 * been set. The returned styles will reflect the given range. The first
4374 * returned <code>StyleRange</code> will have a starting offset >= start
4375 * and the last returned <code>StyleRange</code> will have an ending
4376 * offset <= start + length - 1
4377 *
4378 * @exception SWTException <ul>
4379 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4380 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4381 * </ul>
4382 * @exception IllegalArgumentException <ul>
4383 * <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
4384 * </ul>
4385 *
4386 * @since 3.2
4387 *
4388 * @see #getRanges(int, int)
4389 * @see #setStyleRanges(int[], StyleRange[])
4390 */

4391public StyleRange[] getStyleRanges(int start, int length, boolean includeRanges) {
4392    checkWidget();
4393    int contentLength = getCharCount();
4394    int end = start + length;
4395    if (start > end || start < 0 || end > contentLength) {
4396        SWT.error(SWT.ERROR_INVALID_RANGE);
4397    }
4398    if (!isListening(LineGetStyle)) {
4399        StyleRange[] ranges = renderer.getStyleRanges(start, length, includeRanges);
4400        if (ranges != null) return ranges;
4401    }
4402    return new StyleRange[0];
4403}
4404/**
4405 * Returns the tab width measured in characters.
4406 *
4407 * @return tab width measured in characters
4408 * @exception SWTException <ul>
4409 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4410 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4411 * </ul>
4412 */

4413public int getTabs() {
4414    checkWidget();
4415    return tabLength;
4416}
4417/**
4418 * Returns a copy of the widget content.
4419 *
4420 * @return copy of the widget content
4421 * @exception SWTException <ul>
4422 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4423 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4424 * </ul>
4425 */

4426public String JavaDoc getText() {
4427    checkWidget();
4428    return content.getTextRange(0, getCharCount());
4429}
4430/**
4431 * Returns the widget content between the two offsets.
4432 *
4433 * @param start offset of the first character in the returned String
4434 * @param end offset of the last character in the returned String
4435 * @return widget content starting at start and ending at end
4436 * @see #getTextRange(int,int)
4437 * @exception SWTException <ul>
4438 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4439 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4440 * </ul>
4441 * @exception IllegalArgumentException <ul>
4442 * <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
4443 * </ul>
4444 */

4445public String JavaDoc getText(int start, int end) {
4446    checkWidget();
4447    int contentLength = getCharCount();
4448    if (start < 0 || start >= contentLength || end < 0 || end >= contentLength || start > end) {
4449        SWT.error(SWT.ERROR_INVALID_RANGE);
4450    }
4451    return content.getTextRange(start, end - start + 1);
4452}
4453/**
4454 * Returns the smallest bounding rectangle that includes the characters between two offsets.
4455 *
4456 * @param start offset of the first character included in the bounding box
4457 * @param end offset of the last character included in the bounding box
4458 * @return bounding box of the text between start and end
4459 * @exception SWTException <ul>
4460 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4461 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4462 * </ul>
4463 * @exception IllegalArgumentException <ul>
4464 * <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
4465 * </ul>
4466 * @since 3.1
4467 */

4468public Rectangle getTextBounds(int start, int end) {
4469    checkWidget();
4470    int contentLength = getCharCount();
4471    if (start < 0 || start >= contentLength || end < 0 || end >= contentLength || start > end) {
4472        SWT.error(SWT.ERROR_INVALID_RANGE);
4473    }
4474    int lineStart = content.getLineAtOffset(start);
4475    int lineEnd = content.getLineAtOffset(end);
4476    Rectangle rect;
4477    int y = getLinePixel(lineStart);
4478    int height = 0;
4479    int left = 0x7fffffff, right = 0;
4480    for (int i = lineStart; i <= lineEnd; i++) {
4481        int lineOffset = content.getOffsetAtLine(i);
4482        TextLayout layout = renderer.getTextLayout(i);
4483        if (layout.getText().length() > 0) {
4484            if (i == lineStart && i == lineEnd) {
4485                rect = layout.getBounds(start - lineOffset, end - lineOffset);
4486            } else if (i == lineStart) {
4487                String JavaDoc line = content.getLine(i);
4488                rect = layout.getBounds(start - lineOffset, line.length());
4489            } else if (i == lineEnd) {
4490                rect = layout.getBounds(0, end - lineOffset);
4491            } else {
4492                rect = layout.getBounds();
4493            }
4494            left = Math.min(left, rect.x);
4495            right = Math.max(right, rect.x + rect.width);
4496            height += rect.height;
4497        } else {
4498            height += renderer.getLineHeight();
4499        }
4500        renderer.disposeTextLayout(layout);
4501    }
4502    rect = new Rectangle (left, y, right-left, height);
4503    rect.x += leftMargin - horizontalScrollOffset;
4504    return rect;
4505}
4506/**
4507 * Returns the widget content starting at start for length characters.
4508 *
4509 * @param start offset of the first character in the returned String
4510 * @param length number of characters to return
4511 * @return widget content starting at start and extending length characters.
4512 * @exception SWTException <ul>
4513 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4514 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4515 * </ul>
4516 * @exception IllegalArgumentException <ul>
4517 * <li>ERROR_INVALID_RANGE when start and/or length are outside the widget content</li>
4518 * </ul>
4519 */

4520public String JavaDoc getTextRange(int start, int length) {
4521    checkWidget();
4522    int contentLength = getCharCount();
4523    int end = start + length;
4524    if (start > end || start < 0 || end > contentLength) {
4525        SWT.error(SWT.ERROR_INVALID_RANGE);
4526    }
4527    return content.getTextRange(start, length);
4528}
4529/**
4530 * Returns the maximum number of characters that the receiver is capable of holding.
4531 *
4532 * @return the text limit
4533 *
4534 * @exception SWTException <ul>
4535 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4536 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4537 * </ul>
4538 */

4539public int getTextLimit() {
4540    checkWidget();
4541    return textLimit;
4542}
4543/**
4544 * Gets the top index.
4545 * <p>
4546 * The top index is the index of the fully visible line that is currently
4547 * at the top of the widget or the topmost partially visible line if no line is fully visible.
4548 * The top index changes when the widget is scrolled. Indexing is zero based.
4549 * </p>
4550 *
4551 * @return the index of the top line
4552 * @exception SWTException <ul>
4553 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4554 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4555 * </ul>
4556 */

4557public int getTopIndex() {
4558    checkWidget();
4559    return topIndex;
4560}
4561/**
4562 * Gets the top pixel.
4563 * <p>
4564 * The top pixel is the pixel position of the line that is
4565 * currently at the top of the widget. The text widget can be scrolled by pixels
4566 * by dragging the scroll thumb so that a partial line may be displayed at the top
4567 * the widget. The top pixel changes when the widget is scrolled. The top pixel
4568 * does not include the widget trimming.
4569 * </p>
4570 *
4571 * @return pixel position of the top line
4572 * @exception SWTException <ul>
4573 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4574 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4575 * </ul>
4576 */

4577public int getTopPixel() {
4578    checkWidget();
4579    return getVerticalScrollOffset();
4580}
4581/**
4582 * Returns the vertical scroll increment.
4583 *
4584 * @return vertical scroll increment.
4585 */

4586int getVerticalIncrement() {
4587    return renderer.getLineHeight();
4588}
4589int getVerticalScrollOffset() {
4590    if (verticalScrollOffset == -1) {
4591        renderer.calculate(0, topIndex);
4592        int height = 0;
4593        for (int i = 0; i < topIndex; i++) {
4594            height += renderer.getLineHeight(i);
4595        }
4596        height -= topIndexY;
4597        verticalScrollOffset = height;
4598    }
4599    return verticalScrollOffset;
4600}
4601int getVisualLineIndex(TextLayout layout, int offsetInLine) {
4602    int lineIndex = layout.getLineIndex(offsetInLine);
4603    int[] offsets = layout.getLineOffsets();
4604    if (lineIndex != 0 && offsetInLine == offsets[lineIndex]) {
4605        int lineY = layout.getLineBounds(lineIndex).y;
4606        int caretY = getCaret().getLocation().y - topMargin - getLinePixel(getCaretLine());
4607        if (lineY > caretY) lineIndex--;
4608    }
4609    return lineIndex;
4610}
4611int getCaretDirection() {
4612    if (!isBidiCaret()) return SWT.DEFAULT;
4613    if (!updateCaretDirection && caretDirection != SWT.NULL) return caretDirection;
4614    updateCaretDirection = false;
4615    int caretLine = getCaretLine();
4616    int lineOffset = content.getOffsetAtLine(caretLine);
4617    String JavaDoc line = content.getLine(caretLine);
4618    int offset = caretOffset - lineOffset;
4619    int lineLength = line.length();
4620    if (lineLength == 0) return isMirrored() ? SWT.RIGHT : SWT.LEFT;
4621    if (caretAlignment == PREVIOUS_OFFSET_TRAILING && offset > 0) offset--;
4622    if (offset == lineLength && offset > 0) offset--;
4623    while (offset > 0 && Character.isDigit(line.charAt(offset))) offset--;
4624    if (offset == 0 && Character.isDigit(line.charAt(offset))) {
4625        return isMirrored() ? SWT.RIGHT : SWT.LEFT;
4626    }
4627    TextLayout layout = renderer.getTextLayout(caretLine);
4628    int level = layout.getLevel(offset);
4629    renderer.disposeTextLayout(layout);
4630    return ((level & 1) != 0) ? SWT.RIGHT : SWT.LEFT;
4631}
4632/*
4633 * Returns the index of the line the caret is on.
4634 */

4635int getCaretLine() {
4636    return content.getLineAtOffset(caretOffset);
4637}
4638int getWrapWidth () {
4639    if (wordWrap && !isSingleLine()) {
4640        int width = clientAreaWidth - leftMargin - rightMargin;
4641        return width > 0 ? width : 1;
4642    }
4643    return -1;
4644}
4645int getWordNext (int offset, int movement) {
4646    int newOffset, lineOffset;
4647    String JavaDoc lineText;
4648    if (offset >= getCharCount()) {
4649        newOffset = offset;
4650        int lineIndex = content.getLineCount() - 1;
4651        lineOffset = content.getOffsetAtLine(lineIndex);
4652        lineText = content.getLine(lineIndex);
4653    } else {
4654        int lineIndex = content.getLineAtOffset(offset);
4655        lineOffset = content.getOffsetAtLine(lineIndex);
4656        lineText = content.getLine(lineIndex);
4657        int lineLength = lineText.length();
4658        if (offset == lineOffset + lineLength) {
4659            newOffset = content.getOffsetAtLine(lineIndex + 1);
4660        } else {
4661            TextLayout layout = renderer.getTextLayout(lineIndex);
4662            newOffset = lineOffset + layout.getNextOffset(offset - lineOffset, movement);
4663            renderer.disposeTextLayout(layout);
4664        }
4665    }
4666    return sendWordBoundaryEvent(WordNext, movement, offset, newOffset, lineText, lineOffset);
4667}
4668int getWordPrevious(int offset, int movement) {
4669    int newOffset, lineOffset;
4670    String JavaDoc lineText;
4671    if (offset <= 0) {
4672        newOffset = 0;
4673        int lineIndex = content.getLineAtOffset(newOffset);
4674        lineOffset = content.getOffsetAtLine(lineIndex);
4675        lineText = content.getLine(lineIndex);
4676    } else {
4677        int lineIndex = content.getLineAtOffset(offset);
4678        lineOffset = content.getOffsetAtLine(lineIndex);
4679        lineText = content.getLine(lineIndex);
4680        if (offset == lineOffset) {
4681            String JavaDoc nextLineText = content.getLine(lineIndex - 1);
4682            int nextLineOffset = content.getOffsetAtLine(lineIndex - 1);
4683            newOffset = nextLineOffset + nextLineText.length();
4684        } else {
4685            TextLayout layout = renderer.getTextLayout(lineIndex);
4686            newOffset = lineOffset + layout.getPreviousOffset(offset - lineOffset, movement);
4687            renderer.disposeTextLayout(layout);
4688        }
4689    }
4690    return sendWordBoundaryEvent(WordPrevious, movement, offset, newOffset, lineText, lineOffset);
4691}
4692/**
4693 * Returns whether the widget wraps lines.
4694 *
4695 * @return true if widget wraps lines, false otherwise
4696 * @since 2.0
4697 */

4698public boolean getWordWrap() {
4699    checkWidget();
4700    return wordWrap;
4701}
4702/**
4703 * Returns the location of the given offset.
4704 * <p>
4705 * <b>NOTE:</b> Does not return correct values for true italic fonts (vs. slanted fonts).
4706 * </p>
4707 *
4708 * @return location of the character at the given offset in the line.
4709 */

4710Point getPointAtOffset(int offset) {
4711    int lineIndex = content.getLineAtOffset(offset);
4712    String JavaDoc line = content.getLine(lineIndex);
4713    int lineOffset = content.getOffsetAtLine(lineIndex);
4714    int offsetInLine = offset - lineOffset;
4715    int lineLength = line.length();
4716    if (lineIndex < content.getLineCount() - 1) {
4717        int endLineOffset = content.getOffsetAtLine(lineIndex + 1) - 1;
4718        if (lineLength < offsetInLine && offsetInLine <= endLineOffset) {
4719            offsetInLine = lineLength;
4720        }
4721    }
4722    Point point;
4723    TextLayout layout = renderer.getTextLayout(lineIndex);
4724    if (lineLength != 0 && offsetInLine <= lineLength) {
4725        if (offsetInLine == lineLength) {
4726            point = layout.getLocation(offsetInLine - 1, true);
4727        } else {
4728            switch (caretAlignment) {
4729                case OFFSET_LEADING:
4730                    point = layout.getLocation(offsetInLine, false);
4731                    break;
4732                case PREVIOUS_OFFSET_TRAILING:
4733                default:
4734                    if (offsetInLine == 0) {
4735                        point = layout.getLocation(offsetInLine, false);
4736                    } else {
4737                        point = layout.getLocation(offsetInLine - 1, true);
4738                    }
4739                    break;
4740            }
4741        }
4742    } else {
4743        point = new Point(layout.getIndent(), 0);
4744    }
4745    renderer.disposeTextLayout(layout);
4746    point.x += leftMargin - horizontalScrollOffset;
4747    point.y += getLinePixel(lineIndex);
4748    return point;
4749}
4750/**
4751 * Inserts a string. The old selection is replaced with the new text.
4752 *
4753 * @param string the string
4754 * @see #replaceTextRange(int,int,String)
4755 * @exception SWTException <ul>
4756 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4757 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4758 * </ul>
4759 * @exception IllegalArgumentException <ul>
4760 * <li>ERROR_NULL_ARGUMENT when string is null</li>
4761 * </ul>
4762 */

4763public void insert(String JavaDoc string) {
4764    checkWidget();
4765    if (string == null) {
4766        SWT.error(SWT.ERROR_NULL_ARGUMENT);
4767    }
4768    Point sel = getSelectionRange();
4769    replaceTextRange(sel.x, sel.y, string);
4770}
4771/**
4772 * Creates content change listeners and set the default content model.
4773 */

4774void installDefaultContent() {
4775    textChangeListener = new TextChangeListener() {
4776        public void textChanging(TextChangingEvent event) {
4777            handleTextChanging(event);
4778        }
4779        public void textChanged(TextChangedEvent event) {
4780            handleTextChanged(event);
4781        }
4782        public void textSet(TextChangedEvent event) {
4783            handleTextSet(event);
4784        }
4785    };
4786    content = new DefaultContent();
4787    content.addTextChangeListener(textChangeListener);
4788}
4789/**
4790 * Adds event listeners
4791 */

4792void installListeners() {
4793    ScrollBar verticalBar = getVerticalBar();
4794    ScrollBar horizontalBar = getHorizontalBar();
4795    
4796    listener = new Listener() {
4797        public void handleEvent(Event event) {
4798            switch (event.type) {
4799                case SWT.Dispose: handleDispose(event); break;
4800                case SWT.KeyDown: handleKeyDown(event); break;
4801                case SWT.KeyUp: handleKeyUp(event); break;
4802                case SWT.MouseDown: handleMouseDown(event); break;
4803                case SWT.MouseUp: handleMouseUp(event); break;
4804                case SWT.MouseMove: handleMouseMove(event); break;
4805                case SWT.Paint: handlePaint(event); break;
4806                case SWT.Resize: handleResize(event); break;
4807                case SWT.Traverse: handleTraverse(event); break;
4808            }
4809        }
4810    };
4811    addListener(SWT.Dispose, listener);
4812    addListener(SWT.KeyDown, listener);
4813    addListener(SWT.KeyUp, listener);
4814    addListener(SWT.MouseDown, listener);
4815    addListener(SWT.MouseUp, listener);
4816    addListener(SWT.MouseMove, listener);
4817    addListener(SWT.Paint, listener);
4818    addListener(SWT.Resize, listener);
4819    addListener(SWT.Traverse, listener);
4820    if (verticalBar != null) {
4821        verticalBar.addListener(SWT.Selection, new Listener() {
4822            public void handleEvent(Event event) {
4823                handleVerticalScroll(event);
4824            }
4825        });
4826    }
4827    if (horizontalBar != null) {
4828        horizontalBar.addListener(SWT.Selection, new Listener() {
4829            public void handleEvent(Event event) {
4830                handleHorizontalScroll(event);
4831            }
4832        });
4833    }
4834}
4835void internalRedrawRange(int start, int length) {
4836    if (length <= 0) return;
4837    int end = start + length;
4838    int startLine = content.getLineAtOffset(start);
4839    int endLine = content.getLineAtOffset(end);
4840    int partialBottomIndex = getPartialBottomIndex();
4841    int partialTopIndex = getPartialTopIndex();
4842    if (startLine > partialBottomIndex || endLine < partialTopIndex) {
4843        return;
4844    }
4845    if (partialTopIndex > startLine) {
4846        startLine = partialTopIndex;
4847        start = 0;
4848    } else {
4849        start -= content.getOffsetAtLine(startLine);
4850    }
4851    if (partialBottomIndex < endLine) {
4852        endLine = partialBottomIndex + 1;
4853        end = 0;
4854    } else {
4855        end -= content.getOffsetAtLine(endLine);
4856    }
4857
4858    TextLayout layout = renderer.getTextLayout(startLine);
4859    int lineX = leftMargin - horizontalScrollOffset, startLineY = getLinePixel(startLine);
4860    int[] offsets = layout.getLineOffsets();
4861    int startIndex = layout.getLineIndex(Math.min(start, layout.getText().length()));
4862    
4863    /* Redraw end of line before start line if wrapped and start offset is first char */
4864    if (wordWrap && startIndex > 0 && offsets[startIndex] == start) {
4865        Rectangle rect = layout.getLineBounds(startIndex - 1);
4866        rect.x = rect.width;
4867        rect.width = clientAreaWidth - rightMargin - rect.x;
4868        rect.x += lineX;
4869        rect.y += startLineY;
4870        super.redraw(rect.x, rect.y, rect.width, rect.height, false);
4871    }
4872    
4873    if (startLine == endLine) {
4874        int endIndex = layout.getLineIndex(Math.min(end, layout.getText().length()));
4875        if (startIndex == endIndex) {
4876            /* Redraw rect between start and end offset if start and end offsets are in same wrapped line */
4877            Rectangle rect = layout.getBounds(start, end - 1);
4878            rect.x += lineX;
4879            rect.y += startLineY;
4880            super.redraw(rect.x, rect.y, rect.width, rect.height, false);
4881            renderer.disposeTextLayout(layout);
4882            return;
4883        }
4884    }
4885
4886    /* Redraw start line from the start offset to the end of client area */
4887    Rectangle startRect = layout.getBounds(start, offsets[startIndex + 1] - 1);
4888    if (startRect.height == 0) {
4889        Rectangle bounds = layout.getLineBounds(startIndex);
4890        startRect.x = bounds.width;
4891        startRect.y = bounds.y;
4892        startRect.height = bounds.height;
4893    }
4894    startRect.x += lineX;
4895    startRect.y += startLineY;
4896    startRect.width = clientAreaWidth - rightMargin - startRect.x;
4897    super.redraw(startRect.x, startRect.y, startRect.width, startRect.height, false);
4898
4899    /* Redraw end line from the beginning of the line to the end offset */
4900    if (startLine != endLine) {
4901        renderer.disposeTextLayout(layout);
4902        layout = renderer.getTextLayout(endLine);
4903        offsets = layout.getLineOffsets();
4904    }
4905    int endIndex = layout.getLineIndex(Math.min(end, layout.getText().length()));
4906    Rectangle endRect = layout.getBounds(offsets[endIndex], end - 1);
4907    if (endRect.height == 0) {
4908        Rectangle bounds = layout.getLineBounds(endIndex);
4909        endRect.y = bounds.y;
4910        endRect.height = bounds.height;
4911    }
4912    endRect.x += lineX;
4913    endRect.y += getLinePixel(endLine);
4914    super.redraw(endRect.x, endRect.y, endRect.width, endRect.height, false);
4915    renderer.disposeTextLayout(layout);
4916
4917    /* Redraw all lines in between start and end line */
4918    int y = startRect.y + startRect.height;
4919    if (endRect.y > y) {
4920        super.redraw(leftMargin, y, clientAreaWidth - rightMargin - leftMargin, endRect.y - y, false);
4921    }
4922}
4923/**
4924 * Frees resources.
4925 */

4926void handleDispose(Event event) {
4927    removeListener(SWT.Dispose, listener);
4928    notifyListeners(SWT.Dispose, event);
4929    event.type = SWT.None;
4930
4931    clipboard.dispose();
4932    if (renderer != null) {
4933        renderer.dispose();
4934        renderer = null;
4935    }
4936    if (content != null) {
4937        content.removeTextChangeListener(textChangeListener);
4938        content = null;
4939    }
4940    if (defaultCaret != null) {
4941        defaultCaret.dispose();
4942        defaultCaret = null;
4943    }
4944    if (leftCaretBitmap != null) {
4945        leftCaretBitmap.dispose();
4946        leftCaretBitmap = null;
4947    }
4948    if (rightCaretBitmap != null) {
4949        rightCaretBitmap.dispose();
4950        rightCaretBitmap = null;
4951    }
4952    if (isBidiCaret()) {
4953        BidiUtil.removeLanguageListener(handle);
4954    }
4955    selectionBackground = null;
4956    selectionForeground = null;
4957    textChangeListener = null;
4958    selection = null;
4959    doubleClickSelection = null;
4960    keyActionMap = null;
4961    background = null;
4962    foreground = null;
4963    clipboard = null;
4964}
4965/**
4966 * Scrolls the widget horizontally.
4967 */

4968void handleHorizontalScroll(Event event) {
4969    int scrollPixel = getHorizontalBar().getSelection() - horizontalScrollOffset;
4970    scrollHorizontal(scrollPixel, false);
4971}
4972/**
4973 * If an action has been registered for the key stroke execute the action.
4974 * Otherwise, if a character has been entered treat it as new content.
4975 *
4976 * @param event keyboard event
4977 */

4978void handleKey(Event event) {
4979    int action;
4980    caretAlignment = PREVIOUS_OFFSET_TRAILING;
4981    if (event.keyCode != 0) {
4982        // special key pressed (e.g., F1)
4983
action = getKeyBinding(event.keyCode | event.stateMask);
4984    } else {
4985        // character key pressed
4986
action = getKeyBinding(event.character | event.stateMask);
4987        if (action == SWT.NULL) {
4988            // see if we have a control character
4989
if ((event.stateMask & SWT.CTRL) != 0 && (event.character >= 0) && event.character <= 31) {
4990                // get the character from the CTRL+char sequence, the control
4991
// key subtracts 64 from the value of the key that it modifies
4992
int c = event.character + 64;
4993                action = getKeyBinding(c | event.stateMask);
4994            }
4995        }
4996    }
4997    if (action == SWT.NULL) {
4998        boolean ignore = false;
4999        
5000        if (IS_CARBON) {
5001            // Ignore accelerator key combinations (we do not want to
5002
// insert a character in the text in this instance). Do not
5003
// ignore COMMAND+ALT combinations since that key sequence
5004
// produces characters on the mac.
5005
ignore = (event.stateMask ^ SWT.COMMAND) == 0 ||
5006                    (event.stateMask ^ (SWT.COMMAND | SWT.SHIFT)) == 0;
5007        } else if (IS_MOTIF) {
5008            // Ignore accelerator key combinations (we do not want to
5009
// insert a character in the text in this instance). Do not
5010
// ignore ALT combinations since this key sequence
5011
// produces characters on motif.
5012
ignore = (event.stateMask ^ SWT.CTRL) == 0 ||
5013                    (event.stateMask ^ (SWT.CTRL | SWT.SHIFT)) == 0;
5014        } else {
5015            // Ignore accelerator key combinations (we do not want to
5016
// insert a character in the text in this instance). Don't
5017
// ignore CTRL+ALT combinations since that is the Alt Gr
5018
// key on some keyboards. See bug 20953.
5019
ignore = (event.stateMask ^ SWT.ALT) == 0 ||
5020                    (event.stateMask ^ SWT.CTRL) == 0 ||
5021                    (event.stateMask ^ (SWT.ALT | SWT.SHIFT)) == 0 ||
5022                    (event.stateMask ^ (SWT.CTRL | SWT.SHIFT)) == 0;
5023        }
5024        // -ignore anything below SPACE except for line delimiter keys and tab.
5025
// -ignore DEL
5026
if (!ignore && event.character > 31 && event.character != SWT.DEL ||
5027            event.character == SWT.CR || event.character == SWT.LF ||
5028            event.character == TAB) {
5029            doContent(event.character);
5030            update();
5031        }
5032    } else {
5033        invokeAction(action);
5034    }
5035}
5036/**
5037 * If a VerifyKey listener exists, verify that the key that was entered
5038 * should be processed.
5039 *
5040 * @param event keyboard event
5041 */

5042void handleKeyDown(Event event) {
5043    if (clipboardSelection == null) {
5044        clipboardSelection = new Point(selection.x, selection.y);
5045    }
5046    
5047    Event verifyEvent = new Event();
5048    verifyEvent.character = event.character;
5049    verifyEvent.keyCode = event.keyCode;
5050    verifyEvent.stateMask = event.stateMask;
5051    verifyEvent.doit = true;
5052    notifyListeners(VerifyKey, verifyEvent);
5053    if (verifyEvent.doit) {
5054        handleKey(event);
5055    }
5056}
5057/**
5058 * Update the Selection Clipboard.
5059 *
5060 * @param event keyboard event
5061 */

5062void handleKeyUp(Event event) {
5063    if (clipboardSelection != null) {
5064        if (clipboardSelection.x != selection.x || clipboardSelection.y != selection.y) {
5065            try {
5066                if (selection.y - selection.x > 0) {
5067                    setClipboardContent(selection.x, selection.y - selection.x, DND.SELECTION_CLIPBOARD);
5068                }
5069            } catch (SWTError error) {
5070                // Copy to clipboard failed. This happens when another application
5071
// is accessing the clipboard while we copy. Ignore the error.
5072
// Fixes 1GDQAVN
5073
// Rethrow all other errors. Fixes bug 17578.
5074
if (error.code != DND.ERROR_CANNOT_SET_CLIPBOARD) {
5075                    throw error;
5076                }
5077            }
5078        }
5079    }
5080    clipboardSelection = null;
5081}
5082/**
5083 * Updates the caret location and selection if mouse button 1 has been
5084 * pressed.
5085 */

5086void handleMouseDown(Event event) {
5087    //force focus (object support)
5088
forceFocus();
5089        
5090    //drag detect
5091
if (dragDetect && checkDragDetect(event)) return;
5092        
5093    //paste clipboard selection
5094
if (event.button == 2) {
5095        String JavaDoc text = (String JavaDoc)getClipboardContent(DND.SELECTION_CLIPBOARD);
5096        if (text != null && text.length() > 0) {
5097            // position cursor
5098
doMouseLocationChange(event.x, event.y, false);
5099            // insert text
5100
Event e = new Event();
5101            e.start = selection.x;
5102            e.end = selection.y;
5103            e.text = getModelDelimitedText(text);
5104            sendKeyEvent(e);
5105        }
5106    }
5107    
5108    //set selection
5109
if ((event.button != 1) || (IS_CARBON && (event.stateMask & SWT.MOD4) != 0)) {
5110        return;
5111    }
5112    clickCount = event.count;
5113    if (clickCount == 1) {
5114        boolean select = (event.stateMask & SWT.MOD2) != 0;
5115        doMouseLocationChange(event.x, event.y, select);
5116    } else {
5117        if (doubleClickEnabled) {
5118            clearSelection(false);
5119            int offset = getOffsetAtPoint(event.x, event.y);
5120            int lineIndex = content.getLineAtOffset(offset);
5121            int lineOffset = content.getOffsetAtLine(lineIndex);
5122            int lineEnd = content.getCharCount();
5123            if (lineIndex + 1 < content.getLineCount()) {
5124                lineEnd = content.getOffsetAtLine(lineIndex + 1);
5125            }
5126            int start, end;
5127            if ((clickCount & 1) == 0) {
5128                start = Math.max(0, getWordPrevious(offset, SWT.MOVEMENT_WORD_START));
5129                end = Math.min(content.getCharCount(), getWordNext(start, SWT.MOVEMENT_WORD_END));
5130            } else {
5131                start = lineOffset;
5132                end = lineEnd;
5133            }
5134            selection.x = selection.y = start;
5135            selectionAnchor = -1;
5136            caretOffset = end;
5137            showCaret();
5138            doMouseSelection();
5139            doubleClickSelection = new Point(selection.x, selection.y);
5140        }
5141    }
5142}
5143/**
5144 * Updates the caret location and selection if mouse button 1 is pressed
5145 * during the mouse move.
5146 */

5147void handleMouseMove(Event event) {
5148    if (clickCount == 0) return;
5149    doMouseLocationChange(event.x, event.y, true);
5150    update();
5151    doAutoScroll(event);
5152}
5153/**
5154 * Autoscrolling ends when the mouse button is released.
5155 */

5156void handleMouseUp(Event event) {
5157    clickCount = 0;
5158    endAutoScroll();
5159    if (event.button == 1) {
5160        try {
5161            if (selection.y - selection.x > 0) {
5162                setClipboardContent(selection.x, selection.y - selection.x, DND.SELECTION_CLIPBOARD);
5163            }
5164        } catch (SWTError error) {
5165            // Copy to clipboard failed. This happens when another application
5166
// is accessing the clipboard while we copy. Ignore the error.
5167
// Fixes 1GDQAVN
5168
// Rethrow all other errors. Fixes bug 17578.
5169
if (error.code != DND.ERROR_CANNOT_SET_CLIPBOARD) {
5170                throw error;
5171            }
5172        }
5173    }
5174}
5175/**
5176 * Renders the invalidated area specified in the paint event.
5177 *
5178 * @param event paint event
5179 */

5180void handlePaint(Event event) {
5181    if (event.width == 0 || event.height == 0) return;
5182    if (clientAreaWidth == 0 || clientAreaHeight == 0) return;
5183
5184    int startLine = getLineIndex(event.y);
5185    int y = getLinePixel(startLine);
5186    int endY = event.y + event.height;
5187    GC gc = event.gc;
5188    Color background = getBackground();
5189    Color foreground = getForeground();
5190    if (endY > 0) {
5191        int lineCount = isSingleLine() ? 1 : content.getLineCount();
5192        int x = leftMargin - horizontalScrollOffset;
5193        for (int i = startLine; y < endY && i < lineCount; i++) {
5194            y += renderer.drawLine(i, x, y, gc, background, foreground);
5195        }
5196        if (y < endY) {
5197            gc.setBackground(background);
5198            drawBackground(gc, 0, y, clientAreaWidth, endY - y);
5199        }
5200    }
5201    // fill the margin background
5202
gc.setBackground(background);
5203    if (topMargin > 0) {
5204        drawBackground(gc, 0, 0, clientAreaWidth, topMargin);
5205    }
5206    if (bottomMargin > 0) {
5207        drawBackground(gc, 0, clientAreaHeight - bottomMargin, clientAreaWidth, bottomMargin);
5208    }
5209    if (leftMargin > 0) {
5210        drawBackground(gc, 0, 0, leftMargin, clientAreaHeight);
5211    }
5212    if (rightMargin > 0) {
5213        drawBackground(gc, clientAreaWidth - rightMargin, 0, rightMargin, clientAreaHeight);
5214    }
5215}
5216/**
5217 * Recalculates the scroll bars. Rewraps all lines when in word
5218 * wrap mode.
5219 *
5220 * @param event resize event
5221 */

5222void handleResize(Event event) {
5223    int oldHeight = clientAreaHeight;
5224    int oldWidth = clientAreaWidth;
5225    Rectangle clientArea = getClientArea();
5226    clientAreaHeight = clientArea.height;
5227    clientAreaWidth = clientArea.width;
5228    /* Redraw the old or new right/bottom margin if needed */
5229    if (oldWidth != clientAreaWidth) {
5230        if (rightMargin > 0) {
5231            int x = (oldWidth < clientAreaWidth ? oldWidth : clientAreaWidth) - rightMargin;
5232            super.redraw(x, 0, rightMargin, oldHeight, false);
5233        }
5234    }
5235    if (oldHeight != clientAreaHeight) {
5236        if (bottomMargin > 0) {
5237            int y = (oldHeight < clientAreaHeight ? oldHeight : clientAreaHeight) - bottomMargin;
5238            super.redraw(0, y, oldWidth, bottomMargin, false);
5239        }
5240    }
5241    if (wordWrap) {
5242        if (oldWidth != clientAreaWidth) {
5243            renderer.reset(0, content.getLineCount());
5244            verticalScrollOffset = -1;
5245            renderer.calculateIdle();
5246            super.redraw();
5247        }
5248        if (oldHeight != clientAreaHeight) {
5249            if (oldHeight == 0) topIndexY = 0;
5250            setScrollBars(true);
5251        }
5252        setCaretLocation();
5253    } else {
5254        renderer.calculateClientArea();
5255        setScrollBars(true);
5256        claimRightFreeSpace();
5257        // StyledText allows any value for horizontalScrollOffset when clientArea is zero
5258
// in setHorizontalPixel() and setHorisontalOffset(). Fixes bug 168429.
5259
if (clientAreaWidth != 0) {
5260            ScrollBar horizontalBar = getHorizontalBar();
5261            if (horizontalBar != null && horizontalBar.getVisible()) {
5262                if (horizontalScrollOffset != horizontalBar.getSelection()) {
5263                    horizontalBar.setSelection(horizontalScrollOffset);
5264                    horizontalScrollOffset = horizontalBar.getSelection();
5265                }
5266            }
5267        }
5268    }
5269    claimBottomFreeSpace();
5270    //TODO FIX TOP INDEX DURING RESIZE
5271
// if (oldHeight != clientAreaHeight || wordWrap) {
5272
// calculateTopIndex(0);
5273
// }
5274
}
5275/**
5276 * Updates the caret position and selection and the scroll bars to reflect
5277 * the content change.
5278 */

5279void handleTextChanged(TextChangedEvent event) {
5280    int firstLine = content.getLineAtOffset(lastTextChangeStart);
5281    resetCache(firstLine, 0);
5282    if (!isFixedLineHeight() && topIndex > firstLine) {
5283        topIndex = firstLine;
5284        topIndexY = 0;
5285        super.redraw();
5286    } else {
5287        int lastLine = firstLine + lastTextChangeNewLineCount;
5288        int firstLineTop = getLinePixel(firstLine);
5289        int newLastLineBottom = getLinePixel(lastLine + 1);
5290        if (lastLineBottom != newLastLineBottom) {
5291            super.redraw();
5292            if (wordWrap) setCaretLocation();
5293        } else {
5294            super.redraw(0, firstLineTop, clientAreaWidth, newLastLineBottom - firstLineTop, false);
5295            redrawLinesBullet(renderer.redrawLines);
5296        }
5297    }
5298    renderer.redrawLines = null;
5299    // update selection/caret location after styles have been changed.
5300
// otherwise any text measuring could be incorrect
5301
//
5302
// also, this needs to be done after all scrolling. Otherwise,
5303
// selection redraw would be flushed during scroll which is wrong.
5304
// in some cases new text would be drawn in scroll source area even
5305
// though the intent is to scroll it.
5306
updateSelection(lastTextChangeStart, lastTextChangeReplaceCharCount, lastTextChangeNewCharCount);
5307    if (lastTextChangeReplaceLineCount > 0 || wordWrap) {
5308        claimBottomFreeSpace();
5309    }
5310    if (lastTextChangeReplaceCharCount > 0) {
5311        claimRightFreeSpace();
5312    }
5313}
5314/**
5315 * Updates the screen to reflect a pending content change.
5316 *
5317 * @param event .start the start offset of the change
5318 * @param event .newText text that is going to be inserted or empty String
5319 * if no text will be inserted
5320 * @param event .replaceCharCount length of text that is going to be replaced
5321 * @param event .newCharCount length of text that is going to be inserted
5322 * @param event .replaceLineCount number of lines that are going to be replaced
5323 * @param event .newLineCount number of new lines that are going to be inserted
5324 */

5325void handleTextChanging(TextChangingEvent event) {
5326    if (event.replaceCharCount < 0) {
5327        event.start += event.replaceCharCount;
5328        event.replaceCharCount *= -1;
5329    }
5330    lastTextChangeStart = event.start;
5331    lastTextChangeNewLineCount = event.newLineCount;
5332    lastTextChangeNewCharCount = event.newCharCount;
5333    lastTextChangeReplaceLineCount = event.replaceLineCount;
5334    lastTextChangeReplaceCharCount = event.replaceCharCount;
5335    int lineIndex = content.getLineAtOffset(event.start);
5336    int srcY = getLinePixel(lineIndex + event.replaceLineCount + 1);
5337    int destY = getLinePixel(lineIndex + 1) + event.newLineCount * renderer.getLineHeight();
5338    lastLineBottom = destY;
5339    if (srcY < 0 && destY < 0) {
5340        lastLineBottom += srcY - destY;
5341        verticalScrollOffset += destY - srcY;
5342        calculateTopIndex(destY - srcY);
5343        setScrollBars(true);
5344    } else {
5345        scrollText(srcY, destY);
5346    }
5347
5348    renderer.textChanging(event);
5349    
5350    // Update the caret offset if it is greater than the length of the content.
5351
// This is necessary since style range API may be called between the
5352
// handleTextChanging and handleTextChanged events and this API sets the
5353
// caretOffset.
5354
int newEndOfText = content.getCharCount() - event.replaceCharCount + event.newCharCount;
5355    if (caretOffset > newEndOfText) caretOffset = newEndOfText;
5356}
5357/**
5358 * Called when the widget content is set programmatically, overwriting
5359 * the old content. Resets the caret position, selection and scroll offsets.
5360 * Recalculates the content width and scroll bars. Redraws the widget.
5361 *
5362 * @param event text change event.
5363 */

5364void handleTextSet(TextChangedEvent event) {
5365    reset();
5366}
5367/**
5368 * Called when a traversal key is pressed.
5369 * Allow tab next traversal to occur when the widget is in single
5370 * line mode or in multi line and non-editable mode .
5371 * When in editable multi line mode we want to prevent the tab
5372 * traversal and receive the tab key event instead.
5373 *
5374 * @param event the event
5375 */

5376void handleTraverse(Event event) {
5377    switch (event.detail) {
5378        case SWT.TRAVERSE_ESCAPE:
5379        case SWT.TRAVERSE_PAGE_NEXT:
5380        case SWT.TRAVERSE_PAGE_PREVIOUS:
5381            event.doit = true;
5382            break;
5383        case SWT.TRAVERSE_RETURN:
5384        case SWT.TRAVERSE_TAB_NEXT:
5385        case SWT.TRAVERSE_TAB_PREVIOUS:
5386            if ((getStyle() & SWT.SINGLE) != 0) {
5387                event.doit = true;
5388            } else {
5389                if (!editable || (event.stateMask & SWT.MODIFIER_MASK) != 0) {
5390                    event.doit = true;
5391                }
5392            }
5393            break;
5394    }
5395}
5396/**
5397 * Scrolls the widget vertically.
5398 */

5399void handleVerticalScroll(Event event) {
5400    int scrollPixel = getVerticalBar().getSelection() - getVerticalScrollOffset();
5401    scrollVertical(scrollPixel, false);
5402}
5403/**
5404 * Add accessibility support for the widget.
5405 */

5406void initializeAccessible() {
5407    final Accessible accessible = getAccessible();
5408    accessible.addAccessibleListener(new AccessibleAdapter() {
5409        public void getName (AccessibleEvent e) {
5410            String JavaDoc name = null;
5411            Label label = getAssociatedLabel ();
5412            if (label != null) {
5413                name = stripMnemonic (label.getText());
5414            }
5415            e.result = name;
5416        }
5417        public void getHelp(AccessibleEvent e) {
5418            e.result = getToolTipText();
5419        }
5420        public void getKeyboardShortcut(AccessibleEvent e) {
5421            String JavaDoc shortcut = null;
5422            Label label = getAssociatedLabel ();
5423            if (label != null) {
5424                String JavaDoc text = label.getText ();
5425                if (text != null) {
5426                    char mnemonic = _findMnemonic (text);
5427                    if (mnemonic != '\0') {
5428                        shortcut = "Alt+"+mnemonic; //$NON-NLS-1$
5429
}
5430                }
5431            }
5432            e.result = shortcut;
5433        }
5434    });
5435    accessible.addAccessibleTextListener(new AccessibleTextAdapter() {
5436        public void getCaretOffset(AccessibleTextEvent e) {
5437            e.offset = StyledText.this.getCaretOffset();
5438        }
5439        public void getSelectionRange(AccessibleTextEvent e) {
5440            Point selection = StyledText.this.getSelectionRange();
5441            e.offset = selection.x;
5442            e.length = selection.y;
5443        }
5444    });
5445    accessible.addAccessibleControlListener(new AccessibleControlAdapter() {
5446        public void getRole(AccessibleControlEvent e) {
5447            e.detail = ACC.ROLE_TEXT;
5448        }
5449        public void getState(AccessibleControlEvent e) {
5450            int state = 0;
5451            if (isEnabled()) state |= ACC.STATE_FOCUSABLE;
5452            if (isFocusControl()) state |= ACC.STATE_FOCUSED;
5453            if (!isVisible()) state |= ACC.STATE_INVISIBLE;
5454            if (!getEditable()) state |= ACC.STATE_READONLY;
5455            e.detail = state;
5456        }
5457        public void getValue(AccessibleControlEvent e) {
5458            e.result = StyledText.this.getText();
5459        }
5460    });
5461    addListener(SWT.FocusIn, new Listener() {
5462        public void handleEvent(Event event) {
5463            accessible.setFocus(ACC.CHILDID_SELF);
5464        }
5465    });
5466}
5467/*
5468 * Return the Label immediately preceding the receiver in the z-order,
5469 * or null if none.
5470 */

5471Label getAssociatedLabel () {
5472    Control[] siblings = getParent ().getChildren ();
5473    for (int i = 0; i < siblings.length; i++) {
5474        if (siblings [i] == StyledText.this) {
5475            if (i > 0 && siblings [i-1] instanceof Label) {
5476                return (Label) siblings [i-1];
5477            }
5478        }
5479    }
5480    return null;
5481}
5482String JavaDoc stripMnemonic (String JavaDoc string) {
5483    int index = 0;
5484    int length = string.length ();
5485    do {
5486        while ((index < length) && (string.charAt (index) != '&')) index++;
5487        if (++index >= length) return string;
5488        if (string.charAt (index) != '&') {
5489            return string.substring(0, index-1) + string.substring(index, length);
5490        }
5491        index++;
5492    } while (index < length);
5493    return string;
5494}
5495/*
5496 * Return the lowercase of the first non-'&' character following
5497 * an '&' character in the given string. If there are no '&'
5498 * characters in the given string, return '\0'.
5499 */

5500char _findMnemonic (String JavaDoc string) {
5501    if (string == null) return '\0';
5502    int index = 0;
5503    int length = string.length ();
5504    do {
5505        while (index < length && string.charAt (index) != '&') index++;
5506        if (++index >= length) return '\0';
5507        if (string.charAt (index) != '&') return Character.toLowerCase (string.charAt (index));
5508        index++;
5509    } while (index < length);
5510    return '\0';
5511}
5512/**
5513 * Executes the action.
5514 *
5515 * @param action one of the actions defined in ST.java
5516 */

5517public void invokeAction(int action) {
5518    checkWidget();
5519    updateCaretDirection = true;
5520    switch (action) {
5521        // Navigation
5522
case ST.LINE_UP:
5523            doLineUp(false);
5524            clearSelection(true);
5525            break;
5526        case ST.LINE_DOWN:
5527            doLineDown(false);
5528            clearSelection(true);
5529            break;
5530        case ST.LINE_START:
5531            doLineStart();
5532            clearSelection(true);
5533            break;
5534        case ST.LINE_END:
5535            doLineEnd();
5536            clearSelection(true);
5537            break;
5538        case ST.COLUMN_PREVIOUS:
5539            doCursorPrevious();
5540            clearSelection(true);
5541            break;
5542        case ST.COLUMN_NEXT:
5543            doCursorNext();
5544            clearSelection(true);
5545            break;
5546        case ST.PAGE_UP:
5547            doPageUp(false, -1);
5548            clearSelection(true);
5549            break;
5550        case ST.PAGE_DOWN:
5551            doPageDown(false, -1);
5552            clearSelection(true);
5553            break;
5554        case ST.WORD_PREVIOUS:
5555            doWordPrevious();
5556            clearSelection(true);
5557            break;
5558        case ST.WORD_NEXT:
5559            doWordNext();
5560            clearSelection(true);
5561            break;
5562        case ST.TEXT_START:
5563            doContentStart();
5564            clearSelection(true);
5565            break;
5566        case ST.TEXT_END:
5567            doContentEnd();
5568            clearSelection(true);
5569            break;
5570        case ST.WINDOW_START:
5571            doPageStart();
5572            clearSelection(true);
5573            break;
5574        case ST.WINDOW_END:
5575            doPageEnd();
5576            clearSelection(true);
5577            break;
5578        // Selection
5579
case ST.SELECT_LINE_UP:
5580            doSelectionLineUp();
5581            break;
5582        case ST.SELECT_ALL:
5583            selectAll();
5584            break;
5585        case ST.SELECT_LINE_DOWN:
5586            doSelectionLineDown();
5587            break;
5588        case ST.SELECT_LINE_START:
5589            doLineStart();
5590            doSelection(ST.COLUMN_PREVIOUS);
5591            break;
5592        case ST.SELECT_LINE_END:
5593            doLineEnd();
5594            doSelection(ST.COLUMN_NEXT);
5595            break;
5596        case ST.SELECT_COLUMN_PREVIOUS:
5597            doSelectionCursorPrevious();
5598            doSelection(ST.COLUMN_PREVIOUS);
5599            break;
5600        case ST.SELECT_COLUMN_NEXT:
5601            doSelectionCursorNext();
5602            doSelection(ST.COLUMN_NEXT);
5603            break;
5604        case ST.SELECT_PAGE_UP:
5605            doSelectionPageUp(-1);
5606            break;
5607        case ST.SELECT_PAGE_DOWN:
5608            doSelectionPageDown(-1);
5609            break;
5610        case ST.SELECT_WORD_PREVIOUS:
5611            doSelectionWordPrevious();
5612            doSelection(ST.COLUMN_PREVIOUS);
5613            break;
5614        case ST.SELECT_WORD_NEXT:
5615            doSelectionWordNext();
5616            doSelection(ST.COLUMN_NEXT);
5617            break;
5618        case ST.SELECT_TEXT_START:
5619            doContentStart();
5620            doSelection(ST.COLUMN_PREVIOUS);
5621            break;
5622        case ST.SELECT_TEXT_END:
5623            doContentEnd();
5624            doSelection(ST.COLUMN_NEXT);
5625            break;
5626        case ST.SELECT_WINDOW_START:
5627            doPageStart();
5628            doSelection(ST.COLUMN_PREVIOUS);
5629            break;
5630        case ST.SELECT_WINDOW_END:
5631            doPageEnd();
5632            doSelection(ST.COLUMN_NEXT);
5633            break;
5634        // Modification
5635
case ST.CUT:
5636            cut();
5637            break;
5638        case ST.COPY:
5639            copy();
5640            break;
5641        case ST.PASTE:
5642            paste();
5643            break;
5644        case ST.DELETE_PREVIOUS:
5645            doBackspace();
5646            break;
5647        case ST.DELETE_NEXT:
5648            doDelete();
5649            break;
5650        case ST.DELETE_WORD_PREVIOUS:
5651            doDeleteWordPrevious();
5652            break;
5653        case ST.DELETE_WORD_NEXT:
5654            doDeleteWordNext();
5655            break;
5656        // Miscellaneous
5657
case ST.TOGGLE_OVERWRITE:
5658            overwrite = !overwrite; // toggle insert/overwrite mode
5659
break;
5660    }
5661}
5662/**
5663 * Temporary until SWT provides this
5664 */

5665boolean isBidi() {
5666    return IS_GTK || BidiUtil.isBidiPlatform() || isMirrored;
5667}
5668boolean isBidiCaret() {
5669    return BidiUtil.isBidiPlatform();
5670}
5671boolean isFixedLineHeight() {
5672    return fixedLineHeight;
5673}
5674/**
5675 * Returns whether the given offset is inside a multi byte line delimiter.
5676 * Example:
5677 * "Line1\r\n" isLineDelimiter(5) == false but isLineDelimiter(6) == true
5678 *
5679 * @return true if the given offset is inside a multi byte line delimiter.
5680 * false if the given offset is before or after a line delimiter.
5681 */

5682boolean isLineDelimiter(int offset) {
5683    int line = content.getLineAtOffset(offset);
5684    int lineOffset = content.getOffsetAtLine(line);
5685    int offsetInLine = offset - lineOffset;
5686    // offsetInLine will be greater than line length if the line
5687
// delimiter is longer than one character and the offset is set
5688
// in between parts of the line delimiter.
5689
return offsetInLine > content.getLine(line).length();
5690}
5691/**
5692 * Returns whether the widget is mirrored (right oriented/right to left
5693 * writing order).
5694 *
5695 * @return isMirrored true=the widget is right oriented, false=the widget
5696 * is left oriented
5697 */

5698boolean isMirrored() {
5699    return isMirrored;
5700}
5701/**
5702 * Returns whether the widget can have only one line.
5703 *
5704 * @return true if widget can have only one line, false if widget can have
5705 * multiple lines
5706 */

5707boolean isSingleLine() {
5708    return (getStyle() & SWT.SINGLE) != 0;
5709}
5710/**
5711 * Sends the specified verify event, replace/insert text as defined by
5712 * the event and send a modify event.
5713 *
5714 * @param event the text change event.
5715 * <ul>
5716 * <li>event.start - the replace start offset</li>
5717 * <li>event.end - the replace end offset</li>
5718 * <li>event.text - the new text</li>
5719 * </ul>
5720 * @param updateCaret whether or not he caret should be set behind
5721 * the new text
5722 */

5723void modifyContent(Event event, boolean updateCaret) {
5724    event.doit = true;
5725    notifyListeners(SWT.Verify, event);
5726    if (event.doit) {
5727        StyledTextEvent styledTextEvent = null;
5728        int replacedLength = event.end - event.start;
5729        if (isListening(ExtendedModify)) {
5730            styledTextEvent = new StyledTextEvent(content);
5731            styledTextEvent.start = event.start;
5732            styledTextEvent.end = event.start + event.text.length();
5733            styledTextEvent.text = content.getTextRange(event.start, replacedLength);
5734        }
5735        if (updateCaret) {
5736            //Fix advancing flag for delete/backspace key on direction boundary
5737
if (event.text.length() == 0) {
5738                int lineIndex = content.getLineAtOffset(event.start);
5739                int lineOffset = content.getOffsetAtLine(lineIndex);
5740                TextLayout layout = renderer.getTextLayout(lineIndex);
5741                int levelStart = layout.getLevel(event.start - lineOffset);
5742                int lineIndexEnd = content.getLineAtOffset(event.end);
5743                if (lineIndex != lineIndexEnd) {
5744                    renderer.disposeTextLayout(layout);
5745                    lineOffset = content.getOffsetAtLine(lineIndexEnd);
5746                    layout = renderer.getTextLayout(lineIndexEnd);
5747                }
5748                int levelEnd = layout.getLevel(event.end - lineOffset);
5749                renderer.disposeTextLayout(layout);
5750                if (levelStart != levelEnd) {
5751                    caretAlignment = PREVIOUS_OFFSET_TRAILING;
5752                } else {
5753                    caretAlignment = OFFSET_LEADING;
5754                }
5755            }
5756        }
5757        content.replaceTextRange(event.start, replacedLength, event.text);
5758        // set the caret position prior to sending the modify event.
5759
// fixes 1GBB8NJ
5760
if (updateCaret) {
5761            // always update the caret location. fixes 1G8FODP
5762
setSelection(event.start + event.text.length(), 0, true);
5763            showCaret();
5764        }
5765        sendModifyEvent(event);
5766        if (isListening(ExtendedModify)) {
5767            notifyListeners(ExtendedModify, styledTextEvent);
5768        }
5769    }
5770}
5771void paintObject(GC gc, int x, int y, int ascent, int descent, StyleRange style, Bullet bullet, int bulletIndex) {
5772    if (isListening(PaintObject)) {
5773        StyledTextEvent event = new StyledTextEvent (content) ;
5774        event.gc = gc;
5775        event.x = x;
5776        event.y = y;
5777        event.ascent = ascent;
5778        event.descent = descent;
5779        event.style = style;
5780        event.bullet = bullet;
5781        event.bulletIndex = bulletIndex;
5782        notifyListeners(PaintObject, event);
5783    }
5784}
5785/**
5786 * Replaces the selection with the text on the <code>DND.CLIPBOARD</code>
5787 * clipboard or, if there is no selection, inserts the text at the current
5788 * caret offset. If the widget has the SWT.SINGLE style and the
5789 * clipboard text contains more than one line, only the first line without
5790 * line delimiters is inserted in the widget.
5791 *
5792 * @exception SWTException <ul>
5793 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5794 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5795 * </ul>
5796 */

5797public void paste(){
5798    checkWidget();
5799    String JavaDoc text = (String JavaDoc) getClipboardContent(DND.CLIPBOARD);
5800    if (text != null && text.length() > 0) {
5801        Event event = new Event();
5802        event.start = selection.x;
5803        event.end = selection.y;
5804        event.text = getModelDelimitedText(text);
5805        sendKeyEvent(event);
5806    }
5807}
5808/**
5809 * Prints the widget's text to the default printer.
5810 *
5811 * @exception SWTException <ul>
5812 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5813 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5814 * </ul>
5815 */

5816public void print() {
5817    checkWidget();
5818    Printer printer = new Printer();
5819    StyledTextPrintOptions options = new StyledTextPrintOptions();
5820    options.printTextForeground = true;
5821    options.printTextBackground = true;
5822    options.printTextFontStyle = true;
5823    options.printLineBackground = true;
5824    new Printing(this, printer, options).run();
5825    printer.dispose();
5826}
5827/**
5828 * Returns a runnable that will print the widget's text
5829 * to the specified printer.
5830 * <p>
5831 * The runnable may be run in a non-UI thread.
5832 * </p>
5833 *
5834 * @param printer the printer to print to
5835 * @exception SWTException <ul>
5836 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5837 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5838 * </ul>
5839 * @exception IllegalArgumentException <ul>
5840 * <li>ERROR_NULL_ARGUMENT when printer is null</li>
5841 * </ul>
5842 */

5843public Runnable JavaDoc print(Printer printer) {
5844    checkWidget();
5845    if (printer == null) {
5846        SWT.error(SWT.ERROR_NULL_ARGUMENT);
5847    }
5848    StyledTextPrintOptions options = new StyledTextPrintOptions();
5849    options.printTextForeground = true;
5850    options.printTextBackground = true;
5851    options.printTextFontStyle = true;
5852    options.printLineBackground = true;
5853    return print(printer, options);
5854}
5855/**
5856 * Returns a runnable that will print the widget's text
5857 * to the specified printer.
5858 * <p>
5859 * The runnable may be run in a non-UI thread.
5860 * </p>
5861 *
5862 * @param printer the printer to print to
5863 * @param options print options to use during printing
5864 * @exception SWTException <ul>
5865 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5866 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5867 * </ul>
5868 * @exception IllegalArgumentException <ul>
5869 * <li>ERROR_NULL_ARGUMENT when printer or options is null</li>
5870 * </ul>
5871 * @since 2.1
5872 */

5873public Runnable JavaDoc print(Printer printer, StyledTextPrintOptions options) {
5874    checkWidget();
5875    if (printer == null || options == null) {
5876        SWT.error(SWT.ERROR_NULL_ARGUMENT);
5877    }
5878    return new Printing(this, printer, options);
5879}
5880/**
5881 * Causes the entire bounds of the receiver to be marked
5882 * as needing to be redrawn. The next time a paint request
5883 * is processed, the control will be completely painted.
5884 * <p>
5885 * Recalculates the content width for all lines in the bounds.
5886 * When a <code>LineStyleListener</code> is used a redraw call
5887 * is the only notification to the widget that styles have changed
5888 * and that the content width may have changed.
5889 * </p>
5890 *
5891 * @exception SWTException <ul>
5892 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5893 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5894 * </ul>
5895 *
5896 * @see Control#update()
5897 */

5898public void redraw() {
5899    super.redraw();
5900    int itemCount = getPartialBottomIndex() - topIndex + 1;
5901    renderer.reset(topIndex, itemCount);
5902    renderer.calculate(topIndex, itemCount);
5903    setScrollBars(false);
5904}
5905/**
5906 * Causes the rectangular area of the receiver specified by
5907 * the arguments to be marked as needing to be redrawn.
5908 * The next time a paint request is processed, that area of
5909 * the receiver will be painted. If the <code>all</code> flag
5910 * is <code>true</code>, any children of the receiver which
5911 * intersect with the specified area will also paint their
5912 * intersecting areas. If the <code>all</code> flag is
5913 * <code>false</code>, the children will not be painted.
5914 * <p>
5915 * Marks the content width of all lines in the specified rectangle
5916 * as unknown. Recalculates the content width of all visible lines.
5917 * When a <code>LineStyleListener</code> is used a redraw call
5918 * is the only notification to the widget that styles have changed
5919 * and that the content width may have changed.
5920 * </p>
5921 *
5922 * @param x the x coordinate of the area to draw
5923 * @param y the y coordinate of the area to draw
5924 * @param width the width of the area to draw
5925 * @param height the height of the area to draw
5926 * @param all <code>true</code> if children should redraw, and <code>false</code> otherwise
5927 *
5928 * @exception SWTException <ul>
5929 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5930 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5931 * </ul>
5932 *
5933 * @see Control#update()
5934 */

5935public void redraw(int x, int y, int width, int height, boolean all) {
5936    super.redraw(x, y, width, height, all);
5937    if (height > 0) {
5938        int firstLine = getLineIndex(y);
5939        int lastLine = getLineIndex(y + height);
5940        resetCache(firstLine, lastLine - firstLine + 1);
5941    }
5942}
5943void redrawLines(int startLine, int lineCount) {
5944    // do nothing if redraw range is completely invisible
5945
int partialBottomIndex = getPartialBottomIndex();
5946    if (startLine > partialBottomIndex || startLine + lineCount - 1 < topIndex) {
5947        return;
5948    }
5949    // only redraw visible lines
5950
if (startLine < topIndex) {
5951        lineCount -= topIndex - startLine;
5952        startLine = topIndex;
5953    }
5954    if (startLine + lineCount - 1 > partialBottomIndex) {
5955        lineCount = partialBottomIndex - startLine + 1;
5956    }
5957    startLine -= topIndex;
5958    int redrawTop = getLinePixel(startLine);
5959    int redrawBottom = getLinePixel(startLine + lineCount);
5960    int redrawWidth = clientAreaWidth - leftMargin - rightMargin;
5961    super.redraw(leftMargin, redrawTop, redrawWidth, redrawBottom - redrawTop, true);
5962}
5963void redrawLinesBullet (int[] redrawLines) {
5964    if (redrawLines == null) return;
5965    int topIndex = getPartialTopIndex();
5966    int bottomIndex = getPartialBottomIndex();
5967    for (int i = 0; i < redrawLines.length; i++) {
5968        int lineIndex = redrawLines[i];
5969        if (!(topIndex <= lineIndex && lineIndex <= bottomIndex)) continue;
5970        int width = -1;
5971        Bullet bullet = renderer.getLineBullet(lineIndex, null);
5972        if (bullet != null) {
5973            StyleRange style = bullet.style;
5974            GlyphMetrics metrics = style.metrics;
5975            width = metrics.width;
5976        }
5977        if (width == -1) width = getClientArea().width;
5978        int height = renderer.getLineHeight(lineIndex);
5979        int y = getLinePixel(lineIndex);
5980        super.redraw(0, y, width, height, false);
5981    }
5982}
5983/**
5984 * Redraws the specified text range.
5985 *
5986 * @param start offset of the first character to redraw
5987 * @param length number of characters to redraw
5988 * @param clearBackground true if the background should be cleared as
5989 * part of the redraw operation. If true, the entire redraw range will
5990 * be cleared before anything is redrawn. If the redraw range includes
5991 * the last character of a line (i.e., the entire line is redrawn) the
5992 * line is cleared all the way to the right border of the widget.
5993 * The redraw operation will be faster and smoother if clearBackground
5994 * is set to false. Whether or not the flag can be set to false depends
5995 * on the type of change that has taken place. If font styles or
5996 * background colors for the redraw range have changed, clearBackground
5997 * should be set to true. If only foreground colors have changed for
5998 * the redraw range, clearBackground can be set to false.
5999 * @exception SWTException <ul>
6000 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6001 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6002 * </ul>
6003 * @exception IllegalArgumentException <ul>
6004 * <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
6005 * </ul>
6006 */

6007public void redrawRange(int start, int length, boolean clearBackground) {
6008    checkWidget();
6009    int end = start + length;
6010    int contentLength = content.getCharCount();
6011    if (start > end || start < 0 || end > contentLength) {
6012        SWT.error(SWT.ERROR_INVALID_RANGE);
6013    }
6014    int firstLine = content.getLineAtOffset(start);
6015    int lastLine = content.getLineAtOffset(end);
6016    resetCache(firstLine, lastLine - firstLine + 1);
6017    internalRedrawRange(start, length);
6018}
6019/**
6020 * Removes the specified bidirectional segment listener.
6021 *
6022 * @param listener the listener
6023 * @exception SWTException <ul>
6024 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6025 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6026 * </ul>
6027 * @exception IllegalArgumentException <ul>
6028 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
6029 * </ul>
6030 * @since 2.0
6031 */

6032public void removeBidiSegmentListener(BidiSegmentListener listener) {
6033    checkWidget();
6034    if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
6035    removeListener(LineGetSegments, listener);
6036}
6037/**
6038 * Removes the specified extended modify listener.
6039 *
6040 * @param extendedModifyListener the listener
6041 * @exception SWTException <ul>
6042 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6043 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6044 * </ul>
6045 * @exception IllegalArgumentException <ul>
6046 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
6047 * </ul>
6048 */

6049public void removeExtendedModifyListener(ExtendedModifyListener extendedModifyListener) {
6050    checkWidget();
6051    if (extendedModifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
6052    removeListener(ExtendedModify, extendedModifyListener);
6053}
6054/**
6055 * Removes the specified line background listener.
6056 *
6057 * @param listener the listener
6058 * @exception SWTException <ul>
6059 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6060 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6061 * </ul>
6062 * @exception IllegalArgumentException <ul>
6063 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
6064 * </ul>
6065 */

6066public void removeLineBackgroundListener(LineBackgroundListener listener) {
6067    checkWidget();
6068    if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
6069    removeListener(LineGetBackground, listener);
6070}
6071/**
6072 * Removes the specified line style listener.
6073 *
6074 * @param listener the listener
6075 * @exception SWTException <ul>
6076 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6077 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6078 * </ul>
6079 * @exception IllegalArgumentException <ul>
6080 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
6081 * </ul>
6082 */

6083public void removeLineStyleListener(LineStyleListener listener) {
6084    checkWidget();
6085    if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
6086    removeListener(LineGetStyle, listener);
6087}
6088/**
6089 * Removes the specified modify listener.
6090 *
6091 * @param modifyListener the listener
6092 * @exception SWTException <ul>
6093 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6094 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6095 * </ul>
6096 * @exception IllegalArgumentException <ul>
6097 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
6098 * </ul>
6099 */

6100public void removeModifyListener(ModifyListener modifyListener) {
6101    checkWidget();
6102    if (modifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
6103    removeListener(SWT.Modify, modifyListener);
6104}
6105/**
6106 * Removes the specified listener.
6107 *
6108 * @param listener the listener
6109 * @exception SWTException <ul>
6110 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6111 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6112 * </ul>
6113 * @exception IllegalArgumentException <ul>
6114 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
6115 * </ul>
6116 * @since 3.2
6117 */

6118public void removePaintObjectListener(PaintObjectListener listener) {
6119    checkWidget();
6120    if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
6121    removeListener(PaintObject, listener);
6122}
6123/**
6124 * Removes the listener from the collection of listeners who will
6125 * be notified when the user changes the receiver's selection.
6126 *
6127 * @param listener the listener which should no longer be notified
6128 *
6129 * @exception IllegalArgumentException <ul>
6130 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
6131 * </ul>
6132 * @exception SWTException <ul>
6133 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6134 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6135 * </ul>
6136 *
6137 * @see SelectionListener
6138 * @see #addSelectionListener
6139 */

6140public void removeSelectionListener(SelectionListener listener) {
6141    checkWidget();
6142    if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
6143    removeListener(SWT.Selection, listener);
6144}
6145/**
6146 * Removes the specified verify listener.
6147 *
6148 * @param verifyListener the listener
6149 * @exception SWTException <ul>
6150 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6151 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6152 * </ul>
6153 * @exception IllegalArgumentException <ul>
6154 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
6155 * </ul>
6156 */

6157public void removeVerifyListener(VerifyListener verifyListener) {
6158    checkWidget();
6159    if (verifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
6160    removeListener(SWT.Verify, verifyListener);
6161}
6162/**
6163 * Removes the specified key verify listener.
6164 *
6165 * @param listener the listener
6166 * @exception SWTException <ul>
6167 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6168 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6169 * </ul>
6170 * @exception IllegalArgumentException <ul>
6171 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
6172 * </ul>
6173 */

6174public void removeVerifyKeyListener(VerifyKeyListener listener) {
6175    if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
6176    removeListener(VerifyKey, listener);
6177}
6178/**
6179 * Removes the specified word movement listener.
6180 *
6181 * @param listener the listener
6182 * @exception SWTException <ul>
6183 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6184 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6185 * </ul>
6186 * @exception IllegalArgumentException <ul>
6187 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
6188 * </ul>
6189 *
6190 * @see MovementEvent
6191 * @see MovementListener
6192 * @see #addWordMovementListener
6193 *
6194 * @since 3.3
6195 */

6196
6197public void removeWordMovementListener(MovementListener listener) {
6198    checkWidget();
6199    if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
6200    removeListener(WordNext, listener);
6201    removeListener(WordPrevious, listener);
6202}
6203/**
6204 * Replaces the styles in the given range with new styles. This method
6205 * effectively deletes the styles in the given range and then adds the
6206 * the new styles.
6207 * <p>
6208 * Note: Because a StyleRange includes the start and length, the
6209 * same instance cannot occur multiple times in the array of styles.
6210 * If the same style attributes, such as font and color, occur in
6211 * multiple StyleRanges, <code>setStyleRanges(int, int, int[], StyleRange[])</code>
6212 * can be used to share styles and reduce memory usage.
6213 * </p><p>
6214 * Should not be called if a LineStyleListener has been set since the
6215 * listener maintains the styles.
6216 * </p>
6217 *
6218 * @param start offset of first character where styles will be deleted
6219 * @param length length of the range to delete styles in
6220 * @param ranges StyleRange objects containing the new style information.
6221 * The ranges should not overlap and should be within the specified start
6222 * and length. The style rendering is undefined if the ranges do overlap
6223 * or are ill-defined. Must not be null.
6224 * @exception SWTException <ul>
6225 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6226 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6227 * </ul>
6228 * @exception IllegalArgumentException <ul>
6229 * <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 <= offset <= getCharCount())</li>
6230 * <li>ERROR_NULL_ARGUMENT when ranges is null</li>
6231 * </ul>
6232 *
6233 * @since 2.0
6234 *
6235 * @see #setStyleRanges(int, int, int[], StyleRange[])
6236 */

6237public void replaceStyleRanges(int start, int length, StyleRange[] ranges) {
6238    checkWidget();
6239    if (isListening(LineGetStyle)) return;
6240    if (ranges == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
6241    setStyleRanges(start, length, null, ranges, false);
6242}
6243/**
6244 * Replaces the given text range with new text.
6245 * If the widget has the SWT.SINGLE style and "text" contains more than
6246 * one line, only the first line is rendered but the text is stored
6247 * unchanged. A subsequent call to getText will return the same text
6248 * that was set. Note that only a single line of text should be set when
6249 * the SWT.SINGLE style is used.
6250 * <p>
6251 * <b>NOTE:</b> During the replace operation the current selection is
6252 * changed as follows:
6253 * <ul>
6254 * <li>selection before replaced text: selection unchanged
6255 * <li>selection after replaced text: adjust the selection so that same text
6256 * remains selected
6257 * <li>selection intersects replaced text: selection is cleared and caret
6258 * is placed after inserted text
6259 * </ul>
6260 * </p>
6261 *
6262 * @param start offset of first character to replace
6263 * @param length number of characters to replace. Use 0 to insert text
6264 * @param text new text. May be empty to delete text.
6265 * @exception SWTException <ul>
6266 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6267 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6268 * </ul>
6269 * @exception IllegalArgumentException <ul>
6270 * <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 <= offset <= getCharCount())</li>
6271 * <li>ERROR_INVALID_ARGUMENT when either start or end is inside a multi byte line delimiter.
6272 * 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>
6273 * <li>ERROR_NULL_ARGUMENT when string is null</li>
6274 * </ul>
6275 */

6276public void replaceTextRange(int start, int length, String JavaDoc text) {
6277    checkWidget();
6278    if (text == null) {
6279        SWT.error(SWT.ERROR_NULL_ARGUMENT);
6280    }
6281    int contentLength = getCharCount();
6282    int end = start + length;
6283    if (start > end || start < 0 || end > contentLength) {
6284        SWT.error(SWT.ERROR_INVALID_RANGE);
6285    }
6286    Event event = new Event();
6287    event.start = start;
6288    event.end = end;
6289    event.text = text;
6290    modifyContent(event, false);
6291}
6292/**
6293 * Resets the caret position, selection and scroll offsets. Recalculate
6294 * the content width and scroll bars. Redraw the widget.
6295 */

6296void reset() {
6297    ScrollBar verticalBar = getVerticalBar();
6298    ScrollBar horizontalBar = getHorizontalBar();
6299    caretOffset = 0;
6300    topIndex = 0;
6301    topIndexY = 0;
6302    verticalScrollOffset = 0;
6303    horizontalScrollOffset = 0;
6304    resetSelection();
6305    renderer.setContent(content);
6306    if (verticalBar != null) {
6307        verticalBar.setSelection(0);
6308    }
6309    if (horizontalBar != null) {
6310        horizontalBar.setSelection(0);
6311    }
6312    resetCache(0, 0);
6313    setCaretLocation();
6314    super.redraw();
6315}
6316void resetCache(int firstLine, int count) {
6317    int maxLineIndex = renderer.maxWidthLineIndex;
6318    renderer.reset(firstLine, count);
6319    renderer.calculateClientArea();
6320    if (0 <= maxLineIndex && maxLineIndex < content.getLineCount()) {
6321        renderer.calculate(maxLineIndex, 1);
6322    }
6323    setScrollBars(true);
6324    if (!isFixedLineHeight()) {
6325        if (topIndex > firstLine) {
6326            verticalScrollOffset = -1;
6327        }
6328        renderer.calculateIdle();
6329    }
6330}
6331/**
6332 * Resets the selection.
6333 */

6334void resetSelection() {
6335    selection.x = selection.y = caretOffset;
6336    selectionAnchor = -1;
6337}
6338
6339public void scroll(int destX, int destY, int x, int y, int width, int height, boolean all) {
6340    super.scroll(destX, destY, x, y, width, height, false);
6341    if (all) {
6342        int deltaX = destX - x, deltaY = destY - y;
6343        Control[] children = getChildren();
6344        for (int i=0; i<children.length; i++) {
6345            Control child = children[i];
6346            Rectangle rect = child.getBounds();
6347            child.setLocation(rect.x + deltaX, rect.y + deltaY);
6348        }
6349    }
6350}
6351
6352/**
6353 * Scrolls the widget horizontally.
6354 *
6355 * @param pixels number of pixels to scroll, > 0 = scroll left,
6356 * < 0 scroll right
6357 * @param adjustScrollBar
6358 * true= the scroll thumb will be moved to reflect the new scroll offset.
6359 * false = the scroll thumb will not be moved
6360 * @return
6361 * true=the widget was scrolled
6362 * false=the widget was not scrolled, the given offset is not valid.
6363 */

6364boolean scrollHorizontal(int pixels, boolean adjustScrollBar) {
6365    if (pixels == 0) {
6366        return false;
6367    }
6368    ScrollBar horizontalBar = getHorizontalBar();
6369    if (horizontalBar != null && adjustScrollBar) {
6370        horizontalBar.setSelection(horizontalScrollOffset + pixels);
6371    }
6372    int scrollHeight = clientAreaHeight - topMargin - bottomMargin;
6373    if (pixels > 0) {
6374        int sourceX = leftMargin + pixels;
6375        int scrollWidth = clientAreaWidth - sourceX - rightMargin;
6376        if (scrollWidth > 0) {
6377            scroll(leftMargin, topMargin, sourceX, topMargin, scrollWidth, scrollHeight, true);
6378        }
6379        if (sourceX > scrollWidth) {
6380            super.redraw(leftMargin + scrollWidth, topMargin, pixels - scrollWidth, scrollHeight, true);
6381        }
6382    } else {
6383        int destinationX = leftMargin - pixels;
6384        int scrollWidth = clientAreaWidth - destinationX - rightMargin;
6385        if (scrollWidth > 0) {
6386            scroll(destinationX, topMargin, leftMargin, topMargin, scrollWidth, scrollHeight, true);
6387        }
6388        if (destinationX > scrollWidth) {
6389            super.redraw(leftMargin + scrollWidth, topMargin, -pixels - scrollWidth, scrollHeight, true);
6390        }
6391    }
6392    horizontalScrollOffset += pixels;
6393    int oldColumnX = columnX;
6394    setCaretLocation();
6395    columnX = oldColumnX;
6396    return true;
6397}
6398/**
6399 * Scrolls the widget vertically.
6400 *
6401 * @param pixel the new vertical scroll offset
6402 * @param adjustScrollBar
6403 * true= the scroll thumb will be moved to reflect the new scroll offset.
6404 * false = the scroll thumb will not be moved
6405 * @return
6406 * true=the widget was scrolled
6407 * false=the widget was not scrolled
6408 */

6409boolean scrollVertical(int pixels, boolean adjustScrollBar) {
6410    if (pixels == 0) {
6411        return false;
6412    }
6413    if (verticalScrollOffset != -1) {
6414        ScrollBar verticalBar = getVerticalBar();
6415        if (verticalBar != null && adjustScrollBar) {
6416            verticalBar.setSelection(verticalScrollOffset + pixels);
6417        }
6418        int scrollWidth = clientAreaWidth - leftMargin - rightMargin;
6419        if (pixels > 0) {
6420            int sourceY = topMargin + pixels;
6421            int scrollHeight = clientAreaHeight - sourceY - bottomMargin;
6422            if (scrollHeight > 0) {
6423                scroll(leftMargin, topMargin, leftMargin, sourceY, scrollWidth, scrollHeight, true);
6424            }
6425            if (sourceY > scrollHeight) {
6426                int redrawY = Math.max(0, topMargin + scrollHeight);
6427                int redrawHeight = Math.min(clientAreaHeight, pixels - scrollHeight);
6428                super.redraw(leftMargin, redrawY, scrollWidth, redrawHeight, true);
6429            }
6430        } else {
6431            int destinationY = topMargin - pixels;
6432            int scrollHeight = clientAreaHeight - destinationY - bottomMargin;
6433            if (scrollHeight > 0) {
6434                scroll(leftMargin, destinationY, leftMargin, topMargin, scrollWidth, scrollHeight, true);
6435            }
6436            if (destinationY > scrollHeight) {
6437                int redrawY = Math.max(0, topMargin + scrollHeight);
6438                int redrawHeight = Math.min(clientAreaHeight, -pixels - scrollHeight);
6439                super.redraw(leftMargin, redrawY, scrollWidth, redrawHeight, true);
6440            }
6441        }
6442        verticalScrollOffset += pixels;
6443        calculateTopIndex(pixels);
6444    } else {
6445        calculateTopIndex(pixels);
6446        super.redraw();
6447    }
6448    int oldColumnX = columnX;
6449    setCaretLocation();
6450    columnX = oldColumnX;
6451    return true;
6452}
6453void scrollText(int srcY, int destY) {
6454    if (srcY == destY) return;
6455    int deltaY = destY - srcY;
6456    int scrollWidth = clientAreaWidth - leftMargin - rightMargin, scrollHeight;
6457    if (deltaY > 0) {
6458        scrollHeight = clientAreaHeight - srcY - bottomMargin;
6459    } else {
6460        scrollHeight = clientAreaHeight - destY - bottomMargin;
6461    }
6462    scroll(leftMargin, destY, leftMargin, srcY, scrollWidth, scrollHeight, true);
6463    if ((0 < srcY + scrollHeight) && (topMargin > srcY)) {
6464        super.redraw(leftMargin, deltaY, scrollWidth, topMargin, false);
6465    }
6466    if ((0 < destY + scrollHeight) && (topMargin > destY)) {
6467        super.redraw(leftMargin, 0, scrollWidth, topMargin, false);
6468    }
6469    if ((clientAreaHeight - bottomMargin < srcY + scrollHeight) && (clientAreaHeight > srcY)) {
6470        super.redraw(leftMargin, clientAreaHeight - bottomMargin + deltaY, scrollWidth, bottomMargin, false);
6471    }
6472    if ((clientAreaHeight - bottomMargin < destY + scrollHeight) && (clientAreaHeight > destY)) {
6473        super.redraw(leftMargin, clientAreaHeight - bottomMargin, scrollWidth, bottomMargin, false);
6474    }
6475}
6476/**
6477 * Selects all the text.
6478 *
6479 * @exception SWTException <ul>
6480 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6481 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6482 * </ul>
6483 */

6484public void selectAll() {
6485    checkWidget();
6486    setSelection(0, Math.max(getCharCount(),0));
6487}
6488/**
6489 * Replaces/inserts text as defined by the event.
6490 *
6491 * @param event the text change event.
6492 * <ul>
6493 * <li>event.start - the replace start offset</li>
6494 * <li>event.end - the replace end offset</li>
6495 * <li>event.text - the new text</li>
6496 * </ul>
6497 */

6498void sendKeyEvent(Event event) {
6499    if (editable) {
6500        modifyContent(event, true);
6501    }
6502}
6503/**
6504 * Returns a StyledTextEvent that can be used to request data such
6505 * as styles and background color for a line.
6506 * <p>
6507 * The specified line may be a visual (wrapped) line if in word
6508 * wrap mode. The returned object will always be for a logical
6509 * (unwrapped) line.
6510 * </p>
6511 *
6512 * @param lineOffset offset of the line. This may be the offset of
6513 * a visual line if the widget is in word wrap mode.
6514 * @param line line text. This may be the text of a visual line if
6515 * the widget is in word wrap mode.
6516 * @return StyledTextEvent that can be used to request line data
6517 * for the given line.
6518 */

6519StyledTextEvent sendLineEvent(int eventType, int lineOffset, String JavaDoc line) {
6520    StyledTextEvent event = null;
6521    if (isListening(eventType)) {
6522        event = new StyledTextEvent(content);
6523        event.detail = lineOffset;
6524        event.text = line;
6525        event.alignment = alignment;
6526        event.indent = indent;
6527        event.justify = justify;
6528        notifyListeners(eventType, event);
6529    }
6530    return event;
6531}
6532void sendModifyEvent(Event event) {
6533    Accessible accessible = getAccessible();
6534    if (event.text.length() == 0) {
6535        accessible.textChanged(ACC.TEXT_DELETE, event.start, event.end - event.start);
6536    } else {
6537        if (event.start == event.end) {
6538            accessible.textChanged(ACC.TEXT_INSERT, event.start, event.text.length());
6539        } else {
6540            accessible.textChanged(ACC.TEXT_DELETE, event.start, event.end - event.start);
6541            accessible.textChanged(ACC.TEXT_INSERT, event.start, event.text.length());
6542        }
6543    }
6544    notifyListeners(SWT.Modify, event);
6545}
6546/**
6547 * Sends the specified selection event.
6548 */

6549void sendSelectionEvent() {
6550    getAccessible().textSelectionChanged();
6551    Event event = new Event();
6552    event.x = selection.x;
6553    event.y = selection.y;
6554    notifyListeners(SWT.Selection, event);
6555}
6556int sendWordBoundaryEvent(int eventType, int movement, int offset, int newOffset, String JavaDoc lineText, int lineOffset) {
6557    if (isListening(eventType)) {
6558        StyledTextEvent event = new StyledTextEvent(content);
6559        event.detail = lineOffset;
6560        event.text = lineText;
6561        event.count = movement;
6562        event.start = offset;
6563        event.end = newOffset;
6564        notifyListeners(eventType, event);
6565        offset = event.end;
6566        if (offset != newOffset) {
6567            int length = getCharCount();
6568            if (offset < 0) {
6569                offset = 0;
6570            } else if (offset > length) {
6571                offset = length;
6572            } else {
6573                if (isLineDelimiter(offset)) {
6574                    SWT.error(SWT.ERROR_INVALID_ARGUMENT);
6575                }
6576            }
6577        }
6578        return offset;
6579    }
6580    return newOffset;
6581}
6582/**
6583 * Sets the alignment of the widget. The argument should be one of <code>SWT.LEFT</code>,
6584 * <code>SWT.CENTER</code> or <code>SWT.RIGHT</code>. The alignment applies for all lines.
6585 *
6586 * @param alignment the new alignment
6587 *
6588 * @exception SWTException <ul>
6589 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6590 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6591 * </ul>
6592 *
6593 * @see #setLineAlignment(int, int, int)
6594 *
6595 * @since 3.2
6596 */

6597public void setAlignment(int alignment) {
6598    checkWidget();
6599    alignment &= (SWT.LEFT | SWT.RIGHT | SWT.CENTER);
6600    if (alignment == 0 || this.alignment == alignment) return;
6601    this.alignment = alignment;
6602    resetCache(0, content.getLineCount());
6603    setCaretLocation();
6604    super.redraw();
6605}
6606/**
6607 * @see Control#setBackground(Color)
6608 */

6609public void setBackground(Color color) {
6610    checkWidget();
6611    background = color;
6612    super.redraw();
6613}
6614/**
6615 * Sets the receiver's caret. Set the caret's height and location.
6616 *
6617 * </p>
6618 * @param caret the new caret for the receiver
6619 *
6620 * @exception SWTException <ul>
6621 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6622 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6623 * </ul>
6624 */

6625public void setCaret(Caret caret) {
6626    checkWidget ();
6627    super.setCaret(caret);
6628    caretDirection = SWT.NULL;
6629    if (caret != null) {
6630        setCaretLocation();
6631    }
6632}
6633/**
6634 * Sets the BIDI coloring mode. When true the BIDI text display
6635 * algorithm is applied to segments of text that are the same
6636 * color.
6637 *
6638 * @param mode the new coloring mode
6639 * @exception SWTException <ul>
6640 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6641 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6642 * </ul>
6643 *
6644 * @deprecated use BidiSegmentListener instead.
6645 */

6646public void setBidiColoring(boolean mode) {
6647    checkWidget();
6648    bidiColoring = mode;
6649}
6650/**
6651 * Moves the Caret to the current caret offset.
6652 */

6653void setCaretLocation() {
6654    Point newCaretPos = getPointAtOffset(caretOffset);
6655    setCaretLocation(newCaretPos, getCaretDirection());
6656}
6657void setCaretLocation(Point location, int direction) {
6658    Caret caret = getCaret();
6659    if (caret != null) {
6660        boolean isDefaultCaret = caret == defaultCaret;
6661        int lineHeight = renderer.getLineHeight();
6662        int caretHeight = lineHeight;
6663        if (!isFixedLineHeight() && isDefaultCaret) {
6664            caretHeight = getBoundsAtOffset(caretOffset).height;
6665            if (caretHeight != lineHeight) {
6666                direction = SWT.DEFAULT;
6667            }
6668        }
6669        int imageDirection = direction;
6670        if (isMirrored()) {
6671            if (imageDirection == SWT.LEFT) {
6672                imageDirection = SWT.RIGHT;
6673            } else if (imageDirection == SWT.RIGHT) {
6674                imageDirection = SWT.LEFT;
6675            }
6676        }
6677        if (isDefaultCaret && imageDirection == SWT.RIGHT) {
6678            location.x -= (caret.getSize().x - 1);
6679        }
6680        if (isDefaultCaret) {
6681            caret.setBounds(location.x, location.y, 0, caretHeight);
6682        } else {
6683            caret.setLocation(location);
6684        }
6685        getAccessible().textCaretMoved(getCaretOffset());
6686        if (direction != caretDirection) {
6687            caretDirection = direction;
6688            if (isDefaultCaret) {
6689                if (imageDirection == SWT.DEFAULT) {
6690                    defaultCaret.setImage(null);
6691                } else if (imageDirection == SWT.LEFT) {
6692                    defaultCaret.setImage(leftCaretBitmap);
6693                } else if (imageDirection == SWT.RIGHT) {
6694                    defaultCaret.setImage(rightCaretBitmap);
6695                }
6696            }
6697            if (caretDirection == SWT.LEFT) {
6698                BidiUtil.setKeyboardLanguage(BidiUtil.KEYBOARD_NON_BIDI);
6699            } else if (caretDirection == SWT.RIGHT) {
6700                BidiUtil.setKeyboardLanguage(BidiUtil.KEYBOARD_BIDI);
6701            }
6702        }
6703    }
6704    columnX = location.x;
6705}
6706/**
6707 * Sets the caret offset.
6708 *
6709 * @param offset caret offset, relative to the first character in the text.
6710 * @exception SWTException <ul>
6711 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6712 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6713 * </ul>
6714 * @exception IllegalArgumentException <ul>
6715 * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
6716 * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
6717 * </ul>
6718 */

6719public void setCaretOffset(int offset) {
6720    checkWidget();
6721    int length = getCharCount();
6722    if (length > 0 && offset != caretOffset) {
6723        if (offset < 0) {
6724            caretOffset = 0;
6725        } else if (offset > length) {
6726            caretOffset = length;
6727        } else {
6728            if (isLineDelimiter(offset)) {
6729                // offset is inside a multi byte line delimiter. This is an
6730
// illegal operation and an exception is thrown. Fixes 1GDKK3R
6731
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
6732            }
6733            caretOffset = offset;
6734        }
6735        // clear the selection if the caret is moved.
6736
// don't notify listeners about the selection change.
6737
clearSelection(false);
6738    }
6739    setCaretLocation();
6740}
6741/**
6742 * Copies the specified text range to the clipboard. The text will be placed
6743 * in the clipboard in plain text format and RTF format.
6744 *
6745 * @param start start index of the text
6746 * @param length length of text to place in clipboard
6747 *
6748 * @exception SWTError, see Clipboard.setContents
6749 * @see org.eclipse.swt.dnd.Clipboard#setContents
6750 */

6751void setClipboardContent(int start, int length, int clipboardType) throws SWTError {
6752    if (clipboardType == DND.SELECTION_CLIPBOARD && !(IS_MOTIF || IS_GTK)) return;
6753    TextTransfer plainTextTransfer = TextTransfer.getInstance();
6754    TextWriter plainTextWriter = new TextWriter(start, length);
6755    String JavaDoc plainText = getPlatformDelimitedText(plainTextWriter);
6756    Object JavaDoc[] data;
6757    Transfer[] types;
6758    if (clipboardType == DND.SELECTION_CLIPBOARD) {
6759        data = new Object JavaDoc[]{plainText};
6760        types = new Transfer[]{plainTextTransfer};
6761    } else {
6762        RTFTransfer rtfTransfer = RTFTransfer.getInstance();
6763        RTFWriter rtfWriter = new RTFWriter(start, length);
6764        String JavaDoc rtfText = getPlatformDelimitedText(rtfWriter);
6765        data = new Object JavaDoc[]{rtfText, plainText};
6766        types = new Transfer[]{rtfTransfer, plainTextTransfer};
6767    }
6768    clipboard.setContents(data, types, clipboardType);
6769}
6770/**
6771 * Sets the content implementation to use for text storage.
6772 *
6773 * @param newContent StyledTextContent implementation to use for text storage.
6774 * @exception SWTException <ul>
6775 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6776 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6777 * </ul>
6778 * @exception IllegalArgumentException <ul>
6779 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
6780 * </ul>
6781 */

6782public void setContent(StyledTextContent newContent) {
6783    checkWidget();
6784    if (newContent == null) {
6785        SWT.error(SWT.ERROR_NULL_ARGUMENT);
6786    }
6787    if (content != null) {
6788        content.removeTextChangeListener(textChangeListener);
6789    }
6790    content = newContent;
6791    content.addTextChangeListener(textChangeListener);
6792    reset();
6793}
6794/**
6795 * Sets the receiver's cursor to the cursor specified by the
6796 * argument. Overridden to handle the null case since the
6797 * StyledText widget uses an ibeam as its default cursor.
6798 *
6799 * @see Control#setCursor(Cursor)
6800 */

6801public void setCursor (Cursor cursor) {
6802    if (cursor == null) {
6803        Display display = getDisplay();
6804        super.setCursor(display.getSystemCursor(SWT.CURSOR_IBEAM));
6805    } else {
6806        super.setCursor(cursor);
6807    }
6808}
6809/**
6810 * Sets whether the widget implements double click mouse behavior.
6811 * </p>
6812 *
6813 * @param enable if true double clicking a word selects the word, if false
6814 * double clicks have the same effect as regular mouse clicks.
6815 * @exception SWTException <ul>
6816 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6817 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6818 * </ul>
6819 */

6820public void setDoubleClickEnabled(boolean enable) {
6821    checkWidget();
6822    doubleClickEnabled = enable;
6823}
6824public void setDragDetect (boolean dragDetect) {
6825    checkWidget ();
6826    this.dragDetect = dragDetect;
6827}
6828/**
6829 * Sets whether the widget content can be edited.
6830 * </p>
6831 *
6832 * @param editable if true content can be edited, if false content can not be
6833 * edited
6834 * @exception SWTException <ul>
6835 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6836 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6837 * </ul>
6838 */

6839public void setEditable(boolean editable) {
6840    checkWidget();
6841    this.editable = editable;
6842}
6843/**
6844 * Sets a new font to render text with.
6845 * <p>
6846 * <b>NOTE:</b> Italic fonts are not supported unless they have no overhang
6847 * and the same baseline as regular fonts.
6848 * </p>
6849 *
6850 * @param font new font
6851 * @exception SWTException <ul>
6852 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6853 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6854 * </ul>
6855 */

6856public void setFont(Font font) {
6857    checkWidget();
6858    int oldLineHeight = renderer.getLineHeight();
6859    super.setFont(font);
6860    renderer.setFont(getFont(), tabLength);
6861    // keep the same top line visible. fixes 5815
6862
if (isFixedLineHeight()) {
6863        int lineHeight = renderer.getLineHeight();
6864        if (lineHeight != oldLineHeight) {
6865            int vscroll = (getVerticalScrollOffset() * lineHeight / oldLineHeight) - getVerticalScrollOffset();
6866            scrollVertical(vscroll, true);
6867        }
6868    }
6869    resetCache(0, content.getLineCount());
6870    claimBottomFreeSpace();
6871    calculateScrollBars();
6872    if (isBidiCaret()) createCaretBitmaps();
6873    caretDirection = SWT.NULL;
6874    setCaretLocation();
6875    super.redraw();
6876}
6877/**
6878 * @see org.eclipse.swt.widgets.Control#setForeground
6879 */

6880public void setForeground(Color color) {
6881    checkWidget();
6882    foreground = color;
6883    super.setForeground(getForeground());
6884    super.redraw();
6885}
6886/**
6887 * Sets the horizontal scroll offset relative to the start of the line.
6888 * Do nothing if there is no text set.
6889 * <p>
6890 * <b>NOTE:</b> The horizontal index is reset to 0 when new text is set in the
6891 * widget.
6892 * </p>
6893 *
6894 * @param offset horizontal scroll offset relative to the start
6895 * of the line, measured in character increments starting at 0, if
6896 * equal to 0 the content is not scrolled, if > 0 = the content is scrolled.
6897 * @exception SWTException <ul>
6898 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6899 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6900 * </ul>
6901 */

6902public void setHorizontalIndex(int offset) {
6903    checkWidget();
6904    if (getCharCount() == 0) {
6905        return;
6906    }
6907    if (offset < 0) {
6908        offset = 0;
6909    }
6910    offset *= getHorizontalIncrement();
6911    // allow any value if client area width is unknown or 0.
6912
// offset will be checked in resize handler.
6913
// don't use isVisible since width is known even if widget
6914
// is temporarily invisible
6915
if (clientAreaWidth > 0) {
6916        int width = renderer.getWidth();
6917        // prevent scrolling if the content fits in the client area.
6918
// align end of longest line with right border of client area
6919
// if offset is out of range.
6920
if (offset > width - clientAreaWidth) {
6921            offset = Math.max(0, width - clientAreaWidth);
6922        }
6923    }
6924    scrollHorizontal(offset - horizontalScrollOffset, true);
6925}
6926/**
6927 * Sets the horizontal pixel offset relative to the start of the line.
6928 * Do nothing if there is no text set.
6929 * <p>
6930 * <b>NOTE:</b> The horizontal pixel offset is reset to 0 when new text
6931 * is set in the widget.
6932 * </p>
6933 *
6934 * @param pixel horizontal pixel offset relative to the start
6935 * of the line.
6936 * @exception SWTException <ul>
6937 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6938 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6939 * </ul>
6940 * @since 2.0
6941 */

6942public void setHorizontalPixel(int pixel) {
6943    checkWidget();
6944    if (getCharCount() == 0) {
6945        return;
6946    }
6947    if (pixel < 0) {
6948        pixel = 0;
6949    }
6950    // allow any value if client area width is unknown or 0.
6951
// offset will be checked in resize handler.
6952
// don't use isVisible since width is known even if widget
6953
// is temporarily invisible
6954
if (clientAreaWidth > 0) {
6955        int width = renderer.getWidth();
6956        // prevent scrolling if the content fits in the client area.
6957
// align end of longest line with right border of client area
6958
// if offset is out of range.
6959
if (pixel > width - clientAreaWidth) {
6960            pixel = Math.max(0, width - clientAreaWidth);
6961        }
6962    }
6963    scrollHorizontal(pixel - horizontalScrollOffset, true);
6964}
6965/**
6966 * Sets the line indentation of the widget.
6967 * <p>
6968 * It is the amount of blank space, in pixels, at the beginning of each line.
6969 * When a line wraps in several lines only the first one is indented.
6970 * </p>
6971 *
6972 * @param indent the new indent
6973 *
6974 * @exception SWTException <ul>
6975 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6976 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6977 * </ul>
6978 *
6979 * @see #setLineIndent(int, int, int)
6980 *
6981 * @since 3.2
6982 */

6983public void setIndent(int indent) {
6984    checkWidget();
6985    if (this.indent == indent || indent < 0) return;
6986    this.indent = indent;
6987    resetCache(0, content.getLineCount());
6988    setCaretLocation();
6989    super.redraw();
6990}
6991/**
6992 * Sets whether the widget should justify lines.
6993 *
6994 * @param justify whether lines should be justified
6995 *
6996 * @exception SWTException <ul>
6997 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6998 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6999 * </ul>
7000 *
7001 * @see #setLineJustify(int, int, boolean)
7002 *
7003 * @since 3.2
7004 */

7005public void setJustify(boolean justify) {
7006    checkWidget();
7007    if (this.justify == justify) return;
7008    this.justify = justify;
7009    resetCache(0, content.getLineCount());
7010    setCaretLocation();
7011    super.redraw();
7012}
7013/**
7014 * Maps a key to an action.
7015 * <p>
7016 * One action can be associated with N keys. However, each key can only
7017 * have one action (key:action is N:1 relation).
7018 * </p>
7019 *
7020 * @param key a key code defined in SWT.java or a character.
7021 * Optionally ORd with a state mask. Preferred state masks are one or more of
7022 * SWT.MOD1, SWT.MOD2, SWT.MOD3, since these masks account for modifier platform
7023 * differences. However, there may be cases where using the specific state masks
7024 * (i.e., SWT.CTRL, SWT.SHIFT, SWT.ALT, SWT.COMMAND) makes sense.
7025 * @param action one of the predefined actions defined in ST.java.
7026 * Use SWT.NULL to remove a key binding.
7027 * @exception SWTException <ul>
7028 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7029 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7030 * </ul>
7031 */

7032public void setKeyBinding(int key, int action) {
7033    checkWidget();
7034    int modifierValue = key & SWT.MODIFIER_MASK;
7035    char keyChar = (char)(key & SWT.KEY_MASK);
7036    if (Compatibility.isLetter(keyChar)) {
7037        // make the keybinding case insensitive by adding it
7038
// in its upper and lower case form
7039
char ch = Character.toUpperCase(keyChar);
7040        int newKey = ch | modifierValue;
7041        if (action == SWT.NULL) {
7042            keyActionMap.remove(new Integer JavaDoc(newKey));
7043        } else {
7044            keyActionMap.put(new Integer JavaDoc(newKey), new Integer JavaDoc(action));
7045        }
7046        ch = Character.toLowerCase(keyChar);
7047        newKey = ch | modifierValue;
7048        if (action == SWT.NULL) {
7049            keyActionMap.remove(new Integer JavaDoc(newKey));
7050        } else {
7051            keyActionMap.put(new Integer JavaDoc(newKey), new Integer JavaDoc(action));
7052        }
7053    } else {
7054        if (action == SWT.NULL) {
7055            keyActionMap.remove(new Integer JavaDoc(key));
7056        } else {
7057            keyActionMap.put(new Integer JavaDoc(key), new Integer JavaDoc(action));
7058        }
7059    }
7060}
7061/**
7062 * Sets the alignment of the specified lines. The argument should be one of <code>SWT.LEFT</code>,
7063 * <code>SWT.CENTER</code> or <code>SWT.RIGHT</code>.
7064 * <p>
7065 * Should not be called if a LineStyleListener has been set since the listener
7066 * maintains the line attributes.
7067 * </p><p>
7068 * All line attributes are maintained relative to the line text, not the
7069 * line index that is specified in this method call.
7070 * During text changes, when entire lines are inserted or removed, the line
7071 * attributes that are associated with the lines after the change
7072 * will "move" with their respective text. An entire line is defined as
7073 * extending from the first character on a line to the last and including the
7074 * line delimiter.
7075 * </p><p>
7076 * When two lines are joined by deleting a line delimiter, the top line
7077 * attributes take precedence and the attributes of the bottom line are deleted.
7078 * For all other text changes line attributes will remain unchanged.
7079 *
7080 * @param startLine first line the alignment is applied to, 0 based
7081 * @param lineCount number of lines the alignment applies to.
7082 * @param alignment line alignment
7083 *
7084 * @exception SWTException <ul>
7085 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7086 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7087 * </ul>
7088 * @exception IllegalArgumentException <ul>
7089 * <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
7090 * </ul>
7091 * @see #setAlignment(int)
7092 * @since 3.2
7093 */

7094public void setLineAlignment(int startLine, int lineCount, int alignment) {
7095    checkWidget();
7096    if (isListening(LineGetStyle)) return;
7097    if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
7098        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
7099    }
7100
7101    renderer.setLineAlignment(startLine, lineCount, alignment);
7102    resetCache(startLine, lineCount);
7103    redrawLines(startLine, lineCount);
7104    int caretLine = getCaretLine();
7105    if (startLine <= caretLine && caretLine < startLine + lineCount) {
7106        setCaretLocation();
7107    }
7108}
7109/**
7110 * Sets the background color of the specified lines.
7111 * <p>
7112 * The background color is drawn for the width of the widget. All
7113 * line background colors are discarded when setText is called.
7114 * The text background color if defined in a StyleRange overlays the
7115 * line background color.
7116 * </p><p>
7117 * Should not be called if a LineBackgroundListener has been set since the
7118 * listener maintains the line backgrounds.
7119 * </p><p>
7120 * All line attributes are maintained relative to the line text, not the
7121 * line index that is specified in this method call.
7122 * During text changes, when entire lines are inserted or removed, the line
7123 * attributes that are associated with the lines after the change
7124 * will "move" with their respective text. An entire line is defined as
7125 * extending from the first character on a line to the last and including the
7126 * line delimiter.
7127 * </p><p>
7128 * When two lines are joined by deleting a line delimiter, the top line
7129 * attributes take precedence and the attributes of the bottom line are deleted.
7130 * For all other text changes line attributes will remain unchanged.
7131 * </p>
7132 *
7133 * @param startLine first line the color is applied to, 0 based
7134 * @param lineCount number of lines the color applies to.
7135 * @param background line background color
7136 * @exception SWTException <ul>
7137 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7138 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7139 * </ul>
7140 * @exception IllegalArgumentException <ul>
7141 * <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
7142 * </ul>
7143 */

7144public void setLineBackground(int startLine, int lineCount, Color background) {
7145    checkWidget();
7146    if (isListening(LineGetBackground)) return;
7147    if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
7148        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
7149    }
7150    if (background != null) {
7151        renderer.setLineBackground(startLine, lineCount, background);
7152    } else {
7153        renderer.clearLineBackground(startLine, lineCount);
7154    }
7155    redrawLines(startLine, lineCount);
7156}
7157/**
7158 * Sets the bullet of the specified lines.
7159 * <p>
7160 * Should not be called if a LineStyleListener has been set since the listener
7161 * maintains the line attributes.
7162 * </p><p>
7163 * All line attributes are maintained relative to the line text, not the
7164 * line index that is specified in this method call.
7165 * During text changes, when entire lines are inserted or removed, the line
7166 * attributes that are associated with the lines after the change
7167 * will "move" with their respective text. An entire line is defined as
7168 * extending from the first character on a line to the last and including the
7169 * line delimiter.
7170 * </p><p>
7171 * When two lines are joined by deleting a line delimiter, the top line
7172 * attributes take precedence and the attributes of the bottom line are deleted.
7173 * For all other text changes line attributes will remain unchanged.
7174 * </p>
7175 *
7176 * @param startLine first line the bullet is applied to, 0 based
7177 * @param lineCount number of lines the bullet applies to.
7178 * @param bullet line bullet
7179 *
7180 * @exception SWTException <ul>
7181 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7182 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7183 * </ul>
7184 * @exception IllegalArgumentException <ul>
7185 * <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
7186 * </ul>
7187 * @since 3.2
7188 */

7189public void setLineBullet(int startLine, int lineCount, Bullet bullet) {
7190    checkWidget();
7191    if (isListening(LineGetStyle)) return;
7192    if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
7193        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
7194    }
7195
7196    renderer.setLineBullet(startLine, lineCount, bullet);
7197    resetCache(startLine, lineCount);
7198    redrawLines(startLine, lineCount);
7199    int caretLine = getCaretLine();
7200    if (startLine <= caretLine && caretLine < startLine + lineCount) {
7201        setCaretLocation();
7202    }
7203}
7204void setVariableLineHeight () {
7205    if (!fixedLineHeight) return;
7206    fixedLineHeight = false;
7207    renderer.calculateIdle();
7208}
7209/**
7210 * Sets the indent of the specified lines.
7211 * <p>
7212 * Should not be called if a LineStyleListener has been set since the listener
7213 * maintains the line attributes.
7214 * </p><p>
7215 * All line attributes are maintained relative to the line text, not the
7216 * line index that is specified in this method call.
7217 * During text changes, when entire lines are inserted or removed, the line
7218 * attributes that are associated with the lines after the change
7219 * will "move" with their respective text. An entire line is defined as
7220 * extending from the first character on a line to the last and including the
7221 * line delimiter.
7222 * </p><p>
7223 * When two lines are joined by deleting a line delimiter, the top line
7224 * attributes take precedence and the attributes of the bottom line are deleted.
7225 * For all other text changes line attributes will remain unchanged.
7226 * </p>
7227 *
7228 * @param startLine first line the indent is applied to, 0 based
7229 * @param lineCount number of lines the indent applies to.
7230 * @param indent line indent
7231 *
7232 * @exception SWTException <ul>
7233 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7234 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7235 * </ul>
7236 * @exception IllegalArgumentException <ul>
7237 * <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
7238 * </ul>
7239 * @see #setIndent(int)
7240 * @since 3.2
7241 */

7242public void setLineIndent(int startLine, int lineCount, int indent) {
7243    checkWidget();
7244    if (isListening(LineGetStyle)) return;
7245    if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
7246        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
7247    }
7248
7249    renderer.setLineIndent(startLine, lineCount, indent);
7250    resetCache(startLine, lineCount);
7251    redrawLines(startLine, lineCount);
7252    int caretLine = getCaretLine();
7253    if (startLine <= caretLine && caretLine < startLine + lineCount) {
7254        setCaretLocation();
7255    }
7256}
7257/**
7258 * Sets the justify of the specified lines.
7259 * <p>
7260 * Should not be called if a LineStyleListener has been set since the listener
7261 * maintains the line attributes.
7262 * </p><p>
7263 * All line attributes are maintained relative to the line text, not the
7264 * line index that is specified in this method call.
7265 * During text changes, when entire lines are inserted or removed, the line
7266 * attributes that are associated with the lines after the change
7267 * will "move" with their respective text. An entire line is defined as
7268 * extending from the first character on a line to the last and including the
7269 * line delimiter.
7270 * </p><p>
7271 * When two lines are joined by deleting a line delimiter, the top line
7272 * attributes take precedence and the attributes of the bottom line are deleted.
7273 * For all other text changes line attributes will remain unchanged.
7274 * </p>
7275 *
7276 * @param startLine first line the justify is applied to, 0 based
7277 * @param lineCount number of lines the justify applies to.
7278 * @param justify true if lines should be justified
7279 *
7280 * @exception SWTException <ul>
7281 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7282 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7283 * </ul>
7284 * @exception IllegalArgumentException <ul>
7285 * <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
7286 * </ul>
7287 * @see #setJustify(boolean)
7288 * @since 3.2
7289 */

7290public void setLineJustify(int startLine, int lineCount, boolean justify) {
7291    checkWidget();
7292    if (isListening(LineGetStyle)) return;
7293    if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
7294        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
7295    }
7296
7297    renderer.setLineJustify(startLine, lineCount, justify);
7298    resetCache(startLine, lineCount);
7299    redrawLines(startLine, lineCount);
7300    int caretLine = getCaretLine();
7301    if (startLine <= caretLine && caretLine < startLine + lineCount) {
7302        setCaretLocation();
7303    }
7304}
7305/**
7306 * Sets the line spacing of the widget. The line spacing applies for all lines.
7307 *
7308 * @param lineSpacing the line spacing
7309 * @exception SWTException <ul>
7310 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7311 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7312 * </ul>
7313 * @since 3.2
7314 */

7315public void setLineSpacing(int lineSpacing) {
7316    checkWidget();
7317    if (this.lineSpacing == lineSpacing || lineSpacing < 0) return;
7318    this.lineSpacing = lineSpacing;
7319    setVariableLineHeight();
7320    resetCache(0, content.getLineCount());
7321    setCaretLocation();
7322    super.redraw();
7323}
7324void setMargins (int leftMargin, int topMargin, int rightMargin, int bottomMargin) {
7325    checkWidget();
7326    this.leftMargin = leftMargin;
7327    this.topMargin = topMargin;
7328    this.rightMargin = rightMargin;
7329    this.bottomMargin = bottomMargin;
7330    setCaretLocation();
7331}
7332/**
7333 * Flips selection anchor based on word selection direction.
7334 */

7335void setMouseWordSelectionAnchor() {
7336    if (clickCount > 1) {
7337        if (caretOffset < doubleClickSelection.x) {
7338            selectionAnchor = doubleClickSelection.y;
7339        } else if (caretOffset > doubleClickSelection.y) {
7340            selectionAnchor = doubleClickSelection.x;
7341        }
7342    }
7343}
7344/**
7345 * Sets the orientation of the receiver, which must be one
7346 * of the constants <code>SWT.LEFT_TO_RIGHT</code> or <code>SWT.RIGHT_TO_LEFT</code>.
7347 *
7348 * @param orientation new orientation style
7349 *
7350 * @exception SWTException <ul>
7351 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7352 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7353 * </ul>
7354 *
7355 * @since 2.1.2
7356 */

7357public void setOrientation(int orientation) {
7358    if ((orientation & (SWT.RIGHT_TO_LEFT | SWT.LEFT_TO_RIGHT)) == 0) {
7359        return;
7360    }
7361    if ((orientation & SWT.RIGHT_TO_LEFT) != 0 && (orientation & SWT.LEFT_TO_RIGHT) != 0) {
7362        return;
7363    }
7364    if ((orientation & SWT.RIGHT_TO_LEFT) != 0 && isMirrored()) {
7365        return;
7366    }
7367    if ((orientation & SWT.LEFT_TO_RIGHT) != 0 && !isMirrored()) {
7368        return;
7369    }
7370    if (!BidiUtil.setOrientation(handle, orientation)) {
7371        return;
7372    }
7373    isMirrored = (orientation & SWT.RIGHT_TO_LEFT) != 0;
7374    caretDirection = SWT.NULL;
7375    resetCache(0, content.getLineCount());
7376    setCaretLocation();
7377    keyActionMap.clear();
7378    createKeyBindings();
7379    super.redraw();
7380}
7381/**
7382 * Adjusts the maximum and the page size of the scroll bars to
7383 * reflect content width/length changes.
7384 *
7385 * @param vertical indicates if the vertical scrollbar also needs to be set
7386 */

7387void setScrollBars(boolean vertical) {
7388    int inactive = 1;
7389    if (vertical || !isFixedLineHeight()) {
7390        ScrollBar verticalBar = getVerticalBar();
7391        if (verticalBar != null) {
7392            int maximum = renderer.getHeight();
7393            // only set the real values if the scroll bar can be used
7394
// (ie. because the thumb size is less than the scroll maximum)
7395
// avoids flashing on Motif, fixes 1G7RE1J and 1G5SE92
7396
if (clientAreaHeight < maximum) {
7397                verticalBar.setMaximum(maximum);
7398                verticalBar.setThumb(clientAreaHeight);
7399                verticalBar.setPageIncrement(clientAreaHeight);
7400            } else if (verticalBar.getThumb() != inactive || verticalBar.getMaximum() != inactive) {
7401                verticalBar.setValues(
7402                    verticalBar.getSelection(),
7403                    verticalBar.getMinimum(),
7404                    inactive,
7405                    inactive,
7406                    verticalBar.getIncrement(),
7407                    inactive);
7408            }
7409        }
7410    }
7411    ScrollBar horizontalBar = getHorizontalBar();
7412    if (horizontalBar != null && horizontalBar.getVisible()) {
7413        int maximum = renderer.getWidth();
7414        // only set the real values if the scroll bar can be used
7415
// (ie. because the thumb size is less than the scroll maximum)
7416
// avoids flashing on Motif, fixes 1G7RE1J and 1G5SE92
7417
if (clientAreaWidth < maximum) {
7418            horizontalBar.setMaximum(maximum);
7419            horizontalBar.setThumb(clientAreaWidth - leftMargin - rightMargin);
7420            horizontalBar.setPageIncrement(clientAreaWidth - leftMargin - rightMargin);
7421        } else if (horizontalBar.getThumb() != inactive || horizontalBar.getMaximum() != inactive) {
7422            horizontalBar.setValues(
7423                horizontalBar.getSelection(),
7424                horizontalBar.getMinimum(),
7425                inactive,
7426                inactive,
7427                horizontalBar.getIncrement(),
7428                inactive);
7429        }
7430    }
7431}
7432/**
7433 * Sets the selection to the given position and scrolls it into view. Equivalent to setSelection(start,start).
7434 *
7435 * @param start new caret position
7436 * @see #setSelection(int,int)
7437 * @exception SWTException <ul>
7438 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7439 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7440 * </ul>
7441 * @exception IllegalArgumentException <ul>
7442 * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
7443 * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
7444 * </ul>
7445 */

7446public void setSelection(int start) {
7447    // checkWidget test done in setSelectionRange
7448
setSelection(start, start);
7449}
7450/**
7451 * Sets the selection and scrolls it into view.
7452 * <p>
7453 * Indexing is zero based. Text selections are specified in terms of
7454 * caret positions. In a text widget that contains N characters, there are
7455 * N+1 caret positions, ranging from 0..N
7456 * </p>
7457 *
7458 * @param point x=selection start offset, y=selection end offset
7459 * The caret will be placed at the selection start when x > y.
7460 * @see #setSelection(int,int)
7461 * @exception SWTException <ul>
7462 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7463 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7464 * </ul>
7465 * @exception IllegalArgumentException <ul>
7466 * <li>ERROR_NULL_ARGUMENT when point is null</li>
7467 * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
7468 * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
7469 * </ul>
7470 */

7471public void setSelection(Point point) {
7472    checkWidget();
7473    if (point == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
7474    setSelection(point.x, point.y);
7475}
7476/**
7477 * Sets the receiver's selection background color to the color specified
7478 * by the argument, or to the default system color for the control
7479 * if the argument is null.
7480 *
7481 * @param color the new color (or null)
7482 *
7483 * @exception IllegalArgumentException <ul>
7484 * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
7485 * </ul>
7486 * @exception SWTException <ul>
7487 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7488 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7489 * </ul>
7490 * @since 2.1
7491 */

7492public void setSelectionBackground (Color color) {
7493    checkWidget ();
7494    if (color != null) {
7495        if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
7496    }
7497    selectionBackground = color;
7498    super.redraw();
7499}
7500/**
7501 * Sets the receiver's selection foreground color to the color specified
7502 * by the argument, or to the default system color for the control
7503 * if the argument is null.
7504 * <p>
7505 * Note that this is a <em>HINT</em>. Some platforms do not allow the application
7506 * to change the selection foreground color.
7507 * </p>
7508 * @param color the new color (or null)
7509 *
7510 * @exception IllegalArgumentException <ul>
7511 * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
7512 * </ul>
7513 * @exception SWTException <ul>
7514 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7515 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7516 * </ul>
7517 * @since 2.1
7518 */

7519public void setSelectionForeground (Color color) {
7520    checkWidget ();
7521    if (color != null) {
7522        if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
7523    }
7524    selectionForeground = color;
7525    super.redraw();
7526}
7527/**
7528 * Sets the selection and scrolls it into view.
7529 * <p>
7530 * Indexing is zero based. Text selections are specified in terms of
7531 * caret positions. In a text widget that contains N characters, there are
7532 * N+1 caret positions, ranging from 0..N
7533 * </p>
7534 *
7535 * @param start selection start offset. The caret will be placed at the
7536 * selection start when start > end.
7537 * @param end selection end offset
7538 * @see #setSelectionRange(int,int)
7539 * @exception SWTException <ul>
7540 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7541 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7542 * </ul>
7543 * @exception IllegalArgumentException <ul>
7544 * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
7545 * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
7546 * </ul>
7547 */

7548public void setSelection(int start, int end) {
7549    setSelectionRange(start, end - start);
7550    showSelection();
7551}
7552/**
7553 * Sets the selection.
7554 * <p>
7555 * The new selection may not be visible. Call showSelection to scroll
7556 * the selection into view.
7557 * </p>
7558 *
7559 * @param start offset of the first selected character, start >= 0 must be true.
7560 * @param length number of characters to select, 0 <= start + length
7561 * <= getCharCount() must be true.
7562 * A negative length places the caret at the selection start.
7563 * @param sendEvent a Selection event is sent when set to true and when
7564 * the selection is reset.
7565 */

7566void setSelection(int start, int length, boolean sendEvent) {
7567    int end = start + length;
7568    if (start > end) {
7569        int temp = end;
7570        end = start;
7571        start = temp;
7572    }
7573    // is the selection range different or is the selection direction
7574
// different?
7575
if (selection.x != start || selection.y != end ||
7576        (length > 0 && selectionAnchor != selection.x) ||
7577        (length < 0 && selectionAnchor != selection.y)) {
7578        clearSelection(sendEvent);
7579        if (length < 0) {
7580            selectionAnchor = selection.y = end;
7581            caretOffset = selection.x = start;
7582        } else {
7583            selectionAnchor = selection.x = start;
7584            caretOffset = selection.y = end;
7585        }
7586        internalRedrawRange(selection.x, selection.y - selection.x);
7587    }
7588}
7589/**
7590 * Sets the selection.
7591 * <p>
7592 * The new selection may not be visible. Call showSelection to scroll the selection
7593 * into view. A negative length places the caret at the visual start of the selection.
7594 * </p>
7595 *
7596 * @param start offset of the first selected character
7597 * @param length number of characters to select
7598 *
7599 * @exception SWTException <ul>
7600 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7601 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7602 * </ul>
7603 * @exception IllegalArgumentException <ul>
7604 * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
7605 * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
7606 * </ul>
7607 */

7608public void setSelectionRange(int start, int length) {
7609    checkWidget();
7610    int contentLength = getCharCount();
7611    start = Math.max(0, Math.min (start, contentLength));
7612    int end = start + length;
7613    if (end < 0) {
7614        length = -start;
7615    } else {
7616        if (end > contentLength) length = contentLength - start;
7617    }
7618    if (isLineDelimiter(start) || isLineDelimiter(start + length)) {
7619        // the start offset or end offset of the selection range is inside a
7620
// multi byte line delimiter. This is an illegal operation and an exception
7621
// is thrown. Fixes 1GDKK3R
7622
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
7623    }
7624    setSelection(start, length, false);
7625    setCaretLocation();
7626}
7627/**
7628 * Adds the specified style.
7629 * <p>
7630 * The new style overwrites existing styles for the specified range.
7631 * Existing style ranges are adjusted if they partially overlap with
7632 * the new style. To clear an individual style, call setStyleRange
7633 * with a StyleRange that has null attributes.
7634 * </p><p>
7635 * Should not be called if a LineStyleListener has been set since the
7636 * listener maintains the styles.
7637 * </p>
7638 *
7639 * @param range StyleRange object containing the style information.
7640 * Overwrites the old style in the given range. May be null to delete
7641 * all styles.
7642 * @exception SWTException <ul>
7643 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7644 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7645 * </ul>
7646 * @exception IllegalArgumentException <ul>
7647 * <li>ERROR_INVALID_RANGE when the style range is outside the valid range (> getCharCount())</li>
7648 * </ul>
7649 */

7650public void setStyleRange(StyleRange range) {
7651    checkWidget();
7652    if (isListening(LineGetStyle)) return;
7653    if (range != null) {
7654        if (range.isUnstyled()) {
7655            setStyleRanges(range.start, range.length, null, null, false);
7656        } else {
7657            setStyleRanges(range.start, 0, null, new StyleRange[]{range}, false);
7658        }
7659    } else {
7660        setStyleRanges(0, 0, null, null, true);
7661    }
7662}
7663/**
7664 * Clears the styles in the range specified by <code>start</code> and
7665 * <code>length</code> and adds the new styles.
7666 * <p>
7667 * The ranges array contains start and length pairs. Each pair refers to
7668 * the corresponding style in the styles array. For example, the pair
7669 * that starts at ranges[n] with length ranges[n+1] uses the style
7670 * at styles[n/2]. The range fields within each StyleRange are ignored.
7671 * If ranges or styles is null, the specified range is cleared.
7672 * </p><p>
7673 * Note: It is expected that the same instance of a StyleRange will occur
7674 * multiple times within the styles array, reducing memory usage.
7675 * </p><p>
7676 * Should not be called if a LineStyleListener has been set since the
7677 * listener maintains the styles.
7678 * </p>
7679 *
7680 * @param start offset of first character where styles will be deleted
7681 * @param length length of the range to delete styles in
7682 * @param ranges the array of ranges. The ranges must not overlap and must be in order.
7683 * @param styles the array of StyleRanges. The range fields within the StyleRange are unused.
7684 *
7685 * @exception SWTException <ul>
7686 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7687 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7688 * </ul>
7689 * @exception IllegalArgumentException <ul>
7690 * <li>ERROR_NULL_ARGUMENT when an element in the styles array is null</li>
7691 * <li>ERROR_INVALID_RANGE when the number of ranges and style do not match (ranges.length * 2 == styles.length)</li>
7692 * <li>ERROR_INVALID_RANGE when a range is outside the valid range (> getCharCount() or less than zero)</li>
7693 * <li>ERROR_INVALID_RANGE when a range overlaps</li>
7694 * </ul>
7695 *
7696 * @since 3.2
7697 */

7698public void setStyleRanges(int start, int length, int[] ranges, StyleRange[] styles) {
7699    checkWidget();
7700    if (isListening(LineGetStyle)) return;
7701    if (ranges == null || styles == null) {
7702        setStyleRanges(start, length, null, null, false);
7703    } else {
7704        setStyleRanges(start, length, ranges, styles, false);
7705    }
7706}
7707/**
7708 * Sets styles to be used for rendering the widget content.
7709 * <p>
7710 * All styles in the widget will be replaced with the given set of ranges and styles.
7711 * The ranges array contains start and length pairs. Each pair refers to
7712 * the corresponding style in the styles array. For example, the pair
7713 * that starts at ranges[n] with length ranges[n+1] uses the style
7714 * at styles[n/2]. The range fields within each StyleRange are ignored.
7715 * If either argument is null, the styles are cleared.
7716 * </p><p>
7717 * Note: It is expected that the same instance of a StyleRange will occur
7718 * multiple times within the styles array, reducing memory usage.
7719 * </p><p>
7720 * Should not be called if a LineStyleListener has been set since the
7721 * listener maintains the styles.
7722 * </p>
7723 *
7724 * @param ranges the array of ranges. The ranges must not overlap and must be in order.
7725 * @param styles the array of StyleRanges. The range fields within the StyleRange are unused.
7726 *
7727 * @exception SWTException <ul>
7728 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7729 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7730 * </ul>
7731 * @exception IllegalArgumentException <ul>
7732 * <li>ERROR_NULL_ARGUMENT when an element in the styles array is null</li>
7733 * <li>ERROR_INVALID_RANGE when the number of ranges and style do not match (ranges.length * 2 == styles.length)</li>
7734 * <li>ERROR_INVALID_RANGE when a range is outside the valid range (> getCharCount() or less than zero)</li>
7735 * <li>ERROR_INVALID_RANGE when a range overlaps</li>
7736 * </ul>
7737 *
7738 * @since 3.2
7739 */

7740public void setStyleRanges(int[] ranges, StyleRange[] styles) {
7741    checkWidget();
7742    if (isListening(LineGetStyle)) return;
7743    if (ranges == null || styles == null) {
7744        setStyleRanges(0, 0, null, null, true);
7745    } else {
7746        setStyleRanges(0, 0, ranges, styles, true);
7747    }
7748}
7749void setStyleRanges(int start, int length, int[] ranges, StyleRange[] styles, boolean reset) {
7750    int charCount = content.getCharCount();
7751    int end = start + length;
7752    if (start > end || start < 0) {
7753        SWT.error(SWT.ERROR_INVALID_RANGE);
7754    }
7755    if (styles != null) {
7756        if (end > charCount) {
7757            SWT.error(SWT.ERROR_INVALID_RANGE);
7758        }
7759        if (ranges != null) {
7760            if (ranges.length != styles.length << 1) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
7761        }
7762        int lastOffset = 0;
7763        boolean variableHeight = false;
7764        for (int i = 0; i < styles.length; i ++) {
7765            if (styles[i] == null) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
7766            int rangeStart, rangeLength;
7767            if (ranges != null) {
7768                rangeStart = ranges[i << 1];
7769                rangeLength = ranges[(i << 1) + 1];
7770            } else {
7771                rangeStart = styles[i].start;
7772                rangeLength = styles[i].length;
7773            }
7774            if (rangeLength < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
7775            if (!(0 <= rangeStart && rangeStart + rangeLength <= charCount)) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
7776            if (lastOffset > rangeStart) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
7777            variableHeight |= styles[i].isVariableHeight();
7778            lastOffset = rangeStart + rangeLength;
7779        }
7780        if (variableHeight) setVariableLineHeight();
7781    }
7782    int rangeStart = start, rangeEnd = end;
7783    if (styles != null && styles.length > 0) {
7784        if (ranges != null) {
7785            rangeStart = ranges[0];
7786            rangeEnd = ranges[ranges.length - 2] + ranges[ranges.length - 1];
7787        } else {
7788            rangeStart = styles[0].start;
7789            rangeEnd = styles[styles.length - 1].start + styles[styles.length - 1].length;
7790        }
7791    }
7792    int lastLineBottom = 0;
7793    if (!isFixedLineHeight() && !reset) {
7794        int lineEnd = content.getLineAtOffset(Math.max(end, rangeEnd));
7795        int partialTopIndex = getPartialTopIndex();
7796        int partialBottomIndex = getPartialBottomIndex();
7797        if (partialTopIndex <= lineEnd && lineEnd <= partialBottomIndex) {
7798            lastLineBottom = getLinePixel(lineEnd + 1);
7799        }
7800    }
7801    if (reset) {
7802        renderer.setStyleRanges(null, null);
7803    } else {
7804        renderer.updateRanges(start, length, length);
7805    }
7806    if (styles != null && styles.length > 0) {
7807        renderer.setStyleRanges(ranges, styles);
7808    }
7809    if (reset) {
7810        resetCache(0, content.getLineCount());
7811        super.redraw();
7812    } else {
7813        int lineStart = content.getLineAtOffset(Math.min(start, rangeStart));
7814        int lineEnd = content.getLineAtOffset(Math.max(end, rangeEnd));
7815        resetCache(lineStart, lineEnd - lineStart + 1);
7816        int partialTopIndex = getPartialTopIndex();
7817        int partialBottomIndex = getPartialBottomIndex();
7818        if (!(lineStart > partialBottomIndex || lineEnd < partialTopIndex)) {
7819            int y = 0;
7820            int height = clientAreaHeight;
7821            if (partialTopIndex <= lineStart && lineStart <= partialBottomIndex) {
7822                int lineTop = Math.max(y, getLinePixel(lineStart));
7823                y = lineTop;
7824                height -= lineTop;
7825            }
7826            if (partialTopIndex <= lineEnd && lineEnd <= partialBottomIndex) {
7827                int newLastLineBottom = getLinePixel(lineEnd + 1);
7828                if (!isFixedLineHeight()) {
7829                    scrollText(lastLineBottom, newLastLineBottom);
7830                }
7831                height = newLastLineBottom - y;
7832            }
7833            super.redraw(0, y, clientAreaWidth, height, false);
7834        }
7835    }
7836    setCaretLocation();
7837}
7838/**
7839 * Sets styles to be used for rendering the widget content. All styles
7840 * in the widget will be replaced with the given set of styles.
7841 * <p>
7842 * Note: Because a StyleRange includes the start and length, the
7843 * same instance cannot occur multiple times in the array of styles.
7844 * If the same style attributes, such as font and color, occur in
7845 * multiple StyleRanges, <code>setStyleRanges(int[], StyleRange[])</code>
7846 * can be used to share styles and reduce memory usage.
7847 * </p><p>
7848 * Should not be called if a LineStyleListener has been set since the
7849 * listener maintains the styles.
7850 * </p>
7851 *
7852 * @param ranges StyleRange objects containing the style information.
7853 * The ranges should not overlap. The style rendering is undefined if
7854 * the ranges do overlap. Must not be null. The styles need to be in order.
7855 * @exception SWTException <ul>
7856 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7857 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7858 * </ul>
7859 * @exception IllegalArgumentException <ul>
7860 * <li>ERROR_NULL_ARGUMENT when the list of ranges is null</li>
7861 * <li>ERROR_INVALID_RANGE when the last of the style ranges is outside the valid range (> getCharCount())</li>
7862 * </ul>
7863 *
7864 * @see #setStyleRanges(int[], StyleRange[])
7865 */

7866public void setStyleRanges(StyleRange[] ranges) {
7867    checkWidget();
7868    if (isListening(LineGetStyle)) return;
7869    if (ranges == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7870    setStyleRanges(0, 0, null, ranges, true);
7871}
7872/**
7873 * Sets the tab width.
7874 *
7875 * @param tabs tab width measured in characters.
7876 * @exception SWTException <ul>
7877 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7878 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7879 * </ul>
7880 */

7881public void setTabs(int tabs) {
7882    checkWidget();
7883    tabLength = tabs;
7884    renderer.setFont(null, tabs);
7885    if (caretOffset > 0) {
7886        caretOffset = 0;
7887        showCaret();
7888        clearSelection(false);
7889    }
7890    resetCache(0, content.getLineCount());
7891    super.redraw();
7892}
7893/**
7894 * Sets the widget content.
7895 * If the widget has the SWT.SINGLE style and "text" contains more than
7896 * one line, only the first line is rendered but the text is stored
7897 * unchanged. A subsequent call to getText will return the same text
7898 * that was set.
7899 * <p>
7900 * <b>Note:</b> Only a single line of text should be set when the SWT.SINGLE
7901 * style is used.
7902 * </p>
7903 *
7904 * @param text new widget content. Replaces existing content. Line styles
7905 * that were set using StyledText API are discarded. The
7906 * current selection is also discarded.
7907 * @exception SWTException <ul>
7908 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7909 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7910 * </ul>
7911 * @exception IllegalArgumentException <ul>
7912 * <li>ERROR_NULL_ARGUMENT when string is null</li>
7913 * </ul>
7914 */

7915public void setText(String JavaDoc text) {
7916    checkWidget();
7917    if (text == null) {
7918        SWT.error(SWT.ERROR_NULL_ARGUMENT);
7919    }
7920    Event event = new Event();
7921    event.start = 0;
7922    event.end = getCharCount();
7923    event.text = text;
7924    event.doit = true;
7925    notifyListeners(SWT.Verify, event);
7926    if (event.doit) {
7927        StyledTextEvent styledTextEvent = null;
7928        if (isListening(ExtendedModify)) {
7929            styledTextEvent = new StyledTextEvent(content);
7930            styledTextEvent.start = event.start;
7931            styledTextEvent.end = event.start + event.text.length();
7932            styledTextEvent.text = content.getTextRange(event.start, event.end - event.start);
7933        }
7934        content.setText(event.text);
7935        sendModifyEvent(event);
7936        if (styledTextEvent != null) {
7937            notifyListeners(ExtendedModify, styledTextEvent);
7938        }
7939    }
7940}
7941/**
7942 * Sets the text limit to the specified number of characters.
7943 * <p>
7944 * The text limit specifies the amount of text that
7945 * the user can type into the widget.
7946 * </p>
7947 *
7948 * @param limit the new text limit.
7949 * @exception SWTException <ul>
7950 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7951 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7952 * </ul>
7953 * @exception IllegalArgumentException <ul>
7954 * <li>ERROR_CANNOT_BE_ZERO when limit is 0</li>
7955 * </ul>
7956 */

7957public void setTextLimit(int limit) {
7958    checkWidget();
7959    if (limit == 0) {
7960        SWT.error(SWT.ERROR_CANNOT_BE_ZERO);
7961    }
7962    textLimit = limit;
7963}
7964/**
7965 * Sets the top index. Do nothing if there is no text set.
7966 * <p>
7967 * The top index is the index of the line that is currently at the top
7968 * of the widget. The top index changes when the widget is scrolled.
7969 * Indexing starts from zero.
7970 * Note: The top index is reset to 0 when new text is set in the widget.
7971 * </p>
7972 *
7973 * @param topIndex new top index. Must be between 0 and
7974 * getLineCount() - fully visible lines per page. If no lines are fully
7975 * visible the maximum value is getLineCount() - 1. An out of range
7976 * index will be adjusted accordingly.
7977 * @exception SWTException <ul>
7978 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7979 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7980 * </ul>
7981 */

7982public void setTopIndex(int topIndex) {
7983    checkWidget();
7984    if (getCharCount() == 0) {
7985        return;
7986    }
7987    int lineCount = content.getLineCount(), pixel;
7988    if (isFixedLineHeight()) {
7989        int pageSize = Math.max(1, Math.min(lineCount, getLineCountWhole()));
7990        if (topIndex < 0) {
7991            topIndex = 0;
7992        } else if (topIndex > lineCount - pageSize) {
7993            topIndex = lineCount - pageSize;
7994        }
7995        pixel = getLinePixel(topIndex);
7996    } else {
7997        topIndex = Math.max(0, Math.min(lineCount - 1, topIndex));
7998        pixel = getLinePixel(topIndex);
7999        if (pixel > 0) {
8000            pixel = getAvailableHeightBellow(pixel);
8001        } else {
8002            pixel = getAvailableHeightAbove(pixel);
8003        }
8004    }
8005    scrollVertical(pixel, true);
8006}
8007/**
8008 * Sets the top pixel offset. Do nothing if there is no text set.
8009 * <p>
8010 * The top pixel offset is the vertical pixel offset of the widget. The
8011 * widget is scrolled so that the given pixel position is at the top.
8012 * The top index is adjusted to the corresponding top line.
8013 * Note: The top pixel is reset to 0 when new text is set in the widget.
8014 * </p>
8015 *
8016 * @param pixel new top pixel offset. Must be between 0 and
8017 * (getLineCount() - visible lines per page) / getLineHeight()). An out
8018 * of range offset will be adjusted accordingly.
8019 * @exception SWTException <ul>
8020 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8021 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8022 * </ul>
8023 * @since 2.0
8024 */

8025public void setTopPixel(int pixel) {
8026    checkWidget();
8027    if (getCharCount() == 0) {
8028        return;
8029    }
8030    if (pixel < 0) pixel = 0;
8031    int lineCount = content.getLineCount();
8032    int height = clientAreaHeight - topMargin - bottomMargin;
8033    int verticalOffset = getVerticalScrollOffset();
8034    if (isFixedLineHeight()) {
8035        int maxTopPixel = Math.max(0, lineCount * getVerticalIncrement() - height);
8036        if (pixel > maxTopPixel) pixel = maxTopPixel;
8037        pixel -= verticalOffset;
8038    } else {
8039        pixel -= verticalOffset;
8040        if (pixel > 0) {
8041            pixel = getAvailableHeightBellow(pixel);
8042        }
8043    }
8044    scrollVertical(pixel, true);
8045}
8046/**
8047 * Sets whether the widget wraps lines.
8048 * <p>
8049 * This overrides the creation style bit SWT.WRAP.
8050 * </p>
8051 *
8052 * @param wrap true=widget wraps lines, false=widget does not wrap lines
8053 * @since 2.0
8054 */

8055public void setWordWrap(boolean wrap) {
8056    checkWidget();
8057    if ((getStyle() & SWT.SINGLE) != 0) return;
8058    if (wordWrap == wrap) return;
8059    wordWrap = wrap;
8060    setVariableLineHeight();
8061    resetCache(0, content.getLineCount());
8062    horizontalScrollOffset = 0;
8063    ScrollBar horizontalBar = getHorizontalBar();
8064    if (horizontalBar != null) {
8065        horizontalBar.setVisible(!wordWrap);
8066    }
8067    setScrollBars(true);
8068    setCaretLocation();
8069    super.redraw();
8070}
8071boolean showLocation(Rectangle rect, boolean scrollPage) {
8072    int clientAreaWidth = this.clientAreaWidth - leftMargin - rightMargin;
8073    int clientAreaHeight = this.clientAreaHeight - topMargin - bottomMargin;
8074    boolean scrolled = false;
8075    if (rect.y <= topMargin) {
8076        scrolled = scrollVertical(rect.y - topMargin, true);
8077    } else if (rect.y + rect.height > clientAreaHeight) {
8078        if (clientAreaHeight == 0) {
8079            scrolled = scrollVertical(rect.y, true);
8080        } else {
8081            scrolled = scrollVertical(rect.y + rect.height - clientAreaHeight, true);
8082        }
8083    }
8084    if (clientAreaWidth > 0) {
8085        int minScroll = scrollPage ? clientAreaWidth / 4 : 0;
8086        if (rect.x < leftMargin) {
8087            int scrollWidth = Math.max(leftMargin - rect.x, minScroll);
8088            int maxScroll = horizontalScrollOffset;
8089            scrolled = scrollHorizontal(-Math.min(maxScroll, scrollWidth), true);
8090        } else if (rect.x + rect.width > clientAreaWidth) {
8091            int scrollWidth = Math.max(rect.x + rect.width - clientAreaWidth, minScroll);
8092            int maxScroll = renderer.getWidth() - horizontalScrollOffset - this.clientAreaWidth;
8093            scrolled = scrollHorizontal(Math.min(maxScroll, scrollWidth), true);
8094        }
8095    }
8096    return scrolled;
8097}
8098/**
8099 * Sets the caret location and scrolls the caret offset into view.
8100 */

8101void showCaret() {
8102    Rectangle bounds = getBoundsAtOffset(caretOffset);
8103    if (!showLocation(bounds, true)) {
8104        setCaretLocation();
8105    }
8106}
8107/**
8108 * Scrolls the selection into view.
8109 * <p>
8110 * The end of the selection will be scrolled into view.
8111 * Note that if a right-to-left selection exists, the end of the selection is
8112 * the visual beginning of the selection (i.e., where the caret is located).
8113 * </p>
8114 *
8115 * @exception SWTException <ul>
8116 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8117 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8118 * </ul>
8119 */

8120public void showSelection() {
8121    checkWidget();
8122    // is selection from right-to-left?
8123
boolean rightToLeft = caretOffset == selection.x;
8124    int startOffset, endOffset;
8125    if (rightToLeft) {
8126        startOffset = selection.y;
8127        endOffset = selection.x;
8128    } else {
8129        startOffset = selection.x;
8130        endOffset = selection.y;
8131    }
8132    
8133    Rectangle startBounds = getBoundsAtOffset(startOffset);
8134    Rectangle endBounds = getBoundsAtOffset(endOffset);
8135    
8136    // can the selection be fully displayed within the widget's visible width?
8137
int w = clientAreaWidth - leftMargin - rightMargin;
8138    boolean selectionFits = rightToLeft ? startBounds.x - endBounds.x <= w : endBounds.x - startBounds.x <= w;
8139    if (selectionFits) {
8140        // show as much of the selection as possible by first showing
8141
// the start of the selection
8142
if (showLocation(startBounds, false)) {
8143            // endX value could change if showing startX caused a scroll to occur
8144
endBounds = getBoundsAtOffset(endOffset);
8145        }
8146        // the character at endOffset is not part of the selection
8147
endBounds.width = 0;
8148        showLocation(endBounds, false);
8149    } else {
8150        // just show the end of the selection since the selection start
8151
// will not be visible
8152
showLocation(endBounds, true);
8153    }
8154}
8155/**
8156 * Updates the selection and caret position depending on the text change.
8157 * <p>
8158 * If the selection intersects with the replaced text, the selection is
8159 * reset and the caret moved to the end of the new text.
8160 * If the selection is behind the replaced text it is moved so that the
8161 * same text remains selected. If the selection is before the replaced text
8162 * it is left unchanged.
8163 * </p>
8164 *
8165 * @param startOffset offset of the text change
8166 * @param replacedLength length of text being replaced
8167 * @param newLength length of new text
8168 */

8169void updateSelection(int startOffset, int replacedLength, int newLength) {
8170    if (selection.y <= startOffset) {
8171        // selection ends before text change
8172
return;
8173    }
8174    if (selection.x < startOffset) {
8175        // clear selection fragment before text change
8176
internalRedrawRange(selection.x, startOffset - selection.x);
8177    }
8178    if (selection.y > startOffset + replacedLength && selection.x < startOffset + replacedLength) {
8179        // clear selection fragment after text change.
8180
// do this only when the selection is actually affected by the
8181
// change. Selection is only affected if it intersects the change (1GDY217).
8182
int netNewLength = newLength - replacedLength;
8183        int redrawStart = startOffset + newLength;
8184        internalRedrawRange(redrawStart, selection.y + netNewLength - redrawStart);
8185    }
8186    if (selection.y > startOffset && selection.x < startOffset + replacedLength) {
8187        // selection intersects replaced text. set caret behind text change
8188
setSelection(startOffset + newLength, 0, true);
8189    } else {
8190        // move selection to keep same text selected
8191
setSelection(selection.x + newLength - replacedLength, selection.y - selection.x, true);
8192    }
8193    setCaretLocation();
8194}
8195}
8196
Popular Tags