View | Details | Raw Unified | Return to bug 19825 | Differences between
and this patch

Collapse All | Expand All

(-)StyledText.java (-5749 / +5864 lines)
Lines 1-9 Link Here
1
package org.eclipse.swt.custom;
1
package org.eclipse.swt.custom;
2
2
3
/*
3
/*
4
 * Copyright (c) 2000, 2002 IBM Corp.  All rights reserved.
4
 * Copyright (c) 2000, 2002 IBM Corp.  All rights reserved.
5
 * This file is made available under the terms of the Common Public License v1.0
5
 * This file is made available under the terms of the Common Public License v1.0
6
 * which accompanies this distribution, and is available at
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/cpl-v10.html
7
 * http://www.eclipse.org/legal/cpl-v10.html
8
 */
8
 */
9
9
Lines 18-39 Link Here
18
import org.eclipse.swt.widgets.*;
18
import org.eclipse.swt.widgets.*;
19
19
20
/**
20
/**
21
 * A StyledText is an editable user interface object that displays lines 
21
 * A StyledText is an editable user interface object that displays lines
22
 * of text.  The following style attributes can be defined for the text: 
22
 * of text.  The following style attributes can be defined for the text:
23
 * <ul>
23
 * <ul>
24
 * <li>foreground color 
24
 * <li>foreground color
25
 * <li>background color
25
 * <li>background color
26
 * <li>font style (bold, regular)
26
 * <li>font style (bold, regular)
27
 * </ul>
27
 * </ul>
28
 * <p>
28
 * <p>
29
 * In addition to text style attributes, the background color of a line may 
29
 * In addition to text style attributes, the background color of a line may
30
 * be specified.
30
 * be specified.
31
 * </p>
31
 * </p>
32
 * <p>
32
 * <p>
33
 * There are two ways to use this widget when specifying text style information.  
33
 * There are two ways to use this widget when specifying text style information.
34
 * You may use the API that is defined for StyledText or you may define your own 
34
 * You may use the API that is defined for StyledText or you may define your own
35
 * LineStyleListener.  If you define your own listener, you will be responsible 
35
 * LineStyleListener.  If you define your own listener, you will be responsible
36
 * for maintaining the text style information for the widget.  IMPORTANT: You may 
36
 * for maintaining the text style information for the widget.  IMPORTANT: You may
37
 * not define your own listener and use the StyledText API.  The following
37
 * not define your own listener and use the StyledText API.  The following
38
 * StyledText API is not supported if you have defined a LineStyleListener:
38
 * StyledText API is not supported if you have defined a LineStyleListener:
39
 * <ul>
39
 * <ul>
Lines 46-56 Link Here
46
 * </p>
46
 * </p>
47
 * <p>
47
 * <p>
48
 * There are two ways to use this widget when specifying line background colors.
48
 * There are two ways to use this widget when specifying line background colors.
49
 * You may use the API that is defined for StyledText or you may define your own 
49
 * You may use the API that is defined for StyledText or you may define your own
50
 * LineBackgroundListener.  If you define your own listener, you will be responsible 
50
 * LineBackgroundListener.  If you define your own listener, you will be responsible
51
 * for maintaining the line background color information for the widget.  
51
 * for maintaining the line background color information for the widget.
52
 * IMPORTANT: You may not define your own listener and use the StyledText API.  
52
 * IMPORTANT: You may not define your own listener and use the StyledText API.
53
 * The following StyledText API is not supported if you have defined a 
53
 * The following StyledText API is not supported if you have defined a
54
 * LineBackgroundListener:
54
 * LineBackgroundListener:
55
 * <ul>
55
 * <ul>
56
 * <li>getLineBackground(int)
56
 * <li>getLineBackground(int)
Lines 60-66 Link Here
60
 * <p>
60
 * <p>
61
 * The content implementation for this widget may also be user-defined.  To do so,
61
 * The content implementation for this widget may also be user-defined.  To do so,
62
 * you must implement the StyledTextContent interface and use the StyledText API
62
 * you must implement the StyledTextContent interface and use the StyledText API
63
 * setContent(StyledTextContent) to initialize the widget. 
63
 * setContent(StyledTextContent) to initialize the widget.
64
 * </p>
64
 * </p>
65
 * <p>
65
 * <p>
66
 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
66
 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
Lines 71-1550 Link Here
71
 * </dl>
71
 * </dl>
72
 */
72
 */
73
public class StyledText extends Canvas {
73
public class StyledText extends Canvas {
74
	static final char TAB = '\t';
74
   static final char TAB = '\t';
75
	static final String PlatformLineDelimiter = System.getProperty("line.separator");
75
   static final String PlatformLineDelimiter = System.getProperty("line.separator");
76
	static final int BIDI_CARET_WIDTH = 4;		
76
   static final int BIDI_CARET_WIDTH = 4;
77
	static final int XINSET = BIDI_CARET_WIDTH - 1;
77
   static final int XINSET = BIDI_CARET_WIDTH - 1;
78
	static final int DEFAULT_WIDTH	= 64;
78
   static final int DEFAULT_WIDTH   = 64;
79
	static final int DEFAULT_HEIGHT = 64;
79
   static final int DEFAULT_HEIGHT = 64;
80
	
80
81
	static final int ExtendedModify = 3000;
81
   static final int ExtendedModify = 3000;
82
	static final int LineGetBackground = 3001;
82
   static final int LineGetBackground = 3001;
83
	static final int LineGetStyle = 3002;
83
   static final int LineGetStyle = 3002;
84
	static final int TextChanging = 3003;
84
   static final int TextChanging = 3003;
85
	static final int TextSet = 3004;
85
   static final int TextSet = 3004;
86
	static final int VerifyKey = 3005;
86
   static final int VerifyKey = 3005;
87
	static final int TextChanged = 3006;
87
   static final int TextChanged = 3006;
88
	static final int LineGetSegments = 3007;
88
   static final int LineGetSegments = 3007;
89
	
89
90
	Color selectionBackground;	// selection background color
90
   Color selectionBackground; // selection background color
91
	Color selectionForeground;	// selection foreground color
91
   Color selectionForeground; // selection foreground color
92
	StyledTextContent logicalContent;	// native content (default or user specified)
92
   StyledTextContent logicalContent;   // native content (default or user specified)
93
	StyledTextContent content;			// line wrapping content, same as logicalContent if word wrap is off
93
   StyledTextContent content;       // line wrapping content, same as logicalContent if word wrap is off
94
	DisplayRenderer renderer;
94
   DisplayRenderer renderer;
95
	TextChangeListener textChangeListener;	// listener for TextChanging, TextChanged and TextSet events from StyledTextContent
95
   TextChangeListener textChangeListener; // listener for TextChanging, TextChanged and TextSet events from StyledTextContent
96
	DefaultLineStyler defaultLineStyler;// used for setStyles API when no LineStyleListener is registered
96
   DefaultLineStyler defaultLineStyler;// used for setStyles API when no LineStyleListener is registered
97
	LineCache lineCache;
97
   LineCache lineCache;
98
	boolean userLineStyle = false;		// true=widget is using a user defined line style listener for line styles. false=widget is using the default line styler to store line styles
98
   boolean userLineStyle = false;      // true=widget is using a user defined line style listener for line styles. false=widget is using the default line styler to store line styles
99
	boolean userLineBackground = false;	// true=widget is using a user defined line background listener for line backgrounds. false=widget is using the default line styler to store line backgrounds
99
   boolean userLineBackground = false; // true=widget is using a user defined line background listener for line backgrounds. false=widget is using the default line styler to store line backgrounds
100
	int verticalScrollOffset = 0;		// pixel based
100
   int verticalScrollOffset = 0;    // pixel based
101
	int horizontalScrollOffset = 0;		// pixel based
101
   int horizontalScrollOffset = 0;     // pixel based
102
	int topIndex = 0;					// top visible line
102
   int topIndex = 0;             // top visible line
103
	int topOffset = 0;					// offset of first character in top line
103
   int topOffset = 0;               // offset of first character in top line
104
	int clientAreaHeight = 0;			// the client area height. Needed to calculate content width for new 
104
   int clientAreaHeight = 0;        // the client area height. Needed to calculate content width for new
105
										// visible lines during Resize callback
105
                              // visible lines during Resize callback
106
	int clientAreaWidth = 0;			// the client area width. Needed during Resize callback to determine 
106
   int clientAreaWidth = 0;         // the client area width. Needed during Resize callback to determine
107
										// if line wrap needs to be recalculated
107
                              // if line wrap needs to be recalculated
108
	int lineHeight;						// line height=font height
108
   int lineHeight;                  // line height=font height
109
	int tabLength = 4;					// number of characters in a tab
109
   int tabLength = 4;               // number of characters in a tab
110
	int lineEndSpaceWidth;				// space, in pixel, used to indicated a selected line break
110
   int lineEndSpaceWidth;           // space, in pixel, used to indicated a selected line break
111
	int leftMargin = 1;
111
   int leftMargin = 1;
112
	int topMargin = 1;
112
   int topMargin = 1;
113
	int rightMargin = 2;
113
   int rightMargin = 2;
114
	int bottomMargin = 2;
114
   int bottomMargin = 2;
115
	Cursor ibeamCursor;		
115
   Cursor ibeamCursor;
116
	int columnX;							// keep track of the horizontal caret position
116
   Cursor arrowCursor;
117
										// when changing lines/pages. Fixes bug 5935
117
   Cursor currentCursor;            // tracks the current mouse pointer
118
	int caretOffset = 0;
118
   Cursor customCursor = null;
119
	Point selection = new Point(0, 0);	// x is character offset, y is length
119
   int columnX;                  // keep track of the horizontal caret position
120
	int selectionAnchor;				// position of selection anchor. 0 based offset from beginning of text
120
                              // when changing lines/pages. Fixes bug 5935
121
	Point doubleClickSelection;			// selection after last mouse double click
121
   int caretOffset = 0;
122
	boolean editable = true;
122
   Point selection = new Point(0, 0);  // x is the beginning character offset, y is the ending character offset
123
	boolean wordWrap = false;
123
   int selectionAnchor;          // position of selection anchor. 0 based offset from beginning of text
124
	boolean doubleClickEnabled = true;	// see getDoubleClickEnabled 
124
   Point doubleClickSelection;         // selection after last mouse double click
125
	boolean overwrite = false;			// insert/overwrite edit mode
125
   boolean editable = true;
126
	int textLimit = -1;					// limits the number of characters the user can type in the widget. Unlimited by default.
126
   boolean wordWrap = false;
127
	Hashtable keyActionMap = new Hashtable();
127
   boolean doubleClickEnabled = true;  // see getDoubleClickEnabled
128
	Color background = null;			// workaround for bug 4791
128
   boolean overwrite = false;       // insert/overwrite edit mode
129
	Color foreground = null;			//
129
   int textLimit = -1;              // limits the number of characters the user can type in the widget. Unlimited by default.
130
	Clipboard clipboard;
130
   Hashtable keyActionMap = new Hashtable();
131
	boolean mouseDoubleClick = false;	// true=a double click ocurred. Don't do mouse swipe selection.
131
   Color background = null;         // workaround for bug 4791
132
	int autoScrollDirection = SWT.NULL;	// the direction of autoscrolling (up, down, right, left)
132
   Color foreground = null;         //
133
	int lastTextChangeStart;			// cache data of the 
133
   Clipboard clipboard;
134
	int lastTextChangeNewLineCount;		// last text changing 
134
   boolean mouseDoubleClick = false;   // true=a double click ocurred. Don't do mouse swipe selection.
135
	int lastTextChangeNewCharCount;		// event for use in the 
135
   int autoScrollDirection = SWT.NULL; // the direction of autoscrolling (up, down, right, left)
136
	int lastTextChangeReplaceLineCount;	// text changed handler
136
   int lastTextChangeStart;         // cache data of the
137
	int lastTextChangeReplaceCharCount;	
137
   int lastTextChangeNewLineCount;     // last text changing
138
	boolean isBidi;
138
   int lastTextChangeNewCharCount;     // event for use in the
139
	boolean bidiColoring = false;		// apply the BIDI algorithm on text segments of the same color
139
   int lastTextChangeReplaceLineCount; // text changed handler
140
	Image leftCaretBitmap = null;
140
   int lastTextChangeReplaceCharCount;
141
	Image rightCaretBitmap = null;
141
   boolean isBidi;
142
	int caretDirection = SWT.NULL;
142
   boolean bidiColoring = false;    // apply the BIDI algorithm on text segments of the same color
143
	PaletteData caretPalette = null;	
143
   Image leftCaretBitmap = null;
144
	int lastCaretDirection = SWT.NULL;
144
   Image rightCaretBitmap = null;
145
	boolean isCarbon;					// flag set to true on Mac OSX
145
   int caretDirection = SWT.NULL;
146
	
146
   PaletteData caretPalette = null;
147
	/**
147
   int lastCaretDirection = SWT.NULL;
148
	 * The Printing class implements printing of a range of text.
148
   boolean isCarbon;             // flag set to true on Mac OSX
149
	 * An instance of <class>Printing </class> is returned in the 
149
150
	 * StyledText#print(Printer) API. The run() method may be 
150
   /**
151
	 * invoked from any thread.
151
    * The Printing class implements printing of a range of text.
152
	 */
152
    * An instance of <class>Printing </class> is returned in the
153
	class Printing implements Runnable {
153
    * StyledText#print(Printer) API. The run() method may be
154
		final static int LEFT = 0;						// left aligned header/footer segment
154
    * invoked from any thread.
155
		final static int CENTER = 1;					// centered header/footer segment
155
    */
156
		final static int RIGHT = 2;						// right aligned header/footer segment
156
   class Printing implements Runnable {
157
157
      final static int LEFT = 0;                // left aligned header/footer segment
158
		Printer printer;
158
      final static int CENTER = 1;              // centered header/footer segment
159
		PrintRenderer renderer;
159
      final static int RIGHT = 2;                  // right aligned header/footer segment
160
		StyledTextPrintOptions printOptions;
160
161
		StyledTextContent printerContent;				// copy of the widget content
161
      Printer printer;
162
		Rectangle clientArea;							// client area to print on
162
      PrintRenderer renderer;
163
		Font printerFont;
163
      StyledTextPrintOptions printOptions;
164
		FontData displayFontData;
164
      StyledTextContent printerContent;            // copy of the widget content
165
		Hashtable printerColors;						// printer color cache for line backgrounds and style
165
      Rectangle clientArea;                     // client area to print on
166
		Hashtable lineBackgrounds = new Hashtable();	// cached line backgrounds
166
      Font printerFont;
167
		Hashtable lineStyles = new Hashtable();			// cached line styles
167
      FontData displayFontData;
168
		Hashtable bidiSegments = new Hashtable();		// cached bidi segments when running on a bidi platform
168
      Hashtable printerColors;                  // printer color cache for line backgrounds and style
169
		GC gc;											// printer GC
169
      Hashtable lineBackgrounds = new Hashtable(); // cached line backgrounds
170
		int pageWidth;									// width of a printer page in pixels
170
      Hashtable lineStyles = new Hashtable();         // cached line styles
171
		int startPage;									// first page to print
171
      Hashtable bidiSegments = new Hashtable();    // cached bidi segments when running on a bidi platform
172
		int endPage;									// last page to print
172
      GC gc;                                 // printer GC
173
		int pageSize;									// number of lines on a page
173
      int pageWidth;                         // width of a printer page in pixels
174
		int startLine;									// first (wrapped) line to print
174
      int startPage;                         // first page to print
175
		int endLine;									// last (wrapped) line to print
175
      int endPage;                           // last page to print
176
		boolean singleLine;								// widget single line mode
176
      int pageSize;                          // number of lines on a page
177
		Point selection = null;					// selected text
177
      int startLine;                         // first (wrapped) line to print
178
178
      int endLine;                           // last (wrapped) line to print
179
	/**
179
      boolean singleLine;                       // widget single line mode
180
	 * Creates an instance of <class>Printing</class>.
180
      Point selection = null;             // selected text
181
	 * Copies the widget content and rendering data that needs 
181
182
	 * to be requested from listeners.
182
   /**
183
	 * </p>
183
    * Creates an instance of <class>Printing</class>.
184
	 * @param parent StyledText widget to print.
184
    * Copies the widget content and rendering data that needs
185
	 * @param printer printer device to print on.
185
    * to be requested from listeners.
186
	 * @param printOptions print options
186
    * </p>
187
	 */		
187
    * @param parent StyledText widget to print.
188
	Printing(StyledText parent, Printer printer, StyledTextPrintOptions printOptions) {
188
    * @param printer printer device to print on.
189
		PrinterData data = printer.getPrinterData();
189
    * @param printOptions print options
190
		
190
    */
191
		this.printer = printer;
191
   Printing(StyledText parent, Printer printer, StyledTextPrintOptions printOptions) {
192
		this.printOptions = printOptions;
192
      PrinterData data = printer.getPrinterData();
193
		singleLine = parent.isSingleLine();
193
194
		startPage = 1;
194
      this.printer = printer;
195
		endPage = Integer.MAX_VALUE;
195
      this.printOptions = printOptions;
196
		if (data.scope == PrinterData.PAGE_RANGE) {
196
      singleLine = parent.isSingleLine();
197
			startPage = data.startPage;
197
      startPage = 1;
198
			endPage = data.endPage;
198
      endPage = Integer.MAX_VALUE;
199
			if (endPage < startPage) {
199
      if (data.scope == PrinterData.PAGE_RANGE) {
200
				int temp = endPage;
200
         startPage = data.startPage;
201
				endPage = startPage;
201
         endPage = data.endPage;
202
				startPage = temp;
202
         if (endPage < startPage) {
203
			}			
203
            int temp = endPage;
204
		} if (data.scope == PrinterData.SELECTION) {
204
            endPage = startPage;
205
			selection = parent.getSelectionRange();
205
            startPage = temp;
206
		}
206
         }
207
207
      } if (data.scope == PrinterData.SELECTION) {
208
		displayFontData = getFont().getFontData()[0];
208
         selection = parent.getSelectionRange();
209
		copyContent(parent.getContent());
209
      }
210
		cacheLineData(printerContent);
210
211
	}
211
      displayFontData = getFont().getFontData()[0];
212
	/**
212
      copyContent(parent.getContent());
213
	 * Caches the bidi segments of the given line.
213
      cacheLineData(printerContent);
214
	 * </p>
214
   }
215
	 * @param lineOffset offset of the line to cache bidi segments for. 
215
   /**
216
	 * 	Relative to the start of the document.
216
    * Caches the bidi segments of the given line.
217
	 * @param line line to cache bidi segments for. 
217
    * </p>
218
	 */
218
    * @param lineOffset offset of the line to cache bidi segments for.
219
	void cacheBidiSegments(int lineOffset, String line) {
219
    *    Relative to the start of the document.
220
		int[] segments = getBidiSegments(lineOffset, line);
220
    * @param line line to cache bidi segments for.
221
		
221
    */
222
		if (segments != null) {
222
   void cacheBidiSegments(int lineOffset, String line) {
223
			bidiSegments.put(new Integer(lineOffset), segments);
223
      int[] segments = getBidiSegments(lineOffset, line);
224
		}
224
225
	}
225
      if (segments != null) {
226
	/**
226
         bidiSegments.put(new Integer(lineOffset), segments);
227
	 * Caches the line background color of the given line.
227
      }
228
	 * </p>
228
   }
229
	 * @param lineOffset offset of the line to cache the background 
229
   /**
230
	 * 	color for. Relative to the start of the document.
230
    * Caches the line background color of the given line.
231
	 * @param line line to cache the background color for
231
    * </p>
232
	 */
232
    * @param lineOffset offset of the line to cache the background
233
	void cacheLineBackground(int lineOffset, String line) {
233
    *    color for. Relative to the start of the document.
234
		StyledTextEvent event = getLineBackgroundData(lineOffset, line);
234
    * @param line line to cache the background color for
235
		
235
    */
236
		if (event != null) {
236
   void cacheLineBackground(int lineOffset, String line) {
237
			lineBackgrounds.put(new Integer(lineOffset), event);
237
      StyledTextEvent event = getLineBackgroundData(lineOffset, line);
238
		}
238
239
	}
239
      if (event != null) {
240
	/**
240
         lineBackgrounds.put(new Integer(lineOffset), event);
241
	 * Caches all line data that needs to be requested from a listener.
241
      }
242
	 * </p>
242
   }
243
	 * @param printerContent <class>StyledTextContent</class> to request 
243
   /**
244
	 * 	line data for.
244
    * Caches all line data that needs to be requested from a listener.
245
	 */
245
    * </p>
246
	void cacheLineData(StyledTextContent printerContent) {	
246
    * @param printerContent <class>StyledTextContent</class> to request
247
		for (int i = 0; i < printerContent.getLineCount(); i++) {
247
    *    line data for.
248
			int lineOffset = printerContent.getOffsetAtLine(i);
248
    */
249
			String line = printerContent.getLine(i);
249
   void cacheLineData(StyledTextContent printerContent) {
250
	
250
      for (int i = 0; i < printerContent.getLineCount(); i++) {
251
			if (printOptions.printLineBackground) {
251
         int lineOffset = printerContent.getOffsetAtLine(i);
252
				cacheLineBackground(lineOffset, line);
252
         String line = printerContent.getLine(i);
253
			}
253
254
			if (printOptions.printTextBackground ||
254
         if (printOptions.printLineBackground) {
255
				printOptions.printTextForeground ||
255
            cacheLineBackground(lineOffset, line);
256
				printOptions.printTextFontStyle) {
256
         }
257
				cacheLineStyle(lineOffset, line);
257
         if (printOptions.printTextBackground ||
258
			}
258
            printOptions.printTextForeground ||
259
			if (isBidi()) {
259
            printOptions.printTextFontStyle) {
260
				cacheBidiSegments(lineOffset, line);
260
            cacheLineStyle(lineOffset, line);
261
			}
261
         }
262
		}
262
         if (isBidi()) {
263
	}
263
            cacheBidiSegments(lineOffset, line);
264
	/**
264
         }
265
	 * Caches all line styles of the given line.
265
      }
266
	 * </p>
266
   }
267
	 * @param lineOffset offset of the line to cache the styles for.
267
   /**
268
	 * 	Relative to the start of the document.
268
    * Caches all line styles of the given line.
269
	 * @param line line to cache the styles for.
269
    * </p>
270
	 */
270
    * @param lineOffset offset of the line to cache the styles for.
271
	void cacheLineStyle(int lineOffset, String line) {
271
    *    Relative to the start of the document.
272
		StyledTextEvent event = getLineStyleData(lineOffset, line);
272
    * @param line line to cache the styles for.
273
		
273
    */
274
		if (event != null) {
274
   void cacheLineStyle(int lineOffset, String line) {
275
			StyleRange[] styles = event.styles;
275
      StyledTextEvent event = getLineStyleData(lineOffset, line);
276
			for (int i = 0; i < styles.length; i++) {
276
277
				StyleRange styleCopy = null;
277
      if (event != null) {
278
				if (printOptions.printTextBackground == false && styles[i].background != null) {
278
         StyleRange[] styles = event.styles;
279
					styleCopy = (StyleRange) styles[i].clone();
279
         for (int i = 0; i < styles.length; i++) {
280
					styleCopy.background = null;
280
            StyleRange styleCopy = null;
281
				}
281
            if (printOptions.printTextBackground == false && styles[i].background != null) {
282
				if (printOptions.printTextForeground == false && styles[i].foreground != null) {
282
               styleCopy = (StyleRange) styles[i].clone();
283
					if (styleCopy == null) {
283
               styleCopy.background = null;
284
						styleCopy = (StyleRange) styles[i].clone();
284
            }
285
					}
285
            if (printOptions.printTextForeground == false && styles[i].foreground != null) {
286
					styleCopy.foreground = null;
286
               if (styleCopy == null) {
287
				}
287
                  styleCopy = (StyleRange) styles[i].clone();
288
				if (printOptions.printTextFontStyle == false && styles[i].fontStyle != SWT.NORMAL) {
288
               }
289
					if (styleCopy == null) {
289
               styleCopy.foreground = null;
290
						styleCopy = (StyleRange) styles[i].clone();
290
            }
291
					}
291
            if (printOptions.printTextFontStyle == false && styles[i].fontStyle != SWT.NORMAL) {
292
					styleCopy.fontStyle = SWT.NORMAL;
292
               if (styleCopy == null) {
293
				}
293
                  styleCopy = (StyleRange) styles[i].clone();
294
				if (styleCopy != null) {
294
               }
295
					styles[i] = styleCopy;
295
               styleCopy.fontStyle = SWT.NORMAL;
296
				}
296
            }
297
			}	
297
            if (styleCopy != null) {
298
			lineStyles.put(new Integer(lineOffset), event);
298
               styles[i] = styleCopy;
299
		}
299
            }
300
	}
300
         }
301
	/**
301
         lineStyles.put(new Integer(lineOffset), event);
302
	 * Copies the text of the specified <class>StyledTextContent</class>.
302
      }
303
	 * </p>
303
   }
304
	 * @param original the <class>StyledTextContent</class> to copy.
304
   /**
305
	 */
305
    * Copies the text of the specified <class>StyledTextContent</class>.
306
	void copyContent(StyledTextContent original) {
306
    * </p>
307
		int insertOffset = 0;
307
    * @param original the <class>StyledTextContent</class> to copy.
308
		
308
    */
309
		printerContent = new DefaultContent();
309
   void copyContent(StyledTextContent original) {
310
		for (int i = 0; i < original.getLineCount(); i++) {
310
      int insertOffset = 0;
311
			int insertEndOffset;
311
312
			if (i < original.getLineCount() - 1) {
312
      printerContent = new DefaultContent();
313
				insertEndOffset = original.getOffsetAtLine(i + 1);
313
      for (int i = 0; i < original.getLineCount(); i++) {
314
			}
314
         int insertEndOffset;
315
			else {
315
         if (i < original.getLineCount() - 1) {
316
				insertEndOffset = original.getCharCount();
316
            insertEndOffset = original.getOffsetAtLine(i + 1);
317
			}
317
         }
318
			printerContent.replaceTextRange(insertOffset, 0, original.getTextRange(insertOffset, insertEndOffset - insertOffset));
318
         else {
319
			insertOffset = insertEndOffset;
319
            insertEndOffset = original.getCharCount();
320
		}
320
         }
321
	}
321
         printerContent.replaceTextRange(insertOffset, 0, original.getTextRange(insertOffset, insertEndOffset - insertOffset));
322
	/**
322
         insertOffset = insertEndOffset;
323
	 * Replaces all display colors in the cached line backgrounds and 
323
      }
324
	 * line styles with printer colors.
324
   }
325
	 */
325
   /**
326
	void createPrinterColors() {
326
    * Replaces all display colors in the cached line backgrounds and
327
		Enumeration values = lineBackgrounds.elements();
327
    * line styles with printer colors.
328
		printerColors = new Hashtable();
328
    */
329
		while (values.hasMoreElements()) {
329
   void createPrinterColors() {
330
			StyledTextEvent event = (StyledTextEvent) values.nextElement();
330
      Enumeration values = lineBackgrounds.elements();
331
			event.lineBackground = getPrinterColor(event.lineBackground);
331
      printerColors = new Hashtable();
332
		}
332
      while (values.hasMoreElements()) {
333
		
333
         StyledTextEvent event = (StyledTextEvent) values.nextElement();
334
		values = lineStyles.elements();
334
         event.lineBackground = getPrinterColor(event.lineBackground);
335
		while (values.hasMoreElements()) {
335
      }
336
			StyledTextEvent event = (StyledTextEvent) values.nextElement();
336
337
			for (int i = 0; i < event.styles.length; i++) {
337
      values = lineStyles.elements();
338
				StyleRange style = event.styles[i];
338
      while (values.hasMoreElements()) {
339
				Color printerBackground = getPrinterColor(style.background);
339
         StyledTextEvent event = (StyledTextEvent) values.nextElement();
340
				Color printerForeground = getPrinterColor(style.foreground);
340
         for (int i = 0; i < event.styles.length; i++) {
341
				
341
            StyleRange style = event.styles[i];
342
				if (printerBackground != style.background || 
342
            Color printerBackground = getPrinterColor(style.background);
343
					printerForeground != style.foreground) {
343
            Color printerForeground = getPrinterColor(style.foreground);
344
					style = (StyleRange) style.clone();
344
345
					style.background = printerBackground;
345
            if (printerBackground != style.background ||
346
					style.foreground = printerForeground;
346
               printerForeground != style.foreground) {
347
					event.styles[i] = style;
347
               style = (StyleRange) style.clone();
348
				}
348
               style.background = printerBackground;
349
			}
349
               style.foreground = printerForeground;
350
		}		
350
               event.styles[i] = style;
351
	}
351
            }
352
	/**
352
         }
353
	 * Disposes of the resources and the <class>PrintRenderer</class>.
353
      }
354
	 */
354
   }
355
	void dispose() {
355
   /**
356
		if (printerColors != null) {
356
    * Disposes of the resources and the <class>PrintRenderer</class>.
357
			Enumeration colors = printerColors.elements();
357
    */
358
			
358
   void dispose() {
359
			while (colors.hasMoreElements()) {
359
      if (printerColors != null) {
360
				Color color = (Color) colors.nextElement();
360
         Enumeration colors = printerColors.elements();
361
				color.dispose();
361
362
			}
362
         while (colors.hasMoreElements()) {
363
			printerColors = null;
363
            Color color = (Color) colors.nextElement();
364
		}
364
            color.dispose();
365
		if (gc != null) {
365
         }
366
			gc.dispose();
366
         printerColors = null;
367
			gc = null;
367
      }
368
		}
368
      if (gc != null) {
369
		if (printerFont != null) {
369
         gc.dispose();
370
			printerFont.dispose();
370
         gc = null;
371
			printerFont = null;
371
      }
372
		}
372
      if (printerFont != null) {
373
		if (renderer != null) {
373
         printerFont.dispose();
374
			renderer.dispose();
374
         printerFont = null;
375
			renderer = null;
375
      }
376
		}
376
      if (renderer != null) {
377
	}
377
         renderer.dispose();
378
	/**
378
         renderer = null;
379
	 * Finish printing the indicated page.
379
      }
380
	 * 
380
   }
381
	 * @param page page that was printed
381
   /**
382
	 */
382
    * Finish printing the indicated page.
383
	void endPage(int page) {
383
    *
384
		printDecoration(page, false);
384
    * @param page page that was printed
385
		printer.endPage();
385
    */
386
	}
386
   void endPage(int page) {
387
	/**
387
      printDecoration(page, false);
388
	 * Creates a <class>PrintRenderer</class> and calculate the line range
388
      printer.endPage();
389
	 * to print.
389
   }
390
	 */
390
   /**
391
	void initializeRenderer() {
391
    * Creates a <class>PrintRenderer</class> and calculate the line range
392
		Rectangle trim = printer.computeTrim(0, 0, 0, 0);
392
    * to print.
393
		Point dpi = printer.getDPI();
393
    */
394
		
394
   void initializeRenderer() {
395
		printerFont = new Font(printer, displayFontData.getName(), displayFontData.getHeight(), SWT.NORMAL);
395
      Rectangle trim = printer.computeTrim(0, 0, 0, 0);
396
		clientArea = printer.getClientArea();
396
      Point dpi = printer.getDPI();
397
		pageWidth = clientArea.width;
397
398
		// one inch margin around text
398
      printerFont = new Font(printer, displayFontData.getName(), displayFontData.getHeight(), SWT.NORMAL);
399
		clientArea.x = dpi.x + trim.x; 				
399
      clientArea = printer.getClientArea();
400
		clientArea.y = dpi.y + trim.y;
400
      pageWidth = clientArea.width;
401
		clientArea.width -= (clientArea.x + trim.width);
401
      // one inch margin around text
402
		clientArea.height -= (clientArea.y + trim.height); 
402
      clientArea.x = dpi.x + trim.x;
403
		
403
      clientArea.y = dpi.y + trim.y;
404
		gc = new GC(printer);
404
      clientArea.width -= (clientArea.x + trim.width);
405
		gc.setFont(printerFont);				
405
      clientArea.height -= (clientArea.y + trim.height);
406
		renderer = new PrintRenderer(
406
407
			printer, printerFont, isBidi(), gc, printerContent,
407
      gc = new GC(printer);
408
			lineBackgrounds, lineStyles, bidiSegments, 
408
      gc.setFont(printerFont);
409
			tabLength, clientArea);
409
      renderer = new PrintRenderer(
410
		if (printOptions.header != null) {
410
         printer, printerFont, isBidi(), gc, printerContent,
411
			int lineHeight = renderer.getLineHeight();
411
         lineBackgrounds, lineStyles, bidiSegments,
412
			clientArea.y += lineHeight * 2;
412
         tabLength, clientArea);
413
			clientArea.height -= lineHeight * 2;
413
      if (printOptions.header != null) {
414
		}
414
         int lineHeight = renderer.getLineHeight();
415
		if (printOptions.footer != null) {
415
         clientArea.y += lineHeight * 2;
416
			clientArea.height -= renderer.getLineHeight() * 2;
416
         clientArea.height -= lineHeight * 2;
417
		}
417
      }
418
		pageSize = clientArea.height / renderer.getLineHeight();
418
      if (printOptions.footer != null) {
419
		StyledTextContent content = renderer.getContent();
419
         clientArea.height -= renderer.getLineHeight() * 2;
420
		startLine = 0;
420
      }
421
		endLine = content.getLineCount() - 1;
421
      pageSize = clientArea.height / renderer.getLineHeight();
422
		PrinterData data = printer.getPrinterData();
422
      StyledTextContent content = renderer.getContent();
423
		if (data.scope == PrinterData.PAGE_RANGE) {
423
      startLine = 0;
424
			startLine = (startPage - 1) * pageSize;
424
      endLine = content.getLineCount() - 1;
425
		} else if (data.scope == PrinterData.SELECTION) {
425
      PrinterData data = printer.getPrinterData();
426
			startLine = content.getLineAtOffset(selection.x);
426
      if (data.scope == PrinterData.PAGE_RANGE) {
427
			if (selection.y > 0) {
427
         startLine = (startPage - 1) * pageSize;
428
				endLine = content.getLineAtOffset(selection.x + selection.y - 1);
428
      } else if (data.scope == PrinterData.SELECTION) {
429
			} else {
429
         startLine = content.getLineAtOffset(selection.x);
430
				endLine = startLine - 1;
430
         if (selection.y > 0) {
431
			}
431
            endLine = content.getLineAtOffset(selection.x + selection.y - 1);
432
		}
432
         } else {
433
	}
433
            endLine = startLine - 1;
434
	/**
434
         }
435
	 * Returns the printer color for the given display color.
435
      }
436
	 * </p>
436
   }
437
	 * @param color display color
437
   /**
438
	 * @return color create on the printer with the same RGB values 
438
    * Returns the printer color for the given display color.
439
	 * 	as the display color.
439
    * </p>
440
 	 */
440
    * @param color display color
441
	Color getPrinterColor(Color color) {
441
    * @return color create on the printer with the same RGB values
442
		Color printerColor = null;
442
    *    as the display color.
443
		
443
    */
444
		if (color != null) {
444
   Color getPrinterColor(Color color) {
445
			printerColor = (Color) printerColors.get(color);		
445
      Color printerColor = null;
446
			if (printerColor == null) {
446
447
				printerColor = new Color(printer, color.getRGB());
447
      if (color != null) {
448
				printerColors.put(color, printerColor);
448
         printerColor = (Color) printerColors.get(color);
449
			}
449
         if (printerColor == null) {
450
		}
450
            printerColor = new Color(printer, color.getRGB());
451
		return printerColor;
451
            printerColors.put(color, printerColor);
452
	}
452
         }
453
	/**
453
      }
454
	 * Prints the lines in the specified page range.
454
      return printerColor;
455
	 */
455
   }
456
	void print() {
456
   /**
457
		StyledTextContent content = renderer.getContent();
457
    * Prints the lines in the specified page range.
458
		Color background = gc.getBackground();
458
    */
459
		Color foreground = gc.getForeground();
459
   void print() {
460
		int lineHeight = renderer.getLineHeight();
460
      StyledTextContent content = renderer.getContent();
461
		int lineCount = content.getLineCount();
461
      Color background = gc.getBackground();
462
		int paintY = clientArea.y;
462
      Color foreground = gc.getForeground();
463
		int page = startPage;
463
      int lineHeight = renderer.getLineHeight();
464
		
464
      int lineCount = content.getLineCount();
465
		if (singleLine) {
465
      int paintY = clientArea.y;
466
			lineCount = 1;
466
      int page = startPage;
467
		}
467
468
		for (int i = startLine; i <= endLine && page <= endPage; i++, paintY += lineHeight) {
468
      if (singleLine) {
469
			String line = content.getLine(i);
469
         lineCount = 1;
470
			
470
      }
471
			if (paintY == clientArea.y) {
471
      for (int i = startLine; i <= endLine && page <= endPage; i++, paintY += lineHeight) {
472
				startPage(page);
472
         String line = content.getLine(i);
473
			}
473
474
			renderer.drawLine(
474
         if (paintY == clientArea.y) {
475
				line, i, paintY, gc, background, foreground, true);
475
            startPage(page);
476
			if (paintY + lineHeight * 2 > clientArea.y + clientArea.height) {
476
         }
477
				endPage(page);
477
         renderer.drawLine(
478
				paintY = clientArea.y;
478
            line, i, paintY, gc, background, foreground, true);
479
				page++;
479
         if (paintY + lineHeight * 2 > clientArea.y + clientArea.height) {
480
				if (page > endPage || i == lineCount - 1) {
480
            endPage(page);
481
					break;
481
            paintY = clientArea.y;
482
				}				
482
            page++;
483
			}
483
            if (page > endPage || i == lineCount - 1) {
484
		}
484
               break;
485
		if (paintY > clientArea.y && paintY <= clientArea.y + clientArea.height) {
485
            }
486
			endPage(page);
486
         }
487
		}
487
      }
488
	}
488
      if (paintY > clientArea.y && paintY <= clientArea.y + clientArea.height) {
489
	/**
489
         endPage(page);
490
	 * Print header or footer decorations.
490
      }
491
	 * 
	 * @param page page number to print, if specified in the StyledTextPrintOptions header or footer.
	 * @param header true = print the header, false = print the footer
	 */
491
   }
492
	void printDecoration(int page, boolean header) {
492
   /**
493
		int lastSegmentIndex = 0;
493
    * Print header or footer decorations.
494
		final int SegmentCount = 3;
494
    *
495
		String text;
495
    * @param page page number to print, if specified in the StyledTextPrintOptions header or footer.
496
		
496
    * @param header true = print the header, false = print the footer
497
		if (header) {
497
    */
498
			text = printOptions.header;
498
   void printDecoration(int page, boolean header) {
499
		}
499
      int lastSegmentIndex = 0;
500
		else {
500
      final int SegmentCount = 3;
501
			text = printOptions.footer;
501
      String text;
502
		}
502
503
		if (text == null) {
503
      if (header) {
504
			return;
504
         text = printOptions.header;
505
		}
505
      }
506
		for (int i = 0; i < SegmentCount; i++) {
506
      else {
507
			int segmentIndex = text.indexOf(StyledTextPrintOptions.SEPARATOR, lastSegmentIndex);
507
         text = printOptions.footer;
508
			String segment;
508
      }
509
			
509
      if (text == null) {
510
			if (segmentIndex == -1) {
510
         return;
511
				segment = text.substring(lastSegmentIndex);
511
      }
512
				printDecorationSegment(segment, i, page, header);
512
      for (int i = 0; i < SegmentCount; i++) {
513
				break;
513
         int segmentIndex = text.indexOf(StyledTextPrintOptions.SEPARATOR, lastSegmentIndex);
514
			}
514
         String segment;
515
			else {
515
516
				segment = text.substring(lastSegmentIndex, segmentIndex);
516
         if (segmentIndex == -1) {
517
				printDecorationSegment(segment, i, page, header);
517
            segment = text.substring(lastSegmentIndex);
518
				lastSegmentIndex = segmentIndex + StyledTextPrintOptions.SEPARATOR.length();
518
            printDecorationSegment(segment, i, page, header);
519
			}
519
            break;
520
		}
520
         }
521
	}
521
         else {
522
	/**
522
            segment = text.substring(lastSegmentIndex, segmentIndex);
523
	 * Print one segment of a header or footer decoration.
523
            printDecorationSegment(segment, i, page, header);
524
	 * Headers and footers have three different segments.
524
            lastSegmentIndex = segmentIndex + StyledTextPrintOptions.SEPARATOR.length();
525
	 * One each for left aligned, centered, and right aligned text.
525
         }
526
	 * 
526
      }
527
	 * @param segment decoration segment to print
527
   }
528
	 * @param alignment alignment of the segment. 0=left, 1=center, 2=right 
528
   /**
529
	 * @param page page number to print, if specified in the decoration segment.
529
    * Print one segment of a header or footer decoration.
530
	 * @param header true = print the header, false = print the footer
530
    * Headers and footers have three different segments.
531
	 */
531
    * One each for left aligned, centered, and right aligned text.
532
	void printDecorationSegment(String segment, int alignment, int page, boolean header) {		
532
    *
533
		int pageIndex = segment.indexOf(StyledTextPrintOptions.PAGE_TAG);
533
    * @param segment decoration segment to print
534
		
534
    * @param alignment alignment of the segment. 0=left, 1=center, 2=right
535
		if (pageIndex != -1) {
535
    * @param page page number to print, if specified in the decoration segment.
536
			final int PageTagLength = StyledTextPrintOptions.PAGE_TAG.length();
536
    * @param header true = print the header, false = print the footer
537
			StringBuffer buffer = new StringBuffer(segment);
537
    */
538
			buffer.replace(pageIndex, pageIndex + PageTagLength, new Integer(page).toString());
538
   void printDecorationSegment(String segment, int alignment, int page, boolean header) {
539
			segment = buffer.toString();
539
      int pageIndex = segment.indexOf(StyledTextPrintOptions.PAGE_TAG);
540
		}
540
541
		if (segment.length() > 0) {
541
      if (pageIndex != -1) {
542
			int segmentWidth;
542
         final int PageTagLength = StyledTextPrintOptions.PAGE_TAG.length();
543
			int drawX = 0;
543
         StringBuffer buffer = new StringBuffer(segment);
544
			int drawY;
544
         buffer.replace(pageIndex, pageIndex + PageTagLength, new Integer(page).toString());
545
			StyledTextBidi bidi = null;
545
         segment = buffer.toString();
546
			
546
      }
547
			if (isBidi()) {
547
      if (segment.length() > 0) {
548
				bidi = new StyledTextBidi(gc, tabLength, segment, null, null, new int[] {0, segment.length()});
548
         int segmentWidth;
549
				segmentWidth = bidi.getTextWidth();
549
         int drawX = 0;
550
			}			
550
         int drawY;
551
			else {
551
         StyledTextBidi bidi = null;
552
				segmentWidth = gc.textExtent(segment).x;
552
553
			}
553
         if (isBidi()) {
554
			if (header) {
554
            bidi = new StyledTextBidi(gc, tabLength, segment, null, null, new int[] {0, segment.length()});
555
				drawY = clientArea.y - renderer.getLineHeight() * 2;
555
            segmentWidth = bidi.getTextWidth();
556
			}
556
         }
557
			else {
557
         else {
558
				drawY = clientArea.y + clientArea.height + renderer.getLineHeight();
558
            segmentWidth = gc.textExtent(segment).x;
559
			}			
559
         }
560
			if (alignment == LEFT) {
560
         if (header) {
561
				drawX = clientArea.x;
561
            drawY = clientArea.y - renderer.getLineHeight() * 2;
562
			}
562
         }
563
			else				
563
         else {
564
			if (alignment == CENTER) {
564
            drawY = clientArea.y + clientArea.height + renderer.getLineHeight();
565
				drawX = (pageWidth - segmentWidth) / 2;
565
         }
566
			}
566
         if (alignment == LEFT) {
567
			else 
567
            drawX = clientArea.x;
568
			if (alignment == RIGHT) {
568
         }
569
				drawX = clientArea.x + clientArea.width - segmentWidth;
569
         else
570
			}
570
         if (alignment == CENTER) {
571
			if (bidi != null) {
571
            drawX = (pageWidth - segmentWidth) / 2;
572
				bidi.drawBidiText(0, segment.length(), drawX, drawY);
572
         }
573
			}
573
         else
574
			else {
574
         if (alignment == RIGHT) {
575
				gc.drawString(segment, drawX, drawY, true);
575
            drawX = clientArea.x + clientArea.width - segmentWidth;
576
			}
576
         }
577
		}
577
         if (bidi != null) {
578
	}
578
            bidi.drawBidiText(0, segment.length(), drawX, drawY);
579
	/**
579
         }
580
	 * Starts a print job and prints the pages specified in the constructor.
580
         else {
581
	 */
581
            gc.drawString(segment, drawX, drawY, true);
582
	public void run() {
582
         }
583
		String jobName = printOptions.jobName;
583
      }
584
		
584
   }
585
		if (jobName == null) {
585
   /**
586
			jobName = "Printing";
586
    * Starts a print job and prints the pages specified in the constructor.
587
		}		
587
    */
588
		if (printer.startJob(jobName)) {
588
   public void run() {
589
			createPrinterColors();
589
      String jobName = printOptions.jobName;
590
			initializeRenderer();
590
591
			print();
591
      if (jobName == null) {
592
			dispose();
592
         jobName = "Printing";
593
			printer.endJob();			
593
      }
594
		}
594
      if (printer.startJob(jobName)) {
595
	}
595
         createPrinterColors();
596
	/**
596
         initializeRenderer();
597
	 * Start printing a new page.
597
         print();
598
	 * 
598
         dispose();
599
	 * @param page page number to be started
599
         printer.endJob();
600
	 */
600
      }
601
	void startPage(int page) {
601
   }
602
		printer.startPage();
602
   /**
603
		printDecoration(page, true);
603
    * Start printing a new page.
604
	}	
604
    *
605
	}
605
    * @param page page number to be started
606
	/**
606
    */
607
	 * The <code>RTFWriter</code> class is used to write widget content as
607
   void startPage(int page) {
608
	 * rich text. The implementation complies with the RTF specification 
608
      printer.startPage();
609
	 * version 1.5.
609
      printDecoration(page, true);
610
	 * <p>
610
   }
611
	 * toString() is guaranteed to return a valid RTF string only after 
611
   }
612
	 * close() has been called. 
612
   /**
613
	 * </p>
613
    * The <code>RTFWriter</code> class is used to write widget content as
614
	 * <p>
614
    * rich text. The implementation complies with the RTF specification
615
	 * Whole and partial lines and line breaks can be written. Lines will be
615
    * version 1.5.
616
	 * formatted using the styles queried from the LineStyleListener, if 
616
    * <p>
617
	 * set, or those set directly in the widget. All styles are applied to
617
    * toString() is guaranteed to return a valid RTF string only after
618
	 * the RTF stream like they are rendered by the widget. In addition, the 
618
    * close() has been called.
619
	 * widget font name and size is used for the whole text.
619
    * </p>
620
	 * </p>
620
    * <p>
621
	 */
621
    * Whole and partial lines and line breaks can be written. Lines will be
622
	class RTFWriter extends TextWriter {
622
    * formatted using the styles queried from the LineStyleListener, if
623
		final int DEFAULT_FOREGROUND = 0;
623
    * set, or those set directly in the widget. All styles are applied to
624
		final int DEFAULT_BACKGROUND = 1;
624
    * the RTF stream like they are rendered by the widget. In addition, the
625
		Vector colorTable = new Vector();
625
    * widget font name and size is used for the whole text.
626
		boolean WriteUnicode;
626
    * </p>
627
		
627
    */
628
	/**
628
   class RTFWriter extends TextWriter {
629
	 * Creates a RTF writer that writes content starting at offset "start"
629
      final int DEFAULT_FOREGROUND = 0;
630
	 * in the document.  <code>start</code> and <code>length</code>can be set to specify partial 
630
      final int DEFAULT_BACKGROUND = 1;
631
	 * lines.
631
      Vector colorTable = new Vector();
632
	 * <p>
632
      boolean WriteUnicode;
633
	 *
633
634
	 * @param start start offset of content to write, 0 based from 
634
   /**
635
	 * 	beginning of document
635
    * Creates a RTF writer that writes content starting at offset "start"
636
	 * @param length length of content to write
636
    * in the document.  <code>start</code> and <code>length</code>can be set to specify partial
637
	 */
637
    * lines.
638
	public RTFWriter(int start, int length) {
638
    * <p>
639
		super(start, length);
639
    *
640
		colorTable.addElement(getForeground());
640
    * @param start start offset of content to write, 0 based from
641
		colorTable.addElement(getBackground());		
641
    *    beginning of document
642
		setUnicode();
642
    * @param length length of content to write
643
	}
643
    */
644
	/**
644
   public RTFWriter(int start, int length) {
645
	 * Closes the RTF writer. Once closed no more content can be written.
645
      super(start, length);
646
	 * <b>NOTE:</b>  <code>toString()</code> does not return a valid RTF string until 
646
      colorTable.addElement(getForeground());
647
	 * <code>close()</code> has been called.
647
      colorTable.addElement(getBackground());
648
	 */
648
      setUnicode();
649
	public void close() {
649
   }
650
		if (isClosed() == false) {
650
   /**
651
			writeHeader();
651
    * Closes the RTF writer. Once closed no more content can be written.
652
			write("\n}}\0");
652
    * <b>NOTE:</b>  <code>toString()</code> does not return a valid RTF string until
653
			super.close();
653
    * <code>close()</code> has been called.
654
		}
654
    */
655
	}	
655
   public void close() {
656
	/**
656
      if (isClosed() == false) {
657
	 * Returns the index of the specified color in the RTF color table.
657
         writeHeader();
658
	 * <p>
658
         write("\n}}\0");
659
	 *
659
         super.close();
660
	 * @param color the color
660
      }
661
	 * @param defaultIndex return value if color is null
661
   }
662
	 * @return the index of the specified color in the RTF color table
662
   /**
663
	 * 	or "defaultIndex" if "color" is null.
663
    * Returns the index of the specified color in the RTF color table.
664
	 */
664
    * <p>
665
	int getColorIndex(Color color, int defaultIndex) {
665
    *
666
		int index;
666
    * @param color the color
667
		
667
    * @param defaultIndex return value if color is null
668
		if (color == null) {
668
    * @return the index of the specified color in the RTF color table
669
			index = defaultIndex;
669
    *    or "defaultIndex" if "color" is null.
670
		}
670
    */
671
		else {		
671
   int getColorIndex(Color color, int defaultIndex) {
672
			index = colorTable.indexOf(color);
672
      int index;
673
			if (index == -1) {
673
674
				index = colorTable.size();
674
      if (color == null) {
675
				colorTable.addElement(color);
675
         index = defaultIndex;
676
			}
676
      }
677
		}
677
      else {
678
		return index;
678
         index = colorTable.indexOf(color);
679
	}
679
         if (index == -1) {
680
	/**
680
            index = colorTable.size();
681
	 * Determines if Unicode RTF should be written.
681
            colorTable.addElement(color);
682
	 * Don't write Unicode RTF on Windows 95/98/ME or NT.
682
         }
683
	 */
683
      }
684
	void setUnicode() {
684
      return index;
685
		final String Win95 = "windows 95";
685
   }
686
		final String Win98 = "windows 98";
686
   /**
687
		final String WinME = "windows me";		
687
    * Determines if Unicode RTF should be written.
688
		final String WinNT = "windows nt";
688
    * Don't write Unicode RTF on Windows 95/98/ME or NT.
689
		String osName = System.getProperty("os.name").toLowerCase();
689
    */
690
		String osVersion = System.getProperty("os.version");
690
   void setUnicode() {
691
		int majorVersion = 0;
691
      final String Win95 = "windows 95";
692
		
692
      final String Win98 = "windows 98";
693
		if (osName.startsWith(WinNT) && osVersion != null) {
693
      final String WinME = "windows me";
694
			int majorIndex = osVersion.indexOf('.');
694
      final String WinNT = "windows nt";
695
			if (majorIndex != -1) {
695
      String osName = System.getProperty("os.name").toLowerCase();
696
				osVersion = osVersion.substring(0, majorIndex);
696
      String osVersion = System.getProperty("os.version");
697
				try {
697
      int majorVersion = 0;
698
					majorVersion = Integer.parseInt(osVersion);
698
699
				}
699
      if (osName.startsWith(WinNT) && osVersion != null) {
700
				catch (NumberFormatException exception) {
700
         int majorIndex = osVersion.indexOf('.');
701
					// ignore exception. version number remains unknown.
701
         if (majorIndex != -1) {
702
					// will write without Unicode
702
            osVersion = osVersion.substring(0, majorIndex);
703
				}
703
            try {
704
			}
704
               majorVersion = Integer.parseInt(osVersion);
705
		}
705
            }
706
		if (osName != null &&
706
            catch (NumberFormatException exception) {
707
			osName.startsWith(Win95) == false &&
707
               // ignore exception. version number remains unknown.
708
			osName.startsWith(Win98) == false &&
708
               // will write without Unicode
709
			osName.startsWith(WinME) == false &&
709
            }
710
			(osName.startsWith(WinNT) == false || majorVersion > 4)) {
710
         }
711
			WriteUnicode = true;
711
      }
712
		}
712
      if (osName != null &&
713
		else {
713
         osName.startsWith(Win95) == false &&
714
			WriteUnicode = false;
714
         osName.startsWith(Win98) == false &&
715
		}
715
         osName.startsWith(WinME) == false &&
716
	}
716
         (osName.startsWith(WinNT) == false || majorVersion > 4)) {
717
	/**
717
         WriteUnicode = true;
718
	 * Appends the specified segment of "string" to the RTF data.
718
      }
719
	 * Copy from <code>start</code> up to, but excluding, <code>end</code>.
719
      else {
720
	 * <p>
720
         WriteUnicode = false;
721
	 *
721
      }
722
	 * @param string string to copy a segment from. Must not contain
722
   }
723
	 * 	line breaks. Line breaks should be written using writeLineDelimiter()
723
   /**
724
	 * @param start start offset of segment. 0 based.
724
    * Appends the specified segment of "string" to the RTF data.
725
	 * @param end end offset of segment
725
    * Copy from <code>start</code> up to, but excluding, <code>end</code>.
726
	 */
726
    * <p>
727
	void write(String string, int start, int end) {
727
    *
728
		for (int index = start; index < end; index++) {
728
    * @param string string to copy a segment from. Must not contain
729
			char ch = string.charAt(index);
729
    *    line breaks. Line breaks should be written using writeLineDelimiter()
730
			if (ch > 0xFF && WriteUnicode) {
730
    * @param start start offset of segment. 0 based.
731
				// write the sub string from the last escaped character 
731
    * @param end end offset of segment
732
				// to the current one. Fixes bug 21698.
732
    */
733
				if (index > start) {
733
   void write(String string, int start, int end) {
734
					write(string.substring(start, index));
734
      for (int index = start; index < end; index++) {
735
				}
735
         char ch = string.charAt(index);
736
				write("\\u");
736
         if (ch > 0xFF && WriteUnicode) {
737
				write(Integer.toString((short) ch));
737
            // write the sub string from the last escaped character
738
				write(' ');						// control word delimiter
738
            // to the current one. Fixes bug 21698.
739
				start = index + 1;
739
            if (index > start) {
740
			}
740
               write(string.substring(start, index));
741
			else
741
            }
742
			if (ch == '}' || ch == '{' || ch == '\\') {
742
            write("\\u");
743
				// write the sub string from the last escaped character 
743
            write(Integer.toString((short) ch));
744
				// to the current one. Fixes bug 21698.
744
            write(' ');                // control word delimiter
745
				if (index > start) {
745
            start = index + 1;
746
					write(string.substring(start, index));
746
         }
747
				}
747
         else
748
				write('\\');
748
         if (ch == '}' || ch == '{' || ch == '\\') {
749
				write(ch);
749
            // write the sub string from the last escaped character
750
				start = index + 1;
750
            // to the current one. Fixes bug 21698.
751
			}
751
            if (index > start) {
752
		}
752
               write(string.substring(start, index));
753
		// write from the last escaped character to the end.
753
            }
754
		// Fixes bug 21698.
754
            write('\\');
755
		if (start < end) {
755
            write(ch);
756
			write(string.substring(start, end));
756
            start = index + 1;
757
		}
757
         }
758
	}	
758
      }
759
	/**
759
      // write from the last escaped character to the end.
760
	 * Writes the RTF header including font table and color table.
760
      // Fixes bug 21698.
761
	 */
761
      if (start < end) {
762
	void writeHeader() {
762
         write(string.substring(start, end));
763
		StringBuffer header = new StringBuffer();
763
      }
764
		FontData fontData = getFont().getFontData()[0];
764
   }
765
		header.append("{\\rtf1\\ansi");
765
   /**
766
		// specify code page, necessary for copy to work in bidi 
766
    * Writes the RTF header including font table and color table.
767
		// systems that don't support Unicode RTF.
767
    */
768
		String cpg = System.getProperty("file.encoding").toLowerCase();
768
   void writeHeader() {
769
		if (cpg.startsWith("cp") || cpg.startsWith("ms")) {
769
      StringBuffer header = new StringBuffer();
770
			cpg = cpg.substring(2, cpg.length());
770
      FontData fontData = getFont().getFontData()[0];
771
			header.append("\\ansicpg");
771
      header.append("{\\rtf1\\ansi");
772
			header.append(cpg);
772
      // specify code page, necessary for copy to work in bidi
773
		}
773
      // systems that don't support Unicode RTF.
774
		header.append("\\uc0\\deff0{\\fonttbl{\\f0\\fnil ");
774
      String cpg = System.getProperty("file.encoding").toLowerCase();
775
		header.append(fontData.getName());
775
      if (cpg.startsWith("cp") || cpg.startsWith("ms")) {
776
		header.append(";}}\n{\\colortbl");
776
         cpg = cpg.substring(2, cpg.length());
777
		for (int i = 0; i < colorTable.size(); i++) {
777
         header.append("\\ansicpg");
778
			Color color = (Color) colorTable.elementAt(i);
778
         header.append(cpg);
779
			header.append("\\red");
779
      }
780
			header.append(color.getRed());
780
      header.append("\\uc0\\deff0{\\fonttbl{\\f0\\fnil ");
781
			header.append("\\green");
781
      header.append(fontData.getName());
782
			header.append(color.getGreen());
782
      header.append(";}}\n{\\colortbl");
783
			header.append("\\blue");
783
      for (int i = 0; i < colorTable.size(); i++) {
784
			header.append(color.getBlue());
784
         Color color = (Color) colorTable.elementAt(i);
785
			header.append(";");
785
         header.append("\\red");
786
		} 
786
         header.append(color.getRed());
787
		// some RTF readers ignore the deff0 font tag. Explicitly 
787
         header.append("\\green");
788
		// set the font for the whole document to work around this.
788
         header.append(color.getGreen());
789
		header.append("}\n{\\f0\\fs");
789
         header.append("\\blue");
790
		// font size is specified in half points
790
         header.append(color.getBlue());
791
		header.append(fontData.getHeight() * 2);
791
         header.append(";");
792
		header.append(" ");
792
      }
793
		write(header.toString(), 0);
793
      // some RTF readers ignore the deff0 font tag. Explicitly
794
	}
794
      // set the font for the whole document to work around this.
795
	/**
795
      header.append("}\n{\\f0\\fs");
796
	 * Appends the specified line text to the RTF data.  Lines will be formatted 
796
      // font size is specified in half points
797
	 * using the styles queried from the LineStyleListener, if set, or those set 
797
      header.append(fontData.getHeight() * 2);
798
	 * directly in the widget.
798
      header.append(" ");
799
	 * <p>
799
      write(header.toString(), 0);
800
	 *
800
   }
801
	 * @param line line text to write as RTF. Must not contain line breaks
801
   /**
802
	 * 	Line breaks should be written using writeLineDelimiter()
802
    * Appends the specified line text to the RTF data.  Lines will be formatted
803
	 * @param lineOffset offset of the line. 0 based from the start of the 
803
    * using the styles queried from the LineStyleListener, if set, or those set
804
	 * 	widget document. Any text occurring before the start offset or after the 
804
    * directly in the widget.
805
	 * 	end offset specified during object creation is ignored.
805
    * <p>
806
	 * @exception SWTException <ul>
806
    *
807
	 *   <li>ERROR_IO when the writer is closed.</li>
807
    * @param line line text to write as RTF. Must not contain line breaks
808
	 * </ul>
808
    *    Line breaks should be written using writeLineDelimiter()
809
	 */
809
    * @param lineOffset offset of the line. 0 based from the start of the
810
	public void writeLine(String line, int lineOffset) {
810
    *    widget document. Any text occurring before the start offset or after the
811
		StyleRange[] styles = new StyleRange[0];
811
    *    end offset specified during object creation is ignored.
812
		Color lineBackground = null;
812
    * @exception SWTException <ul>
813
		StyledTextEvent event;
813
    *   <li>ERROR_IO when the writer is closed.</li>
814
		
814
    * </ul>
815
		if (isClosed()) {
815
    */
816
			SWT.error(SWT.ERROR_IO);
816
   public void writeLine(String line, int lineOffset) {
817
		}
817
      StyleRange[] styles = new StyleRange[0];
818
		event = renderer.getLineStyleData(lineOffset, line);
818
      Color lineBackground = null;
819
		if (event != null) {
819
      StyledTextEvent event;
820
			styles = event.styles;
820
821
		}
821
      if (isClosed()) {
822
		event = renderer.getLineBackgroundData(lineOffset, line);
822
         SWT.error(SWT.ERROR_IO);
823
		if (event != null) {
823
      }
824
			lineBackground = event.lineBackground;
824
      event = renderer.getLineStyleData(lineOffset, line);
825
		}
825
      if (event != null) {
826
		if (lineBackground == null) {
826
         styles = event.styles;
827
			lineBackground = getBackground();
827
      }
828
		}
828
      event = renderer.getLineBackgroundData(lineOffset, line);
829
		writeStyledLine(line, lineOffset, styles, lineBackground);
829
      if (event != null) {
830
	}
830
         lineBackground = event.lineBackground;
831
	/**
831
      }
832
	 * Appends the specified line delmimiter to the RTF data.
832
      if (lineBackground == null) {
833
	 * <p>
833
         lineBackground = getBackground();
834
	 *
834
      }
835
	 * @param lineDelimiter line delimiter to write as RTF.
835
      writeStyledLine(line, lineOffset, styles, lineBackground);
836
	 * @exception SWTException <ul>
836
   }
837
	 *   <li>ERROR_IO when the writer is closed.</li>
837
   /**
838
	 * </ul>
838
    * Appends the specified line delmimiter to the RTF data.
839
	 */
839
    * <p>
840
	public void writeLineDelimiter(String lineDelimiter) {
840
    *
841
		if (isClosed()) {
841
    * @param lineDelimiter line delimiter to write as RTF.
842
			SWT.error(SWT.ERROR_IO);
842
    * @exception SWTException <ul>
843
		}
843
    *   <li>ERROR_IO when the writer is closed.</li>
844
		write(lineDelimiter, 0, lineDelimiter.length());
844
    * </ul>
845
		write("\\par ");
845
    */
846
	}
846
   public void writeLineDelimiter(String lineDelimiter) {
847
	/**
847
      if (isClosed()) {
848
	 * Appends the specified line text to the RTF data.
848
         SWT.error(SWT.ERROR_IO);
849
	 * Use the colors and font styles specified in "styles" and "lineBackground".
849
      }
850
	 * Formatting is written to reflect the text rendering by the text widget.
850
      write(lineDelimiter, 0, lineDelimiter.length());
851
	 * Style background colors take precedence over the line background color.
851
      write("\\par ");
852
	 * Background colors are written using the \highlight tag (vs. the \cb tag).
852
   }
853
	 * <p>
853
   /**
854
	 *
854
    * Appends the specified line text to the RTF data.
855
	 * @param line line text to write as RTF. Must not contain line breaks
855
    * Use the colors and font styles specified in "styles" and "lineBackground".
856
	 * 	Line breaks should be written using writeLineDelimiter()
856
    * Formatting is written to reflect the text rendering by the text widget.
857
	 * @param lineOffset offset of the line. 0 based from the start of the 
857
    * Style background colors take precedence over the line background color.
858
	 * 	widget document. Any text occurring before the start offset or after the 
858
    * Background colors are written using the \highlight tag (vs. the \cb tag).
859
	 * 	end offset specified during object creation is ignored.
859
    * <p>
860
	 * @param styles styles to use for formatting. Must not be null.
860
    *
861
	 * @param linebackground line background color to use for formatting. 
861
    * @param line line text to write as RTF. Must not contain line breaks
862
	 * 	May be null.
862
    *    Line breaks should be written using writeLineDelimiter()
863
	 */
863
    * @param lineOffset offset of the line. 0 based from the start of the
864
	void writeStyledLine(String line, int lineOffset, StyleRange[] styles, Color lineBackground) {
864
    *    widget document. Any text occurring before the start offset or after the
865
		int lineLength = line.length();
865
    *    end offset specified during object creation is ignored.
866
		int lineIndex;
866
    * @param styles styles to use for formatting. Must not be null.
867
		int copyEnd;
867
    * @param linebackground line background color to use for formatting.
868
		int startOffset = getStart();		
868
    *    May be null.
869
		int endOffset = startOffset + super.getCharCount();
869
    */
870
		int lineEndOffset = Math.min(lineLength, endOffset - lineOffset);
870
   void writeStyledLine(String line, int lineOffset, StyleRange[] styles, Color lineBackground) {
871
		int writeOffset = startOffset - lineOffset;
871
      int lineLength = line.length();
872
		
872
      int lineIndex;
873
		if (writeOffset >= line.length()) {
873
      int copyEnd;
874
			return;					// whole line is outside write range
874
      int startOffset = getStart();
875
		}
875
      int endOffset = startOffset + super.getCharCount();
876
		else
876
      int lineEndOffset = Math.min(lineLength, endOffset - lineOffset);
877
		if (writeOffset > 0) {
877
      int writeOffset = startOffset - lineOffset;
878
			lineIndex = writeOffset;		// line starts before RTF write start
878
879
		}
879
      if (writeOffset >= line.length()) {
880
		else {
880
         return;              // whole line is outside write range
881
			lineIndex = 0;
881
      }
882
		}
882
      else
883
		if (lineBackground != null) {
883
      if (writeOffset > 0) {
884
			write("{\\highlight");
884
         lineIndex = writeOffset;      // line starts before RTF write start
885
			write(getColorIndex(lineBackground, DEFAULT_BACKGROUND));
885
      }
886
			write(" "); 
886
      else {
887
		}
887
         lineIndex = 0;
888
		for (int i = 0; i < styles.length; i++) {		
888
      }
889
			StyleRange style = styles[i];
889
      if (lineBackground != null) {
890
			int start = style.start - lineOffset;
890
         write("{\\highlight");
891
			int end = start + style.length;
891
         write(getColorIndex(lineBackground, DEFAULT_BACKGROUND));
892
			int colorIndex;
892
         write(" ");
893
			// skip over partial first line
893
      }
894
			if (end < writeOffset) {
894
      for (int i = 0; i < styles.length; i++) {
895
				continue;
895
         StyleRange style = styles[i];
896
			}
896
         int start = style.start - lineOffset;
897
			// style starts beyond line end or RTF write end
897
         int end = start + style.length;
898
			if (start >= lineEndOffset) {
898
         int colorIndex;
899
				break;
899
         // skip over partial first line
900
			}
900
         if (end < writeOffset) {
901
			// write any unstyled text
901
            continue;
902
			if (lineIndex < start) { 
902
         }
903
				// copy to start of style
903
         // style starts beyond line end or RTF write end
904
				// style starting betond end of write range or end of line 
904
         if (start >= lineEndOffset) {
905
				// is guarded against above.
905
            break;
906
				write(line, lineIndex, start);
906
         }
907
				lineIndex = start;
907
         // write any unstyled text
908
			}
908
         if (lineIndex < start) {
909
			// write styled text
909
            // copy to start of style
910
			colorIndex = getColorIndex(style.background, DEFAULT_BACKGROUND);
910
            // style starting betond end of write range or end of line
911
			write("{\\cf");
911
            // is guarded against above.
912
			write(getColorIndex(style.foreground, DEFAULT_FOREGROUND));
912
            write(line, lineIndex, start);
913
			if (colorIndex != DEFAULT_BACKGROUND) {
913
            lineIndex = start;
914
				write("\\highlight");
914
         }
915
				write(colorIndex);
915
         // write styled text
916
			}
916
         colorIndex = getColorIndex(style.background, DEFAULT_BACKGROUND);
917
			if (style.fontStyle == SWT.BOLD) {
917
         write("{\\cf");
918
				write("\\b"); 
918
         write(getColorIndex(style.foreground, DEFAULT_FOREGROUND));
919
			}
919
         if (colorIndex != DEFAULT_BACKGROUND) {
920
			write(" "); 
920
            write("\\highlight");
921
			// copy to end of style or end of write range or end of line
921
            write(colorIndex);
922
			copyEnd = Math.min(end, lineEndOffset);
922
         }
923
			// guard against invalid styles and let style processing continue
923
         if (style.fontStyle == SWT.BOLD) {
924
			copyEnd = Math.max(copyEnd, lineIndex);
924
            write("\\b");
925
			write(line, lineIndex, copyEnd);
925
         }
926
			if (style.fontStyle == SWT.BOLD) {
926
         write(" ");
927
				write("\\b0"); 
927
         // copy to end of style or end of write range or end of line
928
			}
928
         copyEnd = Math.min(end, lineEndOffset);
929
			write("}");
929
         // guard against invalid styles and let style processing continue
930
			lineIndex = copyEnd;
930
         copyEnd = Math.max(copyEnd, lineIndex);
931
		}
931
         write(line, lineIndex, copyEnd);
932
		// write unstyled text at the end of the line
932
         if (style.fontStyle == SWT.BOLD) {
933
		if (lineIndex < lineEndOffset) {
933
            write("\\b0");
934
			write(line, lineIndex, lineEndOffset);
934
         }
935
		}
935
         write("}");
936
		if (lineBackground != null) {
936
         lineIndex = copyEnd;
937
			write("}");
937
      }
938
		}
938
      // write unstyled text at the end of the line
939
	}
939
      if (lineIndex < lineEndOffset) {
940
	}
940
         write(line, lineIndex, lineEndOffset);
941
	/**
941
      }
942
	 * The <code>TextWriter</code> class is used to write widget content to
942
      if (lineBackground != null) {
943
	 * a string.  Whole and partial lines and line breaks can be written. To write 
943
         write("}");
944
	 * partial lines, specify the start and length of the desired segment 
944
      }
945
	 * during object creation.
945
   }
946
	 * <p>
946
   }
947
	 * </b>NOTE:</b> <code>toString()</code> is guaranteed to return a valid string only after close() 
947
   /**
948
	 * has been called. 
948
    * The <code>TextWriter</code> class is used to write widget content to
949
	 */
949
    * a string.  Whole and partial lines and line breaks can be written. To write
950
	class TextWriter {
950
    * partial lines, specify the start and length of the desired segment
951
		private StringBuffer buffer;
951
    * during object creation.
952
		private int startOffset;	// offset of first character that will be written
952
    * <p>
953
		private int endOffset;		// offset of last character that will be written. 
953
    * </b>NOTE:</b> <code>toString()</code> is guaranteed to return a valid string only after close()
954
									// 0 based from the beginning of the widget text. 
954
    * has been called.
955
		private boolean isClosed = false;
955
    */
956
	
956
   class TextWriter {
957
	/**
957
      private StringBuffer buffer;
958
	 * Creates a writer that writes content starting at offset "start"
958
      private int startOffset;   // offset of first character that will be written
959
	 * in the document.  <code>start</code> and <code>length</code> can be set to specify partial lines.
959
      private int endOffset;     // offset of last character that will be written.
960
	 * <p>
960
                           // 0 based from the beginning of the widget text.
961
	 *
961
      private boolean isClosed = false;
962
	 * @param start start offset of content to write, 0 based from beginning of document
962
963
	 * @param length length of content to write
963
   /**
964
	 */
964
    * Creates a writer that writes content starting at offset "start"
965
	public TextWriter(int start, int length) {
965
    * in the document.  <code>start</code> and <code>length</code> can be set to specify partial lines.
966
		buffer = new StringBuffer(length);
966
    * <p>
967
		startOffset = start;
967
    *
968
		endOffset = start + length;
968
    * @param start start offset of content to write, 0 based from beginning of document
969
	}
969
    * @param length length of content to write
970
	/**
970
    */
971
	 * Closes the writer. Once closed no more content can be written.
971
   public TextWriter(int start, int length) {
972
	 * <b>NOTE:</b>  <code>toString()</code> is not guaranteed to return a valid string unless
972
      buffer = new StringBuffer(length);
973
	 * the writer is closed.
973
      startOffset = start;
974
	 */
974
      endOffset = start + length;
975
	public void close() {
975
   }
976
		if (isClosed == false) {
976
   /**
977
			isClosed = true;
977
    * Closes the writer. Once closed no more content can be written.
978
		}
978
    * <b>NOTE:</b>  <code>toString()</code> is not guaranteed to return a valid string unless
979
	}
979
    * the writer is closed.
980
	/** 
980
    */
981
	 * Returns the number of characters to write.
981
   public void close() {
982
	 */
982
      if (isClosed == false) {
983
	public int getCharCount() {
983
         isClosed = true;
984
		return endOffset - startOffset;
984
      }
985
	}	
985
   }
986
	/** 
986
   /**
987
	 * Returns the offset where writing starts. 0 based from the start of 
987
    * Returns the number of characters to write.
988
	 * the widget text. Used to write partial lines.
988
    */
989
	 */
989
   public int getCharCount() {
990
	public int getStart() {
990
      return endOffset - startOffset;
991
		return startOffset;
991
   }
992
	}
992
   /**
993
	/**
993
    * Returns the offset where writing starts. 0 based from the start of
994
	 * Returns whether the writer is closed.
994
    * the widget text. Used to write partial lines.
995
	 */
995
    */
996
	public boolean isClosed() {
996
   public int getStart() {
997
		return isClosed;
997
      return startOffset;
998
	}
998
   }
999
	/**
999
   /**
1000
	 * Returns the string.  <code>close()</code> must be called before <code>toString()</code> 
1000
    * Returns whether the writer is closed.
1001
	 * is guaranteed to return a valid string.
1001
    */
1002
	 * <p>
1002
   public boolean isClosed() {
1003
	 *
1003
      return isClosed;
1004
	 * @return the string
1004
   }
1005
	 */
1005
   /**
1006
	public String toString() {
1006
    * Returns the string.  <code>close()</code> must be called before <code>toString()</code>
1007
		return buffer.toString();
1007
    * is guaranteed to return a valid string.
1008
	}
1008
    * <p>
1009
	/**
1009
    *
1010
	 * Appends the given string to the data.
1010
    * @return the string
1011
	 */
1011
    */
1012
	void write(String string) {
1012
   public String toString() {
1013
		buffer.append(string);
1013
      return buffer.toString();
1014
	}	
1014
   }
1015
	/**
1015
   /**
1016
	 * Inserts the given string to the data at the specified offset.
1016
    * Appends the given string to the data.
1017
	 * Do nothing if "offset" is < 0 or > getCharCount()
1017
    */
1018
	 * <p>
1018
   void write(String string) {
1019
	 *
1019
      buffer.append(string);
1020
	 * @param string text to insert
1020
   }
1021
	 * @param offset offset in the existing data to insert "string" at.
1021
   /**
1022
	 */
1022
    * Inserts the given string to the data at the specified offset.
1023
	void write(String string, int offset) {
1023
    * Do nothing if "offset" is < 0 or > getCharCount()
1024
		if (offset < 0 || offset > buffer.length()) {
1024
    * <p>
1025
			return;
1025
    *
1026
		}
1026
    * @param string text to insert
1027
		buffer.insert(offset, string);
1027
    * @param offset offset in the existing data to insert "string" at.
1028
	}	
1028
    */
1029
	/**
1029
   void write(String string, int offset) {
1030
	 * Appends the given int to the data.
1030
      if (offset < 0 || offset > buffer.length()) {
1031
	 */
1031
         return;
1032
	void write(int i) {
1032
      }
1033
		buffer.append(i);
1033
      buffer.insert(offset, string);
1034
	}
1034
   }
1035
	/**
1035
   /**
1036
	 * Appends the given character to the data.
1036
    * Appends the given int to the data.
1037
	 */
1037
    */
1038
	void write(char i) {
1038
   void write(int i) {
1039
		buffer.append(i);
1039
      buffer.append(i);
1040
	}			
1040
   }
1041
	/**
1041
   /**
1042
	 * Appends the specified line text to the data.
1042
    * Appends the given character to the data.
1043
	 * <p>
1043
    */
1044
	 *
1044
   void write(char i) {
1045
	 * @param line line text to write. Must not contain line breaks
1045
      buffer.append(i);
1046
	 * 	Line breaks should be written using writeLineDelimiter()
1046
   }
1047
	 * @param lineOffset offset of the line. 0 based from the start of the 
1047
   /**
1048
	 * 	widget document. Any text occurring before the start offset or after the 
1048
    * Appends the specified line text to the data.
1049
	 *	end offset specified during object creation is ignored.
1049
    * <p>
1050
	 * @exception SWTException <ul>
1050
    *
1051
	 *   <li>ERROR_IO when the writer is closed.</li>
1051
    * @param line line text to write. Must not contain line breaks
1052
	 * </ul>
1052
    *    Line breaks should be written using writeLineDelimiter()
1053
	 */
1053
    * @param lineOffset offset of the line. 0 based from the start of the
1054
	public void writeLine(String line, int lineOffset) {
1054
    *    widget document. Any text occurring before the start offset or after the
1055
		int lineLength = line.length();
1055
    * end offset specified during object creation is ignored.
1056
		int lineIndex;
1056
    * @exception SWTException <ul>
1057
		int copyEnd;
1057
    *   <li>ERROR_IO when the writer is closed.</li>
1058
		int writeOffset = startOffset - lineOffset;
1058
    * </ul>
1059
		
1059
    */
1060
		if (isClosed) {
1060
   public void writeLine(String line, int lineOffset) {
1061
			SWT.error(SWT.ERROR_IO);
1061
      int lineLength = line.length();
1062
		}		
1062
      int lineIndex;
1063
		if (writeOffset >= lineLength) {
1063
      int copyEnd;
1064
			return;							// whole line is outside write range
1064
      int writeOffset = startOffset - lineOffset;
1065
		}
1065
1066
		else
1066
      if (isClosed) {
1067
		if (writeOffset > 0) {
1067
         SWT.error(SWT.ERROR_IO);
1068
			lineIndex = writeOffset;		// line starts before write start
1068
      }
1069
		}
1069
      if (writeOffset >= lineLength) {
1070
		else {
1070
         return;                    // whole line is outside write range
1071
			lineIndex = 0;
1071
      }
1072
		}
1072
      else
1073
		copyEnd = Math.min(lineLength, endOffset - lineOffset);
1073
      if (writeOffset > 0) {
1074
		if (lineIndex < copyEnd) {
1074
         lineIndex = writeOffset;      // line starts before write start
1075
			write(line.substring(lineIndex, copyEnd));
1075
      }
1076
		}		
1076
      else {
1077
	}
1077
         lineIndex = 0;
1078
	/**
1078
      }
1079
	 * Appends the specified line delmimiter to the data.
1079
      copyEnd = Math.min(lineLength, endOffset - lineOffset);
1080
	 * <p>
1080
      if (lineIndex < copyEnd) {
1081
	 *
1081
         write(line.substring(lineIndex, copyEnd));
1082
	 * @param lineDelimiter line delimiter to write
1082
      }
1083
	 * @exception SWTException <ul>
1083
   }
1084
	 *   <li>ERROR_IO when the writer is closed.</li>
1084
   /**
1085
	 * </ul>
1085
    * Appends the specified line delmimiter to the data.
1086
	 */
1086
    * <p>
1087
	public void writeLineDelimiter(String lineDelimiter) {
1087
    *
1088
		if (isClosed) {
1088
    * @param lineDelimiter line delimiter to write
1089
			SWT.error(SWT.ERROR_IO);
1089
    * @exception SWTException <ul>
1090
		}
1090
    *   <li>ERROR_IO when the writer is closed.</li>
1091
		write(lineDelimiter);
1091
    * </ul>
1092
	}
1092
    */
1093
	}
1093
   public void writeLineDelimiter(String lineDelimiter) {
1094
	/**
1094
      if (isClosed) {
1095
	 * LineCache provides an interface to calculate and invalidate 
1095
         SWT.error(SWT.ERROR_IO);
1096
	 * line based data.
1096
      }
1097
	 * Implementors need to return a line width in <code>getWidth</code>.
1097
      write(lineDelimiter);
1098
	 */
1098
   }
1099
	interface LineCache {
1099
   }
1100
	/**
1100
   /**
1101
	 * Calculates the lines in the specified range.
1101
    * LineCache provides an interface to calculate and invalidate
1102
	 * <p>
1102
    * line based data.
1103
	 * 
1103
    * Implementors need to return a line width in <code>getWidth</code>.
1104
	 * @param startLine first line to calculate
1104
    */
1105
	 * @param lineCount number of lines to calculate
1105
   interface LineCache {
1106
	 */
1106
   /**
1107
	public void calculate(int startLine, int lineCount);
1107
    * Calculates the lines in the specified range.
1108
	/**
1108
    * <p>
1109
	 * Returns a width that will be used by the <code>StyledText</code> 
1109
    *
1110
	 * widget to size a horizontal scroll bar.
1110
    * @param startLine first line to calculate
1111
	 * <p>
1111
    * @param lineCount number of lines to calculate
1112
	 *
1112
    */
1113
	 * @return the line width
1113
   public void calculate(int startLine, int lineCount);
1114
	 */
1114
   /**
1115
	public int getWidth();
1115
    * Returns a width that will be used by the <code>StyledText</code>
1116
	/**
1116
    * widget to size a horizontal scroll bar.
1117
	 * Resets the lines in the specified range.
1117
    * <p>
1118
	 * This method is called in <code>StyledText.redraw()</code>
1118
    *
1119
	 * and allows implementors to call redraw themselves during reset.
1119
    * @return the line width
1120
	 * <p>
1120
    */
1121
	 *
1121
   public int getWidth();
1122
	 * @param startLine the first line to reset
1122
   /**
1123
	 * @param lineCount the number of lines to reset
1123
    * Resets the lines in the specified range.
1124
	 * @param calculateMaxWidth true=implementors should retain a 
1124
    * This method is called in <code>StyledText.redraw()</code>
1125
	 * 	valid width even if it is affected by the reset operation.
1125
    * and allows implementors to call redraw themselves during reset.
1126
	 * 	false=the width may be set to 0
1126
    * <p>
1127
	 */
1127
    *
1128
	public void redrawReset(int startLine, int lineCount, boolean calculateMaxWidth);
1128
    * @param startLine the first line to reset
1129
	/**
1129
    * @param lineCount the number of lines to reset
1130
	 * Resets the lines in the specified range.
1130
    * @param calculateMaxWidth true=implementors should retain a
1131
	 * <p>
1131
    *    valid width even if it is affected by the reset operation.
1132
	 *
1132
    *    false=the width may be set to 0
1133
	 * @param startLine the first line to reset
1133
    */
1134
	 * @param lineCount the number of lines to reset
1134
   public void redrawReset(int startLine, int lineCount, boolean calculateMaxWidth);
1135
	 * @param calculateMaxWidth true=implementors should retain a 
1135
   /**
1136
	 * 	valid width even if it is affected by the reset operation.
1136
    * Resets the lines in the specified range.
1137
	 * 	false=the width may be set to 0
1137
    * <p>
1138
	 */
1138
    *
1139
	public void reset(int startLine, int lineCount, boolean calculateMaxWidth);
1139
    * @param startLine the first line to reset
1140
	/** 
1140
    * @param lineCount the number of lines to reset
1141
	 * Called when a text change occurred.
1141
    * @param calculateMaxWidth true=implementors should retain a
1142
	 * <p>
1142
    *    valid width even if it is affected by the reset operation.
1143
	 *
1143
    *    false=the width may be set to 0
1144
	 * @param startOffset	the start offset of the text change
1144
    */
1145
	 * @param newLineCount the number of inserted lines
1145
   public void reset(int startLine, int lineCount, boolean calculateMaxWidth);
1146
	 * @param replaceLineCount the number of deleted lines
1146
   /**
1147
	 * @param newCharCount the number of new characters
1147
    * Called when a text change occurred.
1148
	 * @param replaceCharCount the number of deleted characters
1148
    * <p>
1149
	 */  
1149
    *
1150
	public void textChanged(int startOffset, int newLineCount, int replaceLineCount, int newCharCount, int replaceCharCount);
1150
    * @param startOffset   the start offset of the text change
1151
	}
1151
    * @param newLineCount the number of inserted lines
1152
	/**
1152
    * @param replaceLineCount the number of deleted lines
1153
	 * Keeps track of line widths and the longest line in the 
1153
    * @param newCharCount the number of new characters
1154
	 * StyledText document.
1154
    * @param replaceCharCount the number of deleted characters
1155
	 * Line widths are calculated when requested by a call to 
1155
    */
1156
	 * <code>calculate</code> and cached until reset by a call 
1156
   public void textChanged(int startOffset, int newLineCount, int replaceLineCount, int newCharCount, int replaceCharCount);
1157
	 * to <code>redrawReset</code> or <code>reset</code>.
1157
   }
1158
	 */
1158
   /**
1159
	class ContentWidthCache implements LineCache {
1159
    * Keeps track of line widths and the longest line in the
1160
		StyledText parent;				// parent widget, used to create a GC for line measuring
1160
    * StyledText document.
1161
		int[] lineWidth;				// width in pixel of each line in the document, -1 for unknown width
1161
    * Line widths are calculated when requested by a call to
1162
		int lineCount;					// number of lines in lineWidth array
1162
    * <code>calculate</code> and cached until reset by a call
1163
		int maxWidth;					// maximum line width of all measured lines
1163
    * to <code>redrawReset</code> or <code>reset</code>.
1164
		int maxWidthLineIndex;			// index of the widest line
1164
    */
1165
				
1165
   class ContentWidthCache implements LineCache {
1166
	/** 
1166
      StyledText parent;            // parent widget, used to create a GC for line measuring
1167
	 * Creates a new <code>ContentWidthCache</code> and allocates space 
1167
      int[] lineWidth;           // width in pixel of each line in the document, -1 for unknown width
1168
	 * for the given number of lines.
1168
      int lineCount;             // number of lines in lineWidth array
1169
	 * <p>
1169
      int maxWidth;              // maximum line width of all measured lines
1170
	 *
1170
      int maxWidthLineIndex;        // index of the widest line
1171
	 * @param parent the StyledText widget used to create a GC for 
1171
1172
	 * 	line measuring
1172
   /**
1173
	 * @param lineCount initial number of lines to allocate space for
1173
    * Creates a new <code>ContentWidthCache</code> and allocates space
1174
	 */
1174
    * for the given number of lines.
1175
	public ContentWidthCache(StyledText parent, int lineCount) {
1175
    * <p>
1176
		this.lineCount = lineCount;
1176
    *
1177
		this.parent = parent;
1177
    * @param parent the StyledText widget used to create a GC for
1178
		lineWidth = new int[lineCount];
1178
    *    line measuring
1179
		reset(0, lineCount, false);
1179
    * @param lineCount initial number of lines to allocate space for
1180
	}
1180
    */
1181
	/**
1181
   public ContentWidthCache(StyledText parent, int lineCount) {
1182
	 * Calculates the width of each line in the given range if it has
1182
      this.lineCount = lineCount;
1183
	 * not been calculated yet.
1183
      this.parent = parent;
1184
	 * If any line in the given range is wider than the currently widest
1184
      lineWidth = new int[lineCount];
1185
	 * line, the maximum line width is updated,
1185
      reset(0, lineCount, false);
1186
	 * <p>
1186
   }
1187
	 * 
1187
   /**
1188
	 * @param startLine first line to calculate the line width of
1188
    * Calculates the width of each line in the given range if it has
1189
	 * @param lineCount number of lines to calculate the line width for
1189
    * not been calculated yet.
1190
	 */
1190
    * If any line in the given range is wider than the currently widest
1191
	public void calculate(int startLine, int lineCount) {
1191
    * line, the maximum line width is updated,
1192
		GC gc = null;
1192
    * <p>
1193
		int caretWidth = 0;
1193
    *
1194
		int stopLine = startLine + lineCount;
1194
    * @param startLine first line to calculate the line width of
1195
			
1195
    * @param lineCount number of lines to calculate the line width for
1196
		for (int i = startLine; i < stopLine; i++) {
1196
    */
1197
			if (lineWidth[i] == -1) {
1197
   public void calculate(int startLine, int lineCount) {
1198
				String line = content.getLine(i);
1198
      GC gc = null;
1199
				int lineOffset = content.getOffsetAtLine(i);
1199
      int caretWidth = 0;
1200
		
1200
      int stopLine = startLine + lineCount;
1201
				if (gc == null) {
1201
1202
					gc = parent.getGC();
1202
      for (int i = startLine; i < stopLine; i++) {
1203
					caretWidth = getCaretWidth();
1203
         if (lineWidth[i] == -1) {
1204
				}		
1204
            String line = content.getLine(i);
1205
				lineWidth[i] = contentWidth(line, lineOffset, gc) + caretWidth;
1205
            int lineOffset = content.getOffsetAtLine(i);
1206
			}
1206
1207
			if (lineWidth[i] > maxWidth) {
1207
            if (gc == null) {
1208
				maxWidth = lineWidth[i];
1208
               gc = parent.getGC();
1209
				maxWidthLineIndex = i;
1209
               caretWidth = getCaretWidth();
1210
			}
1210
            }
1211
		}
1211
            lineWidth[i] = contentWidth(line, lineOffset, gc) + caretWidth;
1212
		if (gc != null) {
1212
         }
1213
			gc.dispose();	
1213
         if (lineWidth[i] > maxWidth) {
1214
		}
1214
            maxWidth = lineWidth[i];
1215
	}
1215
            maxWidthLineIndex = i;
1216
	/** 
1216
         }
1217
	 * Calculates the width of the visible lines in the specified 
1217
      }
1218
	 * range.
1218
      if (gc != null) {
1219
	 * <p>
1219
         gc.dispose();
1220
	 *
1220
      }
1221
	 * @param startLine	the first changed line
1221
   }
1222
	 * @param newLineCount the number of inserted lines
1222
   /**
1223
	 */  
1223
    * Calculates the width of the visible lines in the specified
1224
	void calculateVisible(int startLine, int newLineCount) {
1224
    * range.
1225
		int topIndex = parent.getTopIndex();
1225
    * <p>
1226
		int bottomLine = Math.min(getPartialBottomIndex(), startLine + newLineCount);
1226
    *
1227
		
1227
    * @param startLine  the first changed line
1228
		startLine = Math.max(startLine, topIndex);
1228
    * @param newLineCount the number of inserted lines
1229
		calculate(startLine, bottomLine - startLine + 1);
1229
    */
1230
	}
1230
   void calculateVisible(int startLine, int newLineCount) {
1231
	/**
1231
      int topIndex = parent.getTopIndex();
1232
	 * Measures the width of the given line.
1232
      int bottomLine = Math.min(getPartialBottomIndex(), startLine + newLineCount);
1233
	 * <p>
1233
1234
	 * 
1234
      startLine = Math.max(startLine, topIndex);
1235
	 * @param line the line to measure
1235
      calculate(startLine, bottomLine - startLine + 1);
1236
	 * @param lineOffset start offset of the line to measure, relative 
1236
   }
1237
	 * 	to the start of the document
1237
   /**
1238
	 * @param gc the GC to use for measuring the line
1238
    * Measures the width of the given line.
1239
	 * @param currentFont the font currently set in gc. Cached for better 
1239
    * <p>
1240
	 * 	performance. Null when running in a bidi locale.
1240
    *
1241
	 * @return the width of the given line
1241
    * @param line the line to measure
1242
	 */
1242
    * @param lineOffset start offset of the line to measure, relative
1243
	int contentWidth(String line, int lineOffset, GC gc) {
1243
    *    to the start of the document
1244
		int width;
1244
    * @param gc the GC to use for measuring the line
1245
		
1245
    * @param currentFont the font currently set in gc. Cached for better
1246
		if (isBidi()) {
1246
    *    performance. Null when running in a bidi locale.
1247
			StyledTextBidi bidi = getStyledTextBidi(line, lineOffset, gc);
1247
    * @return the width of the given line
1248
			width = bidi.getTextWidth();
1248
    */
1249
		}
1249
   int contentWidth(String line, int lineOffset, GC gc) {
1250
		else {
1250
      int width;
1251
			StyledTextEvent event = renderer.getLineStyleData(lineOffset, line);
1251
1252
			StyleRange[] styles = null;
1252
      if (isBidi()) {
1253
			if (event != null) {
1253
         StyledTextBidi bidi = getStyledTextBidi(line, lineOffset, gc);
1254
				styles = renderer.filterLineStyles(event.styles);
1254
         width = bidi.getTextWidth();
1255
			}
1255
      }
1256
			width = renderer.getTextWidth(line, lineOffset, 0, line.length(), styles, 0, gc);
1256
      else {
1257
		}
1257
         StyledTextEvent event = renderer.getLineStyleData(lineOffset, line);
1258
		return width + leftMargin;
1258
         StyleRange[] styles = null;
1259
	}
1259
         if (event != null) {
1260
	/**
1260
            styles = renderer.filterLineStyles(event.styles);
1261
	 * Grows the <code>lineWidth</code> array to accomodate new line width
1261
         }
1262
	 * information.
1262
         width = renderer.getTextWidth(line, lineOffset, 0, line.length(), styles, 0, gc);
1263
	 * <p>
1263
      }
1264
	 *
1264
      return width + leftMargin;
1265
	 * @param numLines the number of elements to increase the array by
1265
   }
1266
	 */
1266
   /**
1267
	void expandLines(int numLines) {
1267
    * Grows the <code>lineWidth</code> array to accomodate new line width
1268
		int size = lineWidth.length;
1268
    * information.
1269
		if (size - lineCount >= numLines) {
1269
    * <p>
1270
			return;
1270
    *
1271
		}
1271
    * @param numLines the number of elements to increase the array by
1272
		int[] newLines = new int[Math.max(size * 2, size + numLines)];
1272
    */
1273
		System.arraycopy(lineWidth, 0, newLines, 0, size);
1273
   void expandLines(int numLines) {
1274
		lineWidth = newLines;
1274
      int size = lineWidth.length;
1275
		reset(size, lineWidth.length - size, false);
1275
      if (size - lineCount >= numLines) {
1276
	}
1276
         return;
1277
	/**
1277
      }
1278
	 * Returns the width of the longest measured line.
1278
      int[] newLines = new int[Math.max(size * 2, size + numLines)];
1279
	 * <p>
1279
      System.arraycopy(lineWidth, 0, newLines, 0, size);
1280
	 *
1280
      lineWidth = newLines;
1281
	 * @return the width of the longest measured line.
1281
      reset(size, lineWidth.length - size, false);
1282
	 */
1282
   }
1283
	public int getWidth() {
1283
   /**
1284
		return maxWidth;
1284
    * Returns the width of the longest measured line.
1285
	}
1285
    * <p>
1286
	/**
1286
    *
1287
	 * Updates the line width array to reflect inserted or deleted lines.
1287
    * @return the width of the longest measured line.
1288
	 * <p>
1288
    */
1289
	 *
1289
   public int getWidth() {
1290
	 * @param start	the starting line of the change that took place
1290
      return maxWidth;
1291
	 * @param delta	the number of lines in the change, > 0 indicates lines inserted,
1291
   }
1292
	 * 	< 0 indicates lines deleted
1292
   /**
1293
	 */
1293
    * Updates the line width array to reflect inserted or deleted lines.
1294
	void linesChanged(int startLine, int delta) {
1294
    * <p>
1295
		boolean inserting = delta > 0;
1295
    *
1296
		
1296
    * @param start   the starting line of the change that took place
1297
		if (delta == 0) {
1297
    * @param delta   the number of lines in the change, > 0 indicates lines inserted,
1298
			return;
1298
    *    < 0 indicates lines deleted
1299
		}
1299
    */
1300
		if (inserting) {
1300
   void linesChanged(int startLine, int delta) {
1301
			// shift the lines down to make room for new lines
1301
      boolean inserting = delta > 0;
1302
			expandLines(delta);
1302
1303
			for (int i = lineCount - 1; i >= startLine; i--) {
1303
      if (delta == 0) {
1304
				lineWidth[i + delta] = lineWidth[i];
1304
         return;
1305
			}
1305
      }
1306
			// reset the new lines
1306
      if (inserting) {
1307
			for (int i = startLine + 1; i <= startLine + delta && i < lineWidth.length; i++) {
1307
         // shift the lines down to make room for new lines
1308
				lineWidth[i] = -1;
1308
         expandLines(delta);
1309
			}
1309
         for (int i = lineCount - 1; i >= startLine; i--) {
1310
			// have new lines been inserted above the longest line?
1310
            lineWidth[i + delta] = lineWidth[i];
1311
			if (maxWidthLineIndex >= startLine) {
1311
         }
1312
				maxWidthLineIndex += delta;
1312
         // reset the new lines
1313
			}
1313
         for (int i = startLine + 1; i <= startLine + delta && i < lineWidth.length; i++) {
1314
		} 
1314
            lineWidth[i] = -1;
1315
		else {
1315
         }
1316
			// shift up the lines
1316
         // have new lines been inserted above the longest line?
1317
			for (int i = startLine - delta; i < lineCount; i++) {
1317
         if (maxWidthLineIndex >= startLine) {
1318
				lineWidth[i+delta] = lineWidth[i];
1318
            maxWidthLineIndex += delta;
1319
			}
1319
         }
1320
			// has the longest line been removed?
1320
      }
1321
			if (maxWidthLineIndex > startLine && maxWidthLineIndex <= startLine - delta) {
1321
      else {
1322
				maxWidth = 0;
1322
         // shift up the lines
1323
				maxWidthLineIndex = -1;
1323
         for (int i = startLine - delta; i < lineCount; i++) {
1324
			}
1324
            lineWidth[i+delta] = lineWidth[i];
1325
			else
1325
         }
1326
			if (maxWidthLineIndex >= startLine - delta) {
1326
         // has the longest line been removed?
1327
				maxWidthLineIndex += delta;
1327
         if (maxWidthLineIndex > startLine && maxWidthLineIndex <= startLine - delta) {
1328
			}
1328
            maxWidth = 0;
1329
		}
1329
            maxWidthLineIndex = -1;
1330
		lineCount += delta;
1330
         }
1331
	}
1331
         else
1332
	/**
1332
         if (maxWidthLineIndex >= startLine - delta) {
1333
	 * Resets the line width of the lines in the specified range.
1333
            maxWidthLineIndex += delta;
1334
	 * <p>
1334
         }
1335
	 *
1335
      }
1336
	 * @param startLine	the first line to reset
1336
      lineCount += delta;
1337
	 * @param lineCount the number of lines to reset
1337
   }
1338
	 * @param calculateMaxWidth true=if the widest line is being 
1338
   /**
1339
	 * 	reset the maximum width of all remaining cached lines is 
1339
    * Resets the line width of the lines in the specified range.
1340
	 * 	calculated. false=the maximum width is set to 0 if the 
1340
    * <p>
1341
	 * 	widest line is being reset.
1341
    *
1342
	 */
1342
    * @param startLine  the first line to reset
1343
	public void redrawReset(int startLine, int lineCount, boolean calculateMaxWidth) {
1343
    * @param lineCount the number of lines to reset
1344
		reset(startLine, lineCount, calculateMaxWidth);
1344
    * @param calculateMaxWidth true=if the widest line is being
1345
	}
1345
    *    reset the maximum width of all remaining cached lines is
1346
	/**
1346
    *    calculated. false=the maximum width is set to 0 if the
1347
	 * Resets the line width of the lines in the specified range.
1347
    *    widest line is being reset.
1348
	 * <p>
1348
    */
1349
	 *
1349
   public void redrawReset(int startLine, int lineCount, boolean calculateMaxWidth) {
1350
	 * @param startLine	the first line to reset
1350
      reset(startLine, lineCount, calculateMaxWidth);
1351
	 * @param lineCount the number of lines to reset
1351
   }
1352
	 * @param calculateMaxWidth true=if the widest line is being 
1352
   /**
1353
	 * 	reset the maximum width of all remaining cached lines is 
1353
    * Resets the line width of the lines in the specified range.
1354
	 * 	calculated. false=the maximum width is set to 0 if the 
1354
    * <p>
1355
	 * 	widest line is being reset.
1355
    *
1356
	 */
1356
    * @param startLine  the first line to reset
1357
	public void reset(int startLine, int lineCount, boolean calculateMaxWidth) {
1357
    * @param lineCount the number of lines to reset
1358
		int endLine = startLine + lineCount;
1358
    * @param calculateMaxWidth true=if the widest line is being
1359
		
1359
    *    reset the maximum width of all remaining cached lines is
1360
		if (startLine < 0 || endLine > lineWidth.length) {
1360
    *    calculated. false=the maximum width is set to 0 if the
1361
			return;
1361
    *    widest line is being reset.
1362
		}
1362
    */
1363
		for (int i = startLine; i < endLine; i++) {
1363
   public void reset(int startLine, int lineCount, boolean calculateMaxWidth) {
1364
			lineWidth[i] = -1;
1364
      int endLine = startLine + lineCount;
1365
		}		
1365
1366
		// if the longest line is one of the reset lines, the maximum line 
1366
      if (startLine < 0 || endLine > lineWidth.length) {
1367
		// width is no longer valid
1367
         return;
1368
		if (maxWidthLineIndex >= startLine && maxWidthLineIndex < endLine) {
1368
      }
1369
			maxWidth = 0;
1369
      for (int i = startLine; i < endLine; i++) {
1370
			maxWidthLineIndex = -1;
1370
         lineWidth[i] = -1;
1371
			if (calculateMaxWidth) {
1371
      }
1372
				for (int i = 0; i < lineCount; i++) {
1372
      // if the longest line is one of the reset lines, the maximum line
1373
					if (lineWidth[i] > maxWidth) {
1373
      // width is no longer valid
1374
						maxWidth = lineWidth[i];
1374
      if (maxWidthLineIndex >= startLine && maxWidthLineIndex < endLine) {
1375
						maxWidthLineIndex = i;
1375
         maxWidth = 0;
1376
					}
1376
         maxWidthLineIndex = -1;
1377
				}			
1377
         if (calculateMaxWidth) {
1378
			}
1378
            for (int i = 0; i < lineCount; i++) {
1379
		}
1379
               if (lineWidth[i] > maxWidth) {
1380
	}
1380
                  maxWidth = lineWidth[i];
1381
	/** 
1381
                  maxWidthLineIndex = i;
1382
	 * Updates the line width array to reflect a text change.
1382
               }
1383
	 * Lines affected by the text change will be reset.
1383
            }
1384
	 * <p>
1384
         }
1385
	 *
1385
      }
1386
	 * @param startOffset	the start offset of the text change
1386
   }
1387
	 * @param newLineCount the number of inserted lines
1387
   /**
1388
	 * @param replaceLineCount the number of deleted lines
1388
    * Updates the line width array to reflect a text change.
1389
	 * @param newCharCount the number of new characters
1389
    * Lines affected by the text change will be reset.
1390
	 * @param replaceCharCount the number of deleted characters
1390
    * <p>
1391
	 */  
1391
    *
1392
	public void textChanged(int startOffset, int newLineCount, int replaceLineCount, int newCharCount, int replaceCharCount) {
1392
    * @param startOffset   the start offset of the text change
1393
		int startLine = parent.getLineAtOffset(startOffset);
1393
    * @param newLineCount the number of inserted lines
1394
		boolean removedMaxLine = (maxWidthLineIndex > startLine && maxWidthLineIndex <= startLine + replaceLineCount);
1394
    * @param replaceLineCount the number of deleted lines
1395
		// entire text deleted?
1395
    * @param newCharCount the number of new characters
1396
		if (startLine == 0 && replaceLineCount == lineCount) {
1396
    * @param replaceCharCount the number of deleted characters
1397
			lineCount = newLineCount;
1397
    */
1398
			lineWidth = new int[lineCount];
1398
   public void textChanged(int startOffset, int newLineCount, int replaceLineCount, int newCharCount, int replaceCharCount) {
1399
			reset(0, lineCount, false);
1399
      int startLine = parent.getLineAtOffset(startOffset);
1400
			maxWidth = 0;
1400
      boolean removedMaxLine = (maxWidthLineIndex > startLine && maxWidthLineIndex <= startLine + replaceLineCount);
1401
		}
1401
      // entire text deleted?
1402
		else {
1402
      if (startLine == 0 && replaceLineCount == lineCount) {
1403
			linesChanged(startLine, -replaceLineCount);
1403
         lineCount = newLineCount;
1404
			linesChanged(startLine, newLineCount);
1404
         lineWidth = new int[lineCount];
1405
			lineWidth[startLine] = -1;
1405
         reset(0, lineCount, false);
1406
		}
1406
         maxWidth = 0;
1407
		// only calculate the visible lines. otherwise measurements of changed lines 
1407
      }
1408
		// outside the visible area may subsequently change again without the 
1408
      else {
1409
		// lines ever being visible.
1409
         linesChanged(startLine, -replaceLineCount);
1410
		calculateVisible(startLine, newLineCount);
1410
         linesChanged(startLine, newLineCount);
1411
		// maxWidthLineIndex will be -1 (i.e., unknown line width) if the widget has 
1411
         lineWidth[startLine] = -1;
1412
		// not been visible yet and the changed lines have therefore not been
1412
      }
1413
		// calculated above.
1413
      // only calculate the visible lines. otherwise measurements of changed lines
1414
		if (removedMaxLine || 
1414
      // outside the visible area may subsequently change again without the
1415
			(maxWidthLineIndex != -1 && lineWidth[maxWidthLineIndex] < maxWidth)) {
1415
      // lines ever being visible.
1416
			// longest line has been removed or changed and is now shorter.
1416
      calculateVisible(startLine, newLineCount);
1417
			// need to recalculate maximum content width for all lines
1417
      // maxWidthLineIndex will be -1 (i.e., unknown line width) if the widget has
1418
			maxWidth = 0;
1418
      // not been visible yet and the changed lines have therefore not been
1419
			for (int i = 0; i < lineCount; i++) {
1419
      // calculated above.
1420
				if (lineWidth[i] > maxWidth) {
1420
      if (removedMaxLine ||
1421
					maxWidth = lineWidth[i];
1421
         (maxWidthLineIndex != -1 && lineWidth[maxWidthLineIndex] < maxWidth)) {
1422
					maxWidthLineIndex = i;
1422
         // longest line has been removed or changed and is now shorter.
1423
				}
1423
         // need to recalculate maximum content width for all lines
1424
			}			
1424
         maxWidth = 0;
1425
		}
1425
         for (int i = 0; i < lineCount; i++) {
1426
	}
1426
            if (lineWidth[i] > maxWidth) {
1427
	}
1427
               maxWidth = lineWidth[i];
1428
	/**
1428
               maxWidthLineIndex = i;
1429
	 * Updates the line wrapping of the content.
1429
            }
1430
	 * The line wrapping must always be in a consistent state. 
1430
         }
1431
	 * Therefore, when <code>reset</code> or <code>redrawReset</code>
1431
      }
1432
	 * is called, the line wrapping is recalculated immediately 
1432
   }
1433
	 * instead of in <code>calculate</code>.
1433
   }
1434
	 */
1434
   /**
1435
	class WordWrapCache implements LineCache {
1435
    * Updates the line wrapping of the content.
1436
		StyledText parent;
1436
    * The line wrapping must always be in a consistent state.
1437
		WrappedContent visualContent;
1437
    * Therefore, when <code>reset</code> or <code>redrawReset</code>
1438
				
1438
    * is called, the line wrapping is recalculated immediately
1439
	/** 
1439
    * instead of in <code>calculate</code>.
1440
	 * Creates a new <code>WordWrapCache</code> and calculates an initial
1440
    */
1441
	 * line wrapping.
1441
   class WordWrapCache implements LineCache {
1442
	 * <p>
1442
      StyledText parent;
1443
	 *
1443
      WrappedContent visualContent;
1444
	 * @param parent the StyledText widget to wrap content in.
1444
1445
	 * @param content the content provider that does the actual line wrapping.
1445
   /**
1446
	 */
1446
    * Creates a new <code>WordWrapCache</code> and calculates an initial
1447
	public WordWrapCache(StyledText parent, WrappedContent content) {
1447
    * line wrapping.
1448
		this.parent = parent;
1448
    * <p>
1449
		visualContent = content;
1449
    *
1450
		visualContent.wrapLines();
1450
    * @param parent the StyledText widget to wrap content in.
1451
	}
1451
    * @param content the content provider that does the actual line wrapping.
1452
	/**
1452
    */
1453
	 * Do nothing. Lines are wrapped immediately after reset.
1453
   public WordWrapCache(StyledText parent, WrappedContent content) {
1454
	 * <p>
1454
      this.parent = parent;
1455
	 * 
1455
      visualContent = content;
1456
	 * @param startLine first line to calculate
1456
      visualContent.wrapLines();
1457
	 * @param lineCount number of lines to calculate
1457
   }
1458
	 */
1458
   /**
1459
	public void calculate(int startLine, int lineCount) {
1459
    * Do nothing. Lines are wrapped immediately after reset.
1460
	}
1460
    * <p>
1461
	/**
1461
    *
1462
	 * Returns the client area width. Lines are wrapped so there
1462
    * @param startLine first line to calculate
1463
	 * is no horizontal scroll bar.
1463
    * @param lineCount number of lines to calculate
1464
	 * <p>
1464
    */
1465
	 *
1465
   public void calculate(int startLine, int lineCount) {
1466
	 * @return the line width
1466
   }
1467
	 */
1467
   /**
1468
	public int getWidth() {
1468
    * Returns the client area width. Lines are wrapped so there
1469
		return parent.getClientArea().width;
1469
    * is no horizontal scroll bar.
1470
	}
1470
    * <p>
1471
	/**
1471
    *
1472
	 * Wraps the lines in the specified range.
1472
    * @return the line width
1473
	 * This method is called in <code>StyledText.redraw()</code>.
1473
    */
1474
	 * A redraw is therefore not necessary.
1474
   public int getWidth() {
1475
	 * <p>
1475
      return parent.getClientArea().width;
1476
	 *
1476
   }
1477
	 * @param startLine the first line to reset
1477
   /**
1478
	 * @param lineCount the number of lines to reset
1478
    * Wraps the lines in the specified range.
1479
	 * @param calculateMaxWidth true=implementors should retain a 
1479
    * This method is called in <code>StyledText.redraw()</code>.
1480
	 * 	valid width even if it is affected by the reset operation.
1480
    * A redraw is therefore not necessary.
1481
	 * 	false=the width may be set to 0
1481
    * <p>
1482
	 */
1482
    *
1483
	public void redrawReset(int startLine, int lineCount, boolean calculateMaxWidth) {
1483
    * @param startLine the first line to reset
1484
	    if (lineCount == visualContent.getLineCount()) {
1484
    * @param lineCount the number of lines to reset
1485
			// do a full rewrap if all lines are reset
1485
    * @param calculateMaxWidth true=implementors should retain a
1486
			visualContent.wrapLines();
1486
    *    valid width even if it is affected by the reset operation.
1487
	    }
1487
    *    false=the width may be set to 0
1488
	    else {
1488
    */
1489
		    visualContent.reset(startLine, lineCount);
1489
   public void redrawReset(int startLine, int lineCount, boolean calculateMaxWidth) {
1490
	    }
1490
       if (lineCount == visualContent.getLineCount()) {
1491
	}
1491
         // do a full rewrap if all lines are reset
1492
	/**
1492
         visualContent.wrapLines();
1493
	 * Rewraps the lines in the specified range and redraws
1493
       }
1494
	 * the widget if the line wrapping has changed.
1494
       else {
1495
	 * <p>
1495
          visualContent.reset(startLine, lineCount);
1496
	 *
1496
       }
1497
	 * @param startLine the first line to reset
1497
   }
1498
	 * @param lineCount the number of lines to reset
1498
   /**
1499
	 * @param calculateMaxWidth true=implementors should retain a 
1499
    * Rewraps the lines in the specified range and redraws
1500
	 * 	valid width even if it is affected by the reset operation.
1500
    * the widget if the line wrapping has changed.
1501
	 * 	false=the width may be set to 0
1501
    * <p>
1502
	 */
1502
    *
1503
	public void reset(int startLine, int lineCount, boolean calculateMaxWidth) {
1503
    * @param startLine the first line to reset
1504
		int itemCount = getPartialBottomIndex() - topIndex + 1;
1504
    * @param lineCount the number of lines to reset
1505
	    int[] oldLineOffsets = new int[itemCount];
1505
    * @param calculateMaxWidth true=implementors should retain a
1506
	    
1506
    *    valid width even if it is affected by the reset operation.
1507
	    for (int i = 0; i < itemCount; i++) {
1507
    *    false=the width may be set to 0
1508
	    	oldLineOffsets[i] = visualContent.getOffsetAtLine(i + topIndex);
1508
    */
1509
	    }
1509
   public void reset(int startLine, int lineCount, boolean calculateMaxWidth) {
1510
	    redrawReset(startLine, lineCount, calculateMaxWidth);
1510
      int itemCount = getPartialBottomIndex() - topIndex + 1;
1511
		// check for cases which will require a full redraw
1511
       int[] oldLineOffsets = new int[itemCount];
1512
	    if (getPartialBottomIndex() - topIndex + 1 != itemCount) {
1512
1513
	    	// number of visible lines has changed
1513
       for (int i = 0; i < itemCount; i++) {
1514
	    	parent.internalRedraw();
1514
         oldLineOffsets[i] = visualContent.getOffsetAtLine(i + topIndex);
1515
	    }
1515
       }
1516
	    else {
1516
       redrawReset(startLine, lineCount, calculateMaxWidth);
1517
		    for (int i = 0; i < itemCount; i++) {
1517
      // check for cases which will require a full redraw
1518
		    	if (visualContent.getOffsetAtLine(i + topIndex) != oldLineOffsets[i]) {
1518
       if (getPartialBottomIndex() - topIndex + 1 != itemCount) {
1519
		    		// wrapping of one of the visible lines has changed
1519
         // number of visible lines has changed
1520
		    		parent.internalRedraw();
1520
         parent.internalRedraw();
1521
		    		break;
1521
       }
1522
		    	}
1522
       else {
1523
	    	}	    	
1523
          for (int i = 0; i < itemCount; i++) {
1524
	    }
1524
            if (visualContent.getOffsetAtLine(i + topIndex) != oldLineOffsets[i]) {
1525
	}
1525
               // wrapping of one of the visible lines has changed
1526
	/** 
1526
               parent.internalRedraw();
1527
	 * Passes the text change notification to the line wrap content.
1527
               break;
1528
	 * <p>
1528
            }
1529
	 *
1529
         }
1530
	 * @param startOffset	the start offset of the text change
1530
       }
1531
	 * @param newLineCount the number of inserted lines
1531
   }
1532
	 * @param replaceLineCount the number of deleted lines
1532
   /**
1533
	 * @param newCharCount the number of new characters
1533
    * Passes the text change notification to the line wrap content.
1534
	 * @param replaceCharCount the number of deleted characters
1534
    * <p>
1535
	 */  
1535
    *
1536
	public void textChanged(int startOffset, int newLineCount, int replaceLineCount, int newCharCount, int replaceCharCount) {
1536
    * @param startOffset   the start offset of the text change
1537
		int startLine = visualContent.getLineAtOffset(startOffset);
1537
    * @param newLineCount the number of inserted lines
1538
		
1538
    * @param replaceLineCount the number of deleted lines
1539
		visualContent.textChanged(startOffset, newLineCount, replaceLineCount, newCharCount, replaceCharCount);
1539
    * @param newCharCount the number of new characters
1540
		if (startLine <= getPartialBottomIndex()) {
1540
    * @param replaceCharCount the number of deleted characters
1541
			// only redraw if the text change is inside or above the 
1541
    */
1542
			// visible lines. if it is below the visible lines it will
1542
   public void textChanged(int startOffset, int newLineCount, int replaceLineCount, int newCharCount, int replaceCharCount) {
1543
			// not affect the word wrapping. fixes bug 14047.
1543
      int startLine = visualContent.getLineAtOffset(startOffset);
1544
			parent.internalRedraw();
1544
1545
		}
1545
      visualContent.textChanged(startOffset, newLineCount, replaceLineCount, newCharCount, replaceCharCount);
1546
	}
1546
      if (startLine <= getPartialBottomIndex()) {
1547
	}
1547
         // only redraw if the text change is inside or above the
1548
         // visible lines. if it is below the visible lines it will
1549
         // not affect the word wrapping. fixes bug 14047.
1550
         parent.internalRedraw();
1551
      }
1552
   }
1553
   }
1548
1554
1549
/**
1555
/**
1550
 * Constructs a new instance of this class given its parent
1556
 * Constructs a new instance of this class given its parent
Lines 1552-1558 Link Here
1552
 * <p>
1558
 * <p>
1553
 * The style value is either one of the style constants defined in
1559
 * The style value is either one of the style constants defined in
1554
 * class <code>SWT</code> which is applicable to instances of this
1560
 * class <code>SWT</code> which is applicable to instances of this
1555
 * class, or must be built by <em>bitwise OR</em>'ing together 
1561
 * class, or must be built by <em>bitwise OR</em>'ing together
1556
 * (that is, using the <code>int</code> "|" operator) two or more
1562
 * (that is, using the <code>int</code> "|" operator) two or more
1557
 * of those <code>SWT</code> style constants. The class description
1563
 * of those <code>SWT</code> style constants. The class description
1558
 * lists the style constants that are applicable to the class.
1564
 * lists the style constants that are applicable to the class.
Lines 1577-1636 Link Here
1577
 * @see #getStyle
1583
 * @see #getStyle
1578
 */
1584
 */
1579
public StyledText(Composite parent, int style) {
1585
public StyledText(Composite parent, int style) {
1580
	super(parent, checkStyle(style | SWT.NO_REDRAW_RESIZE | SWT.NO_BACKGROUND));
1586
   super(parent, checkStyle(style | SWT.NO_REDRAW_RESIZE | SWT.NO_BACKGROUND));
1581
	// set the bg/fg in the OS to ensure that these are the same as StyledText, necessary
1587
   // set the bg/fg in the OS to ensure that these are the same as StyledText, necessary
1582
	// for ensuring that the bg/fg the IME box uses is the same as what StyledText uses
1588
   // for ensuring that the bg/fg the IME box uses is the same as what StyledText uses
1583
	super.setForeground(getForeground());
1589
   super.setForeground(getForeground());
1584
	super.setBackground(getBackground());
1590
   super.setBackground(getBackground());
1585
	Display display = getDisplay();
1591
   Display display = getDisplay();
1586
	isBidi = StyledTextBidi.isBidiPlatform();
1592
   isBidi = StyledTextBidi.isBidiPlatform();
1587
	if ((style & SWT.READ_ONLY) != 0) {
1593
   if ((style & SWT.READ_ONLY) != 0) {
1588
		setEditable(false);
1594
      setEditable(false);
1589
	}
1595
   }
1590
	if ((style & SWT.BORDER) == 0 || (style & SWT.SINGLE) == 0) {
1596
   if ((style & SWT.BORDER) == 0 || (style & SWT.SINGLE) == 0) {
1591
		leftMargin = topMargin = rightMargin = bottomMargin = 0;
1597
      leftMargin = topMargin = rightMargin = bottomMargin = 0;
1592
	}
1598
   }
1593
	clipboard = new Clipboard(display);
1599
   clipboard = new Clipboard(display);
1594
	installDefaultContent();
1600
   installDefaultContent();
1595
	initializeRenderer();
1601
   initializeRenderer();
1596
	if ((style & SWT.WRAP) != 0) {
1602
   if ((style & SWT.WRAP) != 0) {
1597
		setWordWrap(true);
1603
      setWordWrap(true);
1598
	}
1604
   }
1599
	else {
1605
   else {
1600
	    lineCache = new ContentWidthCache(this, content.getLineCount());
1606
       lineCache = new ContentWidthCache(this, content.getLineCount());
1601
	}	
1607
   }
1602
	if (isBidi() == false) {
1608
   if (isBidi() == false) {
1603
		Caret caret = new Caret(this, SWT.NULL);
1609
      Caret caret = new Caret(this, SWT.NULL);
1604
		caret.setSize(1, caret.getSize().y);
1610
      caret.setSize(1, caret.getSize().y);
1605
	} 
1611
   }
1606
	else {
1612
   else {
1607
		createCaretBitmaps();
1613
      createCaretBitmaps();
1608
		new Caret(this, SWT.NULL);			
1614
      new Caret(this, SWT.NULL);
1609
		setBidiCaretDirection();
1615
      setBidiCaretDirection();
1610
		Runnable runnable = new Runnable() {
1616
      Runnable runnable = new Runnable() {
1611
			public void run() {
1617
         public void run() {
1612
				// setBidiCaretLocation calculates caret location like during 
1618
            // setBidiCaretLocation calculates caret location like during
1613
				// cursor movement and takes keyboard language into account. 
1619
            // cursor movement and takes keyboard language into account.
1614
				// Fixes 1GKPYMK
1620
            // Fixes 1GKPYMK
1615
				setBidiCaretLocation(null);
1621
            setBidiCaretLocation(null);
1616
			}
1622
         }
1617
		};
1623
      };
1618
		StyledTextBidi.addLanguageListener(this, runnable);
1624
      StyledTextBidi.addLanguageListener(this, runnable);
1619
	}
1625
   }
1620
	
1626
1621
	String platform= SWT.getPlatform();
1627
   String platform= SWT.getPlatform();
1622
	isCarbon = "carbon".equals(platform);	
1628
   isCarbon = "carbon".equals(platform);
1623
	
1629
1624
	// set the caret width, the height of the caret will default to the line height
1630
   // set the caret width, the height of the caret will default to the line height
1625
	calculateScrollBars();
1631
   calculateScrollBars();
1626
	createKeyBindings();
1632
   createKeyBindings();
1627
	ibeamCursor = new Cursor(display, SWT.CURSOR_IBEAM);
1633
1628
	setCursor(ibeamCursor);
1634
   ibeamCursor = new Cursor(display, SWT.CURSOR_IBEAM);
1629
	installListeners();
1635
   arrowCursor = new Cursor(display, SWT.CURSOR_ARROW);
1630
	installDefaultLineStyler();
1636
   currentCursor = ibeamCursor;
1637
   super.setCursor(currentCursor);
1638
1639
   installListeners();
1640
   installDefaultLineStyler();
1631
}
1641
}
1632
/**	 
1642
/**
1633
 * Adds an extended modify listener. An ExtendedModify event is sent by the 
1643
 * Adds an extended modify listener. An ExtendedModify event is sent by the
1634
 * widget when the widget text has changed.
1644
 * widget when the widget text has changed.
1635
 * <p>
1645
 * <p>
1636
 *
1646
 *
Lines 1644-1718 Link Here
1644
 * </ul>
1654
 * </ul>
1645
 */
1655
 */
1646
public void addExtendedModifyListener(ExtendedModifyListener extendedModifyListener) {
1656
public void addExtendedModifyListener(ExtendedModifyListener extendedModifyListener) {
1647
	checkWidget();
1657
   checkWidget();
1648
	if (extendedModifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1658
   if (extendedModifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1649
	StyledTextListener typedListener = new StyledTextListener(extendedModifyListener);
1659
   StyledTextListener typedListener = new StyledTextListener(extendedModifyListener);
1650
	addListener(ExtendedModify, typedListener);
1660
   addListener(ExtendedModify, typedListener);
1651
}
1661
}
1652
/** 
1662
/**
1653
 * Maps a key to an action.
1663
 * Maps a key to an action.
1654
 * One action can be associated with N keys. However, each key can only 
1664
 * One action can be associated with N keys. However, each key can only
1655
 * have one action (key:action is N:1 relation).
1665
 * have one action (key:action is N:1 relation).
1656
 * <p>
1666
 * <p>
1657
 *
1667
 *
1658
 * @param key a key code defined in SWT.java or a character. 
1668
 * @param key a key code defined in SWT.java or a character.
1659
 * 	Optionally ORd with a state mask.  Preferred state masks are one or more of
1669
 *    Optionally ORd with a state mask.  Preferred state masks are one or more of
1660
 *  SWT.MOD1, SWT.MOD2, SWT.MOD3, since these masks account for modifier platform 
1670
 *  SWT.MOD1, SWT.MOD2, SWT.MOD3, since these masks account for modifier platform
1661
 *  differences.  However, there may be cases where using the specific state masks
1671
 *  differences.  However, there may be cases where using the specific state masks
1662
 *  (i.e., SWT.CTRL, SWT.SHIFT, SWT.ALT, SWT.COMMAND) makes sense.
1672
 *  (i.e., SWT.CTRL, SWT.SHIFT, SWT.ALT, SWT.COMMAND) makes sense.
1663
 * @param action one of the predefined actions defined in ST.java. 
1673
 * @param action one of the predefined actions defined in ST.java.
1664
 * 	Use SWT.NULL to remove a key binding.
1674
 *    Use SWT.NULL to remove a key binding.
1665
 * @exception SWTException <ul>
1675
 * @exception SWTException <ul>
1666
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1676
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1667
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1677
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1668
 * </ul>
1678
 * </ul>
1669
 */
1679
 */
1670
public void setKeyBinding(int key, int action) {
1680
public void setKeyBinding(int key, int action) {
1671
	checkWidget(); 
1681
   checkWidget();
1672
	
1682
1673
	int keyValue = key & SWT.KEY_MASK;
1683
   int keyValue = key & SWT.KEY_MASK;
1674
	int modifierValue = key & SWT.MODIFIER_MASK;
1684
   int modifierValue = key & SWT.MODIFIER_MASK;
1675
	char keyChar = (char)keyValue;
1685
   char keyChar = (char)keyValue;
1676
1686
1677
	if (Character.isLetter(keyChar)) {
1687
   if (Character.isLetter(keyChar)) {
1678
		// make the keybinding case insensitive by adding it
1688
      // make the keybinding case insensitive by adding it
1679
		// in its upper and lower case form
1689
      // in its upper and lower case form
1680
		char ch = Character.toUpperCase(keyChar);
1690
      char ch = Character.toUpperCase(keyChar);
1681
		int newKey = ch | modifierValue;
1691
      int newKey = ch | modifierValue;
1682
		if (action == SWT.NULL) {
1692
      if (action == SWT.NULL) {
1683
			keyActionMap.remove(new Integer(newKey));
1693
         keyActionMap.remove(new Integer(newKey));
1684
		}
1694
      }
1685
		else {
1695
      else {
1686
		 	keyActionMap.put(new Integer(newKey), new Integer(action));
1696
         keyActionMap.put(new Integer(newKey), new Integer(action));
1687
		}
1697
      }
1688
		ch = Character.toLowerCase(keyChar);
1698
      ch = Character.toLowerCase(keyChar);
1689
		newKey = ch | modifierValue;
1699
      newKey = ch | modifierValue;
1690
		if (action == SWT.NULL) {
1700
      if (action == SWT.NULL) {
1691
			keyActionMap.remove(new Integer(newKey));
1701
         keyActionMap.remove(new Integer(newKey));
1692
		}
1702
      }
1693
		else {
1703
      else {
1694
		 	keyActionMap.put(new Integer(newKey), new Integer(action));
1704
         keyActionMap.put(new Integer(newKey), new Integer(action));
1695
		}
1705
      }
1696
	} else {
1706
   } else {
1697
		if (action == SWT.NULL) {
1707
      if (action == SWT.NULL) {
1698
			keyActionMap.remove(new Integer(key));
1708
         keyActionMap.remove(new Integer(key));
1699
		}
1709
      }
1700
		else {
1710
      else {
1701
		 	keyActionMap.put(new Integer(key), new Integer(action));
1711
         keyActionMap.put(new Integer(key), new Integer(action));
1702
		}
1712
      }
1703
	}
1713
   }
1704
		
1714
1705
}
1715
}
1706
/**
1716
/**
1707
 * Adds a bidirectional segment listener. A BidiSegmentEvent is sent 
1717
 * Adds a bidirectional segment listener. A BidiSegmentEvent is sent
1708
 * whenever a line of text is measured or rendered. The user can 
1718
 * whenever a line of text is measured or rendered. The user can
1709
 * specify text ranges in the line that should be treated as if they 
1719
 * specify text ranges in the line that should be treated as if they
1710
 * had a different direction than the surrounding text.
1720
 * had a different direction than the surrounding text.
1711
 * This may be used when adjacent segments of right-to-left text should
1721
 * This may be used when adjacent segments of right-to-left text should
1712
 * not be reordered relative to each other. 
1722
 * not be reordered relative to each other.
1713
 * E.g., Multiple Java string literals in a right-to-left language
1723
 * E.g., Multiple Java string literals in a right-to-left language
1714
 * should generally remain in logical order to each other, that is, the
1724
 * should generally remain in logical order to each other, that is, the
1715
 * way they are stored. 
1725
 * way they are stored.
1716
 * <p>
1726
 * <p>
1717
 *
1727
 *
1718
 * @param listener the listener
1728
 * @param listener the listener
Lines 1727-1741 Link Here
1727
 * @since 2.0
1737
 * @since 2.0
1728
 */
1738
 */
1729
public void addBidiSegmentListener(BidiSegmentListener listener) {
1739
public void addBidiSegmentListener(BidiSegmentListener listener) {
1730
	checkWidget();
1740
   checkWidget();
1731
	if (listener == null) {
1741
   if (listener == null) {
1732
		SWT.error(SWT.ERROR_NULL_ARGUMENT);
1742
      SWT.error(SWT.ERROR_NULL_ARGUMENT);
1733
	}
1743
   }
1734
	StyledTextListener typedListener = new StyledTextListener(listener);
1744
   StyledTextListener typedListener = new StyledTextListener(listener);
1735
	addListener(LineGetSegments, typedListener);	
1745
   addListener(LineGetSegments, typedListener);
1736
}
1746
}
1737
/**
1747
/**
1738
 * Adds a line background listener. A LineGetBackground event is sent by the 
1748
 * Adds a line background listener. A LineGetBackground event is sent by the
1739
 * widget to determine the background color for a line.
1749
 * widget to determine the background color for a line.
1740
 * <p>
1750
 * <p>
1741
 *
1751
 *
Lines 1749-1766 Link Here
1749
 * </ul>
1759
 * </ul>
1750
 */
1760
 */
1751
public void addLineBackgroundListener(LineBackgroundListener listener) {
1761
public void addLineBackgroundListener(LineBackgroundListener listener) {
1752
	checkWidget();
1762
   checkWidget();
1753
	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1763
   if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1754
	if (userLineBackground == false) {
1764
   if (userLineBackground == false) {
1755
		removeLineBackgroundListener(defaultLineStyler);
1765
      removeLineBackgroundListener(defaultLineStyler);
1756
		defaultLineStyler.setLineBackground(0, logicalContent.getLineCount(), null);
1766
      defaultLineStyler.setLineBackground(0, logicalContent.getLineCount(), null);
1757
		userLineBackground = true;
1767
      userLineBackground = true;
1758
	}	
1768
   }
1759
	StyledTextListener typedListener = new StyledTextListener(listener);
1769
   StyledTextListener typedListener = new StyledTextListener(listener);
1760
	addListener(LineGetBackground, typedListener);	
1770
   addListener(LineGetBackground, typedListener);
1761
}
1771
}
1762
/**
1772
/**
1763
 * Adds a line style listener. A LineGetStyle event is sent by the widget to 
1773
 * Adds a line style listener. A LineGetStyle event is sent by the widget to
1764
 * determine the styles for a line.
1774
 * determine the styles for a line.
1765
 * <p>
1775
 * <p>
1766
 *
1776
 *
Lines 1774-1793 Link Here
1774
 * </ul>
1784
 * </ul>
1775
 */
1785
 */
1776
public void addLineStyleListener(LineStyleListener listener) {
1786
public void addLineStyleListener(LineStyleListener listener) {
1777
	checkWidget();
1787
   checkWidget();
1778
	if (listener == null) {
1788
   if (listener == null) {
1779
		SWT.error(SWT.ERROR_NULL_ARGUMENT);
1789
      SWT.error(SWT.ERROR_NULL_ARGUMENT);
1780
	}
1790
   }
1781
	if (userLineStyle == false) {
1791
   if (userLineStyle == false) {
1782
		removeLineStyleListener(defaultLineStyler);
1792
      removeLineStyleListener(defaultLineStyler);
1783
		defaultLineStyler.setStyleRange(null);
1793
      defaultLineStyler.setStyleRange(null);
1784
		userLineStyle = true;
1794
      userLineStyle = true;
1785
	}
1795
   }
1786
	StyledTextListener typedListener = new StyledTextListener(listener);
1796
   StyledTextListener typedListener = new StyledTextListener(listener);
1787
	addListener(LineGetStyle, typedListener);	
1797
   addListener(LineGetStyle, typedListener);
1788
}
1798
}
1789
/**	 
1799
/**
1790
 * Adds a modify listener. A Modify event is sent by the widget when the widget text 
1800
 * Adds a modify listener. A Modify event is sent by the widget when the widget text
1791
 * has changed.
1801
 * has changed.
1792
 * <p>
1802
 * <p>
1793
 *
1803
 *
Lines 1801-1815 Link Here
1801
 * </ul>
1811
 * </ul>
1802
 */
1812
 */
1803
public void addModifyListener(ModifyListener modifyListener) {
1813
public void addModifyListener(ModifyListener modifyListener) {
1804
	checkWidget();
1814
   checkWidget();
1805
	if (modifyListener == null) {
1815
   if (modifyListener == null) {
1806
		SWT.error(SWT.ERROR_NULL_ARGUMENT);
1816
      SWT.error(SWT.ERROR_NULL_ARGUMENT);
1807
	}
1817
   }
1808
	TypedListener typedListener = new TypedListener(modifyListener);
1818
   TypedListener typedListener = new TypedListener(modifyListener);
1809
	addListener(SWT.Modify, typedListener);
1819
   addListener(SWT.Modify, typedListener);
1810
}
1820
}
1811
/**	 
1821
/**
1812
 * Adds a selection listener. A Selection event is sent by the widget when the 
1822
 * Adds a selection listener. A Selection event is sent by the widget when the
1813
 * selection has changed.
1823
 * selection has changed.
1814
 * <p>
1824
 * <p>
1815
 *
1825
 *
Lines 1823-1839 Link Here
1823
 * </ul>
1833
 * </ul>
1824
 */
1834
 */
1825
public void addSelectionListener(SelectionListener listener) {
1835
public void addSelectionListener(SelectionListener listener) {
1826
	checkWidget();
1836
   checkWidget();
1827
	if (listener == null) {
1837
   if (listener == null) {
1828
		SWT.error(SWT.ERROR_NULL_ARGUMENT);
1838
      SWT.error(SWT.ERROR_NULL_ARGUMENT);
1829
	}
1839
   }
1830
	TypedListener typedListener = new TypedListener(listener);
1840
   TypedListener typedListener = new TypedListener(listener);
1831
	addListener(SWT.Selection, typedListener);	
1841
   addListener(SWT.Selection, typedListener);
1832
}
1842
}
1833
/**	 
1843
/**
1834
 * Adds a verify key listener. A VerifyKey event is sent by the widget when a key 
1844
 * Adds a verify key listener. A VerifyKey event is sent by the widget when a key
1835
 * is pressed. The widget ignores the key press if the listener sets the doit field 
1845
 * is pressed. The widget ignores the key press if the listener sets the doit field
1836
 * of the event to false. 
1846
 * of the event to false.
1837
 * <p>
1847
 * <p>
1838
 *
1848
 *
1839
 * @param listener the listener
1849
 * @param listener the listener
Lines 1846-1862 Link Here
1846
 * </ul>
1856
 * </ul>
1847
 */
1857
 */
1848
public void addVerifyKeyListener(VerifyKeyListener listener) {
1858
public void addVerifyKeyListener(VerifyKeyListener listener) {
1849
	checkWidget();
1859
   checkWidget();
1850
	if (listener == null) {
1860
   if (listener == null) {
1851
		SWT.error(SWT.ERROR_NULL_ARGUMENT);
1861
      SWT.error(SWT.ERROR_NULL_ARGUMENT);
1852
	}
1862
   }
1853
	StyledTextListener typedListener = new StyledTextListener(listener);
1863
   StyledTextListener typedListener = new StyledTextListener(listener);
1854
	addListener(VerifyKey, typedListener);	
1864
   addListener(VerifyKey, typedListener);
1855
}
1865
}
1856
/**	 
1866
/**
1857
 * Adds a verify listener. A Verify event is sent by the widget when the widget text 
1867
 * Adds a verify listener. A Verify event is sent by the widget when the widget text
1858
 * is about to change. The listener can set the event text and the doit field to 
1868
 * is about to change. The listener can set the event text and the doit field to
1859
 * change the text that is set in the widget or to force the widget to ignore the 
1869
 * change the text that is set in the widget or to force the widget to ignore the
1860
 * text change.
1870
 * text change.
1861
 * <p>
1871
 * <p>
1862
 *
1872
 *
Lines 1870-1883 Link Here
1870
 * </ul>
1880
 * </ul>
1871
 */
1881
 */
1872
public void addVerifyListener(VerifyListener verifyListener) {
1882
public void addVerifyListener(VerifyListener verifyListener) {
1873
	checkWidget();
1883
   checkWidget();
1874
	if (verifyListener == null) {
1884
   if (verifyListener == null) {
1875
		SWT.error(SWT.ERROR_NULL_ARGUMENT);
1885
      SWT.error(SWT.ERROR_NULL_ARGUMENT);
1876
	}
1886
   }
1877
	TypedListener typedListener = new TypedListener(verifyListener);
1887
   TypedListener typedListener = new TypedListener(verifyListener);
1878
	addListener(SWT.Verify, typedListener);
1888
   addListener(SWT.Verify, typedListener);
1879
}
1889
}
1880
/** 
1890
/**
1881
 * Appends a string to the text at the end of the widget.
1891
 * Appends a string to the text at the end of the widget.
1882
 * <p>
1892
 * <p>
1883
 *
1893
 *
Lines 1892-1927 Link Here
1892
 * </ul>
1902
 * </ul>
1893
 */
1903
 */
1894
public void append(String string) {
1904
public void append(String string) {
1895
	checkWidget();
1905
   checkWidget();
1896
	if (string == null) {
1906
   if (string == null) {
1897
		SWT.error(SWT.ERROR_NULL_ARGUMENT);
1907
      SWT.error(SWT.ERROR_NULL_ARGUMENT);
1898
	}
1908
   }
1899
	int lastChar = Math.max(getCharCount(), 0);
1909
   int lastChar = Math.max(getCharCount(), 0);
1900
	replaceTextRange(lastChar, 0, string);
1910
   replaceTextRange(lastChar, 0, string);
1901
}
1911
}
1902
/**
1912
/**
1903
 * Calculates the width of the widest visible line.
1913
 * Calculates the width of the widest visible line.
1904
 */
1914
 */
1905
void calculateContentWidth() {
1915
void calculateContentWidth() {
1906
	if (lineHeight != 0) {
1916
   if (lineHeight != 0) {
1907
	    lineCache = getLineCache(content);
1917
       lineCache = getLineCache(content);
1908
		lineCache.calculate(topIndex, getPartialBottomIndex() - topIndex + 1);
1918
      lineCache.calculate(topIndex, getPartialBottomIndex() - topIndex + 1);
1909
	}
1919
   }
1910
}
1920
}
1911
/**
1921
/**
1912
 * Calculates the scroll bars
1922
 * Calculates the scroll bars
1913
 */
1923
 */
1914
void calculateScrollBars() {
1924
void calculateScrollBars() {
1915
	ScrollBar horizontalBar = getHorizontalBar();
1925
   ScrollBar horizontalBar = getHorizontalBar();
1916
	ScrollBar verticalBar = getVerticalBar();
1926
   ScrollBar verticalBar = getVerticalBar();
1917
	
1927
1918
	setScrollBars();
1928
   setScrollBars();
1919
	if (verticalBar != null) {
1929
   if (verticalBar != null) {
1920
		verticalBar.setIncrement(getVerticalIncrement());
1930
      verticalBar.setIncrement(getVerticalIncrement());
1921
	}	
1931
   }
1922
	if (horizontalBar != null) {
1932
   if (horizontalBar != null) {
1923
		horizontalBar.setIncrement(getHorizontalIncrement());
1933
      horizontalBar.setIncrement(getHorizontalIncrement());
1924
	}
1934
   }
1925
}
1935
}
1926
/**
1936
/**
1927
 * Calculates the top index based on the current vertical scroll offset.
1937
 * Calculates the top index based on the current vertical scroll offset.
Lines 1930-2026 Link Here
1930
 * The top index starts at 0.
1940
 * The top index starts at 0.
1931
 */
1941
 */
1932
void calculateTopIndex() {
1942
void calculateTopIndex() {
1933
	int oldTopIndex = topIndex;
1943
   int oldTopIndex = topIndex;
1934
	int verticalIncrement = getVerticalIncrement();
1944
   int verticalIncrement = getVerticalIncrement();
1935
	int clientAreaHeight = getClientArea().height;
1945
   int clientAreaHeight = getClientArea().height;
1936
	
1946
1937
	if (verticalIncrement == 0) {
1947
   if (verticalIncrement == 0) {
1938
		return;
1948
      return;
1939
	}
1949
   }
1940
	topIndex = Compatibility.ceil(verticalScrollOffset, verticalIncrement);
1950
   topIndex = Compatibility.ceil(verticalScrollOffset, verticalIncrement);
1941
	// Set top index to partially visible top line if no line is fully 
1951
   // Set top index to partially visible top line if no line is fully
1942
	// visible but at least some of the widget client area is visible.
1952
   // visible but at least some of the widget client area is visible.
1943
	// Fixes bug 15088.
1953
   // Fixes bug 15088.
1944
	if (topIndex > 0) {
1954
   if (topIndex > 0) {
1945
		if (clientAreaHeight > 0) {
1955
      if (clientAreaHeight > 0) {
1946
			int bottomPixel = verticalScrollOffset + clientAreaHeight;
1956
         int bottomPixel = verticalScrollOffset + clientAreaHeight;
1947
			int fullLineTopPixel = topIndex * verticalIncrement;
1957
         int fullLineTopPixel = topIndex * verticalIncrement;
1948
			int fullLineVisibleHeight = bottomPixel - fullLineTopPixel;
1958
         int fullLineVisibleHeight = bottomPixel - fullLineTopPixel;
1949
			// set top index to partially visible line if no line fully fits in 
1959
         // set top index to partially visible line if no line fully fits in
1950
			// client area or if space is available but not used (the latter should
1960
         // client area or if space is available but not used (the latter should
1951
			// never happen because we use claimBottomFreeSpace)
1961
         // never happen because we use claimBottomFreeSpace)
1952
			if (fullLineVisibleHeight < verticalIncrement) {
1962
         if (fullLineVisibleHeight < verticalIncrement) {
1953
				topIndex--;
1963
            topIndex--;
1954
			}
1964
         }
1955
		}
1965
      }
1956
		else 
1966
      else
1957
		if (topIndex >= content.getLineCount()) {
1967
      if (topIndex >= content.getLineCount()) {
1958
			topIndex = content.getLineCount() - 1;
1968
         topIndex = content.getLineCount() - 1;
1959
		}
1969
      }
1960
	}
1970
   }
1961
	if (topIndex != oldTopIndex) {
1971
   if (topIndex != oldTopIndex) {
1962
		topOffset = content.getOffsetAtLine(topIndex);
1972
      topOffset = content.getOffsetAtLine(topIndex);
1963
		lineCache.calculate(topIndex, getPartialBottomIndex() - topIndex + 1);
1973
      lineCache.calculate(topIndex, getPartialBottomIndex() - topIndex + 1);
1964
		setHorizontalScrollBar();
1974
      setHorizontalScrollBar();
1965
	}
1975
   }
1966
}
1976
}
1967
/**
1977
/**
1968
 * Hides the scroll bars if widget is created in single line mode.
1978
 * Hides the scroll bars if widget is created in single line mode.
1969
 */
1979
 */
1970
static int checkStyle(int style) {
1980
static int checkStyle(int style) {
1971
	if ((style & SWT.SINGLE) != 0) {
1981
   if ((style & SWT.SINGLE) != 0) {
1972
		style &= ~(SWT.H_SCROLL | SWT.V_SCROLL);
1982
      style &= ~(SWT.H_SCROLL | SWT.V_SCROLL);
1973
	}
1983
   }
1974
	return style;
1984
   return style;
1975
}
1985
}
1976
/**
1986
/**
1977
 * Scrolls down the text to use new space made available by a resize or by 
1987
 * Scrolls down the text to use new space made available by a resize or by
1978
 * deleted lines.
1988
 * deleted lines.
1979
 */
1989
 */
1980
void claimBottomFreeSpace() {
1990
void claimBottomFreeSpace() {
1981
	int newVerticalOffset = Math.max(0, content.getLineCount() * lineHeight - getClientArea().height);
1991
   int newVerticalOffset = Math.max(0, content.getLineCount() * lineHeight - getClientArea().height);
1982
	
1992
1983
	if (newVerticalOffset < verticalScrollOffset) {
1993
   if (newVerticalOffset < verticalScrollOffset) {
1984
		// Scroll up so that empty lines below last text line are used.
1994
      // Scroll up so that empty lines below last text line are used.
1985
		// Fixes 1GEYJM0
1995
      // Fixes 1GEYJM0
1986
		setVerticalScrollOffset(newVerticalOffset, true);
1996
      setVerticalScrollOffset(newVerticalOffset, true);
1987
	}
1997
   }
1988
}
1998
}
1989
/**
1999
/**
1990
 * Scrolls text to the right to use new space made available by a resize.
2000
 * Scrolls text to the right to use new space made available by a resize.
1991
 */
2001
 */
1992
void claimRightFreeSpace() {
2002
void claimRightFreeSpace() {
1993
	int newHorizontalOffset = Math.max(0, lineCache.getWidth() - (getClientArea().width - leftMargin - rightMargin));
2003
   int newHorizontalOffset = Math.max(0, lineCache.getWidth() - (getClientArea().width - leftMargin - rightMargin));
1994
	
2004
1995
	if (newHorizontalOffset < horizontalScrollOffset) {			
2005
   if (newHorizontalOffset < horizontalScrollOffset) {
1996
		// item is no longer drawn past the right border of the client area
2006
      // item is no longer drawn past the right border of the client area
1997
		// align the right end of the item with the right border of the 
2007
      // align the right end of the item with the right border of the
1998
		// client area (window is scrolled right).
2008
      // client area (window is scrolled right).
1999
		scrollHorizontalBar(newHorizontalOffset - horizontalScrollOffset);					
2009
      scrollHorizontalBar(newHorizontalOffset - horizontalScrollOffset);
2000
	}
2010
   }
2001
}
2011
}
2002
/**
2012
/**
2003
 * Clears the widget margin.
2013
 * Clears the widget margin.
2004
 * 
2014
 *
2005
 * @param gc GC to render on
2015
 * @param gc GC to render on
2006
 * @param background background color to use for clearing the margin
2016
 * @param background background color to use for clearing the margin
2007
 * @param clientArea widget client area dimensions
2017
 * @param clientArea widget client area dimensions
2008
 * @param renderHeight height in pixel of the rendered lines
2018
 * @param renderHeight height in pixel of the rendered lines
2009
 */
2019
 */
2010
void clearMargin(GC gc, Color background, Rectangle clientArea, int renderHeight) {
2020
void clearMargin(GC gc, Color background, Rectangle clientArea, int renderHeight) {
2011
	if (renderHeight + topMargin <= 0) {
2021
   if (renderHeight + topMargin <= 0) {
2012
		return;
2022
      return;
2013
	}
2023
   }
2014
	// clear the margin background
2024
   // clear the margin background
2015
	gc.setBackground(background);
2025
   gc.setBackground(background);
2016
	gc.fillRectangle(0, 0, clientArea.width, topMargin);
2026
   gc.fillRectangle(0, 0, clientArea.width, topMargin);
2017
	gc.fillRectangle(0, 0, leftMargin, renderHeight + topMargin);	
2027
   gc.fillRectangle(0, 0, leftMargin, renderHeight + topMargin);
2018
	gc.fillRectangle(
2028
   gc.fillRectangle(
2019
		0, clientArea.height - bottomMargin, 
2029
      0, clientArea.height - bottomMargin,
2020
		clientArea.width, bottomMargin);
2030
      clientArea.width, bottomMargin);
2021
	gc.fillRectangle(
2031
   gc.fillRectangle(
2022
		clientArea.width - rightMargin, 0, 
2032
      clientArea.width - rightMargin, 0,
2023
		rightMargin, renderHeight + topMargin);
2033
      rightMargin, renderHeight + topMargin);
2024
}
2034
}
2025
/**
2035
/**
2026
 * Removes the widget selection.
2036
 * Removes the widget selection.
Lines 2029-2052 Link Here
2029
 * @param sendEvent a Selection event is sent when set to true and when the selection is actually reset.
2039
 * @param sendEvent a Selection event is sent when set to true and when the selection is actually reset.
2030
 */
2040
 */
2031
void clearSelection(boolean sendEvent) {
2041
void clearSelection(boolean sendEvent) {
2032
	int selectionStart = selection.x;
2042
   int selectionStart = selection.x;
2033
	int selectionEnd = selection.y;
2043
   int selectionEnd = selection.y;
2034
	int length = content.getCharCount();
2044
   int length = content.getCharCount();
2035
	
2045
2036
	resetSelection();
2046
   resetSelection();
2037
	// redraw old selection, if any
2047
   // redraw old selection, if any
2038
	if (selectionEnd - selectionStart > 0) {
2048
   if (selectionEnd - selectionStart > 0) {
2039
		// called internally to remove selection after text is removed
2049
      // called internally to remove selection after text is removed
2040
		// therefore make sure redraw range is valid.
2050
      // therefore make sure redraw range is valid.
2041
		int redrawStart = Math.min(selectionStart, length);
2051
      int redrawStart = Math.min(selectionStart, length);
2042
		int redrawEnd = Math.min(selectionEnd, length);
2052
      int redrawEnd = Math.min(selectionEnd, length);
2043
		if (redrawEnd - redrawStart > 0) {
2053
      if (redrawEnd - redrawStart > 0) {
2044
			internalRedrawRange(redrawStart, redrawEnd - redrawStart, true);
2054
         internalRedrawRange(redrawStart, redrawEnd - redrawStart, true);
2045
		}
2055
      }
2046
		if (sendEvent == true) {
2056
      if (sendEvent == true) {
2047
			sendSelectionEvent();
2057
         sendSelectionEvent();
2048
		}
2058
      }
2049
	}
2059
   }
2050
}
2060
}
2051
/**
2061
/**
2052
 * Computes the preferred size.
2062
 * Computes the preferred size.
Lines 2057-2133 Link Here
2057
 * </ul>
2067
 * </ul>
2058
 */
2068
 */
2059
public Point computeSize (int wHint, int hHint, boolean changed) {
2069
public Point computeSize (int wHint, int hHint, boolean changed) {
2060
	checkWidget();
2070
   checkWidget();
2061
	int count, width, height;
2071
   int count, width, height;
2062
	boolean singleLine = (getStyle() & SWT.SINGLE) != 0;
2072
   boolean singleLine = (getStyle() & SWT.SINGLE) != 0;
2063
	
2073
2064
	if (singleLine) {
2074
   if (singleLine) {
2065
		count = 1;
2075
      count = 1;
2066
	} else {
2076
   } else {
2067
		count = content.getLineCount();
2077
      count = content.getLineCount();
2068
	}
2078
   }
2069
	if (wHint != SWT.DEFAULT) {
2079
   if (wHint != SWT.DEFAULT) {
2070
		width = wHint;
2080
      width = wHint;
2071
	} 
2081
   }
2072
	else {
2082
   else {
2073
		width = DEFAULT_WIDTH;
2083
      width = DEFAULT_WIDTH;
2074
	}
2084
   }
2075
2085
2076
	if (wordWrap) {
2086
   if (wordWrap) {
2077
		if (((WrappedContent) content).getVisualLineCount() != 0) {
2087
      if (((WrappedContent) content).getVisualLineCount() != 0) {
2078
			// lines have already been wrapped to a specific width.
2088
         // lines have already been wrapped to a specific width.
2079
			// use existing line count. fixes bug 9191
2089
         // use existing line count. fixes bug 9191
2080
			if (wHint == SWT.DEFAULT) {
2090
         if (wHint == SWT.DEFAULT) {
2081
				width = lineCache.getWidth();
2091
            width = lineCache.getWidth();
2082
			} else {
2092
         } else {
2083
				((WrappedContent) content).wrapLines(width);
2093
            ((WrappedContent) content).wrapLines(width);
2084
			  	// caret may be on a different line after a rewrap
2094
            // caret may be on a different line after a rewrap
2085
		  		setCaretLocation();
2095
            setCaretLocation();
2086
			}	
2096
         }
2087
			if (singleLine == false) {
2097
         if (singleLine == false) {
2088
				count = content.getLineCount();
2098
            count = content.getLineCount();
2089
			}
2099
         }
2090
		}
2100
      }
2091
		else {
2101
      else {
2092
			if (singleLine == false) {
2102
         if (singleLine == false) {
2093
				((WrappedContent) content).wrapLines(width);
2103
            ((WrappedContent) content).wrapLines(width);
2094
			  	// caret may be on a different line after a rewrap
2104
            // caret may be on a different line after a rewrap
2095
		  		setCaretLocation();
2105
            setCaretLocation();
2096
				count = content.getLineCount();
2106
            count = content.getLineCount();
2097
			}
2107
         }
2098
		}
2108
      }
2099
	}
2109
   }
2100
	else if (wHint == SWT.DEFAULT) {
2110
   else if (wHint == SWT.DEFAULT) {
2101
		// Only calculate what can actually be displayed.
2111
      // Only calculate what can actually be displayed.
2102
		// Do this because measuring each text line is a 
2112
      // Do this because measuring each text line is a
2103
		// time-consuming process.
2113
      // time-consuming process.
2104
		int visibleCount = Math.min (count, getDisplay().getBounds().height / lineHeight);
2114
      int visibleCount = Math.min (count, getDisplay().getBounds().height / lineHeight);
2105
		lineCache.calculate(0, visibleCount);
2115
      lineCache.calculate(0, visibleCount);
2106
		width = lineCache.getWidth() + leftMargin + rightMargin;
2116
      width = lineCache.getWidth() + leftMargin + rightMargin;
2107
	}
2117
   }
2108
	if (hHint != SWT.DEFAULT) {
2118
   if (hHint != SWT.DEFAULT) {
2109
		height = hHint;
2119
      height = hHint;
2110
	} 
2120
   }
2111
	else {
2121
   else {
2112
		height = count * lineHeight + topMargin + bottomMargin;
2122
      height = count * lineHeight + topMargin + bottomMargin;
2113
	}
2123
   }
2114
	// Use default values if no text is defined.
2124
   // Use default values if no text is defined.
2115
	if (width == 0) {
2125
   if (width == 0) {
2116
		width = DEFAULT_WIDTH;
2126
      width = DEFAULT_WIDTH;
2117
	}
2127
   }
2118
	if (height == 0) {
2128
   if (height == 0) {
2119
		if (singleLine) {
2129
      if (singleLine) {
2120
			height = lineHeight;
2130
         height = lineHeight;
2121
		}
2131
      }
2122
		else {
2132
      else {
2123
			height = DEFAULT_HEIGHT;
2133
         height = DEFAULT_HEIGHT;
2124
		}
2134
      }
2125
	}
2135
   }
2126
	Rectangle rect = computeTrim(0, 0, width, height);
2136
   Rectangle rect = computeTrim(0, 0, width, height);
2127
	return new Point (rect.width, rect.height);
2137
   return new Point (rect.width, rect.height);
2128
}
2138
}
2129
/**
2139
/**
2130
 * Copies the selected text to the clipboard.  The text will be put in the 
2140
 * Copies the selected text to the clipboard.  The text will be put in the
2131
 * clipboard in plain text format and RTF format.
2141
 * clipboard in plain text format and RTF format.
2132
 * <p>
2142
 * <p>
2133
 *
2143
 *
Lines 2137-2271 Link Here
2137
 * </ul>
2147
 * </ul>
2138
 */
2148
 */
2139
public void copy() {
2149
public void copy() {
2140
	checkWidget();
2150
   checkWidget();
2141
	int length = selection.y - selection.x;
2151
   int length = selection.y - selection.x;
2142
	if (length > 0) {
2152
   if (length > 0) {
2143
		try {
2153
      try {
2144
			setClipboardContent(selection.x, length);
2154
         setClipboardContent(selection.x, length);
2145
		}
2155
      }
2146
		catch (SWTError error) {
2156
      catch (SWTError error) {
2147
			// Copy to clipboard failed. This happens when another application 
2157
         // Copy to clipboard failed. This happens when another application
2148
			// is accessing the clipboard while we copy. Ignore the error.
2158
         // is accessing the clipboard while we copy. Ignore the error.
2149
			// Fixes 1GDQAVN
2159
         // Fixes 1GDQAVN
2150
			// Rethrow all other errors. Fixes bug 17578.
2160
         // Rethrow all other errors. Fixes bug 17578.
2151
			if (error.code != DND.ERROR_CANNOT_SET_CLIPBOARD) {
2161
         if (error.code != DND.ERROR_CANNOT_SET_CLIPBOARD) {
2152
				throw error;
2162
            throw error;
2153
			}
2163
         }
2154
		}
2164
      }
2155
	}
2165
   }
2156
}
2166
}
2157
/**
2167
/**
2158
 * Returns a string that uses only the line delimiter specified by the 
2168
 * Returns a string that uses only the line delimiter specified by the
2159
 * StyledTextContent implementation.
2169
 * StyledTextContent implementation.
2160
 * Returns only the first line if the widget has the SWT.SINGLE style.
2170
 * Returns only the first line if the widget has the SWT.SINGLE style.
2161
 * <p>
2171
 * <p>
2162
 *
2172
 *
2163
 * @param text the text that may have line delimiters that don't 
2173
 * @param text the text that may have line delimiters that don't
2164
 * 	match the model line delimiter. Possible line delimiters 
2174
 *    match the model line delimiter. Possible line delimiters
2165
 * 	are CR ('\r'), LF ('\n'), CR/LF ("\r\n")
2175
 *    are CR ('\r'), LF ('\n'), CR/LF ("\r\n")
2166
 * @return the converted text that only uses the line delimiter 
2176
 * @return the converted text that only uses the line delimiter
2167
 * 	specified by the model. Returns only the first line if the widget 
2177
 *    specified by the model. Returns only the first line if the widget
2168
 * 	has the SWT.SINGLE style.
2178
 *    has the SWT.SINGLE style.
2169
 */
2179
 */
2170
String getModelDelimitedText(String text) {
2180
String getModelDelimitedText(String text) {
2171
	StringBuffer convertedText;
2181
   StringBuffer convertedText;
2172
	String delimiter = getLineDelimiter();
2182
   String delimiter = getLineDelimiter();
2173
	int length = text.length();	
2183
   int length = text.length();
2174
	int crIndex = 0;
2184
   int crIndex = 0;
2175
	int lfIndex = 0;
2185
   int lfIndex = 0;
2176
	int i = 0;
2186
   int i = 0;
2177
	
2187
2178
	if (length == 0) {
2188
   if (length == 0) {
2179
		return text;
2189
      return text;
2180
	}
2190
   }
2181
	convertedText = new StringBuffer(length);
2191
   convertedText = new StringBuffer(length);
2182
	while (i < length) {
2192
   while (i < length) {
2183
		if (crIndex != -1) {
2193
      if (crIndex != -1) {
2184
			crIndex = text.indexOf(SWT.CR, i);
2194
         crIndex = text.indexOf(SWT.CR, i);
2185
		}
2195
      }
2186
		if (lfIndex != -1) {
2196
      if (lfIndex != -1) {
2187
			lfIndex = text.indexOf(SWT.LF, i);
2197
         lfIndex = text.indexOf(SWT.LF, i);
2188
		}
2198
      }
2189
		if (lfIndex == -1 && crIndex == -1) {	// no more line breaks?
2199
      if (lfIndex == -1 && crIndex == -1) {  // no more line breaks?
2190
			break;
2200
         break;
2191
		}
2201
      }
2192
		else									// CR occurs before LF or no LF present?
2202
      else                          // CR occurs before LF or no LF present?
2193
		if ((crIndex < lfIndex && crIndex != -1) || lfIndex == -1) {	
2203
      if ((crIndex < lfIndex && crIndex != -1) || lfIndex == -1) {
2194
			convertedText.append(text.substring(i, crIndex));
2204
         convertedText.append(text.substring(i, crIndex));
2195
			if (lfIndex == crIndex + 1) {		// CR/LF combination?
2205
         if (lfIndex == crIndex + 1) {    // CR/LF combination?
2196
				i = lfIndex + 1;
2206
            i = lfIndex + 1;
2197
			}
2207
         }
2198
			else {
2208
         else {
2199
				i = crIndex + 1;
2209
            i = crIndex + 1;
2200
			}
2210
         }
2201
		}
2211
      }
2202
		else {									// LF occurs before CR!
2212
      else {                           // LF occurs before CR!
2203
			convertedText.append(text.substring(i, lfIndex));
2213
         convertedText.append(text.substring(i, lfIndex));
2204
			i = lfIndex + 1;
2214
         i = lfIndex + 1;
2205
		}
2215
      }
2206
		if (isSingleLine()) {
2216
      if (isSingleLine()) {
2207
			break;
2217
         break;
2208
		}
2218
      }
2209
		convertedText.append(delimiter);
2219
      convertedText.append(delimiter);
2210
	}
2220
   }
2211
	// copy remaining text if any and if not in single line mode or no 
2221
   // copy remaining text if any and if not in single line mode or no
2212
	// text copied thus far (because there only is one line)
2222
   // text copied thus far (because there only is one line)
2213
	if (i < length && (isSingleLine() == false || convertedText.length() == 0)) {
2223
   if (i < length && (isSingleLine() == false || convertedText.length() == 0)) {
2214
		convertedText.append(text.substring(i));
2224
      convertedText.append(text.substring(i));
2215
	}
2225
   }
2216
	return convertedText.toString();
2226
   return convertedText.toString();
2217
}
2227
}
2218
/**
2228
/**
2219
 * Creates default key bindings.
2229
 * Creates default key bindings.
2220
 */
2230
 */
2221
void createKeyBindings() {
2231
void createKeyBindings() {
2222
	// Navigation
2232
   // Navigation
2223
	setKeyBinding(SWT.ARROW_UP, ST.LINE_UP);	
2233
   setKeyBinding(SWT.ARROW_UP, ST.LINE_UP);
2224
	setKeyBinding(SWT.ARROW_DOWN, ST.LINE_DOWN);
2234
   setKeyBinding(SWT.ARROW_DOWN, ST.LINE_DOWN);
2225
	setKeyBinding(SWT.HOME, ST.LINE_START);
2235
   setKeyBinding(SWT.HOME, ST.LINE_START);
2226
	setKeyBinding(SWT.END, ST.LINE_END);
2236
   setKeyBinding(SWT.END, ST.LINE_END);
2227
	setKeyBinding(SWT.ARROW_LEFT, ST.COLUMN_PREVIOUS);
2237
   setKeyBinding(SWT.ARROW_LEFT, ST.COLUMN_PREVIOUS);
2228
	setKeyBinding(SWT.ARROW_RIGHT, ST.COLUMN_NEXT);
2238
   setKeyBinding(SWT.ARROW_RIGHT, ST.COLUMN_NEXT);
2229
	setKeyBinding(SWT.PAGE_UP, ST.PAGE_UP);
2239
   setKeyBinding(SWT.PAGE_UP, ST.PAGE_UP);
2230
	setKeyBinding(SWT.PAGE_DOWN, ST.PAGE_DOWN);
2240
   setKeyBinding(SWT.PAGE_DOWN, ST.PAGE_DOWN);
2231
	setKeyBinding(SWT.ARROW_LEFT | SWT.MOD1, ST.WORD_PREVIOUS);
2241
   setKeyBinding(SWT.ARROW_LEFT | SWT.MOD1, ST.WORD_PREVIOUS);
2232
	setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD1, ST.WORD_NEXT);
2242
   setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD1, ST.WORD_NEXT);
2233
	setKeyBinding(SWT.HOME | SWT.MOD1, ST.TEXT_START);	
2243
   setKeyBinding(SWT.HOME | SWT.MOD1, ST.TEXT_START);
2234
	setKeyBinding(SWT.END | SWT.MOD1, ST.TEXT_END);
2244
   setKeyBinding(SWT.END | SWT.MOD1, ST.TEXT_END);
2235
	setKeyBinding(SWT.PAGE_UP | SWT.MOD1, ST.WINDOW_START);
2245
   setKeyBinding(SWT.PAGE_UP | SWT.MOD1, ST.WINDOW_START);
2236
	setKeyBinding(SWT.PAGE_DOWN | SWT.MOD1, ST.WINDOW_END);
2246
   setKeyBinding(SWT.PAGE_DOWN | SWT.MOD1, ST.WINDOW_END);
2237
	// Selection
2247
   // Selection
2238
	setKeyBinding(SWT.ARROW_UP | SWT.MOD2, ST.SELECT_LINE_UP);	
2248
   setKeyBinding(SWT.ARROW_UP | SWT.MOD2, ST.SELECT_LINE_UP);
2239
	setKeyBinding(SWT.ARROW_DOWN | SWT.MOD2, ST.SELECT_LINE_DOWN);
2249
   setKeyBinding(SWT.ARROW_DOWN | SWT.MOD2, ST.SELECT_LINE_DOWN);
2240
	setKeyBinding(SWT.HOME | SWT.MOD2, ST.SELECT_LINE_START);
2250
   setKeyBinding(SWT.HOME | SWT.MOD2, ST.SELECT_LINE_START);
2241
	setKeyBinding(SWT.END | SWT.MOD2, ST.SELECT_LINE_END);
2251
   setKeyBinding(SWT.END | SWT.MOD2, ST.SELECT_LINE_END);
2242
	setKeyBinding(SWT.ARROW_LEFT | SWT.MOD2, ST.SELECT_COLUMN_PREVIOUS);
2252
   setKeyBinding(SWT.ARROW_LEFT | SWT.MOD2, ST.SELECT_COLUMN_PREVIOUS);
2243
	setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD2, ST.SELECT_COLUMN_NEXT);
2253
   setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD2, ST.SELECT_COLUMN_NEXT);
2244
	setKeyBinding(SWT.PAGE_UP | SWT.MOD2, ST.SELECT_PAGE_UP);
2254
   setKeyBinding(SWT.PAGE_UP | SWT.MOD2, ST.SELECT_PAGE_UP);
2245
	setKeyBinding(SWT.PAGE_DOWN | SWT.MOD2, ST.SELECT_PAGE_DOWN);
2255
   setKeyBinding(SWT.PAGE_DOWN | SWT.MOD2, ST.SELECT_PAGE_DOWN);
2246
	setKeyBinding(SWT.ARROW_LEFT | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_PREVIOUS);
2256
   setKeyBinding(SWT.ARROW_LEFT | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_PREVIOUS);
2247
	setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_NEXT);
2257
   setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_NEXT);
2248
	setKeyBinding(SWT.HOME | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_START);	
2258
   setKeyBinding(SWT.HOME | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_START);
2249
	setKeyBinding(SWT.END | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_END);
2259
   setKeyBinding(SWT.END | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_END);
2250
	setKeyBinding(SWT.PAGE_UP | SWT.MOD1 | SWT.MOD2, ST.SELECT_WINDOW_START);
2260
   setKeyBinding(SWT.PAGE_UP | SWT.MOD1 | SWT.MOD2, ST.SELECT_WINDOW_START);
2251
	setKeyBinding(SWT.PAGE_DOWN | SWT.MOD1 | SWT.MOD2, ST.SELECT_WINDOW_END);
2261
   setKeyBinding(SWT.PAGE_DOWN | SWT.MOD1 | SWT.MOD2, ST.SELECT_WINDOW_END);
2252
	
2262
2253
	// Modification
2263
   // Modification
2254
	// Cut, Copy, Paste
2264
   // Cut, Copy, Paste
2255
	setKeyBinding('X' | SWT.MOD1, ST.CUT);
2265
   setKeyBinding('X' | SWT.MOD1, ST.CUT);
2256
	setKeyBinding('C' | SWT.MOD1, ST.COPY);
2266
   setKeyBinding('C' | SWT.MOD1, ST.COPY);
2257
	setKeyBinding('V' | SWT.MOD1, ST.PASTE);
2267
   setKeyBinding('V' | SWT.MOD1, ST.PASTE);
2258
	// Cut, Copy, Paste Wordstar style
2268
   // Cut, Copy, Paste Wordstar style
2259
	setKeyBinding(SWT.DEL | SWT.MOD2, ST.CUT);
2269
   setKeyBinding(SWT.DEL | SWT.MOD2, ST.CUT);
2260
	setKeyBinding(SWT.INSERT | SWT.MOD1, ST.COPY);
2270
   setKeyBinding(SWT.INSERT | SWT.MOD1, ST.COPY);
2261
	setKeyBinding(SWT.INSERT | SWT.MOD2, ST.PASTE);
2271
   setKeyBinding(SWT.INSERT | SWT.MOD2, ST.PASTE);
2262
	setKeyBinding(SWT.BS | SWT.MOD2, ST.DELETE_PREVIOUS);
2272
   setKeyBinding(SWT.BS | SWT.MOD2, ST.DELETE_PREVIOUS);
2263
	
2273
2264
	setKeyBinding(SWT.BS, ST.DELETE_PREVIOUS);
2274
   setKeyBinding(SWT.BS, ST.DELETE_PREVIOUS);
2265
	setKeyBinding(SWT.DEL, ST.DELETE_NEXT);
2275
   setKeyBinding(SWT.DEL, ST.DELETE_NEXT);
2266
	
2276
2267
	// Miscellaneous
2277
   // Miscellaneous
2268
	setKeyBinding(SWT.INSERT, ST.TOGGLE_OVERWRITE);
2278
   setKeyBinding(SWT.INSERT, ST.TOGGLE_OVERWRITE);
2269
}
2279
}
2270
/**
2280
/**
2271
 * Create the bitmaps to use for the caret in bidi mode.  This
2281
 * Create the bitmaps to use for the caret in bidi mode.  This
Lines 2273-2309 Link Here
2273
 * font changes (the caret bitmap height needs to match font height).
2283
 * font changes (the caret bitmap height needs to match font height).
2274
 */
2284
 */
2275
void createCaretBitmaps() {
2285
void createCaretBitmaps() {
2276
	int caretWidth = BIDI_CARET_WIDTH;
2286
   int caretWidth = BIDI_CARET_WIDTH;
2277
	
2287
2278
	Display display = getDisplay();	
2288
   Display display = getDisplay();
2279
	if (caretPalette == null) {
2289
   if (caretPalette == null) {
2280
		caretPalette = new PaletteData(new RGB[] {new RGB (0,0,0), new RGB (255,255,255)});
2290
      caretPalette = new PaletteData(new RGB[] {new RGB (0,0,0), new RGB (255,255,255)});
2281
	}	
2291
   }
2282
	if (leftCaretBitmap != null) {
2292
   if (leftCaretBitmap != null) {
2283
		leftCaretBitmap.dispose();
2293
      leftCaretBitmap.dispose();
2284
	}
2294
   }
2285
	ImageData imageData = new ImageData(caretWidth, lineHeight, 1, caretPalette);
2295
   ImageData imageData = new ImageData(caretWidth, lineHeight, 1, caretPalette);
2286
	leftCaretBitmap = new Image(display, imageData);
2296
   leftCaretBitmap = new Image(display, imageData);
2287
	GC gc = new GC (leftCaretBitmap);
2297
   GC gc = new GC (leftCaretBitmap);
2288
	gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
2298
   gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
2289
	gc.drawLine(0,0,0,lineHeight);
2299
   gc.drawLine(0,0,0,lineHeight);
2290
	gc.drawLine(0,0,caretWidth-1,0);
2300
   gc.drawLine(0,0,caretWidth-1,0);
2291
	gc.drawLine(0,1,1,1);
2301
   gc.drawLine(0,1,1,1);
2292
	gc.dispose();	
2302
   gc.dispose();
2293
	
2303
2294
	if (rightCaretBitmap != null) {
2304
   if (rightCaretBitmap != null) {
2295
		rightCaretBitmap.dispose();
2305
      rightCaretBitmap.dispose();
2296
	}
2306
   }
2297
	rightCaretBitmap = new Image(display, imageData);
2307
   rightCaretBitmap = new Image(display, imageData);
2298
	gc = new GC (rightCaretBitmap);
2308
   gc = new GC (rightCaretBitmap);
2299
	gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
2309
   gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
2300
	gc.drawLine(caretWidth-1,0,caretWidth-1,lineHeight);
2310
   gc.drawLine(caretWidth-1,0,caretWidth-1,lineHeight);
2301
	gc.drawLine(0,0,caretWidth-1,0);
2311
   gc.drawLine(0,0,caretWidth-1,0);
2302
	gc.drawLine(caretWidth-1,1,1,1);
2312
   gc.drawLine(caretWidth-1,1,1,1);
2303
	gc.dispose();	
2313
   gc.dispose();
2304
}
2314
}
2305
/**
2315
/**
2306
 * Moves the selected text to the clipboard.  The text will be put in the 
2316
 * Moves the selected text to the clipboard.  The text will be put in the
2307
 * clipboard in plain text format and RTF format.
2317
 * clipboard in plain text format and RTF format.
2308
 * <p>
2318
 * <p>
2309
 *
2319
 *
Lines 2313-2876 Link Here
2313
 * </ul>
2323
 * </ul>
2314
 */
2324
 */
2315
public void cut(){
2325
public void cut(){
2316
	checkWidget();
2326
   checkWidget();
2317
	int length = selection.y - selection.x;
2327
   int length = selection.y - selection.x;
2318
	
2328
2319
	if (length > 0) {
2329
   if (length > 0) {
2320
		try {
2330
      try {
2321
			setClipboardContent(selection.x, length);
2331
         setClipboardContent(selection.x, length);
2322
		}
2332
      }
2323
		catch (SWTError error) {
2333
      catch (SWTError error) {
2324
			// Copy to clipboard failed. This happens when another application 
2334
         // Copy to clipboard failed. This happens when another application
2325
			// is accessing the clipboard while we copy. Ignore the error.
2335
         // is accessing the clipboard while we copy. Ignore the error.
2326
			// Fixes 1GDQAVN
2336
         // Fixes 1GDQAVN
2327
			// Rethrow all other errors. Fixes bug 17578.
2337
         // Rethrow all other errors. Fixes bug 17578.
2328
			if (error.code != DND.ERROR_CANNOT_SET_CLIPBOARD) {
2338
         if (error.code != DND.ERROR_CANNOT_SET_CLIPBOARD) {
2329
				throw error;
2339
            throw error;
2330
			}
2340
         }
2331
			// Abort cut operation if copy to clipboard fails.
2341
         // Abort cut operation if copy to clipboard fails.
2332
			// Fixes bug 21030.
2342
         // Fixes bug 21030.
2333
			return;
2343
         return;
2334
		}
2344
      }
2335
		doDelete();
2345
      doDelete();
2336
	}
2346
   }
2337
}
2347
}
2338
/** 
2348
/**
2339
 * A mouse move event has occurred.  See if we should start autoscrolling.  If
2349
 * A mouse move event has occurred.  See if we should start autoscrolling.  If
2340
 * the move position is outside of the client area, initiate autoscrolling.  
2350
 * the move position is outside of the client area, initiate autoscrolling.
2341
 * Otherwise, we've moved back into the widget so end autoscrolling.
2351
 * Otherwise, we've moved back into the widget so end autoscrolling.
2342
 */
2352
 */
2343
void doAutoScroll(Event event) {
2353
void doAutoScroll(Event event) {
2344
	Rectangle area = getClientArea();		
2354
   Rectangle area = getClientArea();
2345
	
2355
2346
	if (event.y > area.height) {
2356
   if (event.y > area.height) {
2347
		doAutoScroll(SWT.DOWN);
2357
      doAutoScroll(SWT.DOWN);
2348
	}
2358
   }
2349
	else 
2359
   else
2350
	if (event.y < 0) {
2360
   if (event.y < 0) {
2351
		doAutoScroll(SWT.UP);
2361
      doAutoScroll(SWT.UP);
2352
	}
2362
   }
2353
	else 
2363
   else
2354
	if (event.x < leftMargin && wordWrap == false) {
2364
   if (event.x < leftMargin && wordWrap == false) {
2355
		doAutoScroll(SWT.LEFT);
2365
      doAutoScroll(SWT.LEFT);
2356
	}
2366
   }
2357
	else 
2367
   else
2358
	if (event.x > area.width - leftMargin - rightMargin && wordWrap == false) {
2368
   if (event.x > area.width - leftMargin - rightMargin && wordWrap == false) {
2359
		doAutoScroll(SWT.RIGHT);
2369
      doAutoScroll(SWT.RIGHT);
2360
	}
2370
   }
2361
	else {
2371
   else {
2362
		endAutoScroll();
2372
      endAutoScroll();
2363
	}
2373
   }
2364
}
2374
}
2365
/** 
2375
/**
2366
 * Initiates autoscrolling.
2376
 * Initiates autoscrolling.
2367
 * <p>
2377
 * <p>
2368
 *
2378
 *
2369
 * @param direction SWT.UP, SWT.DOWN, SWT.RIGHT, SWT.LEFT
2379
 * @param direction SWT.UP, SWT.DOWN, SWT.RIGHT, SWT.LEFT
2370
 */
2380
 */
2371
void doAutoScroll(int direction) {
2381
void doAutoScroll(int direction) {
2372
	Runnable timer = null;
2382
   Runnable timer = null;
2373
	final int TIMER_INTERVAL = 5;
2383
   final int TIMER_INTERVAL = 5;
2374
	
2384
2375
	// If we're already autoscrolling in the given direction do nothing
2385
   // If we're already autoscrolling in the given direction do nothing
2376
	if (autoScrollDirection == direction) {
2386
   if (autoScrollDirection == direction) {
2377
		return;
2387
      return;
2378
	}
2388
   }
2379
	
2389
2380
	final Display display = getDisplay();
2390
   final Display display = getDisplay();
2381
	// Set a timer that will simulate the user pressing and holding
2391
   // Set a timer that will simulate the user pressing and holding
2382
	// down a cursor key (i.e., arrowUp, arrowDown).
2392
   // down a cursor key (i.e., arrowUp, arrowDown).
2383
	if (direction == SWT.UP) {
2393
   if (direction == SWT.UP) {
2384
		timer = new Runnable() {
2394
      timer = new Runnable() {
2385
			public void run() {
2395
         public void run() {
2386
				if (autoScrollDirection == SWT.UP) {
2396
            if (autoScrollDirection == SWT.UP) {
2387
					doSelectionLineUp();
2397
               doSelectionLineUp();
2388
					display.timerExec(TIMER_INTERVAL, this);
2398
               display.timerExec(TIMER_INTERVAL, this);
2389
				}
2399
            }
2390
			}
2400
         }
2391
		};
2401
      };
2392
	} else if (direction == SWT.DOWN) {
2402
   } else if (direction == SWT.DOWN) {
2393
		timer = new Runnable() {
2403
      timer = new Runnable() {
2394
			public void run() {
2404
         public void run() {
2395
				if (autoScrollDirection == SWT.DOWN) {
2405
            if (autoScrollDirection == SWT.DOWN) {
2396
					doSelectionLineDown();
2406
               doSelectionLineDown();
2397
					display.timerExec(TIMER_INTERVAL, this);
2407
               display.timerExec(TIMER_INTERVAL, this);
2398
				}
2408
            }
2399
			}
2409
         }
2400
		};
2410
      };
2401
	} else if (direction == SWT.RIGHT) {
2411
   } else if (direction == SWT.RIGHT) {
2402
		timer = new Runnable() {
2412
      timer = new Runnable() {
2403
			public void run() {
2413
         public void run() {
2404
				if (autoScrollDirection == SWT.RIGHT) {
2414
            if (autoScrollDirection == SWT.RIGHT) {
2405
					doColumnRight();
2415
               doColumnRight();
2406
					setMouseWordSelectionAnchor();
2416
               setMouseWordSelectionAnchor();
2407
					doSelection(SWT.RIGHT);
2417
               doSelection(SWT.RIGHT);
2408
					display.timerExec(TIMER_INTERVAL, this);
2418
               display.timerExec(TIMER_INTERVAL, this);
2409
				}
2419
            }
2410
			}
2420
         }
2411
		};
2421
      };
2412
	} else if (direction == SWT.LEFT) {
2422
   } else if (direction == SWT.LEFT) {
2413
		timer = new Runnable() {
2423
      timer = new Runnable() {
2414
			public void run() {
2424
         public void run() {
2415
				if (autoScrollDirection == SWT.LEFT) {
2425
            if (autoScrollDirection == SWT.LEFT) {
2416
					doColumnLeft();
2426
               doColumnLeft();
2417
					setMouseWordSelectionAnchor();
2427
               setMouseWordSelectionAnchor();
2418
					doSelection(SWT.LEFT);
2428
               doSelection(SWT.LEFT);
2419
					display.timerExec(TIMER_INTERVAL, this);
2429
               display.timerExec(TIMER_INTERVAL, this);
2420
				}
2430
            }
2421
			}
2431
         }
2422
		};
2432
      };
2423
	} 	
2433
   }
2424
	if (timer != null) {
2434
   if (timer != null) {
2425
		autoScrollDirection = direction;
2435
      autoScrollDirection = direction;
2426
		display.timerExec(TIMER_INTERVAL, timer);
2436
      display.timerExec(TIMER_INTERVAL, timer);
2427
	}
2437
   }
2428
}
2438
}
2429
/**
2439
/**
2430
 * Deletes the previous character. Delete the selected text if any.
2440
 * Deletes the previous character. Delete the selected text if any.
2431
 * Move the caret in front of the deleted text.
2441
 * Move the caret in front of the deleted text.
2432
 */
2442
 */
2433
void doBackspace() {
2443
void doBackspace() {
2434
	Event event = new Event();
2444
   Event event = new Event();
2435
	event.text = "";
2445
   event.text = "";
2436
	if (selection.x != selection.y) {
2446
   if (selection.x != selection.y) {
2437
		event.start = selection.x;
2447
      event.start = selection.x;
2438
		event.end = selection.y;
2448
      event.end = selection.y;
2439
		sendKeyEvent(event);
2449
      sendKeyEvent(event);
2440
	}
2450
   }
2441
	else
2451
   else
2442
	if (caretOffset > 0) {
2452
   if (caretOffset > 0) {
2443
		int line = content.getLineAtOffset(caretOffset);
2453
      int line = content.getLineAtOffset(caretOffset);
2444
		int lineOffset = content.getOffsetAtLine(line);			
2454
      int lineOffset = content.getOffsetAtLine(line);
2445
	
2455
2446
		if (caretOffset == lineOffset) {
2456
      if (caretOffset == lineOffset) {
2447
			lineOffset = content.getOffsetAtLine(line - 1);
2457
         lineOffset = content.getOffsetAtLine(line - 1);
2448
			event.start = lineOffset + content.getLine(line - 1).length();
2458
         event.start = lineOffset + content.getLine(line - 1).length();
2449
			event.end = caretOffset;
2459
         event.end = caretOffset;
2450
		}
2460
      }
2451
		else {
2461
      else {
2452
			event.start = caretOffset - 1;
2462
         event.start = caretOffset - 1;
2453
			event.end = caretOffset;
2463
         event.end = caretOffset;
2454
		}
2464
      }
2455
		sendKeyEvent(event);
2465
      sendKeyEvent(event);
2456
	}
2466
   }
2457
}
2467
}
2458
/**
2468
/**
2459
 * Moves the caret one character to the left.  Do not go to the previous line.
2469
 * Moves the caret one character to the left.  Do not go to the previous line.
2460
 * When in a bidi locale and at a R2L character the caret is moved to the 
2470
 * When in a bidi locale and at a R2L character the caret is moved to the
2461
 * beginning of the R2L segment (visually right) and then one character to the 
2471
 * beginning of the R2L segment (visually right) and then one character to the
2462
 * left (visually left because it's now in a L2R segment).
2472
 * left (visually left because it's now in a L2R segment).
2463
 */
2473
 */
2464
void doColumnLeft() {
2474
void doColumnLeft() {
2465
	int line = content.getLineAtOffset(caretOffset);
2475
   int line = content.getLineAtOffset(caretOffset);
2466
	int lineOffset = content.getOffsetAtLine(line);	
2476
   int lineOffset = content.getOffsetAtLine(line);
2467
	int offsetInLine = caretOffset - lineOffset;
2477
   int offsetInLine = caretOffset - lineOffset;
2468
	
2478
2469
	if (isBidi()) {
2479
   if (isBidi()) {
2470
		String lineText = content.getLine(line);
2480
      String lineText = content.getLine(line);
2471
		int lineLength = lineText.length();
2481
      int lineLength = lineText.length();
2472
		GC gc = getGC();
2482
      GC gc = getGC();
2473
		StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc);
2483
      StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc);
2474
		
2484
2475
		if (horizontalScrollOffset > 0 || offsetInLine > 0) {
2485
      if (horizontalScrollOffset > 0 || offsetInLine > 0) {
2476
			if (offsetInLine < lineLength && bidi.isRightToLeft(offsetInLine)) {
2486
         if (offsetInLine < lineLength && bidi.isRightToLeft(offsetInLine)) {
2477
				// advance caret logically if in R2L segment (move visually left)
2487
            // advance caret logically if in R2L segment (move visually left)
2478
				caretOffset++;
2488
            caretOffset++;
2479
				doSelection(SWT.RIGHT);
2489
            doSelection(SWT.RIGHT);
2480
				if (caretOffset - lineOffset == lineLength) {
2490
            if (caretOffset - lineOffset == lineLength) {
2481
					// if the line end is reached in a R2L segment, make the 
2491
               // if the line end is reached in a R2L segment, make the
2482
					// caret position (visual left border) visible before 
2492
               // caret position (visual left border) visible before
2483
					// jumping to segment start
2493
               // jumping to segment start
2484
					showCaret();
2494
               showCaret();
2485
				}
2495
            }
2486
				// end of R2L segment reached (visual left side)?
2496
            // end of R2L segment reached (visual left side)?
2487
				if (bidi.isRightToLeft(caretOffset - lineOffset) == false) {
2497
            if (bidi.isRightToLeft(caretOffset - lineOffset) == false) {
2488
					if (bidi.getTextPosition(caretOffset - lineOffset) < horizontalScrollOffset) {
2498
               if (bidi.getTextPosition(caretOffset - lineOffset) < horizontalScrollOffset) {
2489
						// make beginning of R2L segment visible before going 
2499
                  // make beginning of R2L segment visible before going
2490
						// left, to L2R segment important if R2L segment ends 
2500
                  // left, to L2R segment important if R2L segment ends
2491
						// at visual left in order to scroll all the way to the
2501
                  // at visual left in order to scroll all the way to the
2492
						// left. Fixes 1GKM3XS
2502
                  // left. Fixes 1GKM3XS
2493
						showCaret();
2503
                  showCaret();
2494
					}
2504
               }
2495
					// go to beginning of R2L segment (visually end of next L2R
2505
               // go to beginning of R2L segment (visually end of next L2R
2496
					// segment)/beginning of line
2506
               // segment)/beginning of line
2497
					caretOffset--;
2507
               caretOffset--;
2498
					while (caretOffset - lineOffset > 0 &&
2508
               while (caretOffset - lineOffset > 0 &&
2499
						   bidi.isRightToLeft(caretOffset - lineOffset)) {
2509
                     bidi.isRightToLeft(caretOffset - lineOffset)) {
2500
						caretOffset--;
2510
                  caretOffset--;
2501
					}
2511
               }
2502
				}
2512
            }
2503
			}
2513
         }
2504
			else
2514
         else
2505
			if (offsetInLine == lineLength && 
2515
         if (offsetInLine == lineLength &&
2506
				bidi.getTextPosition(lineLength) != XINSET) {
2516
            bidi.getTextPosition(lineLength) != XINSET) {
2507
				// at logical line end in R2L segment but there's more text (a
2517
            // at logical line end in R2L segment but there's more text (a
2508
				// L2R segment) go to end of R2L segment (visually left of next
2518
            // L2R segment) go to end of R2L segment (visually left of next
2509
				// L2R segment)/end of line
2519
            // L2R segment)/end of line
2510
				caretOffset--;
2520
            caretOffset--;
2511
				while (caretOffset - lineOffset > 0 && 
2521
            while (caretOffset - lineOffset > 0 &&
2512
					   bidi.isRightToLeft(caretOffset - lineOffset)) {
2522
                  bidi.isRightToLeft(caretOffset - lineOffset)) {
2513
					caretOffset--;
2523
               caretOffset--;
2514
				}
2524
            }
2515
			}
2525
         }
2516
			else
2526
         else
2517
			if (offsetInLine > 0 && bidi.isRightToLeft(offsetInLine) == false) {
2527
         if (offsetInLine > 0 && bidi.isRightToLeft(offsetInLine) == false) {
2518
				// decrease caret logically if in L2R segment (move visually left)
2528
            // decrease caret logically if in L2R segment (move visually left)
2519
				caretOffset--;
2529
            caretOffset--;
2520
				doSelection(SWT.LEFT);
2530
            doSelection(SWT.LEFT);
2521
				// end of L2R segment reached (visual left side of preceeding R2L 
2531
            // end of L2R segment reached (visual left side of preceeding R2L
2522
				// segment)?
2532
            // segment)?
2523
				if (caretOffset - lineOffset > 0 && 
2533
            if (caretOffset - lineOffset > 0 &&
2524
					bidi.isRightToLeft(caretOffset - lineOffset - 1)) {
2534
               bidi.isRightToLeft(caretOffset - lineOffset - 1)) {
2525
					// go to beginning of R2L segment (visually start of next L2R
2535
               // go to beginning of R2L segment (visually start of next L2R
2526
					// segment)/beginning of line
2536
               // segment)/beginning of line
2527
					caretOffset--;
2537
               caretOffset--;
2528
					while (caretOffset - lineOffset > 0 &&
2538
               while (caretOffset - lineOffset > 0 &&
2529
						   bidi.isRightToLeft(caretOffset - lineOffset - 1)) {
2539
                     bidi.isRightToLeft(caretOffset - lineOffset - 1)) {
2530
						caretOffset--;
2540
                  caretOffset--;
2531
					}
2541
               }
2532
				}
2542
            }
2533
			}
2543
         }
2534
			// if new caret position is to the left of the client area
2544
         // if new caret position is to the left of the client area
2535
			if (bidi.getTextPosition(caretOffset - lineOffset) < horizontalScrollOffset) {
2545
         if (bidi.getTextPosition(caretOffset - lineOffset) < horizontalScrollOffset) {
2536
				// scroll to the caret position
2546
            // scroll to the caret position
2537
				showCaret();
2547
            showCaret();
2538
			}
2548
         }
2539
			else {
2549
         else {
2540
				// otherwise just update caret position without scrolling it into view
2550
            // otherwise just update caret position without scrolling it into view
2541
				setBidiCaretLocation(null);
2551
            setBidiCaretLocation(null);
2542
				setBidiKeyboardLanguage();
2552
            setBidiKeyboardLanguage();
2543
			}
2553
         }
2544
			// Beginning of line reached (auto scroll finished) but not scrolled 
2554
         // Beginning of line reached (auto scroll finished) but not scrolled
2545
			// completely to the left? Fixes 1GKM193
2555
         // completely to the left? Fixes 1GKM193
2546
			if (caretOffset - lineOffset == 0 && horizontalScrollOffset > 0 && 
2556
         if (caretOffset - lineOffset == 0 && horizontalScrollOffset > 0 &&
2547
				horizontalScrollOffset <= XINSET) {
2557
            horizontalScrollOffset <= XINSET) {
2548
				scrollHorizontalBar(-horizontalScrollOffset);
2558
            scrollHorizontalBar(-horizontalScrollOffset);
2549
			}
2559
         }
2550
		}
2560
      }
2551
		gc.dispose();
2561
      gc.dispose();
2552
	}
2562
   }
2553
	else
2563
   else
2554
	if (offsetInLine > 0) {
2564
   if (offsetInLine > 0) {
2555
		caretOffset--;
2565
      caretOffset--;
2556
		showCaret();
2566
      showCaret();
2557
	}
2567
   }
2558
}
2568
}
2559
/**
2569
/**
2560
 * Moves the caret one character to the right.  Do not go to the next line.
2570
 * Moves the caret one character to the right.  Do not go to the next line.
2561
 * When in a bidi locale and at a R2L character the caret is moved to the 
2571
 * When in a bidi locale and at a R2L character the caret is moved to the
2562
 * end of the R2L segment (visually left) and then one character to the 
2572
 * end of the R2L segment (visually left) and then one character to the
2563
 * right (visually right because it's now in a L2R segment).
2573
 * right (visually right because it's now in a L2R segment).
2564
 */
2574
 */
2565
void doColumnRight() {
2575
void doColumnRight() {
2566
	int line = content.getLineAtOffset(caretOffset);
2576
   int line = content.getLineAtOffset(caretOffset);
2567
	int lineOffset = content.getOffsetAtLine(line);	
2577
   int lineOffset = content.getOffsetAtLine(line);
2568
	int offsetInLine = caretOffset - lineOffset;
2578
   int offsetInLine = caretOffset - lineOffset;
2569
	String lineText = content.getLine(line);
2579
   String lineText = content.getLine(line);
2570
	int lineLength = lineText.length();
2580
   int lineLength = lineText.length();
2571
	
2581
2572
	if (isBidi()) {
2582
   if (isBidi()) {
2573
		GC gc = getGC();
2583
      GC gc = getGC();
2574
		StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc);
2584
      StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc);
2575
		if (bidi.getTextWidth() + leftMargin > horizontalScrollOffset + getClientArea().width || 
2585
      if (bidi.getTextWidth() + leftMargin > horizontalScrollOffset + getClientArea().width ||
2576
			offsetInLine < lineLength) {
2586
         offsetInLine < lineLength) {
2577
			if (bidi.isRightToLeft(offsetInLine) == false && 
2587
         if (bidi.isRightToLeft(offsetInLine) == false &&
2578
				offsetInLine < lineLength) {
2588
            offsetInLine < lineLength) {
2579
				// advance caret logically if in L2R segment (move visually right)
2589
            // advance caret logically if in L2R segment (move visually right)
2580
				caretOffset++;
2590
            caretOffset++;
2581
				doSelection(SWT.RIGHT);
2591
            doSelection(SWT.RIGHT);
2582
				// end of L2R segment reached (visual right side)?
2592
            // end of L2R segment reached (visual right side)?
2583
				if (bidi.isRightToLeft(caretOffset - lineOffset)) {
2593
            if (bidi.isRightToLeft(caretOffset - lineOffset)) {
2584
					// go to end of R2L segment (visually left of next R2L segment)/ 
2594
               // go to end of R2L segment (visually left of next R2L segment)/
2585
					// end of line
2595
               // end of line
2586
					caretOffset++;
2596
               caretOffset++;
2587
					while (caretOffset < lineOffset + lineLength &&
2597
               while (caretOffset < lineOffset + lineLength &&
2588
						   bidi.isRightToLeft(caretOffset - lineOffset)) {
2598
                     bidi.isRightToLeft(caretOffset - lineOffset)) {
2589
						caretOffset++;
2599
                  caretOffset++;
2590
					}
2600
               }
2591
				}
2601
            }
2592
			}
2602
         }
2593
			else
2603
         else
2594
			if (offsetInLine > 0 && 
2604
         if (offsetInLine > 0 &&
2595
				(bidi.isRightToLeft(offsetInLine) || 
2605
            (bidi.isRightToLeft(offsetInLine) ||
2596
				bidi.getTextWidth() + leftMargin > horizontalScrollOffset + getClientArea().width ||
2606
            bidi.getTextWidth() + leftMargin > horizontalScrollOffset + getClientArea().width ||
2597
				offsetInLine < lineLength)) {
2607
            offsetInLine < lineLength)) {
2598
				// advance caret visually if in R2L segment or logically at line end 
2608
            // advance caret visually if in R2L segment or logically at line end
2599
				// but right end of line is not fully visible yet
2609
            // but right end of line is not fully visible yet
2600
				caretOffset--;
2610
            caretOffset--;
2601
				doSelection(SWT.LEFT);
2611
            doSelection(SWT.LEFT);
2602
				offsetInLine = caretOffset - lineOffset;
2612
            offsetInLine = caretOffset - lineOffset;
2603
				// end of R2L segment reached (visual right side)?
2613
            // end of R2L segment reached (visual right side)?
2604
				if (offsetInLine > 0 && bidi.isRightToLeft(offsetInLine) == false) {
2614
            if (offsetInLine > 0 && bidi.isRightToLeft(offsetInLine) == false) {
2605
					// go to end of R2L segment (visually left of next L2R segment)/ 
2615
               // go to end of R2L segment (visually left of next L2R segment)/
2606
					// end of line
2616
               // end of line
2607
					caretOffset++;
2617
               caretOffset++;
2608
					while (caretOffset < lineOffset + lineLength &&
2618
               while (caretOffset < lineOffset + lineLength &&
2609
						   bidi.isRightToLeft(caretOffset - lineOffset)) {
2619
                     bidi.isRightToLeft(caretOffset - lineOffset)) {
2610
						caretOffset++;
2620
                  caretOffset++;
2611
					}
2621
               }
2612
				}
2622
            }
2613
			}
2623
         }
2614
			else
2624
         else
2615
			if (offsetInLine == 0 && bidi.getTextPosition(0) != bidi.getTextWidth()) {
2625
         if (offsetInLine == 0 && bidi.getTextPosition(0) != bidi.getTextWidth()) {
2616
				// at logical line start in R2L segment but there's more text (a L2R
2626
            // at logical line start in R2L segment but there's more text (a L2R
2617
				// segment) go to end of R2L segment (visually left of next L2R
2627
            // segment) go to end of R2L segment (visually left of next L2R
2618
				// segment)/end of line
2628
            // segment)/end of line
2619
				caretOffset++;
2629
            caretOffset++;
2620
				while (caretOffset < lineOffset + lineLength &&
2630
            while (caretOffset < lineOffset + lineLength &&
2621
					   bidi.isRightToLeft(caretOffset - lineOffset - 1)) {
2631
                  bidi.isRightToLeft(caretOffset - lineOffset - 1)) {
2622
					caretOffset++;
2632
               caretOffset++;
2623
				}
2633
            }
2624
			}
2634
         }
2625
			offsetInLine = caretOffset - lineOffset;
2635
         offsetInLine = caretOffset - lineOffset;
2626
			// if new caret position is to the right of the client area
2636
         // if new caret position is to the right of the client area
2627
			if (bidi.getTextPosition(offsetInLine) >= horizontalScrollOffset) {
2637
         if (bidi.getTextPosition(offsetInLine) >= horizontalScrollOffset) {
2628
				// scroll to the caret position
2638
            // scroll to the caret position
2629
				showCaret();
2639
            showCaret();
2630
			}
2640
         }
2631
			else {
2641
         else {
2632
				// otherwise just update caret position without scrolling it into view
2642
            // otherwise just update caret position without scrolling it into view
2633
				setBidiCaretLocation(null);
2643
            setBidiCaretLocation(null);
2634
				setBidiKeyboardLanguage();
2644
            setBidiKeyboardLanguage();
2635
			}
2645
         }
2636
			if (offsetInLine > 0 && offsetInLine < lineLength - 1) {
2646
         if (offsetInLine > 0 && offsetInLine < lineLength - 1) {
2637
				int clientAreaEnd = horizontalScrollOffset + getClientArea().width;
2647
            int clientAreaEnd = horizontalScrollOffset + getClientArea().width;
2638
				boolean directionChange = bidi.isRightToLeft(offsetInLine - 1) == false && bidi.isRightToLeft(offsetInLine);
2648
            boolean directionChange = bidi.isRightToLeft(offsetInLine - 1) == false && bidi.isRightToLeft(offsetInLine);
2639
				int textWidth = bidi.getTextWidth() + leftMargin;
2649
            int textWidth = bidi.getTextWidth() + leftMargin;
2640
				// between L2R and R2L segment and second character of R2L segment is
2650
            // between L2R and R2L segment and second character of R2L segment is
2641
				// left of right border and logical line end is left of right border
2651
            // left of right border and logical line end is left of right border
2642
				// but visual line end is not left of right border
2652
            // but visual line end is not left of right border
2643
				if (directionChange && 
2653
            if (directionChange &&
2644
					bidi.isRightToLeft(offsetInLine + 1) &&
2654
               bidi.isRightToLeft(offsetInLine + 1) &&
2645
					bidi.getTextPosition(offsetInLine + 1) + leftMargin < clientAreaEnd && 
2655
               bidi.getTextPosition(offsetInLine + 1) + leftMargin < clientAreaEnd &&
2646
					bidi.getTextPosition(lineLength) + leftMargin < clientAreaEnd && textWidth > clientAreaEnd) {
2656
               bidi.getTextPosition(lineLength) + leftMargin < clientAreaEnd && textWidth > clientAreaEnd) {
2647
					// make visual line end visible
2657
               // make visual line end visible
2648
					scrollHorizontalBar(textWidth - clientAreaEnd);
2658
               scrollHorizontalBar(textWidth - clientAreaEnd);
2649
				}
2659
            }
2650
			}
2660
         }
2651
		}
2661
      }
2652
		gc.dispose();
2662
      gc.dispose();
2653
	}
2663
   }
2654
	else
2664
   else
2655
	if (offsetInLine < lineLength) {
2665
   if (offsetInLine < lineLength) {
2656
		caretOffset++;
2666
      caretOffset++;
2657
		showCaret();
2667
      showCaret();
2658
	}
2668
   }
2659
}
2669
}
2660
/**
2670
/**
2661
 * Replaces the selection with the character or insert the character at the 
2671
 * Replaces the selection with the character or insert the character at the
2662
 * current caret position if no selection exists.
2672
 * current caret position if no selection exists.
2663
 * If a carriage return was typed replace it with the line break character 
2673
 * If a carriage return was typed replace it with the line break character
2664
 * used by the widget on this platform.
2674
 * used by the widget on this platform.
2665
 * <p>
2675
 * <p>
2666
 *
2676
 *
2667
 * @param key the character typed by the user
2677
 * @param key the character typed by the user
2668
 */
2678
 */
2669
void doContent(char key) {
2679
void doContent(char key) {
2670
	Event event;
2680
   Event event;
2671
	
2681
2672
	if (textLimit > 0 && 
2682
   if (textLimit > 0 &&
2673
		content.getCharCount() - (selection.y - selection.x) >= textLimit) {
2683
      content.getCharCount() - (selection.y - selection.x) >= textLimit) {
2674
		return;
2684
      return;
2675
	}	
2685
   }
2676
	event = new Event();
2686
   event = new Event();
2677
	event.start = selection.x;
2687
   event.start = selection.x;
2678
	event.end = selection.y;
2688
   event.end = selection.y;
2679
	// replace a CR line break with the widget line break
2689
   // replace a CR line break with the widget line break
2680
	// CR does not make sense on Windows since most (all?) applications
2690
   // CR does not make sense on Windows since most (all?) applications
2681
	// don't recognize CR as a line break.
2691
   // don't recognize CR as a line break.
2682
	if (key == SWT.CR || key == SWT.LF) {
2692
   if (key == SWT.CR || key == SWT.LF) {
2683
		if (isSingleLine() == false) {
2693
      if (isSingleLine() == false) {
2684
			event.text = getLineDelimiter();
2694
         event.text = getLineDelimiter();
2685
		}
2695
      }
2686
	}
2696
   }
2687
	// no selection and overwrite mode is on and the typed key is not a
2697
   // no selection and overwrite mode is on and the typed key is not a
2688
	// tab character (tabs are always inserted without overwriting)?
2698
   // tab character (tabs are always inserted without overwriting)?
2689
	else
2699
   else
2690
	if (selection.x == selection.y && overwrite == true && key != TAB) {
2700
   if (selection.x == selection.y && overwrite == true && key != TAB) {
2691
		int lineIndex = content.getLineAtOffset(event.end);
2701
      int lineIndex = content.getLineAtOffset(event.end);
2692
		int lineOffset = content.getOffsetAtLine(lineIndex);
2702
      int lineOffset = content.getOffsetAtLine(lineIndex);
2693
		String line = content.getLine(lineIndex);
2703
      String line = content.getLine(lineIndex);
2694
		// replace character at caret offset if the caret is not at the 
2704
      // replace character at caret offset if the caret is not at the
2695
		// end of the line
2705
      // end of the line
2696
		if (event.end < lineOffset + line.length()) {
2706
      if (event.end < lineOffset + line.length()) {
2697
			event.end++;
2707
         event.end++;
2698
		}
2708
      }
2699
		event.text = new String(new char[] {key});
2709
      event.text = new String(new char[] {key});
2700
	}
2710
   }
2701
	else {
2711
   else {
2702
		event.text = new String(new char[] {key});
2712
      event.text = new String(new char[] {key});
2703
	}
2713
   }
2704
	if (event.text != null) {
2714
   if (event.text != null) {
2705
		sendKeyEvent(event);
2715
      sendKeyEvent(event);
2706
	}
2716
   }
2707
}
2717
}
2708
/**
2718
/**
2709
 * Moves the caret after the last character of the widget content.
2719
 * Moves the caret after the last character of the widget content.
2710
 */
2720
 */
2711
void doContentEnd() {
2721
void doContentEnd() {
2712
	// place caret at end of first line if receiver is in single 
2722
   // place caret at end of first line if receiver is in single
2713
	// line mode. fixes 4820.
2723
   // line mode. fixes 4820.
2714
	if (isSingleLine()) {
2724
   if (isSingleLine()) {
2715
		doLineEnd();
2725
      doLineEnd();
2716
	}
2726
   }
2717
	else {
2727
   else {
2718
		int length = content.getCharCount();		
2728
      int length = content.getCharCount();
2719
		if (caretOffset < length) {
2729
      if (caretOffset < length) {
2720
			caretOffset = length;
2730
         caretOffset = length;
2721
			showCaret();
2731
         showCaret();
2722
		}
2732
      }
2723
	}
2733
   }
2724
}
2734
}
2725
/**
2735
/**
2726
 * Moves the caret in front of the first character of the widget content.
2736
 * Moves the caret in front of the first character of the widget content.
2727
 */
2737
 */
2728
void doContentStart() {
2738
void doContentStart() {
2729
	if (caretOffset > 0) {
2739
   if (caretOffset > 0) {
2730
		caretOffset = 0;
2740
      caretOffset = 0;
2731
		showCaret();
2741
      showCaret();
2732
	}
2742
   }
2733
}
2743
}
2734
/**
2744
/**
2735
 * Moves the caret to the start of the selection if a selection exists.
2745
 * Moves the caret to the start of the selection if a selection exists.
2736
 * Otherwise, if no selection exists move the cursor according to the 
2746
 * Otherwise, if no selection exists move the cursor according to the
2737
 * cursor selection rules.
2747
 * cursor selection rules.
2738
 * <p>
2748
 * <p>
2739
 *
2749
 *
2740
 * @see #doSelectionCursorPrevious
2750
 * @see #doSelectionCursorPrevious
2741
 */
2751
 */
2742
void doCursorPrevious() {
2752
void doCursorPrevious() {
2743
	if (selection.y - selection.x > 0) {
2753
   if (selection.y - selection.x > 0) {
2744
		int caretLine;
2754
      int caretLine;
2745
		
2755
2746
		caretOffset = selection.x;
2756
      caretOffset = selection.x;
2747
		caretLine = getCaretLine();
2757
      caretLine = getCaretLine();
2748
		showCaret(caretLine);
2758
      showCaret(caretLine);
2749
	}
2759
   }
2750
	else {
2760
   else {
2751
		doSelectionCursorPrevious();
2761
      doSelectionCursorPrevious();
2752
	}
2762
   }
2753
}
2763
}
2754
/**
2764
/**
2755
 * Moves the caret to the end of the selection if a selection exists.
2765
 * Moves the caret to the end of the selection if a selection exists.
2756
 * Otherwise, if no selection exists move the cursor according to the 
2766
 * Otherwise, if no selection exists move the cursor according to the
2757
 * cursor selection rules.
2767
 * cursor selection rules.
2758
 * <p>
2768
 * <p>
2759
 *
2769
 *
2760
 * @see #doSelectionCursorNext
2770
 * @see #doSelectionCursorNext
2761
 */
2771
 */
2762
void doCursorNext() {
2772
void doCursorNext() {
2763
	if (selection.y - selection.x > 0) {
2773
   if (selection.y - selection.x > 0) {
2764
		int caretLine;
2774
      int caretLine;
2765
2775
2766
		caretOffset = selection.y;
2776
      caretOffset = selection.y;
2767
		caretLine = getCaretLine();
2777
      caretLine = getCaretLine();
2768
		showCaret(caretLine);
2778
      showCaret(caretLine);
2769
	}
2779
   }
2770
	else {
2780
   else {
2771
		doSelectionCursorNext();
2781
      doSelectionCursorNext();
2772
	}
2782
   }
2773
}
2783
}
2774
/**
2784
/**
2775
 * Deletes the next character. Delete the selected text if any.
2785
 * Deletes the next character. Delete the selected text if any.
2776
 */
2786
 */
2777
void doDelete() {
2787
void doDelete() {
2778
	Event event = new Event();
2788
   Event event = new Event();
2779
	
2789
2780
	event.text = "";
2790
   event.text = "";
2781
	if (selection.x != selection.y) {
2791
   if (selection.x != selection.y) {
2782
		event.start = selection.x;
2792
      event.start = selection.x;
2783
		event.end = selection.y;
2793
      event.end = selection.y;
2784
		sendKeyEvent(event);
2794
      sendKeyEvent(event);
2785
	}
2795
   }
2786
	else
2796
   else
2787
	if (caretOffset < content.getCharCount()) {
2797
   if (caretOffset < content.getCharCount()) {
2788
		int line = content.getLineAtOffset(caretOffset);
2798
      int line = content.getLineAtOffset(caretOffset);
2789
		int lineOffset = content.getOffsetAtLine(line);
2799
      int lineOffset = content.getOffsetAtLine(line);
2790
		int lineLength = content.getLine(line).length();
2800
      int lineLength = content.getLine(line).length();
2791
				
2801
2792
		if (caretOffset == lineOffset + lineLength) {
2802
      if (caretOffset == lineOffset + lineLength) {
2793
			event.start = caretOffset;
2803
         event.start = caretOffset;
2794
			event.end = content.getOffsetAtLine(line + 1);
2804
         event.end = content.getOffsetAtLine(line + 1);
2795
		}
2805
      }
2796
		else {
2806
      else {
2797
			event.start = caretOffset;
2807
         event.start = caretOffset;
2798
			event.end = caretOffset + 1;
2808
         event.end = caretOffset + 1;
2799
		}
2809
      }
2800
		sendKeyEvent(event);
2810
      sendKeyEvent(event);
2801
	}
2811
   }
2802
}
2812
}
2803
/**
2813
/**
2804
 * Moves the caret one line down and to the same character offset relative 
2814
 * Moves the caret one line down and to the same character offset relative
2805
 * to the beginning of the line. Move the caret to the end of the new line 
2815
 * to the beginning of the line. Move the caret to the end of the new line
2806
 * if the new line is shorter than the character offset.
2816
 * if the new line is shorter than the character offset.
2807
 * 
2817
 *
2808
 * @return index of the new line relative to the first line in the document
2818
 * @return index of the new line relative to the first line in the document
2809
 */
2819
 */
2810
int doLineDown() {
2820
int doLineDown() {
2811
	if (isSingleLine()) {
2821
   if (isSingleLine()) {
2812
		return 0;
2822
      return 0;
2813
	}
2823
   }
2814
	// allow line down action only if receiver is not in single line mode.
2824
   // allow line down action only if receiver is not in single line mode.
2815
	// fixes 4820.
2825
   // fixes 4820.
2816
	int caretLine = getCaretLine(); 
2826
   int caretLine = getCaretLine();
2817
	if (caretLine < content.getLineCount() - 1) {
2827
   if (caretLine < content.getLineCount() - 1) {
2818
		caretLine++;
2828
      caretLine++;
2819
		if (isBidi()) {
2829
      if (isBidi()) {
2820
			caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine);
2830
         caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine);
2821
		}
2831
      }
2822
		else {
2832
      else {
2823
			caretOffset = getOffsetAtMouseLocation(columnX, caretLine);
2833
         caretOffset = getOffsetAtMouseLocation(columnX, caretLine);
2824
		}		
2834
      }
2825
	}
2835
   }
2826
	return caretLine;
2836
   return caretLine;
2827
}
2837
}
2828
/**
2838
/**
2829
 * Moves the caret to the end of the line.
2839
 * Moves the caret to the end of the line.
2830
 */
2840
 */
2831
void doLineEnd() {
2841
void doLineEnd() {
2832
	int caretLine = getCaretLine();
2842
   int caretLine = getCaretLine();
2833
	int lineOffset = content.getOffsetAtLine(caretLine);	
2843
   int lineOffset = content.getOffsetAtLine(caretLine);
2834
	int lineLength = content.getLine(caretLine).length();
2844
   int lineLength = content.getLine(caretLine).length();
2835
	int lineEndOffset = lineOffset + lineLength;
2845
   int lineEndOffset = lineOffset + lineLength;
2836
	
2846
2837
	if (caretOffset < lineEndOffset) {
2847
   if (caretOffset < lineEndOffset) {
2838
		caretOffset = lineEndOffset;
2848
      caretOffset = lineEndOffset;
2839
		showCaret();
2849
      showCaret();
2840
	}
2850
   }
2841
}
2851
}
2842
/**
2852
/**
2843
 * Moves the caret to the beginning of the line.
2853
 * Moves the caret to the beginning of the line.
2844
 */
2854
 */
2845
void doLineStart() {
2855
void doLineStart() {
2846
	int caretLine = getCaretLine();
2856
   int caretLine = getCaretLine();
2847
	int lineOffset = content.getOffsetAtLine(caretLine);
2857
   int lineOffset = content.getOffsetAtLine(caretLine);
2848
		
2858
2849
	if (caretOffset > lineOffset) {
2859
   if (caretOffset > lineOffset) {
2850
		caretOffset = lineOffset;
2860
      caretOffset = lineOffset;
2851
		showCaret(caretLine);
2861
      showCaret(caretLine);
2852
	}
2862
   }
2853
}
2863
}
2854
/**
2864
/**
2855
 * Moves the caret one line up and to the same character offset relative 
2865
 * Moves the caret one line up and to the same character offset relative
2856
 * to the beginning of the line. Move the caret to the end of the new line 
2866
 * to the beginning of the line. Move the caret to the end of the new line
2857
 * if the new line is shorter than the character offset.
2867
 * if the new line is shorter than the character offset.
2858
 * 
2868
 *
2859
 * @return index of the new line relative to the first line in the document
2869
 * @return index of the new line relative to the first line in the document
2860
 */
2870
 */
2861
int doLineUp() {
2871
int doLineUp() {
2862
	int caretLine = getCaretLine();
2872
   int caretLine = getCaretLine();
2863
2873
2864
	if (caretLine > 0) {
2874
   if (caretLine > 0) {
2865
		caretLine--;
2875
      caretLine--;
2866
		if (isBidi()) {
2876
      if (isBidi()) {
2867
			caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine);
2877
         caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine);
2868
		}
2878
      }
2869
		else {
2879
      else {
2870
			caretOffset = getOffsetAtMouseLocation(columnX, caretLine);
2880
         caretOffset = getOffsetAtMouseLocation(columnX, caretLine);
2871
		}		
2881
      }
2872
	}
2882
   }
2873
	return caretLine;
2883
   return caretLine;
2874
}
2884
}
2875
/**
2885
/**
2876
 * Moves the caret to the specified location.
2886
 * Moves the caret to the specified location.
Lines 2879-3248 Link Here
2879
 * @param x x location of the new caret position
2889
 * @param x x location of the new caret position
2880
 * @param y y location of the new caret position
2890
 * @param y y location of the new caret position
2881
 * @param select the location change is a selection operation.
2891
 * @param select the location change is a selection operation.
2882
 * 	include the line delimiter in the selection
2892
 *    include the line delimiter in the selection
2883
 */
2893
 */
2884
void doMouseLocationChange(int x, int y, boolean select) {
2894
void doMouseLocationChange(int x, int y, boolean select) {
2885
	int line = (y + verticalScrollOffset) / lineHeight;
2895
   int line = (y + verticalScrollOffset) / lineHeight;
2886
	int lineCount = content.getLineCount();
2896
   int lineCount = content.getLineCount();
2887
	int newCaretOffset;
2897
   int newCaretOffset;
2888
	int newCaretLine;
2898
   int newCaretLine;
2889
	
2899
2890
	if (line > lineCount - 1) {
2900
   if (line > lineCount - 1) {
2891
		line = lineCount - 1;
2901
      line = lineCount - 1;
2892
	}	
2902
   }
2893
	// allow caret to be placed below first line only if receiver is 
2903
   // allow caret to be placed below first line only if receiver is
2894
	// not in single line mode. fixes 4820.
2904
   // not in single line mode. fixes 4820.
2895
	if (line < 0 || (isSingleLine() && line > 0)) {
2905
   if (line < 0 || (isSingleLine() && line > 0)) {
2896
		return;
2906
      return;
2897
	}
2907
   }
2898
	if (isBidi()) {
2908
   if (isBidi()) {
2899
		newCaretOffset = getBidiOffsetAtMouseLocation(x, line);	
2909
      newCaretOffset = getBidiOffsetAtMouseLocation(x, line);
2900
	}
2910
   }
2901
	else {
2911
   else {
2902
		newCaretOffset = getOffsetAtMouseLocation(x, line);
2912
      newCaretOffset = getOffsetAtMouseLocation(x, line);
2903
	}
2913
   }
2904
	if (mouseDoubleClick) {
2914
   if (mouseDoubleClick) {
2905
		// double click word select the previous/next word. fixes bug 15610
2915
      // double click word select the previous/next word. fixes bug 15610
2906
		newCaretOffset = doMouseWordSelect(x, newCaretOffset, line);
2916
      newCaretOffset = doMouseWordSelect(x, newCaretOffset, line);
2907
	}
2917
   }
2908
	newCaretLine = content.getLineAtOffset(newCaretOffset);
2918
   newCaretLine = content.getLineAtOffset(newCaretOffset);
2909
	// Is the mouse within the left client area border or on 
2919
   // Is the mouse within the left client area border or on
2910
	// a different line? If not the autoscroll selection 
2920
   // a different line? If not the autoscroll selection
2911
	// could be incorrectly reset. Fixes 1GKM3XS
2921
   // could be incorrectly reset. Fixes 1GKM3XS
2912
	if (y >= 0 && y < getClientArea().height && 
2922
   if (y >= 0 && y < getClientArea().height &&
2913
		(x >= 0 || newCaretLine != content.getLineAtOffset(caretOffset))) {
2923
      (x >= 0 || newCaretLine != content.getLineAtOffset(caretOffset))) {
2914
		if (newCaretOffset != caretOffset) {
2924
      if (newCaretOffset != caretOffset) {
2915
			caretOffset = newCaretOffset;
2925
         caretOffset = newCaretOffset;
2916
			if (select) {
2926
         if (select) {
2917
				doMouseSelection();
2927
            doMouseSelection();
2918
			}
2928
         }
2919
			showCaret();
2929
         showCaret();
2920
		}
2930
      }
2921
	}
2931
   }
2922
	if (select == false) {
2932
   if (select == false) {
2923
		clearSelection(true);
2933
      clearSelection(true);
2924
	}
2934
   }
2925
}
2935
}
2926
/**
2936
/**
2927
 * Updates the selection based on the caret position
2937
 * Updates the selection based on the caret position
2928
 */
2938
 */
2929
void doMouseSelection() {
2939
void doMouseSelection() {
2930
	if (caretOffset <= selection.x || 
2940
   if (caretOffset <= selection.x ||
2931
		(caretOffset > selection.x && 
2941
      (caretOffset > selection.x &&
2932
		 caretOffset < selection.y && selectionAnchor == selection.x)) {
2942
       caretOffset < selection.y && selectionAnchor == selection.x)) {
2933
		doSelection(SWT.LEFT);
2943
      doSelection(SWT.LEFT);
2934
	}
2944
   }
2935
	else {
2945
   else {
2936
		doSelection(SWT.RIGHT);
2946
      doSelection(SWT.RIGHT);
2937
	}
2947
   }
2938
}
2948
}
2939
/**
2949
/**
2940
 * Returns the offset of the word at the specified offset. 
2950
 * Returns the offset of the word at the specified offset.
2941
 * If the current selection extends from high index to low index 
2951
 * If the current selection extends from high index to low index
2942
 * (i.e., right to left, or caret is at left border of selecton on 
2952
 * (i.e., right to left, or caret is at left border of selecton on
2943
 * non-bidi platforms) the start offset of the word preceeding the
2953
 * non-bidi platforms) the start offset of the word preceeding the
2944
 * selection is returned. If the current selection extends from 
2954
 * selection is returned. If the current selection extends from
2945
 * low index to high index the end offset of the word following 
2955
 * low index to high index the end offset of the word following
2946
 * the selection is returned.
2956
 * the selection is returned.
2947
 * 
2957
 *
2948
 * @param x mouse x location
2958
 * @param x mouse x location
2949
 * @param newCaretOffset caret offset of the mouse cursor location
2959
 * @param newCaretOffset caret offset of the mouse cursor location
2950
 * @param line line index of the mouse cursor location
2960
 * @param line line index of the mouse cursor location
2951
 */
2961
 */
2952
int doMouseWordSelect(int x, int newCaretOffset, int line) {
2962
int doMouseWordSelect(int x, int newCaretOffset, int line) {
2953
	int wordOffset;
2963
   int wordOffset;
2954
2964
2955
	// flip selection anchor based on word selection direction from 
2965
   // flip selection anchor based on word selection direction from
2956
	// base double click. Always do this here (and don't rely on doAutoScroll)
2966
   // base double click. Always do this here (and don't rely on doAutoScroll)
2957
	// because auto scroll only does not cover all possible mouse selections
2967
   // because auto scroll only does not cover all possible mouse selections
2958
	// (e.g., mouse x < 0 && mouse y > caret line y)
2968
   // (e.g., mouse x < 0 && mouse y > caret line y)
2959
 	if (newCaretOffset < selectionAnchor && selectionAnchor == selection.x) {
2969
   if (newCaretOffset < selectionAnchor && selectionAnchor == selection.x) {
2960
		selectionAnchor = doubleClickSelection.y;
2970
      selectionAnchor = doubleClickSelection.y;
2961
	}
2971
   }
2962
	else
2972
   else
2963
	if (newCaretOffset > selectionAnchor && selectionAnchor == selection.y) {
2973
   if (newCaretOffset > selectionAnchor && selectionAnchor == selection.y) {
2964
		selectionAnchor = doubleClickSelection.x;
2974
      selectionAnchor = doubleClickSelection.x;
2965
	}
2975
   }
2966
	if (x >= 0 && x < getClientArea().width) {
2976
   if (x >= 0 && x < getClientArea().width) {
2967
		// find the previous/next word
2977
      // find the previous/next word
2968
		if (caretOffset == selection.x) {
2978
      if (caretOffset == selection.x) {
2969
			wordOffset = getWordStart(newCaretOffset);
2979
         wordOffset = getWordStart(newCaretOffset);
2970
		}
2980
      }
2971
		else {
2981
      else {
2972
			wordOffset = getWordEndNoSpaces(newCaretOffset);
2982
         wordOffset = getWordEndNoSpaces(newCaretOffset);
2973
		}
2983
      }
2974
		// mouse word select only on same line mouse cursor is on
2984
      // mouse word select only on same line mouse cursor is on
2975
		if (content.getLineAtOffset(wordOffset) == line) {
2985
      if (content.getLineAtOffset(wordOffset) == line) {
2976
			newCaretOffset = wordOffset;
2986
         newCaretOffset = wordOffset;
2977
		}
2987
      }
2978
	}
2988
   }
2979
	return newCaretOffset;
2989
   return newCaretOffset;
2980
}
2990
}
2981
/**
2991
/**
2982
 * Scrolls one page down so that the last line (truncated or whole)
2992
 * Scrolls one page down so that the last line (truncated or whole)
2983
 * of the current page becomes the fully visible top line.
2993
 * of the current page becomes the fully visible top line.
2984
 * The caret is scrolled the same number of lines so that its location 
2994
 * The caret is scrolled the same number of lines so that its location
2985
 * relative to the top line remains the same. The exception is the end 
2995
 * relative to the top line remains the same. The exception is the end
2986
 * of the text where a full page scroll is not possible. In this case 
2996
 * of the text where a full page scroll is not possible. In this case
2987
 * the caret is moved after the last character.
2997
 * the caret is moved after the last character.
2988
 * <p>
2998
 * <p>
2989
 *
2999
 *
2990
 * @param select whether or not to select the page
3000
 * @param select whether or not to select the page
2991
 */
3001
 */
2992
void doPageDown(boolean select) {
3002
void doPageDown(boolean select) {
2993
	int lineCount = content.getLineCount();
3003
   int lineCount = content.getLineCount();
2994
	int oldColumnX = columnX;
3004
   int oldColumnX = columnX;
2995
	int caretLine;
3005
   int caretLine;
2996
	
3006
2997
	// do nothing if in single line mode. fixes 5673
3007
   // do nothing if in single line mode. fixes 5673
2998
	if (isSingleLine()) {
3008
   if (isSingleLine()) {
2999
		return;
3009
      return;
3000
	}
3010
   }
3001
	caretLine = getCaretLine();
3011
   caretLine = getCaretLine();
3002
	if (caretLine < lineCount - 1) {
3012
   if (caretLine < lineCount - 1) {
3003
		int verticalMaximum = lineCount * getVerticalIncrement();
3013
      int verticalMaximum = lineCount * getVerticalIncrement();
3004
		int pageSize = getClientArea().height;
3014
      int pageSize = getClientArea().height;
3005
		int scrollLines = Math.min(lineCount - caretLine - 1, getLineCountWhole());
3015
      int scrollLines = Math.min(lineCount - caretLine - 1, getLineCountWhole());
3006
		int scrollOffset;
3016
      int scrollOffset;
3007
		
3017
3008
		// ensure that scrollLines never gets negative and at leat one 
3018
      // ensure that scrollLines never gets negative and at leat one
3009
		// line is scrolled. fixes bug 5602.
3019
      // line is scrolled. fixes bug 5602.
3010
		scrollLines = Math.max(1, scrollLines);
3020
      scrollLines = Math.max(1, scrollLines);
3011
		caretLine += scrollLines;		
3021
      caretLine += scrollLines;
3012
		if (isBidi()) {
3022
      if (isBidi()) {
3013
			caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine);
3023
         caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine);
3014
		}
3024
      }
3015
		else {
3025
      else {
3016
			caretOffset = getOffsetAtMouseLocation(columnX, caretLine);
3026
         caretOffset = getOffsetAtMouseLocation(columnX, caretLine);
3017
		}	
3027
      }
3018
		if (select) {
3028
      if (select) {
3019
			doSelection(SWT.RIGHT);
3029
         doSelection(SWT.RIGHT);
3020
		}
3030
      }
3021
		// scroll one page down or to the bottom
3031
      // scroll one page down or to the bottom
3022
		scrollOffset = verticalScrollOffset + scrollLines * getVerticalIncrement();
3032
      scrollOffset = verticalScrollOffset + scrollLines * getVerticalIncrement();
3023
		if (scrollOffset + pageSize > verticalMaximum) {
3033
      if (scrollOffset + pageSize > verticalMaximum) {
3024
			scrollOffset = verticalMaximum - pageSize;
3034
         scrollOffset = verticalMaximum - pageSize;
3025
		}
3035
      }
3026
		if (scrollOffset > verticalScrollOffset) {		
3036
      if (scrollOffset > verticalScrollOffset) {
3027
			setVerticalScrollOffset(scrollOffset, true);
3037
         setVerticalScrollOffset(scrollOffset, true);
3028
		}
3038
      }
3029
	}
3039
   }
3030
	// explicitly go to the calculated caret line. may be different 
3040
   // explicitly go to the calculated caret line. may be different
3031
	// from content.getLineAtOffset(caretOffset) when in word wrap mode
3041
   // from content.getLineAtOffset(caretOffset) when in word wrap mode
3032
	showCaret(caretLine);
3042
   showCaret(caretLine);
3033
	// save the original horizontal caret position	
3043
   // save the original horizontal caret position
3034
	columnX = oldColumnX;
3044
   columnX = oldColumnX;
3035
}
3045
}
3036
/**
3046
/**
3037
 * Moves the cursor to the end of the last fully visible line.
3047
 * Moves the cursor to the end of the last fully visible line.
3038
 */
3048
 */
3039
void doPageEnd() {
3049
void doPageEnd() {
3040
	// go to end of line if in single line mode. fixes 5673
3050
   // go to end of line if in single line mode. fixes 5673
3041
	if (isSingleLine()) {
3051
   if (isSingleLine()) {
3042
		doLineEnd();
3052
      doLineEnd();
3043
	}
3053
   }
3044
	else {
3054
   else {
3045
		int line = getBottomIndex();
3055
      int line = getBottomIndex();
3046
		int bottomCaretOffset = content.getOffsetAtLine(line) + content.getLine(line).length();	
3056
      int bottomCaretOffset = content.getOffsetAtLine(line) + content.getLine(line).length();
3047
3057
3048
		if (caretOffset < bottomCaretOffset) {
3058
      if (caretOffset < bottomCaretOffset) {
3049
			caretOffset = bottomCaretOffset;
3059
         caretOffset = bottomCaretOffset;
3050
			showCaret();
3060
         showCaret();
3051
		}
3061
      }
3052
	}
3062
   }
3053
}
3063
}
3054
/**
3064
/**
3055
 * Moves the cursor to the beginning of the first fully visible line.
3065
 * Moves the cursor to the beginning of the first fully visible line.
3056
 */
3066
 */
3057
void doPageStart() {
3067
void doPageStart() {
3058
	int topCaretOffset = content.getOffsetAtLine(topIndex);
3068
   int topCaretOffset = content.getOffsetAtLine(topIndex);
3059
	
3069
3060
	if (caretOffset > topCaretOffset) {
3070
   if (caretOffset > topCaretOffset) {
3061
		caretOffset = topCaretOffset;
3071
      caretOffset = topCaretOffset;
3062
		// explicitly go to the calculated caret line. may be different 
3072
      // explicitly go to the calculated caret line. may be different
3063
		// from content.getLineAtOffset(caretOffset) when in word wrap mode
3073
      // from content.getLineAtOffset(caretOffset) when in word wrap mode
3064
		showCaret(topIndex);
3074
      showCaret(topIndex);
3065
	}
3075
   }
3066
}
3076
}
3067
/**
3077
/**
3068
 * Scrolls one page up so that the first line (truncated or whole)
3078
 * Scrolls one page up so that the first line (truncated or whole)
3069
 * of the current page becomes the fully visible last line.
3079
 * of the current page becomes the fully visible last line.
3070
 * The caret is scrolled the same number of lines so that its location 
3080
 * The caret is scrolled the same number of lines so that its location
3071
 * relative to the top line remains the same. The exception is the beginning 
3081
 * relative to the top line remains the same. The exception is the beginning
3072
 * of the text where a full page scroll is not possible. In this case the
3082
 * of the text where a full page scroll is not possible. In this case the
3073
 * caret is moved in front of the first character.
3083
 * caret is moved in front of the first character.
3074
 */
3084
 */
3075
void doPageUp() {
3085
void doPageUp() {
3076
	int oldColumnX = columnX;
3086
   int oldColumnX = columnX;
3077
	int caretLine = getCaretLine();
3087
   int caretLine = getCaretLine();
3078
	
3088
3079
	if (caretLine > 0) {	
3089
   if (caretLine > 0) {
3080
		int scrollLines = Math.max(1, Math.min(caretLine, getLineCountWhole()));
3090
      int scrollLines = Math.max(1, Math.min(caretLine, getLineCountWhole()));
3081
		int scrollOffset;
3091
      int scrollOffset;
3082
		
3092
3083
		caretLine -= scrollLines;		
3093
      caretLine -= scrollLines;
3084
		if (isBidi()) {
3094
      if (isBidi()) {
3085
			caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine);
3095
         caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine);
3086
		}
3096
      }
3087
		else {
3097
      else {
3088
			caretOffset = getOffsetAtMouseLocation(columnX, caretLine);
3098
         caretOffset = getOffsetAtMouseLocation(columnX, caretLine);
3089
		}	
3099
      }
3090
		// scroll one page up or to the top
3100
      // scroll one page up or to the top
3091
		scrollOffset = Math.max(0, verticalScrollOffset - scrollLines * getVerticalIncrement());
3101
      scrollOffset = Math.max(0, verticalScrollOffset - scrollLines * getVerticalIncrement());
3092
		if (scrollOffset < verticalScrollOffset) {				
3102
      if (scrollOffset < verticalScrollOffset) {
3093
			setVerticalScrollOffset(scrollOffset, true);
3103
         setVerticalScrollOffset(scrollOffset, true);
3094
		}
3104
      }
3095
	}
3105
   }
3096
	// explicitly go to the calculated caret line. may be different 
3106
   // explicitly go to the calculated caret line. may be different
3097
	// from content.getLineAtOffset(caretOffset) when in word wrap mode
3107
   // from content.getLineAtOffset(caretOffset) when in word wrap mode
3098
	showCaret(caretLine);
3108
   showCaret(caretLine);
3099
	// save the original horizontal caret position	
3109
   // save the original horizontal caret position
3100
	columnX = oldColumnX;
3110
   columnX = oldColumnX;
3101
}
3111
}
3102
/**
3112
/**
3103
 * Updates the selection to extend to the current caret position.
3113
 * Updates the selection to extend to the current caret position.
3104
 */
3114
 */
3105
void doSelection(int direction) {
3115
void doSelection(int direction) {
3106
	int redrawStart = -1;
3116
   int redrawStart = -1;
3107
	int redrawEnd = -1;
3117
   int redrawEnd = -1;
3108
	
3118
3109
	if (selectionAnchor == -1) {
3119
   if (selectionAnchor == -1) {
3110
		selectionAnchor = selection.x;
3120
      selectionAnchor = selection.x;
3111
	}
3121
   }
3112
	if (direction == SWT.LEFT) {
3122
   if (direction == SWT.LEFT) {
3113
		if (caretOffset < selection.x) {
3123
      if (caretOffset < selection.x) {
3114
			// grow selection
3124
         // grow selection
3115
			redrawEnd = selection.x; 
3125
         redrawEnd = selection.x;
3116
			redrawStart = selection.x = caretOffset;		
3126
         redrawStart = selection.x = caretOffset;
3117
			// check if selection has reversed direction
3127
         // check if selection has reversed direction
3118
			if (selection.y != selectionAnchor) {
3128
         if (selection.y != selectionAnchor) {
3119
				redrawEnd = selection.y;
3129
            redrawEnd = selection.y;
3120
				selection.y = selectionAnchor;
3130
            selection.y = selectionAnchor;
3121
			}
3131
         }
3122
		}
3132
      }
3123
		else	// test whether selection actually changed. Fixes 1G71EO1
3133
      else  // test whether selection actually changed. Fixes 1G71EO1
3124
		if (selectionAnchor == selection.x && caretOffset < selection.y) {
3134
      if (selectionAnchor == selection.x && caretOffset < selection.y) {
3125
			// caret moved towards selection anchor (left side of selection). 
3135
         // caret moved towards selection anchor (left side of selection).
3126
			// shrink selection			
3136
         // shrink selection
3127
			redrawEnd = selection.y;
3137
         redrawEnd = selection.y;
3128
			redrawStart = selection.y = caretOffset;		
3138
         redrawStart = selection.y = caretOffset;
3129
		}
3139
      }
3130
	}
3140
   }
3131
	else {
3141
   else {
3132
		if (caretOffset > selection.y) {
3142
      if (caretOffset > selection.y) {
3133
			// grow selection
3143
         // grow selection
3134
			redrawStart = selection.y;
3144
         redrawStart = selection.y;
3135
			redrawEnd = selection.y = caretOffset;
3145
         redrawEnd = selection.y = caretOffset;
3136
			// check if selection has reversed direction
3146
         // check if selection has reversed direction
3137
			if (selection.x != selectionAnchor) {
3147
         if (selection.x != selectionAnchor) {
3138
				redrawStart = selection.x;				
3148
            redrawStart = selection.x;
3139
				selection.x = selectionAnchor;
3149
            selection.x = selectionAnchor;
3140
			}
3150
         }
3141
		}
3151
      }
3142
		else	// test whether selection actually changed. Fixes 1G71EO1
3152
      else  // test whether selection actually changed. Fixes 1G71EO1
3143
		if (selectionAnchor == selection.y && caretOffset > selection.x) {
3153
      if (selectionAnchor == selection.y && caretOffset > selection.x) {
3144
			// caret moved towards selection anchor (right side of selection). 
3154
         // caret moved towards selection anchor (right side of selection).
3145
			// shrink selection			
3155
         // shrink selection
3146
			redrawStart = selection.x;
3156
         redrawStart = selection.x;
3147
			redrawEnd = selection.x = caretOffset;		
3157
         redrawEnd = selection.x = caretOffset;
3148
		}
3158
      }
3149
	}
3159
   }
3150
	if (redrawStart != -1 && redrawEnd != -1) {
3160
   if (redrawStart != -1 && redrawEnd != -1) {
3151
		internalRedrawRange(redrawStart, redrawEnd - redrawStart, true);
3161
      internalRedrawRange(redrawStart, redrawEnd - redrawStart, true);
3152
		sendSelectionEvent();
3162
      sendSelectionEvent();
3153
	}
3163
   }
3154
}
3164
}
3155
/**
3165
/**
3156
 * Moves the caret to the next character or to the beginning of the 
3166
 * Moves the caret to the next character or to the beginning of the
3157
 * next line if the cursor is at the end of a line.
3167
 * next line if the cursor is at the end of a line.
3158
 */
3168
 */
3159
void doSelectionCursorNext() {
3169
void doSelectionCursorNext() {
3160
	int caretLine = getCaretLine();
3170
   int caretLine = getCaretLine();
3161
	int lineOffset = content.getOffsetAtLine(caretLine);
3171
   int lineOffset = content.getOffsetAtLine(caretLine);
3162
	int offsetInLine = caretOffset - lineOffset;
3172
   int offsetInLine = caretOffset - lineOffset;
3163
		
3173
3164
	if (offsetInLine < content.getLine(caretLine).length()) {
3174
   if (offsetInLine < content.getLine(caretLine).length()) {
3165
		// Remember the last direction. Always update lastCaretDirection,
3175
      // Remember the last direction. Always update lastCaretDirection,
3166
		// even though it's not used in non-bidi mode in order to avoid 
3176
      // even though it's not used in non-bidi mode in order to avoid
3167
		// extra methods.		
3177
      // extra methods.
3168
		lastCaretDirection = ST.COLUMN_NEXT;
3178
      lastCaretDirection = ST.COLUMN_NEXT;
3169
		caretOffset++;
3179
      caretOffset++;
3170
		showCaret();
3180
      showCaret();
3171
	}
3181
   }
3172
	else
3182
   else
3173
	if (caretLine < content.getLineCount() - 1 && isSingleLine() == false) {
3183
   if (caretLine < content.getLineCount() - 1 && isSingleLine() == false) {
3174
		// only go to next line if not in single line mode. fixes 5673
3184
      // only go to next line if not in single line mode. fixes 5673
3175
		caretLine++;		
3185
      caretLine++;
3176
		caretOffset = content.getOffsetAtLine(caretLine);
3186
      caretOffset = content.getOffsetAtLine(caretLine);
3177
		// explicitly go to the calculated caret line. may be different 
3187
      // explicitly go to the calculated caret line. may be different
3178
		// from content.getLineAtOffset(caretOffset) when in word wrap mode
3188
      // from content.getLineAtOffset(caretOffset) when in word wrap mode
3179
		showCaret(caretLine);
3189
      showCaret(caretLine);
3180
	}
3190
   }
3181
}
3191
}
3182
/**
3192
/**
3183
 * Moves the caret to the previous character or to the end of the previous 
3193
 * Moves the caret to the previous character or to the end of the previous
3184
 * line if the cursor is at the beginning of a line.
3194
 * line if the cursor is at the beginning of a line.
3185
 */
3195
 */
3186
void doSelectionCursorPrevious() {
3196
void doSelectionCursorPrevious() {
3187
	int caretLine = getCaretLine();
3197
   int caretLine = getCaretLine();
3188
	int lineOffset = content.getOffsetAtLine(caretLine);
3198
   int lineOffset = content.getOffsetAtLine(caretLine);
3189
	int offsetInLine = caretOffset - lineOffset;
3199
   int offsetInLine = caretOffset - lineOffset;
3190
	
3200
3191
	if (offsetInLine > 0) {
3201
   if (offsetInLine > 0) {
3192
		// Remember the last direction. Always update lastCaretDirection,
3202
      // Remember the last direction. Always update lastCaretDirection,
3193
		// even though it's not used in non-bidi mode in order to avoid 
3203
      // even though it's not used in non-bidi mode in order to avoid
3194
		// extra methods.			
3204
      // extra methods.
3195
		lastCaretDirection = ST.COLUMN_PREVIOUS;		
3205
      lastCaretDirection = ST.COLUMN_PREVIOUS;
3196
		caretOffset--;
3206
      caretOffset--;
3197
		// explicitly go to the calculated caret line. may be different 
3207
      // explicitly go to the calculated caret line. may be different
3198
		// from content.getLineAtOffset(caretOffset) when in word wrap mode
3208
      // from content.getLineAtOffset(caretOffset) when in word wrap mode
3199
		showCaret(caretLine);
3209
      showCaret(caretLine);
3200
	}
3210
   }
3201
	else
3211
   else
3202
	if (caretLine > 0) {
3212
   if (caretLine > 0) {
3203
		caretLine--;
3213
      caretLine--;
3204
		lineOffset = content.getOffsetAtLine(caretLine);
3214
      lineOffset = content.getOffsetAtLine(caretLine);
3205
		caretOffset = lineOffset + content.getLine(caretLine).length();
3215
      caretOffset = lineOffset + content.getLine(caretLine).length();
3206
		showCaret();
3216
      showCaret();
3207
	}
3217
   }
3208
}
3218
}
3209
/**
3219
/**
3210
 * Moves the caret one line down and to the same character offset relative 
3220
 * Moves the caret one line down and to the same character offset relative
3211
 * to the beginning of the line. Moves the caret to the end of the new line 
3221
 * to the beginning of the line. Moves the caret to the end of the new line
3212
 * if the new line is shorter than the character offset.
3222
 * if the new line is shorter than the character offset.
3213
 * Moves the caret to the end of the text if the caret already is on the 
3223
 * Moves the caret to the end of the text if the caret already is on the
3214
 * last line.
3224
 * last line.
3215
 * Adjusts the selection according to the caret change. This can either add
3225
 * Adjusts the selection according to the caret change. This can either add
3216
 * to or subtract from the old selection, depending on the previous selection
3226
 * to or subtract from the old selection, depending on the previous selection
3217
 * direction.
3227
 * direction.
3218
 */
3228
 */
3219
void doSelectionLineDown() {
3229
void doSelectionLineDown() {
3220
	int oldColumnX = columnX;
3230
   int oldColumnX = columnX;
3221
	int caretLine;
3231
   int caretLine;
3222
	
3232
3223
	if (isSingleLine()) {
3233
   if (isSingleLine()) {
3224
		return;
3234
      return;
3225
	}
3235
   }
3226
	caretLine = getCaretLine();	
3236
   caretLine = getCaretLine();
3227
	if (caretLine == content.getLineCount() - 1) {
3237
   if (caretLine == content.getLineCount() - 1) {
3228
		caretOffset = content.getCharCount();
3238
      caretOffset = content.getCharCount();
3229
	}
3239
   }
3230
	else {
3240
   else {
3231
		caretLine = doLineDown();
3241
      caretLine = doLineDown();
3232
	}
3242
   }
3233
	setMouseWordSelectionAnchor();	
3243
   setMouseWordSelectionAnchor();
3234
	// select first and then scroll to reduce flash when key 
3244
   // select first and then scroll to reduce flash when key
3235
	// repeat scrolls lots of lines
3245
   // repeat scrolls lots of lines
3236
	doSelection(SWT.RIGHT);
3246
   doSelection(SWT.RIGHT);
3237
	// explicitly go to the calculated caret line. may be different 
3247
   // explicitly go to the calculated caret line. may be different
3238
	// from content.getLineAtOffset(caretOffset) when in word wrap mode
3248
   // from content.getLineAtOffset(caretOffset) when in word wrap mode
3239
	showCaret(caretLine);
3249
   showCaret(caretLine);
3240
	// save the original horizontal caret position
3250
   // save the original horizontal caret position
3241
	columnX = oldColumnX;
3251
   columnX = oldColumnX;
3242
}
3252
}
3243
/**
3253
/**
3244
 * Moves the caret one line up and to the same character offset relative 
3254
 * Moves the caret one line up and to the same character offset relative
3245
 * to the beginning of the line. Moves the caret to the end of the new line 
3255
 * to the beginning of the line. Moves the caret to the end of the new line
3246
 * if the new line is shorter than the character offset.
3256
 * if the new line is shorter than the character offset.
3247
 * Moves the caret to the beginning of the document if it is already on the
3257
 * Moves the caret to the beginning of the document if it is already on the
3248
 * first line.
3258
 * first line.
Lines 3251-3304 Link Here
3251
 * direction.
3261
 * direction.
3252
 */
3262
 */
3253
void doSelectionLineUp() {
3263
void doSelectionLineUp() {
3254
	int oldColumnX = columnX;
3264
   int oldColumnX = columnX;
3255
	int caretLine = getCaretLine();	
3265
   int caretLine = getCaretLine();
3256
	
3266
3257
	if (caretLine == 0) {
3267
   if (caretLine == 0) {
3258
		caretOffset = 0;
3268
      caretOffset = 0;
3259
	}
3269
   }
3260
	else {
3270
   else {
3261
		caretLine = doLineUp();
3271
      caretLine = doLineUp();
3262
	}
3272
   }
3263
	setMouseWordSelectionAnchor();
3273
   setMouseWordSelectionAnchor();
3264
	// explicitly go to the calculated caret line. may be different 
3274
   // explicitly go to the calculated caret line. may be different
3265
	// from content.getLineAtOffset(caretOffset) when in word wrap mode
3275
   // from content.getLineAtOffset(caretOffset) when in word wrap mode
3266
	showCaret(caretLine);
3276
   showCaret(caretLine);
3267
	doSelection(SWT.LEFT);
3277
   doSelection(SWT.LEFT);
3268
	// save the original horizontal caret position	
3278
   // save the original horizontal caret position
3269
	columnX = oldColumnX;
3279
   columnX = oldColumnX;
3270
}
3280
}
3271
/**
3281
/**
3272
 * Moves the caret to the end of the next word .
3282
 * Moves the caret to the end of the next word .
3273
 */
3283
 */
3274
void doSelectionWordNext() {
3284
void doSelectionWordNext() {
3275
	int newCaretOffset = getWordEnd(caretOffset);
3285
   int newCaretOffset = getWordEnd(caretOffset);
3276
	
3286
3277
	// don't change caret position if in single line mode and the cursor 
3287
   // don't change caret position if in single line mode and the cursor
3278
	// would be on a different line. fixes 5673
3288
   // would be on a different line. fixes 5673
3279
	if (isSingleLine() == false || 
3289
   if (isSingleLine() == false ||
3280
		content.getLineAtOffset(caretOffset) == content.getLineAtOffset(newCaretOffset)) {
3290
      content.getLineAtOffset(caretOffset) == content.getLineAtOffset(newCaretOffset)) {
3281
		lastCaretDirection = ST.COLUMN_NEXT;
3291
      lastCaretDirection = ST.COLUMN_NEXT;
3282
		caretOffset = newCaretOffset;
3292
      caretOffset = newCaretOffset;
3283
		showCaret();
3293
      showCaret();
3284
	}
3294
   }
3285
}
3295
}
3286
/**
3296
/**
3287
 * Moves the caret to the start of the previous word.
3297
 * Moves the caret to the start of the previous word.
3288
 */
3298
 */
3289
void doSelectionWordPrevious() {
3299
void doSelectionWordPrevious() {
3290
	int caretLine;
3300
   int caretLine;
3291
	
3301
3292
	lastCaretDirection = ST.COLUMN_PREVIOUS;
3302
   lastCaretDirection = ST.COLUMN_PREVIOUS;
3293
	caretOffset = getWordStart(caretOffset);
3303
   caretOffset = getWordStart(caretOffset);
3294
	caretLine = content.getLineAtOffset(caretOffset);
3304
   caretLine = content.getLineAtOffset(caretOffset);
3295
	// word previous always comes from bottom line. when
3305
   // word previous always comes from bottom line. when
3296
	// wrapping lines, stay on bottom line when on line boundary
3306
   // wrapping lines, stay on bottom line when on line boundary
3297
	if (wordWrap && caretLine < content.getLineCount() - 1 &&
3307
   if (wordWrap && caretLine < content.getLineCount() - 1 &&
3298
		caretOffset == content.getOffsetAtLine(caretLine + 1)) {
3308
      caretOffset == content.getOffsetAtLine(caretLine + 1)) {
3299
		caretLine++;
3309
      caretLine++;
3300
	}
3310
   }
3301
	showCaret(caretLine);
3311
   showCaret(caretLine);
3302
}
3312
}
3303
/**
3313
/**
3304
 * Moves the caret to the end of the next word.
3314
 * Moves the caret to the end of the next word.
Lines 3306-3321 Link Here
3306
 * and remove the selection.
3316
 * and remove the selection.
3307
 */
3317
 */
3308
void doWordNext() {
3318
void doWordNext() {
3309
	if (selection.y - selection.x > 0) {
3319
   if (selection.y - selection.x > 0) {
3310
		int caretLine;
3320
      int caretLine;
3311
		
3321
3312
		caretOffset = selection.y;
3322
      caretOffset = selection.y;
3313
		caretLine = getCaretLine();
3323
      caretLine = getCaretLine();
3314
		showCaret(caretLine);
3324
      showCaret(caretLine);
3315
	}
3325
   }
3316
	else {
3326
   else {
3317
		doSelectionWordNext();
3327
      doSelectionWordNext();
3318
	}
3328
   }
3319
}
3329
}
3320
/**
3330
/**
3321
 * Moves the caret to the start of the previous word.
3331
 * Moves the caret to the start of the previous word.
Lines 3323-3342 Link Here
3323
 * and remove the selection.
3333
 * and remove the selection.
3324
 */
3334
 */
3325
void doWordPrevious() {
3335
void doWordPrevious() {
3326
	if (selection.y - selection.x > 0) {
3336
   if (selection.y - selection.x > 0) {
3327
		int caretLine;
3337
      int caretLine;
3328
		
3338
3329
		caretOffset = selection.x;
3339
      caretOffset = selection.x;
3330
		caretLine = getCaretLine();
3340
      caretLine = getCaretLine();
3331
		showCaret(caretLine);
3341
      showCaret(caretLine);
3332
	}
3342
   }
3333
	else {
3343
   else {
3334
		doSelectionWordPrevious();
3344
      doSelectionWordPrevious();
3335
	}
3345
   }
3336
}
3346
}
3337
/**
3347
/**
3338
 * Draws the specified rectangle.
3348
 * Draws the specified rectangle.
3339
 * Draw directly without invalidating the affected area when clearBackground is 
3349
 * Draw directly without invalidating the affected area when clearBackground is
3340
 * false.
3350
 * false.
3341
 * <p>
3351
 * <p>
3342
 *
3352
 *
Lines 3344-3396 Link Here
3344
 * @param y the y position
3354
 * @param y the y position
3345
 * @param width the width
3355
 * @param width the width
3346
 * @param height the height
3356
 * @param height the height
3347
 * @param clearBackground true=clear the background by invalidating the requested 
3357
 * @param clearBackground true=clear the background by invalidating the requested
3348
 * 	redraw area, false=draw the foreground directly without invalidating the 
3358
 *    redraw area, false=draw the foreground directly without invalidating the
3349
 * 	redraw area.
3359
 *    redraw area.
3350
 */
3360
 */
3351
void draw(int x, int y, int width, int height, boolean clearBackground) {
3361
void draw(int x, int y, int width, int height, boolean clearBackground) {
3352
	if (clearBackground) {
3362
   if (clearBackground) {
3353
		redraw(x + leftMargin, y + topMargin, width, height, true);
3363
      redraw(x + leftMargin, y + topMargin, width, height, true);
3354
	}
3364
   }
3355
	else {
3365
   else {
3356
		int startLine = (y + verticalScrollOffset) / lineHeight;
3366
      int startLine = (y + verticalScrollOffset) / lineHeight;
3357
		int endY = y + height;
3367
      int endY = y + height;
3358
		int paintYFromTopLine = (startLine - topIndex) * lineHeight;
3368
      int paintYFromTopLine = (startLine - topIndex) * lineHeight;
3359
		int topLineOffset = (topIndex * lineHeight - verticalScrollOffset);
3369
      int topLineOffset = (topIndex * lineHeight - verticalScrollOffset);
3360
		int paintY = paintYFromTopLine + topLineOffset + topMargin;	// adjust y position for pixel based scrolling
3370
      int paintY = paintYFromTopLine + topLineOffset + topMargin; // adjust y position for pixel based scrolling
3361
		int lineCount = content.getLineCount();
3371
      int lineCount = content.getLineCount();
3362
		Color background = getBackground();
3372
      Color background = getBackground();
3363
		Color foreground = getForeground();
3373
      Color foreground = getForeground();
3364
		GC gc = getGC();
3374
      GC gc = getGC();
3365
	
3375
3366
		if (isSingleLine()) {
3376
      if (isSingleLine()) {
3367
			lineCount = 1;
3377
         lineCount = 1;
3368
			if (startLine > 1) {
3378
         if (startLine > 1) {
3369
				startLine = 1;
3379
            startLine = 1;
3370
			}
3380
         }
3371
		}
3381
      }
3372
		for (int i = startLine; paintY < endY && i < lineCount; i++, paintY += lineHeight) {
3382
      for (int i = startLine; paintY < endY && i < lineCount; i++, paintY += lineHeight) {
3373
			String line = content.getLine(i);
3383
         String line = content.getLine(i);
3374
			renderer.drawLine(line, i, paintY, gc, background, foreground, clearBackground);
3384
         renderer.drawLine(line, i, paintY, gc, background, foreground, clearBackground);
3375
		}
3385
      }
3376
		gc.dispose();	
3386
      gc.dispose();
3377
	}
3387
   }
3378
}
3388
}
3379
/** 
3389
/**
3380
 * Ends the autoscroll process.
3390
 * Ends the autoscroll process.
3381
 */
3391
 */
3382
void endAutoScroll() {
3392
void endAutoScroll() {
3383
	autoScrollDirection = SWT.NULL;
3393
   autoScrollDirection = SWT.NULL;
3384
}
3394
}
3385
/**
3395
/**
3386
 * @see org.eclipse.swt.widgets.Control#getBackground
3396
 * @see org.eclipse.swt.widgets.Control#getBackground
3387
 */
3397
 */
3388
public Color getBackground() {
3398
public Color getBackground() {
3389
	checkWidget();
3399
   checkWidget();
3390
	if (background == null) {
3400
   if (background == null) {
3391
		return getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
3401
      return getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
3392
	}
3402
   }
3393
	return background;
3403
   return background;
3394
}
3404
}
3395
/**
3405
/**
3396
 * Gets the BIDI coloring mode.  When true the BIDI text display
3406
 * Gets the BIDI coloring mode.  When true the BIDI text display
Lines 3407-3476 Link Here
3407
 * </p>
3417
 * </p>
3408
 */
3418
 */
3409
public boolean getBidiColoring() {
3419
public boolean getBidiColoring() {
3410
	checkWidget();
3420
   checkWidget();
3411
	return bidiColoring;
3421
   return bidiColoring;
3412
}
3422
}
3413
/**
3423
/**
3414
 * Returns the offset at the specified x location in the specified line.
3424
 * Returns the offset at the specified x location in the specified line.
3415
 * Also sets the caret direction so that the caret is placed correctly 
3425
 * Also sets the caret direction so that the caret is placed correctly
3416
 * depending on whether the mouse location is in a R2L or L2R segment.
3426
 * depending on whether the mouse location is in a R2L or L2R segment.
3417
 * <p>
3427
 * <p>
3418
 *
3428
 *
3419
 * @param x	x location of the mouse location
3429
 * @param x x location of the mouse location
3420
 * @param line	line the mouse location is in
3430
 * @param line line the mouse location is in
3421
 * @return the offset at the specified x location in the specified line,
3431
 * @return the offset at the specified x location in the specified line,
3422
 * 	relative to the beginning of the document
3432
 *    relative to the beginning of the document
3423
 */
3433
 */
3424
int getBidiOffsetAtMouseLocation(int x, int line) {
3434
int getBidiOffsetAtMouseLocation(int x, int line) {
3425
	String lineText = content.getLine(line);
3435
   String lineText = content.getLine(line);
3426
	int lineOffset = content.getOffsetAtLine(line);
3436
   int lineOffset = content.getOffsetAtLine(line);
3427
	GC gc = getGC();
3437
   GC gc = getGC();
3428
	StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc);
3438
   StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc);
3429
	int[] values;
3439
   int[] values;
3430
	int offsetInLine;
3440
   int offsetInLine;
3431
	x += horizontalScrollOffset;
3441
   x += horizontalScrollOffset;
3432
	values = bidi.getCaretOffsetAndDirectionAtX(x - leftMargin);
3442
   values = bidi.getCaretOffsetAndDirectionAtX(x - leftMargin);
3433
	offsetInLine = values[0];
3443
   offsetInLine = values[0];
3434
	lastCaretDirection = values[1];
3444
   lastCaretDirection = values[1];
3435
	gc.dispose();
3445
   gc.dispose();
3436
	
3446
3437
	return lineOffset + offsetInLine;
3447
   return lineOffset + offsetInLine;
3438
}
3448
}
3439
/**
3449
/**
3440
 * Returns the x position of the character at the specified offset 
3450
 * Returns the x position of the character at the specified offset
3441
 * relative to the first character in the line.
3451
 * relative to the first character in the line.
3442
 * </p>
3452
 * </p>
3443
 *
3453
 *
3444
 * @param text text to be measured.
3454
 * @param text text to be measured.
3445
 * @param endOffset offset of the character
3455
 * @param endOffset offset of the character
3446
 * @param bidi the bidi object to use for measuring text in bidi
3456
 * @param bidi the bidi object to use for measuring text in bidi
3447
 *	locales. 
3457
 * locales.
3448
 * @return x position of the character at the specified offset. 
3458
 * @return x position of the character at the specified offset.
3449
 * 	0 if the length is outside the specified text.
3459
 *    0 if the length is outside the specified text.
3450
 */
3460
 */
3451
int getBidiTextPosition(String text, int endOffset, StyledTextBidi bidi) {
3461
int getBidiTextPosition(String text, int endOffset, StyledTextBidi bidi) {
3452
	if (endOffset > text.length()) {
3462
   if (endOffset > text.length()) {
3453
		return 0;
3463
      return 0;
3454
	}
3464
   }
3455
	// Use lastCaretDirection in order to get same results as during
3465
   // Use lastCaretDirection in order to get same results as during
3456
	// caret positioning (setBidiCaretLocation). Fixes 1GKU4C5.
3466
   // caret positioning (setBidiCaretLocation). Fixes 1GKU4C5.
3457
	return bidi.getTextPosition(endOffset, lastCaretDirection);
3467
   return bidi.getTextPosition(endOffset, lastCaretDirection);
3458
}
3468
}
3459
/** 
3469
/**
3460
 * Returns the index of the last fully visible line.
3470
 * Returns the index of the last fully visible line.
3461
 * <p>
3471
 * <p>
3462
 *
3472
 *
3463
 * @return index of the last fully visible line.
3473
 * @return index of the last fully visible line.
3464
 */
3474
 */
3465
int getBottomIndex() {
3475
int getBottomIndex() {
3466
	int lineCount = 1;
3476
   int lineCount = 1;
3467
	
3477
3468
	if (lineHeight != 0) {
3478
   if (lineHeight != 0) {
3469
		// calculate the number of lines that are fully visible
3479
      // calculate the number of lines that are fully visible
3470
		int partialTopLineHeight = topIndex * lineHeight - verticalScrollOffset;
3480
      int partialTopLineHeight = topIndex * lineHeight - verticalScrollOffset;
3471
		lineCount = (getClientArea().height - partialTopLineHeight) / lineHeight;
3481
      lineCount = (getClientArea().height - partialTopLineHeight) / lineHeight;
3472
	}
3482
   }
3473
	return Math.min(content.getLineCount() - 1, topIndex + Math.max(0, lineCount - 1));
3483
   return Math.min(content.getLineCount() - 1, topIndex + Math.max(0, lineCount - 1));
3474
}
3484
}
3475
/**
3485
/**
3476
 * Returns the caret position relative to the start of the text.
3486
 * Returns the caret position relative to the start of the text.
Lines 3483-3535 Link Here
3483
 * </ul>
3493
 * </ul>
3484
 */
3494
 */
3485
public int getCaretOffset() {
3495
public int getCaretOffset() {
3486
	checkWidget();
3496
   checkWidget();
3487
	
3497
3488
	return caretOffset;
3498
   return caretOffset;
3489
}
3499
}
3490
/**
3500
/**
3491
 * Returns the caret offset at the given x location in the line.
3501
 * Returns the caret offset at the given x location in the line.
3492
 * The caret offset is the offset of the character where the caret will be
3502
 * The caret offset is the offset of the character where the caret will be
3493
 * placed when a mouse click occurs. The caret offset will be the offset of 
3503
 * placed when a mouse click occurs. The caret offset will be the offset of
3494
 * the character after the clicked one if the mouse click occurs at the second 
3504
 * the character after the clicked one if the mouse click occurs at the second
3495
 * half of a character.
3505
 * half of a character.
3496
 * Doesn't properly handle ligatures and other context dependent characters 
3506
 * Doesn't properly handle ligatures and other context dependent characters
3497
 * unless the current locale is a bidi locale. 
3507
 * unless the current locale is a bidi locale.
3498
 * Ligatures are handled properly as long as they don't occur at lineXOffset.
3508
 * Ligatures are handled properly as long as they don't occur at lineXOffset.
3499
 * <p>
3509
 * <p>
3500
 *
3510
 *
3501
 * @param line text of the line to calculate the offset in
3511
 * @param line text of the line to calculate the offset in
3502
 * @param lineOffset offset of the first character in the line. 
3512
 * @param lineOffset offset of the first character in the line.
3503
 * 	0 based from the beginning of the document.
3513
 *    0 based from the beginning of the document.
3504
 * @param lineXOffset x location in the line
3514
 * @param lineXOffset x location in the line
3505
 * @return caret offset at the x location relative to the start of the line.
3515
 * @return caret offset at the x location relative to the start of the line.
3506
 */
3516
 */
3507
int getCaretOffsetAtX(String line, int lineOffset, int lineXOffset) {
3517
int getCaretOffsetAtX(String line, int lineOffset, int lineXOffset) {
3508
	int offset = 0;
3518
   int offset = 0;
3509
	GC gc = getGC();
3519
   GC gc = getGC();
3510
	StyleRange[] styles = null;
3520
   StyleRange[] styles = null;
3511
	StyledTextEvent event = renderer.getLineStyleData(lineOffset, line);
3521
   StyledTextEvent event = renderer.getLineStyleData(lineOffset, line);
3512
	
3522
3513
	lineXOffset += horizontalScrollOffset;
3523
   lineXOffset += horizontalScrollOffset;
3514
	if (event != null) {
3524
   if (event != null) {
3515
		styles = renderer.filterLineStyles(event.styles);
3525
      styles = renderer.filterLineStyles(event.styles);
3516
	}
3526
   }
3517
	int low = -1;
3527
   int low = -1;
3518
	int high = line.length();
3528
   int high = line.length();
3519
	while (high - low > 1) {
3529
   while (high - low > 1) {
3520
		offset = (high + low) / 2;
3530
      offset = (high + low) / 2;
3521
		int x = renderer.getTextPosition(line, lineOffset, offset, styles, gc) + leftMargin;
3531
      int x = renderer.getTextPosition(line, lineOffset, offset, styles, gc) + leftMargin;
3522
		int charWidth = renderer.getTextPosition(line, lineOffset, offset + 1, styles, gc) + leftMargin - x;
3532
      int charWidth = renderer.getTextPosition(line, lineOffset, offset + 1, styles, gc) + leftMargin - x;
3523
		if (lineXOffset <= x + charWidth / 2) {
3533
      if (lineXOffset <= x + charWidth / 2) {
3524
			high = offset;			
3534
         high = offset;
3525
		}
3535
      }
3526
		else {
3536
      else {
3527
			low = offset;
3537
         low = offset;
3528
		}
3538
      }
3529
	}
3539
   }
3530
	offset = high;
3540
   offset = high;
3531
	gc.dispose();
3541
   gc.dispose();
3532
	return offset;	
3542
   return offset;
3533
}
3543
}
3534
/**
3544
/**
3535
 * Returns the caret width.
3545
 * Returns the caret width.
Lines 3538-3553 Link Here
3538
 * @return the caret width, 0 if caret is null.
3548
 * @return the caret width, 0 if caret is null.
3539
 */
3549
 */
3540
int getCaretWidth() {
3550
int getCaretWidth() {
3541
	Caret caret = getCaret();
3551
   Caret caret = getCaret();
3542
	if (caret == null) return 0;
3552
   if (caret == null) return 0;
3543
	return caret.getSize().x;
3553
   return caret.getSize().x;
3544
}
3554
}
3545
/**
3555
/**
3546
 * Returns the content implementation that is used for text storage
3556
 * Returns the content implementation that is used for text storage
3547
 * or null if no user defined content implementation has been set.
3557
 * or null if no user defined content implementation has been set.
3548
 * <p>
3558
 * <p>
3549
 *
3559
 *
3550
 * @return content implementation that is used for text storage or null 
3560
 * @return content implementation that is used for text storage or null
3551
 * if no user defined content implementation has been set.
3561
 * if no user defined content implementation has been set.
3552
 * @exception SWTException <ul>
3562
 * @exception SWTException <ul>
3553
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3563
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
Lines 3555-3565 Link Here
3555
 * </ul>
3565
 * </ul>
3556
 */
3566
 */
3557
public StyledTextContent getContent() {
3567
public StyledTextContent getContent() {
3558
	checkWidget();
3568
   checkWidget();
3559
	
3569
3560
	return logicalContent;
3570
   return logicalContent;
3561
}
3571
}
3562
/** 
3572
/**
3563
 * Returns whether the widget implements double click mouse behavior.
3573
 * Returns whether the widget implements double click mouse behavior.
3564
 * <p>
3574
 * <p>
3565
 *
3575
 *
Lines 3571-3578 Link Here
3571
 * </ul>
3581
 * </ul>
3572
 */
3582
 */
3573
public boolean getDoubleClickEnabled() {
3583
public boolean getDoubleClickEnabled() {
3574
	checkWidget();
3584
   checkWidget();
3575
	return doubleClickEnabled;
3585
   return doubleClickEnabled;
3576
}
3586
}
3577
/**
3587
/**
3578
 * Returns whether the widget content can be edited.
3588
 * Returns whether the widget content can be edited.
Lines 3585-3604 Link Here
3585
 * </ul>
3595
 * </ul>
3586
 */
3596
 */
3587
public boolean getEditable() {
3597
public boolean getEditable() {
3588
	checkWidget();
3598
   checkWidget();
3589
	return editable;
3599
   return editable;
3590
}
3600
}
3591
/**
3601
/**
3592
 * @see org.eclipse.swt.widgets.Control#getForeground
3602
 * @see org.eclipse.swt.widgets.Control#getForeground
3593
 */
3603
 */
3594
public Color getForeground() {
3604
public Color getForeground() {
3595
	checkWidget();
3605
   checkWidget();
3596
	if (foreground == null) {
3606
   if (foreground == null) {
3597
		return getDisplay().getSystemColor(SWT.COLOR_LIST_FOREGROUND);
3607
      return getDisplay().getSystemColor(SWT.COLOR_LIST_FOREGROUND);
3598
	}
3608
   }
3599
	return foreground;
3609
   return foreground;
3600
}
3610
}
3601
/** 
3611
/**
3602
 * Return a GC to use for rendering and update the cached font style to
3612
 * Return a GC to use for rendering and update the cached font style to
3603
 * represent the current style.
3613
 * represent the current style.
3604
 * <p>
3614
 * <p>
Lines 3606-3628 Link Here
3606
 * @return GC.
3616
 * @return GC.
3607
 */
3617
 */
3608
GC getGC() {
3618
GC getGC() {
3609
	renderer.setCurrentFontStyle(SWT.NORMAL);
3619
   renderer.setCurrentFontStyle(SWT.NORMAL);
3610
	return new GC(this);
3620
   return new GC(this);
3611
}
3621
}
3612
/** 
3622
/**
3613
 * Returns the horizontal scroll increment.
3623
 * Returns the horizontal scroll increment.
3614
 * <p>
3624
 * <p>
3615
 *
3625
 *
3616
 * @return horizontal scroll increment.
3626
 * @return horizontal scroll increment.
3617
 */
3627
 */
3618
int getHorizontalIncrement() {
3628
int getHorizontalIncrement() {
3619
	GC gc = getGC();
3629
   GC gc = getGC();
3620
	int increment = gc.getFontMetrics().getAverageCharWidth();
3630
   int increment = gc.getFontMetrics().getAverageCharWidth();
3621
	
3631
3622
	gc.dispose();
3632
   gc.dispose();
3623
	return increment;
3633
   return increment;
3624
}
3634
}
3625
/** 
3635
/**
3626
 * Returns the horizontal scroll offset relative to the start of the line.
3636
 * Returns the horizontal scroll offset relative to the start of the line.
3627
 * <p>
3637
 * <p>
3628
 *
3638
 *
Lines 3633-3643 Link Here
3633
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3643
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3634
 * </ul>
3644
 * </ul>
3635
 */
3645
 */
3636
public int getHorizontalIndex() {	
3646
public int getHorizontalIndex() {
3637
	checkWidget();
3647
   checkWidget();
3638
	return horizontalScrollOffset / getHorizontalIncrement();
3648
   return horizontalScrollOffset / getHorizontalIncrement();
3639
}
3649
}
3640
/** 
3650
/**
3641
 * Returns the horizontal scroll offset relative to the start of the line.
3651
 * Returns the horizontal scroll offset relative to the start of the line.
3642
 * <p>
3652
 * <p>
3643
 *
3653
 *
Lines 3648-3686 Link Here
3648
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3658
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3649
 * </ul>
3659
 * </ul>
3650
 */
3660
 */
3651
public int getHorizontalPixel() {	
3661
public int getHorizontalPixel() {
3652
	checkWidget();
3662
   checkWidget();
3653
	return horizontalScrollOffset;
3663
   return horizontalScrollOffset;
3654
}
3664
}
3655
/** 
3665
/**
3656
 * Returns the action assigned to the key.
3666
 * Returns the action assigned to the key.
3657
 * Returns SWT.NULL if there is no action associated with the key.
3667
 * Returns SWT.NULL if there is no action associated with the key.
3658
 * <p>
3668
 * <p>
3659
 *
3669
 *
3660
 * @param key a key code defined in SWT.java or a character. 
3670
 * @param key a key code defined in SWT.java or a character.
3661
 * 	Optionally ORd with a state mask.  Preferred state masks are one or more of
3671
 *    Optionally ORd with a state mask.  Preferred state masks are one or more of
3662
 *  SWT.MOD1, SWT.MOD2, SWT.MOD3, since these masks account for modifier platform 
3672
 *  SWT.MOD1, SWT.MOD2, SWT.MOD3, since these masks account for modifier platform
3663
 *  differences.  However, there may be cases where using the specific state masks
3673
 *  differences.  However, there may be cases where using the specific state masks
3664
 *  (i.e., SWT.CTRL, SWT.SHIFT, SWT.ALT, SWT.COMMAND) makes sense.
3674
 *  (i.e., SWT.CTRL, SWT.SHIFT, SWT.ALT, SWT.COMMAND) makes sense.
3665
 * @return one of the predefined actions defined in ST.java or SWT.NULL 
3675
 * @return one of the predefined actions defined in ST.java or SWT.NULL
3666
 * 	if there is no action associated with the key.
3676
 *    if there is no action associated with the key.
3667
 * @exception SWTException <ul>
3677
 * @exception SWTException <ul>
3668
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3678
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3669
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3679
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3670
 * </ul>
3680
 * </ul>
3671
 */
3681
 */
3672
public int getKeyBinding(int key) {
3682
public int getKeyBinding(int key) {
3673
	checkWidget();
3683
   checkWidget();
3674
	Integer action = (Integer) keyActionMap.get(new Integer(key));
3684
   Integer action = (Integer) keyActionMap.get(new Integer(key));
3675
	int intAction;
3685
   int intAction;
3676
	
3686
3677
	if (action == null) {
3687
   if (action == null) {
3678
		intAction = SWT.NULL;
3688
      intAction = SWT.NULL;
3679
	}
3689
   }
3680
	else {
3690
   else {
3681
		intAction = action.intValue();
3691
      intAction = action.intValue();
3682
	}
3692
   }
3683
	return intAction;
3693
   return intAction;
3684
}
3694
}
3685
/**
3695
/**
3686
 * Gets the number of characters.
3696
 * Gets the number of characters.
Lines 3693-3704 Link Here
3693
 * </ul>
3703
 * </ul>
3694
 */
3704
 */
3695
public int getCharCount() {
3705
public int getCharCount() {
3696
	checkWidget();
3706
   checkWidget();
3697
	return content.getCharCount();
3707
   return content.getCharCount();
3698
}
3708
}
3699
/**
3709
/**
3700
 * Returns the background color of the line at the given index.
3710
 * Returns the background color of the line at the given index.
3701
 * Returns null if a LineBackgroundListener has been set or if no background 
3711
 * Returns null if a LineBackgroundListener has been set or if no background
3702
 * color has been specified for the line. Should not be called if a
3712
 * color has been specified for the line. Should not be called if a
3703
 * LineBackgroundListener has been set since the listener maintains the
3713
 * LineBackgroundListener has been set since the listener maintains the
3704
 * line background colors.
3714
 * line background colors.
Lines 3714-3743 Link Here
3714
 * </ul>
3724
 * </ul>
3715
 */
3725
 */
3716
public Color getLineBackground(int index) {
3726
public Color getLineBackground(int index) {
3717
	checkWidget();
3727
   checkWidget();
3718
	Color lineBackground = null;
3728
   Color lineBackground = null;
3719
	
3729
3720
	if (index < 0 || index > logicalContent.getLineCount()) {
3730
   if (index < 0 || index > logicalContent.getLineCount()) {
3721
		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3731
      SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3722
	}
3732
   }
3723
	if (userLineBackground == false) {
3733
   if (userLineBackground == false) {
3724
		lineBackground = defaultLineStyler.getLineBackground(index);
3734
      lineBackground = defaultLineStyler.getLineBackground(index);
3725
	}
3735
   }
3726
	return lineBackground;
3736
   return lineBackground;
3727
}
3737
}
3728
/**
3738
/**
3729
 * Returns the line background data for the given line or null if 
3739
 * Returns the line background data for the given line or null if
3730
 * there is none.
3740
 * there is none.
3731
 * <p>
3741
 * <p>
3732
 * @param lineOffset offset of the line start relative to the start
3742
 * @param lineOffset offset of the line start relative to the start
3733
 * 	of the content.
3743
 *    of the content.
3734
 * @param line line to get line background data for
3744
 * @param line line to get line background data for
3735
 * @return line background data for the given line.
3745
 * @return line background data for the given line.
3736
 */
3746
 */
3737
StyledTextEvent getLineBackgroundData(int lineOffset, String line) {
3747
StyledTextEvent getLineBackgroundData(int lineOffset, String line) {
3738
	return sendLineEvent(LineGetBackground, lineOffset, line);
3748
   return sendLineEvent(LineGetBackground, lineOffset, line);
3739
}
3749
}
3740
/** 
3750
/**
3741
 * Gets the number of text lines.
3751
 * Gets the number of text lines.
3742
 * <p>
3752
 * <p>
3743
 *
3753
 *
Lines 3748-3774 Link Here
3748
 * </ul>
3758
 * </ul>
3749
 */
3759
 */
3750
public int getLineCount() {
3760
public int getLineCount() {
3751
	checkWidget();
3761
   checkWidget();
3752
	return getLineAtOffset(getCharCount()) + 1;
3762
   return getLineAtOffset(getCharCount()) + 1;
3753
}
3763
}
3754
/**
3764
/**
3755
 * Returns the number of lines that can be completely displayed in the 
3765
 * Returns the number of lines that can be completely displayed in the
3756
 * widget client area.
3766
 * widget client area.
3757
 * <p>
3767
 * <p>
3758
 *
3768
 *
3759
 * @return number of lines that can be completely displayed in the widget 
3769
 * @return number of lines that can be completely displayed in the widget
3760
 * 	client area.
3770
 *    client area.
3761
 */
3771
 */
3762
int getLineCountWhole() {
3772
int getLineCountWhole() {
3763
	int lineCount;
3773
   int lineCount;
3764
	
3774
3765
	if (lineHeight != 0) {
3775
   if (lineHeight != 0) {
3766
		lineCount = getClientArea().height / lineHeight;
3776
      lineCount = getClientArea().height / lineHeight;
3767
	}
3777
   }
3768
	else {
3778
   else {
3769
		lineCount = 1;
3779
      lineCount = 1;
3770
	}
3780
   }
3771
	return lineCount;
3781
   return lineCount;
3772
}
3782
}
3773
/**
3783
/**
3774
 * Returns the line at the specified offset in the text.
3784
 * Returns the line at the specified offset in the text.
Lines 3776-3799 Link Here
3776
 * returns the line of the insert location.
3786
 * returns the line of the insert location.
3777
 * <p>
3787
 * <p>
3778
 *
3788
 *
3779
 * @param offset offset relative to the start of the content. 
3789
 * @param offset offset relative to the start of the content.
3780
 * 	0 <= offset <= getCharCount()
3790
 *    0 <= offset <= getCharCount()
3781
 * @return line at the specified offset in the text
3791
 * @return line at the specified offset in the text
3782
 * @exception SWTException <ul>
3792
 * @exception SWTException <ul>
3783
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3793
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3784
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3794
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3785
 * </ul>
3795
 * </ul>
3786
 * @exception IllegalArgumentException <ul>
3796
 * @exception IllegalArgumentException <ul>
3787
 *   <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li> 
3797
 *   <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
3788
 * </ul>
3798
 * </ul>
3789
 */
3799
 */
3790
public int getLineAtOffset(int offset) {
3800
public int getLineAtOffset(int offset) {
3791
	checkWidget();
3801
   checkWidget();
3792
	
3802
3793
	if (offset < 0 || offset > getCharCount()) {
3803
   if (offset < 0 || offset > getCharCount()) {
3794
		SWT.error(SWT.ERROR_INVALID_RANGE);		
3804
      SWT.error(SWT.ERROR_INVALID_RANGE);
3795
	}
3805
   }
3796
	return logicalContent.getLineAtOffset(offset);
3806
   return logicalContent.getLineAtOffset(offset);
3797
}
3807
}
3798
/**
3808
/**
3799
 * Returns the line delimiter used for entering new lines by key down
3809
 * Returns the line delimiter used for entering new lines by key down
Lines 3808-3850 Link Here
3808
 * </ul>
3818
 * </ul>
3809
 */
3819
 */
3810
public String getLineDelimiter() {
3820
public String getLineDelimiter() {
3811
	checkWidget();
3821
   checkWidget();
3812
	return content.getLineDelimiter();
3822
   return content.getLineDelimiter();
3813
}
3823
}
3814
/**
3824
/**
3815
 * Returns a StyledTextEvent that can be used to request data such 
3825
 * Returns a StyledTextEvent that can be used to request data such
3816
 * as styles and background color for a line.
3826
 * as styles and background color for a line.
3817
 * The specified line may be a visual (wrapped) line if in word 
3827
 * The specified line may be a visual (wrapped) line if in word
3818
 * wrap mode. The returned object will always be for a logical 
3828
 * wrap mode. The returned object will always be for a logical
3819
 * (unwrapped) line.
3829
 * (unwrapped) line.
3820
 * <p>
3830
 * <p>
3821
 *
3831
 *
3822
 * @param lineOffset offset of the line. This may be the offset of
3832
 * @param lineOffset offset of the line. This may be the offset of
3823
 * 	a visual line if the widget is in word wrap mode.
3833
 *    a visual line if the widget is in word wrap mode.
3824
 * @param line line text. This may be the text of a visualline if 
3834
 * @param line line text. This may be the text of a visualline if
3825
 * 	the widget is in word wrap mode.
3835
 *    the widget is in word wrap mode.
3826
 * @return StyledTextEvent that can be used to request line data 
3836
 * @return StyledTextEvent that can be used to request line data
3827
 * 	for the given line.
3837
 *    for the given line.
3828
 */
3838
 */
3829
StyledTextEvent sendLineEvent(int eventType, int lineOffset, String line) {
3839
StyledTextEvent sendLineEvent(int eventType, int lineOffset, String line) {
3830
	StyledTextEvent event = null;
3840
   StyledTextEvent event = null;
3831
	
3841
3832
	if (isListening(eventType)) {
3842
   if (isListening(eventType)) {
3833
		event = new StyledTextEvent(logicalContent);		
3843
      event = new StyledTextEvent(logicalContent);
3834
		if (wordWrap) {
3844
      if (wordWrap) {
3835
		    // if word wrap is on, the line offset and text may be visual (wrapped)
3845
          // if word wrap is on, the line offset and text may be visual (wrapped)
3836
		    int lineIndex = logicalContent.getLineAtOffset(lineOffset);
3846
          int lineIndex = logicalContent.getLineAtOffset(lineOffset);
3837
		    
3847
3838
		    event.detail = logicalContent.getOffsetAtLine(lineIndex);
3848
          event.detail = logicalContent.getOffsetAtLine(lineIndex);
3839
			event.text = logicalContent.getLine(lineIndex);
3849
         event.text = logicalContent.getLine(lineIndex);
3840
		}
3850
      }
3841
		else {
3851
      else {
3842
			event.detail = lineOffset;
3852
         event.detail = lineOffset;
3843
			event.text = line;
3853
         event.text = line;
3844
		}
3854
      }
3845
		notifyListeners(eventType, event);
3855
      notifyListeners(eventType, event);
3846
	}
3856
   }
3847
	return event;	
3857
   return event;
3848
}
3858
}
3849
/**
3859
/**
3850
 * Returns the line height.
3860
 * Returns the line height.
Lines 3857-3964 Link Here
3857
 * </ul>
3867
 * </ul>
3858
 */
3868
 */
3859
public int getLineHeight() {
3869
public int getLineHeight() {
3860
	checkWidget();
3870
   checkWidget();
3861
	return lineHeight;
3871
   return lineHeight;
3862
}
3872
}
3863
/**
3873
/**
3864
 * Returns a LineCache implementation. Depending on whether or not
3874
 * Returns a LineCache implementation. Depending on whether or not
3865
 * word wrap is on this may be a line wrapping or line width 
3875
 * word wrap is on this may be a line wrapping or line width
3866
 * calculating implementaiton.
3876
 * calculating implementaiton.
3867
 * <p>
3877
 * <p>
3868
 * 
3878
 *
3869
 * @param content StyledTextContent to create the LineCache on.
3879
 * @param content StyledTextContent to create the LineCache on.
3870
 * @return a LineCache implementation
3880
 * @return a LineCache implementation
3871
 */
3881
 */
3872
LineCache getLineCache(StyledTextContent content) {
3882
LineCache getLineCache(StyledTextContent content) {
3873
    LineCache lineCache;
3883
    LineCache lineCache;
3874
    
3884
3875
	if (wordWrap) {
3885
   if (wordWrap) {
3876
	    lineCache = new WordWrapCache(this, (WrappedContent) content);
3886
       lineCache = new WordWrapCache(this, (WrappedContent) content);
3877
	}
3887
   }
3878
	else {
3888
   else {
3879
	    lineCache = new ContentWidthCache(this, content.getLineCount());
3889
       lineCache = new ContentWidthCache(this, content.getLineCount());
3880
	}
3890
   }
3881
	return lineCache;
3891
   return lineCache;
3882
}
3892
}
3883
/**
3893
/**
3884
 * Returns the line style data for the given line or null if there is 
3894
 * Returns the line style data for the given line or null if there is
3885
 * none. If there is a LineStyleListener but it does not set any styles, 
3895
 * none. If there is a LineStyleListener but it does not set any styles,
3886
 * the StyledTextEvent.styles field will be initialized to an empty 
3896
 * the StyledTextEvent.styles field will be initialized to an empty
3887
 * array.
3897
 * array.
3888
 * <p>
3898
 * <p>
3889
 * 
3899
 *
3890
 * @param lineOffset offset of the line start relative to the start of 
3900
 * @param lineOffset offset of the line start relative to the start of
3891
 * 	the content.
3901
 *    the content.
3892
 * @param line line to get line styles for
3902
 * @param line line to get line styles for
3893
 * @return line style data for the given line. Styles may start before 
3903
 * @return line style data for the given line. Styles may start before
3894
 * 	line start and end after line end
3904
 *    line start and end after line end
3895
 */
3905
 */
3896
StyledTextEvent getLineStyleData(int lineOffset, String line) {
3906
StyledTextEvent getLineStyleData(int lineOffset, String line) {
3897
	return sendLineEvent(LineGetStyle, lineOffset, line);
3907
   return sendLineEvent(LineGetStyle, lineOffset, line);
3898
}
3908
}
3899
/**
3909
/**
3900
 * Returns the x, y location of the upper left corner of the character 
3910
 * Returns the x, y location of the upper left corner of the character
3901
 * bounding box at the specified offset in the text. The point is 
3911
 * bounding box at the specified offset in the text. The point is
3902
 * relative to the upper left corner of the widget client area.
3912
 * relative to the upper left corner of the widget client area.
3903
 * <p>
3913
 * <p>
3904
 *
3914
 *
3905
 * @param offset offset relative to the start of the content. 
3915
 * @param offset offset relative to the start of the content.
3906
 * 	0 <= offset <= getCharCount()
3916
 *    0 <= offset <= getCharCount()
3907
 * @return x, y location of the upper left corner of the character 
3917
 * @return x, y location of the upper left corner of the character
3908
 * 	bounding box at the specified offset in the text.
3918
 *    bounding box at the specified offset in the text.
3909
 * @exception SWTException <ul>
3919
 * @exception SWTException <ul>
3910
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3920
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3911
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3921
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3912
 * </ul>
3922
 * </ul>
3913
 * @exception IllegalArgumentException <ul>
3923
 * @exception IllegalArgumentException <ul>
3914
 *   <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li> 
3924
 *   <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
3915
 * </ul>
3925
 * </ul>
3916
 */
3926
 */
3917
public Point getLocationAtOffset(int offset) {
3927
public Point getLocationAtOffset(int offset) {
3918
	checkWidget();
3928
   checkWidget();
3919
	if (offset < 0 || offset > getCharCount()) {
3929
   if (offset < 0 || offset > getCharCount()) {
3920
		SWT.error(SWT.ERROR_INVALID_RANGE);		
3930
      SWT.error(SWT.ERROR_INVALID_RANGE);
3921
	}
3931
   }
3922
	int line = content.getLineAtOffset(offset);
3932
   int line = content.getLineAtOffset(offset);
3923
	int lineOffset = content.getOffsetAtLine(line);
3933
   int lineOffset = content.getOffsetAtLine(line);
3924
	String lineContent = content.getLine(line);
3934
   String lineContent = content.getLine(line);
3925
	int x = getXAtOffset(lineContent, line, offset - lineOffset);
3935
   int x = getXAtOffset(lineContent, line, offset - lineOffset);
3926
	int y = line * lineHeight - verticalScrollOffset;
3936
   int y = line * lineHeight - verticalScrollOffset;
3927
	
3937
3928
	return new Point(x, y);
3938
   return new Point(x, y);
3929
}
3939
}
3930
/**
3940
/**
3931
 * Returns the character offset of the first character of the given line.
3941
 * Returns the character offset of the first character of the given line.
3932
 * <p>
3942
 * <p>
3933
 *
3943
 *
3934
 * @param lineIndex index of the line, 0 based relative to the first 
3944
 * @param lineIndex index of the line, 0 based relative to the first
3935
 * 	line in the content. 0 <= lineIndex < getLineCount(), except
3945
 *    line in the content. 0 <= lineIndex < getLineCount(), except
3936
 * 	lineIndex may always be 0
3946
 *    lineIndex may always be 0
3937
 * @return offset offset of the first character of the line, relative to
3947
 * @return offset offset of the first character of the line, relative to
3938
 * 	the beginning of the document. The first character of the document is
3948
 *    the beginning of the document. The first character of the document is
3939
 *	at offset 0.  
3949
 * at offset 0.
3940
 *  When there are not any lines, getOffsetAtLine(0) is a valid call that 
3950
 *  When there are not any lines, getOffsetAtLine(0) is a valid call that
3941
 * 	answers 0.
3951
 *    answers 0.
3942
 * @exception SWTException <ul>
3952
 * @exception SWTException <ul>
3943
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3953
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3944
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3954
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3945
 * </ul>
3955
 * </ul>
3946
 * @exception IllegalArgumentException <ul>
3956
 * @exception IllegalArgumentException <ul>
3947
 *   <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li> 
3957
 *   <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
3948
 * </ul>
3958
 * </ul>
3949
 * @since 2.0
3959
 * @since 2.0
3950
 */
3960
 */
3951
public int getOffsetAtLine(int lineIndex) {
3961
public int getOffsetAtLine(int lineIndex) {
3952
	checkWidget();
3962
   checkWidget();
3953
	
3963
3954
	if (lineIndex < 0 || 
3964
   if (lineIndex < 0 ||
3955
		(lineIndex > 0 && lineIndex >= logicalContent.getLineCount())) {
3965
      (lineIndex > 0 && lineIndex >= logicalContent.getLineCount())) {
3956
		SWT.error(SWT.ERROR_INVALID_RANGE);		
3966
      SWT.error(SWT.ERROR_INVALID_RANGE);
3957
	}
3967
   }
3958
	return logicalContent.getOffsetAtLine(lineIndex);
3968
   return logicalContent.getOffsetAtLine(lineIndex);
3959
}
3969
}
3960
/**
3970
/**
3961
 * Returns the offset of the character at the given location relative 
3971
 * Returns the offset of the character at the given location relative
3962
 * to the first character in the document.
3972
 * to the first character in the document.
3963
 * The return value reflects the character offset that the caret will
3973
 * The return value reflects the character offset that the caret will
3964
 * be placed at if a mouse click occurred at the specified location.
3974
 * be placed at if a mouse click occurred at the specified location.
Lines 3966-3975 Link Here
3966
 * the returned offset will be behind the character.
3976
 * the returned offset will be behind the character.
3967
 * <p>
3977
 * <p>
3968
 *
3978
 *
3969
 * @param point the origin of character bounding box relative to 
3979
 * @param point the origin of character bounding box relative to
3970
 * 	the origin of the widget client area.
3980
 *    the origin of the widget client area.
3971
 * @return offset of the character at the given location relative 
3981
 * @return offset of the character at the given location relative
3972
 * 	to the first character in the document.
3982
 *    to the first character in the document.
3973
 * @exception SWTException <ul>
3983
 * @exception SWTException <ul>
3974
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3984
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3975
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3985
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
Lines 3980-4127 Link Here
3980
 * </ul>
3990
 * </ul>
3981
 */
3991
 */
3982
public int getOffsetAtLocation(Point point) {
3992
public int getOffsetAtLocation(Point point) {
3983
	checkWidget();
3993
   checkWidget();
3984
	int line;
3994
   int line;
3985
	int lineOffset;
3995
   int lineOffset;
3986
	int offsetInLine;
3996
   int offsetInLine;
3987
	String lineText;
3997
   String lineText;
3988
	
3998
3989
	if (point == null) {
3999
   if (point == null) {
3990
		SWT.error(SWT.ERROR_NULL_ARGUMENT);
4000
      SWT.error(SWT.ERROR_NULL_ARGUMENT);
3991
	}
4001
   }
3992
	// is y above first line or is x before first column?
4002
   // is y above first line or is x before first column?
3993
	if (point.y + verticalScrollOffset < 0 || point.x + horizontalScrollOffset < 0) {
4003
   if (point.y + verticalScrollOffset < 0 || point.x + horizontalScrollOffset < 0) {
3994
		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4004
      SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3995
	}	
4005
   }
3996
	line = (getTopPixel() + point.y) / lineHeight;	
4006
   line = (getTopPixel() + point.y) / lineHeight;
3997
	// does the referenced line exist?
4007
   // does the referenced line exist?
3998
	if (line >= content.getLineCount()) {
4008
   if (line >= content.getLineCount()) {
3999
		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4009
      SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4000
	}	
4010
   }
4001
	lineText = content.getLine(line);
4011
   lineText = content.getLine(line);
4002
	lineOffset = content.getOffsetAtLine(line);	
4012
   lineOffset = content.getOffsetAtLine(line);
4003
	offsetInLine = getOffsetAtX(lineText, lineOffset, point.x);
4013
   offsetInLine = getOffsetAtX(lineText, lineOffset, point.x);
4004
	// is the x position within the line?
4014
   // is the x position within the line?
4005
	if (offsetInLine == -1) {
4015
   if (offsetInLine == -1) {
4006
		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4016
      SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4007
	}
4017
   }
4008
	return lineOffset + offsetInLine;
4018
   return lineOffset + offsetInLine;
4009
}
4019
}
4010
/**
4020
/**
4011
 * Returns the offset at the specified x location in the specified line.
4021
 * Returns the offset at the specified x location in the specified line.
4012
 * <p>
4022
 * <p>
4013
 *
4023
 *
4014
 * @param x	x location of the mouse location
4024
 * @param x x location of the mouse location
4015
 * @param line	line the mouse location is in
4025
 * @param line line the mouse location is in
4016
 * @return the offset at the specified x location in the specified line,
4026
 * @return the offset at the specified x location in the specified line,
4017
 * 	relative to the beginning of the document
4027
 *    relative to the beginning of the document
4018
 */
4028
 */
4019
int getOffsetAtMouseLocation(int x, int line) {
4029
int getOffsetAtMouseLocation(int x, int line) {
4020
	String lineText = content.getLine(line);
4030
   String lineText = content.getLine(line);
4021
	int lineOffset = content.getOffsetAtLine(line);
4031
   int lineOffset = content.getOffsetAtLine(line);
4022
	int offsetInLine = getCaretOffsetAtX(lineText, lineOffset, x);
4032
   int offsetInLine = getCaretOffsetAtX(lineText, lineOffset, x);
4023
	return lineOffset + offsetInLine;
4033
   return lineOffset + offsetInLine;
4024
}
4034
}
4025
/**
4035
/**
4026
 * Returns the offset of the character at the given x location in the line.
4036
 * Returns the offset of the character at the given x location in the line.
4027
 * <p>
4037
 * <p>
4028
 *
4038
 *
4029
 * @param line text of the line to calculate the offset in
4039
 * @param line text of the line to calculate the offset in
4030
 * @param lineOffset offset of the first character in the line. 
4040
 * @param lineOffset offset of the first character in the line.
4031
 * 	0 based from the beginning of the document.
4041
 *    0 based from the beginning of the document.
4032
 * @param lineXOffset x location in the line
4042
 * @param lineXOffset x location in the line
4033
 * @return offset of the character at the x location relative to the start 
4043
 * @return offset of the character at the x location relative to the start
4034
 * 	of the line. -1 if the x location is past the end if the line.
4044
 *    of the line. -1 if the x location is past the end if the line.
4035
 */
4045
 */
4036
int getOffsetAtX(String line, int lineOffset, int lineXOffset) {
4046
int getOffsetAtX(String line, int lineOffset, int lineXOffset) {
4037
	GC gc = getGC();
4047
   GC gc = getGC();
4038
	int offset;	
4048
   int offset;
4039
	
4049
4040
	lineXOffset += (horizontalScrollOffset - leftMargin);
4050
   lineXOffset += (horizontalScrollOffset - leftMargin);
4041
	if (isBidi()) {
4051
   if (isBidi()) {
4042
		StyledTextBidi bidi = getStyledTextBidi(line, lineOffset, gc);
4052
      StyledTextBidi bidi = getStyledTextBidi(line, lineOffset, gc);
4043
		offset = bidi.getOffsetAtX(lineXOffset);
4053
      offset = bidi.getOffsetAtX(lineXOffset);
4044
	}		
4054
   }
4045
	else {
4055
   else {
4046
		StyleRange[] styles = null;
4056
      StyleRange[] styles = null;
4047
		StyledTextEvent event = renderer.getLineStyleData(lineOffset, line);
4057
      StyledTextEvent event = renderer.getLineStyleData(lineOffset, line);
4048
					
4058
4049
		if (event != null) {
4059
      if (event != null) {
4050
			styles = renderer.filterLineStyles(event.styles);
4060
         styles = renderer.filterLineStyles(event.styles);
4051
		}
4061
      }
4052
		int low = -1;
4062
      int low = -1;
4053
		int high = line.length();
4063
      int high = line.length();
4054
		while (high - low > 1) {
4064
      while (high - low > 1) {
4055
			offset = (high + low) / 2;
4065
         offset = (high + low) / 2;
4056
			// Restrict right/high search boundary only if x is within searched text segment.
4066
         // Restrict right/high search boundary only if x is within searched text segment.
4057
			// Fixes 1GL4ZVE.			
4067
         // Fixes 1GL4ZVE.
4058
			if (lineXOffset < renderer.getTextPosition(line, lineOffset, offset + 1, styles, gc)) {
4068
         if (lineXOffset < renderer.getTextPosition(line, lineOffset, offset + 1, styles, gc)) {
4059
				high = offset;			
4069
            high = offset;
4060
			}
4070
         }
4061
			else 
4071
         else
4062
			if (high == line.length() && high - offset == 1) {
4072
         if (high == line.length() && high - offset == 1) {
4063
				// requested x location is past end of line
4073
            // requested x location is past end of line
4064
				high = -1;
4074
            high = -1;
4065
			}
4075
         }
4066
			else {
4076
         else {
4067
				low = offset;
4077
            low = offset;
4068
			}
4078
         }
4069
		}
4079
      }
4070
		offset = high;
4080
      offset = high;
4071
	}
4081
   }
4072
	gc.dispose();	
4082
   gc.dispose();
4073
	return offset;	
4083
   return offset;
4074
}
4084
}
4075
/** 
4085
/**
4076
 * Returns the index of the last partially visible line.
4086
 * Returns the index of the last partially visible line.
4077
 *
4087
 *
4078
 * @return index of the last partially visible line.
4088
 * @return index of the last partially visible line.
4079
 */
4089
 */
4080
int getPartialBottomIndex() {
4090
int getPartialBottomIndex() {
4081
	int partialLineCount = Compatibility.ceil(getClientArea().height, lineHeight);
4091
   int partialLineCount = Compatibility.ceil(getClientArea().height, lineHeight);
4082
	return Math.min(content.getLineCount(), topIndex + partialLineCount) - 1;
4092
   return Math.min(content.getLineCount(), topIndex + partialLineCount) - 1;
4083
}
4093
}
4084
/**
4094
/**
4085
 * Returns the content in the specified range using the platform line 
4095
 * Returns the content in the specified range using the platform line
4086
 * delimiter to separate lines.
4096
 * delimiter to separate lines.
4087
 * <p>
4097
 * <p>
4088
 *
4098
 *
4089
 * @param writer the TextWriter to write line text into
4099
 * @param writer the TextWriter to write line text into
4090
 * @return the content in the specified range using the platform line 
4100
 * @return the content in the specified range using the platform line
4091
 * 	delimiter to separate lines as written by the specified TextWriter.
4101
 *    delimiter to separate lines as written by the specified TextWriter.
4092
 */
4102
 */
4093
String getPlatformDelimitedText(TextWriter writer) {
4103
String getPlatformDelimitedText(TextWriter writer) {
4094
	int end = writer.getStart() + writer.getCharCount();
4104
   int end = writer.getStart() + writer.getCharCount();
4095
	int startLine = logicalContent.getLineAtOffset(writer.getStart());
4105
   int startLine = logicalContent.getLineAtOffset(writer.getStart());
4096
	int endLine = logicalContent.getLineAtOffset(end);
4106
   int endLine = logicalContent.getLineAtOffset(end);
4097
	String endLineText = logicalContent.getLine(endLine);
4107
   String endLineText = logicalContent.getLine(endLine);
4098
	int endLineOffset = logicalContent.getOffsetAtLine(endLine);
4108
   int endLineOffset = logicalContent.getOffsetAtLine(endLine);
4099
	
4109
4100
	for (int i = startLine; i <= endLine; i++) {
4110
   for (int i = startLine; i <= endLine; i++) {
4101
		writer.writeLine(logicalContent.getLine(i), logicalContent.getOffsetAtLine(i));
4111
      writer.writeLine(logicalContent.getLine(i), logicalContent.getOffsetAtLine(i));
4102
		if (i < endLine) {
4112
      if (i < endLine) {
4103
			writer.writeLineDelimiter(PlatformLineDelimiter);
4113
         writer.writeLineDelimiter(PlatformLineDelimiter);
4104
		}
4114
      }
4105
	}
4115
   }
4106
	if (end > endLineOffset + endLineText.length()) {
4116
   if (end > endLineOffset + endLineText.length()) {
4107
		writer.writeLineDelimiter(PlatformLineDelimiter);
4117
      writer.writeLineDelimiter(PlatformLineDelimiter);
4108
	}
4118
   }
4109
	writer.close();
4119
   writer.close();
4110
	return writer.toString();
4120
   return writer.toString();
4111
}
4121
}
4112
/**
4122
/**
4113
 * Returns the selection.
4123
 * Returns the selection.
4114
 * <p>
4124
 * <p>
4115
 * Text selections are specified in terms of caret positions.  In a text
4125
 * Text selections are specified in terms of caret positions.  In a text
4116
 * widget that contains N characters, there are N+1 caret positions, 
4126
 * widget that contains N characters, there are N+1 caret positions,
4117
 * ranging from 0..N
4127
 * ranging from 0..N
4118
 * <p>
4128
 * <p>
4119
 *
4129
 *
4120
 * @return start and end of the selection, x is the offset of the first 
4130
 * @return start and end of the selection, x is the offset of the first
4121
 * 	selected character, y is the offset after the last selected character.
4131
 *    selected character, y is the offset after the last selected character.
4122
 *  The selection values returned are visual (i.e., x will always always be   
4132
 *  The selection values returned are visual (i.e., x will always always be
4123
 *  <= y).  To determine if a selection is right-to-left (RtoL) vs. left-to-right 
4133
 *  <= y).  To determine if a selection is right-to-left (RtoL) vs. left-to-right
4124
 *  (LtoR), compare the caretOffset to the start and end of the selection 
4134
 *  (LtoR), compare the caretOffset to the start and end of the selection
4125
 *  (e.g., caretOffset == start of selection implies that the selection is RtoL).
4135
 *  (e.g., caretOffset == start of selection implies that the selection is RtoL).
4126
 * @see #getSelectionRange
4136
 * @see #getSelectionRange
4127
 * @exception SWTException <ul>
4137
 * @exception SWTException <ul>
Lines 4130-4148 Link Here
4130
 * </ul>
4140
 * </ul>
4131
 */
4141
 */
4132
public Point getSelection() {
4142
public Point getSelection() {
4133
	checkWidget();
4143
   checkWidget();
4134
	return new Point(selection.x, selection.y);
4144
   return new Point(selection.x, selection.y);
4135
}
4145
}
4136
/**
4146
/**
4137
 * Returns the selection.
4147
 * Returns the selection.
4138
 * <p>
4148
 * <p>
4139
 *
4149
 *
4140
 * @return start and length of the selection, x is the offset of the 
4150
 * @return start and length of the selection, x is the offset of the
4141
 * 	first selected character, relative to the first character of the 
4151
 *    first selected character, relative to the first character of the
4142
 * 	widget content. y is the length of the selection. 
4152
 *    widget content. y is the length of the selection.
4143
 *  The selection values returned are visual (i.e., length will always always be   
4153
 *  The selection values returned are visual (i.e., length will always always be
4144
 *  positive).  To determine if a selection is right-to-left (RtoL) vs. left-to-right 
4154
 *  positive).  To determine if a selection is right-to-left (RtoL) vs. left-to-right
4145
 *  (LtoR), compare the caretOffset to the start and end of the selection 
4155
 *  (LtoR), compare the caretOffset to the start and end of the selection
4146
 *  (e.g., caretOffset == start of selection implies that the selection is RtoL).
4156
 *  (e.g., caretOffset == start of selection implies that the selection is RtoL).
4147
 * @exception SWTException <ul>
4157
 * @exception SWTException <ul>
4148
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4158
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
Lines 4150-4157 Link Here
4150
 * </ul>
4160
 * </ul>
4151
 */
4161
 */
4152
public Point getSelectionRange() {
4162
public Point getSelectionRange() {
4153
	checkWidget();
4163
   checkWidget();
4154
	return new Point(selection.x, selection.y - selection.x);
4164
   return new Point(selection.x, selection.y - selection.x);
4155
}
4165
}
4156
/**
4166
/**
4157
 * Returns the receiver's selection background color.
4167
 * Returns the receiver's selection background color.
Lines 4165-4175 Link Here
4165
 * @since 2.1
4175
 * @since 2.1
4166
 */
4176
 */
4167
public Color getSelectionBackground() {
4177
public Color getSelectionBackground() {
4168
	checkWidget();
4178
   checkWidget();
4169
	if (selectionBackground == null) {
4179
   if (selectionBackground == null) {
4170
		return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION);
4180
      return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION);
4171
	}
4181
   }
4172
	return selectionBackground;
4182
   return selectionBackground;
4173
}
4183
}
4174
/**
4184
/**
4175
 * Gets the number of selected characters.
4185
 * Gets the number of selected characters.
Lines 4182-4189 Link Here
4182
 * </ul>
4192
 * </ul>
4183
 */
4193
 */
4184
public int getSelectionCount() {
4194
public int getSelectionCount() {
4185
	checkWidget();
4195
   checkWidget();
4186
	return getSelectionRange().y;
4196
   return getSelectionRange().y;
4187
}
4197
}
4188
/**
4198
/**
4189
 * Returns the receiver's selection foreground color.
4199
 * Returns the receiver's selection foreground color.
Lines 4197-4207 Link Here
4197
 * @since 2.1
4207
 * @since 2.1
4198
 */
4208
 */
4199
public Color getSelectionForeground() {
4209
public Color getSelectionForeground() {
4200
	checkWidget();
4210
   checkWidget();
4201
	if (selectionForeground == null) {
4211
   if (selectionForeground == null) {
4202
		return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT);
4212
      return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT);
4203
	}
4213
   }
4204
	return selectionForeground;
4214
   return selectionForeground;
4205
}
4215
}
4206
/**
4216
/**
4207
 * Returns the selected text.
4217
 * Returns the selected text.
Lines 4214-4342 Link Here
4214
 * </ul>
4224
 * </ul>
4215
 */
4225
 */
4216
public String getSelectionText() {
4226
public String getSelectionText() {
4217
	checkWidget();
4227
   checkWidget();
4218
	return content.getTextRange(selection.x, selection.y - selection.x);
4228
   return content.getTextRange(selection.x, selection.y - selection.x);
4219
}
4229
}
4220
/**
4230
/**
4221
 * Returns the text segments that should be treated as if they 
4231
 * Returns the text segments that should be treated as if they
4222
 * had a different direction than the surrounding text.
4232
 * had a different direction than the surrounding text.
4223
 * <p>
4233
 * <p>
4224
 *
4234
 *
4225
 * @param lineOffset offset of the first character in the line. 
4235
 * @param lineOffset offset of the first character in the line.
4226
 * 	0 based from the beginning of the document.
4236
 *    0 based from the beginning of the document.
4227
 * @param line text of the line to specify bidi segments for
4237
 * @param line text of the line to specify bidi segments for
4228
 * @return text segments that should be treated as if they had a
4238
 * @return text segments that should be treated as if they had a
4229
 * 	different direction than the surrounding text. Only the start 
4239
 *    different direction than the surrounding text. Only the start
4230
 * 	index of a segment is specified, relative to the start of the 
4240
 *    index of a segment is specified, relative to the start of the
4231
 * 	line. Always starts with 0 and ends with the line length. 
4241
 *    line. Always starts with 0 and ends with the line length.
4232
 * @exception IllegalArgumentException <ul>
4242
 * @exception IllegalArgumentException <ul>
4233
 *    <li>ERROR_INVALID_ARGUMENT - if the segment indices returned 
4243
 *    <li>ERROR_INVALID_ARGUMENT - if the segment indices returned
4234
 * 		by the listener do not start with 0, are not in ascending order,
4244
 *       by the listener do not start with 0, are not in ascending order,
4235
 * 		exceed the line length or have duplicates</li>
4245
 *       exceed the line length or have duplicates</li>
4236
 * </ul>
4246
 * </ul>
4237
 */
4247
 */
4238
int [] getBidiSegments(int lineOffset, String line) {
4248
int [] getBidiSegments(int lineOffset, String line) {
4239
	if (isListening(LineGetSegments) == false) {
4249
   if (isListening(LineGetSegments) == false) {
4240
		return getBidiSegmentsCompatibility(line, lineOffset);
4250
      return getBidiSegmentsCompatibility(line, lineOffset);
4241
	}
4251
   }
4242
	StyledTextEvent event = sendLineEvent(LineGetSegments, lineOffset, line);
4252
   StyledTextEvent event = sendLineEvent(LineGetSegments, lineOffset, line);
4243
	int lineLength = line.length();
4253
   int lineLength = line.length();
4244
	int[] segments;
4254
   int[] segments;
4245
	if (event == null || event.segments == null || event.segments.length == 0) {
4255
   if (event == null || event.segments == null || event.segments.length == 0) {
4246
		segments = new int[] {0, lineLength};
4256
      segments = new int[] {0, lineLength};
4247
	}
4257
   }
4248
	else {
4258
   else {
4249
		int segmentCount = event.segments.length;
4259
      int segmentCount = event.segments.length;
4250
		
4260
4251
		// test segment index consistency
4261
      // test segment index consistency
4252
		if (event.segments[0] != 0) {
4262
      if (event.segments[0] != 0) {
4253
			SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4263
         SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4254
		} 	
4264
      }
4255
		for (int i = 1; i < segmentCount; i++) {
4265
      for (int i = 1; i < segmentCount; i++) {
4256
			if (event.segments[i] <= event.segments[i - 1] || event.segments[i] > lineLength) {
4266
         if (event.segments[i] <= event.segments[i - 1] || event.segments[i] > lineLength) {
4257
				SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4267
            SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4258
			} 	
4268
         }
4259
		}
4269
      }
4260
		// ensure that last segment index is line end offset
4270
      // ensure that last segment index is line end offset
4261
		if (event.segments[segmentCount - 1] != lineLength) {
4271
      if (event.segments[segmentCount - 1] != lineLength) {
4262
			segments = new int[segmentCount + 1];
4272
         segments = new int[segmentCount + 1];
4263
			System.arraycopy(event.segments, 0, segments, 0, segmentCount);
4273
         System.arraycopy(event.segments, 0, segments, 0, segmentCount);
4264
			segments[segmentCount] = lineLength;
4274
         segments[segmentCount] = lineLength;
4265
		}
4275
      }
4266
		else {
4276
      else {
4267
			segments = event.segments;
4277
         segments = event.segments;
4268
		}
4278
      }
4269
	}
4279
   }
4270
	return segments;
4280
   return segments;
4271
}
4281
}
4272
/**
4282
/**
4273
 * @see getBidiSegments
4283
 * @see getBidiSegments
4274
 * Supports deprecated setBidiColoring API. Remove when API is removed.
4284
 * Supports deprecated setBidiColoring API. Remove when API is removed.
4275
 */
4285
 */
4276
int [] getBidiSegmentsCompatibility(String line, int lineOffset) {
4286
int [] getBidiSegmentsCompatibility(String line, int lineOffset) {
4277
	StyledTextEvent event;
4287
   StyledTextEvent event;
4278
	StyleRange [] styles = new StyleRange [0];
4288
   StyleRange [] styles = new StyleRange [0];
4279
	int lineLength = line.length();
4289
   int lineLength = line.length();
4280
	if (bidiColoring == false) {
4290
   if (bidiColoring == false) {
4281
		return new int[] {0, lineLength};
4291
      return new int[] {0, lineLength};
4282
	}
4292
   }
4283
	event = renderer.getLineStyleData(lineOffset, line);
4293
   event = renderer.getLineStyleData(lineOffset, line);
4284
	if (event != null) {
4294
   if (event != null) {
4285
		styles = event.styles;
4295
      styles = event.styles;
4286
	}
4296
   }
4287
	if (styles.length == 0) {
4297
   if (styles.length == 0) {
4288
		return new int[] {0, lineLength};
4298
      return new int[] {0, lineLength};
4289
	}
4299
   }
4290
	int k=0, count = 1;
4300
   int k=0, count = 1;
4291
	while (k < styles.length && styles[k].start == 0 && styles[k].length == lineLength) {
4301
   while (k < styles.length && styles[k].start == 0 && styles[k].length == lineLength) {
4292
		k++;
4302
      k++;
4293
	}
4303
   }
4294
	int[] offsets = new int[(styles.length - k) * 2 + 2];
4304
   int[] offsets = new int[(styles.length - k) * 2 + 2];
4295
	for (int i = k; i < styles.length; i++) {
4305
   for (int i = k; i < styles.length; i++) {
4296
		StyleRange style = styles[i];
4306
      StyleRange style = styles[i];
4297
		int styleLineStart = Math.max(style.start - lineOffset, 0);
4307
      int styleLineStart = Math.max(style.start - lineOffset, 0);
4298
		int styleLineEnd = Math.max(style.start + style.length - lineOffset, styleLineStart);
4308
      int styleLineEnd = Math.max(style.start + style.length - lineOffset, styleLineStart);
4299
		styleLineEnd = Math.min (styleLineEnd, line.length ());
4309
      styleLineEnd = Math.min (styleLineEnd, line.length ());
4300
		if (i > 0 && count > 1 &&
4310
      if (i > 0 && count > 1 &&
4301
			((styleLineStart >= offsets[count-2] && styleLineStart <= offsets[count-1]) ||
4311
         ((styleLineStart >= offsets[count-2] && styleLineStart <= offsets[count-1]) ||
4302
			 (styleLineEnd >= offsets[count-2] && styleLineEnd <= offsets[count-1])) &&
4312
          (styleLineEnd >= offsets[count-2] && styleLineEnd <= offsets[count-1])) &&
4303
			 style.similarTo(styles[i-1])) {
4313
          style.similarTo(styles[i-1])) {
4304
			offsets[count-2] = Math.min(offsets[count-2], styleLineStart);
4314
         offsets[count-2] = Math.min(offsets[count-2], styleLineStart);
4305
			offsets[count-1] = Math.max(offsets[count-1], styleLineEnd);
4315
         offsets[count-1] = Math.max(offsets[count-1], styleLineEnd);
4306
		} else {
4316
      } else {
4307
			if (styleLineStart > offsets[count - 1]) {
4317
         if (styleLineStart > offsets[count - 1]) {
4308
				offsets[count] = styleLineStart;
4318
            offsets[count] = styleLineStart;
4309
				count++;
4319
            count++;
4310
			}
4320
         }
4311
			offsets[count] = styleLineEnd;
4321
         offsets[count] = styleLineEnd;
4312
			count++;
4322
         count++;
4313
		}
4323
      }
4314
	}
4324
   }
4315
	// add offset for last non-colored segment in line, if any
4325
   // add offset for last non-colored segment in line, if any
4316
	if (lineLength > offsets[count-1]) {
4326
   if (lineLength > offsets[count-1]) {
4317
		offsets [count] = lineLength;
4327
      offsets [count] = lineLength;
4318
		count++;
4328
      count++;
4319
	}		
4329
   }
4320
	if (count == offsets.length) {
4330
   if (count == offsets.length) {
4321
		return offsets;
4331
      return offsets;
4322
	}
4332
   }
4323
	int [] result = new int [count];
4333
   int [] result = new int [count];
4324
	System.arraycopy (offsets, 0, result, 0, count);
4334
   System.arraycopy (offsets, 0, result, 0, count);
4325
	return result;
4335
   return result;
4326
}
4336
}
4327
/**
4337
/**
4328
 * Returns the style range at the given offset.
4338
 * Returns the style range at the given offset.
4329
 * Returns null if a LineStyleListener has been set or if a style is not set
4339
 * Returns null if a LineStyleListener has been set or if a style is not set
4330
 * for the offset. 
4340
 * for the offset.
4331
 * Should not be called if a LineStyleListener has been set since the 
4341
 * Should not be called if a LineStyleListener has been set since the
4332
 * listener maintains the styles.
4342
 * listener maintains the styles.
4333
 * <p>
4343
 * <p>
4334
 *
4344
 *
4335
 * @param offset the offset to return the style for. 
4345
 * @param offset the offset to return the style for.
4336
 * 	0 <= offset < getCharCount() must be true.
4346
 *    0 <= offset < getCharCount() must be true.
4337
 * @return a StyleRange with start == offset and length == 1, indicating
4347
 * @return a StyleRange with start == offset and length == 1, indicating
4338
 * 	the style at the given offset. null if a LineStyleListener has been set 
4348
 *    the style at the given offset. null if a LineStyleListener has been set
4339
 * 	or if a style is not set for the given offset.
4349
 *    or if a style is not set for the given offset.
4340
 * @exception SWTException <ul>
4350
 * @exception SWTException <ul>
4341
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4351
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4342
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4352
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
Lines 4346-4364 Link Here
4346
 * </ul>
4356
 * </ul>
4347
 */
4357
 */
4348
public StyleRange getStyleRangeAtOffset(int offset) {
4358
public StyleRange getStyleRangeAtOffset(int offset) {
4349
	checkWidget();
4359
   checkWidget();
4350
	if (offset < 0 || offset >= getCharCount()) {
4360
   if (offset < 0 || offset >= getCharCount()) {
4351
		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4361
      SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4352
	} 	
4362
   }
4353
	if (userLineStyle == false) {
4363
   if (userLineStyle == false) {
4354
		return defaultLineStyler.getStyleRangeAtOffset(offset);
4364
      return defaultLineStyler.getStyleRangeAtOffset(offset);
4355
	} 
4365
   }
4356
	return null;
4366
   return null;
4357
}
4367
}
4358
/**
4368
/**
4359
 * Returns the styles.
4369
 * Returns the styles.
4360
 * Returns an empty array if a LineStyleListener has been set. 
4370
 * Returns an empty array if a LineStyleListener has been set.
4361
 * Should not be called if a LineStyleListener has been set since the 
4371
 * Should not be called if a LineStyleListener has been set since the
4362
 * listener maintains the styles.
4372
 * listener maintains the styles.
4363
 * <p>
4373
 * <p>
4364
 *
4374
 *
Lines 4369-4415 Link Here
4369
 * </ul>
4379
 * </ul>
4370
 */
4380
 */
4371
public StyleRange [] getStyleRanges() {
4381
public StyleRange [] getStyleRanges() {
4372
	checkWidget();
4382
   checkWidget();
4373
	StyleRange styles[];
4383
   StyleRange styles[];
4374
	
4384
4375
	if (userLineStyle == false) {
4385
   if (userLineStyle == false) {
4376
		styles = defaultLineStyler.getStyleRanges();
4386
      styles = defaultLineStyler.getStyleRanges();
4377
	}
4387
   }
4378
	else {
4388
   else {
4379
		styles = new StyleRange[0];
4389
      styles = new StyleRange[0];
4380
	}
4390
   }
4381
	return styles;
4391
   return styles;
4382
}
4392
}
4383
/**
4393
/**
4384
 * Returns a StyledTextBidi object for the specified line.
4394
 * Returns a StyledTextBidi object for the specified line.
4385
 * <p>
4395
 * <p>
4386
 * 
4396
 *
4387
 * @param lineText the line that the StyledTextBidi object should 
4397
 * @param lineText the line that the StyledTextBidi object should
4388
 * 	work on.
4398
 *    work on.
4389
 * @param lineOffset offset of the beginning of the line, relative 
4399
 * @param lineOffset offset of the beginning of the line, relative
4390
 * 	to the beginning of the document
4400
 *    to the beginning of the document
4391
 * @param gc GC to use when creating a new StyledTextBidi object.
4401
 * @param gc GC to use when creating a new StyledTextBidi object.
4392
 * @return a StyledTextBidi object for the specified line.
4402
 * @return a StyledTextBidi object for the specified line.
4393
 */
4403
 */
4394
StyledTextBidi getStyledTextBidi(String lineText, int lineOffset, GC gc) {
4404
StyledTextBidi getStyledTextBidi(String lineText, int lineOffset, GC gc) {
4395
	return getStyledTextBidi(lineText, lineOffset, gc, null);
4405
   return getStyledTextBidi(lineText, lineOffset, gc, null);
4396
}
4406
}
4397
/**
4407
/**
4398
 * Returns a StyledTextBidi object for the specified line.
4408
 * Returns a StyledTextBidi object for the specified line.
4399
 * <p>
4409
 * <p>
4400
 * 
4410
 *
4401
 * @param lineText the line that the StyledTextBidi object should 
4411
 * @param lineText the line that the StyledTextBidi object should
4402
 * 	work on.
4412
 *    work on.
4403
 * @param lineOffset offset of the beginning of the line, relative 
4413
 * @param lineOffset offset of the beginning of the line, relative
4404
 * 	to the beginning of the document
4414
 *    to the beginning of the document
4405
 * @param gc GC to use when creating a new StyledTextBidi object.
4415
 * @param gc GC to use when creating a new StyledTextBidi object.
4406
 * @param styles StyleRanges to use when creating a new StyledTextBidi 
4416
 * @param styles StyleRanges to use when creating a new StyledTextBidi
4407
 * 	object.
4417
 *    object.
4408
 * @return a StyledTextBidi object for the specified line.
4418
 * @return a StyledTextBidi object for the specified line.
4409
 */
4419
 */
4410
StyledTextBidi getStyledTextBidi(String lineText, int lineOffset, GC gc, StyleRange[] styles) {
4420
StyledTextBidi getStyledTextBidi(String lineText, int lineOffset, GC gc, StyleRange[] styles) {
4411
	return renderer.getStyledTextBidi(lineText, lineOffset, gc, styles);
4421
   return renderer.getStyledTextBidi(lineText, lineOffset, gc, styles);
4412
}		
4422
}
4413
/**
4423
/**
4414
 * Returns the tab width measured in characters.
4424
 * Returns the tab width measured in characters.
4415
 *
4425
 *
Lines 4420-4427 Link Here
4420
 * </ul>
4430
 * </ul>
4421
 */
4431
 */
4422
public int getTabs() {
4432
public int getTabs() {
4423
	checkWidget();
4433
   checkWidget();
4424
	return tabLength;
4434
   return tabLength;
4425
}
4435
}
4426
/**
4436
/**
4427
 * Returns a copy of the widget content.
4437
 * Returns a copy of the widget content.
Lines 4434-4448 Link Here
4434
 * </ul>
4444
 * </ul>
4435
 */
4445
 */
4436
public String getText() {
4446
public String getText() {
4437
	checkWidget();
4447
   checkWidget();
4438
	return content.getTextRange(0, getCharCount());
4448
   return content.getTextRange(0, getCharCount());
4439
}	
4449
}
4440
/**
4450
/**
4441
 * Returns the widget content between the two offsets.
4451
 * Returns the widget content between the two offsets.
4442
 * <p>
4452
 * <p>
4443
 *
4453
 *
4444
 * @param start offset of the first character in the returned String
4454
 * @param start offset of the first character in the returned String
4445
 * @param end offset of the last character in the returned String 
4455
 * @param end offset of the last character in the returned String
4446
 * @return widget content starting at start and ending at end
4456
 * @return widget content starting at start and ending at end
4447
 * @see #getTextRange(int,int)
4457
 * @see #getTextRange(int,int)
4448
 * @exception SWTException <ul>
4458
 * @exception SWTException <ul>
Lines 4450-4494 Link Here
4450
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4460
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4451
 * </ul>
4461
 * </ul>
4452
 * @exception IllegalArgumentException <ul>
4462
 * @exception IllegalArgumentException <ul>
4453
 *   <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li> 
4463
 *   <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
4454
 * </ul>
4464
 * </ul>
4455
 */
4465
 */
4456
public String getText(int start, int end) {
4466
public String getText(int start, int end) {
4457
	checkWidget();
4467
   checkWidget();
4458
	int contentLength = getCharCount();
4468
   int contentLength = getCharCount();
4459
	
4469
4460
	if (start < 0 || start >= contentLength || end < 0 || end >= contentLength || start > end) {
4470
   if (start < 0 || start >= contentLength || end < 0 || end >= contentLength || start > end) {
4461
		SWT.error(SWT.ERROR_INVALID_RANGE);
4471
      SWT.error(SWT.ERROR_INVALID_RANGE);
4462
	}	
4472
   }
4463
	return content.getTextRange(start, end - start + 1);
4473
   return content.getTextRange(start, end - start + 1);
4464
}
4474
}
4465
/**
4475
/**
4466
 * Returns the widget content starting at start for length characters.
4476
 * Returns the widget content starting at start for length characters.
4467
 * <p>
4477
 * <p>
4468
 *
4478
 *
4469
 * @param start offset of the first character in the returned String
4479
 * @param start offset of the first character in the returned String
4470
 * @param length number of characters to return 
4480
 * @param length number of characters to return
4471
 * @return widget content starting at start and extending length characters.
4481
 * @return widget content starting at start and extending length characters.
4472
 * @exception SWTException <ul>
4482
 * @exception SWTException <ul>
4473
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4483
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4474
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4484
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4475
 * </ul>
4485
 * </ul>
4476
 * @exception IllegalArgumentException <ul>
4486
 * @exception IllegalArgumentException <ul>
4477
 *   <li>ERROR_INVALID_RANGE when start and/or length are outside the widget content</li> 
4487
 *   <li>ERROR_INVALID_RANGE when start and/or length are outside the widget content</li>
4478
 * </ul>
4488
 * </ul>
4479
 */
4489
 */
4480
public String getTextRange(int start, int length) {
4490
public String getTextRange(int start, int length) {
4481
	checkWidget();
4491
   checkWidget();
4482
	int contentLength = getCharCount();
4492
   int contentLength = getCharCount();
4483
	int end = start + length;
4493
   int end = start + length;
4484
	
4494
4485
	if (start > end || start < 0 || end > contentLength) {
4495
   if (start > end || start < 0 || end > contentLength) {
4486
		SWT.error(SWT.ERROR_INVALID_RANGE);
4496
      SWT.error(SWT.ERROR_INVALID_RANGE);
4487
	}	
4497
   }
4488
	return content.getTextRange(start, length);
4498
   return content.getTextRange(start, length);
4489
}
4499
}
4490
/**
4500
/**
4491
 * Gets the text limit.  The text limit specifies the amount of text that the user 
4501
 * Gets the text limit.  The text limit specifies the amount of text that the user
4492
 * can type into the widget.
4502
 * can type into the widget.
4493
 * <p>
4503
 * <p>
4494
 *
4504
 *
Lines 4498-4548 Link Here
4498
 * </ul>
4508
 * </ul>
4499
 */
4509
 */
4500
public int getTextLimit() {
4510
public int getTextLimit() {
4501
	checkWidget();
4511
   checkWidget();
4502
	
4512
4503
	return textLimit;
4513
   return textLimit;
4504
}
4514
}
4505
/**
4515
/**
4506
 * Returns the x position of the character at the specified offset 
4516
 * Returns the x position of the character at the specified offset
4507
 * relative to the first character in the line.
4517
 * relative to the first character in the line.
4508
 * Expands tabs to tab stops using the widget tab width.
4518
 * Expands tabs to tab stops using the widget tab width.
4509
 * <p>
4519
 * <p>
4510
 *
4520
 *
4511
 * @param line line to be measured.
4521
 * @param line line to be measured.
4512
 * @param lineIndex index of the line relative to the first kine of the 
4522
 * @param lineIndex index of the line relative to the first kine of the
4513
 * 	document
4523
 *    document
4514
 * @param length number of characters to measure. Tabs are counted 
4524
 * @param length number of characters to measure. Tabs are counted
4515
 * 	as one character in this parameter.
4525
 *    as one character in this parameter.
4516
 * @param gc GC to use for measuring text
4526
 * @param gc GC to use for measuring text
4517
 * @return x position of the character at the specified offset 
4527
 * @return x position of the character at the specified offset
4518
 * 	with tabs expanded to tab stops. 0 if the length is outside the 
4528
 *    with tabs expanded to tab stops. 0 if the length is outside the
4519
 * 	specified text.
4529
 *    specified text.
4520
 */
4530
 */
4521
int getTextPosition(String line, int lineIndex, int length, GC gc) {
4531
int getTextPosition(String line, int lineIndex, int length, GC gc) {
4522
	int lineOffset = content.getOffsetAtLine(lineIndex);
4532
   int lineOffset = content.getOffsetAtLine(lineIndex);
4523
	int lineLength = line.length();
4533
   int lineLength = line.length();
4524
	int width;
4534
   int width;
4525
	if (lineLength == 0 || length > lineLength) {
4535
   if (lineLength == 0 || length > lineLength) {
4526
		return 0;
4536
      return 0;
4527
	}	
4537
   }
4528
	if (isBidi()) {
4538
   if (isBidi()) {
4529
		StyledTextBidi bidi = getStyledTextBidi(line, lineOffset, gc, null);
4539
      StyledTextBidi bidi = getStyledTextBidi(line, lineOffset, gc, null);
4530
		width = getBidiTextPosition(line, length, bidi);
4540
      width = getBidiTextPosition(line, length, bidi);
4531
	}
4541
   }
4532
	else {
4542
   else {
4533
		StyledTextEvent event = renderer.getLineStyleData(lineOffset, line);
4543
      StyledTextEvent event = renderer.getLineStyleData(lineOffset, line);
4534
		StyleRange[] styles = null;
4544
      StyleRange[] styles = null;
4535
		if (event != null) {
4545
      if (event != null) {
4536
			styles = renderer.filterLineStyles(event.styles);
4546
         styles = renderer.filterLineStyles(event.styles);
4537
		}
4547
      }
4538
		width = renderer.getTextPosition(line, lineOffset, length, styles, gc);
4548
      width = renderer.getTextPosition(line, lineOffset, length, styles, gc);
4539
	}
4549
   }
4540
	return width;
4550
   return width;
4541
}
4551
}
4542
/**
4552
/**
4543
 * Gets the top index.  The top index is the index of the fully visible line that
4553
 * Gets the top index.  The top index is the index of the fully visible line that
4544
 * is currently at the top of the widget or the topmost partially visible line if 
4554
 * is currently at the top of the widget or the topmost partially visible line if
4545
 * no line is fully visible. 
4555
 * no line is fully visible.
4546
 * The top index changes when the widget is scrolled. Indexing is zero based.
4556
 * The top index changes when the widget is scrolled. Indexing is zero based.
4547
 * <p>
4557
 * <p>
4548
 *
4558
 *
Lines 4553-4572 Link Here
4553
 * </ul>
4563
 * </ul>
4554
 */
4564
 */
4555
public int getTopIndex() {
4565
public int getTopIndex() {
4556
	checkWidget();
4566
   checkWidget();
4557
	int logicalTopIndex = topIndex;
4567
   int logicalTopIndex = topIndex;
4558
	
4568
4559
	if (wordWrap) {
4569
   if (wordWrap) {
4560
		int visualLineOffset = content.getOffsetAtLine(topIndex);
4570
      int visualLineOffset = content.getOffsetAtLine(topIndex);
4561
		logicalTopIndex = logicalContent.getLineAtOffset(visualLineOffset);
4571
      logicalTopIndex = logicalContent.getLineAtOffset(visualLineOffset);
4562
	}
4572
   }
4563
	return logicalTopIndex;
4573
   return logicalTopIndex;
4564
}
4574
}
4565
/**
4575
/**
4566
 * Gets the top pixel.  The top pixel is the pixel position of the line that is 
4576
 * Gets the top pixel.  The top pixel is the pixel position of the line that is
4567
 * currently at the top of the widget.The text widget can be scrolled by pixels 
4577
 * currently at the top of the widget.The text widget can be scrolled by pixels
4568
 * by dragging the scroll thumb so that a partial line may be displayed at the top 
4578
 * by dragging the scroll thumb so that a partial line may be displayed at the top
4569
 * the widget.  The top pixel changes when the widget is scrolled.  The top pixel 
4579
 * the widget.  The top pixel changes when the widget is scrolled.  The top pixel
4570
 * does not include the widget trimming.
4580
 * does not include the widget trimming.
4571
 * <p>
4581
 * <p>
4572
 *
4582
 *
Lines 4577-4615 Link Here
4577
 * </ul>
4587
 * </ul>
4578
 */
4588
 */
4579
public int getTopPixel() {
4589
public int getTopPixel() {
4580
	checkWidget();
4590
   checkWidget();
4581
	return verticalScrollOffset;
4591
   return verticalScrollOffset;
4582
}
4592
}
4583
/** 
4593
/**
4584
 * Returns the vertical scroll increment.
4594
 * Returns the vertical scroll increment.
4585
 * <p>
4595
 * <p>
4586
 *
4596
 *
4587
 * @return vertical scroll increment.
4597
 * @return vertical scroll increment.
4588
 */
4598
 */
4589
int getVerticalIncrement() {
4599
int getVerticalIncrement() {
4590
	return lineHeight;
4600
   return lineHeight;
4591
}
4601
}
4592
/**
4602
/**
4593
 * Returns the index of the line the caret is on.
4603
 * Returns the index of the line the caret is on.
4594
 * When in word wrap mode and at the end of one wrapped line/ 
4604
 * When in word wrap mode and at the end of one wrapped line/
4595
 * beginning of the continuing wrapped line the caret offset
4605
 * beginning of the continuing wrapped line the caret offset
4596
 * is not sufficient to determine the caret line.
4606
 * is not sufficient to determine the caret line.
4597
 * 
4607
 *
4598
 * @return the index of the line the caret is on.
4608
 * @return the index of the line the caret is on.
4599
 */
4609
 */
4600
int getCaretLine() {
4610
int getCaretLine() {
4601
	int caretLine = content.getLineAtOffset(caretOffset);
4611
   int caretLine = content.getLineAtOffset(caretOffset);
4602
	int leftColumnX = 0;
4612
   int leftColumnX = 0;
4603
	
4613
4604
	if (isBidi()) {
4614
   if (isBidi()) {
4605
		leftColumnX = XINSET;
4615
      leftColumnX = XINSET;
4606
	}	
4616
   }
4607
	if (wordWrap && columnX <= leftColumnX &&
4617
   if (wordWrap && columnX <= leftColumnX &&
4608
		caretLine < content.getLineCount() - 1 &&
4618
      caretLine < content.getLineCount() - 1 &&
4609
		caretOffset == content.getOffsetAtLine(caretLine + 1)) {
4619
      caretOffset == content.getOffsetAtLine(caretLine + 1)) {
4610
		caretLine++;
4620
      caretLine++;
4611
	}
4621
   }
4612
	return caretLine;
4622
   return caretLine;
4613
}
4623
}
4614
/**
4624
/**
4615
 * Returns the offset of the character after the word at the specified
4625
 * Returns the offset of the character after the word at the specified
Lines 4623-4664 Link Here
4623
 * </p>
4633
 * </p>
4624
 * <p>
4634
 * <p>
4625
 * Space characters ' ' (ASCII 20) are special as they are treated as
4635
 * Space characters ' ' (ASCII 20) are special as they are treated as
4626
 * part of the word leading up to the space character.  Line breaks are 
4636
 * part of the word leading up to the space character.  Line breaks are
4627
 * treated as one word.
4637
 * treated as one word.
4628
 * </p>
4638
 * </p>
4629
 */
4639
 */
4630
int getWordEnd(int offset) {
4640
int getWordEnd(int offset) {
4631
	int line = logicalContent.getLineAtOffset(offset);
4641
   int line = logicalContent.getLineAtOffset(offset);
4632
	int lineOffset = logicalContent.getOffsetAtLine(line);
4642
   int lineOffset = logicalContent.getOffsetAtLine(line);
4633
	String lineText = logicalContent.getLine(line);
4643
   String lineText = logicalContent.getLine(line);
4634
	int lineLength = lineText.length();
4644
   int lineLength = lineText.length();
4635
	
4645
4636
	if (offset >= getCharCount()) {
4646
   if (offset >= getCharCount()) {
4637
		return offset;
4647
      return offset;
4638
	}
4648
   }
4639
	if (offset == lineOffset + lineLength) {
4649
   if (offset == lineOffset + lineLength) {
4640
		line++;
4650
      line++;
4641
		offset = logicalContent.getOffsetAtLine(line);
4651
      offset = logicalContent.getOffsetAtLine(line);
4642
	}
4652
   }
4643
	else {
4653
   else {
4644
		offset -= lineOffset;
4654
      offset -= lineOffset;
4645
		char ch = lineText.charAt(offset);
4655
      char ch = lineText.charAt(offset);
4646
		boolean letterOrDigit = Compatibility.isLetterOrDigit(ch);
4656
      boolean letterOrDigit = Compatibility.isLetterOrDigit(ch);
4647
		while (offset < lineLength - 1 && Compatibility.isLetterOrDigit(ch) == letterOrDigit) {
4657
      while (offset < lineLength - 1 && Compatibility.isLetterOrDigit(ch) == letterOrDigit) {
4648
			offset++;
4658
         offset++;
4649
			ch = lineText.charAt(offset);
4659
         ch = lineText.charAt(offset);
4650
		}
4660
      }
4651
		// skip over trailing whitespace
4661
      // skip over trailing whitespace
4652
		while (offset < lineLength - 1 && Compatibility.isSpaceChar(ch)) {
4662
      while (offset < lineLength - 1 && Compatibility.isSpaceChar(ch)) {
4653
			offset++;
4663
         offset++;
4654
			ch = lineText.charAt(offset);		
4664
         ch = lineText.charAt(offset);
4655
		}
4665
      }
4656
		if (offset == lineLength - 1 && (Compatibility.isLetterOrDigit(ch) == letterOrDigit || Compatibility.isSpaceChar(ch))) {
4666
      if (offset == lineLength - 1 && (Compatibility.isLetterOrDigit(ch) == letterOrDigit || Compatibility.isSpaceChar(ch))) {
4657
			offset++;
4667
         offset++;
4658
		}
4668
      }
4659
		offset += lineOffset;
4669
      offset += lineOffset;
4660
	}
4670
   }
4661
	return offset;
4671
   return offset;
4662
}
4672
}
4663
/**
4673
/**
4664
 * Returns the offset of the character after the word at the specified
4674
 * Returns the offset of the character after the word at the specified
Lines 4671-4708 Link Here
4671
 * </ul>
4681
 * </ul>
4672
 * </p>
4682
 * </p>
4673
 * <p>
4683
 * <p>
4674
 * Spaces are ignored and do not represent a word.  Line breaks are treated 
4684
 * Spaces are ignored and do not represent a word.  Line breaks are treated
4675
 * as one word.
4685
 * as one word.
4676
 * </p>
4686
 * </p>
4677
 */
4687
 */
4678
int getWordEndNoSpaces(int offset) {
4688
int getWordEndNoSpaces(int offset) {
4679
	int line = logicalContent.getLineAtOffset(offset);
4689
   int line = logicalContent.getLineAtOffset(offset);
4680
	int lineOffset = logicalContent.getOffsetAtLine(line);
4690
   int lineOffset = logicalContent.getOffsetAtLine(line);
4681
	String lineText = logicalContent.getLine(line);
4691
   String lineText = logicalContent.getLine(line);
4682
	int lineLength = lineText.length();
4692
   int lineLength = lineText.length();
4683
	
4693
4684
	if (offset >= getCharCount()) {
4694
   if (offset >= getCharCount()) {
4685
		return offset;
4695
      return offset;
4686
	}
4696
   }
4687
	if (offset == lineOffset + lineLength) {
4697
   if (offset == lineOffset + lineLength) {
4688
		line++;
4698
      line++;
4689
		offset = logicalContent.getOffsetAtLine(line);
4699
      offset = logicalContent.getOffsetAtLine(line);
4690
	}
4700
   }
4691
	else {
4701
   else {
4692
		offset -= lineOffset;
4702
      offset -= lineOffset;
4693
		char ch = lineText.charAt(offset);
4703
      char ch = lineText.charAt(offset);
4694
		boolean letterOrDigit = Compatibility.isLetterOrDigit(ch);
4704
      boolean letterOrDigit = Compatibility.isLetterOrDigit(ch);
4695
		
4705
4696
		while (offset < lineLength - 1 && Compatibility.isLetterOrDigit(ch) == letterOrDigit && Compatibility.isSpaceChar(ch) == false) {
4706
      while (offset < lineLength - 1 && Compatibility.isLetterOrDigit(ch) == letterOrDigit && Compatibility.isSpaceChar(ch) == false) {
4697
			offset++;
4707
         offset++;
4698
			ch = lineText.charAt(offset);
4708
         ch = lineText.charAt(offset);
4699
		}
4709
      }
4700
		if (offset == lineLength - 1 && Compatibility.isLetterOrDigit(ch) == letterOrDigit && Compatibility.isSpaceChar(ch) == false) {
4710
      if (offset == lineLength - 1 && Compatibility.isLetterOrDigit(ch) == letterOrDigit && Compatibility.isSpaceChar(ch) == false) {
4701
			offset++;
4711
         offset++;
4702
		}
4712
      }
4703
		offset += lineOffset;
4713
      offset += lineOffset;
4704
	}
4714
   }
4705
	return offset;
4715
   return offset;
4706
}
4716
}
4707
/**
4717
/**
4708
 * Returns the start offset of the word at the specified offset.
4718
 * Returns the start offset of the word at the specified offset.
Lines 4715-4758 Link Here
4715
 * </p>
4725
 * </p>
4716
 * <p>
4726
 * <p>
4717
 * Space characters ' ' (ASCII 20) are special as they are treated as
4727
 * Space characters ' ' (ASCII 20) are special as they are treated as
4718
 * part of the word leading up to the space character.  Line breaks are treated 
4728
 * part of the word leading up to the space character.  Line breaks are treated
4719
 * as one word.
4729
 * as one word.
4720
 * </p>
4730
 * </p>
4721
 */
4731
 */
4722
int getWordStart(int offset) {
4732
int getWordStart(int offset) {
4723
	int line = logicalContent.getLineAtOffset(offset);
4733
   int line = logicalContent.getLineAtOffset(offset);
4724
	int lineOffset = logicalContent.getOffsetAtLine(line);
4734
   int lineOffset = logicalContent.getOffsetAtLine(line);
4725
	String lineText = logicalContent.getLine(line);
4735
   String lineText = logicalContent.getLine(line);
4726
	
4736
4727
	if (offset <= 0) {
4737
   if (offset <= 0) {
4728
		return offset;
4738
      return offset;
4729
	}
4739
   }
4730
	if (offset == lineOffset) {
4740
   if (offset == lineOffset) {
4731
		line--;
4741
      line--;
4732
		lineText = logicalContent.getLine(line);
4742
      lineText = logicalContent.getLine(line);
4733
		offset = logicalContent.getOffsetAtLine(line) + lineText.length();
4743
      offset = logicalContent.getOffsetAtLine(line) + lineText.length();
4734
	}
4744
   }
4735
	else {
4745
   else {
4736
		char ch;
4746
      char ch;
4737
		boolean letterOrDigit;
4747
      boolean letterOrDigit;
4738
		
4748
4739
		offset -= lineOffset;
4749
      offset -= lineOffset;
4740
		// skip over trailing whitespace
4750
      // skip over trailing whitespace
4741
		do {		
4751
      do {
4742
			offset--;
4752
         offset--;
4743
			ch = lineText.charAt(offset);
4753
         ch = lineText.charAt(offset);
4744
		} while (offset > 0 && Compatibility.isSpaceChar(ch));
4754
      } while (offset > 0 && Compatibility.isSpaceChar(ch));
4745
		letterOrDigit = Compatibility.isLetterOrDigit(ch);
4755
      letterOrDigit = Compatibility.isLetterOrDigit(ch);
4746
		while (offset > 0 && Compatibility.isLetterOrDigit(ch) == letterOrDigit && Compatibility.isSpaceChar(ch) == false) {
4756
      while (offset > 0 && Compatibility.isLetterOrDigit(ch) == letterOrDigit && Compatibility.isSpaceChar(ch) == false) {
4747
			offset--;
4757
         offset--;
4748
			ch = lineText.charAt(offset);
4758
         ch = lineText.charAt(offset);
4749
		}
4759
      }
4750
		if (offset > 0 || Compatibility.isLetterOrDigit(ch) != letterOrDigit) {
4760
      if (offset > 0 || Compatibility.isLetterOrDigit(ch) != letterOrDigit) {
4751
			offset++;
4761
         offset++;
4752
		}
4762
      }
4753
		offset += lineOffset;
4763
      offset += lineOffset;
4754
	}
4764
   }
4755
	return offset;
4765
   return offset;
4756
}
4766
}
4757
/**
4767
/**
4758
 * Returns whether the widget wraps lines.
4768
 * Returns whether the widget wraps lines.
Lines 4762-4771 Link Here
4762
 * @since 2.0
4772
 * @since 2.0
4763
 */
4773
 */
4764
public boolean getWordWrap() {
4774
public boolean getWordWrap() {
4765
	checkWidget();
4775
   checkWidget();
4766
	return wordWrap;
4776
   return wordWrap;
4767
}
4777
}
4768
/** 
4778
/**
4769
 * Returns the x location of the character at the give offset in the line.
4779
 * Returns the x location of the character at the give offset in the line.
4770
 * <b>NOTE:</b> Does not return correct values for true italic fonts (vs. slanted fonts).
4780
 * <b>NOTE:</b> Does not return correct values for true italic fonts (vs. slanted fonts).
4771
 * <p>
4781
 * <p>
Lines 4773-4796 Link Here
4773
 * @return x location of the character at the given offset in the line.
4783
 * @return x location of the character at the given offset in the line.
4774
 */
4784
 */
4775
int getXAtOffset(String line, int lineIndex, int lineOffset) {
4785
int getXAtOffset(String line, int lineIndex, int lineOffset) {
4776
	int x;
4786
   int x;
4777
	if (lineOffset == 0 && isBidi() == false) {
4787
   if (lineOffset == 0 && isBidi() == false) {
4778
		x = leftMargin;
4788
      x = leftMargin;
4779
	}
4789
   }
4780
	else {
4790
   else {
4781
		GC gc = getGC();		
4791
      GC gc = getGC();
4782
		x = getTextPosition(line, lineIndex, Math.min(line.length(), lineOffset), gc) + leftMargin;
4792
      x = getTextPosition(line, lineIndex, Math.min(line.length(), lineOffset), gc) + leftMargin;
4783
		gc.dispose();
4793
      gc.dispose();
4784
		if (lineOffset > line.length()) {
4794
      if (lineOffset > line.length()) {
4785
			// offset is not on the line. return an x location one character 
4795
         // offset is not on the line. return an x location one character
4786
			// after the line to indicate the line delimiter.
4796
         // after the line to indicate the line delimiter.
4787
			x += lineEndSpaceWidth;
4797
         x += lineEndSpaceWidth;
4788
		}
4798
      }
4789
	}
4799
   }
4790
	return x - horizontalScrollOffset;
4800
   return x - horizontalScrollOffset;
4791
}
4801
}
4792
/** 
4802
/**
4793
 * Inserts a string.  The old selection is replaced with the new text.  
4803
 * Inserts a string.  The old selection is replaced with the new text.
4794
 * <p>
4804
 * <p>
4795
 *
4805
 *
4796
 * @param string the string
4806
 * @param string the string
Lines 4804-4833 Link Here
4804
 * </ul>
4814
 * </ul>
4805
 */
4815
 */
4806
public void insert(String string) {
4816
public void insert(String string) {
4807
	checkWidget();
4817
   checkWidget();
4808
	if (string == null) {
4818
   if (string == null) {
4809
		SWT.error(SWT.ERROR_NULL_ARGUMENT);
4819
      SWT.error(SWT.ERROR_NULL_ARGUMENT);
4810
	}
4820
   }
4811
	Point sel = getSelectionRange();
4821
   Point sel = getSelectionRange();
4812
	replaceTextRange(sel.x, sel.y, string);
4822
   replaceTextRange(sel.x, sel.y, string);
4813
}
4823
}
4814
/**
4824
/**
4815
 * Creates content change listeners and set the default content model.
4825
 * Creates content change listeners and set the default content model.
4816
 */
4826
 */
4817
void installDefaultContent() {
4827
void installDefaultContent() {
4818
	textChangeListener = new TextChangeListener() {
4828
   textChangeListener = new TextChangeListener() {
4819
		public void textChanging(TextChangingEvent event) {
4829
      public void textChanging(TextChangingEvent event) {
4820
			handleTextChanging(event);
4830
         handleTextChanging(event);
4821
		}
4831
      }
4822
		public void textChanged(TextChangedEvent event) {
4832
      public void textChanged(TextChangedEvent event) {
4823
			handleTextChanged(event);
4833
         handleTextChanged(event);
4824
		}
4834
      }
4825
		public void textSet(TextChangedEvent event) {
4835
      public void textSet(TextChangedEvent event) {
4826
			handleTextSet(event);
4836
         handleTextSet(event);
4827
		}
4837
      }
4828
	};
4838
   };
4829
	logicalContent = content = new DefaultContent();
4839
   logicalContent = content = new DefaultContent();
4830
	content.addTextChangeListener(textChangeListener);
4840
   content.addTextChangeListener(textChangeListener);
4831
}
4841
}
4832
/**
4842
/**
4833
 * Creates a default line style listener.
4843
 * Creates a default line style listener.
Lines 4838-4996 Link Here
4838
 * @see #addLineStyleListener
4848
 * @see #addLineStyleListener
4839
 */
4849
 */
4840
void installDefaultLineStyler() {
4850
void installDefaultLineStyler() {
4841
	defaultLineStyler = new DefaultLineStyler(logicalContent);
4851
   defaultLineStyler = new DefaultLineStyler(logicalContent);
4842
	StyledTextListener typedListener = new StyledTextListener(defaultLineStyler);
4852
   StyledTextListener typedListener = new StyledTextListener(defaultLineStyler);
4843
	if (userLineStyle == false) {
4853
   if (userLineStyle == false) {
4844
		addListener(LineGetStyle, typedListener);
4854
      addListener(LineGetStyle, typedListener);
4845
	}
4855
   }
4846
	if (userLineBackground == false) {
4856
   if (userLineBackground == false) {
4847
		addListener(LineGetBackground, typedListener);
4857
      addListener(LineGetBackground, typedListener);
4848
	}
4858
   }
4849
}
4859
}
4850
/** 
4860
/**
4851
 * Adds event listeners
4861
 * Adds event listeners
4852
 */
4862
 */
4853
void installListeners() {
4863
void installListeners() {
4854
	ScrollBar verticalBar = getVerticalBar();
4864
   ScrollBar verticalBar = getVerticalBar();
4855
	ScrollBar horizontalBar = getHorizontalBar();
4865
   ScrollBar horizontalBar = getHorizontalBar();
4856
	
4866
4857
	addListener(SWT.Dispose, new Listener() {
4867
   addListener(SWT.Dispose, new Listener() {
4858
		public void handleEvent(Event event) {
4868
      public void handleEvent(Event event) {
4859
			handleDispose();
4869
         handleDispose();
4860
		}
4870
      }
4861
	});
4871
   });
4862
	addListener(SWT.KeyDown, new Listener() {
4872
   addListener(SWT.KeyDown, new Listener() {
4863
		public void handleEvent(Event event) {
4873
      public void handleEvent(Event event) {
4864
			handleKeyDown(event);
4874
         handleKeyDown(event);
4865
		}
4875
      }
4866
	});
4876
   });
4867
	addListener(SWT.MouseDown, new Listener() {
4877
   addListener(SWT.MouseDown, new Listener() {
4868
		public void handleEvent(Event event) {
4878
      public void handleEvent(Event event) {
4869
			handleMouseDown(event);
4879
         handleMouseDown(event);
4870
		}
4880
      }
4871
	});
4881
   });
4872
	addListener(SWT.MouseUp, new Listener() {
4882
   addListener(SWT.MouseUp, new Listener() {
4873
		public void handleEvent(Event event) {
4883
      public void handleEvent(Event event) {
4874
			handleMouseUp(event);
4884
         handleMouseUp(event);
4875
		}
4885
      }
4876
	});
4886
   });
4877
	addListener(SWT.MouseDoubleClick, new Listener() {
4887
   addListener(SWT.MouseDoubleClick, new Listener() {
4878
		public void handleEvent(Event event) {
4888
      public void handleEvent(Event event) {
4879
			handleMouseDoubleClick(event);
4889
         handleMouseDoubleClick(event);
4880
		}
4890
      }
4881
	});
4891
   });
4882
	addListener(SWT.MouseMove, new Listener() {
4892
   addListener(SWT.MouseMove, new Listener() {
4883
		public void handleEvent(Event event) {
4893
      public void handleEvent(Event event) {
4884
			handleMouseMove(event);
4894
         handleMouseMove(event);
4885
		}
4895
      }
4886
	});
4896
   });
4887
	addListener(SWT.Paint, new Listener() {
4897
   addListener(SWT.Paint, new Listener() {
4888
		public void handleEvent(Event event) {
4898
      public void handleEvent(Event event) {
4889
			handlePaint(event);
4899
         handlePaint(event);
4890
		}
4900
      }
4891
	});
4901
   });
4892
	addListener(SWT.Resize, new Listener() {
4902
   addListener(SWT.Resize, new Listener() {
4893
		public void handleEvent(Event event) {
4903
      public void handleEvent(Event event) {
4894
			handleResize(event);
4904
         handleResize(event);
4895
		}
4905
      }
4896
	});
4906
   });
4897
	addListener(SWT.Traverse, new Listener() {
4907
   addListener(SWT.Traverse, new Listener() {
4898
		public void handleEvent(Event event) {
4908
      public void handleEvent(Event event) {
4899
			handleTraverse(event);
4909
         handleTraverse(event);
4900
		}
4910
      }
4901
	});
4911
   });
4902
	if (verticalBar != null) {
4912
   if (verticalBar != null) {
4903
		verticalBar.addListener(SWT.Selection, new Listener() {
4913
      verticalBar.addListener(SWT.Selection, new Listener() {
4904
			public void handleEvent(Event event) {
4914
         public void handleEvent(Event event) {
4905
				handleVerticalScroll(event);
4915
            handleVerticalScroll(event);
4906
			}
4916
         }
4907
		});
4917
      });
4908
	}
4918
   }
4909
	if (horizontalBar != null) {
4919
   if (horizontalBar != null) {
4910
		horizontalBar.addListener(SWT.Selection, new Listener() {
4920
      horizontalBar.addListener(SWT.Selection, new Listener() {
4911
			public void handleEvent(Event event) {
4921
         public void handleEvent(Event event) {
4912
				handleHorizontalScroll(event);
4922
            handleHorizontalScroll(event);
4913
			}
4923
         }
4914
		});
4924
      });
4915
	}
4925
   }
4916
}
4926
}
4917
StyledTextContent internalGetContent() {
4927
StyledTextContent internalGetContent() {
4918
	return content;
4928
   return content;
4919
}
4929
}
4920
int internalGetHorizontalPixel() {
4930
int internalGetHorizontalPixel() {
4921
	return horizontalScrollOffset;
4931
   return horizontalScrollOffset;
4922
}
4932
}
4923
LineCache internalGetLineCache() {
4933
LineCache internalGetLineCache() {
4924
	return lineCache;
4934
   return lineCache;
4925
}
4935
}
4926
Point internalGetSelection() {
4936
Point internalGetSelection() {
4927
	return selection;
4937
   return selection;
4928
}
4938
}
4929
boolean internalGetWordWrap() {
4939
boolean internalGetWordWrap() {
4930
	return wordWrap;
4940
   return wordWrap;
4931
}
4941
}
4932
/**
4942
/**
4933
 * Used by WordWrapCache to bypass StyledText.redraw which does
4943
 * Used by WordWrapCache to bypass StyledText.redraw which does
4934
 * an unwanted cache reset.
4944
 * an unwanted cache reset.
4935
 */
4945
 */
4936
void internalRedraw() {
4946
void internalRedraw() {
4937
	super.redraw();
4947
   super.redraw();
4938
}
4948
}
4939
/** 
4949
/**
4940
 * Redraws the specified text range.
4950
 * Redraws the specified text range.
4941
 * <p>
4951
 * <p>
4942
 *
4952
 *
4943
 * @param start offset of the first character to redraw
4953
 * @param start offset of the first character to redraw
4944
 * @param length number of characters to redraw
4954
 * @param length number of characters to redraw
4945
 * @param clearBackground true if the background should be cleared as 
4955
 * @param clearBackground true if the background should be cleared as
4946
 * 	part of the redraw operation.  If true, the entire redraw range will
4956
 *    part of the redraw operation.  If true, the entire redraw range will
4947
 *  be cleared before anything is redrawn.  If the redraw range includes
4957
 *  be cleared before anything is redrawn.  If the redraw range includes
4948
 *	the last character of a line (i.e., the entire line is redrawn) the 
4958
 * the last character of a line (i.e., the entire line is redrawn) the
4949
 * 	line is cleared all the way to the right border of the widget.
4959
 *    line is cleared all the way to the right border of the widget.
4950
 *  The redraw operation will be faster and smoother if clearBackground is 
4960
 *  The redraw operation will be faster and smoother if clearBackground is
4951
 * 	set to false.  Whether or not the flag can be set to false depends on 
4961
 *    set to false.  Whether or not the flag can be set to false depends on
4952
 * 	the type of change that has taken place.  If font styles or background 
4962
 *    the type of change that has taken place.  If font styles or background
4953
 * 	colors for the redraw range have changed, clearBackground should be 
4963
 *    colors for the redraw range have changed, clearBackground should be
4954
 * 	set to true.  If only foreground colors have changed for the redraw 
4964
 *    set to true.  If only foreground colors have changed for the redraw
4955
 * 	range, clearBackground can be set to false. 
4965
 *    range, clearBackground can be set to false.
4956
 */
4966
 */
4957
void internalRedrawRange(int start, int length, boolean clearBackground) {
4967
void internalRedrawRange(int start, int length, boolean clearBackground) {
4958
	int end = start + length;
4968
   int end = start + length;
4959
	int firstLine = content.getLineAtOffset(start);
4969
   int firstLine = content.getLineAtOffset(start);
4960
	int lastLine = content.getLineAtOffset(end);
4970
   int lastLine = content.getLineAtOffset(end);
4961
	int offsetInFirstLine;
4971
   int offsetInFirstLine;
4962
	int partialBottomIndex = getPartialBottomIndex();
4972
   int partialBottomIndex = getPartialBottomIndex();
4963
	int partialTopIndex = verticalScrollOffset / lineHeight;
4973
   int partialTopIndex = verticalScrollOffset / lineHeight;
4964
	// do nothing if redraw range is completely invisible	
4974
   // do nothing if redraw range is completely invisible
4965
	if (firstLine > partialBottomIndex || lastLine < partialTopIndex) {
4975
   if (firstLine > partialBottomIndex || lastLine < partialTopIndex) {
4966
		return;
4976
      return;
4967
	}
4977
   }
4968
	// only redraw visible lines
4978
   // only redraw visible lines
4969
	if (partialTopIndex > firstLine) {
4979
   if (partialTopIndex > firstLine) {
4970
		firstLine = partialTopIndex;
4980
      firstLine = partialTopIndex;
4971
		offsetInFirstLine = 0;
4981
      offsetInFirstLine = 0;
4972
	}
4982
   }
4973
	else {
4983
   else {
4974
		offsetInFirstLine = start - content.getOffsetAtLine(firstLine);
4984
      offsetInFirstLine = start - content.getOffsetAtLine(firstLine);
4975
	}
4985
   }
4976
	if (partialBottomIndex + 1 < lastLine) {
4986
   if (partialBottomIndex + 1 < lastLine) {
4977
		lastLine = partialBottomIndex + 1;	// + 1 to redraw whole bottom line, including line break
4987
      lastLine = partialBottomIndex + 1;  // + 1 to redraw whole bottom line, including line break
4978
		end = content.getOffsetAtLine(lastLine);
4988
      end = content.getOffsetAtLine(lastLine);
4979
	}
4989
   }
4980
	// redraw first and last lines
4990
   // redraw first and last lines
4981
	if (isBidi()) {
4991
   if (isBidi()) {
4982
		redrawBidiLines(firstLine, offsetInFirstLine, lastLine, end, clearBackground);
4992
      redrawBidiLines(firstLine, offsetInFirstLine, lastLine, end, clearBackground);
4983
	}	
4993
   }
4984
	else {
4994
   else {
4985
		redrawLines(firstLine, offsetInFirstLine, lastLine, end, clearBackground);
4995
      redrawLines(firstLine, offsetInFirstLine, lastLine, end, clearBackground);
4986
	}
4996
   }
4987
	// redraw entire center lines if redraw range includes more than two lines
4997
   // redraw entire center lines if redraw range includes more than two lines
4988
	if (lastLine - firstLine > 1) {
4998
   if (lastLine - firstLine > 1) {
4989
		Rectangle clientArea = getClientArea();
4999
      Rectangle clientArea = getClientArea();
4990
		int redrawStopY = lastLine * lineHeight - verticalScrollOffset;		
5000
      int redrawStopY = lastLine * lineHeight - verticalScrollOffset;
4991
		int redrawY = (firstLine + 1) * lineHeight - verticalScrollOffset;				
5001
      int redrawY = (firstLine + 1) * lineHeight - verticalScrollOffset;
4992
		draw(0, redrawY, clientArea.width, redrawStopY - redrawY, clearBackground);
5002
      draw(0, redrawY, clientArea.width, redrawStopY - redrawY, clearBackground);
4993
	}
5003
   }
4994
}
5004
}
4995
/**
5005
/**
4996
 * Returns the widget text with style information encoded using RTF format
5006
 * Returns the widget text with style information encoded using RTF format
Lines 5003-5043 Link Here
5003
 * </ul>
5013
 * </ul>
5004
 */
5014
 */
5005
String getRtf(){
5015
String getRtf(){
5006
	checkWidget();
5016
   checkWidget();
5007
	RTFWriter rtfWriter = new RTFWriter(0, getCharCount());
5017
   RTFWriter rtfWriter = new RTFWriter(0, getCharCount());
5008
	return getPlatformDelimitedText(rtfWriter);
5018
   return getPlatformDelimitedText(rtfWriter);
5009
}
5019
}
5010
/** 
5020
/**
5011
 * Frees resources.
5021
 * Frees resources.
5012
 */
5022
 */
5013
void handleDispose() {
5023
void handleDispose() {
5014
	clipboard.dispose();
5024
   clipboard.dispose();
5015
	ibeamCursor.dispose();
5025
   ibeamCursor.dispose();
5016
	if (renderer != null) {
5026
   if (renderer != null) {
5017
		renderer.dispose();
5027
      renderer.dispose();
5018
		renderer = null;
5028
      renderer = null;
5019
	}
5029
   }
5020
	if (content != null) {
5030
   if (content != null) {
5021
		content.removeTextChangeListener(textChangeListener);
5031
      content.removeTextChangeListener(textChangeListener);
5022
	}	
5032
   }
5023
	if (leftCaretBitmap != null) {
5033
   if (leftCaretBitmap != null) {
5024
		leftCaretBitmap.dispose();
5034
      leftCaretBitmap.dispose();
5025
		leftCaretBitmap = null;
5035
      leftCaretBitmap = null;
5026
	}
5036
   }
5027
	if (rightCaretBitmap != null) {
5037
   if (rightCaretBitmap != null) {
5028
		rightCaretBitmap.dispose();
5038
      rightCaretBitmap.dispose();
5029
		rightCaretBitmap = null;
5039
      rightCaretBitmap = null;
5030
	}
5040
   }
5031
	if (isBidi()) {
5041
   if (isBidi()) {
5032
		StyledTextBidi.removeLanguageListener(this);
5042
      StyledTextBidi.removeLanguageListener(this);
5033
	}
5043
   }
5034
}
5044
}
5035
/** 
5045
/**
5036
 * Scrolls the widget horizontally.
5046
 * Scrolls the widget horizontally.
5037
 */
5047
 */
5038
void handleHorizontalScroll(Event event) {
5048
void handleHorizontalScroll(Event event) {
5039
	int scrollPixel = getHorizontalBar().getSelection() - horizontalScrollOffset;
5049
   int scrollPixel = getHorizontalBar().getSelection() - horizontalScrollOffset;
5040
	scrollHorizontal(scrollPixel);
5050
   scrollHorizontal(scrollPixel);
5041
}
5051
}
5042
/**
5052
/**
5043
 * If an action has been registered for the key stroke execute the action.
5053
 * If an action has been registered for the key stroke execute the action.
Lines 5047-5102 Link Here
5047
 * @param event keyboard event
5057
 * @param event keyboard event
5048
 */
5058
 */
5049
void handleKey(Event event) {
5059
void handleKey(Event event) {
5050
	int action;
5060
   int action;
5051
	
5061
5052
	if (event.keyCode != 0) {
5062
   if (event.keyCode != 0) {
5053
		// special key pressed (e.g., F1)
5063
      // special key pressed (e.g., F1)
5054
		action = getKeyBinding(event.keyCode | event.stateMask);
5064
      action = getKeyBinding(event.keyCode | event.stateMask);
5055
	}
5065
   }
5056
	else {
5066
   else {
5057
		// character key pressed
5067
      // character key pressed
5058
		action = getKeyBinding(event.character | event.stateMask);
5068
      action = getKeyBinding(event.character | event.stateMask);
5059
		if (action == SWT.NULL) { 
5069
      if (action == SWT.NULL) {
5060
			// see if we have a control character
5070
         // see if we have a control character
5061
			if ((event.stateMask & SWT.CTRL) != 0 && (event.character >= 0) && event.character <= 31) {
5071
         if ((event.stateMask & SWT.CTRL) != 0 && (event.character >= 0) && event.character <= 31) {
5062
				// get the character from the CTRL+char sequence, the control
5072
            // get the character from the CTRL+char sequence, the control
5063
				// key subtracts 64 from the value of the key that it modifies
5073
            // key subtracts 64 from the value of the key that it modifies
5064
				int c = event.character + 64;
5074
            int c = event.character + 64;
5065
				action = getKeyBinding(c | event.stateMask);
5075
            action = getKeyBinding(c | event.stateMask);
5066
			}
5076
         }
5067
		}
5077
      }
5068
	}
5078
   }
5069
	if (action == SWT.NULL) {
5079
   if (action == SWT.NULL) {
5070
		boolean ignore = false;
5080
      boolean ignore = false;
5071
		
5081
5072
		if (isCarbon) {
5082
      if (isCarbon) {
5073
			// Ignore acclerator key combinations (we do not want to 
5083
         // Ignore acclerator key combinations (we do not want to
5074
			// insert a character in the text in this instance). Do not  
5084
         // insert a character in the text in this instance). Do not
5075
			// ignore COMMAND+ALT combinations since that key sequence
5085
         // ignore COMMAND+ALT combinations since that key sequence
5076
			// produces characters on the mac.
5086
         // produces characters on the mac.
5077
			ignore = (event.stateMask ^ SWT.COMMAND) == 0 ||
5087
         ignore = (event.stateMask ^ SWT.COMMAND) == 0 ||
5078
					(event.stateMask ^ (SWT.COMMAND | SWT.SHIFT)) == 0;
5088
               (event.stateMask ^ (SWT.COMMAND | SWT.SHIFT)) == 0;
5079
		} else {
5089
      } else {
5080
			// Ignore acclerator key combinations (we do not want to 
5090
         // Ignore acclerator key combinations (we do not want to
5081
			// insert a character in the text in this instance). Don't  
5091
         // insert a character in the text in this instance). Don't
5082
			// ignore CTRL+ALT combinations since that is the Alt Gr 
5092
         // ignore CTRL+ALT combinations since that is the Alt Gr
5083
			// key on some keyboards.  See bug 20953. 
5093
         // key on some keyboards.  See bug 20953.
5084
			ignore = (event.stateMask ^ SWT.ALT) == 0 || 
5094
         ignore = (event.stateMask ^ SWT.ALT) == 0 ||
5085
					(event.stateMask ^ SWT.CTRL) == 0 ||
5095
               (event.stateMask ^ SWT.CTRL) == 0 ||
5086
					(event.stateMask ^ (SWT.ALT | SWT.SHIFT)) == 0 ||
5096
               (event.stateMask ^ (SWT.ALT | SWT.SHIFT)) == 0 ||
5087
					(event.stateMask ^ (SWT.CTRL | SWT.SHIFT)) == 0;
5097
               (event.stateMask ^ (SWT.CTRL | SWT.SHIFT)) == 0;
5088
		}
5098
      }
5089
		// -ignore anything below SPACE except for line delimiter keys and tab.
5099
      // -ignore anything below SPACE except for line delimiter keys and tab.
5090
		// -ignore DEL 
5100
      // -ignore DEL
5091
		if (!ignore && event.character > 31 && event.character != SWT.DEL || 
5101
      if (!ignore && event.character > 31 && event.character != SWT.DEL ||
5092
		    event.character == SWT.CR || event.character == SWT.LF || 
5102
          event.character == SWT.CR || event.character == SWT.LF ||
5093
		    event.character == TAB) {
5103
          event.character == TAB) {
5094
			doContent(event.character);
5104
         doContent(event.character);
5095
		}
5105
      }
5096
	}
5106
   }
5097
	else {
5107
   else {
5098
		invokeAction(action);		
5108
      invokeAction(action);
5099
	}
5109
   }
5100
}
5110
}
5101
/**
5111
/**
5102
 * If a VerifyKey listener exists, verify that the key that was entered
5112
 * If a VerifyKey listener exists, verify that the key that was entered
Lines 5106-5171 Link Here
5106
 * @param event keyboard event
5116
 * @param event keyboard event
5107
 */
5117
 */
5108
void handleKeyDown(Event event) {
5118
void handleKeyDown(Event event) {
5109
	Event verifyEvent = new Event();
5119
   Event verifyEvent = new Event();
5110
	
5120
5111
	verifyEvent.character = event.character;
5121
   verifyEvent.character = event.character;
5112
	verifyEvent.keyCode = event.keyCode;
5122
   verifyEvent.keyCode = event.keyCode;
5113
	verifyEvent.stateMask = event.stateMask;
5123
   verifyEvent.stateMask = event.stateMask;
5114
	verifyEvent.doit = true;
5124
   verifyEvent.doit = true;
5115
	notifyListeners(VerifyKey, verifyEvent);
5125
   notifyListeners(VerifyKey, verifyEvent);
5116
	if (verifyEvent.doit == true) {
5126
   if (verifyEvent.doit == true) {
5117
		handleKey(event);
5127
      handleKey(event);
5118
	}
5128
   }
5119
}
5129
}
5120
/**
5130
/**
5121
 * Updates the caret location and selection if mouse button 1 has been 
5131
 * Updates the caret location and selection if mouse button 1 has been
5122
 * pressed.
5132
 * pressed.
5123
 */
5133
 */
5124
void handleMouseDoubleClick(Event event) {
5134
void handleMouseDoubleClick(Event event) {
5125
	if (event.button != 1 || doubleClickEnabled == false) {
5135
   if (event.button != 1 || doubleClickEnabled == false) {
5126
		return;
5136
      return;
5127
	}
5137
   }
5128
	event.y -= topMargin;
5138
   event.y -= topMargin;
5129
	mouseDoubleClick = true;
5139
   mouseDoubleClick = true;
5130
	caretOffset = getWordStart(caretOffset);
5140
   caretOffset = getWordStart(caretOffset);
5131
	resetSelection();
5141
   resetSelection();
5132
	caretOffset = getWordEndNoSpaces(caretOffset);
5142
   caretOffset = getWordEndNoSpaces(caretOffset);
5133
	showCaret();
5143
   showCaret();
5134
	doMouseSelection();
5144
   doMouseSelection();
5135
	doubleClickSelection = new Point(selection.x, selection.y);
5145
   doubleClickSelection = new Point(selection.x, selection.y);
5136
}
5146
}
5137
/** 
5147
/**
5138
 * Updates the caret location and selection if mouse button 1 has been 
5148
 * Updates the caret location and selection if mouse button 1 has been
5139
 * pressed.
5149
 * pressed.
5140
 */
5150
 */
5141
void handleMouseDown(Event event) {
5151
void handleMouseDown(Event event) {
5142
	boolean select = (event.stateMask & SWT.MOD2) != 0;
5152
   boolean select = (event.stateMask & SWT.MOD2) != 0;
5143
	
5153
5144
	if (event.button != 1) {
5154
   if ((event.button == 1) || (event.button == 3)) {
5145
		return;
5155
      if ((event.button == 3) && (isMouseOverSelection(event.x, event.y))) {
5146
	}
5156
         // we have a right click over a selection, so don't alter the selection
5147
	mouseDoubleClick = false;
5157
         return;
5148
	event.y -= topMargin;
5158
      }
5149
	doMouseLocationChange(event.x, event.y, select);
5159
      mouseDoubleClick = false;
5160
      event.y -= topMargin;
5161
      doMouseLocationChange(event.x, event.y, select);
5162
   }
5150
}
5163
}
5151
/** 
5164
/**
5152
 * Updates the caret location and selection if mouse button 1 is pressed 
5165
 * Updates the caret location and selection if mouse button 1 is pressed
5153
 * during the mouse move.
5166
 * during the mouse move.
5154
 */
5167
 */
5155
void handleMouseMove(Event event) {
5168
void handleMouseMove(Event event) {
5156
	if ((event.stateMask & SWT.BUTTON1) == 0) {
5169
   if ((event.stateMask & SWT.BUTTON1) != 0) {
5157
		return;
5170
      event.y -= topMargin;
5158
	}
5171
      doMouseLocationChange(event.x, event.y, true);
5159
	event.y -= topMargin;
5172
      doAutoScroll(event);
5160
	doMouseLocationChange(event.x, event.y, true);
5173
   } else if (event.stateMask == 0) {
5161
	doAutoScroll(event);
5174
      // changes the mouse pointer based on whether or not it is over a selection
5175
      internalSetCursor(event.x, event.y);
5176
   }
5162
}
5177
}
5163
/** 
5178
/**
5164
 * Autoscrolling ends when the mouse button is released.
5179
 * Autoscrolling ends when the mouse button is released.
5165
 */
5180
 */
5166
void handleMouseUp(Event event) {
5181
void handleMouseUp(Event event) {
5167
	event.y -= topMargin;
5182
   event.y -= topMargin;
5168
	endAutoScroll();
5183
   endAutoScroll();
5169
}
5184
}
5170
/**
5185
/**
5171
 * Renders the invalidated area specified in the paint event.
5186
 * Renders the invalidated area specified in the paint event.
Lines 5174-5377 Link Here
5174
 * @param event paint event
5189
 * @param event paint event
5175
 */
5190
 */
5176
void handlePaint(Event event) {
5191
void handlePaint(Event event) {
5177
	int startLine = Math.max(0, (event.y - topMargin + verticalScrollOffset) / lineHeight);
5192
   int startLine = Math.max(0, (event.y - topMargin + verticalScrollOffset) / lineHeight);
5178
	int paintYFromTopLine = (startLine - topIndex) * lineHeight;
5193
   int paintYFromTopLine = (startLine - topIndex) * lineHeight;
5179
	int topLineOffset = topIndex * lineHeight - verticalScrollOffset;
5194
   int topLineOffset = topIndex * lineHeight - verticalScrollOffset;
5180
	int startY = paintYFromTopLine + topLineOffset + topMargin;	// adjust y position for pixel based scrolling and top margin
5195
   int startY = paintYFromTopLine + topLineOffset + topMargin; // adjust y position for pixel based scrolling and top margin
5181
	int renderHeight = event.y + event.height - startY;
5196
   int renderHeight = event.y + event.height - startY;
5182
	Rectangle clientArea = getClientArea();
5197
   Rectangle clientArea = getClientArea();
5183
	
5198
5184
	// Check if there is work to do. clientArea.width should never be 0
5199
   // Check if there is work to do. clientArea.width should never be 0
5185
	// if we receive a paint event but we never want to try and create 
5200
   // if we receive a paint event but we never want to try and create
5186
	// an Image with 0 width.
5201
   // an Image with 0 width.
5187
	if (clientArea.width == 0 || event.height == 0) {		
5202
   if (clientArea.width == 0 || event.height == 0) {
5188
		return;
5203
      return;
5189
	}
5204
   }
5190
	performPaint(event.gc, startLine, startY, renderHeight);
5205
   performPaint(event.gc, startLine, startY, renderHeight);
5191
}	
5206
}
5192
/**
5207
/**
5193
 * Recalculates the scroll bars. Rewraps all lines when in word 
5208
 * Recalculates the scroll bars. Rewraps all lines when in word
5194
 * wrap mode.
5209
 * wrap mode.
5195
 * <p>
5210
 * <p>
5196
 *
5211
 *
5197
 * @param event resize event
5212
 * @param event resize event
5198
 */
5213
 */
5199
void handleResize(Event event) {
5214
void handleResize(Event event) {
5200
	int oldHeight = clientAreaHeight;
5215
   int oldHeight = clientAreaHeight;
5201
	int oldWidth = clientAreaWidth;
5216
   int oldWidth = clientAreaWidth;
5202
	
5217
5203
	clientAreaHeight = getClientArea().height;
5218
   clientAreaHeight = getClientArea().height;
5204
	clientAreaWidth = getClientArea().width;
5219
   clientAreaWidth = getClientArea().width;
5205
	if (wordWrap) {
5220
   if (wordWrap) {
5206
	    if (oldWidth != clientAreaWidth) {	
5221
       if (oldWidth != clientAreaWidth) {
5207
	    	wordWrapResize(oldWidth);
5222
         wordWrapResize(oldWidth);
5208
	    }
5223
       }
5209
	}
5224
   }
5210
	else
5225
   else
5211
	if (clientAreaHeight > oldHeight) {
5226
   if (clientAreaHeight > oldHeight) {
5212
		int lineCount = content.getLineCount();
5227
      int lineCount = content.getLineCount();
5213
		int oldBottomIndex = topIndex + oldHeight / lineHeight;
5228
      int oldBottomIndex = topIndex + oldHeight / lineHeight;
5214
		int newItemCount = Compatibility.ceil(clientAreaHeight - oldHeight, lineHeight);
5229
      int newItemCount = Compatibility.ceil(clientAreaHeight - oldHeight, lineHeight);
5215
		
5230
5216
		oldBottomIndex = Math.min(oldBottomIndex, lineCount);
5231
      oldBottomIndex = Math.min(oldBottomIndex, lineCount);
5217
		newItemCount = Math.min(newItemCount, lineCount - oldBottomIndex);
5232
      newItemCount = Math.min(newItemCount, lineCount - oldBottomIndex);
5218
		lineCache.calculate(oldBottomIndex, newItemCount);
5233
      lineCache.calculate(oldBottomIndex, newItemCount);
5219
	}	
5234
   }
5220
	setScrollBars();
5235
   setScrollBars();
5221
	claimBottomFreeSpace();
5236
   claimBottomFreeSpace();
5222
	claimRightFreeSpace();	
5237
   claimRightFreeSpace();
5223
	if (oldHeight != clientAreaHeight) {
5238
   if (oldHeight != clientAreaHeight) {
5224
		calculateTopIndex();
5239
      calculateTopIndex();
5225
	}
5240
   }
5226
}
5241
}
5227
/**
5242
/**
5228
 * Updates the caret position and selection and the scroll bars to reflect 
5243
 * Updates the caret position and selection and the scroll bars to reflect
5229
 * the content change.
5244
 * the content change.
5230
 * <p>
5245
 * <p>
5231
 */
5246
 */
5232
void handleTextChanged(TextChangedEvent event) {
5247
void handleTextChanged(TextChangedEvent event) {
5233
	lineCache.textChanged(lastTextChangeStart, 
5248
   lineCache.textChanged(lastTextChangeStart,
5234
		lastTextChangeNewLineCount, 
5249
      lastTextChangeNewLineCount,
5235
		lastTextChangeReplaceLineCount,
5250
      lastTextChangeReplaceLineCount,
5236
		lastTextChangeNewCharCount,
5251
      lastTextChangeNewCharCount,
5237
		lastTextChangeReplaceCharCount);
5252
      lastTextChangeReplaceCharCount);
5238
	setScrollBars();
5253
   setScrollBars();
5239
	// update selection/caret location after styles have been changed.
5254
   // update selection/caret location after styles have been changed.
5240
	// otherwise any text measuring could be incorrect
5255
   // otherwise any text measuring could be incorrect
5241
	// 
5256
   //
5242
	// also, this needs to be done after all scrolling. Otherwise, 
5257
   // also, this needs to be done after all scrolling. Otherwise,
5243
	// selection redraw would be flushed during scroll which is wrong.
5258
   // selection redraw would be flushed during scroll which is wrong.
5244
	// in some cases new text would be drawn in scroll source area even 
5259
   // in some cases new text would be drawn in scroll source area even
5245
	// though the intent is to scroll it.
5260
   // though the intent is to scroll it.
5246
	// fixes 1GB93QT
5261
   // fixes 1GB93QT
5247
	updateSelection(
5262
   updateSelection(
5248
		lastTextChangeStart, 
5263
      lastTextChangeStart,
5249
		lastTextChangeReplaceCharCount, 
5264
      lastTextChangeReplaceCharCount,
5250
		lastTextChangeNewCharCount);
5265
      lastTextChangeNewCharCount);
5251
		
5266
5252
	if (lastTextChangeReplaceLineCount > 0) {
5267
   if (lastTextChangeReplaceLineCount > 0) {
5253
		// Only check for unused space when lines are deleted.
5268
      // Only check for unused space when lines are deleted.
5254
		// Fixes 1GFL4LY
5269
      // Fixes 1GFL4LY
5255
		// Scroll up so that empty lines below last text line are used.
5270
      // Scroll up so that empty lines below last text line are used.
5256
		// Fixes 1GEYJM0
5271
      // Fixes 1GEYJM0
5257
		claimBottomFreeSpace();
5272
      claimBottomFreeSpace();
5258
	}
5273
   }
5259
	if (lastTextChangeReplaceCharCount > 0) {
5274
   if (lastTextChangeReplaceCharCount > 0) {
5260
		// fixes bug 8273
5275
      // fixes bug 8273
5261
		claimRightFreeSpace();
5276
      claimRightFreeSpace();
5262
	}
5277
   }
5263
	// do direct drawing if the text change is confined to a single line.
5278
   // do direct drawing if the text change is confined to a single line.
5264
	// optimization and fixes bug 13999. see also handleTextChanging.
5279
   // optimization and fixes bug 13999. see also handleTextChanging.
5265
	if (lastTextChangeNewLineCount == 0 && lastTextChangeReplaceLineCount == 0) {
5280
   if (lastTextChangeNewLineCount == 0 && lastTextChangeReplaceLineCount == 0) {
5266
		int startLine = content.getLineAtOffset(lastTextChangeStart);
5281
      int startLine = content.getLineAtOffset(lastTextChangeStart);
5267
		int startY = startLine * lineHeight - verticalScrollOffset + topMargin;
5282
      int startY = startLine * lineHeight - verticalScrollOffset + topMargin;
5268
5283
5269
		GC gc = getGC();
5284
      GC gc = getGC();
5270
		Caret caret = getCaret();
5285
      Caret caret = getCaret();
5271
		boolean caretVisible = false;
5286
      boolean caretVisible = false;
5272
		
5287
5273
		if (caret != null) {
5288
      if (caret != null) {
5274
			caretVisible = caret.getVisible();
5289
         caretVisible = caret.getVisible();
5275
			caret.setVisible(false);
5290
         caret.setVisible(false);
5276
		}
5291
      }
5277
		performPaint(gc, startLine, startY, lineHeight);
5292
      performPaint(gc, startLine, startY, lineHeight);
5278
		if (caret != null) {		
5293
      if (caret != null) {
5279
			caret.setVisible(caretVisible);
5294
         caret.setVisible(caretVisible);
5280
		}
5295
      }
5281
		gc.dispose();
5296
      gc.dispose();
5282
	}
5297
   }
5283
}
5298
}
5284
/**
5299
/**
5285
 * Updates the screen to reflect a pending content change.
5300
 * Updates the screen to reflect a pending content change.
5286
 * <p>
5301
 * <p>
5287
 *
5302
 *
5288
 * @param event.start the start offset of the change
5303
 * @param event.start the start offset of the change
5289
 * @param event.newText text that is going to be inserted or empty String 
5304
 * @param event.newText text that is going to be inserted or empty String
5290
 *	if no text will be inserted
5305
 * if no text will be inserted
5291
 * @param event.replaceCharCount length of text that is going to be replaced
5306
 * @param event.replaceCharCount length of text that is going to be replaced
5292
 * @param event.newCharCount length of text that is going to be inserted
5307
 * @param event.newCharCount length of text that is going to be inserted
5293
 * @param event.replaceLineCount number of lines that are going to be replaced
5308
 * @param event.replaceLineCount number of lines that are going to be replaced
5294
 * @param event.newLineCount number of new lines that are going to be inserted
5309
 * @param event.newLineCount number of new lines that are going to be inserted
5295
 */
5310
 */
5296
void handleTextChanging(TextChangingEvent event) {
5311
void handleTextChanging(TextChangingEvent event) {
5297
	int firstLine;	
5312
   int firstLine;
5298
	int textChangeY;
5313
   int textChangeY;
5299
	boolean isMultiLineChange = event.replaceLineCount > 0 || event.newLineCount > 0;
5314
   boolean isMultiLineChange = event.replaceLineCount > 0 || event.newLineCount > 0;
5300
			
5315
5301
	if (event.replaceCharCount < 0) {
5316
   if (event.replaceCharCount < 0) {
5302
		event.start += event.replaceCharCount;
5317
      event.start += event.replaceCharCount;
5303
		event.replaceCharCount *= -1;
5318
      event.replaceCharCount *= -1;
5304
	}
5319
   }
5305
	lastTextChangeStart = event.start;
5320
   lastTextChangeStart = event.start;
5306
	lastTextChangeNewLineCount = event.newLineCount;
5321
   lastTextChangeNewLineCount = event.newLineCount;
5307
	lastTextChangeNewCharCount = event.newCharCount;
5322
   lastTextChangeNewCharCount = event.newCharCount;
5308
	lastTextChangeReplaceLineCount = event.replaceLineCount;
5323
   lastTextChangeReplaceLineCount = event.replaceLineCount;
5309
	lastTextChangeReplaceCharCount = event.replaceCharCount;
5324
   lastTextChangeReplaceCharCount = event.replaceCharCount;
5310
	firstLine = content.getLineAtOffset(event.start);
5325
   firstLine = content.getLineAtOffset(event.start);
5311
	textChangeY = firstLine * lineHeight - verticalScrollOffset + topMargin;
5326
   textChangeY = firstLine * lineHeight - verticalScrollOffset + topMargin;
5312
	if (isMultiLineChange) {
5327
   if (isMultiLineChange) {
5313
		redrawMultiLineChange(textChangeY, event.newLineCount, event.replaceLineCount);
5328
      redrawMultiLineChange(textChangeY, event.newLineCount, event.replaceLineCount);
5314
	}
5329
   }
5315
	// notify default line styler about text change
5330
   // notify default line styler about text change
5316
	if (defaultLineStyler != null) {
5331
   if (defaultLineStyler != null) {
5317
		defaultLineStyler.textChanging(event);
5332
      defaultLineStyler.textChanging(event);
5318
	}
5333
   }
5319
	
5334
5320
	// Update the caret offset if it is greater than the length of the content.
5335
   // Update the caret offset if it is greater than the length of the content.
5321
	// This is necessary since style range API may be called between the
5336
   // This is necessary since style range API may be called between the
5322
	// handleTextChanging and handleTextChanged events and this API sets the
5337
   // handleTextChanging and handleTextChanged events and this API sets the
5323
	// caretOffset.
5338
   // caretOffset.
5324
	int newEndOfText = content.getCharCount() - event.replaceCharCount + event.newCharCount;
5339
   int newEndOfText = content.getCharCount() - event.replaceCharCount + event.newCharCount;
5325
	if (caretOffset > newEndOfText) caretOffset = newEndOfText;
5340
   if (caretOffset > newEndOfText) caretOffset = newEndOfText;
5326
}
5341
}
5327
/**
5342
/**
5328
 * Called when the widget content is set programatically, overwriting 
5343
 * Called when the widget content is set programatically, overwriting
5329
 * the old content. Resets the caret position, selection and scroll offsets. 
5344
 * the old content. Resets the caret position, selection and scroll offsets.
5330
 * Recalculates the content width and scroll bars. Redraws the widget.
5345
 * Recalculates the content width and scroll bars. Redraws the widget.
5331
 * <p>
5346
 * <p>
5332
 *
5347
 *
5333
 * @param event text change event. 
5348
 * @param event text change event.
5334
 */
5349
 */
5335
void handleTextSet(TextChangedEvent event) {
5350
void handleTextSet(TextChangedEvent event) {
5336
	reset();
5351
   reset();
5337
}
5352
}
5338
/**
5353
/**
5339
 * Called when a traversal key is pressed.
5354
 * Called when a traversal key is pressed.
5340
 * Allow tab next traversal to occur when the widget is in single 
5355
 * Allow tab next traversal to occur when the widget is in single
5341
 * line mode or in multi line and non-editable mode . 
5356
 * line mode or in multi line and non-editable mode .
5342
 * When in editable multi line mode we want to prevent the tab 
5357
 * When in editable multi line mode we want to prevent the tab
5343
 * traversal and receive the tab key event instead.
5358
 * traversal and receive the tab key event instead.
5344
 * <p>
5359
 * <p>
5345
 *
5360
 *
5346
 * @param event the event
5361
 * @param event the event
5347
 */
5362
 */
5348
void handleTraverse(Event event) {
5363
void handleTraverse(Event event) {
5349
	int style = getStyle();
5364
   int style = getStyle();
5350
	boolean ignoreTab = (style & SWT.MULTI) != 0 && !editable || isSingleLine();
5365
   boolean ignoreTab = (style & SWT.MULTI) != 0 && !editable || isSingleLine();
5351
	
5366
5352
	if ((event.detail == SWT.TRAVERSE_TAB_NEXT || 
5367
   if ((event.detail == SWT.TRAVERSE_TAB_NEXT ||
5353
		 event.detail == SWT.TRAVERSE_RETURN) && ignoreTab) {
5368
       event.detail == SWT.TRAVERSE_RETURN) && ignoreTab) {
5354
		event.doit = true;
5369
      event.doit = true;
5355
	}
5370
   }
5356
}
5371
}
5357
/** 
5372
/**
5358
 * Scrolls the widget vertically.
5373
 * Scrolls the widget vertically.
5359
 */
5374
 */
5360
void handleVerticalScroll(Event event) {
5375
void handleVerticalScroll(Event event) {
5361
	setVerticalScrollOffset(getVerticalBar().getSelection(), false);
5376
   setVerticalScrollOffset(getVerticalBar().getSelection(), false);
5362
}
5377
}
5363
/** 
5378
/**
5364
 * Initializes the fonts used to render font styles.
5379
 * Initializes the fonts used to render font styles.
5365
 * Presently only regular and bold fonts are supported.
5380
 * Presently only regular and bold fonts are supported.
5366
 */
5381
 */
5367
void initializeRenderer() {
5382
void initializeRenderer() {
5368
	if (renderer != null) {
5383
   if (renderer != null) {
5369
		renderer.dispose();
5384
      renderer.dispose();
5370
	}
5385
   }
5371
	renderer = new DisplayRenderer(
5386
   renderer = new DisplayRenderer(
5372
		getDisplay(), getFont(), isBidi(), leftMargin, this, tabLength);
5387
      getDisplay(), getFont(), isBidi(), leftMargin, this, tabLength);
5373
	lineHeight = renderer.getLineHeight();
5388
   lineHeight = renderer.getLineHeight();
5374
	lineEndSpaceWidth = renderer.getLineEndSpaceWidth();
5389
   lineEndSpaceWidth = renderer.getLineEndSpaceWidth();
5375
}
5390
}
5376
/**
5391
/**
5377
 * Executes the action.
5392
 * Executes the action.
Lines 5380-5557 Link Here
5380
 * @param action one of the actions defined in ST.java
5395
 * @param action one of the actions defined in ST.java
5381
 */
5396
 */
5382
public void invokeAction(int action) {
5397
public void invokeAction(int action) {
5383
	int oldColumnX;
5398
   int oldColumnX;
5384
	int caretLine;
5399
   int caretLine;
5385
	
5400
5386
	checkWidget();	
5401
   checkWidget();
5387
	switch (action) {
5402
   switch (action) {
5388
		// Navigation
5403
      // Navigation
5389
		case ST.LINE_UP:
5404
      case ST.LINE_UP:
5390
			caretLine = doLineUp();
5405
         caretLine = doLineUp();
5391
			oldColumnX = columnX;
5406
         oldColumnX = columnX;
5392
			// explicitly go to the calculated caret line. may be different 
5407
         // explicitly go to the calculated caret line. may be different
5393
			// from content.getLineAtOffset(caretOffset) when in word wrap mode
5408
         // from content.getLineAtOffset(caretOffset) when in word wrap mode
5394
			showCaret(caretLine);
5409
         showCaret(caretLine);
5395
			// save the original horizontal caret position
5410
         // save the original horizontal caret position
5396
			columnX = oldColumnX;
5411
         columnX = oldColumnX;
5397
			clearSelection(true);
5412
         clearSelection(true);
5398
			break;
5413
         break;
5399
		case ST.LINE_DOWN:
5414
      case ST.LINE_DOWN:
5400
			caretLine = doLineDown();
5415
         caretLine = doLineDown();
5401
			oldColumnX = columnX;
5416
         oldColumnX = columnX;
5402
			// explicitly go to the calculated caret line. may be different 
5417
         // explicitly go to the calculated caret line. may be different
5403
			// from content.getLineAtOffset(caretOffset) when in word wrap mode
5418
         // from content.getLineAtOffset(caretOffset) when in word wrap mode
5404
			showCaret(caretLine);
5419
         showCaret(caretLine);
5405
			// save the original horizontal caret position
5420
         // save the original horizontal caret position
5406
			columnX = oldColumnX;
5421
         columnX = oldColumnX;
5407
			clearSelection(true);
5422
         clearSelection(true);
5408
			break;
5423
         break;
5409
		case ST.LINE_START:
5424
      case ST.LINE_START:
5410
			doLineStart();
5425
         doLineStart();
5411
			clearSelection(true);
5426
         clearSelection(true);
5412
			break;
5427
         break;
5413
		case ST.LINE_END:
5428
      case ST.LINE_END:
5414
			doLineEnd();
5429
         doLineEnd();
5415
			clearSelection(true);
5430
         clearSelection(true);
5416
			break;
5431
         break;
5417
		case ST.COLUMN_PREVIOUS:
5432
      case ST.COLUMN_PREVIOUS:
5418
			doCursorPrevious();
5433
         doCursorPrevious();
5419
			clearSelection(true);
5434
         clearSelection(true);
5420
			break;
5435
         break;
5421
		case ST.COLUMN_NEXT:
5436
      case ST.COLUMN_NEXT:
5422
			doCursorNext();
5437
         doCursorNext();
5423
			clearSelection(true);
5438
         clearSelection(true);
5424
			break;
5439
         break;
5425
		case ST.PAGE_UP:
5440
      case ST.PAGE_UP:
5426
			doPageUp();
5441
         doPageUp();
5427
			clearSelection(true);
5442
         clearSelection(true);
5428
			break;
5443
         break;
5429
		case ST.PAGE_DOWN:
5444
      case ST.PAGE_DOWN:
5430
			doPageDown(false);
5445
         doPageDown(false);
5431
			clearSelection(true);
5446
         clearSelection(true);
5432
			break;
5447
         break;
5433
		case ST.WORD_PREVIOUS:
5448
      case ST.WORD_PREVIOUS:
5434
			doWordPrevious();
5449
         doWordPrevious();
5435
			clearSelection(true);
5450
         clearSelection(true);
5436
			break;
5451
         break;
5437
		case ST.WORD_NEXT:
5452
      case ST.WORD_NEXT:
5438
			doWordNext();
5453
         doWordNext();
5439
			clearSelection(true);
5454
         clearSelection(true);
5440
			break;
5455
         break;
5441
		case ST.TEXT_START:
5456
      case ST.TEXT_START:
5442
			doContentStart();
5457
         doContentStart();
5443
			clearSelection(true);
5458
         clearSelection(true);
5444
			break;
5459
         break;
5445
		case ST.TEXT_END:
5460
      case ST.TEXT_END:
5446
			doContentEnd();
5461
         doContentEnd();
5447
			clearSelection(true);
5462
         clearSelection(true);
5448
			break;
5463
         break;
5449
		case ST.WINDOW_START:
5464
      case ST.WINDOW_START:
5450
			doPageStart();
5465
         doPageStart();
5451
			clearSelection(true);
5466
         clearSelection(true);
5452
			break;
5467
         break;
5453
		case ST.WINDOW_END:
5468
      case ST.WINDOW_END:
5454
			doPageEnd();
5469
         doPageEnd();
5455
			clearSelection(true);
5470
         clearSelection(true);
5456
			break;
5471
         break;
5457
		// Selection	
5472
      // Selection
5458
		case ST.SELECT_LINE_UP:
5473
      case ST.SELECT_LINE_UP:
5459
			doSelectionLineUp();
5474
         doSelectionLineUp();
5460
			break;
5475
         break;
5461
		case ST.SELECT_LINE_DOWN:
5476
      case ST.SELECT_LINE_DOWN:
5462
			doSelectionLineDown();
5477
         doSelectionLineDown();
5463
			break;
5478
         break;
5464
		case ST.SELECT_LINE_START:
5479
      case ST.SELECT_LINE_START:
5465
			doLineStart();
5480
         doLineStart();
5466
			doSelection(SWT.LEFT);
5481
         doSelection(SWT.LEFT);
5467
			break;
5482
         break;
5468
		case ST.SELECT_LINE_END:
5483
      case ST.SELECT_LINE_END:
5469
			doLineEnd();
5484
         doLineEnd();
5470
			doSelection(SWT.RIGHT);
5485
         doSelection(SWT.RIGHT);
5471
			break;
5486
         break;
5472
		case ST.SELECT_COLUMN_PREVIOUS:
5487
      case ST.SELECT_COLUMN_PREVIOUS:
5473
			doSelectionCursorPrevious();
5488
         doSelectionCursorPrevious();
5474
			doSelection(SWT.LEFT);
5489
         doSelection(SWT.LEFT);
5475
			break;
5490
         break;
5476
		case ST.SELECT_COLUMN_NEXT:
5491
      case ST.SELECT_COLUMN_NEXT:
5477
			doSelectionCursorNext();
5492
         doSelectionCursorNext();
5478
			doSelection(SWT.RIGHT);
5493
         doSelection(SWT.RIGHT);
5479
			break;
5494
         break;
5480
		case ST.SELECT_PAGE_UP:
5495
      case ST.SELECT_PAGE_UP:
5481
			doPageUp();
5496
         doPageUp();
5482
			doSelection(SWT.LEFT);
5497
         doSelection(SWT.LEFT);
5483
			break;
5498
         break;
5484
		case ST.SELECT_PAGE_DOWN:
5499
      case ST.SELECT_PAGE_DOWN:
5485
			doPageDown(true);
5500
         doPageDown(true);
5486
			break;
5501
         break;
5487
		case ST.SELECT_WORD_PREVIOUS:
5502
      case ST.SELECT_WORD_PREVIOUS:
5488
			doSelectionWordPrevious();
5503
         doSelectionWordPrevious();
5489
			doSelection(SWT.LEFT);
5504
         doSelection(SWT.LEFT);
5490
			break;
5505
         break;
5491
		case ST.SELECT_WORD_NEXT:
5506
      case ST.SELECT_WORD_NEXT:
5492
			doSelectionWordNext();
5507
         doSelectionWordNext();
5493
			doSelection(SWT.RIGHT);
5508
         doSelection(SWT.RIGHT);
5494
			break;
5509
         break;
5495
		case ST.SELECT_TEXT_START:
5510
      case ST.SELECT_TEXT_START:
5496
			doContentStart();
5511
         doContentStart();
5497
			doSelection(SWT.LEFT);
5512
         doSelection(SWT.LEFT);
5498
			break;
5513
         break;
5499
		case ST.SELECT_TEXT_END:
5514
      case ST.SELECT_TEXT_END:
5500
			doContentEnd();
5515
         doContentEnd();
5501
			doSelection(SWT.RIGHT);
5516
         doSelection(SWT.RIGHT);
5502
			break;
5517
         break;
5503
		case ST.SELECT_WINDOW_START:
5518
      case ST.SELECT_WINDOW_START:
5504
			doPageStart();
5519
         doPageStart();
5505
			doSelection(SWT.LEFT);
5520
         doSelection(SWT.LEFT);
5506
			break;
5521
         break;
5507
		case ST.SELECT_WINDOW_END:
5522
      case ST.SELECT_WINDOW_END:
5508
			doPageEnd();
5523
         doPageEnd();
5509
			doSelection(SWT.RIGHT);
5524
         doSelection(SWT.RIGHT);
5510
			break;
5525
         break;
5511
		// Modification			
5526
      // Modification
5512
		case ST.CUT:
5527
      case ST.CUT:
5513
			cut();
5528
         cut();
5514
			break;
5529
         break;
5515
		case ST.COPY:
5530
      case ST.COPY:
5516
			copy();
5531
         copy();
5517
			break;
5532
         break;
5518
		case ST.PASTE:
5533
      case ST.PASTE:
5519
			paste();
5534
         paste();
5520
			break;
5535
         break;
5521
		case ST.DELETE_PREVIOUS:
5536
      case ST.DELETE_PREVIOUS:
5522
			doBackspace();
5537
         doBackspace();
5523
			break;
5538
         break;
5524
		case ST.DELETE_NEXT:
5539
      case ST.DELETE_NEXT:
5525
			doDelete();
5540
         doDelete();
5526
			break;
5541
         break;
5527
		// Miscellaneous
5542
      // Miscellaneous
5528
		case ST.TOGGLE_OVERWRITE:
5543
      case ST.TOGGLE_OVERWRITE:
5529
			overwrite = !overwrite;		// toggle insert/overwrite mode
5544
         overwrite = !overwrite;    // toggle insert/overwrite mode
5530
			break;
5545
         break;
5531
	}
5546
   }
5532
}
5547
}
5533
/**
5548
/**
5534
 * Temporary until SWT provides this
5549
 * Temporary until SWT provides this
5535
 */
5550
 */
5536
boolean isBidi() {
5551
boolean isBidi() {
5537
	return isBidi;
5552
   return isBidi;
5538
}
5553
}
5539
/**
5554
/**
5540
 * Returns whether the given offset is inside a multi byte line delimiter.
5555
 * Returns whether the given offset is inside a multi byte line delimiter.
5541
 * Example: 
5556
 * Example:
5542
 * "Line1\r\n" isLineDelimiter(5) == false but isLineDelimiter(6) == true
5557
 * "Line1\r\n" isLineDelimiter(5) == false but isLineDelimiter(6) == true
5543
 * 
5558
 *
5544
 * @return true if the given offset is inside a multi byte line delimiter.
5559
 * @return true if the given offset is inside a multi byte line delimiter.
5545
 * false if the given offset is before or after a line delimiter.
5560
 * false if the given offset is before or after a line delimiter.
5546
 */
5561
 */
5547
boolean isLineDelimiter(int offset) {
5562
boolean isLineDelimiter(int offset) {
5548
	int line = content.getLineAtOffset(offset);
5563
   int line = content.getLineAtOffset(offset);
5549
	int lineOffset = content.getOffsetAtLine(line);	
5564
   int lineOffset = content.getOffsetAtLine(line);
5550
	int offsetInLine = offset - lineOffset;
5565
   int offsetInLine = offset - lineOffset;
5551
	// offsetInLine will be greater than line length if the line 
5566
   // offsetInLine will be greater than line length if the line
5552
	// delimiter is longer than one character and the offset is set
5567
   // delimiter is longer than one character and the offset is set
5553
	// in between parts of the line delimiter.
5568
   // in between parts of the line delimiter.
5554
	return offsetInLine > content.getLine(line).length();
5569
   return offsetInLine > content.getLine(line).length();
5555
}
5570
}
5556
/**
5571
/**
5557
 * Returns whether or not the given lines are visible.
5572
 * Returns whether or not the given lines are visible.
Lines 5561-5750 Link Here
5561
 * false if none of the lines is visible
5576
 * false if none of the lines is visible
5562
 */
5577
 */
5563
boolean isAreaVisible(int firstLine, int lastLine) {
5578
boolean isAreaVisible(int firstLine, int lastLine) {
5564
	int partialBottomIndex = getPartialBottomIndex();
5579
   int partialBottomIndex = getPartialBottomIndex();
5565
	int partialTopIndex = verticalScrollOffset / lineHeight;
5580
   int partialTopIndex = verticalScrollOffset / lineHeight;
5566
	boolean notVisible = firstLine > partialBottomIndex || lastLine < partialTopIndex;
5581
   boolean notVisible = firstLine > partialBottomIndex || lastLine < partialTopIndex;
5567
	return !notVisible;
5582
   return !notVisible;
5583
}
5584
/**
5585
 * Tests whether the mouse pointer is over a selection.
5586
 *
5587
 * @param x mouse x position in client coordinates
5588
 * @param y mouse y position in client coordinates
5589
 */
5590
private boolean isMouseOverSelection(int x, int y) {
5591
   if (selectionAnchor == -1) {
5592
      // no selection
5593
      return false;
5594
   } else {
5595
      // there is a selection
5596
      int mouseLineIndex = (y + verticalScrollOffset) / lineHeight;
5597
      int mouseCharOffset;
5598
      int selLineIndexX;
5599
      int selLineIndexY;
5600
      int selLineOffset;
5601
      String lineContent = null;
5602
5603
      selLineIndexX = content.getLineAtOffset(selection.x);
5604
      selLineIndexY = content.getLineAtOffset(selection.y);
5605
5606
      if ((mouseLineIndex < selLineIndexX) || (mouseLineIndex > selLineIndexY)) {
5607
         // mouse is before the first or after the last line of the selection
5608
         return false;
5609
      }
5610
5611
      if (mouseLineIndex == selLineIndexX) {
5612
         // the mouse is on the first line of the selection
5613
         selLineOffset = content.getOffsetAtLine(selLineIndexX);
5614
         lineContent = content.getLine(selLineIndexX);
5615
         if (x < getXAtOffset(lineContent, selLineIndexX, selection.x - selLineOffset)) {
5616
            // mouse is before the start of the selection
5617
            return false;
5618
         }
5619
      }
5620
5621
      if (mouseLineIndex == selLineIndexY) {
5622
         // the mouse is on the last line of the selection
5623
         selLineOffset = content.getOffsetAtLine(selLineIndexY);
5624
         if (selLineIndexY != selLineIndexX) {
5625
            lineContent = content.getLine(selLineIndexY);
5626
         }
5627
         if (x > getXAtOffset(lineContent, selLineIndexY, selection.y - selLineOffset)) {
5628
            // mouse is after the end of the selection
5629
            return false;
5630
         }
5631
      }
5632
5633
      // mouse is over the selection
5634
      return true;
5635
   }
5568
}
5636
}
5569
/**
5637
/**
5570
 * Returns whether or not the given styles will necessitate a redraw for the given start line.  
5638
 * Returns whether or not the given styles will necessitate a redraw for the given start line.
5571
 * A redraw is necessary when font style changes after the start of a style will take place.  
5639
 * A redraw is necessary when font style changes after the start of a style will take place.
5572
 * This method assumes ranges is in order and non-overlapping.
5640
 * This method assumes ranges is in order and non-overlapping.
5573
 * <p>
5641
 * <p>
5574
 *
5642
 *
5575
 * @return true if a redraw of the given line is necessary, false otherwise
5643
 * @return true if a redraw of the given line is necessary, false otherwise
5576
 */
5644
 */
5577
boolean isRedrawFirstLine(StyleRange[] ranges, int firstLine, int firstLineOffset) {
5645
boolean isRedrawFirstLine(StyleRange[] ranges, int firstLine, int firstLineOffset) {
5578
	int lineEnd = firstLineOffset + content.getLine(firstLine).length();
5646
   int lineEnd = firstLineOffset + content.getLine(firstLine).length();
5579
	for (int i=0; i<ranges.length; i++) {
5647
   for (int i=0; i<ranges.length; i++) {
5580
		StyleRange range = ranges[i];
5648
      StyleRange range = ranges[i];
5581
		// does the style start on the first line?
5649
      // does the style start on the first line?
5582
		if (range.start < lineEnd) {
5650
      if (range.start < lineEnd) {
5583
			int rangeEnd = range.start + range.length;
5651
         int rangeEnd = range.start + range.length;
5584
			if (isStyleChanging(range, range.start, Math.min(rangeEnd, lineEnd))) return true;
5652
         if (isStyleChanging(range, range.start, Math.min(rangeEnd, lineEnd))) return true;
5585
		} else {
5653
      } else {
5586
			return false;
5654
         return false;
5587
		}
5655
      }
5588
	}
5656
   }
5589
	return false;
5657
   return false;
5590
}
5658
}
5591
/**
5659
/**
5592
 * Returns whether or not the given styles will necessitate a redraw for the given end line.  
5660
 * Returns whether or not the given styles will necessitate a redraw for the given end line.
5593
 * A redraw is necessary when font style changes after the start of a style will take place.  
5661
 * A redraw is necessary when font style changes after the start of a style will take place.
5594
 * This method assumes ranges is in order and non-overlapping.
5662
 * This method assumes ranges is in order and non-overlapping.
5595
 * <p>
5663
 * <p>
5596
 *
5664
 *
5597
 * @return true if a redraw of the last line is necessary, false otherwise
5665
 * @return true if a redraw of the last line is necessary, false otherwise
5598
 */
5666
 */
5599
boolean isRedrawLastLine(StyleRange[] ranges, int lastLine, int lastLineOffset) {
5667
boolean isRedrawLastLine(StyleRange[] ranges, int lastLine, int lastLineOffset) {
5600
	for (int i = ranges.length - 1; i >= 0; i--) {
5668
   for (int i = ranges.length - 1; i >= 0; i--) {
5601
		StyleRange range = ranges[i];
5669
      StyleRange range = ranges[i];
5602
		int rangeEnd = range.start + range.length;
5670
      int rangeEnd = range.start + range.length;
5603
		// does style range end on the last line?
5671
      // does style range end on the last line?
5604
		if (rangeEnd >= lastLineOffset) {
5672
      if (rangeEnd >= lastLineOffset) {
5605
			if (isStyleChanging(range, Math.max(range.start, lastLineOffset), rangeEnd)) return true;
5673
         if (isStyleChanging(range, Math.max(range.start, lastLineOffset), rangeEnd)) return true;
5606
		} else {
5674
      } else {
5607
			break;
5675
         break;
5608
		}
5676
      }
5609
	}
5677
   }
5610
	return false;
5678
   return false;
5611
}
5679
}
5612
/**
5680
/**
5613
 * Returns whether the widget can have only one line.
5681
 * Returns whether the widget can have only one line.
5614
 * <p>
5682
 * <p>
5615
 *
5683
 *
5616
 * @return true if widget can have only one line, false if widget can have 
5684
 * @return true if widget can have only one line, false if widget can have
5617
 * 	multiple lines
5685
 *    multiple lines
5618
 */
5686
 */
5619
boolean isSingleLine() {
5687
boolean isSingleLine() {
5620
	return (getStyle() & SWT.SINGLE) != 0;
5688
   return (getStyle() & SWT.SINGLE) != 0;
5621
}
5689
}
5622
/** 
5690
/**
5623
 * Returns whether the font style in the given style range is changing 
5691
 * Returns whether the font style in the given style range is changing
5624
 * from SWT.NORMAL to SWT.BOLD or vice versa.
5692
 * from SWT.NORMAL to SWT.BOLD or vice versa.
5625
 * <p>
5693
 * <p>
5626
 *
5694
 *
5627
 * @param range StyleRange to compare current font style with.
5695
 * @param range StyleRange to compare current font style with.
5628
 * @param start offset of the first font style to compare 
5696
 * @param start offset of the first font style to compare
5629
 * @param end offset behind the last font style to compare
5697
 * @param end offset behind the last font style to compare
5630
 * @return true if the font style is changing in the given style range,
5698
 * @return true if the font style is changing in the given style range,
5631
 * 	false if the font style is not changing in the given style range.
5699
 *    false if the font style is not changing in the given style range.
5632
 * @exception SWTException <ul>
5700
 * @exception SWTException <ul>
5633
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5701
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5634
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5702
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5635
 * </ul>
5703
 * </ul>
5636
 */
5704
 */
5637
boolean isStyleChanging(StyleRange range, int start, int end) {
5705
boolean isStyleChanging(StyleRange range, int start, int end) {
5638
	checkWidget();
5706
   checkWidget();
5639
	StyleRange[] styles = defaultLineStyler.getStyleRangesFor(start, end - start);
5707
   StyleRange[] styles = defaultLineStyler.getStyleRangesFor(start, end - start);
5640
	
5708
5641
	if (styles == null) {
5709
   if (styles == null) {
5642
		return (range.fontStyle != SWT.NORMAL);
5710
      return (range.fontStyle != SWT.NORMAL);
5643
	}
5711
   }
5644
	for (int i = 0; i < styles.length; i++) {
5712
   for (int i = 0; i < styles.length; i++) {
5645
		StyleRange newStyle = styles[i];
5713
      StyleRange newStyle = styles[i];
5646
		if (newStyle.fontStyle != range.fontStyle) {
5714
      if (newStyle.fontStyle != range.fontStyle) {
5647
			return true;
5715
         return true;
5648
		}
5716
      }
5649
	}
5717
   }
5650
	return false;
5718
   return false;
5651
}
5719
}
5652
/**
5720
/**
5653
 * Sends the specified verify event, replace/insert text as defined by 
5721
 * Sends the specified verify event, replace/insert text as defined by
5654
 * the event and send a modify event.
5722
 * the event and send a modify event.
5655
 * <p>
5723
 * <p>
5656
 *
5724
 *
5657
 * @param event	the text change event. 
5725
 * @param event   the text change event.
5658
 *	<ul>
5726
 * <ul>
5659
 *	<li>event.start - the replace start offset</li>
5727
 * <li>event.start - the replace start offset</li>
5660
 * 	<li>event.end - the replace end offset</li>
5728
 *    <li>event.end - the replace end offset</li>
5661
 * 	<li>event.text - the new text</li>
5729
 *    <li>event.text - the new text</li>
5662
 *	</ul>
5730
 * </ul>
5663
 * @param updateCaret whether or not he caret should be set behind
5731
 * @param updateCaret whether or not he caret should be set behind
5664
 *	the new text
5732
 * the new text
5665
 */
5733
 */
5666
void modifyContent(Event event, boolean updateCaret) {
5734
void modifyContent(Event event, boolean updateCaret) {
5667
	event.doit = true;
5735
   event.doit = true;
5668
	notifyListeners(SWT.Verify, event);
5736
   notifyListeners(SWT.Verify, event);
5669
	if (event.doit) {
5737
   if (event.doit) {
5670
		StyledTextEvent styledTextEvent = null;
5738
      StyledTextEvent styledTextEvent = null;
5671
		int replacedLength = event.end - event.start;
5739
      int replacedLength = event.end - event.start;
5672
		boolean isCharacterRemove = replacedLength == 1 && event.text.length() == 0;
5740
      boolean isCharacterRemove = replacedLength == 1 && event.text.length() == 0;
5673
		boolean isBackspace = event.start < caretOffset;
5741
      boolean isBackspace = event.start < caretOffset;
5674
		boolean isDirectionBoundary = false;
5742
      boolean isDirectionBoundary = false;
5675
				
5743
5676
		if (updateCaret && isBidi() && isCharacterRemove) {
5744
      if (updateCaret && isBidi() && isCharacterRemove) {
5677
			// set the keyboard language to the language of the deleted character.
5745
         // set the keyboard language to the language of the deleted character.
5678
			// determine direction boundary so that caret location can be updated 
5746
         // determine direction boundary so that caret location can be updated
5679
			// properly.
5747
         // properly.
5680
			int line = content.getLineAtOffset(caretOffset);
5748
         int line = content.getLineAtOffset(caretOffset);
5681
			int lineStartOffset = content.getOffsetAtLine(line);		
5749
         int lineStartOffset = content.getOffsetAtLine(line);
5682
			int offsetInLine = caretOffset - lineStartOffset;
5750
         int offsetInLine = caretOffset - lineStartOffset;
5683
			String lineText = content.getLine(line);
5751
         String lineText = content.getLine(line);
5684
			GC gc = getGC();
5752
         GC gc = getGC();
5685
			StyledTextBidi bidi = new StyledTextBidi(gc, lineText, getBidiSegments(lineStartOffset, lineText));			
5753
         StyledTextBidi bidi = new StyledTextBidi(gc, lineText, getBidiSegments(lineStartOffset, lineText));
5686
			if (isBackspace) {
5754
         if (isBackspace) {
5687
				if (offsetInLine > 0) {
5755
            if (offsetInLine > 0) {
5688
					// the line start/end does not represent a direction boundary 
5756
               // the line start/end does not represent a direction boundary
5689
					// even if the previous/next line has a different direction.
5757
               // even if the previous/next line has a different direction.
5690
					isDirectionBoundary = 
5758
               isDirectionBoundary =
5691
						offsetInLine < lineText.length() && 
5759
                  offsetInLine < lineText.length() &&
5692
						(bidi.isRightToLeft(offsetInLine) != bidi.isRightToLeft(offsetInLine - 1) || 
5760
                  (bidi.isRightToLeft(offsetInLine) != bidi.isRightToLeft(offsetInLine - 1) ||
5693
						 bidi.isLocalNumber(offsetInLine) != bidi.isLocalNumber(offsetInLine - 1));
5761
                   bidi.isLocalNumber(offsetInLine) != bidi.isLocalNumber(offsetInLine - 1));
5694
					bidi.setKeyboardLanguage(offsetInLine - 1);
5762
               bidi.setKeyboardLanguage(offsetInLine - 1);
5695
				}
5763
            }
5696
			}
5764
         }
5697
			else {
5765
         else {
5698
				if (offsetInLine < lineText.length()) {
5766
            if (offsetInLine < lineText.length()) {
5699
					// the line start/end does not represent a direction boundary 
5767
               // the line start/end does not represent a direction boundary
5700
					// even if the previous/next line has a different direction.
5768
               // even if the previous/next line has a different direction.
5701
					isDirectionBoundary = 
5769
               isDirectionBoundary =
5702
						offsetInLine > 0 && 
5770
                  offsetInLine > 0 &&
5703
						(bidi.isRightToLeft(offsetInLine) != bidi.isRightToLeft(offsetInLine - 1) || 
5771
                  (bidi.isRightToLeft(offsetInLine) != bidi.isRightToLeft(offsetInLine - 1) ||
5704
						 bidi.isLocalNumber(offsetInLine) != bidi.isLocalNumber(offsetInLine - 1));
5772
                   bidi.isLocalNumber(offsetInLine) != bidi.isLocalNumber(offsetInLine - 1));
5705
					bidi.setKeyboardLanguage(offsetInLine);
5773
               bidi.setKeyboardLanguage(offsetInLine);
5706
				}
5774
            }
5707
			}
5775
         }
5708
			gc.dispose();
5776
         gc.dispose();
5709
		}						
5777
      }
5710
		if (isListening(ExtendedModify)) {
5778
      if (isListening(ExtendedModify)) {
5711
			styledTextEvent = new StyledTextEvent(logicalContent);
5779
         styledTextEvent = new StyledTextEvent(logicalContent);
5712
			styledTextEvent.start = event.start;
5780
         styledTextEvent.start = event.start;
5713
			styledTextEvent.end = event.start + event.text.length();
5781
         styledTextEvent.end = event.start + event.text.length();
5714
			styledTextEvent.text = content.getTextRange(event.start, replacedLength);
5782
         styledTextEvent.text = content.getTextRange(event.start, replacedLength);
5715
		}
5783
      }
5716
		content.replaceTextRange(event.start, replacedLength, event.text);
5784
      content.replaceTextRange(event.start, replacedLength, event.text);
5717
		// set the caret position prior to sending the modify event.
5785
      // set the caret position prior to sending the modify event.
5718
		// fixes 1GBB8NJ
5786
      // fixes 1GBB8NJ
5719
		if (updateCaret) {		
5787
      if (updateCaret) {
5720
			// always update the caret location. fixes 1G8FODP
5788
         // always update the caret location. fixes 1G8FODP
5721
			internalSetSelection(event.start + event.text.length(), 0, true);
5789
         internalSetSelection(event.start + event.text.length(), 0, true);
5722
			if (isBidi()) {
5790
         if (isBidi()) {
5723
				// Update the caret direction so that the caret moves to the 
5791
            // Update the caret direction so that the caret moves to the
5724
				// typed/deleted character. Fixes 1GJLQ16.
5792
            // typed/deleted character. Fixes 1GJLQ16.
5725
				if (isCharacterRemove) {
5793
            if (isCharacterRemove) {
5726
					updateBidiDirection(isBackspace, isDirectionBoundary);
5794
               updateBidiDirection(isBackspace, isDirectionBoundary);
5727
				}
5795
            }
5728
				else {
5796
            else {
5729
					lastCaretDirection = ST.COLUMN_NEXT;
5797
               lastCaretDirection = ST.COLUMN_NEXT;
5730
				}
5798
            }
5731
				showBidiCaret();
5799
            showBidiCaret();
5732
			}
5800
         }
5733
			else {
5801
         else {
5734
				showCaret();
5802
            showCaret();
5735
			}
5803
         }
5736
		}	
5804
      }
5737
		notifyListeners(SWT.Modify, event);		
5805
      notifyListeners(SWT.Modify, event);
5738
		if (isListening(ExtendedModify)) {
5806
      if (isListening(ExtendedModify)) {
5739
			notifyListeners(ExtendedModify, styledTextEvent);
5807
         notifyListeners(ExtendedModify, styledTextEvent);
5740
		}
5808
      }
5741
	}
5809
   }
5742
}
5810
}
5743
/** 
5811
/**
5744
 * Replaces the selection with the clipboard text or insert the text at 
5812
 * Replaces the selection with the clipboard text or insert the text at
5745
 * the current caret offset if there is no selection. 
5813
 * the current caret offset if there is no selection.
5746
 * If the widget has the SWT.SINGLE style and the clipboard text contains
5814
 * If the widget has the SWT.SINGLE style and the clipboard text contains
5747
 * more than one line, only the first line without line delimiters is 
5815
 * more than one line, only the first line without line delimiters is
5748
 * inserted in the widget.
5816
 * inserted in the widget.
5749
 * <p>
5817
 * <p>
5750
 *
5818
 *
Lines 5754-5826 Link Here
5754
 * </ul>
5822
 * </ul>
5755
 */
5823
 */
5756
public void paste(){
5824
public void paste(){
5757
	checkWidget();	
5825
   checkWidget();
5758
	TextTransfer transfer = TextTransfer.getInstance();
5826
   TextTransfer transfer = TextTransfer.getInstance();
5759
	String text;
5827
   String text;
5760
	text = (String) clipboard.getContents(transfer);
5828
   text = (String) clipboard.getContents(transfer);
5761
	if (text != null && text.length() > 0) {
5829
   if (text != null && text.length() > 0) {
5762
		Event event = new Event();
5830
      Event event = new Event();
5763
		event.start = selection.x;
5831
      event.start = selection.x;
5764
		event.end = selection.y;
5832
      event.end = selection.y;
5765
		event.text = getModelDelimitedText(text);
5833
      event.text = getModelDelimitedText(text);
5766
		sendKeyEvent(event);
5834
      sendKeyEvent(event);
5767
	}
5835
   }
5768
}
5836
}
5769
/**
5837
/**
5770
 * Render the specified area.  Broken out as its own method to support
5838
 * Render the specified area.  Broken out as its own method to support
5771
 * direct drawing.
5839
 * direct drawing.
5772
 * <p>
5840
 * <p>
5773
 *
5841
 *
5774
 * @param gc GC to render on 
5842
 * @param gc GC to render on
5775
 * @param startLine first line to render
5843
 * @param startLine first line to render
5776
 * @param startY y pixel location to start rendering at
5844
 * @param startY y pixel location to start rendering at
5777
 * @param renderHeight renderHeight widget area that needs to be filled with lines
5845
 * @param renderHeight renderHeight widget area that needs to be filled with lines
5778
 */
5846
 */
5779
void performPaint(GC gc,int startLine,int startY, int renderHeight)	{
5847
void performPaint(GC gc,int startLine,int startY, int renderHeight)  {
5780
	Rectangle clientArea = getClientArea();
5848
   Rectangle clientArea = getClientArea();
5781
	Color background = getBackground();
5849
   Color background = getBackground();
5782
	
5850
5783
	// Check if there is work to do. We never want to try and create 
5851
   // Check if there is work to do. We never want to try and create
5784
	// an Image with 0 width or 0 height.
5852
   // an Image with 0 width or 0 height.
5785
	if (clientArea.width == 0) {
5853
   if (clientArea.width == 0) {
5786
		return;
5854
      return;
5787
	}
5855
   }
5788
	if (renderHeight > 0) {
5856
   if (renderHeight > 0) {
5789
		// renderHeight will be negative when only top margin needs redrawing
5857
      // renderHeight will be negative when only top margin needs redrawing
5790
		Color foreground = getForeground();
5858
      Color foreground = getForeground();
5791
		int lineCount = content.getLineCount();
5859
      int lineCount = content.getLineCount();
5792
		int paintY = 0;
5860
      int paintY = 0;
5793
		
5861
5794
		if (isSingleLine()) {
5862
      if (isSingleLine()) {
5795
			lineCount = 1;
5863
         lineCount = 1;
5796
			if (startLine > 1) {
5864
         if (startLine > 1) {
5797
				startLine = 1;
5865
            startLine = 1;
5798
			}
5866
         }
5799
		}
5867
      }
5800
		Image lineBuffer = new Image(getDisplay(), clientArea.width, renderHeight);
5868
      Image lineBuffer = new Image(getDisplay(), clientArea.width, renderHeight);
5801
		GC lineGC = new GC(lineBuffer);	
5869
      GC lineGC = new GC(lineBuffer);
5802
	
5870
5803
		lineGC.setFont(getFont());
5871
      lineGC.setFont(getFont());
5804
		renderer.setCurrentFontStyle(SWT.NORMAL);
5872
      renderer.setCurrentFontStyle(SWT.NORMAL);
5805
		lineGC.setForeground(foreground);
5873
      lineGC.setForeground(foreground);
5806
		lineGC.setBackground(background);
5874
      lineGC.setBackground(background);
5807
		
5875
5808
		for (int i = startLine; paintY < renderHeight && i < lineCount; i++, paintY += lineHeight) {
5876
      for (int i = startLine; paintY < renderHeight && i < lineCount; i++, paintY += lineHeight) {
5809
			String line = content.getLine(i);
5877
         String line = content.getLine(i);
5810
			renderer.drawLine(line, i, paintY, lineGC, background, foreground, true);
5878
         renderer.drawLine(line, i, paintY, lineGC, background, foreground, true);
5811
		}
5879
      }
5812
		if (paintY < renderHeight) {
5880
      if (paintY < renderHeight) {
5813
			lineGC.setBackground(background);
5881
         lineGC.setBackground(background);
5814
			lineGC.setForeground(background);
5882
         lineGC.setForeground(background);
5815
			lineGC.fillRectangle(0, paintY, clientArea.width, renderHeight - paintY);
5883
         lineGC.fillRectangle(0, paintY, clientArea.width, renderHeight - paintY);
5816
		}
5884
      }
5817
		gc.drawImage(lineBuffer, 0, startY);
5885
      gc.drawImage(lineBuffer, 0, startY);
5818
		lineGC.dispose();
5886
      lineGC.dispose();
5819
		lineBuffer.dispose();
5887
      lineBuffer.dispose();
5820
	}
5888
   }
5821
	clearMargin(gc, background, clientArea, renderHeight);
5889
   clearMargin(gc, background, clientArea, renderHeight);
5822
}
5890
}
5823
/** 
5891
/**
5824
 * Prints the widget's text to the default printer.
5892
 * Prints the widget's text to the default printer.
5825
 *
5893
 *
5826
 * @exception SWTException <ul>
5894
 * @exception SWTException <ul>
Lines 5829-5852 Link Here
5829
 * </ul>
5897
 * </ul>
5830
 */
5898
 */
5831
public void print() {
5899
public void print() {
5832
	checkWidget();
5900
   checkWidget();
5833
	Printer printer = new Printer();
5901
   Printer printer = new Printer();
5834
	StyledTextPrintOptions options = new StyledTextPrintOptions();
5902
   StyledTextPrintOptions options = new StyledTextPrintOptions();
5835
	
5903
5836
	options.printTextForeground = true;
5904
   options.printTextForeground = true;
5837
	options.printTextBackground = true;
5905
   options.printTextBackground = true;
5838
	options.printTextFontStyle = true;
5906
   options.printTextFontStyle = true;
5839
	options.printLineBackground = true;	
5907
   options.printLineBackground = true;
5840
	new Printing(this, printer, options).run();
5908
   new Printing(this, printer, options).run();
5841
	printer.dispose();
5909
   printer.dispose();
5842
}
5910
}
5843
/** 
5911
/**
5844
 * Returns a runnable that will print the widget's text
5912
 * Returns a runnable that will print the widget's text
5845
 * to the specified printer.
5913
 * to the specified printer.
5846
 * <p>
5914
 * <p>
5847
 * The runnable may be run in a non-UI thread.
5915
 * The runnable may be run in a non-UI thread.
5848
 * </p>
5916
 * </p>
5849
 * 
5917
 *
5850
 * @param printer the printer to print to
5918
 * @param printer the printer to print to
5851
 * @exception SWTException <ul>
5919
 * @exception SWTException <ul>
5852
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5920
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
Lines 5857-5881 Link Here
5857
 * </ul>
5925
 * </ul>
5858
 */
5926
 */
5859
public Runnable print(Printer printer) {
5927
public Runnable print(Printer printer) {
5860
	StyledTextPrintOptions options = new StyledTextPrintOptions();
5928
   StyledTextPrintOptions options = new StyledTextPrintOptions();
5861
5929
5862
	checkWidget();	
5930
   checkWidget();
5863
	options.printTextForeground = true;
5931
   options.printTextForeground = true;
5864
	options.printTextBackground = true;
5932
   options.printTextBackground = true;
5865
	options.printTextFontStyle = true;
5933
   options.printTextFontStyle = true;
5866
	options.printLineBackground = true;
5934
   options.printLineBackground = true;
5867
	if (printer == null) {
5935
   if (printer == null) {
5868
		SWT.error(SWT.ERROR_NULL_ARGUMENT);
5936
      SWT.error(SWT.ERROR_NULL_ARGUMENT);
5869
	}
5937
   }
5870
	return print(printer, options);
5938
   return print(printer, options);
5871
}
5939
}
5872
/** 
5940
/**
5873
 * Returns a runnable that will print the widget's text
5941
 * Returns a runnable that will print the widget's text
5874
 * to the specified printer.
5942
 * to the specified printer.
5875
 * <p>
5943
 * <p>
5876
 * The runnable may be run in a non-UI thread.
5944
 * The runnable may be run in a non-UI thread.
5877
 * </p>
5945
 * </p>
5878
 * 
5946
 *
5879
 * @param printer the printer to print to
5947
 * @param printer the printer to print to
5880
 * @param options print options to use during printing
5948
 * @param options print options to use during printing
5881
 * @exception SWTException <ul>
5949
 * @exception SWTException <ul>
Lines 5888-5898 Link Here
5888
 * @since 2.1
5956
 * @since 2.1
5889
 */
5957
 */
5890
public Runnable print(Printer printer, StyledTextPrintOptions options) {
5958
public Runnable print(Printer printer, StyledTextPrintOptions options) {
5891
	checkWidget();
5959
   checkWidget();
5892
	if (printer == null || options == null) {
5960
   if (printer == null || options == null) {
5893
		SWT.error(SWT.ERROR_NULL_ARGUMENT);
5961
      SWT.error(SWT.ERROR_NULL_ARGUMENT);
5894
	}
5962
   }
5895
	return new Printing(this, printer, options);
5963
   return new Printing(this, printer, options);
5896
}
5964
}
5897
/**
5965
/**
5898
 * Causes the entire bounds of the receiver to be marked
5966
 * Causes the entire bounds of the receiver to be marked
Lines 5900-5907 Link Here
5900
 * is processed, the control will be completely painted.
5968
 * is processed, the control will be completely painted.
5901
 * <p>
5969
 * <p>
5902
 * Recalculates the content width for all lines in the bounds.
5970
 * Recalculates the content width for all lines in the bounds.
5903
 * When a <code>LineStyleListener</code> is used a redraw call 
5971
 * When a <code>LineStyleListener</code> is used a redraw call
5904
 * is the only notification to the widget that styles have changed 
5972
 * is the only notification to the widget that styles have changed
5905
 * and that the content width may have changed.
5973
 * and that the content width may have changed.
5906
 * </p>
5974
 * </p>
5907
 *
5975
 *
Lines 5913-5940 Link Here
5913
 * @see Control#update
5981
 * @see Control#update
5914
 */
5982
 */
5915
public void redraw() {
5983
public void redraw() {
5916
	int itemCount;
5984
   int itemCount;
5917
	
5985
5918
	super.redraw();
5986
   super.redraw();
5919
	itemCount = getPartialBottomIndex() - topIndex + 1;
5987
   itemCount = getPartialBottomIndex() - topIndex + 1;
5920
	lineCache.redrawReset(topIndex, itemCount, true);
5988
   lineCache.redrawReset(topIndex, itemCount, true);
5921
	lineCache.calculate(topIndex, itemCount);
5989
   lineCache.calculate(topIndex, itemCount);
5922
	setHorizontalScrollBar();
5990
   setHorizontalScrollBar();
5923
}
5991
}
5924
/**
5992
/**
5925
 * Causes the rectangular area of the receiver specified by
5993
 * Causes the rectangular area of the receiver specified by
5926
 * the arguments to be marked as needing to be redrawn. 
5994
 * the arguments to be marked as needing to be redrawn.
5927
 * The next time a paint request is processed, that area of
5995
 * The next time a paint request is processed, that area of
5928
 * the receiver will be painted. If the <code>all</code> flag
5996
 * the receiver will be painted. If the <code>all</code> flag
5929
 * is <code>true</code>, any children of the receiver which
5997
 * is <code>true</code>, any children of the receiver which
5930
 * intersect with the specified area will also paint their
5998
 * intersect with the specified area will also paint their
5931
 * intersecting areas. If the <code>all</code> flag is 
5999
 * intersecting areas. If the <code>all</code> flag is
5932
 * <code>false</code>, the children will not be painted.
6000
 * <code>false</code>, the children will not be painted.
5933
 * <p>
6001
 * <p>
5934
 * Marks the content width of all lines in the specified rectangle
6002
 * Marks the content width of all lines in the specified rectangle
5935
 * as unknown. Recalculates the content width of all visible lines.
6003
 * as unknown. Recalculates the content width of all visible lines.
5936
 * When a <code>LineStyleListener</code> is used a redraw call 
6004
 * When a <code>LineStyleListener</code> is used a redraw call
5937
 * is the only notification to the widget that styles have changed 
6005
 * is the only notification to the widget that styles have changed
5938
 * and that the content width may have changed.
6006
 * and that the content width may have changed.
5939
 * </p>
6007
 * </p>
5940
 *
6008
 *
Lines 5952-5975 Link Here
5952
 * @see Control#update
6020
 * @see Control#update
5953
 */
6021
 */
5954
public void redraw(int x, int y, int width, int height, boolean all) {
6022
public void redraw(int x, int y, int width, int height, boolean all) {
5955
	super.redraw(x, y, width, height, all);
6023
   super.redraw(x, y, width, height, all);
5956
	if (height > 0) {
6024
   if (height > 0) {
5957
		int lineCount = content.getLineCount();
6025
      int lineCount = content.getLineCount();
5958
		int startLine = (getTopPixel() + y) / lineHeight;
6026
      int startLine = (getTopPixel() + y) / lineHeight;
5959
		int endLine = startLine + Compatibility.ceil(height, lineHeight);
6027
      int endLine = startLine + Compatibility.ceil(height, lineHeight);
5960
		int itemCount;
6028
      int itemCount;
5961
		
6029
5962
		// reset all lines in the redraw rectangle
6030
      // reset all lines in the redraw rectangle
5963
		startLine = Math.min(startLine, lineCount);
6031
      startLine = Math.min(startLine, lineCount);
5964
		itemCount = Math.min(endLine, lineCount) - startLine;
6032
      itemCount = Math.min(endLine, lineCount) - startLine;
5965
		lineCache.reset(startLine, itemCount, true);
6033
      lineCache.reset(startLine, itemCount, true);
5966
		// only calculate the visible lines
6034
      // only calculate the visible lines
5967
		itemCount = getPartialBottomIndex() - topIndex + 1;
6035
      itemCount = getPartialBottomIndex() - topIndex + 1;
5968
		lineCache.calculate(topIndex, itemCount);
6036
      lineCache.calculate(topIndex, itemCount);
5969
		setHorizontalScrollBar();
6037
      setHorizontalScrollBar();
5970
	}
6038
   }
5971
}
6039
}
5972
/** 
6040
/**
5973
 * Redraws a text range in the specified lines
6041
 * Redraws a text range in the specified lines
5974
 * <p>
6042
 * <p>
5975
 *
6043
 *
Lines 5982-6034 Link Here
5982
 *  without invalidating the redraw range.
6050
 *  without invalidating the redraw range.
5983
 */
6051
 */
5984
void redrawBidiLines(int firstLine, int offsetInFirstLine, int lastLine, int endOffset, boolean clearBackground) {
6052
void redrawBidiLines(int firstLine, int offsetInFirstLine, int lastLine, int endOffset, boolean clearBackground) {
5985
	int lineCount = lastLine - firstLine + 1;
6053
   int lineCount = lastLine - firstLine + 1;
5986
	int redrawY = firstLine * lineHeight - verticalScrollOffset;
6054
   int redrawY = firstLine * lineHeight - verticalScrollOffset;
5987
	int firstLineOffset = content.getOffsetAtLine(firstLine);
6055
   int firstLineOffset = content.getOffsetAtLine(firstLine);
5988
	String line = content.getLine(firstLine);
6056
   String line = content.getLine(firstLine);
5989
	GC gc = getGC();
6057
   GC gc = getGC();
5990
	StyledTextBidi bidi = getStyledTextBidi(line, firstLineOffset, gc);
6058
   StyledTextBidi bidi = getStyledTextBidi(line, firstLineOffset, gc);
5991
		
6059
5992
	bidi.redrawRange(
6060
   bidi.redrawRange(
5993
		this, offsetInFirstLine, 
6061
      this, offsetInFirstLine,
5994
		Math.min(line.length(), endOffset) - offsetInFirstLine, 
6062
      Math.min(line.length(), endOffset) - offsetInFirstLine,
5995
		leftMargin - horizontalScrollOffset, redrawY + topMargin, lineHeight);
6063
      leftMargin - horizontalScrollOffset, redrawY + topMargin, lineHeight);
5996
	// redraw line break marker (either space or full client area width)
6064
   // redraw line break marker (either space or full client area width)
5997
	// if redraw range extends over more than one line and background should be redrawn
6065
   // if redraw range extends over more than one line and background should be redrawn
5998
	if (lastLine > firstLine && clearBackground) {
6066
   if (lastLine > firstLine && clearBackground) {
5999
		int lineBreakWidth;		
6067
      int lineBreakWidth;
6000
		int lineBreakStartX = bidi.getTextWidth();
6068
      int lineBreakStartX = bidi.getTextWidth();
6001
		// handle empty line case
6069
      // handle empty line case
6002
		if (lineBreakStartX == leftMargin) {
6070
      if (lineBreakStartX == leftMargin) {
6003
			lineBreakStartX += XINSET;
6071
         lineBreakStartX += XINSET;
6004
		}
6072
      }
6005
		lineBreakStartX = lineBreakStartX - horizontalScrollOffset;
6073
      lineBreakStartX = lineBreakStartX - horizontalScrollOffset;
6006
		if ((getStyle() & SWT.FULL_SELECTION) != 0) {
6074
      if ((getStyle() & SWT.FULL_SELECTION) != 0) {
6007
			lineBreakWidth = getClientArea().width - lineBreakStartX;
6075
         lineBreakWidth = getClientArea().width - lineBreakStartX;
6008
		}
6076
      }
6009
		else {
6077
      else {
6010
			lineBreakWidth = lineEndSpaceWidth;
6078
         lineBreakWidth = lineEndSpaceWidth;
6011
		}
6079
      }
6012
		draw(lineBreakStartX, redrawY, lineBreakWidth, lineHeight, clearBackground);
6080
      draw(lineBreakStartX, redrawY, lineBreakWidth, lineHeight, clearBackground);
6013
	}
6081
   }
6014
	// redraw last line if more than one line needs redrawing 
6082
   // redraw last line if more than one line needs redrawing
6015
	if (lineCount > 1) {
6083
   if (lineCount > 1) {
6016
		int lastLineOffset = content.getOffsetAtLine(lastLine);
6084
      int lastLineOffset = content.getOffsetAtLine(lastLine);
6017
		int offsetInLastLine = endOffset - lastLineOffset;	
6085
      int offsetInLastLine = endOffset - lastLineOffset;
6018
		// no redraw necessary if redraw offset is 0
6086
      // no redraw necessary if redraw offset is 0
6019
		if (offsetInLastLine > 0) {				
6087
      if (offsetInLastLine > 0) {
6020
			line = content.getLine(lastLine);
6088
         line = content.getLine(lastLine);
6021
			redrawY = lastLine * lineHeight - verticalScrollOffset;		
6089
         redrawY = lastLine * lineHeight - verticalScrollOffset;
6022
			bidi = getStyledTextBidi(line, lastLineOffset, gc);
6090
         bidi = getStyledTextBidi(line, lastLineOffset, gc);
6023
			bidi.redrawRange(
6091
         bidi.redrawRange(
6024
				this, 0, offsetInLastLine, 
6092
            this, 0, offsetInLastLine,
6025
				leftMargin - horizontalScrollOffset, 
6093
            leftMargin - horizontalScrollOffset,
6026
				redrawY + topMargin, lineHeight);
6094
            redrawY + topMargin, lineHeight);
6027
		}
6095
      }
6028
	}
6096
   }
6029
	gc.dispose();
6097
   gc.dispose();
6030
}
6098
}
6031
/** 
6099
/**
6032
 * Redraw the given line.
6100
 * Redraw the given line.
6033
 * <p>
6101
 * <p>
6034
 *
6102
 *
Lines 6036-6052 Link Here
6036
 * @param offset offset in line to start redrawing
6104
 * @param offset offset in line to start redrawing
6037
 */
6105
 */
6038
void redrawLine(int line, int offset) {
6106
void redrawLine(int line, int offset) {
6039
	int redrawX = 0;
6107
   int redrawX = 0;
6040
	if (offset > 0) {
6108
   if (offset > 0) {
6041
		String lineText = content.getLine(line);
6109
      String lineText = content.getLine(line);
6042
		redrawX = getXAtOffset(lineText, line, offset);
6110
      redrawX = getXAtOffset(lineText, line, offset);
6043
	}
6111
   }
6044
	int redrawY = line * lineHeight - verticalScrollOffset;
6112
   int redrawY = line * lineHeight - verticalScrollOffset;
6045
	super.redraw(
6113
   super.redraw(
6046
		redrawX + leftMargin, redrawY + topMargin, 
6114
      redrawX + leftMargin, redrawY + topMargin,
6047
		getClientArea().width, lineHeight, true);
6115
      getClientArea().width, lineHeight, true);
6048
}
6116
}
6049
/** 
6117
/**
6050
 * Redraws a text range in the specified lines
6118
 * Redraws a text range in the specified lines
6051
 * <p>
6119
 * <p>
6052
 *
6120
 *
Lines 6055-6109 Link Here
6055
 * @param lastLine last line to redraw
6123
 * @param lastLine last line to redraw
6056
 * @param endOffset offset in the last where redrawing should stop
6124
 * @param endOffset offset in the last where redrawing should stop
6057
 * @param clearBackground true=clear the background by invalidating
6125
 * @param clearBackground true=clear the background by invalidating
6058
 *  the requested redraw range. If the redraw range includes the 
6126
 *  the requested redraw range. If the redraw range includes the
6059
 * 	last character of a line (i.e., the entire line is redrawn) the 
6127
 *    last character of a line (i.e., the entire line is redrawn) the
6060
 * 	line is cleared all the way to the right border of the widget.
6128
 *    line is cleared all the way to the right border of the widget.
6061
 *  false=draw the foreground directly without invalidating the 
6129
 *  false=draw the foreground directly without invalidating the
6062
 * 	redraw range.
6130
 *    redraw range.
6063
 */
6131
 */
6064
void redrawLines(int firstLine, int offsetInFirstLine, int lastLine, int endOffset, boolean clearBackground) {
6132
void redrawLines(int firstLine, int offsetInFirstLine, int lastLine, int endOffset, boolean clearBackground) {
6065
	String line = content.getLine(firstLine);
6133
   String line = content.getLine(firstLine);
6066
	int lineCount = lastLine - firstLine + 1;
6134
   int lineCount = lastLine - firstLine + 1;
6067
	int redrawX = getXAtOffset(line, firstLine, offsetInFirstLine) - leftMargin;
6135
   int redrawX = getXAtOffset(line, firstLine, offsetInFirstLine) - leftMargin;
6068
	int redrawStopX;
6136
   int redrawStopX;
6069
	int redrawY = firstLine * lineHeight - verticalScrollOffset;
6137
   int redrawY = firstLine * lineHeight - verticalScrollOffset;
6070
	int firstLineOffset = content.getOffsetAtLine(firstLine);
6138
   int firstLineOffset = content.getOffsetAtLine(firstLine);
6071
	boolean fullLineRedraw = ((getStyle() & SWT.FULL_SELECTION) != 0 && lastLine > firstLine);
6139
   boolean fullLineRedraw = ((getStyle() & SWT.FULL_SELECTION) != 0 && lastLine > firstLine);
6072
6140
6073
	// if redraw range includes last character on the first line, 
6141
   // if redraw range includes last character on the first line,
6074
	// clear background to right widget border. fixes bug 19595.
6142
   // clear background to right widget border. fixes bug 19595.
6075
	if (clearBackground && endOffset - firstLineOffset >= line.length()) {
6143
   if (clearBackground && endOffset - firstLineOffset >= line.length()) {
6076
		fullLineRedraw = true;
6144
      fullLineRedraw = true;
6077
	}
6145
   }
6078
	// calculate redraw stop location
6146
   // calculate redraw stop location
6079
	if (fullLineRedraw) {
6147
   if (fullLineRedraw) {
6080
		redrawStopX = getClientArea().width - leftMargin;
6148
      redrawStopX = getClientArea().width - leftMargin;
6081
	}
6149
   }
6082
	else {
6150
   else {
6083
		redrawStopX = getXAtOffset(line, firstLine, endOffset - firstLineOffset) - leftMargin;
6151
      redrawStopX = getXAtOffset(line, firstLine, endOffset - firstLineOffset) - leftMargin;
6084
	}
6152
   }
6085
	draw(redrawX, redrawY, redrawStopX - redrawX, lineHeight, clearBackground);
6153
   draw(redrawX, redrawY, redrawStopX - redrawX, lineHeight, clearBackground);
6086
	// redraw last line if more than one line needs redrawing 
6154
   // redraw last line if more than one line needs redrawing
6087
	if (lineCount > 1) {
6155
   if (lineCount > 1) {
6088
		int offsetInLastLine = endOffset - content.getOffsetAtLine(lastLine);	
6156
      int offsetInLastLine = endOffset - content.getOffsetAtLine(lastLine);
6089
		// no redraw necessary if redraw offset is 0
6157
      // no redraw necessary if redraw offset is 0
6090
		if (offsetInLastLine > 0) {
6158
      if (offsetInLastLine > 0) {
6091
			line = content.getLine(lastLine);
6159
         line = content.getLine(lastLine);
6092
			// if redraw range includes last character on the last line, 
6160
         // if redraw range includes last character on the last line,
6093
			// clear background to right widget border. fixes bug 19595.
6161
         // clear background to right widget border. fixes bug 19595.
6094
			if (clearBackground && offsetInLastLine >= line.length()) {
6162
         if (clearBackground && offsetInLastLine >= line.length()) {
6095
				fullLineRedraw = true;
6163
            fullLineRedraw = true;
6096
			}
6164
         }
6097
			if (fullLineRedraw) {
6165
         if (fullLineRedraw) {
6098
				redrawStopX = getClientArea().width - leftMargin;
6166
            redrawStopX = getClientArea().width - leftMargin;
6099
			}
6167
         }
6100
			else {
6168
         else {
6101
				redrawStopX = getXAtOffset(line, lastLine, offsetInLastLine) - leftMargin;
6169
            redrawStopX = getXAtOffset(line, lastLine, offsetInLastLine) - leftMargin;
6102
			}
6170
         }
6103
			redrawY = lastLine * lineHeight - verticalScrollOffset;
6171
         redrawY = lastLine * lineHeight - verticalScrollOffset;
6104
			draw(0, redrawY, redrawStopX, lineHeight, clearBackground);
6172
         draw(0, redrawY, redrawStopX, lineHeight, clearBackground);
6105
		}
6173
      }
6106
	}
6174
   }
6107
}
6175
}
6108
/**
6176
/**
6109
 * Fixes the widget to display a text change.
6177
 * Fixes the widget to display a text change.
Lines 6115-6164 Link Here
6115
 * @param replacedLineCount number of replaced lines.
6183
 * @param replacedLineCount number of replaced lines.
6116
 */
6184
 */
6117
void redrawMultiLineChange(int y, int newLineCount, int replacedLineCount) {
6185
void redrawMultiLineChange(int y, int newLineCount, int replacedLineCount) {
6118
	Rectangle clientArea = getClientArea();
6186
   Rectangle clientArea = getClientArea();
6119
	int lineCount = newLineCount - replacedLineCount;
6187
   int lineCount = newLineCount - replacedLineCount;
6120
	int sourceY;
6188
   int sourceY;
6121
	int destinationY;
6189
   int destinationY;
6122
		
6190
6123
	if (lineCount > 0) {
6191
   if (lineCount > 0) {
6124
		sourceY = Math.max(0, y + lineHeight);
6192
      sourceY = Math.max(0, y + lineHeight);
6125
		destinationY = sourceY + lineCount * lineHeight;
6193
      destinationY = sourceY + lineCount * lineHeight;
6126
	} 
6194
   }
6127
	else {
6195
   else {
6128
		destinationY = Math.max(0, y + lineHeight);
6196
      destinationY = Math.max(0, y + lineHeight);
6129
		sourceY = destinationY - lineCount * lineHeight;
6197
      sourceY = destinationY - lineCount * lineHeight;
6130
	}	
6198
   }
6131
	scroll(
6199
   scroll(
6132
		0, destinationY,			// destination x, y
6200
      0, destinationY,        // destination x, y
6133
		0, sourceY,					// source x, y
6201
      0, sourceY,             // source x, y
6134
		clientArea.width, clientArea.height, true);
6202
      clientArea.width, clientArea.height, true);
6135
	// Always redrawing causes the bottom line to flash when a line is
6203
   // Always redrawing causes the bottom line to flash when a line is
6136
	// deleted. This is because SWT merges the paint area of the scroll
6204
   // deleted. This is because SWT merges the paint area of the scroll
6137
	// with the paint area of the redraw call below.
6205
   // with the paint area of the redraw call below.
6138
	// To prevent this we could call update after the scroll. However,
6206
   // To prevent this we could call update after the scroll. However,
6139
	// adding update can cause even more flash if the client does other 
6207
   // adding update can cause even more flash if the client does other
6140
	// redraw/update calls (ie. for syntax highlighting).
6208
   // redraw/update calls (ie. for syntax highlighting).
6141
	// We could also redraw only when a line has been added or when 
6209
   // We could also redraw only when a line has been added or when
6142
	// contents has been added to a line. This would require getting 
6210
   // contents has been added to a line. This would require getting
6143
	// line index info from the content and is not worth the trouble
6211
   // line index info from the content and is not worth the trouble
6144
	// (the flash is only on the bottom line and minor).
6212
   // (the flash is only on the bottom line and minor).
6145
	// Specifying the NO_MERGE_PAINTS style bit prevents the merged 
6213
   // Specifying the NO_MERGE_PAINTS style bit prevents the merged
6146
	// redraw but could cause flash/slowness elsewhere.
6214
   // redraw but could cause flash/slowness elsewhere.
6147
	if (y + lineHeight > 0 && y <= clientArea.height) {
6215
   if (y + lineHeight > 0 && y <= clientArea.height) {
6148
		// redraw first changed line in case a line was split/joined
6216
      // redraw first changed line in case a line was split/joined
6149
		super.redraw(0, y, clientArea.width, lineHeight, true);
6217
      super.redraw(0, y, clientArea.width, lineHeight, true);
6150
	}
6218
   }
6151
	if (newLineCount > 0) {
6219
   if (newLineCount > 0) {
6152
		int redrawStartY = y + lineHeight;
6220
      int redrawStartY = y + lineHeight;
6153
		int redrawHeight = newLineCount * lineHeight;
6221
      int redrawHeight = newLineCount * lineHeight;
6154
		
6222
6155
		if (redrawStartY + redrawHeight > 0 && redrawStartY <= clientArea.height) {
6223
      if (redrawStartY + redrawHeight > 0 && redrawStartY <= clientArea.height) {
6156
			// display new text
6224
         // display new text
6157
			super.redraw(0, redrawStartY, clientArea.width, redrawHeight, true);
6225
         super.redraw(0, redrawStartY, clientArea.width, redrawHeight, true);
6158
		}
6226
      }
6159
	}
6227
   }
6160
}
6228
}
6161
/** 
6229
/**
6162
 * Redraws the specified text range.
6230
 * Redraws the specified text range.
6163
 * <p>
6231
 * <p>
6164
 *
6232
 *
Lines 6167-6204 Link Here
6167
 * @param clearBackground true if the background should be cleared as
6235
 * @param clearBackground true if the background should be cleared as
6168
 *  part of the redraw operation.  If true, the entire redraw range will
6236
 *  part of the redraw operation.  If true, the entire redraw range will
6169
 *  be cleared before anything is redrawn.  If the redraw range includes
6237
 *  be cleared before anything is redrawn.  If the redraw range includes
6170
 *	the last character of a line (i.e., the entire line is redrawn) the 
6238
 * the last character of a line (i.e., the entire line is redrawn) the
6171
 * 	line is cleared all the way to the right border of the widget.
6239
 *    line is cleared all the way to the right border of the widget.
6172
 * 	The redraw operation will be faster and smoother if clearBackground 
6240
 *    The redraw operation will be faster and smoother if clearBackground
6173
 * 	is set to false.  Whether or not the flag can be set to false depends 
6241
 *    is set to false.  Whether or not the flag can be set to false depends
6174
 * 	on the type of change that has taken place.  If font styles or 
6242
 *    on the type of change that has taken place.  If font styles or
6175
 * 	background colors for the redraw range have changed, clearBackground 
6243
 *    background colors for the redraw range have changed, clearBackground
6176
 * 	should be set to true.  If only foreground colors have changed for 
6244
 *    should be set to true.  If only foreground colors have changed for
6177
 * 	the redraw range, clearBackground can be set to false. 
6245
 *    the redraw range, clearBackground can be set to false.
6178
 * @exception SWTException <ul>
6246
 * @exception SWTException <ul>
6179
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6247
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6180
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6248
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6181
 * </ul>
6249
 * </ul>
6182
 * @exception IllegalArgumentException <ul>
6250
 * @exception IllegalArgumentException <ul>
6183
 *   <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li> 
6251
 *   <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
6184
 * </ul>
6252
 * </ul>
6185
 */
6253
 */
6186
public void redrawRange(int start, int length, boolean clearBackground) {
6254
public void redrawRange(int start, int length, boolean clearBackground) {
6187
	checkWidget();
6255
   checkWidget();
6188
	int end = start + length;
6256
   int end = start + length;
6189
	int contentLength = content.getCharCount();
6257
   int contentLength = content.getCharCount();
6190
	int firstLine;
6258
   int firstLine;
6191
	int lastLine;
6259
   int lastLine;
6192
	
6260
6193
	if (start > end || start < 0 || end > contentLength) {
6261
   if (start > end || start < 0 || end > contentLength) {
6194
		SWT.error(SWT.ERROR_INVALID_RANGE);
6262
      SWT.error(SWT.ERROR_INVALID_RANGE);
6195
	}	
6263
   }
6196
	firstLine = content.getLineAtOffset(start);
6264
   firstLine = content.getLineAtOffset(start);
6197
	lastLine = content.getLineAtOffset(end);
6265
   lastLine = content.getLineAtOffset(end);
6198
	// reset all affected lines but let the redraw recalculate only 
6266
   // reset all affected lines but let the redraw recalculate only
6199
	// those that are visible.
6267
   // those that are visible.
6200
	lineCache.reset(firstLine, lastLine - firstLine + 1, true);
6268
   lineCache.reset(firstLine, lastLine - firstLine + 1, true);
6201
	internalRedrawRange(start, length, clearBackground);
6269
   internalRedrawRange(start, length, clearBackground);
6202
}
6270
}
6203
/**
6271
/**
6204
 * Removes the specified bidirectional segment listener.
6272
 * Removes the specified bidirectional segment listener.
Lines 6215-6223 Link Here
6215
 * @since 2.0
6283
 * @since 2.0
6216
 */
6284
 */
6217
public void removeBidiSegmentListener(BidiSegmentListener listener) {
6285
public void removeBidiSegmentListener(BidiSegmentListener listener) {
6218
	checkWidget();
6286
   checkWidget();
6219
	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
6287
   if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
6220
	removeListener(LineGetSegments, listener);	
6288
   removeListener(LineGetSegments, listener);
6221
}
6289
}
6222
/**
6290
/**
6223
 * Removes the specified extended modify listener.
6291
 * Removes the specified extended modify listener.
Lines 6233-6241 Link Here
6233
 * </ul>
6301
 * </ul>
6234
 */
6302
 */
6235
public void removeExtendedModifyListener(ExtendedModifyListener extendedModifyListener) {
6303
public void removeExtendedModifyListener(ExtendedModifyListener extendedModifyListener) {
6236
	checkWidget();
6304
   checkWidget();
6237
	if (extendedModifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
6305
   if (extendedModifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
6238
	removeListener(ExtendedModify, extendedModifyListener);	
6306
   removeListener(ExtendedModify, extendedModifyListener);
6239
}
6307
}
6240
/**
6308
/**
6241
 * Removes the specified line background listener.
6309
 * Removes the specified line background listener.
Lines 6251-6265 Link Here
6251
 * </ul>
6319
 * </ul>
6252
 */
6320
 */
6253
public void removeLineBackgroundListener(LineBackgroundListener listener) {
6321
public void removeLineBackgroundListener(LineBackgroundListener listener) {
6254
	checkWidget();
6322
   checkWidget();
6255
	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
6323
   if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
6256
	removeListener(LineGetBackground, listener);	
6324
   removeListener(LineGetBackground, listener);
6257
	// use default line styler if last user line styler was removed.
6325
   // use default line styler if last user line styler was removed.
6258
	if (isListening(LineGetBackground) == false && userLineBackground) {
6326
   if (isListening(LineGetBackground) == false && userLineBackground) {
6259
		StyledTextListener typedListener = new StyledTextListener(defaultLineStyler);
6327
      StyledTextListener typedListener = new StyledTextListener(defaultLineStyler);
6260
		addListener(LineGetBackground, typedListener);	
6328
      addListener(LineGetBackground, typedListener);
6261
		userLineBackground = false;
6329
      userLineBackground = false;
6262
	}
6330
   }
6263
}
6331
}
6264
/**
6332
/**
6265
 * Removes the specified line style listener.
6333
 * Removes the specified line style listener.
Lines 6275-6291 Link Here
6275
 * </ul>
6343
 * </ul>
6276
 */
6344
 */
6277
public void removeLineStyleListener(LineStyleListener listener) {
6345
public void removeLineStyleListener(LineStyleListener listener) {
6278
	checkWidget();
6346
   checkWidget();
6279
	if (listener == null) {
6347
   if (listener == null) {
6280
		SWT.error(SWT.ERROR_NULL_ARGUMENT);
6348
      SWT.error(SWT.ERROR_NULL_ARGUMENT);
6281
	}
6349
   }
6282
	removeListener(LineGetStyle, listener);	
6350
   removeListener(LineGetStyle, listener);
6283
	// use default line styler if last user line styler was removed. Fixes 1G7B1X2
6351
   // use default line styler if last user line styler was removed. Fixes 1G7B1X2
6284
	if (isListening(LineGetStyle) == false && userLineStyle) {
6352
   if (isListening(LineGetStyle) == false && userLineStyle) {
6285
		StyledTextListener typedListener = new StyledTextListener(defaultLineStyler);
6353
      StyledTextListener typedListener = new StyledTextListener(defaultLineStyler);
6286
		addListener(LineGetStyle, typedListener);	
6354
      addListener(LineGetStyle, typedListener);
6287
		userLineStyle = false;
6355
      userLineStyle = false;
6288
	}
6356
   }
6289
}
6357
}
6290
/**
6358
/**
6291
 * Removes the specified modify listener.
6359
 * Removes the specified modify listener.
Lines 6301-6311 Link Here
6301
 * </ul>
6369
 * </ul>
6302
 */
6370
 */
6303
public void removeModifyListener(ModifyListener modifyListener) {
6371
public void removeModifyListener(ModifyListener modifyListener) {
6304
	checkWidget();
6372
   checkWidget();
6305
	if (modifyListener == null) {
6373
   if (modifyListener == null) {
6306
		SWT.error(SWT.ERROR_NULL_ARGUMENT);
6374
      SWT.error(SWT.ERROR_NULL_ARGUMENT);
6307
	}
6375
   }
6308
	removeListener(SWT.Modify, modifyListener);	
6376
   removeListener(SWT.Modify, modifyListener);
6309
}
6377
}
6310
/**
6378
/**
6311
 * Removes the specified selection listener.
6379
 * Removes the specified selection listener.
Lines 6321-6331 Link Here
6321
 * </ul>
6389
 * </ul>
6322
 */
6390
 */
6323
public void removeSelectionListener(SelectionListener listener) {
6391
public void removeSelectionListener(SelectionListener listener) {
6324
	checkWidget();
6392
   checkWidget();
6325
	if (listener == null) {
6393
   if (listener == null) {
6326
		SWT.error(SWT.ERROR_NULL_ARGUMENT);
6394
      SWT.error(SWT.ERROR_NULL_ARGUMENT);
6327
	}
6395
   }
6328
	removeListener(SWT.Selection, listener);	
6396
   removeListener(SWT.Selection, listener);
6329
}
6397
}
6330
/**
6398
/**
6331
 * Removes the specified verify listener.
6399
 * Removes the specified verify listener.
Lines 6341-6351 Link Here
6341
 * </ul>
6409
 * </ul>
6342
 */
6410
 */
6343
public void removeVerifyListener(VerifyListener verifyListener) {
6411
public void removeVerifyListener(VerifyListener verifyListener) {
6344
	checkWidget();
6412
   checkWidget();
6345
	if (verifyListener == null) {
6413
   if (verifyListener == null) {
6346
		SWT.error(SWT.ERROR_NULL_ARGUMENT);
6414
      SWT.error(SWT.ERROR_NULL_ARGUMENT);
6347
	}
6415
   }
6348
	removeListener(SWT.Verify, verifyListener);	
6416
   removeListener(SWT.Verify, verifyListener);
6349
}
6417
}
6350
/**
6418
/**
6351
 * Removes the specified key verify listener.
6419
 * Removes the specified key verify listener.
Lines 6361-6382 Link Here
6361
 * </ul>
6429
 * </ul>
6362
 */
6430
 */
6363
public void removeVerifyKeyListener(VerifyKeyListener listener) {
6431
public void removeVerifyKeyListener(VerifyKeyListener listener) {
6364
	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
6432
   if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
6365
	removeListener(VerifyKey, listener);	
6433
   removeListener(VerifyKey, listener);
6366
}
6434
}
6367
/** 
6435
/**
6368
 * Replaces the styles in the given range with new styles.  This method
6436
 * Replaces the styles in the given range with new styles.  This method
6369
 * effectively deletes the styles in the given range and then adds the
6437
 * effectively deletes the styles in the given range and then adds the
6370
 * the new styles. 
6438
 * the new styles.
6371
 * <p>
6439
 * <p>
6372
 * Should not be called if a LineStyleListener has been set since the 
6440
 * Should not be called if a LineStyleListener has been set since the
6373
 * listener maintains the styles.
6441
 * listener maintains the styles.
6374
 * </p>
6442
 * </p>
6375
 *
6443
 *
6376
 * @param start offset of first character where styles will be deleted
6444
 * @param start offset of first character where styles will be deleted
6377
 * @param length length of the range to delete styles in
6445
 * @param length length of the range to delete styles in
6378
 * @param ranges StyleRange objects containing the new style information.
6446
 * @param ranges StyleRange objects containing the new style information.
6379
 * The ranges should not overlap and should be within the specified start 
6447
 * The ranges should not overlap and should be within the specified start
6380
 * and length. The style rendering is undefined if the ranges do overlap
6448
 * and length. The style rendering is undefined if the ranges do overlap
6381
 * or are ill-defined. Must not be null.
6449
 * or are ill-defined. Must not be null.
6382
 * @exception SWTException <ul>
6450
 * @exception SWTException <ul>
Lines 6384-6476 Link Here
6384
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6452
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6385
 * </ul>
6453
 * </ul>
6386
 * @exception IllegalArgumentException <ul>
6454
 * @exception IllegalArgumentException <ul>
6387
 *   <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 <= offset <= getCharCount())</li> 
6455
 *   <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 <= offset <= getCharCount())</li>
6388
 *   <li>ERROR_NULL_ARGUMENT when string is null</li>
6456
 *   <li>ERROR_NULL_ARGUMENT when string is null</li>
6389
 * </ul>
6457
 * </ul>
6390
 * @since 2.0
6458
 * @since 2.0
6391
 */
6459
 */
6392
public void replaceStyleRanges(int start, int length, StyleRange[] ranges) {
6460
public void replaceStyleRanges(int start, int length, StyleRange[] ranges) {
6393
	checkWidget();
6461
   checkWidget();
6394
	if (userLineStyle) {
6462
   if (userLineStyle) {
6395
		return;
6463
      return;
6396
	}
6464
   }
6397
 	if (ranges == null) {
6465
   if (ranges == null) {
6398
 		SWT.error(SWT.ERROR_NULL_ARGUMENT);
6466
      SWT.error(SWT.ERROR_NULL_ARGUMENT);
6399
 	}
6467
   }
6400
 	if (ranges.length == 0) {
6468
   if (ranges.length == 0) {
6401
 		setStyleRange(new StyleRange(start, length, null, null));
6469
      setStyleRange(new StyleRange(start, length, null, null));
6402
 		return;
6470
      return;
6403
 	}
6471
   }
6404
	int end = start + length;
6472
   int end = start + length;
6405
	if (start > end || start < 0 || end > getCharCount()) {
6473
   if (start > end || start < 0 || end > getCharCount()) {
6406
		SWT.error(SWT.ERROR_INVALID_RANGE);
6474
      SWT.error(SWT.ERROR_INVALID_RANGE);
6407
	}	
6475
   }
6408
	
6476
6409
	int firstLine = content.getLineAtOffset(start);
6477
   int firstLine = content.getLineAtOffset(start);
6410
	int lastLine = content.getLineAtOffset(end);
6478
   int lastLine = content.getLineAtOffset(end);
6411
6479
6412
	// if the area is not visible, there is no need to redraw
6480
   // if the area is not visible, there is no need to redraw
6413
	boolean redrawLines = isAreaVisible(firstLine, lastLine);
6481
   boolean redrawLines = isAreaVisible(firstLine, lastLine);
6414
6482
6415
	if (!redrawLines) {
6483
   if (!redrawLines) {
6416
		defaultLineStyler.replaceStyleRanges(start, length, ranges);
6484
      defaultLineStyler.replaceStyleRanges(start, length, ranges);
6417
		lineCache.reset(firstLine, lastLine - firstLine + 1, true);
6485
      lineCache.reset(firstLine, lastLine - firstLine + 1, true);
6418
	} else {
6486
   } else {
6419
		boolean redrawFirstLine = false;
6487
      boolean redrawFirstLine = false;
6420
		boolean redrawLastLine = false;	
6488
      boolean redrawLastLine = false;
6421
		// the first and last line needs to be redrawn completely if the 
6489
      // the first and last line needs to be redrawn completely if the
6422
		// font style is changing from SWT.NORMAL to something else or 
6490
      // font style is changing from SWT.NORMAL to something else or
6423
		// vice versa. fixes 1G7M5WE.
6491
      // vice versa. fixes 1G7M5WE.
6424
		int firstLineOffset = content.getOffsetAtLine(firstLine);
6492
      int firstLineOffset = content.getOffsetAtLine(firstLine);
6425
		if (isBidi()) {
6493
      if (isBidi()) {
6426
			redrawFirstLine = true;
6494
         redrawFirstLine = true;
6427
			redrawLastLine = true;
6495
         redrawLastLine = true;
6428
		} else {
6496
      } else {
6429
			int firstLineEnd = firstLineOffset + content.getLine(firstLine).length();
6497
         int firstLineEnd = firstLineOffset + content.getLine(firstLine).length();
6430
			redrawFirstLine = isRedrawFirstLine(ranges, firstLine, firstLineOffset);
6498
         redrawFirstLine = isRedrawFirstLine(ranges, firstLine, firstLineOffset);
6431
			// check if any bold styles will be cleared
6499
         // check if any bold styles will be cleared
6432
			StyleRange clearRange = new StyleRange(firstLineOffset, firstLineEnd - firstLineOffset, null, null);
6500
         StyleRange clearRange = new StyleRange(firstLineOffset, firstLineEnd - firstLineOffset, null, null);
6433
			redrawFirstLine = redrawFirstLine || isRedrawFirstLine(new StyleRange[] {clearRange}, firstLine, firstLineOffset);
6501
         redrawFirstLine = redrawFirstLine || isRedrawFirstLine(new StyleRange[] {clearRange}, firstLine, firstLineOffset);
6434
			if (lastLine != firstLine) {
6502
         if (lastLine != firstLine) {
6435
				int lastLineOffset = content.getOffsetAtLine(lastLine);
6503
            int lastLineOffset = content.getOffsetAtLine(lastLine);
6436
				int lastLineEnd = lastLineOffset + content.getLine(lastLine).length();
6504
            int lastLineEnd = lastLineOffset + content.getLine(lastLine).length();
6437
				redrawLastLine = isRedrawLastLine(ranges, lastLine, lastLineOffset);
6505
            redrawLastLine = isRedrawLastLine(ranges, lastLine, lastLineOffset);
6438
				// check if any bold styles will be cleared
6506
            // check if any bold styles will be cleared
6439
				clearRange = new StyleRange(lastLineOffset, lastLineEnd - lastLineOffset, null, null);
6507
            clearRange = new StyleRange(lastLineOffset, lastLineEnd - lastLineOffset, null, null);
6440
				redrawLastLine = redrawLastLine || isRedrawLastLine(new StyleRange[] {clearRange}, lastLine, lastLineOffset);
6508
            redrawLastLine = redrawLastLine || isRedrawLastLine(new StyleRange[] {clearRange}, lastLine, lastLineOffset);
6441
			}
6509
         }
6442
		}
6510
      }
6443
		defaultLineStyler.replaceStyleRanges(start, length, ranges);
6511
      defaultLineStyler.replaceStyleRanges(start, length, ranges);
6444
		// reset all lines affected by the style change but let the redraw
6512
      // reset all lines affected by the style change but let the redraw
6445
		// recalculate only those that are visible.
6513
      // recalculate only those that are visible.
6446
		lineCache.reset(firstLine, lastLine - firstLine + 1, true);
6514
      lineCache.reset(firstLine, lastLine - firstLine + 1, true);
6447
		internalRedrawRange(start, length, true);
6515
      internalRedrawRange(start, length, true);
6448
		if (redrawFirstLine) {
6516
      if (redrawFirstLine) {
6449
			redrawLine(firstLine, start - firstLineOffset);
6517
         redrawLine(firstLine, start - firstLineOffset);
6450
		}
6518
      }
6451
		if (redrawLastLine) {
6519
      if (redrawLastLine) {
6452
			redrawLine(lastLine, 0);
6520
         redrawLine(lastLine, 0);
6453
		}
6521
      }
6454
	}
6522
   }
6455
6523
6456
	// make sure that the caret is positioned correctly.
6524
   // make sure that the caret is positioned correctly.
6457
	// caret location may change if font style changes.
6525
   // caret location may change if font style changes.
6458
	// fixes 1G8FODP
6526
   // fixes 1G8FODP
6459
	setCaretLocation();
6527
   setCaretLocation();
6460
}
6528
}
6461
/**
6529
/**
6462
 * Replaces the given text range with new text.
6530
 * Replaces the given text range with new text.
6463
 * If the widget has the SWT.SINGLE style and "text" contains more than 
6531
 * If the widget has the SWT.SINGLE style and "text" contains more than
6464
 * one line, only the first line is rendered but the text is stored 
6532
 * one line, only the first line is rendered but the text is stored
6465
 * unchanged. A subsequent call to getText will return the same text 
6533
 * unchanged. A subsequent call to getText will return the same text
6466
 * that was set. Note that only a single line of text should be set when 
6534
 * that was set. Note that only a single line of text should be set when
6467
 * the SWT.SINGLE style is used.
6535
 * the SWT.SINGLE style is used.
6468
 * <p>
6536
 * <p>
6469
 * <b>NOTE:</b> During the replace operation the current selection is
6537
 * <b>NOTE:</b> During the replace operation the current selection is
6470
 * changed as follows:
6538
 * changed as follows:
6471
 * <ul>	
6539
 * <ul>
6472
 * <li>selection before replaced text: selection unchanged
6540
 * <li>selection before replaced text: selection unchanged
6473
 * <li>selection after replaced text: adjust the selection so that same text 
6541
 * <li>selection after replaced text: adjust the selection so that same text
6474
 * remains selected
6542
 * remains selected
6475
 * <li>selection intersects replaced text: selection is cleared and caret
6543
 * <li>selection intersects replaced text: selection is cleared and caret
6476
 * is placed after inserted text
6544
 * is placed after inserted text
Lines 6485-6599 Link Here
6485
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6553
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6486
 * </ul>
6554
 * </ul>
6487
 * @exception IllegalArgumentException <ul>
6555
 * @exception IllegalArgumentException <ul>
6488
 *   <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 <= offset <= getCharCount())</li> 
6556
 *   <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 <= offset <= getCharCount())</li>
6489
 *   <li>ERROR_INVALID_ARGUMENT when either start or end is inside a multi byte line delimiter. 
6557
 *   <li>ERROR_INVALID_ARGUMENT when either start or end is inside a multi byte line delimiter.
6490
 * 		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>  
6558
 *       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>
6491
 *   <li>ERROR_NULL_ARGUMENT when string is null</li>
6559
 *   <li>ERROR_NULL_ARGUMENT when string is null</li>
6492
 * </ul>
6560
 * </ul>
6493
 */
6561
 */
6494
public void replaceTextRange(int start, int length, String text) {
6562
public void replaceTextRange(int start, int length, String text) {
6495
	checkWidget();
6563
   checkWidget();
6496
	int contentLength = getCharCount();
6564
   int contentLength = getCharCount();
6497
	int end = start + length;
6565
   int end = start + length;
6498
	Event event = new Event();
6566
   Event event = new Event();
6499
	
6567
6500
	if (start > end || start < 0 || end > contentLength) {
6568
   if (start > end || start < 0 || end > contentLength) {
6501
		SWT.error(SWT.ERROR_INVALID_RANGE);
6569
      SWT.error(SWT.ERROR_INVALID_RANGE);
6502
	}	
6570
   }
6503
	if (text == null) {
6571
   if (text == null) {
6504
		SWT.error(SWT.ERROR_NULL_ARGUMENT);
6572
      SWT.error(SWT.ERROR_NULL_ARGUMENT);
6505
	}
6573
   }
6506
	event.start = start;
6574
   event.start = start;
6507
	event.end = end;
6575
   event.end = end;
6508
	event.text = text;
6576
   event.text = text;
6509
	modifyContent(event, false);
6577
   modifyContent(event, false);
6510
}
6578
}
6511
/**
6579
/**
6512
 * Resets the caret position, selection and scroll offsets. Recalculate
6580
 * Resets the caret position, selection and scroll offsets. Recalculate
6513
 * the content width and scroll bars. Redraw the widget.
6581
 * the content width and scroll bars. Redraw the widget.
6514
 */
6582
 */
6515
void reset() {
6583
void reset() {
6516
	ScrollBar verticalBar = getVerticalBar();
6584
   ScrollBar verticalBar = getVerticalBar();
6517
	ScrollBar horizontalBar = getHorizontalBar();
6585
   ScrollBar horizontalBar = getHorizontalBar();
6518
	caretOffset = 0;
6586
   caretOffset = 0;
6519
	topIndex = 0;
6587
   topIndex = 0;
6520
	topOffset = 0;
6588
   topOffset = 0;
6521
	verticalScrollOffset = 0;
6589
   verticalScrollOffset = 0;
6522
	horizontalScrollOffset = 0;	
6590
   horizontalScrollOffset = 0;
6523
	resetSelection();
6591
   resetSelection();
6524
	// discard any styles that may have been set by creating a 
6592
   // discard any styles that may have been set by creating a
6525
	// new default line styler
6593
   // new default line styler
6526
	if (defaultLineStyler != null) {
6594
   if (defaultLineStyler != null) {
6527
		removeLineBackgroundListener(defaultLineStyler);
6595
      removeLineBackgroundListener(defaultLineStyler);
6528
		removeLineStyleListener(defaultLineStyler);
6596
      removeLineStyleListener(defaultLineStyler);
6529
		installDefaultLineStyler();
6597
      installDefaultLineStyler();
6530
	}	
6598
   }
6531
	calculateContentWidth();
6599
   calculateContentWidth();
6532
	if (verticalBar != null) {
6600
   if (verticalBar != null) {
6533
		verticalBar.setSelection(0);
6601
      verticalBar.setSelection(0);
6534
	}
6602
   }
6535
	if (horizontalBar != null) {
6603
   if (horizontalBar != null) {
6536
		horizontalBar.setSelection(0);	
6604
      horizontalBar.setSelection(0);
6537
	}
6605
   }
6538
	setScrollBars();
6606
   setScrollBars();
6539
	setCaretLocation();
6607
   setCaretLocation();
6540
	super.redraw();
6608
   super.redraw();
6541
}
6609
}
6542
/**
6610
/**
6543
 * Resets the selection.
6611
 * Resets the selection.
6544
 */
6612
 */
6545
void resetSelection() {
6613
void resetSelection() {
6546
	selection.x = selection.y = caretOffset;
6614
   selection.x = selection.y = caretOffset;
6547
	selectionAnchor = -1;
6615
   selectionAnchor = -1;
6616
   // see if we need to update the mouse pointer
6617
   internalSetCursor();
6548
}
6618
}
6549
/**
6619
/**
6550
 * Scrolls the widget horizontally.
6620
 * Scrolls the widget horizontally.
6551
 * <p>
6621
 * <p>
6552
 *
6622
 *
6553
 * @param pixels number of pixels to scroll, > 0 = scroll left,
6623
 * @param pixels number of pixels to scroll, > 0 = scroll left,
6554
 * 	< 0 scroll right
6624
 *    < 0 scroll right
6555
 */
6625
 */
6556
void scrollHorizontal(int pixels) {
6626
void scrollHorizontal(int pixels) {
6557
	Rectangle clientArea;
6627
   Rectangle clientArea;
6558
	
6628
6559
	if (pixels == 0) {
6629
   if (pixels == 0) {
6560
		return;
6630
      return;
6561
	}
6631
   }
6562
	clientArea = getClientArea();
6632
   clientArea = getClientArea();
6563
	if (pixels > 0) {
6633
   if (pixels > 0) {
6564
		int sourceX = leftMargin + pixels;
6634
      int sourceX = leftMargin + pixels;
6565
		int scrollWidth = clientArea.width - sourceX - rightMargin;
6635
      int scrollWidth = clientArea.width - sourceX - rightMargin;
6566
		int scrollHeight = clientArea.height - topMargin - bottomMargin;
6636
      int scrollHeight = clientArea.height - topMargin - bottomMargin;
6567
		scroll(
6637
      scroll(
6568
			leftMargin, topMargin, 						// destination x, y
6638
         leftMargin, topMargin,                 // destination x, y
6569
			sourceX, topMargin,							// source x, y
6639
         sourceX, topMargin,                    // source x, y
6570
			scrollWidth, scrollHeight, true);
6640
         scrollWidth, scrollHeight, true);
6571
		if (sourceX > scrollWidth) {
6641
      if (sourceX > scrollWidth) {
6572
			// redraw from end of scrolled area to beginning of scroll 
6642
         // redraw from end of scrolled area to beginning of scroll
6573
			// invalidated area
6643
         // invalidated area
6574
			super.redraw(
6644
         super.redraw(
6575
				leftMargin + scrollWidth, topMargin, 
6645
            leftMargin + scrollWidth, topMargin,
6576
				pixels - scrollWidth, scrollHeight, true);
6646
            pixels - scrollWidth, scrollHeight, true);
6577
		}
6647
      }
6578
	}
6648
   }
6579
	else {
6649
   else {
6580
		int destinationX = leftMargin - pixels;
6650
      int destinationX = leftMargin - pixels;
6581
		int scrollWidth = clientArea.width - destinationX - rightMargin;
6651
      int scrollWidth = clientArea.width - destinationX - rightMargin;
6582
		int scrollHeight = clientArea.height - topMargin - bottomMargin;
6652
      int scrollHeight = clientArea.height - topMargin - bottomMargin;
6583
		scroll(
6653
      scroll(
6584
			destinationX, topMargin,					// destination x, y
6654
         destinationX, topMargin,               // destination x, y
6585
			leftMargin, topMargin,						// source x, y
6655
         leftMargin, topMargin,                 // source x, y
6586
			scrollWidth, scrollHeight, true);
6656
         scrollWidth, scrollHeight, true);
6587
		if (destinationX > scrollWidth) {
6657
      if (destinationX > scrollWidth) {
6588
			// redraw from end of scroll invalidated area to scroll 
6658
         // redraw from end of scroll invalidated area to scroll
6589
			// destination
6659
         // destination
6590
			super.redraw(
6660
         super.redraw(
6591
				leftMargin + scrollWidth, topMargin, 
6661
            leftMargin + scrollWidth, topMargin,
6592
				-pixels - scrollWidth, scrollHeight, true);	
6662
            -pixels - scrollWidth, scrollHeight, true);
6593
		}
6663
      }
6594
	}
6664
   }
6595
	horizontalScrollOffset += pixels;
6665
   horizontalScrollOffset += pixels;
6596
	setCaretLocation();
6666
   setCaretLocation();
6597
}
6667
}
6598
/**
6668
/**
6599
 * Scrolls the widget horizontally and adjust the horizontal scroll
6669
 * Scrolls the widget horizontally and adjust the horizontal scroll
Lines 6601-6623 Link Here
6601
 * <p>
6671
 * <p>
6602
 *
6672
 *
6603
 * @param pixels number of pixels to scroll, > 0 = scroll left,
6673
 * @param pixels number of pixels to scroll, > 0 = scroll left,
6604
 * 	< 0 scroll right
6674
 *    < 0 scroll right
6605
 * @return
6675
 * @return
6606
 *	true=the widget was scrolled 
6676
 * true=the widget was scrolled
6607
 *	false=the widget was not scrolled, the given offset is not valid.
6677
 * false=the widget was not scrolled, the given offset is not valid.
6608
 */
6678
 */
6609
boolean scrollHorizontalBar(int pixels) {
6679
boolean scrollHorizontalBar(int pixels) {
6610
	if (pixels == 0) {
6680
   if (pixels == 0) {
6611
		return false;
6681
      return false;
6612
	}
6682
   }
6613
	ScrollBar horizontalBar = getHorizontalBar();
6683
   ScrollBar horizontalBar = getHorizontalBar();
6614
	if (horizontalBar != null) {
6684
   if (horizontalBar != null) {
6615
		horizontalBar.setSelection(horizontalScrollOffset + pixels);
6685
      horizontalBar.setSelection(horizontalScrollOffset + pixels);
6616
	}
6686
   }
6617
	scrollHorizontal(pixels);
6687
   scrollHorizontal(pixels);
6618
	return true;
6688
   return true;
6619
}
6689
}
6620
/** 
6690
/**
6621
 * Selects all the text.
6691
 * Selects all the text.
6622
 * <p>
6692
 * <p>
6623
 *
6693
 *
Lines 6627-6660 Link Here
6627
 * </ul>
6697
 * </ul>
6628
 */
6698
 */
6629
public void selectAll() {
6699
public void selectAll() {
6630
	checkWidget();
6700
   checkWidget();
6631
	setSelection(new Point(0, Math.max(getCharCount(),0)));
6701
   setSelection(new Point(0, Math.max(getCharCount(),0)));
6632
}
6702
}
6633
/**
6703
/**
6634
 * Replaces/inserts text as defined by the event.
6704
 * Replaces/inserts text as defined by the event.
6635
 * <p>
6705
 * <p>
6636
 *
6706
 *
6637
 * @param event the text change event. 
6707
 * @param event the text change event.
6638
 *	<ul>
6708
 * <ul>
6639
 *	<li>event.start - the replace start offset</li>
6709
 * <li>event.start - the replace start offset</li>
6640
 * 	<li>event.end - the replace end offset</li>
6710
 *    <li>event.end - the replace end offset</li>
6641
 * 	<li>event.text - the new text</li>
6711
 *    <li>event.text - the new text</li>
6642
 *	</ul>
6712
 * </ul>
6643
 */
6713
 */
6644
void sendKeyEvent(Event event) {
6714
void sendKeyEvent(Event event) {
6645
	if (editable == false) {
6715
   if (editable == false) {
6646
		return;
6716
      return;
6647
	}
6717
   }
6648
	modifyContent(event, true);
6718
   modifyContent(event, true);
6649
}
6719
}
6650
/**
6720
/**
6651
 * Sends the specified selection event.
6721
 * Sends the specified selection event.
6652
 */
6722
 */
6653
void sendSelectionEvent() {
6723
void sendSelectionEvent() {
6654
	Event event = new Event();
6724
   Event event = new Event();
6655
	event.x = selection.x;
6725
   event.x = selection.x;
6656
	event.y = selection.y;
6726
   event.y = selection.y;
6657
	notifyListeners(SWT.Selection, event);
6727
   notifyListeners(SWT.Selection, event);
6658
}
6728
}
6659
/**
6729
/**
6660
 * Sets whether the widget wraps lines.
6730
 * Sets whether the widget wraps lines.
Lines 6665-6723 Link Here
6665
 * @since 2.0
6735
 * @since 2.0
6666
 */
6736
 */
6667
public void setWordWrap(boolean wrap) {
6737
public void setWordWrap(boolean wrap) {
6668
	checkWidget();
6738
   checkWidget();
6669
	
6739
6670
	if (wrap != wordWrap) {
6740
   if (wrap != wordWrap) {
6671
		ScrollBar horizontalBar = getHorizontalBar();
6741
      ScrollBar horizontalBar = getHorizontalBar();
6672
		
6742
6673
		wordWrap = wrap;
6743
      wordWrap = wrap;
6674
		if (wordWrap) {
6744
      if (wordWrap) {
6675
		    logicalContent = content;
6745
          logicalContent = content;
6676
		    content = new WrappedContent(renderer, logicalContent);
6746
          content = new WrappedContent(renderer, logicalContent);
6677
		}
6747
      }
6678
		else {
6748
      else {
6679
		    content = logicalContent;
6749
          content = logicalContent;
6680
		}
6750
      }
6681
		calculateContentWidth();
6751
      calculateContentWidth();
6682
	    horizontalScrollOffset = 0;
6752
       horizontalScrollOffset = 0;
6683
	    if (horizontalBar != null) {
6753
       if (horizontalBar != null) {
6684
			horizontalBar.setVisible(!wordWrap);		
6754
         horizontalBar.setVisible(!wordWrap);
6685
	    }
6755
       }
6686
		setScrollBars();
6756
      setScrollBars();
6687
		setCaretLocation();
6757
      setCaretLocation();
6688
		super.redraw();		
6758
      super.redraw();
6689
	}
6759
   }
6690
}
6760
}
6691
/**
6761
/**
6692
 * Sets the caret location and scrolls the caret offset into view.
6762
 * Sets the caret location and scrolls the caret offset into view.
6693
 */
6763
 */
6694
void showBidiCaret() {
6764
void showBidiCaret() {
6695
	int line = content.getLineAtOffset(caretOffset);
6765
   int line = content.getLineAtOffset(caretOffset);
6696
	int lineOffset = content.getOffsetAtLine(line);
6766
   int lineOffset = content.getOffsetAtLine(line);
6697
	int offsetInLine = caretOffset - lineOffset;
6767
   int offsetInLine = caretOffset - lineOffset;
6698
	String lineText = content.getLine(line);
6768
   String lineText = content.getLine(line);
6699
	int xAtOffset = 0;
6769
   int xAtOffset = 0;
6700
	boolean scrolled = false;		
6770
   boolean scrolled = false;
6701
	GC gc = getGC();
6771
   GC gc = getGC();
6702
	StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc);
6772
   StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc);
6703
	// getXAtOffset, inlined for better performance
6773
   // getXAtOffset, inlined for better performance
6704
	xAtOffset = getBidiTextPosition(lineText, offsetInLine, bidi) + leftMargin;
6774
   xAtOffset = getBidiTextPosition(lineText, offsetInLine, bidi) + leftMargin;
6705
	if (offsetInLine > lineText.length()) {
6775
   if (offsetInLine > lineText.length()) {
6706
		// offset is not on the line. return an x location one character 
6776
      // offset is not on the line. return an x location one character
6707
		// after the line to indicate the line delimiter.
6777
      // after the line to indicate the line delimiter.
6708
		xAtOffset += lineEndSpaceWidth;
6778
      xAtOffset += lineEndSpaceWidth;
6709
	}
6779
   }
6710
	xAtOffset -= horizontalScrollOffset;
6780
   xAtOffset -= horizontalScrollOffset;
6711
	//
6781
   //
6712
	scrolled = showLocation(xAtOffset, line);
6782
   scrolled = showLocation(xAtOffset, line);
6713
	if (scrolled == false) {
6783
   if (scrolled == false) {
6714
		setBidiCaretLocation(bidi);
6784
      setBidiCaretLocation(bidi);
6715
	}
6785
   }
6716
	gc.dispose();
6786
   gc.dispose();
6717
}
6787
}
6718
/**
6788
/**
6719
 * Sets the receiver's caret.  Set the caret's height and location.
6789
 * Sets the receiver's caret.  Set the caret's height and location.
6720
 * 
6790
 *
6721
 * </p>
6791
 * </p>
6722
 * @param caret the new caret for the receiver
6792
 * @param caret the new caret for the receiver
6723
 *
6793
 *
Lines 6727-6826 Link Here
6727
 * </ul>
6797
 * </ul>
6728
 */
6798
 */
6729
public void setCaret(Caret caret) {
6799
public void setCaret(Caret caret) {
6730
	checkWidget ();
6800
   checkWidget ();
6731
	super.setCaret(caret);
6801
   super.setCaret(caret);
6732
	if (caret != null) {
6802
   if (caret != null) {
6733
		if (isBidi() == false) {
6803
      if (isBidi() == false) {
6734
			caret.setSize(caret.getSize().x, lineHeight);
6804
         caret.setSize(caret.getSize().x, lineHeight);
6735
		}
6805
      }
6736
		setCaretLocation();
6806
      setCaretLocation();
6737
		if (isBidi()) {
6807
      if (isBidi()) {
6738
			setBidiKeyboardLanguage();	
6808
         setBidiKeyboardLanguage();
6739
		}
6809
      }
6740
	}		
6810
   }
6741
}
6811
}
6742
/**
6812
/**
6743
 * @see org.eclipse.swt.widgets.Control#setBackground
6813
 * @see org.eclipse.swt.widgets.Control#setBackground
6744
 */
6814
 */
6745
public void setBackground(Color color) {
6815
public void setBackground(Color color) {
6746
	checkWidget();
6816
   checkWidget();
6747
	super.setBackground(color);
6817
   super.setBackground(color);
6748
	background = color;
6818
   background = color;
6749
	redraw();
6819
   redraw();
6750
}
6820
}
6751
/**
6821
/**
6752
 * Set the caret to indicate the current typing direction.
6822
 * Set the caret to indicate the current typing direction.
6753
 */
6823
 */
6754
void setBidiCaretDirection() {
6824
void setBidiCaretDirection() {
6755
	Caret caret = getCaret();
6825
   Caret caret = getCaret();
6756
	int direction = StyledTextBidi.getKeyboardLanguageDirection();
6826
   int direction = StyledTextBidi.getKeyboardLanguageDirection();
6757
	
6827
6758
	if (caret == null || direction == caretDirection) {
6828
   if (caret == null || direction == caretDirection) {
6759
		return;
6829
      return;
6760
	}
6830
   }
6761
	caretDirection = direction;
6831
   caretDirection = direction;
6762
	if (direction == SWT.DEFAULT) {
6832
   if (direction == SWT.DEFAULT) {
6763
		caret.setImage(null);
6833
      caret.setImage(null);
6764
		caret.setSize(caret.getSize().x, lineHeight);
6834
      caret.setSize(caret.getSize().x, lineHeight);
6765
	} 
6835
   }
6766
	else 
6836
   else
6767
	if (caretDirection == SWT.LEFT) {
6837
   if (caretDirection == SWT.LEFT) {
6768
		caret.setImage(leftCaretBitmap);			
6838
      caret.setImage(leftCaretBitmap);
6769
	} 
6839
   }
6770
	else 
6840
   else
6771
	if (caretDirection == SWT.RIGHT) {
6841
   if (caretDirection == SWT.RIGHT) {
6772
		caret.setImage(rightCaretBitmap);			
6842
      caret.setImage(rightCaretBitmap);
6773
	}
6843
   }
6774
}
6844
}
6775
/**
6845
/**
6776
 * Moves the Caret to the current caret offset.
6846
 * Moves the Caret to the current caret offset.
6777
 * <p>
6847
 * <p>
6778
 * 
6848
 *
6779
 * @param bidi StyledTextBidi object to use for measuring.
6849
 * @param bidi StyledTextBidi object to use for measuring.
6780
 * 	May be left null in which case a new object will be created.
6850
 *    May be left null in which case a new object will be created.
6781
 */
6851
 */
6782
void setBidiCaretLocation(StyledTextBidi bidi) {
6852
void setBidiCaretLocation(StyledTextBidi bidi) {
6783
	int caretLine = getCaretLine();
6853
   int caretLine = getCaretLine();
6784
	
6854
6785
	setBidiCaretLocation(bidi, caretLine);
6855
   setBidiCaretLocation(bidi, caretLine);
6786
}
6856
}
6787
/**
6857
/**
6788
 * Moves the Caret to the current caret offset.
6858
 * Moves the Caret to the current caret offset.
6789
 * <p>
6859
 * <p>
6790
 * 
6860
 *
6791
 * @param bidi StyledTextBidi object to use for measuring.
6861
 * @param bidi StyledTextBidi object to use for measuring.
6792
 * 	May be left null in which case a new object will be created.
6862
 *    May be left null in which case a new object will be created.
6793
 * @param caretLine line the caret should be placed on. Relative to
6863
 * @param caretLine line the caret should be placed on. Relative to
6794
 * 	first line in document
6864
 *    first line in document
6795
 */
6865
 */
6796
void setBidiCaretLocation(StyledTextBidi bidi, int caretLine) {
6866
void setBidiCaretLocation(StyledTextBidi bidi, int caretLine) {
6797
	Caret caret = getCaret();
6867
   Caret caret = getCaret();
6798
	String lineText = content.getLine(caretLine);
6868
   String lineText = content.getLine(caretLine);
6799
	int lineStartOffset = content.getOffsetAtLine(caretLine);
6869
   int lineStartOffset = content.getOffsetAtLine(caretLine);
6800
	int offsetInLine = caretOffset - lineStartOffset;
6870
   int offsetInLine = caretOffset - lineStartOffset;
6801
	GC gc = null;
6871
   GC gc = null;
6802
	
6872
6803
	if (bidi == null) {
6873
   if (bidi == null) {
6804
		gc = getGC();
6874
      gc = getGC();
6805
		bidi = getStyledTextBidi(lineText, lineStartOffset, gc);
6875
      bidi = getStyledTextBidi(lineText, lineStartOffset, gc);
6806
	}		
6876
   }
6807
	if (lastCaretDirection == SWT.NULL) {
6877
   if (lastCaretDirection == SWT.NULL) {
6808
		columnX = bidi.getTextPosition(offsetInLine) + leftMargin - horizontalScrollOffset;
6878
      columnX = bidi.getTextPosition(offsetInLine) + leftMargin - horizontalScrollOffset;
6809
	} else {
6879
   } else {
6810
		columnX = bidi.getTextPosition(offsetInLine, lastCaretDirection) + leftMargin - horizontalScrollOffset;
6880
      columnX = bidi.getTextPosition(offsetInLine, lastCaretDirection) + leftMargin - horizontalScrollOffset;
6811
	}
6881
   }
6812
	if (StyledTextBidi.getKeyboardLanguageDirection() == SWT.RIGHT) {
6882
   if (StyledTextBidi.getKeyboardLanguageDirection() == SWT.RIGHT) {
6813
		columnX -= (getCaretWidth() - 1);
6883
      columnX -= (getCaretWidth() - 1);
6814
	}
6884
   }
6815
	if (caret != null) {
6885
   if (caret != null) {
6816
		setBidiCaretDirection();		
6886
      setBidiCaretDirection();
6817
		caret.setLocation(
6887
      caret.setLocation(
6818
			columnX, 
6888
         columnX,
6819
			caretLine * lineHeight - verticalScrollOffset + topMargin);
6889
         caretLine * lineHeight - verticalScrollOffset + topMargin);
6820
	}
6890
   }
6821
	if (gc != null) {
6891
   if (gc != null) {
6822
		gc.dispose();
6892
      gc.dispose();
6823
	}
6893
   }
6824
}
6894
}
6825
/**
6895
/**
6826
 * Sets the BIDI coloring mode.  When true the BIDI text display
6896
 * Sets the BIDI coloring mode.  When true the BIDI text display
Lines 6837-6923 Link Here
6837
 * </p>
6907
 * </p>
6838
 */
6908
 */
6839
public void setBidiColoring(boolean mode) {
6909
public void setBidiColoring(boolean mode) {
6840
	checkWidget();
6910
   checkWidget();
6841
	bidiColoring = mode;
6911
   bidiColoring = mode;
6842
}
6912
}
6843
/**
6913
/**
6844
 * Switches the keyboard language according to the current editing 
6914
 * Switches the keyboard language according to the current editing
6845
 * position and cursor direction.
6915
 * position and cursor direction.
6846
 */
6916
 */
6847
void setBidiKeyboardLanguage() {
6917
void setBidiKeyboardLanguage() {
6848
	int caretLine = getCaretLine();	
6918
   int caretLine = getCaretLine();
6849
	int lineStartOffset = content.getOffsetAtLine(caretLine);
6919
   int lineStartOffset = content.getOffsetAtLine(caretLine);
6850
	int offsetInLine = caretOffset - lineStartOffset;
6920
   int offsetInLine = caretOffset - lineStartOffset;
6851
	String lineText = content.getLine(caretLine);
6921
   String lineText = content.getLine(caretLine);
6852
	GC gc = getGC();
6922
   GC gc = getGC();
6853
	StyledTextBidi bidi;
6923
   StyledTextBidi bidi;
6854
	int lineLength = lineText.length();
6924
   int lineLength = lineText.length();
6855
	
6925
6856
	// Don't supply the bold styles/font since we don't want to measure anything
6926
   // Don't supply the bold styles/font since we don't want to measure anything
6857
	bidi = new StyledTextBidi(gc, lineText, getBidiSegments(lineStartOffset, lineText));
6927
   bidi = new StyledTextBidi(gc, lineText, getBidiSegments(lineStartOffset, lineText));
6858
	if (offsetInLine == 0) {
6928
   if (offsetInLine == 0) {
6859
		bidi.setKeyboardLanguage(offsetInLine);
6929
      bidi.setKeyboardLanguage(offsetInLine);
6860
	}
6930
   }
6861
	else
6931
   else
6862
	if (offsetInLine >= lineLength) {
6932
   if (offsetInLine >= lineLength) {
6863
		offsetInLine = Math.min(offsetInLine, lineLength - 1);
6933
      offsetInLine = Math.min(offsetInLine, lineLength - 1);
6864
		bidi.setKeyboardLanguage(offsetInLine);
6934
      bidi.setKeyboardLanguage(offsetInLine);
6865
	}
6935
   }
6866
	else
6936
   else
6867
	if (lastCaretDirection == ST.COLUMN_NEXT) {
6937
   if (lastCaretDirection == ST.COLUMN_NEXT) {
6868
		// continue with previous character type
6938
      // continue with previous character type
6869
		bidi.setKeyboardLanguage(offsetInLine - 1);
6939
      bidi.setKeyboardLanguage(offsetInLine - 1);
6870
	} 
6940
   }
6871
	else {
6941
   else {
6872
		bidi.setKeyboardLanguage(offsetInLine);
6942
      bidi.setKeyboardLanguage(offsetInLine);
6873
	}	
6943
   }
6874
	gc.dispose();
6944
   gc.dispose();
6875
}
6945
}
6876
/**
6946
/**
6877
 * Moves the Caret to the current caret offset.
6947
 * Moves the Caret to the current caret offset.
6878
 * <p>
6948
 * <p>
6879
 * 
6949
 *
6880
 * @param newCaretX the new x location of the caret.
6950
 * @param newCaretX the new x location of the caret.
6881
 * 	passed in for better performance when it has already been 
6951
 *    passed in for better performance when it has already been
6882
 * 	calculated outside this method.
6952
 *    calculated outside this method.
6883
 * @param line index of the line the caret is on. Relative to 
6953
 * @param line index of the line the caret is on. Relative to
6884
 *	the first line in the document.
6954
 * the first line in the document.
6885
 */
6955
 */
6886
void setCaretLocation(int newCaretX, int line) {
6956
void setCaretLocation(int newCaretX, int line) {
6887
	if (isBidi()) {
6957
   if (isBidi()) {
6888
		setBidiCaretLocation(null, line);
6958
      setBidiCaretLocation(null, line);
6889
	}
6959
   }
6890
	else {	
6960
   else {
6891
		Caret caret = getCaret();
6961
      Caret caret = getCaret();
6892
		
6962
6893
		columnX = newCaretX;
6963
      columnX = newCaretX;
6894
		if (caret != null) {
6964
      if (caret != null) {
6895
			caret.setLocation(
6965
         caret.setLocation(
6896
				newCaretX, 
6966
            newCaretX,
6897
				line * lineHeight - verticalScrollOffset + topMargin);
6967
            line * lineHeight - verticalScrollOffset + topMargin);
6898
		}
6968
      }
6899
	}
6969
   }
6900
}
6970
}
6901
/**
6971
/**
6902
 * Moves the Caret to the current caret offset.
6972
 * Moves the Caret to the current caret offset.
6903
 */
6973
 */
6904
void setCaretLocation() {
6974
void setCaretLocation() {
6905
	if (isBidi()) {
6975
   if (isBidi()) {
6906
		setBidiCaretLocation(null);
6976
      setBidiCaretLocation(null);
6907
	}
6977
   }
6908
	else {	
6978
   else {
6909
		Caret caret = getCaret();
6979
      Caret caret = getCaret();
6910
		int caretLine = getCaretLine();	
6980
      int caretLine = getCaretLine();
6911
		int lineStartOffset = content.getOffsetAtLine(caretLine);
6981
      int lineStartOffset = content.getOffsetAtLine(caretLine);
6912
									
6982
6913
		columnX = getXAtOffset(
6983
      columnX = getXAtOffset(
6914
			content.getLine(caretLine), caretLine, caretOffset - lineStartOffset);
6984
         content.getLine(caretLine), caretLine, caretOffset - lineStartOffset);
6915
		if (caret != null) {
6985
      if (caret != null) {
6916
			caret.setLocation(
6986
         caret.setLocation(
6917
				columnX, 
6987
            columnX,
6918
				caretLine * lineHeight - verticalScrollOffset + topMargin);
6988
            caretLine * lineHeight - verticalScrollOffset + topMargin);
6919
		}
6989
      }
6920
	}
6990
   }
6921
}
6991
}
6922
/**
6992
/**
6923
 * Sets the caret offset.
6993
 * Sets the caret offset.
Lines 6928-6967 Link Here
6928
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6998
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
6929
 * </ul>
6999
 * </ul>
6930
 * @exception IllegalArgumentException <ul>
7000
 * @exception IllegalArgumentException <ul>
6931
 *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a 
7001
 *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
6932
 * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
7002
 * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
6933
 * </ul>
7003
 * </ul>
6934
 */
7004
 */
6935
public void setCaretOffset(int offset) {
7005
public void setCaretOffset(int offset) {
6936
	checkWidget();
7006
   checkWidget();
6937
	int length = getCharCount();
7007
   int length = getCharCount();
6938
				
7008
6939
	if (length > 0 && offset != caretOffset) {
7009
   if (length > 0 && offset != caretOffset) {
6940
		if (offset < 0) {
7010
      if (offset < 0) {
6941
			caretOffset = 0;
7011
         caretOffset = 0;
6942
		}
7012
      }
6943
		else
7013
      else
6944
		if (offset > length) {
7014
      if (offset > length) {
6945
			caretOffset = length;
7015
         caretOffset = length;
6946
		}
7016
      }
6947
		else {
7017
      else {
6948
			if (isLineDelimiter(offset)) {
7018
         if (isLineDelimiter(offset)) {
6949
				// offset is inside a multi byte line delimiter. This is an 
7019
            // offset is inside a multi byte line delimiter. This is an
6950
				// illegal operation and an exception is thrown. Fixes 1GDKK3R
7020
            // illegal operation and an exception is thrown. Fixes 1GDKK3R
6951
				SWT.error(SWT.ERROR_INVALID_ARGUMENT);
7021
            SWT.error(SWT.ERROR_INVALID_ARGUMENT);
6952
			}
7022
         }
6953
			caretOffset = offset;
7023
         caretOffset = offset;
6954
		}
7024
      }
6955
		// clear the selection if the caret is moved.
7025
      // clear the selection if the caret is moved.
6956
		// don't notify listeners about the selection change.
7026
      // don't notify listeners about the selection change.
6957
		clearSelection(false);
7027
      clearSelection(false);
6958
	}
7028
   }
6959
	// always update the caret location. fixes 1G8FODP
7029
   // always update the caret location. fixes 1G8FODP
6960
	setCaretLocation();
7030
   setCaretLocation();
6961
	if (isBidi()) {
7031
   if (isBidi()) {
6962
		setBidiKeyboardLanguage();	
7032
      setBidiKeyboardLanguage();
6963
	}
7033
   }
6964
}	
7034
}
6965
/**
7035
/**
6966
 * Copies the specified text range to the clipboard.  The text will be placed
7036
 * Copies the specified text range to the clipboard.  The text will be placed
6967
 * in the clipboard in plain text format and RTF format.
7037
 * in the clipboard in plain text format and RTF format.
Lines 6969-6989 Link Here
6969
 *
7039
 *
6970
 * @param start start index of the text
7040
 * @param start start index of the text
6971
 * @param length length of text to place in clipboard
7041
 * @param length length of text to place in clipboard
6972
 * 
7042
 *
6973
 * @exception SWTError, see Clipboard.setContents
7043
 * @exception SWTError, see Clipboard.setContents
6974
 * @see org.eclipse.swt.dnd.Clipboard.setContents
7044
 * @see org.eclipse.swt.dnd.Clipboard.setContents
6975
 */
7045
 */
6976
void setClipboardContent(int start, int length) throws SWTError {
7046
void setClipboardContent(int start, int length) throws SWTError {
6977
	RTFTransfer rtfTransfer = RTFTransfer.getInstance();
7047
   RTFTransfer rtfTransfer = RTFTransfer.getInstance();
6978
	TextTransfer plainTextTransfer = TextTransfer.getInstance();
7048
   TextTransfer plainTextTransfer = TextTransfer.getInstance();
6979
	RTFWriter rtfWriter = new RTFWriter(start, length);
7049
   RTFWriter rtfWriter = new RTFWriter(start, length);
6980
	TextWriter plainTextWriter = new TextWriter(start, length);
7050
   TextWriter plainTextWriter = new TextWriter(start, length);
6981
	String rtfText = getPlatformDelimitedText(rtfWriter);
7051
   String rtfText = getPlatformDelimitedText(rtfWriter);
6982
	String plainText = getPlatformDelimitedText(plainTextWriter);
7052
   String plainText = getPlatformDelimitedText(plainTextWriter);
6983
7053
6984
	clipboard.setContents(
7054
   clipboard.setContents(
6985
		new String[]{rtfText, plainText}, 
7055
      new String[]{rtfText, plainText},
6986
		new Transfer[]{rtfTransfer, plainTextTransfer});
7056
      new Transfer[]{rtfTransfer, plainTextTransfer});
6987
}
7057
}
6988
/**
7058
/**
6989
 * Sets the content implementation to use for text storage.
7059
 * Sets the content implementation to use for text storage.
Lines 6999-7064 Link Here
6999
 * </ul>
7069
 * </ul>
7000
 */
7070
 */
7001
public void setContent(StyledTextContent newContent) {
7071
public void setContent(StyledTextContent newContent) {
7002
	checkWidget();	
7072
   checkWidget();
7003
	if (newContent == null) {
7073
   if (newContent == null) {
7004
		SWT.error(SWT.ERROR_NULL_ARGUMENT);
7074
      SWT.error(SWT.ERROR_NULL_ARGUMENT);
7005
	}
7075
   }
7006
	if (content != null) {
7076
   if (content != null) {
7007
		content.removeTextChangeListener(textChangeListener);
7077
      content.removeTextChangeListener(textChangeListener);
7008
	}	
7078
   }
7009
	logicalContent = newContent;
7079
   logicalContent = newContent;
7010
	if (wordWrap) {
7080
   if (wordWrap) {
7011
	    content = new WrappedContent(renderer, logicalContent);
7081
       content = new WrappedContent(renderer, logicalContent);
7012
	}
7082
   }
7013
	else {
7083
   else {
7014
	    content = logicalContent;
7084
       content = logicalContent;
7015
	}
7085
   }
7016
	content.addTextChangeListener(textChangeListener);
7086
   content.addTextChangeListener(textChangeListener);
7017
	reset();
7087
   reset();
7018
}
7088
}
7019
/**
7089
/**
7020
 * Sets the receiver's cursor to the cursor specified by the
7090
 * Sets the receiver's cursor to the cursor specified by the
7021
 * argument.  Overridden to handle the null case since the 
7091
 * argument.  Overridden to handle the null case since the
7022
 * StyledText widget uses an ibeam as its default cursor.
7092
 * StyledText widget uses an ibeam as its default cursor.
7023
 *
7093
 *
7024
 * @see org.eclipse.swt.widgets.Control#setCursor
7094
 * @see org.eclipse.swt.widgets.Control#setCursor
7025
 */
7095
 */
7026
public void setCursor (Cursor cursor) {
7096
public void setCursor (Cursor cursor) {
7027
	if (cursor == null) {
7097
   if (cursor == null || cursor.equals(ibeamCursor)) {
7028
		super.setCursor(ibeamCursor);
7098
      customCursor = null;
7029
	} else {
7099
   } else {
7030
		super.setCursor(cursor);
7100
      customCursor = cursor;
7031
	}
7101
   }
7102
   internalSetCursor();
7032
}
7103
}
7033
/** 
7104
/**
7034
 * Sets whether the widget implements double click mouse behavior.
7105
 * Sets whether the widget implements double click mouse behavior.
7035
 * </p>
7106
 * </p>
7036
 *
7107
 *
7037
 * @param enable if true double clicking a word selects the word, if false
7108
 * @param enable if true double clicking a word selects the word, if false
7038
 * 	double clicks have the same effect as regular mouse clicks.
7109
 *    double clicks have the same effect as regular mouse clicks.
7039
 * @exception SWTException <ul>
7110
 * @exception SWTException <ul>
7040
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7111
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7041
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7112
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7042
 * </ul>
7113
 * </ul>
7043
 */
7114
 */
7044
public void setDoubleClickEnabled(boolean enable) {
7115
public void setDoubleClickEnabled(boolean enable) {
7045
	checkWidget();
7116
   checkWidget();
7046
	doubleClickEnabled = enable;
7117
   doubleClickEnabled = enable;
7047
}
7118
}
7048
/**
7119
/**
7049
 * Sets whether the widget content can be edited.
7120
 * Sets whether the widget content can be edited.
7050
 * </p>
7121
 * </p>
7051
 *
7122
 *
7052
 * @param editable if true content can be edited, if false content can not be 
7123
 * @param editable if true content can be edited, if false content can not be
7053
 * 	edited
7124
 *    edited
7054
 * @exception SWTException <ul>
7125
 * @exception SWTException <ul>
7055
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7126
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7056
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7127
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7057
 * </ul>
7128
 * </ul>
7058
 */
7129
 */
7059
public void setEditable(boolean editable) {
7130
public void setEditable(boolean editable) {
7060
	checkWidget();
7131
   checkWidget();
7061
	this.editable = editable;
7132
   this.editable = editable;
7062
}
7133
}
7063
/**
7134
/**
7064
 * Sets a new font to render text with.
7135
 * Sets a new font to render text with.
Lines 7074-7166 Link Here
7074
 * </ul>
7145
 * </ul>
7075
 */
7146
 */
7076
public void setFont(Font font) {
7147
public void setFont(Font font) {
7077
	checkWidget();
7148
   checkWidget();
7078
	int oldLineHeight = lineHeight;
7149
   int oldLineHeight = lineHeight;
7079
	
7150
7080
	super.setFont(font);	
7151
   super.setFont(font);
7081
	initializeRenderer();
7152
   initializeRenderer();
7082
	// keep the same top line visible. fixes 5815
7153
   // keep the same top line visible. fixes 5815
7083
	if (lineHeight != oldLineHeight) {
7154
   if (lineHeight != oldLineHeight) {
7084
		setVerticalScrollOffset(verticalScrollOffset * lineHeight / oldLineHeight, true);
7155
      setVerticalScrollOffset(verticalScrollOffset * lineHeight / oldLineHeight, true);
7085
		claimBottomFreeSpace();
7156
      claimBottomFreeSpace();
7086
	}
7157
   }
7087
	calculateContentWidth();
7158
   calculateContentWidth();
7088
	calculateScrollBars();
7159
   calculateScrollBars();
7089
	if (isBidi()) {
7160
   if (isBidi()) {
7090
		caretDirection = SWT.NULL;
7161
      caretDirection = SWT.NULL;
7091
		createCaretBitmaps();
7162
      createCaretBitmaps();
7092
		setBidiCaretDirection();
7163
      setBidiCaretDirection();
7093
	} 
7164
   }
7094
	else {
7165
   else {
7095
		Caret caret = getCaret();
7166
      Caret caret = getCaret();
7096
		if (caret != null) {
7167
      if (caret != null) {
7097
			caret.setSize(caret.getSize().x, lineHeight);
7168
         caret.setSize(caret.getSize().x, lineHeight);
7098
		}
7169
      }
7099
	}
7170
   }
7100
	// always set the caret location. Fixes 6685
7171
   // always set the caret location. Fixes 6685
7101
	setCaretLocation();
7172
   setCaretLocation();
7102
	super.redraw();
7173
   super.redraw();
7103
}
7174
}
7104
/**
7175
/**
7105
 * @see org.eclipse.swt.widgets.Control#setForeground
7176
 * @see org.eclipse.swt.widgets.Control#setForeground
7106
 */
7177
 */
7107
public void setForeground(Color color) {
7178
public void setForeground(Color color) {
7108
	checkWidget();
7179
   checkWidget();
7109
	super.setForeground(color);
7180
   super.setForeground(color);
7110
	foreground = color;
7181
   foreground = color;
7111
	redraw();
7182
   redraw();
7112
}
7183
}
7113
/** 
7184
/**
7114
 * Sets the horizontal scroll offset relative to the start of the line.
7185
 * Sets the horizontal scroll offset relative to the start of the line.
7115
 * Do nothing if there is no text set.
7186
 * Do nothing if there is no text set.
7116
 * <p>
7187
 * <p>
7117
 * <b>NOTE:</b> The horizontal index is reset to 0 when new text is set in the 
7188
 * <b>NOTE:</b> The horizontal index is reset to 0 when new text is set in the
7118
 * widget.
7189
 * widget.
7119
 * </p>
7190
 * </p>
7120
 *
7191
 *
7121
 * @param offset horizontal scroll offset relative to the start 
7192
 * @param offset horizontal scroll offset relative to the start
7122
 * 	of the line, measured in character increments starting at 0, if 
7193
 *    of the line, measured in character increments starting at 0, if
7123
 * 	equal to 0 the content is not scrolled, if > 0 = the content is scrolled.
7194
 *    equal to 0 the content is not scrolled, if > 0 = the content is scrolled.
7124
 * @exception SWTException <ul>
7195
 * @exception SWTException <ul>
7125
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7196
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7126
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7197
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7127
 * </ul>
7198
 * </ul>
7128
 */
7199
 */
7129
public void setHorizontalIndex(int offset) {
7200
public void setHorizontalIndex(int offset) {
7130
	checkWidget();
7201
   checkWidget();
7131
	int clientAreaWidth = getClientArea().width;
7202
   int clientAreaWidth = getClientArea().width;
7132
	if (getCharCount() == 0) {
7203
   if (getCharCount() == 0) {
7133
		return;
7204
      return;
7134
	}	
7205
   }
7135
	if (offset < 0) {
7206
   if (offset < 0) {
7136
		offset = 0;
7207
      offset = 0;
7137
	}
7208
   }
7138
	offset *= getHorizontalIncrement();
7209
   offset *= getHorizontalIncrement();
7139
	// allow any value if client area width is unknown or 0. 
7210
   // allow any value if client area width is unknown or 0.
7140
	// offset will be checked in resize handler.
7211
   // offset will be checked in resize handler.
7141
	// don't use isVisible since width is known even if widget 
7212
   // don't use isVisible since width is known even if widget
7142
	// is temporarily invisible
7213
   // is temporarily invisible
7143
	if (clientAreaWidth > 0) {
7214
   if (clientAreaWidth > 0) {
7144
		int width = lineCache.getWidth();
7215
      int width = lineCache.getWidth();
7145
		// prevent scrolling if the content fits in the client area.
7216
      // prevent scrolling if the content fits in the client area.
7146
		// align end of longest line with right border of client area
7217
      // align end of longest line with right border of client area
7147
		// if offset is out of range.
7218
      // if offset is out of range.
7148
		if (offset > width - clientAreaWidth) {
7219
      if (offset > width - clientAreaWidth) {
7149
			offset = Math.max(0, width - clientAreaWidth);
7220
         offset = Math.max(0, width - clientAreaWidth);
7150
		}
7221
      }
7151
	}
7222
   }
7152
	scrollHorizontalBar(offset - horizontalScrollOffset);
7223
   scrollHorizontalBar(offset - horizontalScrollOffset);
7153
}
7224
}
7154
/** 
7225
/**
7155
 * Sets the horizontal pixel offset relative to the start of the line.
7226
 * Sets the horizontal pixel offset relative to the start of the line.
7156
 * Do nothing if there is no text set.
7227
 * Do nothing if there is no text set.
7157
 * <p>
7228
 * <p>
7158
 * <b>NOTE:</b> The horizontal pixel offset is reset to 0 when new text 
7229
 * <b>NOTE:</b> The horizontal pixel offset is reset to 0 when new text
7159
 * is set in the widget.
7230
 * is set in the widget.
7160
 * </p>
7231
 * </p>
7161
 *
7232
 *
7162
 * @param pixel horizontal pixel offset relative to the start 
7233
 * @param pixel horizontal pixel offset relative to the start
7163
 * 	of the line.
7234
 *    of the line.
7164
 * @exception SWTException <ul>
7235
 * @exception SWTException <ul>
7165
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7236
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7166
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7237
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
Lines 7168-7252 Link Here
7168
 * @since 2.0
7239
 * @since 2.0
7169
 */
7240
 */
7170
public void setHorizontalPixel(int pixel) {
7241
public void setHorizontalPixel(int pixel) {
7171
	checkWidget();
7242
   checkWidget();
7172
	int clientAreaWidth = getClientArea().width;
7243
   int clientAreaWidth = getClientArea().width;
7173
	if (getCharCount() == 0) {
7244
   if (getCharCount() == 0) {
7174
		return;
7245
      return;
7175
	}	
7246
   }
7176
	if (pixel < 0) {
7247
   if (pixel < 0) {
7177
		pixel = 0;
7248
      pixel = 0;
7178
	}
7249
   }
7179
	// allow any value if client area width is unknown or 0. 
7250
   // allow any value if client area width is unknown or 0.
7180
	// offset will be checked in resize handler.
7251
   // offset will be checked in resize handler.
7181
	// don't use isVisible since width is known even if widget 
7252
   // don't use isVisible since width is known even if widget
7182
	// is temporarily invisible
7253
   // is temporarily invisible
7183
	if (clientAreaWidth > 0) {
7254
   if (clientAreaWidth > 0) {
7184
		int width = lineCache.getWidth();
7255
      int width = lineCache.getWidth();
7185
		// prevent scrolling if the content fits in the client area.
7256
      // prevent scrolling if the content fits in the client area.
7186
		// align end of longest line with right border of client area
7257
      // align end of longest line with right border of client area
7187
		// if offset is out of range.
7258
      // if offset is out of range.
7188
		if (pixel > width - clientAreaWidth) {
7259
      if (pixel > width - clientAreaWidth) {
7189
			pixel = Math.max(0, width - clientAreaWidth);
7260
         pixel = Math.max(0, width - clientAreaWidth);
7190
		}
7261
      }
7191
	}
7262
   }
7192
	scrollHorizontalBar(pixel - horizontalScrollOffset);
7263
   scrollHorizontalBar(pixel - horizontalScrollOffset);
7193
}
7264
}
7194
/**
7265
/**
7195
 * Adjusts the maximum and the page size of the horizontal scroll bar 
7266
 * Adjusts the maximum and the page size of the horizontal scroll bar
7196
 * to reflect content width changes.
7267
 * to reflect content width changes.
7197
 */
7268
 */
7198
void setHorizontalScrollBar() {
7269
void setHorizontalScrollBar() {
7199
	ScrollBar horizontalBar = getHorizontalBar();
7270
   ScrollBar horizontalBar = getHorizontalBar();
7200
	
7271
7201
	if (horizontalBar != null && horizontalBar.getVisible()) {
7272
   if (horizontalBar != null && horizontalBar.getVisible()) {
7202
		final int INACTIVE = 1;
7273
      final int INACTIVE = 1;
7203
		Rectangle clientArea = getClientArea();
7274
      Rectangle clientArea = getClientArea();
7204
		// only set the real values if the scroll bar can be used 
7275
      // only set the real values if the scroll bar can be used
7205
		// (ie. because the thumb size is less than the scroll maximum)
7276
      // (ie. because the thumb size is less than the scroll maximum)
7206
		// avoids flashing on Motif, fixes 1G7RE1J and 1G5SE92
7277
      // avoids flashing on Motif, fixes 1G7RE1J and 1G5SE92
7207
		if (clientArea.width < lineCache.getWidth()) {
7278
      if (clientArea.width < lineCache.getWidth()) {
7208
			horizontalBar.setValues(
7279
         horizontalBar.setValues(
7209
				horizontalBar.getSelection(),
7280
            horizontalBar.getSelection(),
7210
				horizontalBar.getMinimum(),
7281
            horizontalBar.getMinimum(),
7211
				lineCache.getWidth(),							// maximum
7282
            lineCache.getWidth(),                     // maximum
7212
				clientArea.width - leftMargin - rightMargin,	// thumb size
7283
            clientArea.width - leftMargin - rightMargin, // thumb size
7213
				horizontalBar.getIncrement(),
7284
            horizontalBar.getIncrement(),
7214
				clientArea.width - leftMargin - rightMargin);	// page size
7285
            clientArea.width - leftMargin - rightMargin);   // page size
7215
		}
7286
      }
7216
		else 
7287
      else
7217
		if (horizontalBar.getThumb() != INACTIVE || horizontalBar.getMaximum() != INACTIVE) {
7288
      if (horizontalBar.getThumb() != INACTIVE || horizontalBar.getMaximum() != INACTIVE) {
7218
			horizontalBar.setValues(
7289
         horizontalBar.setValues(
7219
				horizontalBar.getSelection(),
7290
            horizontalBar.getSelection(),
7220
				horizontalBar.getMinimum(),
7291
            horizontalBar.getMinimum(),
7221
				INACTIVE,
7292
            INACTIVE,
7222
				INACTIVE,
7293
            INACTIVE,
7223
				horizontalBar.getIncrement(),
7294
            horizontalBar.getIncrement(),
7224
				INACTIVE);
7295
            INACTIVE);
7225
		}
7296
      }
7226
	}
7297
   }
7227
}
7298
}
7228
/** 
7299
/**
7229
 * Sets the background color of the specified lines.
7300
 * Sets the background color of the specified lines.
7230
 * The background color is drawn for the width of the widget. All
7301
 * The background color is drawn for the width of the widget. All
7231
 * line background colors are discarded when setText is called.
7302
 * line background colors are discarded when setText is called.
7232
 * The text background color if defined in a StyleRange overlays the 
7303
 * The text background color if defined in a StyleRange overlays the
7233
 * line background color. Should not be called if a LineBackgroundListener 
7304
 * line background color. Should not be called if a LineBackgroundListener
7234
 * has been set since the listener maintains the line backgrounds.
7305
 * has been set since the listener maintains the line backgrounds.
7235
 * <p>
7306
 * <p>
7236
 * Line background colors are maintained relative to the line text, not the 
7307
 * Line background colors are maintained relative to the line text, not the
7237
 * line index that is specified in this method call.
7308
 * line index that is specified in this method call.
7238
 * During text changes, when entire lines are inserted or removed, the line 
7309
 * During text changes, when entire lines are inserted or removed, the line
7239
 * background colors that are associated with the lines after the change 
7310
 * background colors that are associated with the lines after the change
7240
 * will "move" with their respective text. An entire line is defined as 
7311
 * will "move" with their respective text. An entire line is defined as
7241
 * extending from the first character on a line to the last and including the 
7312
 * extending from the first character on a line to the last and including the
7242
 * line delimiter. 
7313
 * line delimiter.
7243
 * </p>
7314
 * </p>
7244
 * <p>
7315
 * <p>
7245
 * When two lines are joined by deleting a line delimiter, the top line 
7316
 * When two lines are joined by deleting a line delimiter, the top line
7246
 * background takes precedence and the color of the bottom line is deleted. 
7317
 * background takes precedence and the color of the bottom line is deleted.
7247
 * For all other text changes line background colors will remain unchanged. 
7318
 * For all other text changes line background colors will remain unchanged.
7248
 * </p>
7319
 * </p>
7249
 * 
7320
 *
7250
 * @param startLine first line the color is applied to, 0 based
7321
 * @param startLine first line the color is applied to, 0 based
7251
 * @param lineCount number of lines the color applies to.
7322
 * @param lineCount number of lines the color applies to.
7252
 * @param background line background color
7323
 * @param background line background color
Lines 7259-7345 Link Here
7259
 * </ul>
7330
 * </ul>
7260
 */
7331
 */
7261
public void setLineBackground(int startLine, int lineCount, Color background) {
7332
public void setLineBackground(int startLine, int lineCount, Color background) {
7262
	checkWidget();
7333
   checkWidget();
7263
	int partialBottomIndex = getPartialBottomIndex();
7334
   int partialBottomIndex = getPartialBottomIndex();
7264
	
7335
7265
	// this API can not be used if the client is providing the line background
7336
   // this API can not be used if the client is providing the line background
7266
	if (userLineBackground) {
7337
   if (userLineBackground) {
7267
		return;
7338
      return;
7268
	}
7339
   }
7269
	if (startLine < 0 || startLine + lineCount > logicalContent.getLineCount()) {
7340
   if (startLine < 0 || startLine + lineCount > logicalContent.getLineCount()) {
7270
		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
7341
      SWT.error(SWT.ERROR_INVALID_ARGUMENT);
7271
	} 
7342
   }
7272
	defaultLineStyler.setLineBackground(startLine, lineCount, background);
7343
   defaultLineStyler.setLineBackground(startLine, lineCount, background);
7273
	// do nothing if redraw range is completely invisible	
7344
   // do nothing if redraw range is completely invisible
7274
	if (startLine > partialBottomIndex || startLine + lineCount - 1 < topIndex) {
7345
   if (startLine > partialBottomIndex || startLine + lineCount - 1 < topIndex) {
7275
		return;
7346
      return;
7276
	}
7347
   }
7277
	// only redraw visible lines
7348
   // only redraw visible lines
7278
	if (startLine < topIndex) {
7349
   if (startLine < topIndex) {
7279
		lineCount -= topIndex - startLine;
7350
      lineCount -= topIndex - startLine;
7280
		startLine = topIndex;
7351
      startLine = topIndex;
7281
	}
7352
   }
7282
	if (startLine + lineCount - 1 > partialBottomIndex) {
7353
   if (startLine + lineCount - 1 > partialBottomIndex) {
7283
		lineCount = partialBottomIndex - startLine + 1;
7354
      lineCount = partialBottomIndex - startLine + 1;
7284
	}
7355
   }
7285
	startLine -= topIndex;
7356
   startLine -= topIndex;
7286
	super.redraw(
7357
   super.redraw(
7287
		leftMargin, startLine * lineHeight + topMargin, 
7358
      leftMargin, startLine * lineHeight + topMargin,
7288
		getClientArea().width - leftMargin - rightMargin, lineCount * lineHeight, true);
7359
      getClientArea().width - leftMargin - rightMargin, lineCount * lineHeight, true);
7289
}
7360
}
7290
/**
7361
/**
7291
 * Flips selection anchor based on word selection direction.
7362
 * Flips selection anchor based on word selection direction.
7292
 */
7363
 */
7293
void setMouseWordSelectionAnchor() {
7364
void setMouseWordSelectionAnchor() {
7294
	if (mouseDoubleClick == false) {
7365
   if (mouseDoubleClick == false) {
7295
		return;
7366
      return;
7296
	}
7367
   }
7297
 	if (caretOffset < doubleClickSelection.x) {
7368
   if (caretOffset < doubleClickSelection.x) {
7298
		selectionAnchor = doubleClickSelection.y;
7369
      selectionAnchor = doubleClickSelection.y;
7299
	}
7370
   }
7300
	else
7371
   else
7301
	if (caretOffset > doubleClickSelection.y) {
7372
   if (caretOffset > doubleClickSelection.y) {
7302
		selectionAnchor = doubleClickSelection.x;
7373
      selectionAnchor = doubleClickSelection.x;
7303
	}
7374
   }
7304
}
7375
}
7305
/**
7376
/**
7306
 * Adjusts the maximum and the page size of the scroll bars to 
7377
 * Adjusts the maximum and the page size of the scroll bars to
7307
 * reflect content width/length changes.
7378
 * reflect content width/length changes.
7308
 */
7379
 */
7309
void setScrollBars() {
7380
void setScrollBars() {
7310
	ScrollBar verticalBar = getVerticalBar();
7381
   ScrollBar verticalBar = getVerticalBar();
7311
	
7382
7312
	if (verticalBar != null) {
7383
   if (verticalBar != null) {
7313
		Rectangle clientArea = getClientArea();
7384
      Rectangle clientArea = getClientArea();
7314
		final int INACTIVE = 1;
7385
      final int INACTIVE = 1;
7315
		int maximum = content.getLineCount() * getVerticalIncrement();
7386
      int maximum = content.getLineCount() * getVerticalIncrement();
7316
		
7387
7317
		// only set the real values if the scroll bar can be used 
7388
      // only set the real values if the scroll bar can be used
7318
		// (ie. because the thumb size is less than the scroll maximum)
7389
      // (ie. because the thumb size is less than the scroll maximum)
7319
		// avoids flashing on Motif, fixes 1G7RE1J and 1G5SE92
7390
      // avoids flashing on Motif, fixes 1G7RE1J and 1G5SE92
7320
		if (clientArea.height < maximum) {
7391
      if (clientArea.height < maximum) {
7321
			verticalBar.setValues(
7392
         verticalBar.setValues(
7322
				verticalBar.getSelection(),
7393
            verticalBar.getSelection(),
7323
				verticalBar.getMinimum(),
7394
            verticalBar.getMinimum(),
7324
				maximum,
7395
            maximum,
7325
				clientArea.height,				// thumb size
7396
            clientArea.height,            // thumb size
7326
				verticalBar.getIncrement(),
7397
            verticalBar.getIncrement(),
7327
				clientArea.height);				// page size
7398
            clientArea.height);           // page size
7328
		}
7399
      }
7329
		else
7400
      else
7330
		if (verticalBar.getThumb() != INACTIVE || verticalBar.getMaximum() != INACTIVE) {
7401
      if (verticalBar.getThumb() != INACTIVE || verticalBar.getMaximum() != INACTIVE) {
7331
			verticalBar.setValues(
7402
         verticalBar.setValues(
7332
				verticalBar.getSelection(),
7403
            verticalBar.getSelection(),
7333
				verticalBar.getMinimum(),
7404
            verticalBar.getMinimum(),
7334
				INACTIVE,
7405
            INACTIVE,
7335
				INACTIVE,
7406
            INACTIVE,
7336
				verticalBar.getIncrement(),
7407
            verticalBar.getIncrement(),
7337
				INACTIVE);
7408
            INACTIVE);
7338
		}		
7409
      }
7339
	}
7410
   }
7340
	setHorizontalScrollBar();
7411
   setHorizontalScrollBar();
7341
}
7412
}
7342
/** 
7413
/**
7343
 * Sets the selection to the given position and scrolls it into view.  Equivalent to setSelection(start,start).
7414
 * Sets the selection to the given position and scrolls it into view.  Equivalent to setSelection(start,start).
7344
 * <p>
7415
 * <p>
7345
 *
7416
 *
Lines 7351-7374 Link Here
7351
 * </ul>
7422
 * </ul>
7352
 * @exception IllegalArgumentException <ul>
7423
 * @exception IllegalArgumentException <ul>
7353
 *   <li>ERROR_INVALID_RANGE when start is outside the widget content
7424
 *   <li>ERROR_INVALID_RANGE when start is outside the widget content
7354
 *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a 
7425
 *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
7355
 * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
7426
 * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
7356
 * </ul> 
7427
 * </ul>
7357
 */
7428
 */
7358
public void setSelection(int start) {
7429
public void setSelection(int start) {
7359
	// checkWidget test done in setSelectionRange	
7430
   // checkWidget test done in setSelectionRange
7360
	setSelection(start, start);
7431
   setSelection(start, start);
7361
}
7432
}
7362
/** 
7433
/**
7363
 * Sets the selection and scrolls it into view.
7434
 * Sets the selection and scrolls it into view.
7364
 * <p>
7435
 * <p>
7365
 * Indexing is zero based.  Text selections are specified in terms of
7436
 * Indexing is zero based.  Text selections are specified in terms of
7366
 * caret positions.  In a text widget that contains N characters, there are 
7437
 * caret positions.  In a text widget that contains N characters, there are
7367
 * N+1 caret positions, ranging from 0..N
7438
 * N+1 caret positions, ranging from 0..N
7368
 * </p>
7439
 * </p>
7369
 *
7440
 *
7370
 * @param point x=selection start offset, y=selection end offset
7441
 * @param point x=selection start offset, y=selection end offset
7371
 * 	The caret will be placed at the selection start when x > y.
7442
 *    The caret will be placed at the selection start when x > y.
7372
 * @see #setSelection(int,int)
7443
 * @see #setSelection(int,int)
7373
 * @exception SWTException <ul>
7444
 * @exception SWTException <ul>
7374
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7445
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
Lines 7377-7390 Link Here
7377
 * @exception IllegalArgumentException <ul>
7448
 * @exception IllegalArgumentException <ul>
7378
 *   <li>ERROR_NULL_ARGUMENT when point is null</li>
7449
 *   <li>ERROR_NULL_ARGUMENT when point is null</li>
7379
 *   <li>ERROR_INVALID_RANGE when start or end is outside the widget content
7450
 *   <li>ERROR_INVALID_RANGE when start or end is outside the widget content
7380
 *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a 
7451
 *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
7381
 * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
7452
 * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
7382
 * </ul> 
7453
 * </ul>
7383
 */
7454
 */
7384
public void setSelection(Point point) {
7455
public void setSelection(Point point) {
7385
	checkWidget();
7456
   checkWidget();
7386
	if (point == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);	
7457
   if (point == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
7387
	setSelection(point.x, point.y);
7458
   setSelection(point.x, point.y);
7388
}
7459
}
7389
/**
7460
/**
7390
 * Sets the receiver's selection background color to the color specified
7461
 * Sets the receiver's selection background color to the color specified
Lines 7394-7400 Link Here
7394
 * @param color the new color (or null)
7465
 * @param color the new color (or null)
7395
 *
7466
 *
7396
 * @exception IllegalArgumentException <ul>
7467
 * @exception IllegalArgumentException <ul>
7397
 *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> 
7468
 *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
7398
 * </ul>
7469
 * </ul>
7399
 * @exception SWTException <ul>
7470
 * @exception SWTException <ul>
7400
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7471
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
Lines 7403-7415 Link Here
7403
 * @since 2.1
7474
 * @since 2.1
7404
 */
7475
 */
7405
public void setSelectionBackground (Color color) {
7476
public void setSelectionBackground (Color color) {
7406
	checkWidget ();
7477
   checkWidget ();
7407
	if (color != null) {
7478
   if (color != null) {
7408
		if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
7479
      if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
7409
	}
7480
   }
7410
	selectionBackground = color;
7481
   selectionBackground = color;
7411
	redraw();
7482
   redraw();
7412
}	
7483
}
7413
/**
7484
/**
7414
 * Sets the receiver's selection foreground color to the color specified
7485
 * Sets the receiver's selection foreground color to the color specified
7415
 * by the argument, or to the default system color for the control
7486
 * by the argument, or to the default system color for the control
Lines 7418-7424 Link Here
7418
 * @param color the new color (or null)
7489
 * @param color the new color (or null)
7419
 *
7490
 *
7420
 * @exception IllegalArgumentException <ul>
7491
 * @exception IllegalArgumentException <ul>
7421
 *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> 
7492
 *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
7422
 * </ul>
7493
 * </ul>
7423
 * @exception SWTException <ul>
7494
 * @exception SWTException <ul>
7424
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7495
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
Lines 7427-7449 Link Here
7427
 * @since 2.1
7498
 * @since 2.1
7428
 */
7499
 */
7429
public void setSelectionForeground (Color color) {
7500
public void setSelectionForeground (Color color) {
7430
	checkWidget ();
7501
   checkWidget ();
7431
	if (color != null) {
7502
   if (color != null) {
7432
		if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
7503
      if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
7433
	}
7504
   }
7434
	selectionForeground = color;
7505
   selectionForeground = color;
7435
	redraw();
7506
   redraw();
7436
}	
7507
}
7437
/** 
7508
/**
7438
 * Sets the selection and scrolls it into view.
7509
 * Sets the selection and scrolls it into view.
7439
 * <p>
7510
 * <p>
7440
 * Indexing is zero based.  Text selections are specified in terms of
7511
 * Indexing is zero based.  Text selections are specified in terms of
7441
 * caret positions.  In a text widget that contains N characters, there are 
7512
 * caret positions.  In a text widget that contains N characters, there are
7442
 * N+1 caret positions, ranging from 0..N
7513
 * N+1 caret positions, ranging from 0..N
7443
 * </p>
7514
 * </p>
7444
 *
7515
 *
7445
 * @param start selection start offset. The caret will be placed at the 
7516
 * @param start selection start offset. The caret will be placed at the
7446
 * 	selection start when start > end.
7517
 *    selection start when start > end.
7447
 * @param end selection end offset
7518
 * @param end selection end offset
7448
 * @see #setSelectionRange(int,int)
7519
 * @see #setSelectionRange(int,int)
7449
 * @exception SWTException <ul>
7520
 * @exception SWTException <ul>
Lines 7452-7551 Link Here
7452
 * </ul>
7523
 * </ul>
7453
 * @exception IllegalArgumentException <ul>
7524
 * @exception IllegalArgumentException <ul>
7454
 *   <li>ERROR_INVALID_RANGE when start or end is outside the widget content
7525
 *   <li>ERROR_INVALID_RANGE when start or end is outside the widget content
7455
 *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a 
7526
 *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
7456
 * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
7527
 * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
7457
 * </ul>
7528
 * </ul>
7458
 */
7529
 */
7459
public void setSelection(int start, int end) {
7530
public void setSelection(int start, int end) {
7460
	// checkWidget test done in setSelectionRange
7531
   // checkWidget test done in setSelectionRange
7461
	setSelectionRange(start, end - start);
7532
   setSelectionRange(start, end - start);
7462
	showSelection();
7533
   showSelection();
7463
}
7534
}
7464
/** 
7535
/**
7465
 * Sets the selection. The new selection may not be visible. Call showSelection to scroll 
7536
 * Sets the selection. The new selection may not be visible. Call showSelection to scroll
7466
 * the selection into view.
7537
 * the selection into view.
7467
 * <p>
7538
 * <p>
7468
 *
7539
 *
7469
 * @param start offset of the first selected character, start >= 0 must be true.
7540
 * @param start offset of the first selected character, start >= 0 must be true.
7470
 * @param length number of characters to select, 0 <= start + length <= getCharCount() 
7541
 * @param length number of characters to select, 0 <= start + length <= getCharCount()
7471
 * 	must be true.
7542
 *    must be true.
7472
 * 	A negative length places the caret at the visual start of the selection.
7543
 *    A negative length places the caret at the visual start of the selection.
7473
 * @exception SWTException <ul>
7544
 * @exception SWTException <ul>
7474
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7545
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7475
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7546
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7476
 * </ul>
7547
 * </ul>
7477
 * @exception IllegalArgumentException <ul>
7548
 * @exception IllegalArgumentException <ul>
7478
 *   <li>ERROR_INVALID_RANGE when the range specified by start and length is outside the widget content
7549
 *   <li>ERROR_INVALID_RANGE when the range specified by start and length is outside the widget content
7479
 *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a 
7550
 *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
7480
 * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
7551
 * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
7481
 * </ul>
7552
 * </ul>
7482
 */
7553
 */
7483
public void setSelectionRange(int start, int length) {
7554
public void setSelectionRange(int start, int length) {
7484
	checkWidget();
7555
   checkWidget();
7485
	int contentLength = getCharCount();
7556
   int contentLength = getCharCount();
7486
	int end = start + length;
7557
   int end = start + length;
7487
	
7558
7488
	if (start < 0 || end < 0 || start > contentLength || end > contentLength) {
7559
   if (start < 0 || end < 0 || start > contentLength || end > contentLength) {
7489
		SWT.error(SWT.ERROR_INVALID_RANGE);
7560
      SWT.error(SWT.ERROR_INVALID_RANGE);
7490
	}
7561
   }
7491
	if (isLineDelimiter(start) || isLineDelimiter(end)) {
7562
   if (isLineDelimiter(start) || isLineDelimiter(end)) {
7492
		// the start offset or end offset of the selection range is inside a 
7563
      // the start offset or end offset of the selection range is inside a
7493
		// multi byte line delimiter. This is an illegal operation and an exception 
7564
      // multi byte line delimiter. This is an illegal operation and an exception
7494
		// is thrown. Fixes 1GDKK3R
7565
      // is thrown. Fixes 1GDKK3R
7495
		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
7566
      SWT.error(SWT.ERROR_INVALID_ARGUMENT);
7496
	}					
7567
   }
7497
	internalSetSelection(start, length, false);
7568
   internalSetSelection(start, length, false);
7498
	// always update the caret location. fixes 1G8FODP
7569
   // always update the caret location. fixes 1G8FODP
7499
	setCaretLocation();
7570
   setCaretLocation();
7500
	if (isBidi()) {
7571
   if (isBidi()) {
7501
		setBidiKeyboardLanguage();	
7572
      setBidiKeyboardLanguage();
7502
	}
7573
   }
7503
}
7574
}
7504
/** 
7575
/**
7505
 * Sets the selection. 
7576
 * Sets the selection.
7506
 * The new selection may not be visible. Call showSelection to scroll 
7577
 * The new selection may not be visible. Call showSelection to scroll
7507
 * the selection into view.
7578
 * the selection into view.
7508
 * <p>
7579
 * <p>
7509
 *
7580
 *
7510
 * @param start offset of the first selected character, start >= 0 must be true.
7581
 * @param start offset of the first selected character, start >= 0 must be true.
7511
 * @param length number of characters to select, 0 <= start + length 
7582
 * @param length number of characters to select, 0 <= start + length
7512
 * 	<= getCharCount() must be true. 
7583
 *    <= getCharCount() must be true.
7513
 * 	A negative length places the caret at the selection start.
7584
 *    A negative length places the caret at the selection start.
7514
 * @param sendEvent a Selection event is sent when set to true and when 
7585
 * @param sendEvent a Selection event is sent when set to true and when
7515
 * 	the selection is reset.
7586
 *    the selection is reset.
7516
 */
7587
 */
7517
void internalSetSelection(int start, int length, boolean sendEvent) {
7588
void internalSetSelection(int start, int length, boolean sendEvent) {
7518
	int end = start + length;
7589
   int end = start + length;
7519
	
7590
7520
	if (start > end) {
7591
   if (start > end) {
7521
		int temp = end;
7592
      int temp = end;
7522
		end = start;
7593
      end = start;
7523
		start = temp;
7594
      start = temp;
7524
	}
7595
   }
7525
	// is the selection range different or is the selection direction 
7596
   // is the selection range different or is the selection direction
7526
	// different?
7597
   // different?
7527
	if (selection.x != start || selection.y != end || 
7598
   if (selection.x != start || selection.y != end ||
7528
		(length > 0 && selectionAnchor != selection.x) || 
7599
      (length > 0 && selectionAnchor != selection.x) ||
7529
		(length < 0 && selectionAnchor != selection.y)) {
7600
      (length < 0 && selectionAnchor != selection.y)) {
7530
		clearSelection(sendEvent);
7601
      clearSelection(sendEvent);
7531
		if (length < 0) {
7602
      if (length < 0) {
7532
			selectionAnchor = selection.y = end;
7603
         selectionAnchor = selection.y = end;
7533
			caretOffset = selection.x = start;
7604
         caretOffset = selection.x = start;
7534
		}
7605
      }
7535
		else {
7606
      else {
7536
			selectionAnchor = selection.x = start;
7607
         selectionAnchor = selection.x = start;
7537
			caretOffset = selection.y = end;
7608
         caretOffset = selection.y = end;
7538
		}
7609
      }
7539
		internalRedrawRange(selection.x, selection.y - selection.x, true);
7610
      internalRedrawRange(selection.x, selection.y - selection.x, true);
7540
	}
7611
   }
7612
   // see if we need to update the mouse pointer
7613
   internalSetCursor();
7614
}
7615
/**
7616
 * Sets the mouse pointer based on whether it is over a selection and whether
7617
 * a custom cursor has been set.
7618
 */
7619
void internalSetCursor() {
7620
   Point pos = this.toControl(getDisplay().getCursorLocation());
7621
   internalSetCursor(pos.x, pos.y);
7622
}
7623
/**
7624
 * Sets the mouse pointer based on whether it is over a selection and whether
7625
 * a custom cursor has been set.
7626
 *
7627
 * @param x mouse x position in client coordinates
7628
 * @param y mouse y position in client coordinates
7629
 */
7630
void internalSetCursor(int x, int y) {
7631
   int mouseLineIndex = (y + verticalScrollOffset) / lineHeight;
7632
7633
   if (mouseLineIndex < 0 || mouseLineIndex > content.getLineCount() - 1) {
7634
      // mouse is above or below content, set ibeam/custom cursor
7635
      Cursor cursor = (customCursor != null) ? customCursor : ibeamCursor;
7636
      if (!currentCursor.equals(cursor)) {
7637
         currentCursor = cursor;
7638
         super.setCursor(cursor);
7639
      }
7640
   } else {
7641
      if (isMouseOverSelection(x, y)) {
7642
         // mouse is over a selection, set arrow cursor
7643
         if (!currentCursor.equals(arrowCursor)) {
7644
            currentCursor = arrowCursor;
7645
            super.setCursor(arrowCursor);
7646
         }
7647
      } else {
7648
         Cursor cursor = (customCursor != null) ? customCursor : ibeamCursor;
7649
         // mouse is not over a selection, set ibeam/custom cursor
7650
         if (!currentCursor.equals(cursor)) {
7651
            currentCursor = cursor;
7652
            super.setCursor(cursor);
7653
         }
7654
      }
7655
   }
7541
}
7656
}
7542
/** 
7657
/**
7543
 * Adds the specified style. The new style overwrites existing styles for the
7658
 * Adds the specified style. The new style overwrites existing styles for the
7544
 * specified range.  Existing style ranges are adjusted if they partially 
7659
 * specified range.  Existing style ranges are adjusted if they partially
7545
 * overlap with the new style, To clear an individual style, call setStyleRange 
7660
 * overlap with the new style, To clear an individual style, call setStyleRange
7546
 * with a StyleRange that has null attributes. 
7661
 * with a StyleRange that has null attributes.
7547
 * <p>
7662
 * <p>
7548
 * Should not be called if a LineStyleListener has been set since the 
7663
 * Should not be called if a LineStyleListener has been set since the
7549
 * listener maintains the styles.
7664
 * listener maintains the styles.
7550
 * </p>
7665
 * </p>
7551
 *
7666
 *
Lines 7557-7640 Link Here
7557
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7672
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7558
 * </ul>
7673
 * </ul>
7559
 * @exception IllegalArgumentException <ul>
7674
 * @exception IllegalArgumentException <ul>
7560
 *   <li>ERROR_INVALID_RANGE when the style range is outside the valid range (> getCharCount())</li> 
7675
 *   <li>ERROR_INVALID_RANGE when the style range is outside the valid range (> getCharCount())</li>
7561
 * </ul>
7676
 * </ul>
7562
 */
7677
 */
7563
public void setStyleRange(StyleRange range) {
7678
public void setStyleRange(StyleRange range) {
7564
	checkWidget();
7679
   checkWidget();
7565
	
7680
7566
	// this API can not be used if the client is providing the line styles
7681
   // this API can not be used if the client is providing the line styles
7567
	if (userLineStyle) {
7682
   if (userLineStyle) {
7568
		return;
7683
      return;
7569
	}
7684
   }
7570
 	// check the range, make sure it falls within the range of the
7685
   // check the range, make sure it falls within the range of the
7571
 	// text 
7686
   // text
7572
	if (range != null && range.start + range.length > content.getCharCount()) {
7687
   if (range != null && range.start + range.length > content.getCharCount()) {
7573
		SWT.error(SWT.ERROR_INVALID_RANGE);
7688
      SWT.error(SWT.ERROR_INVALID_RANGE);
7574
	} 	
7689
   }
7575
	if (range != null) {
7690
   if (range != null) {
7576
		boolean redrawFirstLine = false;
7691
      boolean redrawFirstLine = false;
7577
		boolean redrawLastLine = false;
7692
      boolean redrawLastLine = false;
7578
		int firstLine = content.getLineAtOffset(range.start);
7693
      int firstLine = content.getLineAtOffset(range.start);
7579
		int lastLine = content.getLineAtOffset(range.start + range.length);
7694
      int lastLine = content.getLineAtOffset(range.start + range.length);
7580
7695
7581
		// if the style is not visible, there is no need to redraw
7696
      // if the style is not visible, there is no need to redraw
7582
		boolean redrawLines = isAreaVisible(firstLine, lastLine);
7697
      boolean redrawLines = isAreaVisible(firstLine, lastLine);
7583
7698
7584
		if (!redrawLines) {
7699
      if (!redrawLines) {
7585
			defaultLineStyler.setStyleRange(range);
7700
         defaultLineStyler.setStyleRange(range);
7586
			lineCache.reset(firstLine, lastLine - firstLine + 1, true);
7701
         lineCache.reset(firstLine, lastLine - firstLine + 1, true);
7587
		} else {
7702
      } else {
7588
			// the first and last line needs to be redrawn completely if the 
7703
         // the first and last line needs to be redrawn completely if the
7589
			// font style is changing from SWT.NORMAL to something else or 
7704
         // font style is changing from SWT.NORMAL to something else or
7590
			// vice versa. fixes 1G7M5WE.
7705
         // vice versa. fixes 1G7M5WE.
7591
			int firstLineOffset = content.getOffsetAtLine(firstLine);
7706
         int firstLineOffset = content.getOffsetAtLine(firstLine);
7592
			int lastLineOffset = content.getOffsetAtLine(lastLine);
7707
         int lastLineOffset = content.getOffsetAtLine(lastLine);
7593
			if (isBidi()) {
7708
         if (isBidi()) {
7594
				if (firstLine != lastLine) {
7709
            if (firstLine != lastLine) {
7595
					redrawFirstLine = true;
7710
               redrawFirstLine = true;
7596
				}
7711
            }
7597
				redrawLastLine = true;
7712
            redrawLastLine = true;
7598
			} else {
7713
         } else {
7599
				redrawFirstLine = isRedrawFirstLine(new StyleRange[] {range}, firstLine, firstLineOffset);
7714
            redrawFirstLine = isRedrawFirstLine(new StyleRange[] {range}, firstLine, firstLineOffset);
7600
				if (lastLine != firstLine) {
7715
            if (lastLine != firstLine) {
7601
					redrawLastLine = isRedrawLastLine(new StyleRange[] {range}, lastLine, lastLineOffset);
7716
               redrawLastLine = isRedrawLastLine(new StyleRange[] {range}, lastLine, lastLineOffset);
7602
				}
7717
            }
7603
			}
7718
         }
7604
			defaultLineStyler.setStyleRange(range);
7719
         defaultLineStyler.setStyleRange(range);
7605
			// reset all lines affected by the style change but let the redraw
7720
         // reset all lines affected by the style change but let the redraw
7606
			// recalculate only those that are visible.
7721
         // recalculate only those that are visible.
7607
			lineCache.reset(firstLine, lastLine - firstLine + 1, true);
7722
         lineCache.reset(firstLine, lastLine - firstLine + 1, true);
7608
			internalRedrawRange(range.start, range.length, true);
7723
         internalRedrawRange(range.start, range.length, true);
7609
			if (redrawFirstLine) {
7724
         if (redrawFirstLine) {
7610
				redrawLine(firstLine, range.start - firstLineOffset);
7725
            redrawLine(firstLine, range.start - firstLineOffset);
7611
			}
7726
         }
7612
			if (redrawLastLine) {
7727
         if (redrawLastLine) {
7613
				redrawLine(lastLine, 0);
7728
            redrawLine(lastLine, 0);
7614
			}
7729
         }
7615
		}
7730
      }
7616
	} else {
7731
   } else {
7617
		// clearing all styles
7732
      // clearing all styles
7618
		defaultLineStyler.setStyleRange(range);
7733
      defaultLineStyler.setStyleRange(range);
7619
		lineCache.reset(0, content.getLineCount(), false);
7734
      lineCache.reset(0, content.getLineCount(), false);
7620
		redraw();
7735
      redraw();
7621
	}
7736
   }
7622
	
7737
7623
	// make sure that the caret is positioned correctly.
7738
   // make sure that the caret is positioned correctly.
7624
	// caret location may change if font style changes.
7739
   // caret location may change if font style changes.
7625
	// fixes 1G8FODP
7740
   // fixes 1G8FODP
7626
	setCaretLocation();
7741
   setCaretLocation();
7627
}
7742
}
7628
/** 
7743
/**
7629
 * Sets styles to be used for rendering the widget content. All styles 
7744
 * Sets styles to be used for rendering the widget content. All styles
7630
 * in the widget will be replaced with the given set of styles.
7745
 * in the widget will be replaced with the given set of styles.
7631
 * <p>
7746
 * <p>
7632
 * Should not be called if a LineStyleListener has been set since the 
7747
 * Should not be called if a LineStyleListener has been set since the
7633
 * listener maintains the styles.
7748
 * listener maintains the styles.
7634
 * </p>
7749
 * </p>
7635
 *
7750
 *
7636
 * @param ranges StyleRange objects containing the style information.
7751
 * @param ranges StyleRange objects containing the style information.
7637
 * The ranges should not overlap. The style rendering is undefined if 
7752
 * The ranges should not overlap. The style rendering is undefined if
7638
 * the ranges do overlap. Must not be null.
7753
 * the ranges do overlap. Must not be null.
7639
 * @exception SWTException <ul>
7754
 * @exception SWTException <ul>
7640
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7755
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
Lines 7642-7686 Link Here
7642
 * </ul>
7757
 * </ul>
7643
 * @exception IllegalArgumentException <ul>
7758
 * @exception IllegalArgumentException <ul>
7644
 *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
7759
 *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
7645
 *    <li>ERROR_INVALID_RANGE when the last of the style ranges is outside the valid range (> getCharCount())</li> 
7760
 *    <li>ERROR_INVALID_RANGE when the last of the style ranges is outside the valid range (> getCharCount())</li>
7646
 * </ul>
7761
 * </ul>
7647
 */
7762
 */
7648
public void setStyleRanges(StyleRange[] ranges) {
7763
public void setStyleRanges(StyleRange[] ranges) {
7649
	checkWidget();
7764
   checkWidget();
7650
	// this API can not be used if the client is providing the line styles
7765
   // this API can not be used if the client is providing the line styles
7651
	if (userLineStyle) {
7766
   if (userLineStyle) {
7652
		return;
7767
      return;
7653
	}
7768
   }
7654
 	if (ranges == null) {
7769
   if (ranges == null) {
7655
 		SWT.error(SWT.ERROR_NULL_ARGUMENT);
7770
      SWT.error(SWT.ERROR_NULL_ARGUMENT);
7656
 	}
7771
   }
7657
 	// check the last range, make sure it falls within the range of the
7772
   // check the last range, make sure it falls within the range of the
7658
 	// current text 
7773
   // current text
7659
 	if (ranges.length != 0) {
7774
   if (ranges.length != 0) {
7660
 		StyleRange last = ranges[ranges.length-1];
7775
      StyleRange last = ranges[ranges.length-1];
7661
 		int lastEnd = last.start + last.length;
7776
      int lastEnd = last.start + last.length;
7662
		int firstLine = content.getLineAtOffset(ranges[0].start);
7777
      int firstLine = content.getLineAtOffset(ranges[0].start);
7663
		int lastLine;
7778
      int lastLine;
7664
		if (lastEnd > content.getCharCount()) {
7779
      if (lastEnd > content.getCharCount()) {
7665
			SWT.error(SWT.ERROR_INVALID_RANGE);
7780
         SWT.error(SWT.ERROR_INVALID_RANGE);
7666
		} 	
7781
      }
7667
		lastLine = content.getLineAtOffset(lastEnd);
7782
      lastLine = content.getLineAtOffset(lastEnd);
7668
		// reset all lines affected by the style change
7783
      // reset all lines affected by the style change
7669
		lineCache.reset(firstLine, lastLine - firstLine + 1, true);
7784
      lineCache.reset(firstLine, lastLine - firstLine + 1, true);
7670
 	}
7785
   }
7671
 	else {
7786
   else {
7672
		// reset all lines
7787
      // reset all lines
7673
		lineCache.reset(0, content.getLineCount(), false);
7788
      lineCache.reset(0, content.getLineCount(), false);
7674
 	}
7789
   }
7675
	defaultLineStyler.setStyleRanges(ranges);
7790
   defaultLineStyler.setStyleRanges(ranges);
7676
	redraw(); // should only redraw affected area to avoid flashing
7791
   redraw(); // should only redraw affected area to avoid flashing
7677
	// make sure that the caret is positioned correctly.
7792
   // make sure that the caret is positioned correctly.
7678
	// caret location may change if font style changes.
7793
   // caret location may change if font style changes.
7679
	// fixes 1G8FODP
7794
   // fixes 1G8FODP
7680
	setCaretLocation();
7795
   setCaretLocation();
7681
}
7796
}
7682
/** 
7797
/**
7683
 * Sets the tab width. 
7798
 * Sets the tab width.
7684
 * <p>
7799
 * <p>
7685
 *
7800
 *
7686
 * @param tabs tab width measured in characters.
7801
 * @param tabs tab width measured in characters.
Lines 7690-7726 Link Here
7690
 * </ul>
7805
 * </ul>
7691
 */
7806
 */
7692
public void setTabs(int tabs) {
7807
public void setTabs(int tabs) {
7693
	checkWidget();	
7808
   checkWidget();
7694
	tabLength = tabs;
7809
   tabLength = tabs;
7695
	renderer.setTabLength(tabLength);
7810
   renderer.setTabLength(tabLength);
7696
	if (caretOffset > 0) {
7811
   if (caretOffset > 0) {
7697
		caretOffset = 0;
7812
      caretOffset = 0;
7698
		if (isBidi()) {
7813
      if (isBidi()) {
7699
			showBidiCaret();
7814
         showBidiCaret();
7700
		}
7815
      }
7701
		else {
7816
      else {
7702
			showCaret();
7817
         showCaret();
7703
		}
7818
      }
7704
		clearSelection(false);
7819
      clearSelection(false);
7705
	}
7820
   }
7706
	// reset all line widths when the tab width changes
7821
   // reset all line widths when the tab width changes
7707
	lineCache.reset(0, content.getLineCount(), false);
7822
   lineCache.reset(0, content.getLineCount(), false);
7708
	redraw();
7823
   redraw();
7709
}
7824
}
7710
/** 
7825
/**
7711
 * Sets the widget content. 
7826
 * Sets the widget content.
7712
 * If the widget has the SWT.SINGLE style and "text" contains more than 
7827
 * If the widget has the SWT.SINGLE style and "text" contains more than
7713
 * one line, only the first line is rendered but the text is stored 
7828
 * one line, only the first line is rendered but the text is stored
7714
 * unchanged. A subsequent call to getText will return the same text 
7829
 * unchanged. A subsequent call to getText will return the same text
7715
 * that was set.
7830
 * that was set.
7716
 * <p>
7831
 * <p>
7717
 * <b>Note:</b> Only a single line of text should be set when the SWT.SINGLE 
7832
 * <b>Note:</b> Only a single line of text should be set when the SWT.SINGLE
7718
 * style is used.
7833
 * style is used.
7719
 * </p>
7834
 * </p>
7720
 *
7835
 *
7721
 * @param text new widget content. Replaces existing content. Line styles 
7836
 * @param text new widget content. Replaces existing content. Line styles
7722
 * 	that were set using StyledText API are discarded.  The
7837
 *    that were set using StyledText API are discarded.  The
7723
 * 	current selection is also discarded.
7838
 *    current selection is also discarded.
7724
 * @exception SWTException <ul>
7839
 * @exception SWTException <ul>
7725
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7840
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7726
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7841
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
Lines 7730-7761 Link Here
7730
 * </ul>
7845
 * </ul>
7731
 */
7846
 */
7732
public void setText(String text) {
7847
public void setText(String text) {
7733
	checkWidget();
7848
   checkWidget();
7734
	Event event = new Event();
7849
   Event event = new Event();
7735
	
7850
7736
	if (text == null) {
7851
   if (text == null) {
7737
		SWT.error(SWT.ERROR_NULL_ARGUMENT);
7852
      SWT.error(SWT.ERROR_NULL_ARGUMENT);
7738
	}
7853
   }
7739
	event.start = 0;
7854
   event.start = 0;
7740
	event.end = getCharCount();
7855
   event.end = getCharCount();
7741
	event.text = text;
7856
   event.text = text;
7742
	event.doit = true;	
7857
   event.doit = true;
7743
	notifyListeners(SWT.Verify, event);
7858
   notifyListeners(SWT.Verify, event);
7744
	if (event.doit) {
7859
   if (event.doit) {
7745
		StyledTextEvent styledTextEvent = null;
7860
      StyledTextEvent styledTextEvent = null;
7746
		
7861
7747
		if (isListening(ExtendedModify)) {		
7862
      if (isListening(ExtendedModify)) {
7748
			styledTextEvent = new StyledTextEvent(logicalContent);
7863
         styledTextEvent = new StyledTextEvent(logicalContent);
7749
			styledTextEvent.start = event.start;
7864
         styledTextEvent.start = event.start;
7750
			styledTextEvent.end = event.start + event.text.length();
7865
         styledTextEvent.end = event.start + event.text.length();
7751
			styledTextEvent.text = content.getTextRange(event.start, event.end - event.start);
7866
         styledTextEvent.text = content.getTextRange(event.start, event.end - event.start);
7752
		}
7867
      }
7753
		content.setText(event.text);
7868
      content.setText(event.text);
7754
		notifyListeners(SWT.Modify, event);	
7869
      notifyListeners(SWT.Modify, event);
7755
		if (styledTextEvent != null) {
7870
      if (styledTextEvent != null) {
7756
			notifyListeners(ExtendedModify, styledTextEvent);
7871
         notifyListeners(ExtendedModify, styledTextEvent);
7757
		}
7872
      }
7758
	}
7873
   }
7759
}
7874
}
7760
/**
7875
/**
7761
 * Sets the text limit.
7876
 * Sets the text limit.
Lines 7774-7823 Link Here
7774
 * </ul>
7889
 * </ul>
7775
 */
7890
 */
7776
public void setTextLimit(int limit) {
7891
public void setTextLimit(int limit) {
7777
	checkWidget();
7892
   checkWidget();
7778
	if (limit == 0) {
7893
   if (limit == 0) {
7779
		SWT.error(SWT.ERROR_CANNOT_BE_ZERO);
7894
      SWT.error(SWT.ERROR_CANNOT_BE_ZERO);
7780
	}
7895
   }
7781
	textLimit = limit;
7896
   textLimit = limit;
7782
}
7897
}
7783
/**
7898
/**
7784
 * Sets the top index. Do nothing if there is no text set.
7899
 * Sets the top index. Do nothing if there is no text set.
7785
 * <p>
7900
 * <p>
7786
 * The top index is the index of the line that is currently at the top 
7901
 * The top index is the index of the line that is currently at the top
7787
 * of the widget. The top index changes when the widget is scrolled.
7902
 * of the widget. The top index changes when the widget is scrolled.
7788
 * Indexing starts from zero.
7903
 * Indexing starts from zero.
7789
 * Note: The top index is reset to 0 when new text is set in the widget.
7904
 * Note: The top index is reset to 0 when new text is set in the widget.
7790
 * </p>
7905
 * </p>
7791
 *
7906
 *
7792
 * @param index new top index. Must be between 0 and 
7907
 * @param index new top index. Must be between 0 and
7793
 * 	getLineCount() - fully visible lines per page. If no lines are fully 
7908
 *    getLineCount() - fully visible lines per page. If no lines are fully
7794
 * 	visible the maximum value is getLineCount() - 1. An out of range 
7909
 *    visible the maximum value is getLineCount() - 1. An out of range
7795
 * 	index will be adjusted accordingly.
7910
 *    index will be adjusted accordingly.
7796
 * @exception SWTException <ul>
7911
 * @exception SWTException <ul>
7797
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7912
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7798
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7913
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7799
 * </ul>
7914
 * </ul>
7800
 */
7915
 */
7801
public void setTopIndex(int topIndex) {
7916
public void setTopIndex(int topIndex) {
7802
	checkWidget();
7917
   checkWidget();
7803
	int lineCount = logicalContent.getLineCount();
7918
   int lineCount = logicalContent.getLineCount();
7804
	int pageSize = Math.max(1, Math.min(lineCount, getLineCountWhole()));
7919
   int pageSize = Math.max(1, Math.min(lineCount, getLineCountWhole()));
7805
	
7920
7806
	if (getCharCount() == 0) {
7921
   if (getCharCount() == 0) {
7807
		return;
7922
      return;
7808
	}	
7923
   }
7809
	if (topIndex < 0) {
7924
   if (topIndex < 0) {
7810
		topIndex = 0;
7925
      topIndex = 0;
7811
	}
7926
   }
7812
	else 
7927
   else
7813
	if (topIndex > lineCount - pageSize) {
7928
   if (topIndex > lineCount - pageSize) {
7814
		topIndex = lineCount - pageSize;
7929
      topIndex = lineCount - pageSize;
7815
	}
7930
   }
7816
	if (wordWrap) {
7931
   if (wordWrap) {
7817
		int logicalLineOffset = logicalContent.getOffsetAtLine(topIndex);
7932
      int logicalLineOffset = logicalContent.getOffsetAtLine(topIndex);
7818
		topIndex = content.getLineAtOffset(logicalLineOffset);
7933
      topIndex = content.getLineAtOffset(logicalLineOffset);
7819
	}
7934
   }
7820
	setVerticalScrollOffset(topIndex * getVerticalIncrement(), true);
7935
   setVerticalScrollOffset(topIndex * getVerticalIncrement(), true);
7821
}
7936
}
7822
/**
7937
/**
7823
 * Sets the top pixel offset. Do nothing if there is no text set.
7938
 * Sets the top pixel offset. Do nothing if there is no text set.
Lines 7828-7836 Link Here
7828
 * Note: The top pixel is reset to 0 when new text is set in the widget.
7943
 * Note: The top pixel is reset to 0 when new text is set in the widget.
7829
 * </p>
7944
 * </p>
7830
 *
7945
 *
7831
 * @param pixel new top pixel offset. Must be between 0 and 
7946
 * @param pixel new top pixel offset. Must be between 0 and
7832
 * 	(getLineCount() - visible lines per page) / getLineHeight()). An out
7947
 *    (getLineCount() - visible lines per page) / getLineHeight()). An out
7833
 * 	of range offset will be adjusted accordingly.
7948
 *    of range offset will be adjusted accordingly.
7834
 * @exception SWTException <ul>
7949
 * @exception SWTException <ul>
7835
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7950
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7836
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7951
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
Lines 7838-7967 Link Here
7838
 * @since 2.0
7953
 * @since 2.0
7839
 */
7954
 */
7840
public void setTopPixel(int pixel) {
7955
public void setTopPixel(int pixel) {
7841
	checkWidget();
7956
   checkWidget();
7842
	int lineCount =content.getLineCount();
7957
   int lineCount =content.getLineCount();
7843
	int height = getClientArea().height;
7958
   int height = getClientArea().height;
7844
	int maxTopPixel = Math.max(0, lineCount * getVerticalIncrement() - height);
7959
   int maxTopPixel = Math.max(0, lineCount * getVerticalIncrement() - height);
7845
	
7960
7846
	if (getCharCount() == 0) {
7961
   if (getCharCount() == 0) {
7847
		return;
7962
      return;
7848
	}	
7963
   }
7849
	if (pixel < 0) {
7964
   if (pixel < 0) {
7850
		pixel = 0;
7965
      pixel = 0;
7851
	}
7966
   }
7852
	else 
7967
   else
7853
	if (pixel > maxTopPixel) {
7968
   if (pixel > maxTopPixel) {
7854
		pixel = maxTopPixel;
7969
      pixel = maxTopPixel;
7855
	}
7970
   }
7856
	setVerticalScrollOffset(pixel, true);
7971
   setVerticalScrollOffset(pixel, true);
7857
}
7972
}
7858
/**
7973
/**
7859
 * Scrolls the widget vertically.
7974
 * Scrolls the widget vertically.
7860
 * <p>
7975
 * <p>
7861
 *
7976
 *
7862
 * @param pixelOffset the new vertical scroll offset
7977
 * @param pixelOffset the new vertical scroll offset
7863
 * @param adjustScrollBar 
7978
 * @param adjustScrollBar
7864
 * 	true= the scroll thumb will be moved to reflect the new scroll offset.
7979
 *    true= the scroll thumb will be moved to reflect the new scroll offset.
7865
 * 	false = the scroll thumb will not be moved
7980
 *    false = the scroll thumb will not be moved
7866
 * @return 
7981
 * @return
7867
 *	true=the widget was scrolled 
7982
 * true=the widget was scrolled
7868
 *	false=the widget was not scrolled, the given offset is not valid.
7983
 * false=the widget was not scrolled, the given offset is not valid.
7869
 */
7984
 */
7870
boolean setVerticalScrollOffset(int pixelOffset, boolean adjustScrollBar) {
7985
boolean setVerticalScrollOffset(int pixelOffset, boolean adjustScrollBar) {
7871
	Rectangle clientArea;
7986
   Rectangle clientArea;
7872
	ScrollBar verticalBar = getVerticalBar();
7987
   ScrollBar verticalBar = getVerticalBar();
7873
	
7988
7874
	if (pixelOffset == verticalScrollOffset) {
7989
   if (pixelOffset == verticalScrollOffset) {
7875
		return false;
7990
      return false;
7876
	}
7991
   }
7877
	if (verticalBar != null && adjustScrollBar) {
7992
   if (verticalBar != null && adjustScrollBar) {
7878
		verticalBar.setSelection(pixelOffset);
7993
      verticalBar.setSelection(pixelOffset);
7879
	}
7994
   }
7880
	clientArea = getClientArea();
7995
   clientArea = getClientArea();
7881
	scroll(
7996
   scroll(
7882
		0, 0, 									// destination x, y
7997
      0, 0,                            // destination x, y
7883
		0, pixelOffset - verticalScrollOffset,	// source x, y
7998
      0, pixelOffset - verticalScrollOffset, // source x, y
7884
		clientArea.width, clientArea.height, true);
7999
      clientArea.width, clientArea.height, true);
7885
8000
7886
	verticalScrollOffset = pixelOffset;
8001
   verticalScrollOffset = pixelOffset;
7887
	calculateTopIndex();
8002
   calculateTopIndex();
7888
	setCaretLocation();
8003
   setCaretLocation();
7889
	return true;
8004
   return true;
7890
}
8005
}
7891
/**
8006
/**
7892
 * Scrolls the specified location into view.
8007
 * Scrolls the specified location into view.
7893
 * <p>
8008
 * <p>
7894
 * 
8009
 *
7895
 * @param x the x coordinate that should be made visible.
8010
 * @param x the x coordinate that should be made visible.
7896
 * @param line the line that should be made visible. Relative to the
8011
 * @param line the line that should be made visible. Relative to the
7897
 *	first line in the document.
8012
 * first line in the document.
7898
 * @return 
8013
 * @return
7899
 *	true=the widget was scrolled to make the specified location visible. 
8014
 * true=the widget was scrolled to make the specified location visible.
7900
 *	false=the specified location is already visible, the widget was 
8015
 * false=the specified location is already visible, the widget was
7901
 *	not scrolled. 	
8016
 * not scrolled.
7902
 */
8017
 */
7903
boolean showLocation(int x, int line) {
8018
boolean showLocation(int x, int line) {
7904
	int clientAreaWidth = getClientArea().width - leftMargin - rightMargin;
8019
   int clientAreaWidth = getClientArea().width - leftMargin - rightMargin;
7905
	int verticalIncrement = getVerticalIncrement();
8020
   int verticalIncrement = getVerticalIncrement();
7906
	int horizontalIncrement = clientAreaWidth / 4;
8021
   int horizontalIncrement = clientAreaWidth / 4;
7907
	boolean scrolled = false;		
8022
   boolean scrolled = false;
7908
	
8023
7909
	if (x < leftMargin) {
8024
   if (x < leftMargin) {
7910
		// always make 1/4 of a page visible
8025
      // always make 1/4 of a page visible
7911
		x = Math.max(horizontalScrollOffset * -1, x - horizontalIncrement);	
8026
      x = Math.max(horizontalScrollOffset * -1, x - horizontalIncrement);
7912
		scrolled = scrollHorizontalBar(x);
8027
      scrolled = scrollHorizontalBar(x);
7913
	}
8028
   }
7914
	else 
8029
   else
7915
	if (x >= clientAreaWidth) {
8030
   if (x >= clientAreaWidth) {
7916
		// always make 1/4 of a page visible
8031
      // always make 1/4 of a page visible
7917
		x = Math.min(lineCache.getWidth() - horizontalScrollOffset, x + horizontalIncrement);
8032
      x = Math.min(lineCache.getWidth() - horizontalScrollOffset, x + horizontalIncrement);
7918
		scrolled = scrollHorizontalBar(x - clientAreaWidth);
8033
      scrolled = scrollHorizontalBar(x - clientAreaWidth);
7919
	}
8034
   }
7920
	if (line < topIndex) {
8035
   if (line < topIndex) {
7921
		scrolled = setVerticalScrollOffset(line * verticalIncrement, true);
8036
      scrolled = setVerticalScrollOffset(line * verticalIncrement, true);
7922
	}
8037
   }
7923
	else
8038
   else
7924
	if (line > getBottomIndex()) {
8039
   if (line > getBottomIndex()) {
7925
		scrolled = setVerticalScrollOffset((line + 1) * verticalIncrement - getClientArea().height, true);
8040
      scrolled = setVerticalScrollOffset((line + 1) * verticalIncrement - getClientArea().height, true);
7926
	}
8041
   }
7927
	return scrolled;
8042
   return scrolled;
7928
}
8043
}
7929
/**
8044
/**
7930
 * Sets the caret location and scrolls the caret offset into view.
8045
 * Sets the caret location and scrolls the caret offset into view.
7931
 */
8046
 */
7932
void showCaret() {
8047
void showCaret() {
7933
	int caretLine = content.getLineAtOffset(caretOffset);
8048
   int caretLine = content.getLineAtOffset(caretOffset);
7934
	
8049
7935
	showCaret(caretLine);
8050
   showCaret(caretLine);
7936
}
8051
}
7937
/**
8052
/**
7938
 * Sets the caret location and scrolls the caret offset into view.
8053
 * Sets the caret location and scrolls the caret offset into view.
7939
 */
8054
 */
7940
void showCaret(int caretLine) {
8055
void showCaret(int caretLine) {
7941
	int lineOffset = content.getOffsetAtLine(caretLine);
8056
   int lineOffset = content.getOffsetAtLine(caretLine);
7942
	String lineText = content.getLine(caretLine);
8057
   String lineText = content.getLine(caretLine);
7943
	int offsetInLine = caretOffset - lineOffset;
8058
   int offsetInLine = caretOffset - lineOffset;
7944
	int xAtOffset = getXAtOffset(lineText, caretLine, offsetInLine);	
8059
   int xAtOffset = getXAtOffset(lineText, caretLine, offsetInLine);
7945
	boolean scrolled = showLocation(xAtOffset, caretLine);
8060
   boolean scrolled = showLocation(xAtOffset, caretLine);
7946
	boolean setWrapCaretLocation = false;
8061
   boolean setWrapCaretLocation = false;
7947
	Caret caret = getCaret();
8062
   Caret caret = getCaret();
7948
8063
7949
	if (wordWrap && caret != null) {
8064
   if (wordWrap && caret != null) {
7950
		int caretY = caret.getLocation().y;
8065
      int caretY = caret.getLocation().y;
7951
		if ((caretY + verticalScrollOffset) / getVerticalIncrement() - 1 != caretLine) {
8066
      if ((caretY + verticalScrollOffset) / getVerticalIncrement() - 1 != caretLine) {
7952
			setWrapCaretLocation = true;
8067
         setWrapCaretLocation = true;
7953
		}
8068
      }
7954
	}
8069
   }
7955
	if (scrolled == false || setWrapCaretLocation) {
8070
   if (scrolled == false || setWrapCaretLocation) {
7956
		// set the caret location if a scroll operation did not set it (as a 
8071
      // set the caret location if a scroll operation did not set it (as a
7957
		// sideeffect of scrolling) or when in word wrap mode and the caret 
8072
      // sideeffect of scrolling) or when in word wrap mode and the caret
7958
		// line was explicitly specified (i.e., because getWrapCaretLine does 
8073
      // line was explicitly specified (i.e., because getWrapCaretLine does
7959
		// not return the desired line causing scrolling to not set it correctly)
8074
      // not return the desired line causing scrolling to not set it correctly)
7960
		setCaretLocation(xAtOffset, caretLine);
8075
      setCaretLocation(xAtOffset, caretLine);
7961
	}
8076
   }
7962
	if (isBidi()) {
8077
   if (isBidi()) {
7963
		setBidiKeyboardLanguage();
8078
      setBidiKeyboardLanguage();
7964
	}
8079
   }
7965
}
8080
}
7966
/**
8081
/**
7967
 * Scrolls the specified offset into view.
8082
 * Scrolls the specified offset into view.
Lines 7970-7982 Link Here
7970
 * @param offset offset that should be scolled into view
8085
 * @param offset offset that should be scolled into view
7971
 */
8086
 */
7972
void showOffset(int offset) {
8087
void showOffset(int offset) {
7973
	int line = content.getLineAtOffset(offset);
8088
   int line = content.getLineAtOffset(offset);
7974
	int lineOffset = content.getOffsetAtLine(line);
8089
   int lineOffset = content.getOffsetAtLine(line);
7975
	int offsetInLine = offset - lineOffset;
8090
   int offsetInLine = offset - lineOffset;
7976
	String lineText = content.getLine(line);
8091
   String lineText = content.getLine(line);
7977
	int xAtOffset = getXAtOffset(lineText, line, offsetInLine);
8092
   int xAtOffset = getXAtOffset(lineText, line, offsetInLine);
7978
	
8093
7979
	showLocation(xAtOffset, line);	
8094
   showLocation(xAtOffset, line);
7980
}
8095
}
7981
/**
8096
/**
7982
/**
8097
/**
Lines 7991-8082 Link Here
7991
 * </ul>
8106
 * </ul>
7992
 */
8107
 */
7993
public void showSelection() {
8108
public void showSelection() {
7994
	checkWidget();
8109
   checkWidget();
7995
	boolean selectionFits;
8110
   boolean selectionFits;
7996
	int startOffset, startLine, startX, endOffset, endLine, endX, offsetInLine;
8111
   int startOffset, startLine, startX, endOffset, endLine, endX, offsetInLine;
7997
8112
7998
	// is selection from right-to-left?
8113
   // is selection from right-to-left?
7999
	boolean rightToLeft = caretOffset == selection.x;
8114
   boolean rightToLeft = caretOffset == selection.x;
8000
8115
8001
	if (rightToLeft) {
8116
   if (rightToLeft) {
8002
		startOffset = selection.y;
8117
      startOffset = selection.y;
8003
		endOffset = selection.x;
8118
      endOffset = selection.x;
8004
	} else {
8119
   } else {
8005
		startOffset = selection.x;
8120
      startOffset = selection.x;
8006
		endOffset = selection.y;
8121
      endOffset = selection.y;
8007
	}
8122
   }
8008
	
8123
8009
	// calculate the logical start and end values for the selection
8124
   // calculate the logical start and end values for the selection
8010
	startLine = content.getLineAtOffset(startOffset);
8125
   startLine = content.getLineAtOffset(startOffset);
8011
	offsetInLine = startOffset - content.getOffsetAtLine(startLine);
8126
   offsetInLine = startOffset - content.getOffsetAtLine(startLine);
8012
	startX = getXAtOffset(content.getLine(startLine), startLine, offsetInLine);	
8127
   startX = getXAtOffset(content.getLine(startLine), startLine, offsetInLine);
8013
	endLine  = content.getLineAtOffset(endOffset);
8128
   endLine  = content.getLineAtOffset(endOffset);
8014
	offsetInLine = endOffset - content.getOffsetAtLine(endLine);
8129
   offsetInLine = endOffset - content.getOffsetAtLine(endLine);
8015
	endX = getXAtOffset(content.getLine(endLine), endLine, offsetInLine);	
8130
   endX = getXAtOffset(content.getLine(endLine), endLine, offsetInLine);
8016
8131
8017
	// can the selection be fully displayed within the widget's visible width?
8132
   // can the selection be fully displayed within the widget's visible width?
8018
	int w = getClientArea().width;
8133
   int w = getClientArea().width;
8019
	if (rightToLeft) {
8134
   if (rightToLeft) {
8020
		selectionFits = startX - endX <= w;
8135
      selectionFits = startX - endX <= w;
8021
	} else {
8136
   } else {
8022
		selectionFits = endX - startX <= w;
8137
      selectionFits = endX - startX <= w;
8023
	}
8138
   }
8024
	
8139
8025
	if (selectionFits) {
8140
   if (selectionFits) {
8026
		// show as much of the selection as possible by first showing
8141
      // show as much of the selection as possible by first showing
8027
		// the start of the selection
8142
      // the start of the selection
8028
		showLocation(startX, startLine);
8143
      showLocation(startX, startLine);
8029
		// endX value could change if showing startX caused a scroll to occur
8144
      // endX value could change if showing startX caused a scroll to occur
8030
		endX = getXAtOffset(content.getLine(endLine), endLine, offsetInLine);	
8145
      endX = getXAtOffset(content.getLine(endLine), endLine, offsetInLine);
8031
		showLocation(endX, endLine);
8146
      showLocation(endX, endLine);
8032
	} else {
8147
   } else {
8033
		// just show the end of the selection since the selection start 
8148
      // just show the end of the selection since the selection start
8034
		// will not be visible
8149
      // will not be visible
8035
		showLocation(endX, endLine);
8150
      showLocation(endX, endLine);
8036
	}	 
8151
   }
8037
}
8152
}
8038
/**
8153
/**
8039
 * Updates the caret direction when a delete operation occured based on 
8154
 * Updates the caret direction when a delete operation occured based on
8040
 * the type of the delete operation (next/previous character) and the 
8155
 * the type of the delete operation (next/previous character) and the
8041
 * caret location (at a direction boundary or inside a direction segment).
8156
 * caret location (at a direction boundary or inside a direction segment).
8042
 * The intent is to place the caret at the visual location where a
8157
 * The intent is to place the caret at the visual location where a
8043
 * character was deleted.
8158
 * character was deleted.
8044
 * <p>
8159
 * <p>
8045
 * 
8160
 *
8046
 * @param isBackspace true=the previous character was deleted, false=the 
8161
 * @param isBackspace true=the previous character was deleted, false=the
8047
 * 	character next to the caret location was deleted
8162
 *    character next to the caret location was deleted
8048
 * @param isDirectionBoundary true=the caret is between a R2L and L2R segment,
8163
 * @param isDirectionBoundary true=the caret is between a R2L and L2R segment,
8049
 * 	false=the caret is within a direction segment
8164
 *    false=the caret is within a direction segment
8050
 */
8165
 */
8051
void updateBidiDirection(boolean isBackspace, boolean isDirectionBoundary) {
8166
void updateBidiDirection(boolean isBackspace, boolean isDirectionBoundary) {
8052
	if (isDirectionBoundary) {
8167
   if (isDirectionBoundary) {
8053
		if (isBackspace) {
8168
      if (isBackspace) {
8054
			// Deleted previous character (backspace) at a direction boundary
8169
         // Deleted previous character (backspace) at a direction boundary
8055
			// Go to direction segment of deleted character
8170
         // Go to direction segment of deleted character
8056
			lastCaretDirection = ST.COLUMN_NEXT;
8171
         lastCaretDirection = ST.COLUMN_NEXT;
8057
		}
8172
      }
8058
		else {
8173
      else {
8059
			// Deleted next character. Go to direction segment of deleted character
8174
         // Deleted next character. Go to direction segment of deleted character
8060
			lastCaretDirection = ST.COLUMN_PREVIOUS;
8175
         lastCaretDirection = ST.COLUMN_PREVIOUS;
8061
		}
8176
      }
8062
	}
8177
   }
8063
	else {
8178
   else {
8064
		if (isBackspace) {
8179
      if (isBackspace) {
8065
			// Delete previous character inside direction segment (i.e., not at a direction boundary)
8180
         // Delete previous character inside direction segment (i.e., not at a direction boundary)
8066
			lastCaretDirection = ST.COLUMN_PREVIOUS;
8181
         lastCaretDirection = ST.COLUMN_PREVIOUS;
8067
		}
8182
      }
8068
		else {
8183
      else {
8069
			// Deleted next character.
8184
         // Deleted next character.
8070
			lastCaretDirection = ST.COLUMN_NEXT;
8185
         lastCaretDirection = ST.COLUMN_NEXT;
8071
		}
8186
      }
8072
	}
8187
   }
8073
}
8188
}
8074
/**
8189
/**
8075
 * Updates the selection and caret position depending on the text change.
8190
 * Updates the selection and caret position depending on the text change.
8076
 * If the selection intersects with the replaced text, the selection is 
8191
 * If the selection intersects with the replaced text, the selection is
8077
 * reset and the caret moved to the end of the new text.
8192
 * reset and the caret moved to the end of the new text.
8078
 * If the selection is behind the replaced text it is moved so that the
8193
 * If the selection is behind the replaced text it is moved so that the
8079
 * same text remains selected.  If the selection is before the replaced text 
8194
 * same text remains selected.  If the selection is before the replaced text
8080
 * it is left unchanged.
8195
 * it is left unchanged.
8081
 * <p>
8196
 * <p>
8082
 *
8197
 *
Lines 8085-8170 Link Here
8085
 * @param newLength length of new text
8200
 * @param newLength length of new text
8086
 */
8201
 */
8087
void updateSelection(int startOffset, int replacedLength, int newLength) {
8202
void updateSelection(int startOffset, int replacedLength, int newLength) {
8088
	if (selection.y <= startOffset) {
8203
   if (selection.y <= startOffset) {
8089
		// selection ends before text change
8204
      // selection ends before text change
8090
		return;
8205
      return;
8091
	}
8206
   }
8092
	if (selection.x < startOffset) {
8207
   if (selection.x < startOffset) {
8093
		// clear selection fragment before text change
8208
      // clear selection fragment before text change
8094
		internalRedrawRange(selection.x, startOffset - selection.x, true);
8209
      internalRedrawRange(selection.x, startOffset - selection.x, true);
8095
	}
8210
   }
8096
	if (selection.y > startOffset + replacedLength && selection.x < startOffset + replacedLength) {
8211
   if (selection.y > startOffset + replacedLength && selection.x < startOffset + replacedLength) {
8097
		// clear selection fragment after text change.
8212
      // clear selection fragment after text change.
8098
		// do this only when the selection is actually affected by the 
8213
      // do this only when the selection is actually affected by the
8099
		// change. Selection is only affected if it intersects the change (1GDY217).
8214
      // change. Selection is only affected if it intersects the change (1GDY217).
8100
		int netNewLength = newLength - replacedLength;
8215
      int netNewLength = newLength - replacedLength;
8101
		int redrawStart = startOffset + newLength;
8216
      int redrawStart = startOffset + newLength;
8102
		internalRedrawRange(redrawStart, selection.y + netNewLength - redrawStart, true);
8217
      internalRedrawRange(redrawStart, selection.y + netNewLength - redrawStart, true);
8103
	}
8218
   }
8104
	if (selection.y > startOffset && selection.x < startOffset + replacedLength) {
8219
   if (selection.y > startOffset && selection.x < startOffset + replacedLength) {
8105
		// selection intersects replaced text. set caret behind text change
8220
      // selection intersects replaced text. set caret behind text change
8106
		internalSetSelection(startOffset + newLength, 0, true);
8221
      internalSetSelection(startOffset + newLength, 0, true);
8107
		// always update the caret location. fixes 1G8FODP
8222
      // always update the caret location. fixes 1G8FODP
8108
		setCaretLocation();
8223
      setCaretLocation();
8109
	}
8224
   }
8110
	else {
8225
   else {
8111
		// move selection to keep same text selected
8226
      // move selection to keep same text selected
8112
		internalSetSelection(selection.x + newLength - replacedLength, selection.y - selection.x, true);
8227
      internalSetSelection(selection.x + newLength - replacedLength, selection.y - selection.x, true);
8113
		// always update the caret location. fixes 1G8FODP
8228
      // always update the caret location. fixes 1G8FODP
8114
		setCaretLocation();
8229
      setCaretLocation();
8115
	}	
8230
   }
8116
}
8231
}
8117
/**
8232
/**
8118
 * Rewraps all lines
8233
 * Rewraps all lines
8119
 * <p>
8234
 * <p>
8120
 * 
8235
 *
8121
 * @param oldClientAreaWidth client area width before resize 
8236
 * @param oldClientAreaWidth client area width before resize
8122
 * 	occurred
8237
 *    occurred
8123
 */
8238
 */
8124
void wordWrapResize(int oldClientAreaWidth) {
8239
void wordWrapResize(int oldClientAreaWidth) {
8125
	WrappedContent wrappedContent = (WrappedContent) content;
8240
   WrappedContent wrappedContent = (WrappedContent) content;
8126
	int newTopIndex;
8241
   int newTopIndex;
8127
8242
8128
	// all lines are wrapped and no rewrap required if widget has already 
8243
   // all lines are wrapped and no rewrap required if widget has already
8129
	// been visible, client area is now wider and visual (wrapped) line 
8244
   // been visible, client area is now wider and visual (wrapped) line
8130
	// count equals logical line count.
8245
   // count equals logical line count.
8131
	if (oldClientAreaWidth != 0 && clientAreaWidth > oldClientAreaWidth &&
8246
   if (oldClientAreaWidth != 0 && clientAreaWidth > oldClientAreaWidth &&
8132
		wrappedContent.getLineCount() == logicalContent.getLineCount()) {
8247
      wrappedContent.getLineCount() == logicalContent.getLineCount()) {
8133
		return;
8248
      return;
8134
	}
8249
   }
8135
    wrappedContent.wrapLines();
8250
    wrappedContent.wrapLines();
8136
    
8251
8137
    // adjust the top index so that top line remains the same
8252
    // adjust the top index so that top line remains the same
8138
	newTopIndex = content.getLineAtOffset(topOffset);
8253
   newTopIndex = content.getLineAtOffset(topOffset);
8139
	// topOffset is the beginning of the top line. therefore it 
8254
   // topOffset is the beginning of the top line. therefore it
8140
	// needs to be adjusted because in a wrapped line this is also 
8255
   // needs to be adjusted because in a wrapped line this is also
8141
	// the end of the preceeding line.  
8256
   // the end of the preceeding line.
8142
	if (newTopIndex < content.getLineCount() - 1 &&
8257
   if (newTopIndex < content.getLineCount() - 1 &&
8143
		topOffset == content.getOffsetAtLine(newTopIndex + 1)) {
8258
      topOffset == content.getOffsetAtLine(newTopIndex + 1)) {
8144
		newTopIndex++;
8259
      newTopIndex++;
8145
	}
8260
   }
8146
    if (newTopIndex != topIndex) {
8261
    if (newTopIndex != topIndex) {
8147
    	ScrollBar verticalBar = getVerticalBar();
8262
      ScrollBar verticalBar = getVerticalBar();
8148
    	// adjust index and pixel offset manually instead of calling
8263
      // adjust index and pixel offset manually instead of calling
8149
    	// setVerticalScrollOffset because the widget does not actually need
8264
      // setVerticalScrollOffset because the widget does not actually need
8150
    	// to be scrolled. causes flash otherwise.
8265
      // to be scrolled. causes flash otherwise.
8151
    	verticalScrollOffset += (newTopIndex - topIndex) * getVerticalIncrement();
8266
      verticalScrollOffset += (newTopIndex - topIndex) * getVerticalIncrement();
8152
    	// verticalScrollOffset may become negative if first line was 
8267
      // verticalScrollOffset may become negative if first line was
8153
    	// partially visible and second line was top line. prevent this from 
8268
      // partially visible and second line was top line. prevent this from
8154
    	// happening to fix 8503.
8269
      // happening to fix 8503.
8155
    	if (verticalScrollOffset < 0) {
8270
      if (verticalScrollOffset < 0) {
8156
    		verticalScrollOffset = 0;
8271
         verticalScrollOffset = 0;
8157
    	}
8272
      }
8158
    	topIndex = newTopIndex;
8273
      topIndex = newTopIndex;
8159
    	topOffset = content.getOffsetAtLine(topIndex);
8274
      topOffset = content.getOffsetAtLine(topIndex);
8160
    	if (verticalBar != null) {
8275
      if (verticalBar != null) {
8161
			verticalBar.setSelection(verticalScrollOffset);
8276
         verticalBar.setSelection(verticalScrollOffset);
8162
    	}
8277
      }
8163
    }
8278
    }
8164
  	// caret may be on a different line after a rewrap.
8279
   // caret may be on a different line after a rewrap.
8165
  	// call setCaretLocation after fixing vertical scroll offset.
8280
   // call setCaretLocation after fixing vertical scroll offset.
8166
	setCaretLocation();    
8281
   setCaretLocation();
8167
	// word wrap may have changed on one of the visible lines
8282
   // word wrap may have changed on one of the visible lines
8168
    super.redraw();
8283
    super.redraw();
8169
}
8284
}
8170
}
8285
}

Return to bug 19825