25
|
1 /*******************************************************************************
|
|
2 * Copyright (c) 2000, 2008 IBM Corporation and others.
|
|
3 * All rights reserved. This program and the accompanying materials
|
|
4 * are made available under the terms of the Eclipse Public License v1.0
|
|
5 * which accompanies this distribution, and is available at
|
|
6 * http://www.eclipse.org/legal/epl-v10.html
|
|
7 *
|
|
8 * Contributors:
|
|
9 * IBM Corporation - initial API and implementation
|
|
10 * Port to the D programming language:
|
|
11 * Frank Benoit <benoit@tionex.de>
|
|
12 *******************************************************************************/
|
|
13 module org.eclipse.swt.custom.StyledText;
|
|
14
|
|
15
|
|
16 import org.eclipse.swt.SWT;
|
|
17 import org.eclipse.swt.SWTError;
|
|
18 import org.eclipse.swt.SWTException;
|
|
19 import org.eclipse.swt.accessibility.ACC;
|
|
20 import org.eclipse.swt.accessibility.Accessible;
|
|
21 import org.eclipse.swt.accessibility.AccessibleAdapter;
|
|
22 import org.eclipse.swt.accessibility.AccessibleControlAdapter;
|
|
23 import org.eclipse.swt.accessibility.AccessibleControlEvent;
|
|
24 import org.eclipse.swt.accessibility.AccessibleEvent;
|
|
25 import org.eclipse.swt.accessibility.AccessibleTextAdapter;
|
|
26 import org.eclipse.swt.accessibility.AccessibleTextEvent;
|
|
27 import org.eclipse.swt.dnd.Clipboard;
|
|
28 import org.eclipse.swt.dnd.DND;
|
|
29 import org.eclipse.swt.dnd.RTFTransfer;
|
|
30 import org.eclipse.swt.dnd.TextTransfer;
|
|
31 import org.eclipse.swt.dnd.Transfer;
|
|
32 import org.eclipse.swt.events.ModifyListener;
|
|
33 import org.eclipse.swt.events.SelectionEvent;
|
|
34 import org.eclipse.swt.events.SelectionListener;
|
|
35 import org.eclipse.swt.events.VerifyListener;
|
|
36 import org.eclipse.swt.graphics.Color;
|
|
37 import org.eclipse.swt.graphics.Cursor;
|
|
38 import org.eclipse.swt.graphics.Font;
|
|
39 import org.eclipse.swt.graphics.FontData;
|
|
40 import org.eclipse.swt.graphics.FontMetrics;
|
|
41 import org.eclipse.swt.graphics.GC;
|
|
42 import org.eclipse.swt.graphics.GlyphMetrics;
|
|
43 import org.eclipse.swt.graphics.Image;
|
|
44 import org.eclipse.swt.graphics.Device;
|
|
45 import org.eclipse.swt.graphics.Point;
|
|
46 import org.eclipse.swt.graphics.Rectangle;
|
|
47 import org.eclipse.swt.graphics.Resource;
|
|
48 import org.eclipse.swt.graphics.TextLayout;
|
|
49 import org.eclipse.swt.internal.BidiUtil;
|
|
50 import org.eclipse.swt.internal.Compatibility;
|
|
51 import org.eclipse.swt.printing.Printer;
|
|
52 import org.eclipse.swt.printing.PrinterData;
|
|
53 import org.eclipse.swt.widgets.Canvas;
|
|
54 import org.eclipse.swt.widgets.Caret;
|
|
55 import org.eclipse.swt.widgets.Composite;
|
|
56 import org.eclipse.swt.widgets.Control;
|
|
57 import org.eclipse.swt.widgets.Display;
|
|
58 import org.eclipse.swt.widgets.Event;
|
|
59 import org.eclipse.swt.widgets.IME;
|
|
60 import org.eclipse.swt.widgets.Label;
|
|
61 import org.eclipse.swt.widgets.Listener;
|
|
62 import org.eclipse.swt.widgets.ScrollBar;
|
|
63 import org.eclipse.swt.widgets.TypedListener;
|
|
64 import org.eclipse.swt.custom.StyledTextContent;
|
|
65 import org.eclipse.swt.custom.TextChangeListener;
|
|
66 import org.eclipse.swt.custom.StyledTextRenderer;
|
|
67 import org.eclipse.swt.custom.StyledTextPrintOptions;
|
|
68 import org.eclipse.swt.custom.ExtendedModifyListener;
|
|
69 import org.eclipse.swt.custom.BidiSegmentListener;
|
|
70 import org.eclipse.swt.custom.LineBackgroundListener;
|
|
71 import org.eclipse.swt.custom.LineStyleListener;
|
|
72 import org.eclipse.swt.custom.PaintObjectListener;
|
|
73 import org.eclipse.swt.custom.VerifyKeyListener;
|
|
74 import org.eclipse.swt.custom.MovementListener;
|
|
75 import org.eclipse.swt.custom.Bullet;
|
|
76 import org.eclipse.swt.custom.StyledTextEvent;
|
|
77 import org.eclipse.swt.custom.StyleRange;
|
|
78 import org.eclipse.swt.custom.TextChangedEvent;
|
|
79 import org.eclipse.swt.custom.TextChangingEvent;
|
|
80 import org.eclipse.swt.custom.DefaultContent;
|
|
81 import org.eclipse.swt.custom.StyledTextDropTargetEffect;
|
|
82 import org.eclipse.swt.custom.StyledTextListener;
|
|
83 import org.eclipse.swt.custom.ST;
|
|
84
|
|
85 static import tango.text.Util;
|
|
86 static import tango.io.model.IFile;
|
|
87 static import tango.text.convert.Utf;
|
|
88 import tango.util.Convert;
|
|
89 import java.lang.all;
|
|
90
|
|
91
|
|
92 /**
|
|
93 * A StyledText is an editable user interface object that displays lines
|
|
94 * of text. The following style attributes can be defined for the text:
|
|
95 * <ul>
|
|
96 * <li>foreground color
|
|
97 * <li>background color
|
|
98 * <li>font style (bold, italic, bold-italic, regular)
|
|
99 * <li>underline
|
|
100 * <li>strikeout
|
|
101 * </ul>
|
|
102 * <p>
|
|
103 * In addition to text style attributes, the background color of a line may
|
|
104 * be specified.
|
|
105 * </p><p>
|
|
106 * There are two ways to use this widget when specifying text style information.
|
|
107 * You may use the API that is defined for StyledText or you may define your own
|
|
108 * LineStyleListener. If you define your own listener, you will be responsible
|
|
109 * for maintaining the text style information for the widget. IMPORTANT: You may
|
|
110 * not define your own listener and use the StyledText API. The following
|
|
111 * StyledText API is not supported if you have defined a LineStyleListener:
|
|
112 * <ul>
|
|
113 * <li>getStyleRangeAtOffset(int)
|
|
114 * <li>getStyleRanges()
|
|
115 * <li>replaceStyleRanges(int,int,StyleRange[])
|
|
116 * <li>setStyleRange(StyleRange)
|
|
117 * <li>setStyleRanges(StyleRange[])
|
|
118 * </ul>
|
|
119 * </p><p>
|
|
120 * There are two ways to use this widget when specifying line background colors.
|
|
121 * You may use the API that is defined for StyledText or you may define your own
|
|
122 * LineBackgroundListener. If you define your own listener, you will be responsible
|
|
123 * for maintaining the line background color information for the widget.
|
|
124 * IMPORTANT: You may not define your own listener and use the StyledText API.
|
|
125 * The following StyledText API is not supported if you have defined a
|
|
126 * LineBackgroundListener:
|
|
127 * <ul>
|
|
128 * <li>getLineBackground(int)
|
|
129 * <li>setLineBackground(int,int,Color)
|
|
130 * </ul>
|
|
131 * </p><p>
|
|
132 * The content implementation for this widget may also be user-defined. To do so,
|
|
133 * you must implement the StyledTextContent interface and use the StyledText API
|
|
134 * setContent(StyledTextContent) to initialize the widget.
|
|
135 * </p><p>
|
|
136 * <dl>
|
|
137 * <dt><b>Styles:</b><dd>FULL_SELECTION, MULTI, READ_ONLY, SINGLE, WRAP
|
|
138 * <dt><b>Events:</b><dd>ExtendedModify, LineGetBackground, LineGetSegments, LineGetStyle, Modify, Selection, Verify, VerifyKey
|
|
139 * </dl>
|
|
140 * </p><p>
|
|
141 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
|
|
142 * </p>
|
|
143 *
|
|
144 * @see <a href="http://www.eclipse.org/swt/snippets/#styledtext">StyledText snippets</a>
|
|
145 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Examples: CustomControlExample, TextEditor</a>
|
|
146 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
|
|
147 */
|
|
148 public class StyledText : Canvas {
|
|
149 alias Canvas.computeSize computeSize;
|
|
150
|
|
151 static const char TAB = '\t';
|
|
152 static const String PlatformLineDelimiter = tango.io.model.IFile.FileConst.NewlineString;
|
|
153 static const int BIDI_CARET_WIDTH = 3;
|
|
154 static const int DEFAULT_WIDTH = 64;
|
|
155 static const int DEFAULT_HEIGHT = 64;
|
|
156 static const int V_SCROLL_RATE = 50;
|
|
157 static const int H_SCROLL_RATE = 10;
|
|
158
|
|
159 static const int ExtendedModify = 3000;
|
|
160 static const int LineGetBackground = 3001;
|
|
161 static const int LineGetStyle = 3002;
|
|
162 static const int TextChanging = 3003;
|
|
163 static const int TextSet = 3004;
|
|
164 static const int VerifyKey = 3005;
|
|
165 static const int TextChanged = 3006;
|
|
166 static const int LineGetSegments = 3007;
|
|
167 static const int PaintObject = 3008;
|
|
168 static const int WordNext = 3009;
|
|
169 static const int WordPrevious = 3010;
|
|
170
|
|
171 static const int PREVIOUS_OFFSET_TRAILING = 0;
|
|
172 static const int OFFSET_LEADING = 1;
|
|
173
|
|
174 Color selectionBackground; // selection background color
|
|
175 Color selectionForeground; // selection foreground color
|
|
176 StyledTextContent content; // native content (default or user specified)
|
|
177 StyledTextRenderer renderer;
|
|
178 Listener listener;
|
|
179 TextChangeListener textChangeListener; // listener for TextChanging, TextChanged and TextSet events from StyledTextContent
|
|
180 int verticalScrollOffset = 0; // pixel based
|
|
181 int horizontalScrollOffset = 0; // pixel based
|
|
182 int topIndex = 0; // top visible line
|
|
183 int topIndexY;
|
|
184 int clientAreaHeight = 0; // the client area height. Needed to calculate content width for new visible lines during Resize callback
|
|
185 int clientAreaWidth = 0; // the client area width. Needed during Resize callback to determine if line wrap needs to be recalculated
|
|
186 int tabLength = 4; // number of characters in a tab
|
|
187 int leftMargin;
|
|
188 int topMargin;
|
|
189 int rightMargin;
|
|
190 int bottomMargin;
|
|
191 int columnX; // keep track of the horizontal caret position when changing lines/pages. Fixes bug 5935
|
|
192 int caretOffset = 0;
|
|
193 int caretAlignment;
|
|
194 Point selection; // x and y are start and end caret offsets of selection
|
|
195 Point clipboardSelection; // x and y are start and end caret offsets of previous selection
|
|
196 int selectionAnchor; // position of selection anchor. 0 based offset from beginning of text
|
|
197 Point doubleClickSelection; // selection after last mouse double click
|
|
198 bool editable = true;
|
|
199 bool wordWrap = false;
|
|
200 bool doubleClickEnabled = true; // see getDoubleClickEnabled
|
|
201 bool overwrite = false; // insert/overwrite edit mode
|
|
202 int textLimit = -1; // limits the number of characters the user can type in the widget. Unlimited by default.
|
|
203 int[int] keyActionMap;
|
|
204 Color background = null; // workaround for bug 4791
|
|
205 Color foreground = null; //
|
|
206 Clipboard clipboard;
|
|
207 int clickCount;
|
|
208 int autoScrollDirection = SWT.NULL; // the direction of autoscrolling (up, down, right, left)
|
|
209 int autoScrollDistance = 0;
|
|
210 int lastTextChangeStart; // cache data of the
|
|
211 int lastTextChangeNewLineCount; // last text changing
|
|
212 int lastTextChangeNewCharCount; // event for use in the
|
|
213 int lastTextChangeReplaceLineCount; // text changed handler
|
|
214 int lastTextChangeReplaceCharCount;
|
|
215 int lastLineBottom; // the bottom pixel of the last line been replaced
|
|
216 bool isMirrored_;
|
|
217 bool bidiColoring = false; // apply the BIDI algorithm on text segments of the same color
|
|
218 Image leftCaretBitmap = null;
|
|
219 Image rightCaretBitmap = null;
|
|
220 int caretDirection = SWT.NULL;
|
|
221 int caretWidth = 0;
|
|
222 Caret defaultCaret = null;
|
|
223 bool updateCaretDirection = true;
|
|
224 bool fixedLineHeight;
|
|
225 bool dragDetect_ = true;
|
|
226 IME ime;
|
|
227
|
|
228 int alignment;
|
|
229 bool justify;
|
|
230 int indent;
|
|
231 int lineSpacing;
|
|
232
|
|
233 const static bool IS_CARBON, IS_GTK, IS_MOTIF;
|
|
234 static this(){
|
|
235 String platform = SWT.getPlatform();
|
|
236 IS_CARBON = ("carbon" == platform);
|
|
237 IS_GTK = ("gtk" == platform);
|
|
238 IS_MOTIF = ("motif" == platform);
|
|
239 }
|
|
240
|
|
241 /**
|
|
242 * The Printing class : printing of a range of text.
|
|
243 * An instance of <code>Printing</code> is returned in the
|
|
244 * StyledText#print(Printer) API. The run() method may be
|
|
245 * invoked from any thread.
|
|
246 */
|
|
247 static class Printing : Runnable {
|
|
248 const static int LEFT = 0; // left aligned header/footer segment
|
|
249 const static int CENTER = 1; // centered header/footer segment
|
|
250 const static int RIGHT = 2; // right aligned header/footer segment
|
|
251
|
|
252 Printer printer;
|
|
253 StyledTextRenderer printerRenderer;
|
|
254 StyledTextPrintOptions printOptions;
|
|
255 Rectangle clientArea;
|
|
256 FontData fontData;
|
|
257 Font printerFont;
|
|
258 Resource[Resource] resources;
|
|
259 int tabLength;
|
|
260 GC gc; // printer GC
|
|
261 int pageWidth; // width of a printer page in pixels
|
|
262 int startPage; // first page to print
|
|
263 int endPage; // last page to print
|
|
264 int startLine; // first (wrapped) line to print
|
|
265 int endLine; // last (wrapped) line to print
|
|
266 bool singleLine; // widget single line mode
|
|
267 Point selection = null; // selected text
|
|
268 bool mirrored; // indicates the printing gc should be mirrored
|
|
269 int lineSpacing;
|
|
270 int printMargin;
|
|
271
|
|
272 /**
|
|
273 * Creates an instance of <code>Printing</code>.
|
|
274 * Copies the widget content and rendering data that needs
|
|
275 * to be requested from listeners.
|
|
276 * </p>
|
|
277 * @param parent StyledText widget to print.
|
|
278 * @param printer printer device to print on.
|
|
279 * @param printOptions print options
|
|
280 */
|
|
281 this(StyledText styledText, Printer printer, StyledTextPrintOptions printOptions) {
|
|
282 this.printer = printer;
|
|
283 this.printOptions = printOptions;
|
|
284 this.mirrored = (styledText.getStyle() & SWT.MIRRORED) !is 0;
|
|
285 singleLine = styledText.isSingleLine();
|
|
286 startPage = 1;
|
|
287 endPage = int.max;
|
|
288 PrinterData data = printer.getPrinterData();
|
|
289 if (data.scope_ is PrinterData.PAGE_RANGE) {
|
|
290 startPage = data.startPage;
|
|
291 endPage = data.endPage;
|
|
292 if (endPage < startPage) {
|
|
293 int temp = endPage;
|
|
294 endPage = startPage;
|
|
295 startPage = temp;
|
|
296 }
|
|
297 } else if (data.scope_ is PrinterData.SELECTION) {
|
|
298 selection = styledText.getSelectionRange();
|
|
299 }
|
|
300 printerRenderer = new StyledTextRenderer(printer, null);
|
|
301 printerRenderer.setContent(copyContent(styledText.getContent()));
|
|
302 cacheLineData(styledText);
|
|
303 }
|
|
304 /**
|
|
305 * Caches all line data that needs to be requested from a listener.
|
|
306 * </p>
|
|
307 * @param printerContent <code>StyledTextContent</code> to request
|
|
308 * line data for.
|
|
309 */
|
|
310 void cacheLineData(StyledText styledText) {
|
|
311 StyledTextRenderer renderer = styledText.renderer;
|
|
312 renderer.copyInto(printerRenderer);
|
|
313 fontData = styledText.getFont().getFontData()[0];
|
|
314 tabLength = styledText.tabLength;
|
|
315 int lineCount = printerRenderer.lineCount;
|
|
316 if (styledText.isListening(LineGetBackground) || (styledText.isBidi() && styledText.isListening(LineGetSegments)) || styledText.isListening(LineGetStyle)) {
|
|
317 StyledTextContent content = printerRenderer.content;
|
|
318 for (int i = 0; i < lineCount; i++) {
|
|
319 String line = content.getLine(i);
|
|
320 int lineOffset = content.getOffsetAtLine(i);
|
|
321 StyledTextEvent event = styledText.getLineBackgroundData(lineOffset, line);
|
|
322 if (event !is null && event.lineBackground !is null) {
|
|
323 printerRenderer.setLineBackground(i, 1, event.lineBackground);
|
|
324 }
|
|
325 if (styledText.isBidi()) {
|
|
326 int[] segments = styledText.getBidiSegments(lineOffset, line);
|
|
327 printerRenderer.setLineSegments(i, 1, segments);
|
|
328 }
|
|
329 event = styledText.getLineStyleData(lineOffset, line);
|
|
330 if (event !is null) {
|
|
331 printerRenderer.setLineIndent(i, 1, event.indent);
|
|
332 printerRenderer.setLineAlignment(i, 1, event.alignment);
|
|
333 printerRenderer.setLineJustify(i, 1, event.justify);
|
|
334 printerRenderer.setLineBullet(i, 1, event.bullet);
|
|
335 StyleRange[] styles = event.styles;
|
|
336 if (styles !is null && styles.length > 0) {
|
|
337 printerRenderer.setStyleRanges(event.ranges, styles);
|
|
338 }
|
|
339 }
|
|
340 }
|
|
341 }
|
|
342 Point screenDPI = styledText.getDisplay().getDPI();
|
|
343 Point printerDPI = printer.getDPI();
|
|
344 resources = null;
|
|
345 for (int i = 0; i < lineCount; i++) {
|
|
346 Color color = printerRenderer.getLineBackground(i, null);
|
|
347 if (color !is null) {
|
|
348 if (printOptions.printLineBackground) {
|
|
349 Color printerColor;
|
|
350 if ( auto p = color in resources ) {
|
|
351 printerColor = cast(Color)*p;
|
|
352 }
|
|
353 else {
|
|
354 printerColor = new Color (printer, color.getRGB());
|
|
355 resources[color]=printerColor;
|
|
356 }
|
|
357 printerRenderer.setLineBackground(i, 1, printerColor);
|
|
358 } else {
|
|
359 printerRenderer.setLineBackground(i, 1, null);
|
|
360 }
|
|
361 }
|
|
362 int indent = printerRenderer.getLineIndent(i, 0);
|
|
363 if (indent !is 0) {
|
|
364 printerRenderer.setLineIndent(i, 1, indent * printerDPI.x / screenDPI.x);
|
|
365 }
|
|
366 }
|
|
367 StyleRange[] styles = printerRenderer.styles;
|
|
368 for (int i = 0; i < printerRenderer.styleCount; i++) {
|
|
369 StyleRange style = styles[i];
|
|
370 Font font = style.font;
|
|
371 if (style.font !is null) {
|
|
372 Font printerFont;
|
|
373 if ( auto p = font in resources ) {
|
|
374 printerFont = cast(Font)*p;
|
|
375 }
|
|
376 else {
|
|
377 printerFont = new Font (printer, font.getFontData());
|
|
378 resources[font]= printerFont;
|
|
379 }
|
|
380 style.font = printerFont;
|
|
381 }
|
|
382 Color color = style.foreground;
|
|
383 if (color !is null) {
|
|
384 if (printOptions.printTextForeground) {
|
|
385 Color printerColor;
|
|
386 if ( auto p = color in resources ) {
|
|
387 printerColor = cast(Color)*p;
|
|
388 }
|
|
389 else {
|
|
390 printerColor = new Color (printer, color.getRGB());
|
|
391 resources[color]=printerColor;
|
|
392 }
|
|
393 style.foreground = printerColor;
|
|
394 } else {
|
|
395 style.foreground = null;
|
|
396 }
|
|
397 }
|
|
398 color = style.background;
|
|
399 if (color !is null) {
|
|
400 if (printOptions.printTextBackground) {
|
|
401 Color printerColor;
|
|
402 if ( auto p = color in resources ) {
|
|
403 printerColor = cast(Color)*p;
|
|
404 }
|
|
405 else {
|
|
406 printerColor = new Color (printer, color.getRGB());
|
|
407 resources[color]=printerColor;
|
|
408 }
|
|
409 style.background = printerColor;
|
|
410 } else {
|
|
411 style.background = null;
|
|
412 }
|
|
413 }
|
|
414 if (!printOptions.printTextFontStyle) {
|
|
415 style.fontStyle = SWT.NORMAL;
|
|
416 }
|
|
417 style.rise = style.rise * printerDPI.y / screenDPI.y;
|
|
418 GlyphMetrics metrics = style.metrics;
|
|
419 if (metrics !is null) {
|
|
420 metrics.ascent = metrics.ascent * printerDPI.y / screenDPI.y;
|
|
421 metrics.descent = metrics.descent * printerDPI.y / screenDPI.y;
|
|
422 metrics.width = metrics.width * printerDPI.x / screenDPI.x;
|
|
423 }
|
|
424 }
|
|
425 lineSpacing = styledText.lineSpacing * printerDPI.y / screenDPI.y;
|
|
426 if (printOptions.printLineNumbers) {
|
|
427 printMargin = 3 * printerDPI.x / screenDPI.x;
|
|
428 }
|
|
429 }
|
|
430 /**
|
|
431 * Copies the text of the specified <code>StyledTextContent</code>.
|
|
432 * </p>
|
|
433 * @param original the <code>StyledTextContent</code> to copy.
|
|
434 */
|
|
435 StyledTextContent copyContent(StyledTextContent original) {
|
|
436 StyledTextContent printerContent = new DefaultContent();
|
|
437 int insertOffset = 0;
|
|
438 for (int i = 0; i < original.getLineCount(); i++) {
|
|
439 int insertEndOffset;
|
|
440 if (i < original.getLineCount() - 1) {
|
|
441 insertEndOffset = original.getOffsetAtLine(i + 1);
|
|
442 } else {
|
|
443 insertEndOffset = original.getCharCount();
|
|
444 }
|
|
445 printerContent.replaceTextRange(insertOffset, 0, original.getTextRange(insertOffset, insertEndOffset - insertOffset));
|
|
446 insertOffset = insertEndOffset;
|
|
447 }
|
|
448 return printerContent;
|
|
449 }
|
|
450 /**
|
|
451 * Disposes of the resources and the <code>PrintRenderer</code>.
|
|
452 */
|
|
453 void dispose() {
|
|
454 if (gc !is null) {
|
|
455 gc.dispose();
|
|
456 gc = null;
|
|
457 }
|
|
458 foreach( resource; resources.values ){
|
|
459 resource.dispose();
|
|
460 }
|
|
461 resources = null;
|
|
462 if (printerFont !is null) {
|
|
463 printerFont.dispose();
|
|
464 printerFont = null;
|
|
465 }
|
|
466 if (printerRenderer !is null) {
|
|
467 printerRenderer.dispose();
|
|
468 printerRenderer = null;
|
|
469 }
|
|
470 }
|
|
471 void init_() {
|
|
472 Rectangle trim = printer.computeTrim(0, 0, 0, 0);
|
|
473 Point dpi = printer.getDPI();
|
|
474
|
|
475 printerFont = new Font( cast(Device)printer, fontData.getName(), fontData.getHeight(), SWT.NORMAL);
|
|
476 clientArea = printer.getClientArea();
|
|
477 pageWidth = clientArea.width;
|
|
478 // one inch margin around text
|
|
479 clientArea.x = dpi.x + trim.x;
|
|
480 clientArea.y = dpi.y + trim.y;
|
|
481 clientArea.width -= (clientArea.x + trim.width);
|
|
482 clientArea.height -= (clientArea.y + trim.height);
|
|
483
|
|
484 int style = mirrored ? SWT.RIGHT_TO_LEFT : SWT.LEFT_TO_RIGHT;
|
|
485 gc = new GC(printer, style);
|
|
486 gc.setFont(printerFont);
|
|
487 printerRenderer.setFont(printerFont, tabLength);
|
|
488 int lineHeight = printerRenderer.getLineHeight();
|
|
489 if (printOptions.header !is null) {
|
|
490 clientArea.y += lineHeight * 2;
|
|
491 clientArea.height -= lineHeight * 2;
|
|
492 }
|
|
493 if (printOptions.footer !is null) {
|
|
494 clientArea.height -= lineHeight * 2;
|
|
495 }
|
|
496
|
|
497 // TODO not wrapped
|
|
498 StyledTextContent content = printerRenderer.content;
|
|
499 startLine = 0;
|
|
500 endLine = singleLine ? 0 : content.getLineCount() - 1;
|
|
501 PrinterData data = printer.getPrinterData();
|
|
502 if (data.scope_ is PrinterData.PAGE_RANGE) {
|
|
503 int pageSize = clientArea.height / lineHeight;//WRONG
|
|
504 startLine = (startPage - 1) * pageSize;
|
|
505 } else if (data.scope_ is PrinterData.SELECTION) {
|
|
506 startLine = content.getLineAtOffset(selection.x);
|
|
507 if (selection.y > 0) {
|
|
508 endLine = content.getLineAtOffset(selection.x + selection.y - 1);
|
|
509 } else {
|
|
510 endLine = startLine - 1;
|
|
511 }
|
|
512 }
|
|
513 }
|
|
514 /**
|
|
515 * Prints the lines in the specified page range.
|
|
516 */
|
|
517 void print() {
|
|
518 Color background = gc.getBackground();
|
|
519 Color foreground = gc.getForeground();
|
|
520 int paintY = clientArea.y;
|
|
521 int paintX = clientArea.x;
|
|
522 int width = clientArea.width;
|
|
523 int page = startPage;
|
|
524 int pageBottom = clientArea.y + clientArea.height;
|
|
525 int orientation = gc.getStyle() & (SWT.RIGHT_TO_LEFT | SWT.LEFT_TO_RIGHT);
|
|
526 TextLayout printLayout = null;
|
|
527 if (printOptions.printLineNumbers || printOptions.header !is null || printOptions.footer !is null) {
|
|
528 printLayout = new TextLayout(printer);
|
|
529 printLayout.setFont(printerFont);
|
|
530 }
|
|
531 if (printOptions.printLineNumbers) {
|
|
532 int numberingWidth = 0;
|
|
533 int count = endLine - startLine + 1;
|
|
534 String[] lineLabels = printOptions.lineLabels;
|
|
535 if (lineLabels !is null) {
|
|
536 for (int i = startLine; i < Math.min(count, lineLabels.length); i++) {
|
|
537 if (lineLabels[i] !is null) {
|
|
538 printLayout.setText(lineLabels[i]);
|
|
539 int lineWidth = printLayout.getBounds().width;
|
|
540 numberingWidth = Math.max(numberingWidth, lineWidth);
|
|
541 }
|
|
542 }
|
|
543 } else {
|
|
544 StringBuffer buffer = new StringBuffer("0");
|
|
545 while ((count /= 10) > 0) buffer.append("0");
|
|
546 printLayout.setText(buffer.toString());
|
|
547 numberingWidth = printLayout.getBounds().width;
|
|
548 }
|
|
549 numberingWidth += printMargin;
|
|
550 if (numberingWidth > width) numberingWidth = width;
|
|
551 paintX += numberingWidth;
|
|
552 width -= numberingWidth;
|
|
553 }
|
|
554 for (int i = startLine; i <= endLine && page <= endPage; i++) {
|
|
555 if (paintY is clientArea.y) {
|
|
556 printer.startPage();
|
|
557 printDecoration(page, true, printLayout);
|
|
558 }
|
|
559 TextLayout layout = printerRenderer.getTextLayout(i, orientation, width, lineSpacing);
|
|
560 Color lineBackground = printerRenderer.getLineBackground(i, background);
|
|
561 int paragraphBottom = paintY + layout.getBounds().height;
|
|
562 if (paragraphBottom <= pageBottom) {
|
|
563 //normal case, the whole paragraph fits in the current page
|
|
564 printLine(paintX, paintY, gc, foreground, lineBackground, layout, printLayout, i);
|
|
565 paintY = paragraphBottom;
|
|
566 } else {
|
|
567 int lineCount = layout.getLineCount();
|
|
568 while (paragraphBottom > pageBottom && lineCount > 0) {
|
|
569 lineCount--;
|
|
570 paragraphBottom -= layout.getLineBounds(lineCount).height + layout.getSpacing();
|
|
571 }
|
|
572 if (lineCount is 0) {
|
|
573 //the whole paragraph goes to the next page
|
|
574 printDecoration(page, false, printLayout);
|
|
575 printer.endPage();
|
|
576 page++;
|
|
577 if (page <= endPage) {
|
|
578 printer.startPage();
|
|
579 printDecoration(page, true, printLayout);
|
|
580 paintY = clientArea.y;
|
|
581 printLine(paintX, paintY, gc, foreground, lineBackground, layout, printLayout, i);
|
|
582 paintY += layout.getBounds().height;
|
|
583 }
|
|
584 } else {
|
|
585 //draw paragraph top in the current page and paragraph bottom in the next
|
|
586 int height = paragraphBottom - paintY;
|
|
587 gc.setClipping(clientArea.x, paintY, clientArea.width, height);
|
|
588 printLine(paintX, paintY, gc, foreground, lineBackground, layout, printLayout, i);
|
|
589 gc.setClipping(cast(Rectangle)null);
|
|
590 printDecoration(page, false, printLayout);
|
|
591 printer.endPage();
|
|
592 page++;
|
|
593 if (page <= endPage) {
|
|
594 printer.startPage();
|
|
595 printDecoration(page, true, printLayout);
|
|
596 paintY = clientArea.y - height;
|
|
597 int layoutHeight = layout.getBounds().height;
|
|
598 gc.setClipping(clientArea.x, clientArea.y, clientArea.width, layoutHeight - height);
|
|
599 printLine(paintX, paintY, gc, foreground, lineBackground, layout, printLayout, i);
|
|
600 gc.setClipping(cast(Rectangle)null);
|
|
601 paintY += layoutHeight;
|
|
602 }
|
|
603 }
|
|
604 }
|
|
605 printerRenderer.disposeTextLayout(layout);
|
|
606 }
|
|
607 if (page <= endPage && paintY > clientArea.y) {
|
|
608 // close partial page
|
|
609 printDecoration(page, false, printLayout);
|
|
610 printer.endPage();
|
|
611 }
|
|
612 if (printLayout !is null) printLayout.dispose();
|
|
613 }
|
|
614 /**
|
|
615 * Print header or footer decorations.
|
|
616 *
|
|
617 * @param page page number to print, if specified in the StyledTextPrintOptions header or footer.
|
|
618 * @param header true = print the header, false = print the footer
|
|
619 */
|
|
620 void printDecoration(int page, bool header, TextLayout layout) {
|
|
621 String text = header ? printOptions.header : printOptions.footer;
|
|
622 if (text is null) return;
|
|
623 int lastSegmentIndex = 0;
|
|
624 for (int i = 0; i < 3; i++) {
|
|
625 int segmentIndex = text.indexOf( StyledTextPrintOptions.SEPARATOR, lastSegmentIndex);
|
|
626 String segment;
|
|
627 if (segmentIndex is -1 ) {
|
|
628 segment = text.substring(lastSegmentIndex);
|
|
629 printDecorationSegment(segment, i, page, header, layout);
|
|
630 break;
|
|
631 } else {
|
|
632 segment = text.substring(lastSegmentIndex, segmentIndex);
|
|
633 printDecorationSegment(segment, i, page, header, layout);
|
|
634 lastSegmentIndex = segmentIndex + StyledTextPrintOptions.SEPARATOR.length;
|
|
635 }
|
|
636 }
|
|
637 }
|
|
638 /**
|
|
639 * Print one segment of a header or footer decoration.
|
|
640 * Headers and footers have three different segments.
|
|
641 * One each for left aligned, centered, and right aligned text.
|
|
642 *
|
|
643 * @param segment decoration segment to print
|
|
644 * @param alignment alignment of the segment. 0=left, 1=center, 2=right
|
|
645 * @param page page number to print, if specified in the decoration segment.
|
|
646 * @param header true = print the header, false = print the footer
|
|
647 */
|
|
648 void printDecorationSegment(String segment, int alignment, int page, bool header, TextLayout layout) {
|
|
649 int pageIndex = segment.indexOf(StyledTextPrintOptions.PAGE_TAG);
|
|
650 if (pageIndex !is -1 ) {
|
|
651 int pageTagLength = StyledTextPrintOptions.PAGE_TAG.length;
|
|
652 StringBuffer buffer = new StringBuffer(segment.substring (0, pageIndex));
|
|
653 buffer.append (page);
|
|
654 buffer.append (segment.substring(pageIndex + pageTagLength));
|
|
655 segment = buffer.toString().dup;
|
|
656 }
|
|
657 if (segment.length > 0) {
|
|
658 layout.setText(segment);
|
|
659 int segmentWidth = layout.getBounds().width;
|
|
660 int segmentHeight = printerRenderer.getLineHeight();
|
|
661 int drawX = 0, drawY;
|
|
662 if (alignment is LEFT) {
|
|
663 drawX = clientArea.x;
|
|
664 } else if (alignment is CENTER) {
|
|
665 drawX = (pageWidth - segmentWidth) / 2;
|
|
666 } else if (alignment is RIGHT) {
|
|
667 drawX = clientArea.x + clientArea.width - segmentWidth;
|
|
668 }
|
|
669 if (header) {
|
|
670 drawY = clientArea.y - segmentHeight * 2;
|
|
671 } else {
|
|
672 drawY = clientArea.y + clientArea.height + segmentHeight;
|
|
673 }
|
|
674 layout.draw(gc, drawX, drawY);
|
|
675 }
|
|
676 }
|
|
677 void printLine(int x, int y, GC gc, Color foreground, Color background, TextLayout layout, TextLayout printLayout, int index) {
|
|
678 if (background !is null) {
|
|
679 Rectangle rect = layout.getBounds();
|
|
680 gc.setBackground(background);
|
|
681 gc.fillRectangle(x, y, rect.width, rect.height);
|
|
682
|
|
683 // int lineCount = layout.getLineCount();
|
|
684 // for (int i = 0; i < lineCount; i++) {
|
|
685 // Rectangle rect = layout.getLineBounds(i);
|
|
686 // rect.x += paintX;
|
|
687 // rect.y += paintY + layout.getSpacing();
|
|
688 // rect.width = width;//layout bounds
|
|
689 // gc.fillRectangle(rect);
|
|
690 // }
|
|
691 }
|
|
692 if (printOptions.printLineNumbers) {
|
|
693 FontMetrics metrics = layout.getLineMetrics(0);
|
|
694 printLayout.setAscent(metrics.getAscent() + metrics.getLeading());
|
|
695 printLayout.setDescent(metrics.getDescent());
|
|
696 String[] lineLabels = printOptions.lineLabels;
|
|
697 if (lineLabels !is null) {
|
|
698 if (0 <= index && index < lineLabels.length && lineLabels[index] !is null) {
|
|
699 printLayout.setText(lineLabels[index]);
|
|
700 } else {
|
|
701 printLayout.setText("");
|
|
702 }
|
|
703 } else {
|
|
704 printLayout.setText(to!(String)(index));
|
|
705 }
|
|
706 int paintX = x - printMargin - printLayout.getBounds().width;
|
|
707 printLayout.draw(gc, paintX, y);
|
|
708 printLayout.setAscent(-1);
|
|
709 printLayout.setDescent(-1);
|
|
710 }
|
|
711 gc.setForeground(foreground);
|
|
712 layout.draw(gc, x, y);
|
|
713 }
|
|
714 /**
|
|
715 * Starts a print job and prints the pages specified in the constructor.
|
|
716 */
|
|
717 public void run() {
|
|
718 String jobName = printOptions.jobName;
|
|
719 if (jobName is null) {
|
|
720 jobName = "Printing";
|
|
721 }
|
|
722 if (printer.startJob(jobName)) {
|
|
723 init_();
|
|
724 print();
|
|
725 dispose();
|
|
726 printer.endJob();
|
|
727 }
|
|
728 }
|
|
729 }
|
|
730 /**
|
|
731 * The <code>RTFWriter</code> class is used to write widget content as
|
|
732 * rich text. The implementation complies with the RTF specification
|
|
733 * version 1.5.
|
|
734 * <p>
|
|
735 * toString() is guaranteed to return a valid RTF string only after
|
|
736 * close() has been called.
|
|
737 * </p><p>
|
|
738 * Whole and partial lines and line breaks can be written. Lines will be
|
|
739 * formatted using the styles queried from the LineStyleListener, if
|
|
740 * set, or those set directly in the widget. All styles are applied to
|
|
741 * the RTF stream like they are rendered by the widget. In addition, the
|
|
742 * widget font name and size is used for the whole text.
|
|
743 * </p>
|
|
744 */
|
|
745 class RTFWriter : TextWriter {
|
|
746
|
|
747 alias TextWriter.write write;
|
|
748
|
|
749 static const int DEFAULT_FOREGROUND = 0;
|
|
750 static const int DEFAULT_BACKGROUND = 1;
|
|
751 Color[] colorTable;
|
|
752 Font[] fontTable;
|
|
753 bool WriteUnicode;
|
|
754
|
|
755 /**
|
|
756 * Creates a RTF writer that writes content starting at offset "start"
|
|
757 * in the document. <code>start</code> and <code>length</code>can be set to specify partial
|
|
758 * lines.
|
|
759 *
|
|
760 * @param start start offset of content to write, 0 based from
|
|
761 * beginning of document
|
|
762 * @param length length of content to write
|
|
763 */
|
|
764 public this(int start, int length) {
|
|
765 super(start, length);
|
|
766 colorTable ~= getForeground();
|
|
767 colorTable ~= getBackground();
|
|
768 fontTable ~= getFont();
|
|
769 setUnicode();
|
|
770 }
|
|
771 /**
|
|
772 * Closes the RTF writer. Once closed no more content can be written.
|
|
773 * <b>NOTE:</b> <code>toString()</code> does not return a valid RTF string until
|
|
774 * <code>close()</code> has been called.
|
|
775 */
|
|
776 public override void close() {
|
|
777 if (!isClosed()) {
|
|
778 writeHeader();
|
|
779 write("\n}}\0");
|
|
780 super.close();
|
|
781 }
|
|
782 }
|
|
783 /**
|
|
784 * Returns the index of the specified color in the RTF color table.
|
|
785 *
|
|
786 * @param color the color
|
|
787 * @param defaultIndex return value if color is null
|
|
788 * @return the index of the specified color in the RTF color table
|
|
789 * or "defaultIndex" if "color" is null.
|
|
790 */
|
|
791 int getColorIndex(Color color, int defaultIndex) {
|
|
792 if (color is null) return defaultIndex;
|
|
793 int index = -1;
|
|
794 foreach( i, col; colorTable ){
|
|
795 if( col == color ){
|
|
796 index = i;
|
|
797 break;
|
|
798 }
|
|
799 }
|
|
800 if (index is -1) {
|
|
801 index = colorTable.length;
|
|
802 colorTable ~= color;
|
|
803 }
|
|
804 return index;
|
|
805 }
|
|
806 /**
|
|
807 * Returns the index of the specified color in the RTF color table.
|
|
808 *
|
|
809 * @param color the color
|
|
810 * @param defaultIndex return value if color is null
|
|
811 * @return the index of the specified color in the RTF color table
|
|
812 * or "defaultIndex" if "color" is null.
|
|
813 */
|
|
814 int getFontIndex(Font font) {
|
|
815 int index = -1;
|
|
816 foreach( i, f; colorTable ){
|
|
817 if( f == font ){
|
|
818 index = i;
|
|
819 break;
|
|
820 }
|
|
821 }
|
|
822 if (index is -1) {
|
|
823 index = fontTable.length;
|
|
824 fontTable ~= font;
|
|
825 }
|
|
826 return index;
|
|
827 }
|
|
828 /**
|
|
829 * Determines if Unicode RTF should be written.
|
|
830 * Don't write Unicode RTF on Windows 95/98/ME or NT.
|
|
831 */
|
|
832 void setUnicode() {
|
|
833 // const String Win95 = "windows 95";
|
|
834 // const String Win98 = "windows 98";
|
|
835 // const String WinME = "windows me";
|
|
836 // const String WinNT = "windows nt";
|
|
837 // String osName = System.getProperty("os.name").toLowerCase();
|
|
838 // String osVersion = System.getProperty("os.version");
|
|
839 // int majorVersion = 0;
|
|
840 //
|
|
841 // if (osName.startsWith(WinNT) && osVersion !is null) {
|
|
842 // int majorIndex = osVersion.indexOf('.');
|
|
843 // if (majorIndex !is -1) {
|
|
844 // osVersion = osVersion.substring(0, majorIndex);
|
|
845 // try {
|
|
846 // majorVersion = Integer.parseInt(osVersion);
|
|
847 // } catch (NumberFormatException exception) {
|
|
848 // // ignore exception. version number remains unknown.
|
|
849 // // will write without Unicode
|
|
850 // }
|
|
851 // }
|
|
852 // }
|
|
853 // WriteUnicode = !osName.startsWith(Win95) &&
|
|
854 // !osName.startsWith(Win98) &&
|
|
855 // !osName.startsWith(WinME) &&
|
|
856 // (!osName.startsWith(WinNT) || majorVersion > 4);
|
|
857 WriteUnicode = true; // we are on linux-gtk
|
|
858 }
|
|
859 /**
|
|
860 * Appends the specified segment of "string" to the RTF data.
|
|
861 * Copy from <code>start</code> up to, but excluding, <code>end</code>.
|
|
862 *
|
|
863 * @param string string to copy a segment from. Must not contain
|
|
864 * line breaks. Line breaks should be written using writeLineDelimiter()
|
|
865 * @param start start offset of segment. 0 based.
|
|
866 * @param end end offset of segment
|
|
867 */
|
|
868 void write(String string, int start, int end) {
|
|
869 start = 0;
|
|
870 end = string.length;
|
|
871 int incr = 1;
|
|
872 for (int index = start; index < end; index+=incr) {
|
|
873 dchar ch = firstCodePoint( string[index .. $], incr );
|
|
874 if (ch > 0xFF && WriteUnicode) {
|
|
875 // write the sub string from the last escaped character
|
|
876 // to the current one. Fixes bug 21698.
|
|
877 if (index > start) {
|
|
878 write( string[start .. index ] );
|
|
879 }
|
|
880 write("\\u");
|
|
881 write( to!(String)( cast(short)ch ));
|
|
882 write(' '); // control word delimiter
|
|
883 start = index + incr;
|
|
884 } else if (ch is '}' || ch is '{' || ch is '\\') {
|
|
885 // write the sub string from the last escaped character
|
|
886 // to the current one. Fixes bug 21698.
|
|
887 if (index > start) {
|
|
888 write(string[start .. index]);
|
|
889 }
|
|
890 write('\\');
|
|
891 write(cast(char)ch); // ok because one of {}\
|
|
892 start = index + 1;
|
|
893 }
|
|
894 }
|
|
895 // write from the last escaped character to the end.
|
|
896 // Fixes bug 21698.
|
|
897 if (start < end) {
|
|
898 write(string[ start .. end]);
|
|
899 }
|
|
900 }
|
|
901 /**
|
|
902 * Writes the RTF header including font table and color table.
|
|
903 */
|
|
904 void writeHeader() {
|
|
905 StringBuffer header = new StringBuffer();
|
|
906 FontData fontData = getFont().getFontData()[0];
|
|
907 header.append("{\\rtf1\\ansi");
|
|
908 // specify code page, necessary for copy to work in bidi
|
|
909 // systems that don't support Unicode RTF.
|
|
910 // PORTING_TODO: String cpg = System.getProperty("file.encoding").toLowerCase();
|
|
911 String cpg = "UTF16";
|
|
912 /+
|
|
913 if (cpg.startsWith("cp") || cpg.startsWith("ms")) {
|
|
914 cpg = cpg.substring(2, cpg.length());
|
|
915 header.append("\\ansicpg");
|
|
916 header.append(cpg);
|
|
917 }
|
|
918 +/
|
|
919 header.append("\\uc0\\deff0{\\fonttbl{\\f0\\fnil ");
|
|
920 header.append(fontData.getName());
|
|
921 header.append(";");
|
|
922 for (int i = 1; i < fontTable.length; i++) {
|
|
923 header.append("\\f");
|
|
924 header.append(i);
|
|
925 header.append(" ");
|
|
926 FontData fd = (cast(Font)fontTable[i]).getFontData()[0];
|
|
927 header.append(fd.getName());
|
|
928 header.append(";");
|
|
929 }
|
|
930 header.append("}}\n{\\colortbl");
|
|
931 for (int i = 0; i < colorTable.length; i++) {
|
|
932 Color color = cast(Color) colorTable[i];
|
|
933 header.append("\\red");
|
|
934 header.append(color.getRed());
|
|
935 header.append("\\green");
|
|
936 header.append(color.getGreen());
|
|
937 header.append("\\blue");
|
|
938 header.append(color.getBlue());
|
|
939 header.append(";");
|
|
940 }
|
|
941 // some RTF readers ignore the deff0 font tag. Explicitly
|
|
942 // set the font for the whole document to work around this.
|
|
943 header.append("}\n{\\f0\\fs");
|
|
944 // font size is specified in half points
|
|
945 header.append(fontData.getHeight() * 2);
|
|
946 header.append(" ");
|
|
947 write(header.toString(), 0);
|
|
948 }
|
|
949 /**
|
|
950 * Appends the specified line text to the RTF data. Lines will be formatted
|
|
951 * using the styles queried from the LineStyleListener, if set, or those set
|
|
952 * directly in the widget.
|
|
953 *
|
|
954 * @param line line text to write as RTF. Must not contain line breaks
|
|
955 * Line breaks should be written using writeLineDelimiter()
|
|
956 * @param lineOffset offset of the line. 0 based from the start of the
|
|
957 * widget document. Any text occurring before the start offset or after the
|
|
958 * end offset specified during object creation is ignored.
|
|
959 * @exception SWTException <ul>
|
|
960 * <li>ERROR_IO when the writer is closed.</li>
|
|
961 * </ul>
|
|
962 */
|
|
963 public override void writeLine(String line, int lineOffset) {
|
|
964 if (isClosed()) {
|
|
965 SWT.error(SWT.ERROR_IO);
|
|
966 }
|
|
967 int lineIndex = content.getLineAtOffset(lineOffset);
|
|
968 int lineAlignment, lineIndent;
|
|
969 bool lineJustify;
|
|
970 int[] ranges;
|
|
971 StyleRange[] styles;
|
|
972 StyledTextEvent event = getLineStyleData(lineOffset, line);
|
|
973 if (event !is null) {
|
|
974 lineAlignment = event.alignment;
|
|
975 lineIndent = event.indent;
|
|
976 lineJustify = event.justify;
|
|
977 ranges = event.ranges;
|
|
978 styles = event.styles;
|
|
979 } else {
|
|
980 lineAlignment = renderer.getLineAlignment(lineIndex, alignment);
|
|
981 lineIndent = renderer.getLineIndent(lineIndex, indent);
|
|
982 lineJustify = renderer.getLineJustify(lineIndex, justify);
|
|
983 ranges = renderer.getRanges(lineOffset, line.length);
|
|
984 styles = renderer.getStyleRanges(lineOffset, line.length, false);
|
|
985 }
|
|
986 if (styles is null) styles = new StyleRange[0];
|
|
987 Color lineBackground = renderer.getLineBackground(lineIndex, null);
|
|
988 event = getLineBackgroundData(lineOffset, line);
|
|
989 if (event !is null && event.lineBackground !is null) lineBackground = event.lineBackground;
|
|
990 writeStyledLine(line, lineOffset, ranges, styles, lineBackground, lineIndent, lineAlignment, lineJustify);
|
|
991 }
|
|
992 /**
|
|
993 * Appends the specified line delimiter to the RTF data.
|
|
994 *
|
|
995 * @param lineDelimiter line delimiter to write as RTF.
|
|
996 * @exception SWTException <ul>
|
|
997 * <li>ERROR_IO when the writer is closed.</li>
|
|
998 * </ul>
|
|
999 */
|
|
1000 public override void writeLineDelimiter(String lineDelimiter) {
|
|
1001 if (isClosed()) {
|
|
1002 SWT.error(SWT.ERROR_IO);
|
|
1003 }
|
|
1004 write(lineDelimiter, 0, lineDelimiter.length);
|
|
1005 write("\\par ");
|
|
1006 }
|
|
1007 /**
|
|
1008 * Appends the specified line text to the RTF data.
|
|
1009 * <p>
|
|
1010 * Use the colors and font styles specified in "styles" and "lineBackground".
|
|
1011 * Formatting is written to reflect the text rendering by the text widget.
|
|
1012 * Style background colors take precedence over the line background color.
|
|
1013 * Background colors are written using the \highlight tag (vs. the \cb tag).
|
|
1014 * </p>
|
|
1015 *
|
|
1016 * @param line line text to write as RTF. Must not contain line breaks
|
|
1017 * Line breaks should be written using writeLineDelimiter()
|
|
1018 * @param lineOffset offset of the line. 0 based from the start of the
|
|
1019 * widget document. Any text occurring before the start offset or after the
|
|
1020 * end offset specified during object creation is ignored.
|
|
1021 * @param styles styles to use for formatting. Must not be null.
|
|
1022 * @param lineBackground line background color to use for formatting.
|
|
1023 * May be null.
|
|
1024 */
|
|
1025 void writeStyledLine(String line, int lineOffset, int ranges[], StyleRange[] styles, Color lineBackground, int indent, int alignment, bool justify) {
|
|
1026 int lineLength = line.length;
|
|
1027 int startOffset = getStart();
|
|
1028 int writeOffset = startOffset - lineOffset;
|
|
1029 if (writeOffset >= lineLength) return;
|
|
1030 int lineIndex = Math.max(0, writeOffset);
|
|
1031
|
|
1032 write("\\fi");
|
|
1033 write(indent);
|
|
1034 switch (alignment) {
|
|
1035 case SWT.LEFT: write("\\ql"); break;
|
|
1036 case SWT.CENTER: write("\\qc"); break;
|
|
1037 case SWT.RIGHT: write("\\qr"); break;
|
|
1038 default:
|
|
1039 }
|
|
1040 if (justify) write("\\qj");
|
|
1041 write(" ");
|
|
1042
|
|
1043 if (lineBackground !is null) {
|
|
1044 write("{\\highlight");
|
|
1045 write(getColorIndex(lineBackground, DEFAULT_BACKGROUND));
|
|
1046 write(" ");
|
|
1047 }
|
|
1048 int endOffset = startOffset + super.getCharCount();
|
|
1049 int lineEndOffset = Math.min(lineLength, endOffset - lineOffset);
|
|
1050 for (int i = 0; i < styles.length; i++) {
|
|
1051 StyleRange style = styles[i];
|
|
1052 int start, end;
|
|
1053 if (ranges !is null) {
|
|
1054 start = ranges[i << 1] - lineOffset;
|
|
1055 end = start + ranges[(i << 1) + 1];
|
|
1056 } else {
|
|
1057 start = style.start - lineOffset;
|
|
1058 end = start + style.length;
|
|
1059 }
|
|
1060 // skip over partial first line
|
|
1061 if (end < writeOffset) {
|
|
1062 continue;
|
|
1063 }
|
|
1064 // style starts beyond line end or RTF write end
|
|
1065 if (start >= lineEndOffset) {
|
|
1066 break;
|
|
1067 }
|
|
1068 // write any unstyled text
|
|
1069 if (lineIndex < start) {
|
|
1070 // copy to start of style
|
|
1071 // style starting beyond end of write range or end of line
|
|
1072 // is guarded against above.
|
|
1073 write(line, lineIndex, start);
|
|
1074 lineIndex = start;
|
|
1075 }
|
|
1076 // write styled text
|
|
1077 write("{\\cf");
|
|
1078 write(getColorIndex(style.foreground, DEFAULT_FOREGROUND));
|
|
1079 int colorIndex = getColorIndex(style.background, DEFAULT_BACKGROUND);
|
|
1080 if (colorIndex !is DEFAULT_BACKGROUND) {
|
|
1081 write("\\highlight");
|
|
1082 write(colorIndex);
|
|
1083 }
|
|
1084 Font font = style.font;
|
|
1085 if (font !is null) {
|
|
1086 int fontIndex = getFontIndex(font);
|
|
1087 write("\\f");
|
|
1088 write(fontIndex);
|
|
1089 FontData fontData = font.getFontData()[0];
|
|
1090 write("\\fs");
|
|
1091 write(fontData.getHeight() * 2);
|
|
1092 } else {
|
|
1093 if ((style.fontStyle & SWT.BOLD) !is 0) {
|
|
1094 write("\\b");
|
|
1095 }
|
|
1096 if ((style.fontStyle & SWT.ITALIC) !is 0) {
|
|
1097 write("\\i");
|
|
1098 }
|
|
1099 }
|
|
1100 if (style.underline) {
|
|
1101 write("\\ul");
|
|
1102 }
|
|
1103 if (style.strikeout) {
|
|
1104 write("\\strike");
|
|
1105 }
|
|
1106 write(" ");
|
|
1107 // copy to end of style or end of write range or end of line
|
|
1108 int copyEnd = Math.min(end, lineEndOffset);
|
|
1109 // guard against invalid styles and let style processing continue
|
|
1110 copyEnd = Math.max(copyEnd, lineIndex);
|
|
1111 write(line, lineIndex, copyEnd);
|
|
1112 if (font is null) {
|
|
1113 if ((style.fontStyle & SWT.BOLD) !is 0) {
|
|
1114 write("\\b0");
|
|
1115 }
|
|
1116 if ((style.fontStyle & SWT.ITALIC) !is 0) {
|
|
1117 write("\\i0");
|
|
1118 }
|
|
1119 }
|
|
1120 if (style.underline) {
|
|
1121 write("\\ul0");
|
|
1122 }
|
|
1123 if (style.strikeout) {
|
|
1124 write("\\strike0");
|
|
1125 }
|
|
1126 write("}");
|
|
1127 lineIndex = copyEnd;
|
|
1128 }
|
|
1129 // write unstyled text at the end of the line
|
|
1130 if (lineIndex < lineEndOffset) {
|
|
1131 write(line, lineIndex, lineEndOffset);
|
|
1132 }
|
|
1133 if (lineBackground !is null) write("}");
|
|
1134 }
|
|
1135 }
|
|
1136 /**
|
|
1137 * The <code>TextWriter</code> class is used to write widget content to
|
|
1138 * a string. Whole and partial lines and line breaks can be written. To write
|
|
1139 * partial lines, specify the start and length of the desired segment
|
|
1140 * during object creation.
|
|
1141 * <p>
|
|
1142 * </b>NOTE:</b> <code>toString()</code> is guaranteed to return a valid string only after close()
|
|
1143 * has been called.
|
|
1144 * </p>
|
|
1145 */
|
|
1146 class TextWriter {
|
|
1147 private StringBuffer buffer;
|
|
1148 private int startOffset; // offset of first character that will be written
|
|
1149 private int endOffset; // offset of last character that will be written.
|
|
1150 // 0 based from the beginning of the widget text.
|
|
1151 private bool isClosed_ = false;
|
|
1152
|
|
1153 /**
|
|
1154 * Creates a writer that writes content starting at offset "start"
|
|
1155 * in the document. <code>start</code> and <code>length</code> can be set to specify partial lines.
|
|
1156 *
|
|
1157 * @param start start offset of content to write, 0 based from beginning of document
|
|
1158 * @param length length of content to write
|
|
1159 */
|
|
1160 public this(int start, int length) {
|
|
1161 buffer = new StringBuffer(length);
|
|
1162 startOffset = start;
|
|
1163 endOffset = start + length;
|
|
1164 }
|
|
1165 /**
|
|
1166 * Closes the writer. Once closed no more content can be written.
|
|
1167 * <b>NOTE:</b> <code>toString()</code> is not guaranteed to return a valid string unless
|
|
1168 * the writer is closed.
|
|
1169 */
|
|
1170 public void close() {
|
|
1171 if (!isClosed_) {
|
|
1172 isClosed_ = true;
|
|
1173 }
|
|
1174 }
|
|
1175 /**
|
|
1176 * Returns the number of characters to write.
|
|
1177 * @return the integer number of characters to write
|
|
1178 */
|
|
1179 public int getCharCount() {
|
|
1180 return endOffset - startOffset;
|
|
1181 }
|
|
1182 /**
|
|
1183 * Returns the offset where writing starts. 0 based from the start of
|
|
1184 * the widget text. Used to write partial lines.
|
|
1185 * @return the integer offset where writing starts
|
|
1186 */
|
|
1187 public int getStart() {
|
|
1188 return startOffset;
|
|
1189 }
|
|
1190 /**
|
|
1191 * Returns whether the writer is closed.
|
|
1192 * @return a bool specifying whether or not the writer is closed
|
|
1193 */
|
|
1194 public bool isClosed() {
|
|
1195 return isClosed_;
|
|
1196 }
|
|
1197 /**
|
|
1198 * Returns the string. <code>close()</code> must be called before <code>toString()</code>
|
|
1199 * is guaranteed to return a valid string.
|
|
1200 *
|
|
1201 * @return the string
|
|
1202 */
|
|
1203 public override String toString() {
|
|
1204 return buffer.toString();
|
|
1205 }
|
|
1206 /**
|
|
1207 * Appends the given string to the data.
|
|
1208 */
|
|
1209 void write(String string) {
|
|
1210 buffer.append(string);
|
|
1211 }
|
|
1212 /**
|
|
1213 * Inserts the given string to the data at the specified offset.
|
|
1214 * <p>
|
|
1215 * Do nothing if "offset" is < 0 or > getCharCount()
|
|
1216 * </p>
|
|
1217 *
|
|
1218 * @param string text to insert
|
|
1219 * @param offset offset in the existing data to insert "string" at.
|
|
1220 */
|
|
1221 void write(String string, int offset) {
|
|
1222 if (offset < 0 || offset > buffer.length()) {
|
|
1223 return;
|
|
1224 }
|
|
1225 buffer.insert( offset, string );
|
|
1226 }
|
|
1227 /**
|
|
1228 * Appends the given int to the data.
|
|
1229 */
|
|
1230 void write(int i) {
|
|
1231 buffer.append(i);
|
|
1232 }
|
|
1233 /**
|
|
1234 * Appends the given character to the data.
|
|
1235 */
|
|
1236 void write(char i) {
|
|
1237 buffer.append(i);
|
|
1238 }
|
|
1239 /**
|
|
1240 * Appends the specified line text to the data.
|
|
1241 *
|
|
1242 * @param line line text to write. Must not contain line breaks
|
|
1243 * Line breaks should be written using writeLineDelimiter()
|
|
1244 * @param lineOffset offset of the line. 0 based from the start of the
|
|
1245 * widget document. Any text occurring before the start offset or after the
|
|
1246 * end offset specified during object creation is ignored.
|
|
1247 * @exception SWTException <ul>
|
|
1248 * <li>ERROR_IO when the writer is closed.</li>
|
|
1249 * </ul>
|
|
1250 */
|
|
1251 public void writeLine(String line, int lineOffset) {
|
|
1252 if (isClosed_) {
|
|
1253 SWT.error(SWT.ERROR_IO);
|
|
1254 }
|
|
1255 int writeOffset = startOffset - lineOffset;
|
|
1256 int lineLength = line.length;
|
|
1257 int lineIndex;
|
|
1258 if (writeOffset >= lineLength) {
|
|
1259 return; // whole line is outside write range
|
|
1260 } else if (writeOffset > 0) {
|
|
1261 lineIndex = writeOffset; // line starts before write start
|
|
1262 } else {
|
|
1263 lineIndex = 0;
|
|
1264 }
|
|
1265 int copyEnd = Math.min(lineLength, endOffset - lineOffset);
|
|
1266 if (lineIndex < copyEnd) {
|
|
1267 write(line.substring(lineIndex, copyEnd));
|
|
1268 }
|
|
1269 }
|
|
1270 /**
|
|
1271 * Appends the specified line delimiter to the data.
|
|
1272 *
|
|
1273 * @param lineDelimiter line delimiter to write
|
|
1274 * @exception SWTException <ul>
|
|
1275 * <li>ERROR_IO when the writer is closed.</li>
|
|
1276 * </ul>
|
|
1277 */
|
|
1278 public void writeLineDelimiter(String lineDelimiter) {
|
|
1279 if (isClosed_) {
|
|
1280 SWT.error(SWT.ERROR_IO);
|
|
1281 }
|
|
1282 write(lineDelimiter);
|
|
1283 }
|
|
1284 }
|
|
1285
|
|
1286 /**
|
|
1287 * Constructs a new instance of this class given its parent
|
|
1288 * and a style value describing its behavior and appearance.
|
|
1289 * <p>
|
|
1290 * The style value is either one of the style constants defined in
|
|
1291 * class <code>SWT</code> which is applicable to instances of this
|
|
1292 * class, or must be built by <em>bitwise OR</em>'ing together
|
|
1293 * (that is, using the <code>int</code> "|" operator) two or more
|
|
1294 * of those <code>SWT</code> style constants. The class description
|
|
1295 * lists the style constants that are applicable to the class.
|
|
1296 * Style bits are also inherited from superclasses.
|
|
1297 * </p>
|
|
1298 *
|
|
1299 * @param parent a widget which will be the parent of the new instance (cannot be null)
|
|
1300 * @param style the style of widget to construct
|
|
1301 *
|
|
1302 * @exception IllegalArgumentException <ul>
|
|
1303 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
|
|
1304 * </ul>
|
|
1305 * @exception SWTException <ul>
|
|
1306 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
|
|
1307 * </ul>
|
|
1308 *
|
|
1309 * @see SWT#FULL_SELECTION
|
|
1310 * @see SWT#MULTI
|
|
1311 * @see SWT#READ_ONLY
|
|
1312 * @see SWT#SINGLE
|
|
1313 * @see SWT#WRAP
|
|
1314 * @see #getStyle
|
|
1315 */
|
|
1316 public this(Composite parent, int style) {
|
|
1317 selection = new Point(0, 0);
|
|
1318 super(parent, checkStyle(style));
|
|
1319 // set the fg in the OS to ensure that these are the same as StyledText, necessary
|
|
1320 // for ensuring that the bg/fg the IME box uses is the same as what StyledText uses
|
|
1321 super.setForeground(getForeground());
|
|
1322 super.setDragDetect(false);
|
|
1323 Display display = getDisplay();
|
|
1324 isMirrored_ = (super.getStyle() & SWT.MIRRORED) !is 0;
|
|
1325 fixedLineHeight = true;
|
|
1326 if ((style & SWT.READ_ONLY) !is 0) {
|
|
1327 setEditable(false);
|
|
1328 }
|
|
1329 leftMargin = rightMargin = isBidiCaret() ? BIDI_CARET_WIDTH - 1: 0;
|
|
1330 if ((style & SWT.SINGLE) !is 0 && (style & SWT.BORDER) !is 0) {
|
|
1331 leftMargin = topMargin = rightMargin = bottomMargin = 2;
|
|
1332 }
|
|
1333 alignment = style & (SWT.LEFT | SWT.RIGHT | SWT.CENTER);
|
|
1334 if (alignment is 0) alignment = SWT.LEFT;
|
|
1335 clipboard = new Clipboard(display);
|
|
1336 installDefaultContent();
|
|
1337 renderer = new StyledTextRenderer(getDisplay(), this);
|
|
1338 renderer.setContent(content);
|
|
1339 renderer.setFont(getFont(), tabLength);
|
|
1340 ime = new IME(this, SWT.NONE);
|
|
1341 defaultCaret = new Caret(this, SWT.NONE);
|
|
1342 if ((style & SWT.WRAP) !is 0) {
|
|
1343 setWordWrap(true);
|
|
1344 }
|
|
1345 if (isBidiCaret()) {
|
|
1346 createCaretBitmaps();
|
|
1347 Runnable runnable = new class() Runnable {
|
|
1348 public void run() {
|
|
1349 int direction = BidiUtil.getKeyboardLanguage() is BidiUtil.KEYBOARD_BIDI ? SWT.RIGHT : SWT.LEFT;
|
|
1350 if (direction is caretDirection) return;
|
|
1351 if (getCaret() !is defaultCaret) return;
|
|
1352 Point newCaretPos = getPointAtOffset(caretOffset);
|
|
1353 setCaretLocation(newCaretPos, direction);
|
|
1354 }
|
|
1355 };
|
|
1356 BidiUtil.addLanguageListener(this, runnable);
|
|
1357 }
|
|
1358 setCaret(defaultCaret);
|
|
1359 calculateScrollBars();
|
|
1360 createKeyBindings();
|
|
1361 setCursor(display.getSystemCursor(SWT.CURSOR_IBEAM));
|
|
1362 installListeners();
|
|
1363 initializeAccessible();
|
|
1364 setData("DEFAULT_DROP_TARGET_EFFECT", new StyledTextDropTargetEffect(this));
|
|
1365 }
|
|
1366 /**
|
|
1367 * Adds an extended modify listener. An ExtendedModify event is sent by the
|
|
1368 * widget when the widget text has changed.
|
|
1369 *
|
|
1370 * @param extendedModifyListener the listener
|
|
1371 * @exception SWTException <ul>
|
|
1372 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
1373 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
1374 * </ul>
|
|
1375 * @exception IllegalArgumentException <ul>
|
|
1376 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
|
|
1377 * </ul>
|
|
1378 */
|
|
1379 public void addExtendedModifyListener(ExtendedModifyListener extendedModifyListener) {
|
|
1380 checkWidget();
|
|
1381 if (extendedModifyListener is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
1382 StyledTextListener typedListener = new StyledTextListener(extendedModifyListener);
|
|
1383 addListener(ExtendedModify, typedListener);
|
|
1384 }
|
|
1385 /**
|
|
1386 * Adds a bidirectional segment listener.
|
|
1387 * <p>
|
|
1388 * A BidiSegmentEvent is sent
|
|
1389 * whenever a line of text is measured or rendered. The user can
|
|
1390 * specify text ranges in the line that should be treated as if they
|
|
1391 * had a different direction than the surrounding text.
|
|
1392 * This may be used when adjacent segments of right-to-left text should
|
|
1393 * not be reordered relative to each other.
|
|
1394 * E.g., Multiple Java string literals in a right-to-left language
|
|
1395 * should generally remain in logical order to each other, that is, the
|
|
1396 * way they are stored.
|
|
1397 * </p>
|
|
1398 *
|
|
1399 * @param listener the listener
|
|
1400 * @exception SWTException <ul>
|
|
1401 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
1402 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
1403 * </ul>
|
|
1404 * @exception IllegalArgumentException <ul>
|
|
1405 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
|
|
1406 * </ul>
|
|
1407 * @see BidiSegmentEvent
|
|
1408 * @since 2.0
|
|
1409 */
|
|
1410 public void addBidiSegmentListener(BidiSegmentListener listener) {
|
|
1411 checkWidget();
|
|
1412 if (listener is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
1413 addListener(LineGetSegments, new StyledTextListener(listener));
|
|
1414 }
|
|
1415 /**
|
|
1416 * Adds a line background listener. A LineGetBackground event is sent by the
|
|
1417 * widget to determine the background color for a line.
|
|
1418 *
|
|
1419 * @param listener the listener
|
|
1420 * @exception SWTException <ul>
|
|
1421 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
1422 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
1423 * </ul>
|
|
1424 * @exception IllegalArgumentException <ul>
|
|
1425 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
|
|
1426 * </ul>
|
|
1427 */
|
|
1428 public void addLineBackgroundListener(LineBackgroundListener listener) {
|
|
1429 checkWidget();
|
|
1430 if (listener is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
1431 if (!isListening(LineGetBackground)) {
|
|
1432 renderer.clearLineBackground(0, content.getLineCount());
|
|
1433 }
|
|
1434 addListener(LineGetBackground, new StyledTextListener(listener));
|
|
1435 }
|
|
1436 /**
|
|
1437 * Adds a line style listener. A LineGetStyle event is sent by the widget to
|
|
1438 * determine the styles for a line.
|
|
1439 *
|
|
1440 * @param listener the listener
|
|
1441 * @exception SWTException <ul>
|
|
1442 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
1443 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
1444 * </ul>
|
|
1445 * @exception IllegalArgumentException <ul>
|
|
1446 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
|
|
1447 * </ul>
|
|
1448 */
|
|
1449 public void addLineStyleListener(LineStyleListener listener) {
|
|
1450 checkWidget();
|
|
1451 if (listener is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
1452 if (!isListening(LineGetStyle)) {
|
|
1453 setStyleRanges(0, 0, null, null, true);
|
|
1454 renderer.clearLineStyle(0, content.getLineCount());
|
|
1455 }
|
|
1456 addListener(LineGetStyle, new StyledTextListener(listener));
|
|
1457 }
|
|
1458 /**
|
|
1459 * Adds a modify listener. A Modify event is sent by the widget when the widget text
|
|
1460 * has changed.
|
|
1461 *
|
|
1462 * @param modifyListener the listener
|
|
1463 * @exception SWTException <ul>
|
|
1464 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
1465 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
1466 * </ul>
|
|
1467 * @exception IllegalArgumentException <ul>
|
|
1468 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
|
|
1469 * </ul>
|
|
1470 */
|
|
1471 public void addModifyListener(ModifyListener modifyListener) {
|
|
1472 checkWidget();
|
|
1473 if (modifyListener is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
1474 addListener(SWT.Modify, new TypedListener(modifyListener));
|
|
1475 }
|
|
1476 /**
|
|
1477 * Adds a paint object listener. A paint object event is sent by the widget when an object
|
|
1478 * needs to be drawn.
|
|
1479 *
|
|
1480 * @param listener the listener
|
|
1481 * @exception SWTException <ul>
|
|
1482 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
1483 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
1484 * </ul>
|
|
1485 * @exception IllegalArgumentException <ul>
|
|
1486 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
|
|
1487 * </ul>
|
|
1488 *
|
|
1489 * @since 3.2
|
|
1490 *
|
|
1491 * @see PaintObjectListener
|
|
1492 * @see PaintObjectEvent
|
|
1493 */
|
|
1494 public void addPaintObjectListener(PaintObjectListener listener) {
|
|
1495 checkWidget();
|
|
1496 if (listener is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
1497 addListener(PaintObject, new StyledTextListener(listener));
|
|
1498 }
|
|
1499 /**
|
|
1500 * Adds a selection listener. A Selection event is sent by the widget when the
|
|
1501 * user changes the selection.
|
|
1502 * <p>
|
|
1503 * When <code>widgetSelected</code> is called, the event x and y fields contain
|
|
1504 * the start and end caret indices of the selection.
|
|
1505 * <code>widgetDefaultSelected</code> is not called for StyledTexts.
|
|
1506 * </p>
|
|
1507 *
|
|
1508 * @param listener the listener which should be notified when the user changes the receiver's selection
|
|
1509
|
|
1510 * @exception IllegalArgumentException <ul>
|
|
1511 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
|
|
1512 * </ul>
|
|
1513 * @exception SWTException <ul>
|
|
1514 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
1515 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
1516 * </ul>
|
|
1517 *
|
|
1518 * @see SelectionListener
|
|
1519 * @see #removeSelectionListener
|
|
1520 * @see SelectionEvent
|
|
1521 */
|
|
1522 public void addSelectionListener(SelectionListener listener) {
|
|
1523 checkWidget();
|
|
1524 if (listener is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
1525 addListener(SWT.Selection, new TypedListener(listener));
|
|
1526 }
|
|
1527 /**
|
|
1528 * Adds a verify key listener. A VerifyKey event is sent by the widget when a key
|
|
1529 * is pressed. The widget ignores the key press if the listener sets the doit field
|
|
1530 * of the event to false.
|
|
1531 *
|
|
1532 * @param listener the listener
|
|
1533 * @exception SWTException <ul>
|
|
1534 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
1535 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
1536 * </ul>
|
|
1537 * @exception IllegalArgumentException <ul>
|
|
1538 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
|
|
1539 * </ul>
|
|
1540 */
|
|
1541 public void addVerifyKeyListener(VerifyKeyListener listener) {
|
|
1542 checkWidget();
|
|
1543 if (listener is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
1544 addListener(VerifyKey, new StyledTextListener(listener));
|
|
1545 }
|
|
1546 /**
|
|
1547 * Adds a verify listener. A Verify event is sent by the widget when the widget text
|
|
1548 * is about to change. The listener can set the event text and the doit field to
|
|
1549 * change the text that is set in the widget or to force the widget to ignore the
|
|
1550 * text change.
|
|
1551 *
|
|
1552 * @param verifyListener the listener
|
|
1553 * @exception SWTException <ul>
|
|
1554 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
1555 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
1556 * </ul>
|
|
1557 * @exception IllegalArgumentException <ul>
|
|
1558 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
|
|
1559 * </ul>
|
|
1560 */
|
|
1561 public void addVerifyListener(VerifyListener verifyListener) {
|
|
1562 checkWidget();
|
|
1563 if (verifyListener is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
1564 addListener(SWT.Verify, new TypedListener(verifyListener));
|
|
1565 }
|
|
1566 /**
|
|
1567 * Adds a word movement listener. A movement event is sent when the boundary
|
|
1568 * of a word is needed. For example, this occurs during word next and word
|
|
1569 * previous actions.
|
|
1570 *
|
|
1571 * @param movementListener the listener
|
|
1572 * @exception SWTException <ul>
|
|
1573 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
1574 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
1575 * </ul>
|
|
1576 * @exception IllegalArgumentException <ul>
|
|
1577 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
|
|
1578 * </ul>
|
|
1579 *
|
|
1580 * @see MovementEvent
|
|
1581 * @see MovementListener
|
|
1582 * @see #removeWordMovementListener
|
|
1583 *
|
|
1584 * @since 3.3
|
|
1585 */
|
|
1586 public void addWordMovementListener(MovementListener movementListener) {
|
|
1587 checkWidget();
|
|
1588 if (listener is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
1589 addListener(WordNext, new StyledTextListener(movementListener));
|
|
1590 addListener(WordPrevious, new StyledTextListener(movementListener));
|
|
1591 }
|
|
1592 /**
|
|
1593 * Appends a string to the text at the end of the widget.
|
|
1594 *
|
|
1595 * @param string the string to be appended
|
|
1596 * @see #replaceTextRange(int,int,String)
|
|
1597 * @exception SWTException <ul>
|
|
1598 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
1599 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
1600 * </ul>
|
|
1601 */
|
|
1602 public void append(String string) {
|
|
1603 checkWidget();
|
|
1604 // SWT extension: allow null for zero length string
|
|
1605 // if (string is null) {
|
|
1606 // SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
1607 // }
|
|
1608 int lastChar = Math.max(getCharCount(), 0);
|
|
1609 replaceTextRange(lastChar, 0, string);
|
|
1610 }
|
|
1611 /**
|
|
1612 * Calculates the scroll bars
|
|
1613 */
|
|
1614 void calculateScrollBars() {
|
|
1615 ScrollBar horizontalBar = getHorizontalBar();
|
|
1616 ScrollBar verticalBar = getVerticalBar();
|
|
1617 setScrollBars(true);
|
|
1618 if (verticalBar !is null) {
|
|
1619 verticalBar.setIncrement(getVerticalIncrement());
|
|
1620 }
|
|
1621 if (horizontalBar !is null) {
|
|
1622 horizontalBar.setIncrement(getHorizontalIncrement());
|
|
1623 }
|
|
1624 }
|
|
1625 /**
|
|
1626 * Calculates the top index based on the current vertical scroll offset.
|
|
1627 * The top index is the index of the topmost fully visible line or the
|
|
1628 * topmost partially visible line if no line is fully visible.
|
|
1629 * The top index starts at 0.
|
|
1630 */
|
|
1631 void calculateTopIndex(int delta) {
|
|
1632 int oldTopIndex = topIndex;
|
|
1633 int oldTopIndexY = topIndexY;
|
|
1634 if (isFixedLineHeight()) {
|
|
1635 int verticalIncrement = getVerticalIncrement();
|
|
1636 if (verticalIncrement is 0) {
|
|
1637 return;
|
|
1638 }
|
|
1639 topIndex = Compatibility.ceil(getVerticalScrollOffset(), verticalIncrement);
|
|
1640 // Set top index to partially visible top line if no line is fully
|
|
1641 // visible but at least some of the widget client area is visible.
|
|
1642 // Fixes bug 15088.
|
|
1643 if (topIndex > 0) {
|
|
1644 if (clientAreaHeight > 0) {
|
|
1645 int bottomPixel = getVerticalScrollOffset() + clientAreaHeight;
|
|
1646 int fullLineTopPixel = topIndex * verticalIncrement;
|
|
1647 int fullLineVisibleHeight = bottomPixel - fullLineTopPixel;
|
|
1648 // set top index to partially visible line if no line fully fits in
|
|
1649 // client area or if space is available but not used (the latter should
|
|
1650 // never happen because we use claimBottomFreeSpace)
|
|
1651 if (fullLineVisibleHeight < verticalIncrement) {
|
|
1652 topIndex--;
|
|
1653 }
|
|
1654 } else if (topIndex >= content.getLineCount()) {
|
|
1655 topIndex = content.getLineCount() - 1;
|
|
1656 }
|
|
1657 }
|
|
1658 } else {
|
|
1659 if (delta >= 0) {
|
|
1660 delta -= topIndexY;
|
|
1661 int lineIndex = topIndex;
|
|
1662 int lineCount = content.getLineCount();
|
|
1663 while (lineIndex < lineCount) {
|
|
1664 if (delta <= 0) break;
|
|
1665 delta -= renderer.getLineHeight(lineIndex++);
|
|
1666 }
|
|
1667 if (lineIndex < lineCount && -delta + renderer.getLineHeight(lineIndex) <= clientAreaHeight - topMargin - bottomMargin) {
|
|
1668 topIndex = lineIndex;
|
|
1669 topIndexY = -delta;
|
|
1670 } else {
|
|
1671 topIndex = lineIndex - 1;
|
|
1672 topIndexY = -renderer.getLineHeight(topIndex) - delta;
|
|
1673 }
|
|
1674 } else {
|
|
1675 delta -= topIndexY;
|
|
1676 int lineIndex = topIndex;
|
|
1677 while (lineIndex > 0) {
|
|
1678 int lineHeight = renderer.getLineHeight(lineIndex - 1);
|
|
1679 if (delta + lineHeight > 0) break;
|
|
1680 delta += lineHeight;
|
|
1681 lineIndex--;
|
|
1682 }
|
|
1683 if (lineIndex is 0 || -delta + renderer.getLineHeight(lineIndex) <= clientAreaHeight - topMargin - bottomMargin) {
|
|
1684 topIndex = lineIndex;
|
|
1685 topIndexY = - delta;
|
|
1686 } else {
|
|
1687 topIndex = lineIndex - 1;
|
|
1688 topIndexY = - renderer.getLineHeight(topIndex) - delta;
|
|
1689 }
|
|
1690 }
|
|
1691 }
|
|
1692 if (topIndex !is oldTopIndex || oldTopIndexY !is topIndexY) {
|
|
1693 renderer.calculateClientArea();
|
|
1694 setScrollBars(false);
|
|
1695 }
|
|
1696 }
|
|
1697 /**
|
|
1698 * Hides the scroll bars if widget is created in single line mode.
|
|
1699 */
|
|
1700 static int checkStyle(int style) {
|
|
1701 if ((style & SWT.SINGLE) !is 0) {
|
|
1702 style &= ~(SWT.H_SCROLL | SWT.V_SCROLL | SWT.WRAP | SWT.MULTI);
|
|
1703 } else {
|
|
1704 style |= SWT.MULTI;
|
|
1705 if ((style & SWT.WRAP) !is 0) {
|
|
1706 style &= ~SWT.H_SCROLL;
|
|
1707 }
|
|
1708 }
|
|
1709 style |= SWT.NO_REDRAW_RESIZE | SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND;
|
|
1710 return style;
|
|
1711 }
|
|
1712 /**
|
|
1713 * Scrolls down the text to use new space made available by a resize or by
|
|
1714 * deleted lines.
|
|
1715 */
|
|
1716 void claimBottomFreeSpace() {
|
|
1717 int clientAreaHeight = this.clientAreaHeight - topMargin - bottomMargin;
|
|
1718 if (isFixedLineHeight()) {
|
|
1719 int lineHeight = renderer.getLineHeight();
|
|
1720 int newVerticalOffset = Math.max(0, content.getLineCount() * lineHeight - clientAreaHeight);
|
|
1721 if (newVerticalOffset < getVerticalScrollOffset()) {
|
|
1722 scrollVertical(newVerticalOffset - getVerticalScrollOffset(), true);
|
|
1723 }
|
|
1724 } else {
|
|
1725 int bottomIndex = getPartialBottomIndex();
|
|
1726 int height = getLinePixel(bottomIndex + 1);
|
|
1727 if (clientAreaHeight > height) {
|
|
1728 scrollVertical(-getAvailableHeightAbove(clientAreaHeight - height), true);
|
|
1729 }
|
|
1730 }
|
|
1731 }
|
|
1732 /**
|
|
1733 * Scrolls text to the right to use new space made available by a resize.
|
|
1734 */
|
|
1735 void claimRightFreeSpace() {
|
|
1736 int newHorizontalOffset = Math.max(0, renderer.getWidth() - (clientAreaWidth - leftMargin - rightMargin));
|
|
1737 if (newHorizontalOffset < horizontalScrollOffset) {
|
|
1738 // item is no longer drawn past the right border of the client area
|
|
1739 // align the right end of the item with the right border of the
|
|
1740 // client area (window is scrolled right).
|
|
1741 scrollHorizontal(newHorizontalOffset - horizontalScrollOffset, true);
|
|
1742 }
|
|
1743 }
|
|
1744 /**
|
|
1745 * Removes the widget selection.
|
|
1746 *
|
|
1747 * @param sendEvent a Selection event is sent when set to true and when the selection is actually reset.
|
|
1748 */
|
|
1749 void clearSelection(bool sendEvent) {
|
|
1750 int selectionStart = selection.x;
|
|
1751 int selectionEnd = selection.y;
|
|
1752 resetSelection();
|
|
1753 // redraw old selection, if any
|
|
1754 if (selectionEnd - selectionStart > 0) {
|
|
1755 int length = content.getCharCount();
|
|
1756 // called internally to remove selection after text is removed
|
|
1757 // therefore make sure redraw range is valid.
|
|
1758 int redrawStart = Math.min(selectionStart, length);
|
|
1759 int redrawEnd = Math.min(selectionEnd, length);
|
|
1760 if (redrawEnd - redrawStart > 0) {
|
|
1761 internalRedrawRange(redrawStart, redrawEnd - redrawStart);
|
|
1762 }
|
|
1763 if (sendEvent) {
|
|
1764 sendSelectionEvent();
|
|
1765 }
|
|
1766 }
|
|
1767 }
|
|
1768 public override Point computeSize (int wHint, int hHint, bool changed) {
|
|
1769 checkWidget();
|
|
1770 int lineCount = (getStyle() & SWT.SINGLE) !is 0 ? 1 : content.getLineCount();
|
|
1771 int width = 0;
|
|
1772 int height = 0;
|
|
1773 if (wHint is SWT.DEFAULT || hHint is SWT.DEFAULT) {
|
|
1774 Display display = getDisplay();
|
|
1775 int maxHeight = display.getClientArea().height;
|
|
1776 for (int lineIndex = 0; lineIndex < lineCount; lineIndex++) {
|
|
1777 TextLayout layout = renderer.getTextLayout(lineIndex);
|
|
1778 int wrapWidth = layout.getWidth();
|
|
1779 if (wordWrap) layout.setWidth(wHint is 0 ? 1 : wHint);
|
|
1780 Rectangle rect = layout.getBounds();
|
|
1781 height += rect.height;
|
|
1782 width = Math.max(width, rect.width);
|
|
1783 layout.setWidth(wrapWidth);
|
|
1784 renderer.disposeTextLayout(layout);
|
|
1785 if (isFixedLineHeight() && height > maxHeight) break;
|
|
1786 }
|
|
1787 if (isFixedLineHeight()) {
|
|
1788 height = lineCount * renderer.getLineHeight();
|
|
1789 }
|
|
1790 }
|
|
1791 // Use default values if no text is defined.
|
|
1792 if (width is 0) width = DEFAULT_WIDTH;
|
|
1793 if (height is 0) height = DEFAULT_HEIGHT;
|
|
1794 if (wHint !is SWT.DEFAULT) width = wHint;
|
|
1795 if (hHint !is SWT.DEFAULT) height = hHint;
|
|
1796 int wTrim = leftMargin + rightMargin + getCaretWidth();
|
|
1797 int hTrim = topMargin + bottomMargin;
|
|
1798 Rectangle rect = computeTrim(0, 0, width + wTrim, height + hTrim);
|
|
1799 return new Point (rect.width, rect.height);
|
|
1800 }
|
|
1801 /**
|
|
1802 * Copies the selected text to the <code>DND.CLIPBOARD</code> clipboard.
|
|
1803 * <p>
|
|
1804 * The text will be put on the clipboard in plain text format and RTF format.
|
|
1805 * The <code>DND.CLIPBOARD</code> clipboard is used for data that is
|
|
1806 * transferred by keyboard accelerator (such as Ctrl+C/Ctrl+V) or
|
|
1807 * by menu action.
|
|
1808 * </p>
|
|
1809 *
|
|
1810 * @exception SWTException <ul>
|
|
1811 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
1812 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
1813 * </ul>
|
|
1814 */
|
|
1815 public void copy() {
|
|
1816 checkWidget();
|
|
1817 copy(DND.CLIPBOARD);
|
|
1818 }
|
|
1819 /**
|
|
1820 * Copies the selected text to the specified clipboard. The text will be put in the
|
|
1821 * clipboard in plain text format and RTF format.
|
|
1822 * <p>
|
|
1823 * The clipboardType is one of the clipboard constants defined in class
|
|
1824 * <code>DND</code>. The <code>DND.CLIPBOARD</code> clipboard is
|
|
1825 * used for data that is transferred by keyboard accelerator (such as Ctrl+C/Ctrl+V)
|
|
1826 * or by menu action. The <code>DND.SELECTION_CLIPBOARD</code>
|
|
1827 * clipboard is used for data that is transferred by selecting text and pasting
|
|
1828 * with the middle mouse button.
|
|
1829 * </p>
|
|
1830 *
|
|
1831 * @param clipboardType indicates the type of clipboard
|
|
1832 *
|
|
1833 * @exception SWTException <ul>
|
|
1834 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
1835 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
1836 * </ul>
|
|
1837 *
|
|
1838 * @since 3.1
|
|
1839 */
|
|
1840 public void copy(int clipboardType) {
|
|
1841 checkWidget();
|
|
1842 if (clipboardType !is DND.CLIPBOARD && clipboardType !is DND.SELECTION_CLIPBOARD) return;
|
|
1843 int length = selection.y - selection.x;
|
|
1844 if (length > 0) {
|
|
1845 try {
|
|
1846 setClipboardContent(selection.x, length, clipboardType);
|
|
1847 } catch (SWTError error) {
|
|
1848 // Copy to clipboard failed. This happens when another application
|
|
1849 // is accessing the clipboard while we copy. Ignore the error.
|
|
1850 // Fixes 1GDQAVN
|
|
1851 // Rethrow all other errors. Fixes bug 17578.
|
|
1852 if (error.code !is DND.ERROR_CANNOT_SET_CLIPBOARD) {
|
|
1853 throw error;
|
|
1854 }
|
|
1855 }
|
|
1856 }
|
|
1857 }
|
|
1858 /**
|
|
1859 * Returns the alignment of the widget.
|
|
1860 *
|
|
1861 * @return the alignment
|
|
1862 *
|
|
1863 * @exception SWTException <ul>
|
|
1864 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
1865 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
1866 * </ul>
|
|
1867 *
|
|
1868 * @see #getLineAlignment(int)
|
|
1869 *
|
|
1870 * @since 3.2
|
|
1871 */
|
|
1872 public int getAlignment() {
|
|
1873 checkWidget();
|
|
1874 return alignment;
|
|
1875 }
|
|
1876 int getAvailableHeightAbove(int height) {
|
|
1877 int maxHeight = verticalScrollOffset;
|
|
1878 if (maxHeight is -1) {
|
|
1879 int lineIndex = topIndex - 1;
|
|
1880 maxHeight = -topIndexY;
|
|
1881 if (topIndexY > 0) {
|
|
1882 maxHeight += renderer.getLineHeight(lineIndex--);
|
|
1883 }
|
|
1884 while (height > maxHeight && lineIndex >= 0) {
|
|
1885 maxHeight += renderer.getLineHeight(lineIndex--);
|
|
1886 }
|
|
1887 }
|
|
1888 return Math.min(height, maxHeight);
|
|
1889 }
|
|
1890 int getAvailableHeightBellow(int height) {
|
|
1891 int partialBottomIndex = getPartialBottomIndex();
|
|
1892 int topY = getLinePixel(partialBottomIndex);
|
|
1893 int lineHeight = renderer.getLineHeight(partialBottomIndex);
|
|
1894 int availableHeight = 0;
|
|
1895 int clientAreaHeight = this.clientAreaHeight - topMargin - bottomMargin;
|
|
1896 if (topY + lineHeight > clientAreaHeight) {
|
|
1897 availableHeight = lineHeight - (clientAreaHeight - topY);
|
|
1898 }
|
|
1899 int lineIndex = partialBottomIndex + 1;
|
|
1900 int lineCount = content.getLineCount();
|
|
1901 while (height > availableHeight && lineIndex < lineCount) {
|
|
1902 availableHeight += renderer.getLineHeight(lineIndex++);
|
|
1903 }
|
|
1904 return Math.min(height, availableHeight);
|
|
1905 }
|
|
1906 /**
|
|
1907 * Returns a string that uses only the line delimiter specified by the
|
|
1908 * StyledTextContent implementation.
|
|
1909 * <p>
|
|
1910 * Returns only the first line if the widget has the SWT.SINGLE style.
|
|
1911 * </p>
|
|
1912 *
|
|
1913 * @param text the text that may have line delimiters that don't
|
|
1914 * match the model line delimiter. Possible line delimiters
|
|
1915 * are CR ('\r'), LF ('\n'), CR/LF ("\r\n")
|
|
1916 * @return the converted text that only uses the line delimiter
|
|
1917 * specified by the model. Returns only the first line if the widget
|
|
1918 * has the SWT.SINGLE style.
|
|
1919 */
|
|
1920 String getModelDelimitedText(String text) {
|
|
1921 int length = text.length;
|
|
1922 if (length is 0) {
|
|
1923 return text;
|
|
1924 }
|
|
1925 int crIndex = 0;
|
|
1926 int lfIndex = 0;
|
|
1927 int i = 0;
|
|
1928 StringBuffer convertedText = new StringBuffer(length);
|
|
1929 String delimiter = getLineDelimiter();
|
|
1930 while (i < length) {
|
|
1931 if (crIndex !is -1) {
|
|
1932 crIndex = text.indexOf (SWT.CR, i);
|
|
1933 }
|
|
1934 if (lfIndex !is -1) {
|
|
1935 lfIndex = text.indexOf (SWT.LF, i);
|
|
1936 }
|
|
1937 if (lfIndex is -1 && crIndex is -1) { // no more line breaks?
|
|
1938 break;
|
|
1939 } else if ((crIndex < lfIndex && crIndex !is -1) || lfIndex is -1) {
|
|
1940 convertedText.append(text.substring(i, crIndex));
|
|
1941 if (lfIndex is crIndex + 1) { // CR/LF combination?
|
|
1942 i = lfIndex + 1;
|
|
1943 } else {
|
|
1944 i = crIndex + 1;
|
|
1945 }
|
|
1946 } else { // LF occurs before CR!
|
|
1947 convertedText.append(text.substring(i, lfIndex));
|
|
1948 i = lfIndex + 1;
|
|
1949 }
|
|
1950 if (isSingleLine()) {
|
|
1951 break;
|
|
1952 }
|
|
1953 convertedText.append(delimiter);
|
|
1954 }
|
|
1955 // copy remaining text if any and if not in single line mode or no
|
|
1956 // text copied thus far (because there only is one line)
|
|
1957 if (i < length && (!isSingleLine() || convertedText.length() is 0)) {
|
|
1958 convertedText.append(text.substring(i));
|
|
1959 }
|
|
1960 return convertedText.toString();
|
|
1961 }
|
|
1962 bool checkDragDetect(Event event) {
|
|
1963 if (!isListening(SWT.DragDetect)) return false;
|
|
1964 if (IS_MOTIF) {
|
|
1965 if (event.button !is 2) return false;
|
|
1966 } else {
|
|
1967 if (event.button !is 1) return false;
|
|
1968 }
|
|
1969 if (selection.x is selection.y) return false;
|
|
1970 int offset = getOffsetAtPoint(event.x, event.y, null, true);
|
|
1971 if (selection.x <= offset && offset < selection.y) {
|
|
1972 return dragDetect(event);
|
|
1973 }
|
|
1974 return false;
|
|
1975 }
|
|
1976 /**
|
|
1977 * Creates default key bindings.
|
|
1978 */
|
|
1979 void createKeyBindings() {
|
|
1980 int nextKey = isMirrored() ? SWT.ARROW_LEFT : SWT.ARROW_RIGHT;
|
|
1981 int previousKey = isMirrored() ? SWT.ARROW_RIGHT : SWT.ARROW_LEFT;
|
|
1982
|
|
1983 // Navigation
|
|
1984 setKeyBinding(SWT.ARROW_UP, ST.LINE_UP);
|
|
1985 setKeyBinding(SWT.ARROW_DOWN, ST.LINE_DOWN);
|
|
1986 if (IS_CARBON) {
|
|
1987 setKeyBinding(previousKey | SWT.MOD1, ST.LINE_START);
|
|
1988 setKeyBinding(nextKey | SWT.MOD1, ST.LINE_END);
|
|
1989 setKeyBinding(SWT.HOME, ST.TEXT_START);
|
|
1990 setKeyBinding(SWT.END, ST.TEXT_END);
|
|
1991 setKeyBinding(SWT.ARROW_UP | SWT.MOD1, ST.TEXT_START);
|
|
1992 setKeyBinding(SWT.ARROW_DOWN | SWT.MOD1, ST.TEXT_END);
|
|
1993 setKeyBinding(nextKey | SWT.MOD3, ST.WORD_NEXT);
|
|
1994 setKeyBinding(previousKey | SWT.MOD3, ST.WORD_PREVIOUS);
|
|
1995 } else {
|
|
1996 setKeyBinding(SWT.HOME, ST.LINE_START);
|
|
1997 setKeyBinding(SWT.END, ST.LINE_END);
|
|
1998 setKeyBinding(SWT.HOME | SWT.MOD1, ST.TEXT_START);
|
|
1999 setKeyBinding(SWT.END | SWT.MOD1, ST.TEXT_END);
|
|
2000 setKeyBinding(nextKey | SWT.MOD1, ST.WORD_NEXT);
|
|
2001 setKeyBinding(previousKey | SWT.MOD1, ST.WORD_PREVIOUS);
|
|
2002 }
|
|
2003 setKeyBinding(SWT.PAGE_UP, ST.PAGE_UP);
|
|
2004 setKeyBinding(SWT.PAGE_DOWN, ST.PAGE_DOWN);
|
|
2005 setKeyBinding(SWT.PAGE_UP | SWT.MOD1, ST.WINDOW_START);
|
|
2006 setKeyBinding(SWT.PAGE_DOWN | SWT.MOD1, ST.WINDOW_END);
|
|
2007 setKeyBinding(nextKey, ST.COLUMN_NEXT);
|
|
2008 setKeyBinding(previousKey, ST.COLUMN_PREVIOUS);
|
|
2009
|
|
2010 // Selection
|
|
2011 setKeyBinding(SWT.ARROW_UP | SWT.MOD2, ST.SELECT_LINE_UP);
|
|
2012 setKeyBinding(SWT.ARROW_DOWN | SWT.MOD2, ST.SELECT_LINE_DOWN);
|
|
2013 if (IS_CARBON) {
|
|
2014 setKeyBinding(previousKey | SWT.MOD1 | SWT.MOD2, ST.SELECT_LINE_START);
|
|
2015 setKeyBinding(nextKey | SWT.MOD1 | SWT.MOD2, ST.SELECT_LINE_END);
|
|
2016 setKeyBinding(SWT.HOME | SWT.MOD2, ST.SELECT_TEXT_START);
|
|
2017 setKeyBinding(SWT.END | SWT.MOD2, ST.SELECT_TEXT_END);
|
|
2018 setKeyBinding(SWT.ARROW_UP | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_START);
|
|
2019 setKeyBinding(SWT.ARROW_DOWN | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_END);
|
|
2020 setKeyBinding(nextKey | SWT.MOD2 | SWT.MOD3, ST.SELECT_WORD_NEXT);
|
|
2021 setKeyBinding(previousKey | SWT.MOD2 | SWT.MOD3, ST.SELECT_WORD_PREVIOUS);
|
|
2022 } else {
|
|
2023 setKeyBinding(SWT.HOME | SWT.MOD2, ST.SELECT_LINE_START);
|
|
2024 setKeyBinding(SWT.END | SWT.MOD2, ST.SELECT_LINE_END);
|
|
2025 setKeyBinding(SWT.HOME | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_START);
|
|
2026 setKeyBinding(SWT.END | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_END);
|
|
2027 setKeyBinding(nextKey | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_NEXT);
|
|
2028 setKeyBinding(previousKey | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_PREVIOUS);
|
|
2029 }
|
|
2030 setKeyBinding(SWT.PAGE_UP | SWT.MOD2, ST.SELECT_PAGE_UP);
|
|
2031 setKeyBinding(SWT.PAGE_DOWN | SWT.MOD2, ST.SELECT_PAGE_DOWN);
|
|
2032 setKeyBinding(SWT.PAGE_UP | SWT.MOD1 | SWT.MOD2, ST.SELECT_WINDOW_START);
|
|
2033 setKeyBinding(SWT.PAGE_DOWN | SWT.MOD1 | SWT.MOD2, ST.SELECT_WINDOW_END);
|
|
2034 setKeyBinding(nextKey | SWT.MOD2, ST.SELECT_COLUMN_NEXT);
|
|
2035 setKeyBinding(previousKey | SWT.MOD2, ST.SELECT_COLUMN_PREVIOUS);
|
|
2036
|
|
2037 // Modification
|
|
2038 // Cut, Copy, Paste
|
|
2039 setKeyBinding('X' | SWT.MOD1, ST.CUT);
|
|
2040 setKeyBinding('C' | SWT.MOD1, ST.COPY);
|
|
2041 setKeyBinding('V' | SWT.MOD1, ST.PASTE);
|
|
2042 if (IS_CARBON) {
|
|
2043 setKeyBinding(SWT.DEL | SWT.MOD2, ST.DELETE_NEXT);
|
|
2044 setKeyBinding(SWT.BS | SWT.MOD3, ST.DELETE_WORD_PREVIOUS);
|
|
2045 setKeyBinding(SWT.DEL | SWT.MOD3, ST.DELETE_WORD_NEXT);
|
|
2046 } else {
|
|
2047 // Cut, Copy, Paste Wordstar style
|
|
2048 setKeyBinding(SWT.DEL | SWT.MOD2, ST.CUT);
|
|
2049 setKeyBinding(SWT.INSERT | SWT.MOD1, ST.COPY);
|
|
2050 setKeyBinding(SWT.INSERT | SWT.MOD2, ST.PASTE);
|
|
2051 }
|
|
2052 setKeyBinding(SWT.BS | SWT.MOD2, ST.DELETE_PREVIOUS);
|
|
2053 setKeyBinding(SWT.BS, ST.DELETE_PREVIOUS);
|
|
2054 setKeyBinding(SWT.DEL, ST.DELETE_NEXT);
|
|
2055 setKeyBinding(SWT.BS | SWT.MOD1, ST.DELETE_WORD_PREVIOUS);
|
|
2056 setKeyBinding(SWT.DEL | SWT.MOD1, ST.DELETE_WORD_NEXT);
|
|
2057
|
|
2058 // Miscellaneous
|
|
2059 setKeyBinding(SWT.INSERT, ST.TOGGLE_OVERWRITE);
|
|
2060 }
|
|
2061 /**
|
|
2062 * Create the bitmaps to use for the caret in bidi mode. This
|
|
2063 * method only needs to be called upon widget creation and when the
|
|
2064 * font changes (the caret bitmap height needs to match font height).
|
|
2065 */
|
|
2066 void createCaretBitmaps() {
|
|
2067 int caretWidth = BIDI_CARET_WIDTH;
|
|
2068 Display display = getDisplay();
|
|
2069 if (leftCaretBitmap !is null) {
|
|
2070 if (defaultCaret !is null && leftCaretBitmap==/*eq*/defaultCaret.getImage()) {
|
|
2071 defaultCaret.setImage(null);
|
|
2072 }
|
|
2073 leftCaretBitmap.dispose();
|
|
2074 }
|
|
2075 int lineHeight = renderer.getLineHeight();
|
|
2076 leftCaretBitmap = new Image(display, caretWidth, lineHeight);
|
|
2077 GC gc = new GC (leftCaretBitmap);
|
|
2078 gc.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
|
|
2079 gc.fillRectangle(0, 0, caretWidth, lineHeight);
|
|
2080 gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
|
|
2081 gc.drawLine(0,0,0,lineHeight);
|
|
2082 gc.drawLine(0,0,caretWidth-1,0);
|
|
2083 gc.drawLine(0,1,1,1);
|
|
2084 gc.dispose();
|
|
2085
|
|
2086 if (rightCaretBitmap !is null) {
|
|
2087 if (defaultCaret !is null && rightCaretBitmap==/*eq*/defaultCaret.getImage()) {
|
|
2088 defaultCaret.setImage(null);
|
|
2089 }
|
|
2090 rightCaretBitmap.dispose();
|
|
2091 }
|
|
2092 rightCaretBitmap = new Image(display, caretWidth, lineHeight);
|
|
2093 gc = new GC (rightCaretBitmap);
|
|
2094 gc.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
|
|
2095 gc.fillRectangle(0, 0, caretWidth, lineHeight);
|
|
2096 gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
|
|
2097 gc.drawLine(caretWidth-1,0,caretWidth-1,lineHeight);
|
|
2098 gc.drawLine(0,0,caretWidth-1,0);
|
|
2099 gc.drawLine(caretWidth-1,1,1,1);
|
|
2100 gc.dispose();
|
|
2101 }
|
|
2102 /**
|
|
2103 * Moves the selected text to the clipboard. The text will be put in the
|
|
2104 * clipboard in plain text format and RTF format.
|
|
2105 *
|
|
2106 * @exception SWTException <ul>
|
|
2107 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
2108 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
2109 * </ul>
|
|
2110 */
|
|
2111 public void cut(){
|
|
2112 checkWidget();
|
|
2113 int length = selection.y - selection.x;
|
|
2114 if (length > 0) {
|
|
2115 try {
|
|
2116 setClipboardContent(selection.x, length, DND.CLIPBOARD);
|
|
2117 } catch (SWTError error) {
|
|
2118 // Copy to clipboard failed. This happens when another application
|
|
2119 // is accessing the clipboard while we copy. Ignore the error.
|
|
2120 // Fixes 1GDQAVN
|
|
2121 // Rethrow all other errors. Fixes bug 17578.
|
|
2122 if (error.code !is DND.ERROR_CANNOT_SET_CLIPBOARD) {
|
|
2123 throw error;
|
|
2124 }
|
|
2125 // Abort cut operation if copy to clipboard fails.
|
|
2126 // Fixes bug 21030.
|
|
2127 return;
|
|
2128 }
|
|
2129 doDelete();
|
|
2130 }
|
|
2131 }
|
|
2132 /**
|
|
2133 * A mouse move event has occurred. See if we should start autoscrolling. If
|
|
2134 * the move position is outside of the client area, initiate autoscrolling.
|
|
2135 * Otherwise, we've moved back into the widget so end autoscrolling.
|
|
2136 */
|
|
2137 void doAutoScroll(Event event) {
|
|
2138 if (event.y > clientAreaHeight) {
|
|
2139 doAutoScroll(SWT.DOWN, event.y - clientAreaHeight);
|
|
2140 } else if (event.y < 0) {
|
|
2141 doAutoScroll(SWT.UP, -event.y);
|
|
2142 } else if (event.x < leftMargin && !wordWrap) {
|
|
2143 doAutoScroll(ST.COLUMN_PREVIOUS, leftMargin - event.x);
|
|
2144 } else if (event.x > clientAreaWidth - leftMargin - rightMargin && !wordWrap) {
|
|
2145 doAutoScroll(ST.COLUMN_NEXT, event.x - (clientAreaWidth - leftMargin - rightMargin));
|
|
2146 } else {
|
|
2147 endAutoScroll();
|
|
2148 }
|
|
2149 }
|
|
2150 /**
|
|
2151 * Initiates autoscrolling.
|
|
2152 *
|
|
2153 * @param direction SWT.UP, SWT.DOWN, SWT.COLUMN_NEXT, SWT.COLUMN_PREVIOUS
|
|
2154 */
|
|
2155 void doAutoScroll(int direction, int distance) {
|
|
2156 autoScrollDistance = distance;
|
|
2157 // If we're already autoscrolling in the given direction do nothing
|
|
2158 if (autoScrollDirection is direction) {
|
|
2159 return;
|
|
2160 }
|
|
2161
|
|
2162 Runnable timer = null;
|
|
2163 final Display disp = getDisplay();
|
|
2164 // Set a timer that will simulate the user pressing and holding
|
|
2165 // down a cursor key (i.e., arrowUp, arrowDown).
|
|
2166 if (direction is SWT.UP) {
|
|
2167 timer = new class(disp) Runnable {
|
|
2168 Display display;
|
|
2169 this( Display d ){ this.display = d; }
|
|
2170 public void run() {
|
|
2171 if (autoScrollDirection is SWT.UP) {
|
|
2172 doSelectionPageUp(autoScrollDistance);
|
|
2173 display.timerExec(V_SCROLL_RATE, this);
|
|
2174 }
|
|
2175 }
|
|
2176 };
|
|
2177 autoScrollDirection = direction;
|
|
2178 display.timerExec(V_SCROLL_RATE, timer);
|
|
2179 } else if (direction is SWT.DOWN) {
|
|
2180 timer = new class(disp) Runnable {
|
|
2181 Display display;
|
|
2182 this( Display d ){ this.display = d; }
|
|
2183 public void run() {
|
|
2184 if (autoScrollDirection is SWT.DOWN) {
|
|
2185 doSelectionPageDown(autoScrollDistance);
|
|
2186 display.timerExec(V_SCROLL_RATE, this);
|
|
2187 }
|
|
2188 }
|
|
2189 };
|
|
2190 autoScrollDirection = direction;
|
|
2191 display.timerExec(V_SCROLL_RATE, timer);
|
|
2192 } else if (direction is ST.COLUMN_NEXT) {
|
|
2193 timer = new class(disp) Runnable {
|
|
2194 Display display;
|
|
2195 this( Display d ){ this.display = d; }
|
|
2196 public void run() {
|
|
2197 if (autoScrollDirection is ST.COLUMN_NEXT) {
|
|
2198 doVisualNext();
|
|
2199 setMouseWordSelectionAnchor();
|
|
2200 doMouseSelection();
|
|
2201 display.timerExec(H_SCROLL_RATE, this);
|
|
2202 }
|
|
2203 }
|
|
2204 };
|
|
2205 autoScrollDirection = direction;
|
|
2206 display.timerExec(H_SCROLL_RATE, timer);
|
|
2207 } else if (direction is ST.COLUMN_PREVIOUS) {
|
|
2208 timer = new class(disp) Runnable {
|
|
2209 Display display;
|
|
2210 this( Display d ){ this.display = d; }
|
|
2211 public void run() {
|
|
2212 if (autoScrollDirection is ST.COLUMN_PREVIOUS) {
|
|
2213 doVisualPrevious();
|
|
2214 setMouseWordSelectionAnchor();
|
|
2215 doMouseSelection();
|
|
2216 display.timerExec(H_SCROLL_RATE, this);
|
|
2217 }
|
|
2218 }
|
|
2219 };
|
|
2220 autoScrollDirection = direction;
|
|
2221 display.timerExec(H_SCROLL_RATE, timer);
|
|
2222 }
|
|
2223 }
|
|
2224 /**
|
|
2225 * Deletes the previous character. Delete the selected text if any.
|
|
2226 * Move the caret in front of the deleted text.
|
|
2227 */
|
|
2228 void doBackspace() {
|
|
2229 Event event = new Event();
|
|
2230 event.text = "";
|
|
2231 if (selection.x !is selection.y) {
|
|
2232 event.start = selection.x;
|
|
2233 event.end = selection.y;
|
|
2234 sendKeyEvent(event);
|
|
2235 } else if (caretOffset > 0) {
|
|
2236 int lineIndex = content.getLineAtOffset(caretOffset);
|
|
2237 int lineOffset = content.getOffsetAtLine(lineIndex);
|
|
2238 if (caretOffset is lineOffset) {
|
|
2239 // SWT: on line start, delete line break
|
|
2240 lineOffset = content.getOffsetAtLine(lineIndex - 1);
|
|
2241 event.start = lineOffset + content.getLine(lineIndex - 1).length;
|
|
2242 event.end = caretOffset;
|
|
2243 } else {
|
|
2244 TextLayout layout = renderer.getTextLayout(lineIndex);
|
|
2245 int start = layout.getPreviousOffset(caretOffset - lineOffset, SWT.MOVEMENT_CLUSTER);
|
|
2246 renderer.disposeTextLayout(layout);
|
|
2247 event.start = start + lineOffset;
|
|
2248 event.end = caretOffset;
|
|
2249 }
|
|
2250 sendKeyEvent(event);
|
|
2251 }
|
|
2252 }
|
|
2253 /**
|
|
2254 * Replaces the selection with the character or insert the character at the
|
|
2255 * current caret position if no selection exists.
|
|
2256 * <p>
|
|
2257 * If a carriage return was typed replace it with the line break character
|
|
2258 * used by the widget on this platform.
|
|
2259 * </p>
|
|
2260 *
|
|
2261 * @param key the character typed by the user
|
|
2262 */
|
|
2263 void doContent(dchar key) {
|
|
2264 Event event = new Event();
|
|
2265 event.start = selection.x;
|
|
2266 event.end = selection.y;
|
|
2267 // replace a CR line break with the widget line break
|
|
2268 // CR does not make sense on Windows since most (all?) applications
|
|
2269 // don't recognize CR as a line break.
|
|
2270 if (key is SWT.CR || key is SWT.LF) {
|
|
2271 if (!isSingleLine()) {
|
|
2272 event.text = getLineDelimiter();
|
|
2273 }
|
|
2274 } else if (selection.x is selection.y && overwrite && key !is TAB) {
|
|
2275 // no selection and overwrite mode is on and the typed key is not a
|
|
2276 // tab character (tabs are always inserted without overwriting)?
|
|
2277 int lineIndex = content.getLineAtOffset(event.end);
|
|
2278 int lineOffset = content.getOffsetAtLine(lineIndex);
|
|
2279 String line = content.getLine(lineIndex);
|
|
2280 // replace character at caret offset if the caret is not at the
|
|
2281 // end of the line
|
|
2282 if (event.end < lineOffset + line.length) {
|
|
2283 event.end+=dcharToString( key ).length;
|
|
2284 }
|
|
2285 event.text = dcharToString( key );
|
|
2286 } else {
|
|
2287 event.text = dcharToString( key );
|
|
2288 }
|
|
2289 if (event.text !is null) {
|
|
2290 if (textLimit > 0 && content.getCharCount() - (event.end - event.start) >= textLimit) {
|
|
2291 return;
|
|
2292 }
|
|
2293 sendKeyEvent(event);
|
|
2294 }
|
|
2295 }
|
|
2296 /**
|
|
2297 * Moves the caret after the last character of the widget content.
|
|
2298 */
|
|
2299 void doContentEnd() {
|
|
2300 // place caret at end of first line if receiver is in single
|
|
2301 // line mode. fixes 4820.
|
|
2302 if (isSingleLine()) {
|
|
2303 doLineEnd();
|
|
2304 } else {
|
|
2305 int length = content.getCharCount();
|
|
2306 if (caretOffset < length) {
|
|
2307 caretOffset = length;
|
|
2308 showCaret();
|
|
2309 }
|
|
2310 }
|
|
2311 }
|
|
2312 /**
|
|
2313 * Moves the caret in front of the first character of the widget content.
|
|
2314 */
|
|
2315 void doContentStart() {
|
|
2316 if (caretOffset > 0) {
|
|
2317 caretOffset = 0;
|
|
2318 showCaret();
|
|
2319 }
|
|
2320 }
|
|
2321 /**
|
|
2322 * Moves the caret to the start of the selection if a selection exists.
|
|
2323 * Otherwise, if no selection exists move the cursor according to the
|
|
2324 * cursor selection rules.
|
|
2325 *
|
|
2326 * @see #doSelectionCursorPrevious
|
|
2327 */
|
|
2328 void doCursorPrevious() {
|
|
2329 if (selection.y - selection.x > 0) {
|
|
2330 caretOffset = selection.x;
|
|
2331 caretAlignment = OFFSET_LEADING;
|
|
2332 showCaret();
|
|
2333 } else {
|
|
2334 doSelectionCursorPrevious();
|
|
2335 }
|
|
2336 }
|
|
2337 /**
|
|
2338 * Moves the caret to the end of the selection if a selection exists.
|
|
2339 * Otherwise, if no selection exists move the cursor according to the
|
|
2340 * cursor selection rules.
|
|
2341 *
|
|
2342 * @see #doSelectionCursorNext
|
|
2343 */
|
|
2344 void doCursorNext() {
|
|
2345 if (selection.y - selection.x > 0) {
|
|
2346 caretOffset = selection.y;
|
|
2347 caretAlignment = PREVIOUS_OFFSET_TRAILING;
|
|
2348 showCaret();
|
|
2349 } else {
|
|
2350 doSelectionCursorNext();
|
|
2351 }
|
|
2352 }
|
|
2353 /**
|
|
2354 * Deletes the next character. Delete the selected text if any.
|
|
2355 */
|
|
2356 void doDelete() {
|
|
2357 Event event = new Event();
|
|
2358 event.text = "";
|
|
2359 if (selection.x !is selection.y) {
|
|
2360 event.start = selection.x;
|
|
2361 event.end = selection.y;
|
|
2362 sendKeyEvent(event);
|
|
2363 } else if (caretOffset < content.getCharCount()) {
|
|
2364 int line = content.getLineAtOffset(caretOffset);
|
|
2365 int lineOffset = content.getOffsetAtLine(line);
|
|
2366 int lineLength = content.getLine(line).length;
|
|
2367 if (caretOffset is lineOffset + lineLength) {
|
|
2368 event.start = caretOffset;
|
|
2369 event.end = content.getOffsetAtLine(line + 1);
|
|
2370 } else {
|
|
2371 event.start = caretOffset;
|
|
2372 event.end = getClusterNext(caretOffset, line);
|
|
2373 }
|
|
2374 sendKeyEvent(event);
|
|
2375 }
|
|
2376 }
|
|
2377 /**
|
|
2378 * Deletes the next word.
|
|
2379 */
|
|
2380 void doDeleteWordNext() {
|
|
2381 if (selection.x !is selection.y) {
|
|
2382 // if a selection exists, treat the as if
|
|
2383 // only the delete key was pressed
|
|
2384 doDelete();
|
|
2385 } else {
|
|
2386 Event event = new Event();
|
|
2387 event.text = "";
|
|
2388 event.start = caretOffset;
|
|
2389 event.end = getWordNext(caretOffset, SWT.MOVEMENT_WORD);
|
|
2390 sendKeyEvent(event);
|
|
2391 }
|
|
2392 }
|
|
2393 /**
|
|
2394 * Deletes the previous word.
|
|
2395 */
|
|
2396 void doDeleteWordPrevious() {
|
|
2397 if (selection.x !is selection.y) {
|
|
2398 // if a selection exists, treat as if
|
|
2399 // only the backspace key was pressed
|
|
2400 doBackspace();
|
|
2401 } else {
|
|
2402 Event event = new Event();
|
|
2403 event.text = "";
|
|
2404 event.start = getWordPrevious(caretOffset, SWT.MOVEMENT_WORD);
|
|
2405 event.end = caretOffset;
|
|
2406 sendKeyEvent(event);
|
|
2407 }
|
|
2408 }
|
|
2409 /**
|
|
2410 * Moves the caret one line down and to the same character offset relative
|
|
2411 * to the beginning of the line. Move the caret to the end of the new line
|
|
2412 * if the new line is shorter than the character offset.
|
|
2413 */
|
|
2414 void doLineDown(bool select) {
|
|
2415 int caretLine = getCaretLine();
|
|
2416 int lineCount = content.getLineCount();
|
|
2417 int y = 0;
|
|
2418 bool lastLine = false;
|
|
2419 if (wordWrap) {
|
|
2420 int lineOffset = content.getOffsetAtLine(caretLine);
|
|
2421 int offsetInLine = caretOffset - lineOffset;
|
|
2422 TextLayout layout = renderer.getTextLayout(caretLine);
|
|
2423 int lineIndex = getVisualLineIndex(layout, offsetInLine);
|
|
2424 int layoutLineCount = layout.getLineCount();
|
|
2425 if (lineIndex is layoutLineCount - 1) {
|
|
2426 lastLine = caretLine is lineCount - 1;
|
|
2427 caretLine++;
|
|
2428 } else {
|
|
2429 y = layout.getLineBounds(lineIndex + 1).y;
|
|
2430 }
|
|
2431 renderer.disposeTextLayout(layout);
|
|
2432 } else {
|
|
2433 lastLine = caretLine is lineCount - 1;
|
|
2434 caretLine++;
|
|
2435 }
|
|
2436 if (lastLine) {
|
|
2437 if (select) caretOffset = content.getCharCount();
|
|
2438 } else {
|
|
2439 caretOffset = getOffsetAtPoint(columnX, y, caretLine);
|
|
2440 }
|
|
2441 int oldColumnX = columnX;
|
|
2442 int oldHScrollOffset = horizontalScrollOffset;
|
|
2443 if (select) {
|
|
2444 setMouseWordSelectionAnchor();
|
|
2445 // select first and then scroll to reduce flash when key
|
|
2446 // repeat scrolls lots of lines
|
|
2447 doSelection(ST.COLUMN_NEXT);
|
|
2448 }
|
|
2449 showCaret();
|
|
2450 int hScrollChange = oldHScrollOffset - horizontalScrollOffset;
|
|
2451 columnX = oldColumnX + hScrollChange;
|
|
2452 }
|
|
2453 /**
|
|
2454 * Moves the caret to the end of the line.
|
|
2455 */
|
|
2456 void doLineEnd() {
|
|
2457 int caretLine = getCaretLine();
|
|
2458 int lineOffset = content.getOffsetAtLine(caretLine);
|
|
2459 int lineEndOffset;
|
|
2460 if (wordWrap) {
|
|
2461 TextLayout layout = renderer.getTextLayout(caretLine);
|
|
2462 int offsetInLine = caretOffset - lineOffset;
|
|
2463 int lineIndex = getVisualLineIndex(layout, offsetInLine);
|
|
2464 int[] offsets = layout.getLineOffsets();
|
|
2465 lineEndOffset = lineOffset + offsets[lineIndex + 1];
|
|
2466 renderer.disposeTextLayout(layout);
|
|
2467 } else {
|
|
2468 int lineLength = content.getLine(caretLine).length;
|
|
2469 lineEndOffset = lineOffset + lineLength;
|
|
2470 }
|
|
2471 if (caretOffset < lineEndOffset) {
|
|
2472 caretOffset = lineEndOffset;
|
|
2473 caretAlignment = PREVIOUS_OFFSET_TRAILING;
|
|
2474 showCaret();
|
|
2475 }
|
|
2476 }
|
|
2477 /**
|
|
2478 * Moves the caret to the beginning of the line.
|
|
2479 */
|
|
2480 void doLineStart() {
|
|
2481 int caretLine = getCaretLine();
|
|
2482 int lineOffset = content.getOffsetAtLine(caretLine);
|
|
2483 if (wordWrap) {
|
|
2484 TextLayout layout = renderer.getTextLayout(caretLine);
|
|
2485 int offsetInLine = caretOffset - lineOffset;
|
|
2486 int lineIndex = getVisualLineIndex(layout, offsetInLine);
|
|
2487 int[] offsets = layout.getLineOffsets();
|
|
2488 lineOffset += offsets[lineIndex];
|
|
2489 renderer.disposeTextLayout(layout);
|
|
2490 }
|
|
2491 if (caretOffset > lineOffset) {
|
|
2492 caretOffset = lineOffset;
|
|
2493 caretAlignment = OFFSET_LEADING;
|
|
2494 showCaret();
|
|
2495 }
|
|
2496 }
|
|
2497 /**
|
|
2498 * Moves the caret one line up and to the same character offset relative
|
|
2499 * to the beginning of the line. Move the caret to the end of the new line
|
|
2500 * if the new line is shorter than the character offset.
|
|
2501 */
|
|
2502 void doLineUp(bool select) {
|
|
2503 int caretLine = getCaretLine(), y = 0;
|
|
2504 bool firstLine = false;
|
|
2505 if (wordWrap) {
|
|
2506 int lineOffset = content.getOffsetAtLine(caretLine);
|
|
2507 int offsetInLine = caretOffset - lineOffset;
|
|
2508 TextLayout layout = renderer.getTextLayout(caretLine);
|
|
2509 int lineIndex = getVisualLineIndex(layout, offsetInLine);
|
|
2510 if (lineIndex is 0) {
|
|
2511 firstLine = caretLine is 0;
|
|
2512 if (!firstLine) {
|
|
2513 caretLine--;
|
|
2514 y = renderer.getLineHeight(caretLine) - 1;
|
|
2515 }
|
|
2516 } else {
|
|
2517 y = layout.getLineBounds(lineIndex - 1).y;
|
|
2518 }
|
|
2519 renderer.disposeTextLayout(layout);
|
|
2520 } else {
|
|
2521 firstLine = caretLine is 0;
|
|
2522 caretLine--;
|
|
2523 }
|
|
2524 if (firstLine) {
|
|
2525 if (select) caretOffset = 0;
|
|
2526 } else {
|
|
2527 caretOffset = getOffsetAtPoint(columnX, y, caretLine);
|
|
2528 }
|
|
2529 int oldColumnX = columnX;
|
|
2530 int oldHScrollOffset = horizontalScrollOffset;
|
|
2531 if (select) setMouseWordSelectionAnchor();
|
|
2532 showCaret();
|
|
2533 if (select) doSelection(ST.COLUMN_PREVIOUS);
|
|
2534 int hScrollChange = oldHScrollOffset - horizontalScrollOffset;
|
|
2535 columnX = oldColumnX + hScrollChange;
|
|
2536 }
|
|
2537 /**
|
|
2538 * Moves the caret to the specified location.
|
|
2539 *
|
|
2540 * @param x x location of the new caret position
|
|
2541 * @param y y location of the new caret position
|
|
2542 * @param select the location change is a selection operation.
|
|
2543 * include the line delimiter in the selection
|
|
2544 */
|
|
2545 void doMouseLocationChange(int x, int y, bool select) {
|
|
2546 int line = getLineIndex(y);
|
|
2547
|
|
2548 updateCaretDirection = true;
|
|
2549 // allow caret to be placed below first line only if receiver is
|
|
2550 // not in single line mode. fixes 4820.
|
|
2551 if (line < 0 || (isSingleLine() && line > 0)) {
|
|
2552 return;
|
|
2553 }
|
|
2554 int oldCaretAlignment = caretAlignment;
|
|
2555 int newCaretOffset = getOffsetAtPoint(x, y);
|
|
2556
|
|
2557 if (doubleClickEnabled && clickCount > 1) {
|
|
2558 newCaretOffset = doMouseWordSelect(x, newCaretOffset, line);
|
|
2559 }
|
|
2560
|
|
2561 int newCaretLine = content.getLineAtOffset(newCaretOffset);
|
|
2562
|
|
2563 // Is the mouse within the left client area border or on
|
|
2564 // a different line? If not the autoscroll selection
|
|
2565 // could be incorrectly reset. Fixes 1GKM3XS
|
|
2566 if (0 <= y && y < clientAreaHeight &&
|
|
2567 (0 <= x && x < clientAreaWidth || wordWrap ||
|
|
2568 newCaretLine !is content.getLineAtOffset(caretOffset))) {
|
|
2569 if (newCaretOffset !is caretOffset || caretAlignment !is oldCaretAlignment) {
|
|
2570 caretOffset = newCaretOffset;
|
|
2571 if (select) doMouseSelection();
|
|
2572 showCaret();
|
|
2573 }
|
|
2574 }
|
|
2575 if (!select) {
|
|
2576 caretOffset = newCaretOffset;
|
|
2577 clearSelection(true);
|
|
2578 }
|
|
2579 }
|
|
2580 /**
|
|
2581 * Updates the selection based on the caret position
|
|
2582 */
|
|
2583 void doMouseSelection() {
|
|
2584 if (caretOffset <= selection.x ||
|
|
2585 (caretOffset > selection.x &&
|
|
2586 caretOffset < selection.y && selectionAnchor is selection.x)) {
|
|
2587 doSelection(ST.COLUMN_PREVIOUS);
|
|
2588 } else {
|
|
2589 doSelection(ST.COLUMN_NEXT);
|
|
2590 }
|
|
2591 }
|
|
2592 /**
|
|
2593 * Returns the offset of the word at the specified offset.
|
|
2594 * If the current selection extends from high index to low index
|
|
2595 * (i.e., right to left, or caret is at left border of selection on
|
|
2596 * non-bidi platforms) the start offset of the word preceding the
|
|
2597 * selection is returned. If the current selection extends from
|
|
2598 * low index to high index the end offset of the word following
|
|
2599 * the selection is returned.
|
|
2600 *
|
|
2601 * @param x mouse x location
|
|
2602 * @param newCaretOffset caret offset of the mouse cursor location
|
|
2603 * @param line line index of the mouse cursor location
|
|
2604 */
|
|
2605 int doMouseWordSelect(int x, int newCaretOffset, int line) {
|
|
2606 // flip selection anchor based on word selection direction from
|
|
2607 // base double click. Always do this here (and don't rely on doAutoScroll)
|
|
2608 // because auto scroll only does not cover all possible mouse selections
|
|
2609 // (e.g., mouse x < 0 && mouse y > caret line y)
|
|
2610 if (newCaretOffset < selectionAnchor && selectionAnchor is selection.x) {
|
|
2611 selectionAnchor = doubleClickSelection.y;
|
|
2612 } else if (newCaretOffset > selectionAnchor && selectionAnchor is selection.y) {
|
|
2613 selectionAnchor = doubleClickSelection.x;
|
|
2614 }
|
|
2615 if (0 <= x && x < clientAreaWidth) {
|
|
2616 bool wordSelect = (clickCount & 1) is 0;
|
|
2617 if (caretOffset is selection.x) {
|
|
2618 if (wordSelect) {
|
|
2619 newCaretOffset = getWordPrevious(newCaretOffset, SWT.MOVEMENT_WORD_START);
|
|
2620 } else {
|
|
2621 newCaretOffset = content.getOffsetAtLine(line);
|
|
2622 }
|
|
2623 } else {
|
|
2624 if (wordSelect) {
|
|
2625 newCaretOffset = getWordNext(newCaretOffset, SWT.MOVEMENT_WORD_END);
|
|
2626 } else {
|
|
2627 int lineEnd = content.getCharCount();
|
|
2628 if (line + 1 < content.getLineCount()) {
|
|
2629 lineEnd = content.getOffsetAtLine(line + 1);
|
|
2630 }
|
|
2631 newCaretOffset = lineEnd;
|
|
2632 }
|
|
2633 }
|
|
2634 }
|
|
2635 return newCaretOffset;
|
|
2636 }
|
|
2637 /**
|
|
2638 * Scrolls one page down so that the last line (truncated or whole)
|
|
2639 * of the current page becomes the fully visible top line.
|
|
2640 * <p>
|
|
2641 * The caret is scrolled the same number of lines so that its location
|
|
2642 * relative to the top line remains the same. The exception is the end
|
|
2643 * of the text where a full page scroll is not possible. In this case
|
|
2644 * the caret is moved after the last character.
|
|
2645 * </p>
|
|
2646 *
|
|
2647 * @param select whether or not to select the page
|
|
2648 */
|
|
2649 void doPageDown(bool select, int height) {
|
|
2650 if (isSingleLine()) return;
|
|
2651 int oldColumnX = columnX;
|
|
2652 int oldHScrollOffset = horizontalScrollOffset;
|
|
2653 if (isFixedLineHeight()) {
|
|
2654 int lineCount = content.getLineCount();
|
|
2655 int caretLine = getCaretLine();
|
|
2656 if (caretLine < lineCount - 1) {
|
|
2657 int lineHeight = renderer.getLineHeight();
|
|
2658 int lines = (height is -1 ? clientAreaHeight : height) / lineHeight;
|
|
2659 int scrollLines = Math.min(lineCount - caretLine - 1, lines);
|
|
2660 // ensure that scrollLines never gets negative and at least one
|
|
2661 // line is scrolled. fixes bug 5602.
|
|
2662 scrollLines = Math.max(1, scrollLines);
|
|
2663 caretOffset = getOffsetAtPoint(columnX, getLinePixel(caretLine + scrollLines));
|
|
2664 if (select) {
|
|
2665 doSelection(ST.COLUMN_NEXT);
|
|
2666 }
|
|
2667 // scroll one page down or to the bottom
|
|
2668 int verticalMaximum = lineCount * getVerticalIncrement();
|
|
2669 int pageSize = clientAreaHeight;
|
|
2670 int verticalScrollOffset = getVerticalScrollOffset();
|
|
2671 int scrollOffset = verticalScrollOffset + scrollLines * getVerticalIncrement();
|
|
2672 if (scrollOffset + pageSize > verticalMaximum) {
|
|
2673 scrollOffset = verticalMaximum - pageSize;
|
|
2674 }
|
|
2675 if (scrollOffset > verticalScrollOffset) {
|
|
2676 scrollVertical(scrollOffset - verticalScrollOffset, true);
|
|
2677 }
|
|
2678 }
|
|
2679 } else {
|
|
2680 int lineCount = content.getLineCount();
|
|
2681 int caretLine = getCaretLine();
|
|
2682 int lineIndex, lineHeight;
|
|
2683 if (height is -1) {
|
|
2684 lineIndex = getPartialBottomIndex();
|
|
2685 int topY = getLinePixel(lineIndex);
|
|
2686 lineHeight = renderer.getLineHeight(lineIndex);
|
|
2687 height = topY;
|
|
2688 if (topY + lineHeight <= clientAreaHeight) {
|
|
2689 height += lineHeight;
|
|
2690 } else {
|
|
2691 if (wordWrap) {
|
|
2692 TextLayout layout = renderer.getTextLayout(lineIndex);
|
|
2693 int y = clientAreaHeight - topY;
|
|
2694 for (int i = 0; i < layout.getLineCount(); i++) {
|
|
2695 Rectangle bounds = layout.getLineBounds(i);
|
|
2696 if (bounds.contains(bounds.x, y)) {
|
|
2697 height += bounds.y;
|
|
2698 break;
|
|
2699 }
|
|
2700 }
|
|
2701 renderer.disposeTextLayout(layout);
|
|
2702 }
|
|
2703 }
|
|
2704 } else {
|
|
2705 lineIndex = getLineIndex(height);
|
|
2706 int topLineY = getLinePixel(lineIndex);
|
|
2707 if (wordWrap) {
|
|
2708 TextLayout layout = renderer.getTextLayout(lineIndex);
|
|
2709 int y = height - topLineY;
|
|
2710 for (int i = 0; i < layout.getLineCount(); i++) {
|
|
2711 Rectangle bounds = layout.getLineBounds(i);
|
|
2712 if (bounds.contains(bounds.x, y)) {
|
|
2713 height = topLineY + bounds.y + bounds.height;
|
|
2714 break;
|
|
2715 }
|
|
2716 }
|
|
2717 renderer.disposeTextLayout(layout);
|
|
2718 } else {
|
|
2719 height = topLineY + renderer.getLineHeight(lineIndex);
|
|
2720 }
|
|
2721 }
|
|
2722 int caretHeight = height;
|
|
2723 if (wordWrap) {
|
|
2724 TextLayout layout = renderer.getTextLayout(caretLine);
|
|
2725 int offsetInLine = caretOffset - content.getOffsetAtLine(caretLine);
|
|
2726 lineIndex = getVisualLineIndex(layout, offsetInLine);
|
|
2727 caretHeight += layout.getLineBounds(lineIndex).y;
|
|
2728 renderer.disposeTextLayout(layout);
|
|
2729 }
|
|
2730 lineIndex = caretLine;
|
|
2731 lineHeight = renderer.getLineHeight(lineIndex);
|
|
2732 while (caretHeight - lineHeight >= 0 && lineIndex < lineCount - 1) {
|
|
2733 caretHeight -= lineHeight;
|
|
2734 lineHeight = renderer.getLineHeight(++lineIndex);
|
|
2735 }
|
|
2736 caretOffset = getOffsetAtPoint(columnX, caretHeight, lineIndex);
|
|
2737 if (select) doSelection(ST.COLUMN_NEXT);
|
|
2738 height = getAvailableHeightBellow(height);
|
|
2739 scrollVertical(height, true);
|
|
2740 if (height is 0) setCaretLocation();
|
|
2741 }
|
|
2742 showCaret();
|
|
2743 int hScrollChange = oldHScrollOffset - horizontalScrollOffset;
|
|
2744 columnX = oldColumnX + hScrollChange;
|
|
2745 }
|
|
2746 /**
|
|
2747 * Moves the cursor to the end of the last fully visible line.
|
|
2748 */
|
|
2749 void doPageEnd() {
|
|
2750 // go to end of line if in single line mode. fixes 5673
|
|
2751 if (isSingleLine()) {
|
|
2752 doLineEnd();
|
|
2753 } else {
|
|
2754 int bottomOffset;
|
|
2755 if (wordWrap) {
|
|
2756 int lineIndex = getPartialBottomIndex();
|
|
2757 TextLayout layout = renderer.getTextLayout(lineIndex);
|
|
2758 int y = (clientAreaHeight - bottomMargin) - getLinePixel(lineIndex);
|
|
2759 int index = layout.getLineCount() - 1;
|
|
2760 while (index >= 0) {
|
|
2761 Rectangle bounds = layout.getLineBounds(index);
|
|
2762 if (y >= bounds.y + bounds.height) break;
|
|
2763 index--;
|
|
2764 }
|
|
2765 if (index is -1 && lineIndex > 0) {
|
|
2766 bottomOffset = content.getOffsetAtLine(lineIndex - 1) + content.getLine(lineIndex - 1).length;
|
|
2767 } else {
|
|
2768 bottomOffset = content.getOffsetAtLine(lineIndex) + Math.max(0, layout.getLineOffsets()[index + 1] - 1);
|
|
2769 }
|
|
2770 renderer.disposeTextLayout(layout);
|
|
2771 } else {
|
|
2772 int lineIndex = getBottomIndex();
|
|
2773 bottomOffset = content.getOffsetAtLine(lineIndex) + content.getLine(lineIndex).length;
|
|
2774 }
|
|
2775 if (caretOffset < bottomOffset) {
|
|
2776 caretOffset = bottomOffset;
|
|
2777 caretAlignment = OFFSET_LEADING;
|
|
2778 showCaret();
|
|
2779 }
|
|
2780 }
|
|
2781 }
|
|
2782 /**
|
|
2783 * Moves the cursor to the beginning of the first fully visible line.
|
|
2784 */
|
|
2785 void doPageStart() {
|
|
2786 int topOffset;
|
|
2787 if (wordWrap) {
|
|
2788 int y, lineIndex;
|
|
2789 if (topIndexY > 0) {
|
|
2790 lineIndex = topIndex - 1;
|
|
2791 y = renderer.getLineHeight(lineIndex) - topIndexY;
|
|
2792 } else {
|
|
2793 lineIndex = topIndex;
|
|
2794 y = -topIndexY;
|
|
2795 }
|
|
2796 TextLayout layout = renderer.getTextLayout(lineIndex);
|
|
2797 int index = 0;
|
|
2798 int lineCount = layout.getLineCount();
|
|
2799 while (index < lineCount) {
|
|
2800 Rectangle bounds = layout.getLineBounds(index);
|
|
2801 if (y <= bounds.y) break;
|
|
2802 index++;
|
|
2803 }
|
|
2804 if (index is lineCount) {
|
|
2805 topOffset = content.getOffsetAtLine(lineIndex + 1);
|
|
2806 } else {
|
|
2807 topOffset = content.getOffsetAtLine(lineIndex) + layout.getLineOffsets()[index];
|
|
2808 }
|
|
2809 renderer.disposeTextLayout(layout);
|
|
2810 } else {
|
|
2811 topOffset = content.getOffsetAtLine(topIndex);
|
|
2812 }
|
|
2813 if (caretOffset > topOffset) {
|
|
2814 caretOffset = topOffset;
|
|
2815 caretAlignment = OFFSET_LEADING;
|
|
2816 showCaret();
|
|
2817 }
|
|
2818 }
|
|
2819 /**
|
|
2820 * Scrolls one page up so that the first line (truncated or whole)
|
|
2821 * of the current page becomes the fully visible last line.
|
|
2822 * The caret is scrolled the same number of lines so that its location
|
|
2823 * relative to the top line remains the same. The exception is the beginning
|
|
2824 * of the text where a full page scroll is not possible. In this case the
|
|
2825 * caret is moved in front of the first character.
|
|
2826 */
|
|
2827 void doPageUp(bool select, int height) {
|
|
2828 if (isSingleLine()) return;
|
|
2829 int oldHScrollOffset = horizontalScrollOffset;
|
|
2830 int oldColumnX = columnX;
|
|
2831 if (isFixedLineHeight()) {
|
|
2832 int caretLine = getCaretLine();
|
|
2833 if (caretLine > 0) {
|
|
2834 int lineHeight = renderer.getLineHeight();
|
|
2835 int lines = (height is -1 ? clientAreaHeight : height) / lineHeight;
|
|
2836 int scrollLines = Math.max(1, Math.min(caretLine, lines));
|
|
2837 caretLine -= scrollLines;
|
|
2838 caretOffset = getOffsetAtPoint(columnX, getLinePixel(caretLine));
|
|
2839 if (select) {
|
|
2840 doSelection(ST.COLUMN_PREVIOUS);
|
|
2841 }
|
|
2842 int verticalScrollOffset = getVerticalScrollOffset();
|
|
2843 int scrollOffset = Math.max(0, verticalScrollOffset - scrollLines * getVerticalIncrement());
|
|
2844 if (scrollOffset < verticalScrollOffset) {
|
|
2845 scrollVertical(scrollOffset - verticalScrollOffset, true);
|
|
2846 }
|
|
2847 }
|
|
2848 } else {
|
|
2849 int caretLine = getCaretLine();
|
|
2850 int lineHeight, lineIndex;
|
|
2851 if (height is -1) {
|
|
2852 if (topIndexY is 0) {
|
|
2853 height = clientAreaHeight;
|
|
2854 } else {
|
|
2855 int y;
|
|
2856 if (topIndex > 0) {
|
|
2857 lineIndex = topIndex - 1;
|
|
2858 lineHeight = renderer.getLineHeight(lineIndex);
|
|
2859 height = clientAreaHeight - topIndexY;
|
|
2860 y = lineHeight - topIndexY;
|
|
2861 } else {
|
|
2862 lineIndex = topIndex;
|
|
2863 lineHeight = renderer.getLineHeight(lineIndex);
|
|
2864 height = clientAreaHeight - (lineHeight + topIndexY);
|
|
2865 y = -topIndexY;
|
|
2866 }
|
|
2867 if (wordWrap) {
|
|
2868 TextLayout layout = renderer.getTextLayout(lineIndex);
|
|
2869 for (int i = 0; i < layout.getLineCount(); i++) {
|
|
2870 Rectangle bounds = layout.getLineBounds(i);
|
|
2871 if (bounds.contains(bounds.x, y)) {
|
|
2872 height += lineHeight - (bounds.y + bounds.height);
|
|
2873 break;
|
|
2874 }
|
|
2875 }
|
|
2876 renderer.disposeTextLayout(layout);
|
|
2877 }
|
|
2878 }
|
|
2879 } else {
|
|
2880 lineIndex = getLineIndex(clientAreaHeight - height);
|
|
2881 int topLineY = getLinePixel(lineIndex);
|
|
2882 if (wordWrap) {
|
|
2883 TextLayout layout = renderer.getTextLayout(lineIndex);
|
|
2884 int y = topLineY;
|
|
2885 for (int i = 0; i < layout.getLineCount(); i++) {
|
|
2886 Rectangle bounds = layout.getLineBounds(i);
|
|
2887 if (bounds.contains(bounds.x, y)) {
|
|
2888 height = clientAreaHeight - (topLineY + bounds.y);
|
|
2889 break;
|
|
2890 }
|
|
2891 }
|
|
2892 renderer.disposeTextLayout(layout);
|
|
2893 } else {
|
|
2894 height = clientAreaHeight - topLineY;
|
|
2895 }
|
|
2896 }
|
|
2897 int caretHeight = height;
|
|
2898 if (wordWrap) {
|
|
2899 TextLayout layout = renderer.getTextLayout(caretLine);
|
|
2900 int offsetInLine = caretOffset - content.getOffsetAtLine(caretLine);
|
|
2901 lineIndex = getVisualLineIndex(layout, offsetInLine);
|
|
2902 caretHeight += layout.getBounds().height - layout.getLineBounds(lineIndex).y;
|
|
2903 renderer.disposeTextLayout(layout);
|
|
2904 }
|
|
2905 lineIndex = caretLine;
|
|
2906 lineHeight = renderer.getLineHeight(lineIndex);
|
|
2907 while (caretHeight - lineHeight >= 0 && lineIndex > 0) {
|
|
2908 caretHeight -= lineHeight;
|
|
2909 lineHeight = renderer.getLineHeight(--lineIndex);
|
|
2910 }
|
|
2911 lineHeight = renderer.getLineHeight(lineIndex);
|
|
2912 caretOffset = getOffsetAtPoint(columnX, lineHeight - caretHeight, lineIndex);
|
|
2913 if (select) doSelection(ST.COLUMN_PREVIOUS);
|
|
2914 height = getAvailableHeightAbove(height);
|
|
2915 scrollVertical(-height, true);
|
|
2916 if (height is 0) setCaretLocation();
|
|
2917 }
|
|
2918 showCaret();
|
|
2919 int hScrollChange = oldHScrollOffset - horizontalScrollOffset;
|
|
2920 columnX = oldColumnX + hScrollChange;
|
|
2921 }
|
|
2922 /**
|
|
2923 * Updates the selection to extend to the current caret position.
|
|
2924 */
|
|
2925 void doSelection(int direction) {
|
|
2926 int redrawStart = -1;
|
|
2927 int redrawEnd = -1;
|
|
2928 if (selectionAnchor is -1) {
|
|
2929 selectionAnchor = selection.x;
|
|
2930 }
|
|
2931 if (direction is ST.COLUMN_PREVIOUS) {
|
|
2932 if (caretOffset < selection.x) {
|
|
2933 // grow selection
|
|
2934 redrawEnd = selection.x;
|
|
2935 redrawStart = selection.x = caretOffset;
|
|
2936 // check if selection has reversed direction
|
|
2937 if (selection.y !is selectionAnchor) {
|
|
2938 redrawEnd = selection.y;
|
|
2939 selection.y = selectionAnchor;
|
|
2940 }
|
|
2941 // test whether selection actually changed. Fixes 1G71EO1
|
|
2942 } else if (selectionAnchor is selection.x && caretOffset < selection.y) {
|
|
2943 // caret moved towards selection anchor (left side of selection).
|
|
2944 // shrink selection
|
|
2945 redrawEnd = selection.y;
|
|
2946 redrawStart = selection.y = caretOffset;
|
|
2947 }
|
|
2948 } else {
|
|
2949 if (caretOffset > selection.y) {
|
|
2950 // grow selection
|
|
2951 redrawStart = selection.y;
|
|
2952 redrawEnd = selection.y = caretOffset;
|
|
2953 // check if selection has reversed direction
|
|
2954 if (selection.x !is selectionAnchor) {
|
|
2955 redrawStart = selection.x;
|
|
2956 selection.x = selectionAnchor;
|
|
2957 }
|
|
2958 // test whether selection actually changed. Fixes 1G71EO1
|
|
2959 } else if (selectionAnchor is selection.y && caretOffset > selection.x) {
|
|
2960 // caret moved towards selection anchor (right side of selection).
|
|
2961 // shrink selection
|
|
2962 redrawStart = selection.x;
|
|
2963 redrawEnd = selection.x = caretOffset;
|
|
2964 }
|
|
2965 }
|
|
2966 if (redrawStart !is -1 && redrawEnd !is -1) {
|
|
2967 internalRedrawRange(redrawStart, redrawEnd - redrawStart);
|
|
2968 sendSelectionEvent();
|
|
2969 }
|
|
2970 }
|
|
2971 /**
|
|
2972 * Moves the caret to the next character or to the beginning of the
|
|
2973 * next line if the cursor is at the end of a line.
|
|
2974 */
|
|
2975 void doSelectionCursorNext() {
|
|
2976 int caretLine = getCaretLine();
|
|
2977 int lineOffset = content.getOffsetAtLine(caretLine);
|
|
2978 int offsetInLine = caretOffset - lineOffset;
|
|
2979 if (offsetInLine < content.getLine(caretLine).length) {
|
|
2980 TextLayout layout = renderer.getTextLayout(caretLine);
|
|
2981 offsetInLine = layout.getNextOffset(offsetInLine, SWT.MOVEMENT_CLUSTER);
|
|
2982 int lineStart = layout.getLineOffsets()[layout.getLineIndex(offsetInLine)];
|
|
2983 renderer.disposeTextLayout(layout);
|
|
2984 caretOffset = offsetInLine + lineOffset;
|
|
2985 caretAlignment = offsetInLine is lineStart ? OFFSET_LEADING : PREVIOUS_OFFSET_TRAILING;
|
|
2986 showCaret();
|
|
2987 } else if (caretLine < content.getLineCount() - 1 && !isSingleLine()) {
|
|
2988 caretLine++;
|
|
2989 caretOffset = content.getOffsetAtLine(caretLine);
|
|
2990 caretAlignment = PREVIOUS_OFFSET_TRAILING;
|
|
2991 showCaret();
|
|
2992 }
|
|
2993 }
|
|
2994 /**
|
|
2995 * Moves the caret to the previous character or to the end of the previous
|
|
2996 * line if the cursor is at the beginning of a line.
|
|
2997 */
|
|
2998 void doSelectionCursorPrevious() {
|
|
2999 int caretLine = getCaretLine();
|
|
3000 int lineOffset = content.getOffsetAtLine(caretLine);
|
|
3001 int offsetInLine = caretOffset - lineOffset;
|
|
3002 caretAlignment = OFFSET_LEADING;
|
|
3003
|
|
3004 if (offsetInLine > 0) {
|
|
3005 caretOffset = getClusterPrevious(caretOffset, caretLine);
|
|
3006 showCaret();
|
|
3007 } else if (caretLine > 0) {
|
|
3008 caretLine--;
|
|
3009 lineOffset = content.getOffsetAtLine(caretLine);
|
|
3010 caretOffset = lineOffset + content.getLine(caretLine).length;
|
|
3011 showCaret();
|
|
3012 }
|
|
3013 }
|
|
3014 /**
|
|
3015 * Moves the caret one line down and to the same character offset relative
|
|
3016 * to the beginning of the line. Moves the caret to the end of the new line
|
|
3017 * if the new line is shorter than the character offset.
|
|
3018 * Moves the caret to the end of the text if the caret already is on the
|
|
3019 * last line.
|
|
3020 * Adjusts the selection according to the caret change. This can either add
|
|
3021 * to or subtract from the old selection, depending on the previous selection
|
|
3022 * direction.
|
|
3023 */
|
|
3024 void doSelectionLineDown() {
|
|
3025 int oldColumnX = columnX = getPointAtOffset(caretOffset).x;
|
|
3026 doLineDown(true);
|
|
3027 columnX = oldColumnX;
|
|
3028 }
|
|
3029 /**
|
|
3030 * Moves the caret one line up and to the same character offset relative
|
|
3031 * to the beginning of the line. Moves the caret to the end of the new line
|
|
3032 * if the new line is shorter than the character offset.
|
|
3033 * Moves the caret to the beginning of the document if it is already on the
|
|
3034 * first line.
|
|
3035 * Adjusts the selection according to the caret change. This can either add
|
|
3036 * to or subtract from the old selection, depending on the previous selection
|
|
3037 * direction.
|
|
3038 */
|
|
3039 void doSelectionLineUp() {
|
|
3040 int oldColumnX = columnX = getPointAtOffset(caretOffset).x;
|
|
3041 doLineUp(true);
|
|
3042 columnX = oldColumnX;
|
|
3043 }
|
|
3044 /**
|
|
3045 * Scrolls one page down so that the last line (truncated or whole)
|
|
3046 * of the current page becomes the fully visible top line.
|
|
3047 * <p>
|
|
3048 * The caret is scrolled the same number of lines so that its location
|
|
3049 * relative to the top line remains the same. The exception is the end
|
|
3050 * of the text where a full page scroll is not possible. In this case
|
|
3051 * the caret is moved after the last character.
|
|
3052 * <p></p>
|
|
3053 * Adjusts the selection according to the caret change. This can either add
|
|
3054 * to or subtract from the old selection, depending on the previous selection
|
|
3055 * direction.
|
|
3056 * </p>
|
|
3057 */
|
|
3058 void doSelectionPageDown(int pixels) {
|
|
3059 int oldColumnX = columnX = getPointAtOffset(caretOffset).x;
|
|
3060 doPageDown(true, pixels);
|
|
3061 columnX = oldColumnX;
|
|
3062 }
|
|
3063 /**
|
|
3064 * Scrolls one page up so that the first line (truncated or whole)
|
|
3065 * of the current page becomes the fully visible last line.
|
|
3066 * <p>
|
|
3067 * The caret is scrolled the same number of lines so that its location
|
|
3068 * relative to the top line remains the same. The exception is the beginning
|
|
3069 * of the text where a full page scroll is not possible. In this case the
|
|
3070 * caret is moved in front of the first character.
|
|
3071 * </p><p>
|
|
3072 * Adjusts the selection according to the caret change. This can either add
|
|
3073 * to or subtract from the old selection, depending on the previous selection
|
|
3074 * direction.
|
|
3075 * </p>
|
|
3076 */
|
|
3077 void doSelectionPageUp(int pixels) {
|
|
3078 int oldColumnX = columnX = getPointAtOffset(caretOffset).x;
|
|
3079 doPageUp(true, pixels);
|
|
3080 columnX = oldColumnX;
|
|
3081 }
|
|
3082 /**
|
|
3083 * Moves the caret to the end of the next word .
|
|
3084 */
|
|
3085 void doSelectionWordNext() {
|
|
3086 int newCaretOffset = getWordNext(caretOffset, SWT.MOVEMENT_WORD);
|
|
3087 // Force symmetrical movement for word next and previous. Fixes 14536
|
|
3088 caretAlignment = OFFSET_LEADING;
|
|
3089 // don't change caret position if in single line mode and the cursor
|
|
3090 // would be on a different line. fixes 5673
|
|
3091 if (!isSingleLine() ||
|
|
3092 content.getLineAtOffset(caretOffset) is content.getLineAtOffset(newCaretOffset)) {
|
|
3093 caretOffset = newCaretOffset;
|
|
3094 showCaret();
|
|
3095 }
|
|
3096 }
|
|
3097 /**
|
|
3098 * Moves the caret to the start of the previous word.
|
|
3099 */
|
|
3100 void doSelectionWordPrevious() {
|
|
3101 caretAlignment = OFFSET_LEADING;
|
|
3102 caretOffset = getWordPrevious(caretOffset, SWT.MOVEMENT_WORD);
|
|
3103 int caretLine = content.getLineAtOffset(caretOffset);
|
|
3104 // word previous always comes from bottom line. when
|
|
3105 // wrapping lines, stay on bottom line when on line boundary
|
|
3106 if (wordWrap && caretLine < content.getLineCount() - 1 &&
|
|
3107 caretOffset is content.getOffsetAtLine(caretLine + 1)) {
|
|
3108 caretLine++;
|
|
3109 }
|
|
3110 showCaret();
|
|
3111 }
|
|
3112 /**
|
|
3113 * Moves the caret one character to the left. Do not go to the previous line.
|
|
3114 * When in a bidi locale and at a R2L character the caret is moved to the
|
|
3115 * beginning of the R2L segment (visually right) and then one character to the
|
|
3116 * left (visually left because it's now in a L2R segment).
|
|
3117 */
|
|
3118 void doVisualPrevious() {
|
|
3119 caretOffset = getClusterPrevious(caretOffset, getCaretLine());
|
|
3120 showCaret();
|
|
3121 }
|
|
3122 /**
|
|
3123 * Moves the caret one character to the right. Do not go to the next line.
|
|
3124 * When in a bidi locale and at a R2L character the caret is moved to the
|
|
3125 * end of the R2L segment (visually left) and then one character to the
|
|
3126 * right (visually right because it's now in a L2R segment).
|
|
3127 */
|
|
3128 void doVisualNext() {
|
|
3129 caretOffset = getClusterNext(caretOffset, getCaretLine());
|
|
3130 showCaret();
|
|
3131 }
|
|
3132 /**
|
|
3133 * Moves the caret to the end of the next word.
|
|
3134 * If a selection exists, move the caret to the end of the selection
|
|
3135 * and remove the selection.
|
|
3136 */
|
|
3137 void doWordNext() {
|
|
3138 if (selection.y - selection.x > 0) {
|
|
3139 caretOffset = selection.y;
|
|
3140 showCaret();
|
|
3141 } else {
|
|
3142 doSelectionWordNext();
|
|
3143 }
|
|
3144 }
|
|
3145 /**
|
|
3146 * Moves the caret to the start of the previous word.
|
|
3147 * If a selection exists, move the caret to the start of the selection
|
|
3148 * and remove the selection.
|
|
3149 */
|
|
3150 void doWordPrevious() {
|
|
3151 if (selection.y - selection.x > 0) {
|
|
3152 caretOffset = selection.x;
|
|
3153 showCaret();
|
|
3154 } else {
|
|
3155 doSelectionWordPrevious();
|
|
3156 }
|
|
3157 }
|
|
3158 /**
|
|
3159 * Ends the autoscroll process.
|
|
3160 */
|
|
3161 void endAutoScroll() {
|
|
3162 autoScrollDirection = SWT.NULL;
|
|
3163 }
|
|
3164 public override Color getBackground() {
|
|
3165 checkWidget();
|
|
3166 if (background is null) {
|
|
3167 return getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
|
|
3168 }
|
|
3169 return background;
|
|
3170 }
|
|
3171 /**
|
|
3172 * Returns the baseline, in pixels.
|
|
3173 *
|
|
3174 * Note: this API should not be used if a StyleRange attribute causes lines to
|
|
3175 * have different heights (i.e. different fonts, rise, etc).
|
|
3176 *
|
|
3177 * @return baseline the baseline
|
|
3178 * @exception SWTException <ul>
|
|
3179 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3180 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3181 * </ul>
|
|
3182 * @since 3.0
|
|
3183 *
|
|
3184 * @see #getBaseline(int)
|
|
3185 */
|
|
3186 public int getBaseline() {
|
|
3187 checkWidget();
|
|
3188 return renderer.getBaseline();
|
|
3189 }
|
|
3190 /**
|
|
3191 * Returns the baseline at the given offset, in pixels.
|
|
3192 *
|
|
3193 * @param offset the offset
|
|
3194 *
|
|
3195 * @return baseline the baseline
|
|
3196 *
|
|
3197 * @exception SWTException <ul>
|
|
3198 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3199 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3200 * </ul>
|
|
3201 * @exception IllegalArgumentException <ul>
|
|
3202 * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
|
|
3203 * </ul>
|
|
3204 *
|
|
3205 * @since 3.2
|
|
3206 */
|
|
3207 public int getBaseline(int offset) {
|
|
3208 checkWidget();
|
|
3209 if (!(0 <= offset && offset <= content.getCharCount())) {
|
|
3210 SWT.error(SWT.ERROR_INVALID_RANGE);
|
|
3211 }
|
|
3212 if (isFixedLineHeight()) {
|
|
3213 return renderer.getBaseline();
|
|
3214 }
|
|
3215 int lineIndex = content.getLineAtOffset(offset);
|
|
3216 int lineOffset = content.getOffsetAtLine(lineIndex);
|
|
3217 TextLayout layout = renderer.getTextLayout(lineIndex);
|
|
3218 int lineInParagraph = layout.getLineIndex(Math.min(offset - lineOffset, layout.getText().length));
|
|
3219 FontMetrics metrics = layout.getLineMetrics(lineInParagraph);
|
|
3220 renderer.disposeTextLayout(layout);
|
|
3221 return metrics.getAscent() + metrics.getLeading();
|
|
3222 }
|
|
3223 /**
|
|
3224 * Gets the BIDI coloring mode. When true the BIDI text display
|
|
3225 * algorithm is applied to segments of text that are the same
|
|
3226 * color.
|
|
3227 *
|
|
3228 * @return the current coloring mode
|
|
3229 * @exception SWTException <ul>
|
|
3230 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3231 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3232 * </ul>
|
|
3233 *
|
|
3234 * @deprecated use BidiSegmentListener instead.
|
|
3235 */
|
|
3236 public bool getBidiColoring() {
|
|
3237 checkWidget();
|
|
3238 return bidiColoring;
|
|
3239 }
|
|
3240 /**
|
|
3241 * Returns the index of the last fully visible line.
|
|
3242 *
|
|
3243 * @return index of the last fully visible line.
|
|
3244 */
|
|
3245 int getBottomIndex() {
|
|
3246 int bottomIndex;
|
|
3247 if (isFixedLineHeight()) {
|
|
3248 int lineCount = 1;
|
|
3249 int lineHeight = renderer.getLineHeight();
|
|
3250 if (lineHeight !is 0) {
|
|
3251 // calculate the number of lines that are fully visible
|
|
3252 int partialTopLineHeight = topIndex * lineHeight - getVerticalScrollOffset();
|
|
3253 lineCount = (clientAreaHeight - partialTopLineHeight) / lineHeight;
|
|
3254 }
|
|
3255 bottomIndex = Math.min(content.getLineCount() - 1, topIndex + Math.max(0, lineCount - 1));
|
|
3256 } else {
|
|
3257 int clientAreaHeight = this.clientAreaHeight - bottomMargin;
|
|
3258 bottomIndex = getLineIndex(clientAreaHeight);
|
|
3259 if (bottomIndex > 0) {
|
|
3260 int linePixel = getLinePixel(bottomIndex);
|
|
3261 int lineHeight = renderer.getLineHeight(bottomIndex);
|
|
3262 if (linePixel + lineHeight > clientAreaHeight) {
|
|
3263 if (getLinePixel(bottomIndex - 1) >= topMargin) {
|
|
3264 bottomIndex--;
|
|
3265 }
|
|
3266 }
|
|
3267 }
|
|
3268 }
|
|
3269 return bottomIndex;
|
|
3270 }
|
|
3271 Rectangle getBoundsAtOffset(int offset) {
|
|
3272 int lineIndex = content.getLineAtOffset(offset);
|
|
3273 int lineOffset = content.getOffsetAtLine(lineIndex);
|
|
3274 String line = content.getLine(lineIndex);
|
|
3275 Rectangle bounds;
|
|
3276 if (line.length !is 0) {
|
|
3277 int offsetInLine = offset - lineOffset;
|
|
3278 TextLayout layout = renderer.getTextLayout(lineIndex);
|
|
3279 bounds = layout.getBounds(offsetInLine, offsetInLine);
|
|
3280 renderer.disposeTextLayout(layout);
|
|
3281 } else {
|
|
3282 bounds = new Rectangle (0, 0, 0, renderer.getLineHeight());
|
|
3283 }
|
|
3284 if (offset is caretOffset) {
|
|
3285 int lineEnd = lineOffset + line.length;
|
|
3286 if (offset is lineEnd && caretAlignment is PREVIOUS_OFFSET_TRAILING) {
|
|
3287 bounds.width += getCaretWidth();
|
|
3288 }
|
|
3289 }
|
|
3290 bounds.x += leftMargin - horizontalScrollOffset;
|
|
3291 bounds.y += getLinePixel(lineIndex);
|
|
3292 return bounds;
|
|
3293 }
|
|
3294 /**
|
|
3295 * Returns the caret position relative to the start of the text.
|
|
3296 *
|
|
3297 * @return the caret position relative to the start of the text.
|
|
3298 * @exception SWTException <ul>
|
|
3299 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3300 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3301 * </ul>
|
|
3302 */
|
|
3303 public int getCaretOffset() {
|
|
3304 checkWidget();
|
|
3305 return caretOffset;
|
|
3306 }
|
|
3307 /**
|
|
3308 * Returns the caret width.
|
|
3309 *
|
|
3310 * @return the caret width, 0 if caret is null.
|
|
3311 */
|
|
3312 int getCaretWidth() {
|
|
3313 Caret caret = getCaret();
|
|
3314 if (caret is null) return 0;
|
|
3315 return caret.getSize().x;
|
|
3316 }
|
|
3317 Object getClipboardContent(int clipboardType) {
|
|
3318 TextTransfer plainTextTransfer = TextTransfer.getInstance();
|
|
3319 return clipboard.getContents(plainTextTransfer, clipboardType);
|
|
3320 }
|
|
3321 int getClusterNext(int offset, int lineIndex) {
|
|
3322 int lineOffset = content.getOffsetAtLine(lineIndex);
|
|
3323 TextLayout layout = renderer.getTextLayout(lineIndex);
|
|
3324 offset -= lineOffset;
|
|
3325 offset = layout.getNextOffset(offset, SWT.MOVEMENT_CLUSTER);
|
|
3326 offset += lineOffset;
|
|
3327 renderer.disposeTextLayout(layout);
|
|
3328 return offset;
|
|
3329 }
|
|
3330 int getClusterPrevious(int offset, int lineIndex) {
|
|
3331 int lineOffset = content.getOffsetAtLine(lineIndex);
|
|
3332 TextLayout layout = renderer.getTextLayout(lineIndex);
|
|
3333 offset -= lineOffset;
|
|
3334 offset = layout.getPreviousOffset(offset, SWT.MOVEMENT_CLUSTER);
|
|
3335 offset += lineOffset;
|
|
3336 renderer.disposeTextLayout(layout);
|
|
3337 return offset;
|
|
3338 }
|
|
3339 /**
|
|
3340 * Returns the content implementation that is used for text storage.
|
|
3341 *
|
|
3342 * @return content the user defined content implementation that is used for
|
|
3343 * text storage or the default content implementation if no user defined
|
|
3344 * content implementation has been set.
|
|
3345 * @exception SWTException <ul>
|
|
3346 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3347 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3348 * </ul>
|
|
3349 */
|
|
3350 public StyledTextContent getContent() {
|
|
3351 checkWidget();
|
|
3352 return content;
|
|
3353 }
|
|
3354 public override bool getDragDetect () {
|
|
3355 checkWidget ();
|
|
3356 return dragDetect_;
|
|
3357 }
|
|
3358 /**
|
|
3359 * Returns whether the widget implements double click mouse behavior.
|
|
3360 *
|
|
3361 * @return true if double clicking a word selects the word, false if double clicks
|
|
3362 * have the same effect as regular mouse clicks
|
|
3363 * @exception SWTException <ul>
|
|
3364 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3365 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3366 * </ul>
|
|
3367 */
|
|
3368 public bool getDoubleClickEnabled() {
|
|
3369 checkWidget();
|
|
3370 return doubleClickEnabled;
|
|
3371 }
|
|
3372 /**
|
|
3373 * Returns whether the widget content can be edited.
|
|
3374 *
|
|
3375 * @return true if content can be edited, false otherwise
|
|
3376 * @exception SWTException <ul>
|
|
3377 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3378 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3379 * </ul>
|
|
3380 */
|
|
3381 public bool getEditable() {
|
|
3382 checkWidget();
|
|
3383 return editable;
|
|
3384 }
|
|
3385 public override Color getForeground() {
|
|
3386 checkWidget();
|
|
3387 if (foreground is null) {
|
|
3388 return getDisplay().getSystemColor(SWT.COLOR_LIST_FOREGROUND);
|
|
3389 }
|
|
3390 return foreground;
|
|
3391 }
|
|
3392 /**
|
|
3393 * Returns the horizontal scroll increment.
|
|
3394 *
|
|
3395 * @return horizontal scroll increment.
|
|
3396 */
|
|
3397 int getHorizontalIncrement() {
|
|
3398 return renderer.averageCharWidth;
|
|
3399 }
|
|
3400 /**
|
|
3401 * Returns the horizontal scroll offset relative to the start of the line.
|
|
3402 *
|
|
3403 * @return horizontal scroll offset relative to the start of the line,
|
|
3404 * measured in character increments starting at 0, if > 0 the content is scrolled
|
|
3405 * @exception SWTException <ul>
|
|
3406 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3407 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3408 * </ul>
|
|
3409 */
|
|
3410 public int getHorizontalIndex() {
|
|
3411 checkWidget();
|
|
3412 return horizontalScrollOffset / getHorizontalIncrement();
|
|
3413 }
|
|
3414 /**
|
|
3415 * Returns the horizontal scroll offset relative to the start of the line.
|
|
3416 *
|
|
3417 * @return the horizontal scroll offset relative to the start of the line,
|
|
3418 * measured in pixel starting at 0, if > 0 the content is scrolled.
|
|
3419 * @exception SWTException <ul>
|
|
3420 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3421 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3422 * </ul>
|
|
3423 */
|
|
3424 public int getHorizontalPixel() {
|
|
3425 checkWidget();
|
|
3426 return horizontalScrollOffset;
|
|
3427 }
|
|
3428 /**
|
|
3429 * Returns the line indentation of the widget.
|
|
3430 *
|
|
3431 * @return the line indentation
|
|
3432 *
|
|
3433 * @exception SWTException <ul>
|
|
3434 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3435 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3436 * </ul>
|
|
3437 *
|
|
3438 * @see #getLineIndent(int)
|
|
3439 *
|
|
3440 * @since 3.2
|
|
3441 */
|
|
3442 public int getIndent() {
|
|
3443 checkWidget();
|
|
3444 return indent;
|
|
3445 }
|
|
3446 /**
|
|
3447 * Returns whether the widget justifies lines.
|
|
3448 *
|
|
3449 * @return whether lines are justified
|
|
3450 *
|
|
3451 * @exception SWTException <ul>
|
|
3452 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3453 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3454 * </ul>
|
|
3455 *
|
|
3456 * @see #getLineJustify(int)
|
|
3457 *
|
|
3458 * @since 3.2
|
|
3459 */
|
|
3460 public bool getJustify() {
|
|
3461 checkWidget();
|
|
3462 return justify;
|
|
3463 }
|
|
3464 /**
|
|
3465 * Returns the action assigned to the key.
|
|
3466 * Returns SWT.NULL if there is no action associated with the key.
|
|
3467 *
|
|
3468 * @param key a key code defined in SWT.java or a character.
|
|
3469 * Optionally ORd with a state mask. Preferred state masks are one or more of
|
|
3470 * SWT.MOD1, SWT.MOD2, SWT.MOD3, since these masks account for modifier platform
|
|
3471 * differences. However, there may be cases where using the specific state masks
|
|
3472 * (i.e., SWT.CTRL, SWT.SHIFT, SWT.ALT, SWT.COMMAND) makes sense.
|
|
3473 * @return one of the predefined actions defined in ST.java or SWT.NULL
|
|
3474 * if there is no action associated with the key.
|
|
3475 * @exception SWTException <ul>
|
|
3476 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3477 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3478 * </ul>
|
|
3479 */
|
|
3480 public int getKeyBinding(int key) {
|
|
3481 checkWidget();
|
|
3482 if( auto p = key in keyActionMap ){
|
|
3483 return *p;
|
|
3484 }
|
|
3485 return SWT.NULL;
|
|
3486 }
|
|
3487 /**
|
|
3488 * Gets the number of characters.
|
|
3489 *
|
|
3490 * @return number of characters in the widget
|
|
3491 * @exception SWTException <ul>
|
|
3492 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3493 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3494 * </ul>
|
|
3495 */
|
|
3496 public int getCharCount() {
|
|
3497 checkWidget();
|
|
3498 return content.getCharCount();
|
|
3499 }
|
|
3500 /**
|
|
3501 * Returns the line at the given line index without delimiters.
|
|
3502 * Index 0 is the first line of the content. When there are not
|
|
3503 * any lines, getLine(0) is a valid call that answers an empty string.
|
|
3504 * <p>
|
|
3505 *
|
|
3506 * @param lineIndex index of the line to return.
|
|
3507 * @return the line text without delimiters
|
|
3508 *
|
|
3509 * @exception SWTException <ul>
|
|
3510 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3511 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3512 * </ul>
|
|
3513 * @exception IllegalArgumentException <ul>
|
|
3514 * <li>ERROR_INVALID_RANGE when the line index is outside the valid range (< 0 or >= getLineCount())</li>
|
|
3515 * </ul>
|
|
3516 * @since 3.4
|
|
3517 */
|
|
3518 public String getLine(int lineIndex) {
|
|
3519 checkWidget();
|
|
3520 if (lineIndex < 0 ||
|
|
3521 (lineIndex > 0 && lineIndex >= content.getLineCount())) {
|
|
3522 SWT.error(SWT.ERROR_INVALID_RANGE);
|
|
3523 }
|
|
3524 return content.getLine(lineIndex);
|
|
3525 }
|
|
3526 /**
|
|
3527 * Returns the alignment of the line at the given index.
|
|
3528 *
|
|
3529 * @param index the index of the line
|
|
3530 *
|
|
3531 * @return the line alignment
|
|
3532 *
|
|
3533 * @exception SWTException <ul>
|
|
3534 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3535 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3536 * </ul>
|
|
3537 * @exception IllegalArgumentException <ul>
|
|
3538 * <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
|
|
3539 * </ul>
|
|
3540 *
|
|
3541 * @see #getAlignment()
|
|
3542 *
|
|
3543 * @since 3.2
|
|
3544 */
|
|
3545 public int getLineAlignment(int index) {
|
|
3546 checkWidget();
|
|
3547 if (index < 0 || index > content.getLineCount()) {
|
|
3548 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
|
|
3549 }
|
|
3550 return renderer.getLineAlignment(index, alignment);
|
|
3551 }
|
|
3552 /**
|
|
3553 * Returns the line at the specified offset in the text
|
|
3554 * where 0 < offset < getCharCount() so that getLineAtOffset(getCharCount())
|
|
3555 * returns the line of the insert location.
|
|
3556 *
|
|
3557 * @param offset offset relative to the start of the content.
|
|
3558 * 0 <= offset <= getCharCount()
|
|
3559 * @return line at the specified offset in the text
|
|
3560 * @exception SWTException <ul>
|
|
3561 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3562 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3563 * </ul>
|
|
3564 * @exception IllegalArgumentException <ul>
|
|
3565 * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
|
|
3566 * </ul>
|
|
3567 */
|
|
3568 public int getLineAtOffset(int offset) {
|
|
3569 checkWidget();
|
|
3570 if (offset < 0 || offset > getCharCount()) {
|
|
3571 SWT.error(SWT.ERROR_INVALID_RANGE);
|
|
3572 }
|
|
3573 return content.getLineAtOffset(offset);
|
|
3574 }
|
|
3575 /**
|
|
3576 * Returns the background color of the line at the given index.
|
|
3577 * Returns null if a LineBackgroundListener has been set or if no background
|
|
3578 * color has been specified for the line. Should not be called if a
|
|
3579 * LineBackgroundListener has been set since the listener maintains the
|
|
3580 * line background colors.
|
|
3581 *
|
|
3582 * @param index the index of the line
|
|
3583 * @return the background color of the line at the given index.
|
|
3584 *
|
|
3585 * @exception SWTException <ul>
|
|
3586 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3587 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3588 * </ul>
|
|
3589 * @exception IllegalArgumentException <ul>
|
|
3590 * <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
|
|
3591 * </ul>
|
|
3592 */
|
|
3593 public Color getLineBackground(int index) {
|
|
3594 checkWidget();
|
|
3595 if (index < 0 || index > content.getLineCount()) {
|
|
3596 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
|
|
3597 }
|
|
3598 return isListening(LineGetBackground) ? null : renderer.getLineBackground(index, null);
|
|
3599 }
|
|
3600 /**
|
|
3601 * Returns the bullet of the line at the given index.
|
|
3602 *
|
|
3603 * @param index the index of the line
|
|
3604 *
|
|
3605 * @return the line bullet
|
|
3606 *
|
|
3607 * @exception SWTException <ul>
|
|
3608 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3609 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3610 * </ul>
|
|
3611 * @exception IllegalArgumentException <ul>
|
|
3612 * <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
|
|
3613 * </ul>
|
|
3614 *
|
|
3615 * @since 3.2
|
|
3616 */
|
|
3617 public Bullet getLineBullet(int index) {
|
|
3618 checkWidget();
|
|
3619 if (index < 0 || index > content.getLineCount()) {
|
|
3620 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
|
|
3621 }
|
|
3622 return isListening(LineGetStyle) ? null : renderer.getLineBullet(index, null);
|
|
3623 }
|
|
3624 /**
|
|
3625 * Returns the line background data for the given line or null if
|
|
3626 * there is none.
|
|
3627 *
|
|
3628 * @param lineOffset offset of the line start relative to the start
|
|
3629 * of the content.
|
|
3630 * @param line line to get line background data for
|
|
3631 * @return line background data for the given line.
|
|
3632 */
|
|
3633 StyledTextEvent getLineBackgroundData(int lineOffset, String line) {
|
|
3634 return sendLineEvent(LineGetBackground, lineOffset, line);
|
|
3635 }
|
|
3636 /**
|
|
3637 * Gets the number of text lines.
|
|
3638 *
|
|
3639 * @return the number of lines in the widget
|
|
3640 * @exception SWTException <ul>
|
|
3641 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3642 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3643 * </ul>
|
|
3644 */
|
|
3645 public int getLineCount() {
|
|
3646 checkWidget();
|
|
3647 return content.getLineCount();
|
|
3648 }
|
|
3649 /**
|
|
3650 * Returns the number of lines that can be completely displayed in the
|
|
3651 * widget client area.
|
|
3652 *
|
|
3653 * @return number of lines that can be completely displayed in the widget
|
|
3654 * client area.
|
|
3655 */
|
|
3656 int getLineCountWhole() {
|
|
3657 if (isFixedLineHeight()) {
|
|
3658 int lineHeight = renderer.getLineHeight();
|
|
3659 return lineHeight !is 0 ? clientAreaHeight / lineHeight : 1;
|
|
3660 }
|
|
3661 return getBottomIndex() - topIndex + 1;
|
|
3662 }
|
|
3663 /**
|
|
3664 * Returns the line delimiter used for entering new lines by key down
|
|
3665 * or paste operation.
|
|
3666 *
|
|
3667 * @return line delimiter used for entering new lines by key down
|
|
3668 * or paste operation.
|
|
3669 * @exception SWTException <ul>
|
|
3670 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3671 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3672 * </ul>
|
|
3673 */
|
|
3674 public String getLineDelimiter() {
|
|
3675 checkWidget();
|
|
3676 return content.getLineDelimiter();
|
|
3677 }
|
|
3678 /**
|
|
3679 * Returns the line height.
|
|
3680 * <p>
|
|
3681 * Note: this API should not be used if a StyleRange attribute causes lines to
|
|
3682 * have different heights (i.e. different fonts, rise, etc).
|
|
3683 * </p>
|
|
3684 *
|
|
3685 * @return line height in pixel.
|
|
3686 * @exception SWTException <ul>
|
|
3687 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3688 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3689 * </ul>
|
|
3690 * @see #getLineHeight(int)
|
|
3691 */
|
|
3692 public int getLineHeight() {
|
|
3693 checkWidget();
|
|
3694 return renderer.getLineHeight();
|
|
3695 }
|
|
3696 /**
|
|
3697 * Returns the line height at the given offset.
|
|
3698 *
|
|
3699 * @param offset the offset
|
|
3700 *
|
|
3701 * @return line height in pixels
|
|
3702 *
|
|
3703 * @exception SWTException <ul>
|
|
3704 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3705 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3706 * </ul>
|
|
3707 * @exception IllegalArgumentException <ul>
|
|
3708 * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
|
|
3709 * </ul>
|
|
3710 *
|
|
3711 * @since 3.2
|
|
3712 */
|
|
3713 public int getLineHeight(int offset) {
|
|
3714 checkWidget();
|
|
3715 if (!(0 <= offset && offset <= content.getCharCount())) {
|
|
3716 SWT.error(SWT.ERROR_INVALID_RANGE);
|
|
3717 }
|
|
3718 if (isFixedLineHeight()) {
|
|
3719 return renderer.getLineHeight();
|
|
3720 }
|
|
3721 int lineIndex = content.getLineAtOffset(offset);
|
|
3722 int lineOffset = content.getOffsetAtLine(lineIndex);
|
|
3723 TextLayout layout = renderer.getTextLayout(lineIndex);
|
|
3724 int lineInParagraph = layout.getLineIndex(Math.min(offset - lineOffset, layout.getText().length));
|
|
3725 int height = layout.getLineBounds(lineInParagraph).height;
|
|
3726 renderer.disposeTextLayout(layout);
|
|
3727 return height;
|
|
3728 }
|
|
3729 /**
|
|
3730 * Returns the indentation of the line at the given index.
|
|
3731 *
|
|
3732 * @param index the index of the line
|
|
3733 *
|
|
3734 * @return the line indentation
|
|
3735 *
|
|
3736 * @exception SWTException <ul>
|
|
3737 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3738 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3739 * </ul>
|
|
3740 * @exception IllegalArgumentException <ul>
|
|
3741 * <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
|
|
3742 * </ul>
|
|
3743 *
|
|
3744 * @see #getIndent()
|
|
3745 *
|
|
3746 * @since 3.2
|
|
3747 */
|
|
3748 public int getLineIndent(int index) {
|
|
3749 checkWidget();
|
|
3750 if (index < 0 || index > content.getLineCount()) {
|
|
3751 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
|
|
3752 }
|
|
3753 return isListening(LineGetStyle) ? 0 : renderer.getLineIndent(index, indent);
|
|
3754 }
|
|
3755 /**
|
|
3756 * Returns whether the line at the given index is justified.
|
|
3757 *
|
|
3758 * @param index the index of the line
|
|
3759 *
|
|
3760 * @return whether the line is justified
|
|
3761 *
|
|
3762 * @exception SWTException <ul>
|
|
3763 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3764 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3765 * </ul>
|
|
3766 * @exception IllegalArgumentException <ul>
|
|
3767 * <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
|
|
3768 * </ul>
|
|
3769 *
|
|
3770 * @see #getJustify()
|
|
3771 *
|
|
3772 * @since 3.2
|
|
3773 */
|
|
3774 public bool getLineJustify(int index) {
|
|
3775 checkWidget();
|
|
3776 if (index < 0 || index > content.getLineCount()) {
|
|
3777 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
|
|
3778 }
|
|
3779 return isListening(LineGetStyle) ? false : renderer.getLineJustify(index, justify);
|
|
3780 }
|
|
3781 /**
|
|
3782 * Returns the line spacing of the widget.
|
|
3783 *
|
|
3784 * @return the line spacing
|
|
3785 *
|
|
3786 * @exception SWTException <ul>
|
|
3787 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3788 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3789 * </ul>
|
|
3790 *
|
|
3791 * @since 3.2
|
|
3792 */
|
|
3793 public int getLineSpacing() {
|
|
3794 checkWidget();
|
|
3795 return lineSpacing;
|
|
3796 }
|
|
3797 /**
|
|
3798 * Returns the line style data for the given line or null if there is
|
|
3799 * none.
|
|
3800 * <p>
|
|
3801 * If there is a LineStyleListener but it does not set any styles,
|
|
3802 * the StyledTextEvent.styles field will be initialized to an empty
|
|
3803 * array.
|
|
3804 * </p>
|
|
3805 *
|
|
3806 * @param lineOffset offset of the line start relative to the start of
|
|
3807 * the content.
|
|
3808 * @param line line to get line styles for
|
|
3809 * @return line style data for the given line. Styles may start before
|
|
3810 * line start and end after line end
|
|
3811 */
|
|
3812 StyledTextEvent getLineStyleData(int lineOffset, String line) {
|
|
3813 return sendLineEvent(LineGetStyle, lineOffset, line);
|
|
3814 }
|
|
3815 /**
|
|
3816 * Returns the top pixel, relative to the client area, of a given line.
|
|
3817 * Clamps out of ranges index.
|
|
3818 *
|
|
3819 * @param lineIndex the line index, the max value is lineCount. If
|
|
3820 * lineIndex is lineCount it returns the bottom pixel of the last line.
|
|
3821 * It means this function can be used to retrieve the bottom pixel of any line.
|
|
3822 *
|
|
3823 * @return the top pixel of a given line index
|
|
3824 *
|
|
3825 * @since 3.2
|
|
3826 */
|
|
3827 public int getLinePixel(int lineIndex) {
|
|
3828 checkWidget();
|
|
3829 int lineCount = content.getLineCount();
|
|
3830 lineIndex = Math.max(0, Math.min(lineCount, lineIndex));
|
|
3831 if (isFixedLineHeight()) {
|
|
3832 int lineHeight = renderer.getLineHeight();
|
|
3833 return lineIndex * lineHeight - getVerticalScrollOffset() + topMargin;
|
|
3834 }
|
|
3835 if (lineIndex is topIndex) return topIndexY + topMargin;
|
|
3836 int height = topIndexY;
|
|
3837 if (lineIndex > topIndex) {
|
|
3838 for (int i = topIndex; i < lineIndex; i++) {
|
|
3839 height += renderer.getLineHeight(i);
|
|
3840 }
|
|
3841 } else {
|
|
3842 for (int i = topIndex - 1; i >= lineIndex; i--) {
|
|
3843 height -= renderer.getLineHeight(i);
|
|
3844 }
|
|
3845 }
|
|
3846 return height + topMargin;
|
|
3847 }
|
|
3848 /**
|
|
3849 * Returns the line index for a y, relative to the client area.
|
|
3850 * The line index returned is always in the range 0..lineCount - 1.
|
|
3851 *
|
|
3852 * @param y the y-coordinate pixel
|
|
3853 *
|
|
3854 * @return the line index for a given y-coordinate pixel
|
|
3855 *
|
|
3856 * @since 3.2
|
|
3857 */
|
|
3858 public int getLineIndex(int y) {
|
|
3859 checkWidget();
|
|
3860 y -= topMargin;
|
|
3861 if (isFixedLineHeight()) {
|
|
3862 int lineHeight = renderer.getLineHeight();
|
|
3863 int lineIndex = (y + getVerticalScrollOffset()) / lineHeight;
|
|
3864 int lineCount = content.getLineCount();
|
|
3865 lineIndex = Math.max(0, Math.min(lineCount - 1, lineIndex));
|
|
3866 return lineIndex;
|
|
3867 }
|
|
3868 if (y is topIndexY) return topIndex;
|
|
3869 int line = topIndex;
|
|
3870 if (y < topIndexY) {
|
|
3871 while (y < topIndexY && line > 0) {
|
|
3872 y += renderer.getLineHeight(--line);
|
|
3873 }
|
|
3874 } else {
|
|
3875 int lineCount = content.getLineCount();
|
|
3876 int lineHeight = renderer.getLineHeight(line);
|
|
3877 while (y - lineHeight >= topIndexY && line < lineCount - 1) {
|
|
3878 y -= lineHeight;
|
|
3879 lineHeight = renderer.getLineHeight(++line);
|
|
3880 }
|
|
3881 }
|
|
3882 return line;
|
|
3883 }
|
|
3884 /**
|
|
3885 * Returns the x, y location of the upper left corner of the character
|
|
3886 * bounding box at the specified offset in the text. The point is
|
|
3887 * relative to the upper left corner of the widget client area.
|
|
3888 *
|
|
3889 * @param offset offset relative to the start of the content.
|
|
3890 * 0 <= offset <= getCharCount()
|
|
3891 * @return x, y location of the upper left corner of the character
|
|
3892 * bounding box at the specified offset in the text.
|
|
3893 * @exception SWTException <ul>
|
|
3894 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3895 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3896 * </ul>
|
|
3897 * @exception IllegalArgumentException <ul>
|
|
3898 * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
|
|
3899 * </ul>
|
|
3900 */
|
|
3901 public Point getLocationAtOffset(int offset) {
|
|
3902 checkWidget();
|
|
3903 if (offset < 0 || offset > getCharCount()) {
|
|
3904 SWT.error(SWT.ERROR_INVALID_RANGE);
|
|
3905 }
|
|
3906 return getPointAtOffset(offset);
|
|
3907 }
|
|
3908 /**
|
|
3909 * Returns the character offset of the first character of the given line.
|
|
3910 *
|
|
3911 * @param lineIndex index of the line, 0 based relative to the first
|
|
3912 * line in the content. 0 <= lineIndex < getLineCount(), except
|
|
3913 * lineIndex may always be 0
|
|
3914 * @return offset offset of the first character of the line, relative to
|
|
3915 * the beginning of the document. The first character of the document is
|
|
3916 * at offset 0.
|
|
3917 * When there are not any lines, getOffsetAtLine(0) is a valid call that
|
|
3918 * answers 0.
|
|
3919 * @exception SWTException <ul>
|
|
3920 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3921 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3922 * </ul>
|
|
3923 * @exception IllegalArgumentException <ul>
|
|
3924 * <li>ERROR_INVALID_RANGE when the line index is outside the valid range (< 0 or >= getLineCount())</li>
|
|
3925 * </ul>
|
|
3926 * @since 2.0
|
|
3927 */
|
|
3928 public int getOffsetAtLine(int lineIndex) {
|
|
3929 checkWidget();
|
|
3930 if (lineIndex < 0 ||
|
|
3931 (lineIndex > 0 && lineIndex >= content.getLineCount())) {
|
|
3932 SWT.error(SWT.ERROR_INVALID_RANGE);
|
|
3933 }
|
|
3934 return content.getOffsetAtLine(lineIndex);
|
|
3935 }
|
|
3936 /**
|
|
3937 * Returns the offset of the character at the given location relative
|
|
3938 * to the first character in the document.
|
|
3939 * <p>
|
|
3940 * The return value reflects the character offset that the caret will
|
|
3941 * be placed at if a mouse click occurred at the specified location.
|
|
3942 * If the x coordinate of the location is beyond the center of a character
|
|
3943 * the returned offset will be behind the character.
|
|
3944 * </p>
|
|
3945 *
|
|
3946 * @param point the origin of character bounding box relative to
|
|
3947 * the origin of the widget client area.
|
|
3948 * @return offset of the character at the given location relative
|
|
3949 * to the first character in the document.
|
|
3950 * @exception SWTException <ul>
|
|
3951 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
3952 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
3953 * </ul>
|
|
3954 * @exception IllegalArgumentException <ul>
|
|
3955 * <li>ERROR_NULL_ARGUMENT when point is null</li>
|
|
3956 * <li>ERROR_INVALID_ARGUMENT when there is no character at the specified location</li>
|
|
3957 * </ul>
|
|
3958 */
|
|
3959 public int getOffsetAtLocation(Point point) {
|
|
3960 checkWidget();
|
|
3961 if (point is null) {
|
|
3962 SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
3963 }
|
|
3964 int[] trailing = new int[1];
|
|
3965 int offset = getOffsetAtPoint(point.x, point.y, trailing, true);
|
|
3966 if (offset is -1) {
|
|
3967 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
|
|
3968 }
|
|
3969 return offset + trailing[0];
|
|
3970 }
|
|
3971 int getOffsetAtPoint(int x, int y) {
|
|
3972 int lineIndex = getLineIndex(y);
|
|
3973 y -= getLinePixel(lineIndex);
|
|
3974 return getOffsetAtPoint(x, y, lineIndex);
|
|
3975 }
|
|
3976 /**
|
|
3977 * Returns the offset at the specified x location in the specified line.
|
|
3978 *
|
|
3979 * @param x x location of the mouse location
|
|
3980 * @param line line the mouse location is in
|
|
3981 * @return the offset at the specified x location in the specified line,
|
|
3982 * relative to the beginning of the document
|
|
3983 */
|
|
3984 int getOffsetAtPoint(int x, int y, int lineIndex) {
|
|
3985 TextLayout layout = renderer.getTextLayout(lineIndex);
|
|
3986 x += horizontalScrollOffset - leftMargin;
|
|
3987 int[1] trailing;
|
|
3988 int offsetInLine = layout.getOffset(x, y, trailing);
|
|
3989 caretAlignment = OFFSET_LEADING;
|
|
3990 if (trailing[0] !is 0) {
|
|
3991 int lineInParagraph = layout.getLineIndex(offsetInLine + trailing[0]);
|
|
3992 int lineStart = layout.getLineOffsets()[lineInParagraph];
|
|
3993 if (offsetInLine + trailing[0] is lineStart) {
|
|
3994 offsetInLine += trailing[0];
|
|
3995 caretAlignment = PREVIOUS_OFFSET_TRAILING;
|
|
3996 } else {
|
|
3997 String line = content.getLine(lineIndex);
|
|
3998 int level;
|
|
3999 int offset = offsetInLine;
|
|
4000 while (offset > 0 && tango.text.Unicode.isDigit(line[offset])) offset--;
|
|
4001 if (offset is 0 && tango.text.Unicode.isDigit(line[offset])) {
|
|
4002 level = isMirrored() ? 1 : 0;
|
|
4003 } else {
|
|
4004 level = layout.getLevel(offset) & 0x1;
|
|
4005 }
|
|
4006 offsetInLine += trailing[0];
|
|
4007 int trailingLevel = layout.getLevel(offsetInLine) & 0x1;
|
|
4008 if ((level ^ trailingLevel) !is 0) {
|
|
4009 caretAlignment = PREVIOUS_OFFSET_TRAILING;
|
|
4010 } else {
|
|
4011 caretAlignment = OFFSET_LEADING;
|
|
4012 }
|
|
4013 }
|
|
4014 }
|
|
4015 renderer.disposeTextLayout(layout);
|
|
4016 return offsetInLine + content.getOffsetAtLine(lineIndex);
|
|
4017 }
|
|
4018 int getOffsetAtPoint(int x, int y, int[] trailing, bool inTextOnly) {
|
|
4019 if (inTextOnly && y + getVerticalScrollOffset() < 0 || x + horizontalScrollOffset < 0) {
|
|
4020 return -1;
|
|
4021 }
|
|
4022 int bottomIndex = getPartialBottomIndex();
|
|
4023 int height = getLinePixel(bottomIndex + 1);
|
|
4024 if (inTextOnly && y > height) {
|
|
4025 return -1;
|
|
4026 }
|
|
4027 int lineIndex = getLineIndex(y);
|
|
4028 int lineOffset = content.getOffsetAtLine(lineIndex);
|
|
4029 TextLayout layout = renderer.getTextLayout(lineIndex);
|
|
4030 x += horizontalScrollOffset - leftMargin ;
|
|
4031 y -= getLinePixel(lineIndex);
|
|
4032 int offset = layout.getOffset(x, y, trailing);
|
|
4033 Rectangle rect = layout.getLineBounds(layout.getLineIndex(offset));
|
|
4034 renderer.disposeTextLayout(layout);
|
|
4035 if (inTextOnly && !(rect.x <= x && x <= rect.x + rect.width)) {
|
|
4036 return -1;
|
|
4037 }
|
|
4038 return offset + lineOffset;
|
|
4039 }
|
|
4040 /**
|
|
4041 * Returns the orientation of the receiver.
|
|
4042 *
|
|
4043 * @return the orientation style
|
|
4044 *
|
|
4045 * @exception SWTException <ul>
|
|
4046 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
4047 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
4048 * </ul>
|
|
4049 *
|
|
4050 * @since 2.1.2
|
|
4051 */
|
|
4052 public int getOrientation () {
|
|
4053 checkWidget();
|
|
4054 return isMirrored() ? SWT.RIGHT_TO_LEFT : SWT.LEFT_TO_RIGHT;
|
|
4055 }
|
|
4056 /**
|
|
4057 * Returns the index of the last partially visible line.
|
|
4058 *
|
|
4059 * @return index of the last partially visible line.
|
|
4060 */
|
|
4061 int getPartialBottomIndex() {
|
|
4062 if (isFixedLineHeight()) {
|
|
4063 int lineHeight = renderer.getLineHeight();
|
|
4064 int partialLineCount = Compatibility.ceil(clientAreaHeight, lineHeight);
|
|
4065 return Math.max(0, Math.min(content.getLineCount(), topIndex + partialLineCount) - 1);
|
|
4066 }
|
|
4067 return getLineIndex(clientAreaHeight - bottomMargin);
|
|
4068 }
|
|
4069 /**
|
|
4070 * Returns the index of the first partially visible line.
|
|
4071 *
|
|
4072 * @return index of the first partially visible line.
|
|
4073 */
|
|
4074 int getPartialTopIndex() {
|
|
4075 if (isFixedLineHeight()) {
|
|
4076 int lineHeight = renderer.getLineHeight();
|
|
4077 return getVerticalScrollOffset() / lineHeight;
|
|
4078 }
|
|
4079 return topIndexY <= 0 ? topIndex : topIndex - 1;
|
|
4080 }
|
|
4081 /**
|
|
4082 * Returns the content in the specified range using the platform line
|
|
4083 * delimiter to separate lines.
|
|
4084 *
|
|
4085 * @param writer the TextWriter to write line text into
|
|
4086 * @return the content in the specified range using the platform line
|
|
4087 * delimiter to separate lines as written by the specified TextWriter.
|
|
4088 */
|
|
4089 String getPlatformDelimitedText(TextWriter writer) {
|
|
4090 int end = writer.getStart() + writer.getCharCount();
|
|
4091 int startLine = content.getLineAtOffset(writer.getStart());
|
|
4092 int endLine = content.getLineAtOffset(end);
|
|
4093 String endLineText = content.getLine(endLine);
|
|
4094 int endLineOffset = content.getOffsetAtLine(endLine);
|
|
4095
|
|
4096 for (int i = startLine; i <= endLine; i++) {
|
|
4097 writer.writeLine(content.getLine(i), content.getOffsetAtLine(i));
|
|
4098 if (i < endLine) {
|
|
4099 writer.writeLineDelimiter(PlatformLineDelimiter);
|
|
4100 }
|
|
4101 }
|
|
4102 if (end > endLineOffset + endLineText.length) {
|
|
4103 writer.writeLineDelimiter(PlatformLineDelimiter);
|
|
4104 }
|
|
4105 writer.close();
|
|
4106 return writer.toString();
|
|
4107 }
|
|
4108 /**
|
|
4109 * Returns all the ranges of text that have an associated StyleRange.
|
|
4110 * Returns an empty array if a LineStyleListener has been set.
|
|
4111 * Should not be called if a LineStyleListener has been set since the
|
|
4112 * listener maintains the styles.
|
|
4113 * <p>
|
|
4114 * The ranges array contains start and length pairs. Each pair refers to
|
|
4115 * the corresponding style in the styles array. For example, the pair
|
|
4116 * that starts at ranges[n] with length ranges[n+1] uses the style
|
|
4117 * at styles[n/2] returned by <code>getStyleRanges(int, int, bool)</code>.
|
|
4118 * </p>
|
|
4119 *
|
|
4120 * @return the ranges or an empty array if a LineStyleListener has been set.
|
|
4121 *
|
|
4122 * @exception SWTException <ul>
|
|
4123 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
4124 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
4125 * </ul>
|
|
4126 *
|
|
4127 * @since 3.2
|
|
4128 *
|
|
4129 * @see #getStyleRanges(bool)
|
|
4130 */
|
|
4131 public int[] getRanges() {
|
|
4132 checkWidget();
|
|
4133 if (!isListening(LineGetStyle)) {
|
|
4134 int[] ranges = renderer.getRanges(0, content.getCharCount());
|
|
4135 if (ranges !is null) return ranges;
|
|
4136 }
|
|
4137 return new int[0];
|
|
4138 }
|
|
4139 /**
|
|
4140 * Returns the ranges of text that have an associated StyleRange.
|
|
4141 * Returns an empty array if a LineStyleListener has been set.
|
|
4142 * Should not be called if a LineStyleListener has been set since the
|
|
4143 * listener maintains the styles.
|
|
4144 * <p>
|
|
4145 * The ranges array contains start and length pairs. Each pair refers to
|
|
4146 * the corresponding style in the styles array. For example, the pair
|
|
4147 * that starts at ranges[n] with length ranges[n+1] uses the style
|
|
4148 * at styles[n/2] returned by <code>getStyleRanges(int, int, bool)</code>.
|
|
4149 * </p>
|
|
4150 *
|
|
4151 * @param start the start offset of the style ranges to return
|
|
4152 * @param length the number of style ranges to return
|
|
4153 *
|
|
4154 * @return the ranges or an empty array if a LineStyleListener has been set.
|
|
4155 *
|
|
4156 * @exception SWTException <ul>
|
|
4157 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
4158 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
4159 * </ul>
|
|
4160 * @exception IllegalArgumentException <ul>
|
|
4161 * <li>ERROR_INVALID_RANGE if start or length are outside the widget content</li>
|
|
4162 * </ul>
|
|
4163 *
|
|
4164 * @since 3.2
|
|
4165 *
|
|
4166 * @see #getStyleRanges(int, int, bool)
|
|
4167 */
|
|
4168 public int[] getRanges(int start, int length) {
|
|
4169 checkWidget();
|
|
4170 int contentLength = getCharCount();
|
|
4171 int end = start + length;
|
|
4172 if (start > end || start < 0 || end > contentLength) {
|
|
4173 SWT.error(SWT.ERROR_INVALID_RANGE);
|
|
4174 }
|
|
4175 if (!isListening(LineGetStyle)) {
|
|
4176 int[] ranges = renderer.getRanges(start, length);
|
|
4177 if (ranges !is null) return ranges;
|
|
4178 }
|
|
4179 return new int[0];
|
|
4180 }
|
|
4181 /**
|
|
4182 * Returns the selection.
|
|
4183 * <p>
|
|
4184 * Text selections are specified in terms of caret positions. In a text
|
|
4185 * widget that contains N characters, there are N+1 caret positions,
|
|
4186 * ranging from 0..N
|
|
4187 * </p>
|
|
4188 *
|
|
4189 * @return start and end of the selection, x is the offset of the first
|
|
4190 * selected character, y is the offset after the last selected character.
|
|
4191 * The selection values returned are visual (i.e., x will always always be
|
|
4192 * <= y). To determine if a selection is right-to-left (RtoL) vs. left-to-right
|
|
4193 * (LtoR), compare the caretOffset to the start and end of the selection
|
|
4194 * (e.g., caretOffset is start of selection implies that the selection is RtoL).
|
|
4195 * @see #getSelectionRange
|
|
4196 * @exception SWTException <ul>
|
|
4197 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
4198 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
4199 * </ul>
|
|
4200 */
|
|
4201 public Point getSelection() {
|
|
4202 checkWidget();
|
|
4203 return new Point(selection.x, selection.y);
|
|
4204 }
|
|
4205 /**
|
|
4206 * Returns the selection.
|
|
4207 *
|
|
4208 * @return start and length of the selection, x is the offset of the
|
|
4209 * first selected character, relative to the first character of the
|
|
4210 * widget content. y is the length of the selection.
|
|
4211 * The selection values returned are visual (i.e., length will always always be
|
|
4212 * positive). To determine if a selection is right-to-left (RtoL) vs. left-to-right
|
|
4213 * (LtoR), compare the caretOffset to the start and end of the selection
|
|
4214 * (e.g., caretOffset is start of selection implies that the selection is RtoL).
|
|
4215 * @exception SWTException <ul>
|
|
4216 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
4217 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
4218 * </ul>
|
|
4219 */
|
|
4220 public Point getSelectionRange() {
|
|
4221 checkWidget();
|
|
4222 return new Point(selection.x, selection.y - selection.x);
|
|
4223 }
|
|
4224 /**
|
|
4225 * Returns the receiver's selection background color.
|
|
4226 *
|
|
4227 * @return the selection background color
|
|
4228 *
|
|
4229 * @exception SWTException <ul>
|
|
4230 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
4231 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
4232 * </ul>
|
|
4233 * @since 2.1
|
|
4234 */
|
|
4235 public Color getSelectionBackground() {
|
|
4236 checkWidget();
|
|
4237 if (selectionBackground is null) {
|
|
4238 return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION);
|
|
4239 }
|
|
4240 return selectionBackground;
|
|
4241 }
|
|
4242 /**
|
|
4243 * Gets the number of selected characters.
|
|
4244 *
|
|
4245 * @return the number of selected characters.
|
|
4246 * @exception SWTException <ul>
|
|
4247 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
4248 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
4249 * </ul>
|
|
4250 */
|
|
4251 public int getSelectionCount() {
|
|
4252 checkWidget();
|
|
4253 return getSelectionRange().y;
|
|
4254 }
|
|
4255 /**
|
|
4256 * Returns the receiver's selection foreground color.
|
|
4257 *
|
|
4258 * @return the selection foreground color
|
|
4259 *
|
|
4260 * @exception SWTException <ul>
|
|
4261 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
4262 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
4263 * </ul>
|
|
4264 * @since 2.1
|
|
4265 */
|
|
4266 public Color getSelectionForeground() {
|
|
4267 checkWidget();
|
|
4268 if (selectionForeground is null) {
|
|
4269 return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT);
|
|
4270 }
|
|
4271 return selectionForeground;
|
|
4272 }
|
|
4273 /**
|
|
4274 * Returns the selected text.
|
|
4275 *
|
|
4276 * @return selected text, or an empty String if there is no selection.
|
|
4277 * @exception SWTException <ul>
|
|
4278 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
4279 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
4280 * </ul>
|
|
4281 */
|
|
4282 public String getSelectionText() {
|
|
4283 checkWidget();
|
|
4284 return content.getTextRange(selection.x, selection.y - selection.x);
|
|
4285 }
|
|
4286 public override int getStyle() {
|
|
4287 int style = super.getStyle();
|
|
4288 style &= ~(SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT | SWT.MIRRORED);
|
|
4289 if (isMirrored()) {
|
|
4290 style |= SWT.RIGHT_TO_LEFT | SWT.MIRRORED;
|
|
4291 } else {
|
|
4292 style |= SWT.LEFT_TO_RIGHT;
|
|
4293 }
|
|
4294 return style;
|
|
4295 }
|
|
4296
|
|
4297 /**
|
|
4298 * Returns the text segments that should be treated as if they
|
|
4299 * had a different direction than the surrounding text.
|
|
4300 *
|
|
4301 * @param lineOffset offset of the first character in the line.
|
|
4302 * 0 based from the beginning of the document.
|
|
4303 * @param line text of the line to specify bidi segments for
|
|
4304 * @return text segments that should be treated as if they had a
|
|
4305 * different direction than the surrounding text. Only the start
|
|
4306 * index of a segment is specified, relative to the start of the
|
|
4307 * line. Always starts with 0 and ends with the line length.
|
|
4308 * @exception IllegalArgumentException <ul>
|
|
4309 * <li>ERROR_INVALID_ARGUMENT - if the segment indices returned
|
|
4310 * by the listener do not start with 0, are not in ascending order,
|
|
4311 * exceed the line length or have duplicates</li>
|
|
4312 * </ul>
|
|
4313 */
|
|
4314 int [] getBidiSegments(int lineOffset, String line) {
|
|
4315 if (!isBidi()) return null;
|
|
4316 if (!isListening(LineGetSegments)) {
|
|
4317 return getBidiSegmentsCompatibility(line, lineOffset);
|
|
4318 }
|
|
4319 StyledTextEvent event = sendLineEvent(LineGetSegments, lineOffset, line);
|
|
4320 int lineLength = line.length;
|
|
4321 int[] segments;
|
|
4322 if (event is null || event.segments is null || event.segments.length is 0) {
|
|
4323 segments = [0, lineLength];
|
|
4324 } else {
|
|
4325 int segmentCount = event.segments.length;
|
|
4326
|
|
4327 // test segment index consistency
|
|
4328 if (event.segments[0] !is 0) {
|
|
4329 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
|
|
4330 }
|
|
4331 for (int i = 1; i < segmentCount; i++) {
|
|
4332 if (event.segments[i] <= event.segments[i - 1] || event.segments[i] > lineLength) {
|
|
4333 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
|
|
4334 }
|
|
4335 }
|
|
4336 // ensure that last segment index is line end offset
|
|
4337 if (event.segments[segmentCount - 1] !is lineLength) {
|
|
4338 segments = new int[segmentCount + 1];
|
|
4339 System.arraycopy(event.segments, 0, segments, 0, segmentCount);
|
|
4340 segments[segmentCount] = lineLength;
|
|
4341 } else {
|
|
4342 segments = event.segments;
|
|
4343 }
|
|
4344 }
|
|
4345 return segments;
|
|
4346 }
|
|
4347 /**
|
|
4348 * @see #getBidiSegments
|
|
4349 * Supports deprecated setBidiColoring API. Remove when API is removed.
|
|
4350 */
|
|
4351 int [] getBidiSegmentsCompatibility(String line, int lineOffset) {
|
|
4352 int lineLength = line.length;
|
|
4353 if (!bidiColoring) {
|
|
4354 return [0, lineLength];
|
|
4355 }
|
|
4356 StyleRange [] styles = null;
|
|
4357 StyledTextEvent event = getLineStyleData(lineOffset, line);
|
|
4358 if (event !is null) {
|
|
4359 styles = event.styles;
|
|
4360 } else {
|
|
4361 styles = renderer.getStyleRanges(lineOffset, lineLength, true);
|
|
4362 }
|
|
4363 if (styles is null || styles.length is 0) {
|
|
4364 return [0, lineLength];
|
|
4365 }
|
|
4366 int k=0, count = 1;
|
|
4367 while (k < styles.length && styles[k].start is 0 && styles[k].length is lineLength) {
|
|
4368 k++;
|
|
4369 }
|
|
4370 int[] offsets = new int[(styles.length - k) * 2 + 2];
|
|
4371 for (int i = k; i < styles.length; i++) {
|
|
4372 StyleRange style = styles[i];
|
|
4373 int styleLineStart = Math.max(style.start - lineOffset, 0);
|
|
4374 int styleLineEnd = Math.max(style.start + style.length - lineOffset, styleLineStart);
|
|
4375 styleLineEnd = Math.min (styleLineEnd, line.length );
|
|
4376 if (i > 0 && count > 1 &&
|
|
4377 ((styleLineStart >= offsets[count-2] && styleLineStart <= offsets[count-1]) ||
|
|
4378 (styleLineEnd >= offsets[count-2] && styleLineEnd <= offsets[count-1])) &&
|
|
4379 style.similarTo(styles[i-1])) {
|
|
4380 offsets[count-2] = Math.min(offsets[count-2], styleLineStart);
|
|
4381 offsets[count-1] = Math.max(offsets[count-1], styleLineEnd);
|
|
4382 } else {
|
|
4383 if (styleLineStart > offsets[count - 1]) {
|
|
4384 offsets[count] = styleLineStart;
|
|
4385 count++;
|
|
4386 }
|
|
4387 offsets[count] = styleLineEnd;
|
|
4388 count++;
|
|
4389 }
|
|
4390 }
|
|
4391 // add offset for last non-colored segment in line, if any
|
|
4392 if (lineLength > offsets[count-1]) {
|
|
4393 offsets [count] = lineLength;
|
|
4394 count++;
|
|
4395 }
|
|
4396 if (count is offsets.length) {
|
|
4397 return offsets;
|
|
4398 }
|
|
4399 int [] result = new int [count];
|
|
4400 System.arraycopy (offsets, 0, result, 0, count);
|
|
4401 return result;
|
|
4402 }
|
|
4403 /**
|
|
4404 * Returns the style range at the given offset.
|
|
4405 * <p>
|
|
4406 * Returns null if a LineStyleListener has been set or if a style is not set
|
|
4407 * for the offset.
|
|
4408 * Should not be called if a LineStyleListener has been set since the
|
|
4409 * listener maintains the styles.
|
|
4410 * </p>
|
|
4411 *
|
|
4412 * @param offset the offset to return the style for.
|
|
4413 * 0 <= offset < getCharCount() must be true.
|
|
4414 * @return a StyleRange with start is offset and length is 1, indicating
|
|
4415 * the style at the given offset. null if a LineStyleListener has been set
|
|
4416 * or if a style is not set for the given offset.
|
|
4417 * @exception SWTException <ul>
|
|
4418 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
4419 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
4420 * </ul>
|
|
4421 * @exception IllegalArgumentException <ul>
|
|
4422 * <li>ERROR_INVALID_ARGUMENT when the offset is invalid</li>
|
|
4423 * </ul>
|
|
4424 */
|
|
4425 public StyleRange getStyleRangeAtOffset(int offset) {
|
|
4426 checkWidget();
|
|
4427 if (offset < 0 || offset >= getCharCount()) {
|
|
4428 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
|
|
4429 }
|
|
4430 if (!isListening(LineGetStyle)) {
|
|
4431 StyleRange[] ranges = renderer.getStyleRanges(offset, 1, true);
|
|
4432 if (ranges !is null) return ranges[0];
|
|
4433 }
|
|
4434 return null;
|
|
4435 }
|
|
4436 /**
|
|
4437 * Returns the styles.
|
|
4438 * <p>
|
|
4439 * Returns an empty array if a LineStyleListener has been set.
|
|
4440 * Should not be called if a LineStyleListener has been set since the
|
|
4441 * listener maintains the styles.
|
|
4442 * <p></p>
|
|
4443 * Note: Because a StyleRange includes the start and length, the
|
|
4444 * same instance cannot occur multiple times in the array of styles.
|
|
4445 * If the same style attributes, such as font and color, occur in
|
|
4446 * multiple StyleRanges, <code>getStyleRanges(bool)</code>
|
|
4447 * can be used to get the styles without the ranges.
|
|
4448 * </p>
|
|
4449 *
|
|
4450 * @return the styles or an empty array if a LineStyleListener has been set.
|
|
4451 *
|
|
4452 * @exception SWTException <ul>
|
|
4453 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
4454 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
4455 * </ul>
|
|
4456 *
|
|
4457 * @see #getStyleRanges(bool)
|
|
4458 */
|
|
4459 public StyleRange[] getStyleRanges() {
|
|
4460 checkWidget();
|
|
4461 return getStyleRanges(0, content.getCharCount(), true);
|
|
4462 }
|
|
4463 /**
|
|
4464 * Returns the styles.
|
|
4465 * <p>
|
|
4466 * Returns an empty array if a LineStyleListener has been set.
|
|
4467 * Should not be called if a LineStyleListener has been set since the
|
|
4468 * listener maintains the styles.
|
|
4469 * </p><p>
|
|
4470 * Note: When <code>includeRanges</code> is true, the start and length
|
|
4471 * fields of each StyleRange will be valid, however the StyleRange
|
|
4472 * objects may need to be cloned. When <code>includeRanges</code> is
|
|
4473 * false, <code>getRanges(int, int)</code> can be used to get the
|
|
4474 * associated ranges.
|
|
4475 * </p>
|
|
4476 *
|
|
4477 * @param includeRanges whether the start and length field of the StyleRanges should be set.
|
|
4478 *
|
|
4479 * @return the styles or an empty array if a LineStyleListener has been set.
|
|
4480 *
|
|
4481 * @exception SWTException <ul>
|
|
4482 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
4483 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
4484 * </ul>
|
|
4485 *
|
|
4486 * @since 3.2
|
|
4487 *
|
|
4488 * @see #getRanges(int, int)
|
|
4489 * @see #setStyleRanges(int[], StyleRange[])
|
|
4490 */
|
|
4491 public StyleRange[] getStyleRanges(bool includeRanges) {
|
|
4492 checkWidget();
|
|
4493 return getStyleRanges(0, content.getCharCount(), includeRanges);
|
|
4494 }
|
|
4495 /**
|
|
4496 * Returns the styles for the given text range.
|
|
4497 * <p>
|
|
4498 * Returns an empty array if a LineStyleListener has been set.
|
|
4499 * Should not be called if a LineStyleListener has been set since the
|
|
4500 * listener maintains the styles.
|
|
4501 * </p><p>
|
|
4502 * Note: Because the StyleRange includes the start and length, the
|
|
4503 * same instance cannot occur multiple times in the array of styles.
|
|
4504 * If the same style attributes, such as font and color, occur in
|
|
4505 * multiple StyleRanges, <code>getStyleRanges(int, int, bool)</code>
|
|
4506 * can be used to get the styles without the ranges.
|
|
4507 * </p>
|
|
4508 * @param start the start offset of the style ranges to return
|
|
4509 * @param length the number of style ranges to return
|
|
4510 *
|
|
4511 * @return the styles or an empty array if a LineStyleListener has
|
|
4512 * been set. The returned styles will reflect the given range. The first
|
|
4513 * returned <code>StyleRange</code> will have a starting offset >= start
|
|
4514 * and the last returned <code>StyleRange</code> will have an ending
|
|
4515 * offset <= start + length - 1
|
|
4516 *
|
|
4517 * @exception SWTException <ul>
|
|
4518 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
4519 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
4520 * </ul>
|
|
4521 * @exception IllegalArgumentException <ul>
|
|
4522 * <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
|
|
4523 * </ul>
|
|
4524 *
|
|
4525 * @see #getStyleRanges(int, int, bool)
|
|
4526 *
|
|
4527 * @since 3.0
|
|
4528 */
|
|
4529 public StyleRange[] getStyleRanges(int start, int length) {
|
|
4530 checkWidget();
|
|
4531 return getStyleRanges(start, length, true);
|
|
4532 }
|
|
4533 /**
|
|
4534 * Returns the styles for the given text range.
|
|
4535 * <p>
|
|
4536 * Returns an empty array if a LineStyleListener has been set.
|
|
4537 * Should not be called if a LineStyleListener has been set since the
|
|
4538 * listener maintains the styles.
|
|
4539 * </p><p>
|
|
4540 * Note: When <code>includeRanges</code> is true, the start and length
|
|
4541 * fields of each StyleRange will be valid, however the StyleRange
|
|
4542 * objects may need to be cloned. When <code>includeRanges</code> is
|
|
4543 * false, <code>getRanges(int, int)</code> can be used to get the
|
|
4544 * associated ranges.
|
|
4545 * </p>
|
|
4546 *
|
|
4547 * @param start the start offset of the style ranges to return
|
|
4548 * @param length the number of style ranges to return
|
|
4549 * @param includeRanges whether the start and length field of the StyleRanges should be set.
|
|
4550 *
|
|
4551 * @return the styles or an empty array if a LineStyleListener has
|
|
4552 * been set. The returned styles will reflect the given range. The first
|
|
4553 * returned <code>StyleRange</code> will have a starting offset >= start
|
|
4554 * and the last returned <code>StyleRange</code> will have an ending
|
|
4555 * offset <= start + length - 1
|
|
4556 *
|
|
4557 * @exception SWTException <ul>
|
|
4558 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
4559 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
4560 * </ul>
|
|
4561 * @exception IllegalArgumentException <ul>
|
|
4562 * <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
|
|
4563 * </ul>
|
|
4564 *
|
|
4565 * @since 3.2
|
|
4566 *
|
|
4567 * @see #getRanges(int, int)
|
|
4568 * @see #setStyleRanges(int[], StyleRange[])
|
|
4569 */
|
|
4570 public StyleRange[] getStyleRanges(int start, int length, bool includeRanges) {
|
|
4571 checkWidget();
|
|
4572 int contentLength = getCharCount();
|
|
4573 int end = start + length;
|
|
4574 if (start > end || start < 0 || end > contentLength) {
|
|
4575 SWT.error(SWT.ERROR_INVALID_RANGE);
|
|
4576 }
|
|
4577 if (!isListening(LineGetStyle)) {
|
|
4578 StyleRange[] ranges = renderer.getStyleRanges(start, length, includeRanges);
|
|
4579 if (ranges !is null) return ranges;
|
|
4580 }
|
|
4581 return new StyleRange[0];
|
|
4582 }
|
|
4583 /**
|
|
4584 * Returns the tab width measured in characters.
|
|
4585 *
|
|
4586 * @return tab width measured in characters
|
|
4587 * @exception SWTException <ul>
|
|
4588 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
4589 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
4590 * </ul>
|
|
4591 */
|
|
4592 public int getTabs() {
|
|
4593 checkWidget();
|
|
4594 return tabLength;
|
|
4595 }
|
|
4596 /**
|
|
4597 * Returns a copy of the widget content.
|
|
4598 *
|
|
4599 * @return copy of the widget content
|
|
4600 * @exception SWTException <ul>
|
|
4601 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
4602 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
4603 * </ul>
|
|
4604 */
|
|
4605 public String getText() {
|
|
4606 checkWidget();
|
|
4607 return content.getTextRange(0, getCharCount());
|
|
4608 }
|
|
4609 /**
|
|
4610 * Returns the widget content between the two offsets.
|
|
4611 *
|
|
4612 * @param start offset of the first character in the returned String
|
|
4613 * @param end offset of the last character in the returned String
|
|
4614 * @return widget content starting at start and ending at end
|
|
4615 * @see #getTextRange(int,int)
|
|
4616 * @exception SWTException <ul>
|
|
4617 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
4618 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
4619 * </ul>
|
|
4620 * @exception IllegalArgumentException <ul>
|
|
4621 * <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
|
|
4622 * </ul>
|
|
4623 */
|
|
4624 public String getText(int start, int end) {
|
|
4625 checkWidget();
|
|
4626 int contentLength = getCharCount();
|
|
4627 if (start < 0 || start >= contentLength || end < 0 || end >= contentLength || start > end) {
|
|
4628 SWT.error(SWT.ERROR_INVALID_RANGE);
|
|
4629 }
|
|
4630 return content.getTextRange(start, end - start + 1);
|
|
4631 }
|
|
4632 /**
|
|
4633 * Returns the smallest bounding rectangle that includes the characters between two offsets.
|
|
4634 *
|
|
4635 * @param start offset of the first character included in the bounding box
|
|
4636 * @param end offset of the last character included in the bounding box
|
|
4637 * @return bounding box of the text between start and end
|
|
4638 * @exception SWTException <ul>
|
|
4639 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
4640 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
4641 * </ul>
|
|
4642 * @exception IllegalArgumentException <ul>
|
|
4643 * <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
|
|
4644 * </ul>
|
|
4645 * @since 3.1
|
|
4646 */
|
|
4647 public Rectangle getTextBounds(int start, int end) {
|
|
4648 checkWidget();
|
|
4649 int contentLength = getCharCount();
|
|
4650 if (start < 0 || start >= contentLength || end < 0 || end >= contentLength || start > end) {
|
|
4651 SWT.error(SWT.ERROR_INVALID_RANGE);
|
|
4652 }
|
|
4653 int lineStart = content.getLineAtOffset(start);
|
|
4654 int lineEnd = content.getLineAtOffset(end);
|
|
4655 Rectangle rect;
|
|
4656 int y = getLinePixel(lineStart);
|
|
4657 int height = 0;
|
|
4658 int left = 0x7fffffff, right = 0;
|
|
4659 for (int i = lineStart; i <= lineEnd; i++) {
|
|
4660 int lineOffset = content.getOffsetAtLine(i);
|
|
4661 TextLayout layout = renderer.getTextLayout(i);
|
|
4662 int length = layout.getText().length;
|
|
4663 if (length > 0) {
|
|
4664 if (i is lineStart) {
|
|
4665 if (i is lineEnd) {
|
|
4666 rect = layout.getBounds(start - lineOffset, end - lineOffset);
|
|
4667 } else {
|
|
4668 rect = layout.getBounds(start - lineOffset, length);
|
|
4669 }
|
|
4670 y += rect.y;
|
|
4671 } else if (i is lineEnd) {
|
|
4672 rect = layout.getBounds(0, end - lineOffset);
|
|
4673 } else {
|
|
4674 rect = layout.getBounds();
|
|
4675 }
|
|
4676 left = Math.min(left, rect.x);
|
|
4677 right = Math.max(right, rect.x + rect.width);
|
|
4678 height += rect.height;
|
|
4679 } else {
|
|
4680 height += renderer.getLineHeight();
|
|
4681 }
|
|
4682 renderer.disposeTextLayout(layout);
|
|
4683 }
|
|
4684 rect = new Rectangle (left, y, right-left, height);
|
|
4685 rect.x += leftMargin - horizontalScrollOffset;
|
|
4686 return rect;
|
|
4687 }
|
|
4688 /**
|
|
4689 * Returns the widget content starting at start for length characters.
|
|
4690 *
|
|
4691 * @param start offset of the first character in the returned String
|
|
4692 * @param length number of characters to return
|
|
4693 * @return widget content starting at start and extending length characters.
|
|
4694 * @exception SWTException <ul>
|
|
4695 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
4696 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
4697 * </ul>
|
|
4698 * @exception IllegalArgumentException <ul>
|
|
4699 * <li>ERROR_INVALID_RANGE when start and/or length are outside the widget content</li>
|
|
4700 * </ul>
|
|
4701 */
|
|
4702 public String getTextRange(int start, int length) {
|
|
4703 checkWidget();
|
|
4704 int contentLength = getCharCount();
|
|
4705 int end = start + length;
|
|
4706 if (start > end || start < 0 || end > contentLength) {
|
|
4707 SWT.error(SWT.ERROR_INVALID_RANGE);
|
|
4708 }
|
|
4709 return content.getTextRange(start, length);
|
|
4710 }
|
|
4711 /**
|
|
4712 * Returns the maximum number of characters that the receiver is capable of holding.
|
|
4713 *
|
|
4714 * @return the text limit
|
|
4715 *
|
|
4716 * @exception SWTException <ul>
|
|
4717 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
4718 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
4719 * </ul>
|
|
4720 */
|
|
4721 public int getTextLimit() {
|
|
4722 checkWidget();
|
|
4723 return textLimit;
|
|
4724 }
|
|
4725 /**
|
|
4726 * Gets the top index.
|
|
4727 * <p>
|
|
4728 * The top index is the index of the fully visible line that is currently
|
|
4729 * at the top of the widget or the topmost partially visible line if no line is fully visible.
|
|
4730 * The top index changes when the widget is scrolled. Indexing is zero based.
|
|
4731 * </p>
|
|
4732 *
|
|
4733 * @return the index of the top line
|
|
4734 * @exception SWTException <ul>
|
|
4735 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
4736 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
4737 * </ul>
|
|
4738 */
|
|
4739 public int getTopIndex() {
|
|
4740 checkWidget();
|
|
4741 return topIndex;
|
|
4742 }
|
|
4743 /**
|
|
4744 * Gets the top pixel.
|
|
4745 * <p>
|
|
4746 * The top pixel is the pixel position of the line that is
|
|
4747 * currently at the top of the widget. The text widget can be scrolled by pixels
|
|
4748 * by dragging the scroll thumb so that a partial line may be displayed at the top
|
|
4749 * the widget. The top pixel changes when the widget is scrolled. The top pixel
|
|
4750 * does not include the widget trimming.
|
|
4751 * </p>
|
|
4752 *
|
|
4753 * @return pixel position of the top line
|
|
4754 * @exception SWTException <ul>
|
|
4755 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
4756 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
4757 * </ul>
|
|
4758 */
|
|
4759 public int getTopPixel() {
|
|
4760 checkWidget();
|
|
4761 return getVerticalScrollOffset();
|
|
4762 }
|
|
4763 /**
|
|
4764 * Returns the vertical scroll increment.
|
|
4765 *
|
|
4766 * @return vertical scroll increment.
|
|
4767 */
|
|
4768 int getVerticalIncrement() {
|
|
4769 return renderer.getLineHeight();
|
|
4770 }
|
|
4771 int getVerticalScrollOffset() {
|
|
4772 if (verticalScrollOffset is -1) {
|
|
4773 renderer.calculate(0, topIndex);
|
|
4774 int height = 0;
|
|
4775 for (int i = 0; i < topIndex; i++) {
|
|
4776 height += renderer.getLineHeight(i);
|
|
4777 }
|
|
4778 height -= topIndexY;
|
|
4779 verticalScrollOffset = height;
|
|
4780 }
|
|
4781 return verticalScrollOffset;
|
|
4782 }
|
|
4783 int getVisualLineIndex(TextLayout layout, int offsetInLine) {
|
|
4784 int lineIndex = layout.getLineIndex(offsetInLine);
|
|
4785 int[] offsets = layout.getLineOffsets();
|
|
4786 if (lineIndex !is 0 && offsetInLine is offsets[lineIndex]) {
|
|
4787 int lineY = layout.getLineBounds(lineIndex).y;
|
|
4788 int caretY = getCaret().getLocation().y - topMargin - getLinePixel(getCaretLine());
|
|
4789 if (lineY > caretY) lineIndex--;
|
|
4790 }
|
|
4791 return lineIndex;
|
|
4792 }
|
|
4793 int getCaretDirection() {
|
|
4794 if (!isBidiCaret()) return SWT.DEFAULT;
|
|
4795 if (ime.getCompositionOffset() !is -1) return SWT.DEFAULT;
|
|
4796 if (!updateCaretDirection && caretDirection !is SWT.NULL) return caretDirection;
|
|
4797 updateCaretDirection = false;
|
|
4798 int caretLine = getCaretLine();
|
|
4799 int lineOffset = content.getOffsetAtLine(caretLine);
|
|
4800 String line = content.getLine(caretLine);
|
|
4801 int offset = caretOffset - lineOffset;
|
|
4802 int lineLength = line.length;
|
|
4803 if (lineLength is 0) return isMirrored() ? SWT.RIGHT : SWT.LEFT;
|
|
4804 if (caretAlignment is PREVIOUS_OFFSET_TRAILING && offset > 0) offset--;
|
|
4805 if (offset is lineLength && offset > 0) offset--;
|
|
4806 while (offset > 0 && tango.text.Unicode.isDigit(line[offset])) offset--;
|
|
4807 if (offset is 0 && tango.text.Unicode.isDigit(line[offset])) {
|
|
4808 return isMirrored() ? SWT.RIGHT : SWT.LEFT;
|
|
4809 }
|
|
4810 TextLayout layout = renderer.getTextLayout(caretLine);
|
|
4811 int level = layout.getLevel(offset);
|
|
4812 renderer.disposeTextLayout(layout);
|
|
4813 return ((level & 1) !is 0) ? SWT.RIGHT : SWT.LEFT;
|
|
4814 }
|
|
4815 /*
|
|
4816 * Returns the index of the line the caret is on.
|
|
4817 */
|
|
4818 int getCaretLine() {
|
|
4819 return content.getLineAtOffset(caretOffset);
|
|
4820 }
|
|
4821 int getWrapWidth () {
|
|
4822 if (wordWrap && !isSingleLine()) {
|
|
4823 int width = clientAreaWidth - leftMargin - rightMargin - getCaretWidth();
|
|
4824 return width > 0 ? width : 1;
|
|
4825 }
|
|
4826 return -1;
|
|
4827 }
|
|
4828 int getWordNext (int offset, int movement) {
|
|
4829 int newOffset, lineOffset;
|
|
4830 String lineText;
|
|
4831 if (offset >= getCharCount()) {
|
|
4832 newOffset = offset;
|
|
4833 int lineIndex = content.getLineCount() - 1;
|
|
4834 lineOffset = content.getOffsetAtLine(lineIndex);
|
|
4835 lineText = content.getLine(lineIndex);
|
|
4836 } else {
|
|
4837 int lineIndex = content.getLineAtOffset(offset);
|
|
4838 lineOffset = content.getOffsetAtLine(lineIndex);
|
|
4839 lineText = content.getLine(lineIndex);
|
|
4840 int lineLength = lineText.length;
|
|
4841 if (offset is lineOffset + lineLength) {
|
|
4842 newOffset = content.getOffsetAtLine(lineIndex + 1);
|
|
4843 } else {
|
|
4844 TextLayout layout = renderer.getTextLayout(lineIndex);
|
|
4845 newOffset = lineOffset + layout.getNextOffset(offset - lineOffset, movement);
|
|
4846 renderer.disposeTextLayout(layout);
|
|
4847 }
|
|
4848 }
|
|
4849 return sendWordBoundaryEvent(WordNext, movement, offset, newOffset, lineText, lineOffset);
|
|
4850 }
|
|
4851 int getWordPrevious(int offset, int movement) {
|
|
4852 int newOffset, lineOffset;
|
|
4853 String lineText;
|
|
4854 if (offset <= 0) {
|
|
4855 newOffset = 0;
|
|
4856 int lineIndex = content.getLineAtOffset(newOffset);
|
|
4857 lineOffset = content.getOffsetAtLine(lineIndex);
|
|
4858 lineText = content.getLine(lineIndex);
|
|
4859 } else {
|
|
4860 int lineIndex = content.getLineAtOffset(offset);
|
|
4861 lineOffset = content.getOffsetAtLine(lineIndex);
|
|
4862 lineText = content.getLine(lineIndex);
|
|
4863 if (offset is lineOffset) {
|
|
4864 String nextLineText = content.getLine(lineIndex - 1);
|
|
4865 int nextLineOffset = content.getOffsetAtLine(lineIndex - 1);
|
|
4866 newOffset = nextLineOffset + nextLineText.length;
|
|
4867 } else {
|
|
4868 TextLayout layout = renderer.getTextLayout(lineIndex);
|
|
4869 newOffset = lineOffset + layout.getPreviousOffset(offset - lineOffset, movement);
|
|
4870 renderer.disposeTextLayout(layout);
|
|
4871 }
|
|
4872 }
|
|
4873 return sendWordBoundaryEvent(WordPrevious, movement, offset, newOffset, lineText, lineOffset);
|
|
4874 }
|
|
4875 /**
|
|
4876 * Returns whether the widget wraps lines.
|
|
4877 *
|
|
4878 * @return true if widget wraps lines, false otherwise
|
|
4879 * @since 2.0
|
|
4880 */
|
|
4881 public bool getWordWrap() {
|
|
4882 checkWidget();
|
|
4883 return wordWrap;
|
|
4884 }
|
|
4885 /**
|
|
4886 * Returns the location of the given offset.
|
|
4887 * <p>
|
|
4888 * <b>NOTE:</b> Does not return correct values for true italic fonts (vs. slanted fonts).
|
|
4889 * </p>
|
|
4890 *
|
|
4891 * @return location of the character at the given offset in the line.
|
|
4892 */
|
|
4893 Point getPointAtOffset(int offset) {
|
|
4894 int lineIndex = content.getLineAtOffset(offset);
|
|
4895 String line = content.getLine(lineIndex);
|
|
4896 int lineOffset = content.getOffsetAtLine(lineIndex);
|
|
4897 int offsetInLine = offset - lineOffset;
|
|
4898 int lineLength = line.length;
|
|
4899 if (lineIndex < content.getLineCount() - 1) {
|
|
4900 int endLineOffset = content.getOffsetAtLine(lineIndex + 1) - 1;
|
|
4901 if (lineLength < offsetInLine && offsetInLine <= endLineOffset) {
|
|
4902 offsetInLine = lineLength;
|
|
4903 }
|
|
4904 }
|
|
4905 Point point;
|
|
4906 TextLayout layout = renderer.getTextLayout(lineIndex);
|
|
4907 if (lineLength !is 0 && offsetInLine <= lineLength) {
|
|
4908 if (offsetInLine is lineLength) {
|
|
4909 // SWT: Instead of go back one byte, go back one codepoint
|
|
4910 int offsetInLine_m1 = layout.getPreviousOffset(offsetInLine, SWT.MOVEMENT_CLUSTER);
|
|
4911 point = layout.getLocation(offsetInLine_m1, true);
|
|
4912 } else {
|
|
4913 switch (caretAlignment) {
|
|
4914 case OFFSET_LEADING:
|
|
4915 point = layout.getLocation(offsetInLine, false);
|
|
4916 break;
|
|
4917 case PREVIOUS_OFFSET_TRAILING:
|
|
4918 default:
|
|
4919 if (offsetInLine is 0) {
|
|
4920 point = layout.getLocation(offsetInLine, false);
|
|
4921 } else {
|
|
4922 // SWT: Instead of go back one byte, go back one codepoint
|
|
4923 int offsetInLine_m1 = layout.getPreviousOffset(offsetInLine, SWT.MOVEMENT_CLUSTER);
|
|
4924 point = layout.getLocation(offsetInLine_m1, true);
|
|
4925 }
|
|
4926 break;
|
|
4927 }
|
|
4928 }
|
|
4929 } else {
|
|
4930 point = new Point(layout.getIndent(), 0);
|
|
4931 }
|
|
4932 renderer.disposeTextLayout(layout);
|
|
4933 point.x += leftMargin - horizontalScrollOffset;
|
|
4934 point.y += getLinePixel(lineIndex);
|
|
4935 return point;
|
|
4936 }
|
|
4937 /**
|
|
4938 * Inserts a string. The old selection is replaced with the new text.
|
|
4939 *
|
|
4940 * @param string the string
|
|
4941 * @see #replaceTextRange(int,int,String)
|
|
4942 * @exception SWTException <ul>
|
|
4943 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
4944 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
4945 * </ul>
|
|
4946 */
|
|
4947 public void insert(String string) {
|
|
4948 checkWidget();
|
|
4949 // SWT extension: allow null for zero length string
|
|
4950 // if (string is null) {
|
|
4951 // SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
4952 // }
|
|
4953 Point sel = getSelectionRange();
|
|
4954 replaceTextRange(sel.x, sel.y, string);
|
|
4955 }
|
|
4956 /**
|
|
4957 * Creates content change listeners and set the default content model.
|
|
4958 */
|
|
4959 void installDefaultContent() {
|
|
4960 textChangeListener = new class() TextChangeListener {
|
|
4961 public void textChanging(TextChangingEvent event) {
|
|
4962 handleTextChanging(event);
|
|
4963 }
|
|
4964 public void textChanged(TextChangedEvent event) {
|
|
4965 handleTextChanged(event);
|
|
4966 }
|
|
4967 public void textSet(TextChangedEvent event) {
|
|
4968 handleTextSet(event);
|
|
4969 }
|
|
4970 };
|
|
4971 content = new DefaultContent();
|
|
4972 content.addTextChangeListener(textChangeListener);
|
|
4973 }
|
|
4974 /**
|
|
4975 * Adds event listeners
|
|
4976 */
|
|
4977 void installListeners() {
|
|
4978 ScrollBar verticalBar = getVerticalBar();
|
|
4979 ScrollBar horizontalBar = getHorizontalBar();
|
|
4980
|
|
4981 listener = new class() Listener {
|
|
4982 public void handleEvent(Event event) {
|
|
4983 switch (event.type) {
|
|
4984 case SWT.Dispose: handleDispose(event); break;
|
|
4985 case SWT.KeyDown: handleKeyDown(event); break;
|
|
4986 case SWT.KeyUp: handleKeyUp(event); break;
|
|
4987 case SWT.MouseDown: handleMouseDown(event); break;
|
|
4988 case SWT.MouseUp: handleMouseUp(event); break;
|
|
4989 case SWT.MouseMove: handleMouseMove(event); break;
|
|
4990 case SWT.Paint: handlePaint(event); break;
|
|
4991 case SWT.Resize: handleResize(event); break;
|
|
4992 case SWT.Traverse: handleTraverse(event); break;
|
|
4993 default:
|
|
4994 }
|
|
4995 }
|
|
4996 };
|
|
4997 addListener(SWT.Dispose, listener);
|
|
4998 addListener(SWT.KeyDown, listener);
|
|
4999 addListener(SWT.KeyUp, listener);
|
|
5000 addListener(SWT.MouseDown, listener);
|
|
5001 addListener(SWT.MouseUp, listener);
|
|
5002 addListener(SWT.MouseMove, listener);
|
|
5003 addListener(SWT.Paint, listener);
|
|
5004 addListener(SWT.Resize, listener);
|
|
5005 addListener(SWT.Traverse, listener);
|
|
5006 ime.addListener(SWT.ImeComposition, new class() Listener {
|
|
5007 public void handleEvent(Event event) {
|
|
5008 switch (event.detail) {
|
|
5009 case SWT.COMPOSITION_SELECTION: handleCompositionSelection(event); break;
|
|
5010 case SWT.COMPOSITION_CHANGED: handleCompositionChanged(event); break;
|
|
5011 case SWT.COMPOSITION_OFFSET: handleCompositionOffset(event); break;
|
|
5012 default:
|
|
5013 }
|
|
5014 }
|
|
5015 });
|
|
5016 if (verticalBar !is null) {
|
|
5017 verticalBar.addListener(SWT.Selection, new class() Listener {
|
|
5018 public void handleEvent(Event event) {
|
|
5019 handleVerticalScroll(event);
|
|
5020 }
|
|
5021 });
|
|
5022 }
|
|
5023 if (horizontalBar !is null) {
|
|
5024 horizontalBar.addListener(SWT.Selection, new class() Listener {
|
|
5025 public void handleEvent(Event event) {
|
|
5026 handleHorizontalScroll(event);
|
|
5027 }
|
|
5028 });
|
|
5029 }
|
|
5030 }
|
|
5031 void internalRedrawRange(int start, int length) {
|
|
5032 if (length <= 0) return;
|
|
5033 int end = start + length;
|
|
5034 int startLine = content.getLineAtOffset(start);
|
|
5035 int endLine = content.getLineAtOffset(end);
|
|
5036 int partialBottomIndex = getPartialBottomIndex();
|
|
5037 int partialTopIndex = getPartialTopIndex();
|
|
5038 if (startLine > partialBottomIndex || endLine < partialTopIndex) {
|
|
5039 return;
|
|
5040 }
|
|
5041 if (partialTopIndex > startLine) {
|
|
5042 startLine = partialTopIndex;
|
|
5043 start = 0;
|
|
5044 } else {
|
|
5045 start -= content.getOffsetAtLine(startLine);
|
|
5046 }
|
|
5047 if (partialBottomIndex < endLine) {
|
|
5048 endLine = partialBottomIndex + 1;
|
|
5049 end = 0;
|
|
5050 } else {
|
|
5051 end -= content.getOffsetAtLine(endLine);
|
|
5052 }
|
|
5053
|
|
5054 TextLayout layout = renderer.getTextLayout(startLine);
|
|
5055 int lineX = leftMargin - horizontalScrollOffset, startLineY = getLinePixel(startLine);
|
|
5056 int[] offsets = layout.getLineOffsets();
|
|
5057 int startIndex = layout.getLineIndex(Math.min(start, layout.getText().length));
|
|
5058
|
|
5059 /* Redraw end of line before start line if wrapped and start offset is first char */
|
|
5060 if (wordWrap && startIndex > 0 && offsets[startIndex] is start) {
|
|
5061 Rectangle rect = layout.getLineBounds(startIndex - 1);
|
|
5062 rect.x = rect.width;
|
|
5063 rect.width = clientAreaWidth - rightMargin - rect.x;
|
|
5064 rect.x += lineX;
|
|
5065 rect.y += startLineY;
|
|
5066 super.redraw(rect.x, rect.y, rect.width, rect.height, false);
|
|
5067 }
|
|
5068
|
|
5069 if (startLine is endLine) {
|
|
5070 int endIndex = layout.getLineIndex(Math.min(end, layout.getText().length));
|
|
5071 if (startIndex is endIndex) {
|
|
5072 /* Redraw rect between start and end offset if start and end offsets are in same wrapped line */
|
|
5073 Rectangle rect = layout.getBounds(start, end - 1);
|
|
5074 rect.x += lineX;
|
|
5075 rect.y += startLineY;
|
|
5076 super.redraw(rect.x, rect.y, rect.width, rect.height, false);
|
|
5077 renderer.disposeTextLayout(layout);
|
|
5078 return;
|
|
5079 }
|
|
5080 }
|
|
5081
|
|
5082 /* Redraw start line from the start offset to the end of client area */
|
|
5083 Rectangle startRect = layout.getBounds(start, offsets[startIndex + 1] - 1);
|
|
5084 if (startRect.height is 0) {
|
|
5085 Rectangle bounds = layout.getLineBounds(startIndex);
|
|
5086 startRect.x = bounds.width;
|
|
5087 startRect.y = bounds.y;
|
|
5088 startRect.height = bounds.height;
|
|
5089 }
|
|
5090 startRect.x += lineX;
|
|
5091 startRect.y += startLineY;
|
|
5092 startRect.width = clientAreaWidth - rightMargin - startRect.x;
|
|
5093 super.redraw(startRect.x, startRect.y, startRect.width, startRect.height, false);
|
|
5094
|
|
5095 /* Redraw end line from the beginning of the line to the end offset */
|
|
5096 if (startLine !is endLine) {
|
|
5097 renderer.disposeTextLayout(layout);
|
|
5098 layout = renderer.getTextLayout(endLine);
|
|
5099 offsets = layout.getLineOffsets();
|
|
5100 }
|
|
5101 int endIndex = layout.getLineIndex(Math.min(end, layout.getText().length));
|
|
5102 Rectangle endRect = layout.getBounds(offsets[endIndex], end - 1);
|
|
5103 if (endRect.height is 0) {
|
|
5104 Rectangle bounds = layout.getLineBounds(endIndex);
|
|
5105 endRect.y = bounds.y;
|
|
5106 endRect.height = bounds.height;
|
|
5107 }
|
|
5108 endRect.x += lineX;
|
|
5109 endRect.y += getLinePixel(endLine);
|
|
5110 super.redraw(endRect.x, endRect.y, endRect.width, endRect.height, false);
|
|
5111 renderer.disposeTextLayout(layout);
|
|
5112
|
|
5113 /* Redraw all lines in between start and end line */
|
|
5114 int y = startRect.y + startRect.height;
|
|
5115 if (endRect.y > y) {
|
|
5116 super.redraw(leftMargin, y, clientAreaWidth - rightMargin - leftMargin, endRect.y - y, false);
|
|
5117 }
|
|
5118 }
|
|
5119 void handleCompositionOffset (Event event) {
|
|
5120 int[] trailing = new int [1];
|
|
5121 event.index = getOffsetAtPoint(event.x, event.y, trailing, true);
|
|
5122 event.count = trailing[0];
|
|
5123 }
|
|
5124 void handleCompositionSelection (Event event) {
|
|
5125 event.start = selection.x;
|
|
5126 event.end = selection.y;
|
|
5127 event.text = getSelectionText();
|
|
5128 }
|
|
5129 void handleCompositionChanged(Event event) {
|
|
5130 String text = event.text;
|
|
5131 int start = event.start;
|
|
5132 int end = event.end;
|
|
5133 int length = text.length;
|
|
5134 if (length is ime.getCommitCount()) {
|
|
5135 content.replaceTextRange(start, end - start, "");
|
|
5136 caretOffset = ime.getCompositionOffset();
|
|
5137 caretWidth = 0;
|
|
5138 caretDirection = SWT.NULL;
|
|
5139 } else {
|
|
5140 content.replaceTextRange(start, end - start, text);
|
|
5141 caretOffset = ime.getCaretOffset();
|
|
5142 if (ime.getWideCaret()) {
|
|
5143 start = ime.getCompositionOffset();
|
|
5144 int lineIndex = getCaretLine();
|
|
5145 int lineOffset = content.getOffsetAtLine(lineIndex);
|
|
5146 TextLayout layout = renderer.getTextLayout(lineIndex);
|
|
5147 caretWidth = layout.getBounds(start - lineOffset, start + length - 1 - lineOffset).width;
|
|
5148 renderer.disposeTextLayout(layout);
|
|
5149 }
|
|
5150 }
|
|
5151 showCaret();
|
|
5152 }
|
|
5153 /**
|
|
5154 * Frees resources.
|
|
5155 */
|
|
5156 void handleDispose(Event event) {
|
|
5157 removeListener(SWT.Dispose, listener);
|
|
5158 notifyListeners(SWT.Dispose, event);
|
|
5159 event.type = SWT.None;
|
|
5160
|
|
5161 clipboard.dispose();
|
|
5162 if (renderer !is null) {
|
|
5163 renderer.dispose();
|
|
5164 renderer = null;
|
|
5165 }
|
|
5166 if (content !is null) {
|
|
5167 content.removeTextChangeListener(textChangeListener);
|
|
5168 content = null;
|
|
5169 }
|
|
5170 if (defaultCaret !is null) {
|
|
5171 defaultCaret.dispose();
|
|
5172 defaultCaret = null;
|
|
5173 }
|
|
5174 if (leftCaretBitmap !is null) {
|
|
5175 leftCaretBitmap.dispose();
|
|
5176 leftCaretBitmap = null;
|
|
5177 }
|
|
5178 if (rightCaretBitmap !is null) {
|
|
5179 rightCaretBitmap.dispose();
|
|
5180 rightCaretBitmap = null;
|
|
5181 }
|
|
5182 if (isBidiCaret()) {
|
|
5183 BidiUtil.removeLanguageListener(this);
|
|
5184 }
|
|
5185 selectionBackground = null;
|
|
5186 selectionForeground = null;
|
|
5187 textChangeListener = null;
|
|
5188 selection = null;
|
|
5189 doubleClickSelection = null;
|
|
5190 keyActionMap = null;
|
|
5191 background = null;
|
|
5192 foreground = null;
|
|
5193 clipboard = null;
|
|
5194 }
|
|
5195 /**
|
|
5196 * Scrolls the widget horizontally.
|
|
5197 */
|
|
5198 void handleHorizontalScroll(Event event) {
|
|
5199 int scrollPixel = getHorizontalBar().getSelection() - horizontalScrollOffset;
|
|
5200 scrollHorizontal(scrollPixel, false);
|
|
5201 }
|
|
5202 /**
|
|
5203 * If an action has been registered for the key stroke execute the action.
|
|
5204 * Otherwise, if a character has been entered treat it as new content.
|
|
5205 *
|
|
5206 * @param event keyboard event
|
|
5207 */
|
|
5208 void handleKey(Event event) {
|
|
5209 int action;
|
|
5210 caretAlignment = PREVIOUS_OFFSET_TRAILING;
|
|
5211 if (event.keyCode !is 0) {
|
|
5212 // special key pressed (e.g., F1)
|
|
5213 action = getKeyBinding(event.keyCode | event.stateMask);
|
|
5214 } else {
|
|
5215 // character key pressed
|
|
5216 action = getKeyBinding(event.character | event.stateMask);
|
|
5217 if (action is SWT.NULL) {
|
|
5218 // see if we have a control character
|
|
5219 if ((event.stateMask & SWT.CTRL) !is 0 && (event.character >= 0) && event.character <= 31) {
|
|
5220 // get the character from the CTRL+char sequence, the control
|
|
5221 // key subtracts 64 from the value of the key that it modifies
|
|
5222 int c = event.character + 64;
|
|
5223 action = getKeyBinding(c | event.stateMask);
|
|
5224 }
|
|
5225 }
|
|
5226 }
|
|
5227 if (action is SWT.NULL) {
|
|
5228 bool ignore = false;
|
|
5229
|
|
5230 if (IS_CARBON) {
|
|
5231 // Ignore accelerator key combinations (we do not want to
|
|
5232 // insert a character in the text in this instance). Do not
|
|
5233 // ignore COMMAND+ALT combinations since that key sequence
|
|
5234 // produces characters on the mac.
|
|
5235 ignore = (event.stateMask ^ SWT.COMMAND) is 0 ||
|
|
5236 (event.stateMask ^ (SWT.COMMAND | SWT.SHIFT)) is 0;
|
|
5237 } else if (IS_MOTIF) {
|
|
5238 // Ignore accelerator key combinations (we do not want to
|
|
5239 // insert a character in the text in this instance). Do not
|
|
5240 // ignore ALT combinations since this key sequence
|
|
5241 // produces characters on motif.
|
|
5242 ignore = (event.stateMask ^ SWT.CTRL) is 0 ||
|
|
5243 (event.stateMask ^ (SWT.CTRL | SWT.SHIFT)) is 0;
|
|
5244 } else {
|
|
5245 // Ignore accelerator key combinations (we do not want to
|
|
5246 // insert a character in the text in this instance). Don't
|
|
5247 // ignore CTRL+ALT combinations since that is the Alt Gr
|
|
5248 // key on some keyboards. See bug 20953.
|
|
5249 ignore = (event.stateMask ^ SWT.ALT) is 0 ||
|
|
5250 (event.stateMask ^ SWT.CTRL) is 0 ||
|
|
5251 (event.stateMask ^ (SWT.ALT | SWT.SHIFT)) is 0 ||
|
|
5252 (event.stateMask ^ (SWT.CTRL | SWT.SHIFT)) is 0;
|
|
5253 }
|
|
5254 // -ignore anything below SPACE except for line delimiter keys and tab.
|
|
5255 // -ignore DEL
|
|
5256 if (!ignore && event.character > 31 && event.character !is SWT.DEL ||
|
|
5257 event.character is SWT.CR || event.character is SWT.LF ||
|
|
5258 event.character is TAB) {
|
|
5259 doContent(event.character);
|
|
5260 update();
|
|
5261 }
|
|
5262 } else {
|
|
5263 invokeAction(action);
|
|
5264 }
|
|
5265 }
|
|
5266 /**
|
|
5267 * If a VerifyKey listener exists, verify that the key that was entered
|
|
5268 * should be processed.
|
|
5269 *
|
|
5270 * @param event keyboard event
|
|
5271 */
|
|
5272 void handleKeyDown(Event event) {
|
|
5273 if (clipboardSelection is null) {
|
|
5274 clipboardSelection = new Point(selection.x, selection.y);
|
|
5275 }
|
|
5276
|
|
5277 Event verifyEvent = new Event();
|
|
5278 verifyEvent.character = event.character;
|
|
5279 verifyEvent.keyCode = event.keyCode;
|
|
5280 verifyEvent.stateMask = event.stateMask;
|
|
5281 verifyEvent.doit = true;
|
|
5282 notifyListeners(VerifyKey, verifyEvent);
|
|
5283 if (verifyEvent.doit) {
|
|
5284 handleKey(event);
|
|
5285 }
|
|
5286 }
|
|
5287 /**
|
|
5288 * Update the Selection Clipboard.
|
|
5289 *
|
|
5290 * @param event keyboard event
|
|
5291 */
|
|
5292 void handleKeyUp(Event event) {
|
|
5293 if (clipboardSelection !is null) {
|
|
5294 if (clipboardSelection.x !is selection.x || clipboardSelection.y !is selection.y) {
|
|
5295 try {
|
|
5296 if (selection.y - selection.x > 0) {
|
|
5297 setClipboardContent(selection.x, selection.y - selection.x, DND.SELECTION_CLIPBOARD);
|
|
5298 }
|
|
5299 } catch (SWTError error) {
|
|
5300 // Copy to clipboard failed. This happens when another application
|
|
5301 // is accessing the clipboard while we copy. Ignore the error.
|
|
5302 // Fixes 1GDQAVN
|
|
5303 // Rethrow all other errors. Fixes bug 17578.
|
|
5304 if (error.code !is DND.ERROR_CANNOT_SET_CLIPBOARD) {
|
|
5305 throw error;
|
|
5306 }
|
|
5307 }
|
|
5308 }
|
|
5309 }
|
|
5310 clipboardSelection = null;
|
|
5311 }
|
|
5312 /**
|
|
5313 * Updates the caret location and selection if mouse button 1 has been
|
|
5314 * pressed.
|
|
5315 */
|
|
5316 void handleMouseDown(Event event) {
|
|
5317 //force focus (object support)
|
|
5318 forceFocus();
|
|
5319
|
|
5320 //drag detect
|
|
5321 if (dragDetect_ && checkDragDetect(event)) return;
|
|
5322
|
|
5323 //paste clipboard selection
|
|
5324 if (event.button is 2) {
|
|
5325 auto o = cast(ArrayWrapperString)getClipboardContent(DND.SELECTION_CLIPBOARD);
|
|
5326 String text = o.array;
|
|
5327 if (text !is null && text.length > 0) {
|
|
5328 // position cursor
|
|
5329 doMouseLocationChange(event.x, event.y, false);
|
|
5330 // insert text
|
|
5331 Event e = new Event();
|
|
5332 e.start = selection.x;
|
|
5333 e.end = selection.y;
|
|
5334 e.text = getModelDelimitedText(text);
|
|
5335 sendKeyEvent(e);
|
|
5336 }
|
|
5337 }
|
|
5338
|
|
5339 //set selection
|
|
5340 if ((event.button !is 1) || (IS_CARBON && (event.stateMask & SWT.MOD4) !is 0)) {
|
|
5341 return;
|
|
5342 }
|
|
5343 clickCount = event.count;
|
|
5344 if (clickCount is 1) {
|
|
5345 bool select = (event.stateMask & SWT.MOD2) !is 0;
|
|
5346 doMouseLocationChange(event.x, event.y, select);
|
|
5347 } else {
|
|
5348 if (doubleClickEnabled) {
|
|
5349 clearSelection(false);
|
|
5350 int offset = getOffsetAtPoint(event.x, event.y);
|
|
5351 int lineIndex = content.getLineAtOffset(offset);
|
|
5352 int lineOffset = content.getOffsetAtLine(lineIndex);
|
|
5353 int lineEnd = content.getCharCount();
|
|
5354 if (lineIndex + 1 < content.getLineCount()) {
|
|
5355 lineEnd = content.getOffsetAtLine(lineIndex + 1);
|
|
5356 }
|
|
5357 int start, end;
|
|
5358 if ((clickCount & 1) is 0) {
|
|
5359 start = Math.max(0, getWordPrevious(offset, SWT.MOVEMENT_WORD_START));
|
|
5360 end = Math.min(content.getCharCount(), getWordNext(start, SWT.MOVEMENT_WORD_END));
|
|
5361 } else {
|
|
5362 start = lineOffset;
|
|
5363 end = lineEnd;
|
|
5364 }
|
|
5365 caretOffset = start;
|
|
5366 resetSelection();
|
|
5367 caretOffset = end;
|
|
5368 showCaret();
|
|
5369 doMouseSelection();
|
|
5370 doubleClickSelection = new Point(selection.x, selection.y);
|
|
5371 }
|
|
5372 }
|
|
5373 }
|
|
5374 /**
|
|
5375 * Updates the caret location and selection if mouse button 1 is pressed
|
|
5376 * during the mouse move.
|
|
5377 */
|
|
5378 void handleMouseMove(Event event) {
|
|
5379 if (clickCount is 0) return;
|
|
5380 doMouseLocationChange(event.x, event.y, true);
|
|
5381 update();
|
|
5382 doAutoScroll(event);
|
|
5383 }
|
|
5384 /**
|
|
5385 * Autoscrolling ends when the mouse button is released.
|
|
5386 */
|
|
5387 void handleMouseUp(Event event) {
|
|
5388 clickCount = 0;
|
|
5389 endAutoScroll();
|
|
5390 if (event.button is 1) {
|
|
5391 try {
|
|
5392 if (selection.y - selection.x > 0) {
|
|
5393 setClipboardContent(selection.x, selection.y - selection.x, DND.SELECTION_CLIPBOARD);
|
|
5394 }
|
|
5395 } catch (SWTError error) {
|
|
5396 // Copy to clipboard failed. This happens when another application
|
|
5397 // is accessing the clipboard while we copy. Ignore the error.
|
|
5398 // Fixes 1GDQAVN
|
|
5399 // Rethrow all other errors. Fixes bug 17578.
|
|
5400 if (error.code !is DND.ERROR_CANNOT_SET_CLIPBOARD) {
|
|
5401 throw error;
|
|
5402 }
|
|
5403 }
|
|
5404 }
|
|
5405 }
|
|
5406 /**
|
|
5407 * Renders the invalidated area specified in the paint event.
|
|
5408 *
|
|
5409 * @param event paint event
|
|
5410 */
|
|
5411 void handlePaint(Event event) {
|
|
5412 if (event.width is 0 || event.height is 0) return;
|
|
5413 if (clientAreaWidth is 0 || clientAreaHeight is 0) return;
|
|
5414
|
|
5415 int startLine = getLineIndex(event.y);
|
|
5416 int y = getLinePixel(startLine);
|
|
5417 int endY = event.y + event.height;
|
|
5418 GC gc = event.gc;
|
|
5419 Color background = getBackground();
|
|
5420 Color foreground = getForeground();
|
|
5421 if (endY > 0) {
|
|
5422 int lineCount = isSingleLine() ? 1 : content.getLineCount();
|
|
5423 int x = leftMargin - horizontalScrollOffset;
|
|
5424 for (int i = startLine; y < endY && i < lineCount; i++) {
|
|
5425 y += renderer.drawLine(i, x, y, gc, background, foreground);
|
|
5426 }
|
|
5427 if (y < endY) {
|
|
5428 gc.setBackground(background);
|
|
5429 drawBackground(gc, 0, y, clientAreaWidth, endY - y);
|
|
5430 }
|
|
5431 }
|
|
5432 // fill the margin background
|
|
5433 gc.setBackground(background);
|
|
5434 if (topMargin > 0) {
|
|
5435 drawBackground(gc, 0, 0, clientAreaWidth, topMargin);
|
|
5436 }
|
|
5437 if (bottomMargin > 0) {
|
|
5438 drawBackground(gc, 0, clientAreaHeight - bottomMargin, clientAreaWidth, bottomMargin);
|
|
5439 }
|
|
5440 if (leftMargin > 0) {
|
|
5441 drawBackground(gc, 0, 0, leftMargin, clientAreaHeight);
|
|
5442 }
|
|
5443 if (rightMargin > 0) {
|
|
5444 drawBackground(gc, clientAreaWidth - rightMargin, 0, rightMargin, clientAreaHeight);
|
|
5445 }
|
|
5446 }
|
|
5447 /**
|
|
5448 * Recalculates the scroll bars. Rewraps all lines when in word
|
|
5449 * wrap mode.
|
|
5450 *
|
|
5451 * @param event resize event
|
|
5452 */
|
|
5453 void handleResize(Event event) {
|
|
5454 int oldHeight = clientAreaHeight;
|
|
5455 int oldWidth = clientAreaWidth;
|
|
5456 Rectangle clientArea = getClientArea();
|
|
5457 clientAreaHeight = clientArea.height;
|
|
5458 clientAreaWidth = clientArea.width;
|
|
5459 /* Redraw the old or new right/bottom margin if needed */
|
|
5460 if (oldWidth !is clientAreaWidth) {
|
|
5461 if (rightMargin > 0) {
|
|
5462 int x = (oldWidth < clientAreaWidth ? oldWidth : clientAreaWidth) - rightMargin;
|
|
5463 super.redraw(x, 0, rightMargin, oldHeight, false);
|
|
5464 }
|
|
5465 }
|
|
5466 if (oldHeight !is clientAreaHeight) {
|
|
5467 if (bottomMargin > 0) {
|
|
5468 int y = (oldHeight < clientAreaHeight ? oldHeight : clientAreaHeight) - bottomMargin;
|
|
5469 super.redraw(0, y, oldWidth, bottomMargin, false);
|
|
5470 }
|
|
5471 }
|
|
5472 if (wordWrap) {
|
|
5473 if (oldWidth !is clientAreaWidth) {
|
|
5474 renderer.reset(0, content.getLineCount());
|
|
5475 verticalScrollOffset = -1;
|
|
5476 renderer.calculateIdle();
|
|
5477 super.redraw();
|
|
5478 }
|
|
5479 if (oldHeight !is clientAreaHeight) {
|
|
5480 if (oldHeight is 0) topIndexY = 0;
|
|
5481 setScrollBars(true);
|
|
5482 }
|
|
5483 setCaretLocation();
|
|
5484 } else {
|
|
5485 renderer.calculateClientArea();
|
|
5486 setScrollBars(true);
|
|
5487 claimRightFreeSpace();
|
|
5488 // StyledText allows any value for horizontalScrollOffset when clientArea is zero
|
|
5489 // in setHorizontalPixel() and setHorisontalOffset(). Fixes bug 168429.
|
|
5490 if (clientAreaWidth !is 0) {
|
|
5491 ScrollBar horizontalBar = getHorizontalBar();
|
|
5492 if (horizontalBar !is null && horizontalBar.getVisible()) {
|
|
5493 if (horizontalScrollOffset !is horizontalBar.getSelection()) {
|
|
5494 horizontalBar.setSelection(horizontalScrollOffset);
|
|
5495 horizontalScrollOffset = horizontalBar.getSelection();
|
|
5496 }
|
|
5497 }
|
|
5498 }
|
|
5499 }
|
|
5500 claimBottomFreeSpace();
|
|
5501 //TODO FIX TOP INDEX DURING RESIZE
|
|
5502 // if (oldHeight !is clientAreaHeight || wordWrap) {
|
|
5503 // calculateTopIndex(0);
|
|
5504 // }
|
|
5505 }
|
|
5506 /**
|
|
5507 * Updates the caret position and selection and the scroll bars to reflect
|
|
5508 * the content change.
|
|
5509 */
|
|
5510 void handleTextChanged(TextChangedEvent event) {
|
|
5511 int offset = ime.getCompositionOffset();
|
|
5512 if (offset !is -1 && lastTextChangeStart < offset) {
|
|
5513 ime.setCompositionOffset(offset + lastTextChangeNewCharCount - lastTextChangeReplaceCharCount);
|
|
5514 }
|
|
5515 int firstLine = content.getLineAtOffset(lastTextChangeStart);
|
|
5516 resetCache(firstLine, 0);
|
|
5517 if (!isFixedLineHeight() && topIndex > firstLine) {
|
|
5518 topIndex = firstLine;
|
|
5519 topIndexY = 0;
|
|
5520 super.redraw();
|
|
5521 } else {
|
|
5522 int lastLine = firstLine + lastTextChangeNewLineCount;
|
|
5523 int firstLineTop = getLinePixel(firstLine);
|
|
5524 int newLastLineBottom = getLinePixel(lastLine + 1);
|
|
5525 if (lastLineBottom !is newLastLineBottom) {
|
|
5526 super.redraw();
|
|
5527 } else {
|
|
5528 super.redraw(0, firstLineTop, clientAreaWidth, newLastLineBottom - firstLineTop, false);
|
|
5529 redrawLinesBullet(renderer.redrawLines);
|
|
5530 }
|
|
5531 }
|
|
5532 renderer.redrawLines = null;
|
|
5533 // update selection/caret location after styles have been changed.
|
|
5534 // otherwise any text measuring could be incorrect
|
|
5535 //
|
|
5536 // also, this needs to be done after all scrolling. Otherwise,
|
|
5537 // selection redraw would be flushed during scroll which is wrong.
|
|
5538 // in some cases new text would be drawn in scroll source area even
|
|
5539 // though the intent is to scroll it.
|
|
5540 updateSelection(lastTextChangeStart, lastTextChangeReplaceCharCount, lastTextChangeNewCharCount);
|
|
5541 if (lastTextChangeReplaceLineCount > 0 || wordWrap) {
|
|
5542 claimBottomFreeSpace();
|
|
5543 }
|
|
5544 if (lastTextChangeReplaceCharCount > 0) {
|
|
5545 claimRightFreeSpace();
|
|
5546 }
|
|
5547 }
|
|
5548 /**
|
|
5549 * Updates the screen to reflect a pending content change.
|
|
5550 *
|
|
5551 * @param event .start the start offset of the change
|
|
5552 * @param event .newText text that is going to be inserted or empty String
|
|
5553 * if no text will be inserted
|
|
5554 * @param event .replaceCharCount length of text that is going to be replaced
|
|
5555 * @param event .newCharCount length of text that is going to be inserted
|
|
5556 * @param event .replaceLineCount number of lines that are going to be replaced
|
|
5557 * @param event .newLineCount number of new lines that are going to be inserted
|
|
5558 */
|
|
5559 void handleTextChanging(TextChangingEvent event) {
|
|
5560 if (event.replaceCharCount < 0) {
|
|
5561 event.start += event.replaceCharCount;
|
|
5562 event.replaceCharCount *= -1;
|
|
5563 }
|
|
5564 lastTextChangeStart = event.start;
|
|
5565 lastTextChangeNewLineCount = event.newLineCount;
|
|
5566 lastTextChangeNewCharCount = event.newCharCount;
|
|
5567 lastTextChangeReplaceLineCount = event.replaceLineCount;
|
|
5568 lastTextChangeReplaceCharCount = event.replaceCharCount;
|
|
5569 int lineIndex = content.getLineAtOffset(event.start);
|
|
5570 int srcY = getLinePixel(lineIndex + event.replaceLineCount + 1);
|
|
5571 int destY = getLinePixel(lineIndex + 1) + event.newLineCount * renderer.getLineHeight();
|
|
5572 lastLineBottom = destY;
|
|
5573 if (srcY < 0 && destY < 0) {
|
|
5574 lastLineBottom += srcY - destY;
|
|
5575 verticalScrollOffset += destY - srcY;
|
|
5576 calculateTopIndex(destY - srcY);
|
|
5577 setScrollBars(true);
|
|
5578 } else {
|
|
5579 scrollText(srcY, destY);
|
|
5580 }
|
|
5581
|
|
5582 renderer.textChanging(event);
|
|
5583
|
|
5584 // Update the caret offset if it is greater than the length of the content.
|
|
5585 // This is necessary since style range API may be called between the
|
|
5586 // handleTextChanging and handleTextChanged events and this API sets the
|
|
5587 // caretOffset.
|
|
5588 int newEndOfText = content.getCharCount() - event.replaceCharCount + event.newCharCount;
|
|
5589 if (caretOffset > newEndOfText) caretOffset = newEndOfText;
|
|
5590 }
|
|
5591 /**
|
|
5592 * Called when the widget content is set programmatically, overwriting
|
|
5593 * the old content. Resets the caret position, selection and scroll offsets.
|
|
5594 * Recalculates the content width and scroll bars. Redraws the widget.
|
|
5595 *
|
|
5596 * @param event text change event.
|
|
5597 */
|
|
5598 void handleTextSet(TextChangedEvent event) {
|
|
5599 reset();
|
|
5600 }
|
|
5601 /**
|
|
5602 * Called when a traversal key is pressed.
|
|
5603 * Allow tab next traversal to occur when the widget is in single
|
|
5604 * line mode or in multi line and non-editable mode .
|
|
5605 * When in editable multi line mode we want to prevent the tab
|
|
5606 * traversal and receive the tab key event instead.
|
|
5607 *
|
|
5608 * @param event the event
|
|
5609 */
|
|
5610 void handleTraverse(Event event) {
|
|
5611 switch (event.detail) {
|
|
5612 case SWT.TRAVERSE_ESCAPE:
|
|
5613 case SWT.TRAVERSE_PAGE_NEXT:
|
|
5614 case SWT.TRAVERSE_PAGE_PREVIOUS:
|
|
5615 event.doit = true;
|
|
5616 break;
|
|
5617 case SWT.TRAVERSE_RETURN:
|
|
5618 case SWT.TRAVERSE_TAB_NEXT:
|
|
5619 case SWT.TRAVERSE_TAB_PREVIOUS:
|
|
5620 if ((getStyle() & SWT.SINGLE) !is 0) {
|
|
5621 event.doit = true;
|
|
5622 } else {
|
|
5623 if (!editable || (event.stateMask & SWT.MODIFIER_MASK) !is 0) {
|
|
5624 event.doit = true;
|
|
5625 }
|
|
5626 }
|
|
5627 break;
|
|
5628 default:
|
|
5629 }
|
|
5630 }
|
|
5631 /**
|
|
5632 * Scrolls the widget vertically.
|
|
5633 */
|
|
5634 void handleVerticalScroll(Event event) {
|
|
5635 int scrollPixel = getVerticalBar().getSelection() - getVerticalScrollOffset();
|
|
5636 scrollVertical(scrollPixel, false);
|
|
5637 }
|
|
5638 /**
|
|
5639 * Add accessibility support for the widget.
|
|
5640 */
|
|
5641 void initializeAccessible() {
|
|
5642 final Accessible accessible = getAccessible();
|
|
5643 accessible.addAccessibleListener(new class() AccessibleAdapter {
|
|
5644 public void getName (AccessibleEvent e) {
|
|
5645 String name = null;
|
|
5646 Label label = getAssociatedLabel ();
|
|
5647 if (label !is null) {
|
|
5648 name = stripMnemonic (label.getText());
|
|
5649 }
|
|
5650 e.result = name;
|
|
5651 }
|
|
5652 public void getHelp(AccessibleEvent e) {
|
|
5653 e.result = getToolTipText();
|
|
5654 }
|
|
5655 public void getKeyboardShortcut(AccessibleEvent e) {
|
|
5656 String shortcut = null;
|
|
5657 Label label = getAssociatedLabel ();
|
|
5658 if (label !is null) {
|
|
5659 String text = label.getText ();
|
|
5660 if (text !is null) {
|
|
5661 dchar mnemonic = _findMnemonic (text);
|
|
5662 if (mnemonic !is '\0') {
|
|
5663 shortcut = "Alt+"~tango.text.convert.Utf.toString( [mnemonic] ); //$NON-NLS-1$
|
|
5664 }
|
|
5665 }
|
|
5666 }
|
|
5667 e.result = shortcut;
|
|
5668 }
|
|
5669 });
|
|
5670 accessible.addAccessibleTextListener(new class() AccessibleTextAdapter {
|
|
5671 public void getCaretOffset(AccessibleTextEvent e) {
|
|
5672 e.offset = this.outer.getCaretOffset();
|
|
5673 }
|
|
5674 public void getSelectionRange(AccessibleTextEvent e) {
|
|
5675 Point selection = this.outer.getSelectionRange();
|
|
5676 e.offset = selection.x;
|
|
5677 e.length = selection.y;
|
|
5678 }
|
|
5679 });
|
|
5680 accessible.addAccessibleControlListener(new class() AccessibleControlAdapter {
|
|
5681 public void getRole(AccessibleControlEvent e) {
|
|
5682 e.detail = ACC.ROLE_TEXT;
|
|
5683 }
|
|
5684 public void getState(AccessibleControlEvent e) {
|
|
5685 int state = 0;
|
|
5686 if (isEnabled()) state |= ACC.STATE_FOCUSABLE;
|
|
5687 if (isFocusControl()) state |= ACC.STATE_FOCUSED;
|
|
5688 if (!isVisible()) state |= ACC.STATE_INVISIBLE;
|
|
5689 if (!getEditable()) state |= ACC.STATE_READONLY;
|
|
5690 e.detail = state;
|
|
5691 }
|
|
5692 public void getValue(AccessibleControlEvent e) {
|
|
5693 e.result = this.outer.getText();
|
|
5694 }
|
|
5695 });
|
|
5696 addListener(SWT.FocusIn, new class(accessible) Listener {
|
|
5697 Accessible acc;
|
|
5698 this( Accessible acc ){ this.acc = acc; }
|
|
5699 public void handleEvent(Event event) {
|
|
5700 acc.setFocus(ACC.CHILDID_SELF);
|
|
5701 }
|
|
5702 });
|
|
5703 }
|
|
5704 /*
|
|
5705 * Return the Label immediately preceding the receiver in the z-order,
|
|
5706 * or null if none.
|
|
5707 */
|
|
5708 Label getAssociatedLabel () {
|
|
5709 Control[] siblings = getParent ().getChildren ();
|
|
5710 for (int i = 0; i < siblings.length; i++) {
|
|
5711 if (siblings [i] is this) {
|
|
5712 if (i > 0 && ( null !is cast(Label)siblings [i-1])) {
|
|
5713 return cast(Label) siblings [i-1];
|
|
5714 }
|
|
5715 }
|
|
5716 }
|
|
5717 return null;
|
|
5718 }
|
|
5719 String stripMnemonic (String string) {
|
|
5720 int index = 0;
|
|
5721 int length_ = string.length;
|
|
5722 do {
|
|
5723 while ((index < length_) && (string[index] !is '&')) index++;
|
|
5724 if (++index >= length_) return string;
|
|
5725 if (string[index] !is '&') {
|
|
5726 return string.substring(0, index-1) ~ string.substring(index, length_);
|
|
5727 }
|
|
5728 index++;
|
|
5729 } while (index < length_);
|
|
5730 return string;
|
|
5731 }
|
|
5732 /*
|
|
5733 * Return the lowercase of the first non-'&' character following
|
|
5734 * an '&' character in the given string. If there are no '&'
|
|
5735 * characters in the given string, return '\0'.
|
|
5736 */
|
|
5737 dchar _findMnemonic (String string) {
|
|
5738 if (string is null) return '\0';
|
|
5739 int index = 0;
|
|
5740 int length_ = string.length;
|
|
5741 do {
|
|
5742 while (index < length_ && string[index] !is '&') index++;
|
|
5743 if (++index >= length_) return '\0';
|
|
5744 if (string[index] !is '&') return CharacterFirstToLower(string[index .. $ ] );
|
|
5745 index++;
|
|
5746 } while (index < length_);
|
|
5747 return '\0';
|
|
5748 }
|
|
5749 /**
|
|
5750 * Executes the action.
|
|
5751 *
|
|
5752 * @param action one of the actions defined in ST.java
|
|
5753 */
|
|
5754 public void invokeAction(int action) {
|
|
5755 checkWidget();
|
|
5756 updateCaretDirection = true;
|
|
5757 switch (action) {
|
|
5758 // Navigation
|
|
5759 case ST.LINE_UP:
|
|
5760 doLineUp(false);
|
|
5761 clearSelection(true);
|
|
5762 break;
|
|
5763 case ST.LINE_DOWN:
|
|
5764 doLineDown(false);
|
|
5765 clearSelection(true);
|
|
5766 break;
|
|
5767 case ST.LINE_START:
|
|
5768 doLineStart();
|
|
5769 clearSelection(true);
|
|
5770 break;
|
|
5771 case ST.LINE_END:
|
|
5772 doLineEnd();
|
|
5773 clearSelection(true);
|
|
5774 break;
|
|
5775 case ST.COLUMN_PREVIOUS:
|
|
5776 doCursorPrevious();
|
|
5777 clearSelection(true);
|
|
5778 break;
|
|
5779 case ST.COLUMN_NEXT:
|
|
5780 doCursorNext();
|
|
5781 clearSelection(true);
|
|
5782 break;
|
|
5783 case ST.PAGE_UP:
|
|
5784 doPageUp(false, -1);
|
|
5785 clearSelection(true);
|
|
5786 break;
|
|
5787 case ST.PAGE_DOWN:
|
|
5788 doPageDown(false, -1);
|
|
5789 clearSelection(true);
|
|
5790 break;
|
|
5791 case ST.WORD_PREVIOUS:
|
|
5792 doWordPrevious();
|
|
5793 clearSelection(true);
|
|
5794 break;
|
|
5795 case ST.WORD_NEXT:
|
|
5796 doWordNext();
|
|
5797 clearSelection(true);
|
|
5798 break;
|
|
5799 case ST.TEXT_START:
|
|
5800 doContentStart();
|
|
5801 clearSelection(true);
|
|
5802 break;
|
|
5803 case ST.TEXT_END:
|
|
5804 doContentEnd();
|
|
5805 clearSelection(true);
|
|
5806 break;
|
|
5807 case ST.WINDOW_START:
|
|
5808 doPageStart();
|
|
5809 clearSelection(true);
|
|
5810 break;
|
|
5811 case ST.WINDOW_END:
|
|
5812 doPageEnd();
|
|
5813 clearSelection(true);
|
|
5814 break;
|
|
5815 // Selection
|
|
5816 case ST.SELECT_LINE_UP:
|
|
5817 doSelectionLineUp();
|
|
5818 break;
|
|
5819 case ST.SELECT_ALL:
|
|
5820 selectAll();
|
|
5821 break;
|
|
5822 case ST.SELECT_LINE_DOWN:
|
|
5823 doSelectionLineDown();
|
|
5824 break;
|
|
5825 case ST.SELECT_LINE_START:
|
|
5826 doLineStart();
|
|
5827 doSelection(ST.COLUMN_PREVIOUS);
|
|
5828 break;
|
|
5829 case ST.SELECT_LINE_END:
|
|
5830 doLineEnd();
|
|
5831 doSelection(ST.COLUMN_NEXT);
|
|
5832 break;
|
|
5833 case ST.SELECT_COLUMN_PREVIOUS:
|
|
5834 doSelectionCursorPrevious();
|
|
5835 doSelection(ST.COLUMN_PREVIOUS);
|
|
5836 break;
|
|
5837 case ST.SELECT_COLUMN_NEXT:
|
|
5838 doSelectionCursorNext();
|
|
5839 doSelection(ST.COLUMN_NEXT);
|
|
5840 break;
|
|
5841 case ST.SELECT_PAGE_UP:
|
|
5842 doSelectionPageUp(-1);
|
|
5843 break;
|
|
5844 case ST.SELECT_PAGE_DOWN:
|
|
5845 doSelectionPageDown(-1);
|
|
5846 break;
|
|
5847 case ST.SELECT_WORD_PREVIOUS:
|
|
5848 doSelectionWordPrevious();
|
|
5849 doSelection(ST.COLUMN_PREVIOUS);
|
|
5850 break;
|
|
5851 case ST.SELECT_WORD_NEXT:
|
|
5852 doSelectionWordNext();
|
|
5853 doSelection(ST.COLUMN_NEXT);
|
|
5854 break;
|
|
5855 case ST.SELECT_TEXT_START:
|
|
5856 doContentStart();
|
|
5857 doSelection(ST.COLUMN_PREVIOUS);
|
|
5858 break;
|
|
5859 case ST.SELECT_TEXT_END:
|
|
5860 doContentEnd();
|
|
5861 doSelection(ST.COLUMN_NEXT);
|
|
5862 break;
|
|
5863 case ST.SELECT_WINDOW_START:
|
|
5864 doPageStart();
|
|
5865 doSelection(ST.COLUMN_PREVIOUS);
|
|
5866 break;
|
|
5867 case ST.SELECT_WINDOW_END:
|
|
5868 doPageEnd();
|
|
5869 doSelection(ST.COLUMN_NEXT);
|
|
5870 break;
|
|
5871 // Modification
|
|
5872 case ST.CUT:
|
|
5873 cut();
|
|
5874 break;
|
|
5875 case ST.COPY:
|
|
5876 copy();
|
|
5877 break;
|
|
5878 case ST.PASTE:
|
|
5879 paste();
|
|
5880 break;
|
|
5881 case ST.DELETE_PREVIOUS:
|
|
5882 doBackspace();
|
|
5883 break;
|
|
5884 case ST.DELETE_NEXT:
|
|
5885 doDelete();
|
|
5886 break;
|
|
5887 case ST.DELETE_WORD_PREVIOUS:
|
|
5888 doDeleteWordPrevious();
|
|
5889 break;
|
|
5890 case ST.DELETE_WORD_NEXT:
|
|
5891 doDeleteWordNext();
|
|
5892 break;
|
|
5893 // Miscellaneous
|
|
5894 case ST.TOGGLE_OVERWRITE:
|
|
5895 overwrite = !overwrite; // toggle insert/overwrite mode
|
|
5896 break;
|
|
5897 default:
|
|
5898 }
|
|
5899 }
|
|
5900 /**
|
|
5901 * Temporary until SWT provides this
|
|
5902 */
|
|
5903 bool isBidi() {
|
|
5904 return IS_GTK || IS_CARBON || BidiUtil.isBidiPlatform() || isMirrored_;
|
|
5905 }
|
|
5906 bool isBidiCaret() {
|
|
5907 return BidiUtil.isBidiPlatform();
|
|
5908 }
|
|
5909 bool isFixedLineHeight() {
|
|
5910 return fixedLineHeight;
|
|
5911 }
|
|
5912 /**
|
|
5913 * Returns whether the given offset is inside a multi byte line delimiter.
|
|
5914 * Example:
|
|
5915 * "Line1\r\n" isLineDelimiter(5) is false but isLineDelimiter(6) is true
|
|
5916 *
|
|
5917 * @return true if the given offset is inside a multi byte line delimiter.
|
|
5918 * false if the given offset is before or after a line delimiter.
|
|
5919 */
|
|
5920 bool isLineDelimiter(int offset) {
|
|
5921 int line = content.getLineAtOffset(offset);
|
|
5922 int lineOffset = content.getOffsetAtLine(line);
|
|
5923 int offsetInLine = offset - lineOffset;
|
|
5924 // offsetInLine will be greater than line length if the line
|
|
5925 // delimiter is longer than one character and the offset is set
|
|
5926 // in between parts of the line delimiter.
|
|
5927 return offsetInLine > content.getLine(line).length;
|
|
5928 }
|
|
5929 /**
|
|
5930 * Returns whether the widget is mirrored (right oriented/right to left
|
|
5931 * writing order).
|
|
5932 *
|
|
5933 * @return isMirrored true=the widget is right oriented, false=the widget
|
|
5934 * is left oriented
|
|
5935 */
|
|
5936 bool isMirrored() {
|
|
5937 return isMirrored_;
|
|
5938 }
|
|
5939 /**
|
|
5940 * Returns whether the widget can have only one line.
|
|
5941 *
|
|
5942 * @return true if widget can have only one line, false if widget can have
|
|
5943 * multiple lines
|
|
5944 */
|
|
5945 bool isSingleLine() {
|
|
5946 return (getStyle() & SWT.SINGLE) !is 0;
|
|
5947 }
|
|
5948 /**
|
|
5949 * Sends the specified verify event, replace/insert text as defined by
|
|
5950 * the event and send a modify event.
|
|
5951 *
|
|
5952 * @param event the text change event.
|
|
5953 * <ul>
|
|
5954 * <li>event.start - the replace start offset</li>
|
|
5955 * <li>event.end - the replace end offset</li>
|
|
5956 * <li>event.text - the new text</li>
|
|
5957 * </ul>
|
|
5958 * @param updateCaret whether or not he caret should be set behind
|
|
5959 * the new text
|
|
5960 */
|
|
5961 void modifyContent(Event event, bool updateCaret) {
|
|
5962 event.doit = true;
|
|
5963 notifyListeners(SWT.Verify, event);
|
|
5964 if (event.doit) {
|
|
5965 StyledTextEvent styledTextEvent = null;
|
|
5966 int replacedLength = event.end - event.start;
|
|
5967 if (isListening(ExtendedModify)) {
|
|
5968 styledTextEvent = new StyledTextEvent(content);
|
|
5969 styledTextEvent.start = event.start;
|
|
5970 styledTextEvent.end = event.start + event.text.length;
|
|
5971 styledTextEvent.text = content.getTextRange(event.start, replacedLength);
|
|
5972 }
|
|
5973 if (updateCaret) {
|
|
5974 //Fix advancing flag for delete/backspace key on direction boundary
|
|
5975 if (event.text.length is 0) {
|
|
5976 int lineIndex = content.getLineAtOffset(event.start);
|
|
5977 int lineOffset = content.getOffsetAtLine(lineIndex);
|
|
5978 TextLayout layout = renderer.getTextLayout(lineIndex);
|
|
5979 int levelStart = layout.getLevel(event.start - lineOffset);
|
|
5980 int lineIndexEnd = content.getLineAtOffset(event.end);
|
|
5981 if (lineIndex !is lineIndexEnd) {
|
|
5982 renderer.disposeTextLayout(layout);
|
|
5983 lineOffset = content.getOffsetAtLine(lineIndexEnd);
|
|
5984 layout = renderer.getTextLayout(lineIndexEnd);
|
|
5985 }
|
|
5986 int levelEnd = layout.getLevel(event.end - lineOffset);
|
|
5987 renderer.disposeTextLayout(layout);
|
|
5988 if (levelStart !is levelEnd) {
|
|
5989 caretAlignment = PREVIOUS_OFFSET_TRAILING;
|
|
5990 } else {
|
|
5991 caretAlignment = OFFSET_LEADING;
|
|
5992 }
|
|
5993 }
|
|
5994 }
|
|
5995 content.replaceTextRange(event.start, replacedLength, event.text);
|
|
5996 // set the caret position prior to sending the modify event.
|
|
5997 // fixes 1GBB8NJ
|
|
5998 if (updateCaret) {
|
|
5999 // always update the caret location. fixes 1G8FODP
|
|
6000 setSelection(event.start + event.text.length, 0, true);
|
|
6001 showCaret();
|
|
6002 }
|
|
6003 sendModifyEvent(event);
|
|
6004 if (isListening(ExtendedModify)) {
|
|
6005 notifyListeners(ExtendedModify, styledTextEvent);
|
|
6006 }
|
|
6007 }
|
|
6008 }
|
|
6009 void paintObject(GC gc, int x, int y, int ascent, int descent, StyleRange style, Bullet bullet, int bulletIndex) {
|
|
6010 if (isListening(PaintObject)) {
|
|
6011 StyledTextEvent event = new StyledTextEvent (content) ;
|
|
6012 event.gc = gc;
|
|
6013 event.x = x;
|
|
6014 event.y = y;
|
|
6015 event.ascent = ascent;
|
|
6016 event.descent = descent;
|
|
6017 event.style = style;
|
|
6018 event.bullet = bullet;
|
|
6019 event.bulletIndex = bulletIndex;
|
|
6020 notifyListeners(PaintObject, event);
|
|
6021 }
|
|
6022 }
|
|
6023 /**
|
|
6024 * Replaces the selection with the text on the <code>DND.CLIPBOARD</code>
|
|
6025 * clipboard or, if there is no selection, inserts the text at the current
|
|
6026 * caret offset. If the widget has the SWT.SINGLE style and the
|
|
6027 * clipboard text contains more than one line, only the first line without
|
|
6028 * line delimiters is inserted in the widget.
|
|
6029 *
|
|
6030 * @exception SWTException <ul>
|
|
6031 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
6032 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
6033 * </ul>
|
|
6034 */
|
|
6035 public void paste(){
|
|
6036 checkWidget();
|
|
6037 String text = null;
|
|
6038 if( auto o = cast(ArrayWrapperString) getClipboardContent(DND.CLIPBOARD)){
|
|
6039 text = o.array;
|
|
6040 }
|
|
6041 if (text !is null && text.length > 0) {
|
|
6042 Event event = new Event();
|
|
6043 event.start = selection.x;
|
|
6044 event.end = selection.y;
|
|
6045 event.text = getModelDelimitedText(text);
|
|
6046 sendKeyEvent(event);
|
|
6047 }
|
|
6048 }
|
|
6049 /**
|
|
6050 * Prints the widget's text to the default printer.
|
|
6051 *
|
|
6052 * @exception SWTException <ul>
|
|
6053 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
6054 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
6055 * </ul>
|
|
6056 */
|
|
6057 public void print() {
|
|
6058 checkWidget();
|
|
6059 Printer printer = new Printer();
|
|
6060 StyledTextPrintOptions options = new StyledTextPrintOptions();
|
|
6061 options.printTextForeground = true;
|
|
6062 options.printTextBackground = true;
|
|
6063 options.printTextFontStyle = true;
|
|
6064 options.printLineBackground = true;
|
|
6065 (new Printing(this, printer, options)).run();
|
|
6066 printer.dispose();
|
|
6067 }
|
|
6068 /**
|
|
6069 * Returns a runnable that will print the widget's text
|
|
6070 * to the specified printer.
|
|
6071 * <p>
|
|
6072 * The runnable may be run in a non-UI thread.
|
|
6073 * </p>
|
|
6074 *
|
|
6075 * @param printer the printer to print to
|
|
6076 *
|
|
6077 * @return a <code>Runnable</code> for printing the receiver's text
|
|
6078 *
|
|
6079 * @exception SWTException <ul>
|
|
6080 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
6081 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
6082 * </ul>
|
|
6083 * @exception IllegalArgumentException <ul>
|
|
6084 * <li>ERROR_NULL_ARGUMENT when printer is null</li>
|
|
6085 * </ul>
|
|
6086 */
|
|
6087 public Runnable print(Printer printer) {
|
|
6088 checkWidget();
|
|
6089 if (printer is null) {
|
|
6090 SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
6091 }
|
|
6092 StyledTextPrintOptions options = new StyledTextPrintOptions();
|
|
6093 options.printTextForeground = true;
|
|
6094 options.printTextBackground = true;
|
|
6095 options.printTextFontStyle = true;
|
|
6096 options.printLineBackground = true;
|
|
6097 return print(printer, options);
|
|
6098 }
|
|
6099 /**
|
|
6100 * Returns a runnable that will print the widget's text
|
|
6101 * to the specified printer.
|
|
6102 * <p>
|
|
6103 * The runnable may be run in a non-UI thread.
|
|
6104 * </p>
|
|
6105 *
|
|
6106 * @param printer the printer to print to
|
|
6107 * @param options print options to use during printing
|
|
6108 *
|
|
6109 * @return a <code>Runnable</code> for printing the receiver's text
|
|
6110 *
|
|
6111 * @exception SWTException <ul>
|
|
6112 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
6113 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
6114 * </ul>
|
|
6115 * @exception IllegalArgumentException <ul>
|
|
6116 * <li>ERROR_NULL_ARGUMENT when printer or options is null</li>
|
|
6117 * </ul>
|
|
6118 * @since 2.1
|
|
6119 */
|
|
6120 public Runnable print(Printer printer, StyledTextPrintOptions options) {
|
|
6121 checkWidget();
|
|
6122 if (printer is null || options is null) {
|
|
6123 SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
6124 }
|
|
6125 return new Printing(this, printer, options);
|
|
6126 }
|
|
6127 /**
|
|
6128 * Causes the entire bounds of the receiver to be marked
|
|
6129 * as needing to be redrawn. The next time a paint request
|
|
6130 * is processed, the control will be completely painted.
|
|
6131 * <p>
|
|
6132 * Recalculates the content width for all lines in the bounds.
|
|
6133 * When a <code>LineStyleListener</code> is used a redraw call
|
|
6134 * is the only notification to the widget that styles have changed
|
|
6135 * and that the content width may have changed.
|
|
6136 * </p>
|
|
6137 *
|
|
6138 * @exception SWTException <ul>
|
|
6139 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
6140 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
6141 * </ul>
|
|
6142 *
|
|
6143 * @see Control#update()
|
|
6144 */
|
|
6145 public override void redraw() {
|
|
6146 super.redraw();
|
|
6147 int itemCount = getPartialBottomIndex() - topIndex + 1;
|
|
6148 renderer.reset(topIndex, itemCount);
|
|
6149 renderer.calculate(topIndex, itemCount);
|
|
6150 setScrollBars(false);
|
|
6151 }
|
|
6152 /**
|
|
6153 * Causes the rectangular area of the receiver specified by
|
|
6154 * the arguments to be marked as needing to be redrawn.
|
|
6155 * The next time a paint request is processed, that area of
|
|
6156 * the receiver will be painted. If the <code>all</code> flag
|
|
6157 * is <code>true</code>, any children of the receiver which
|
|
6158 * intersect with the specified area will also paint their
|
|
6159 * intersecting areas. If the <code>all</code> flag is
|
|
6160 * <code>false</code>, the children will not be painted.
|
|
6161 * <p>
|
|
6162 * Marks the content width of all lines in the specified rectangle
|
|
6163 * as unknown. Recalculates the content width of all visible lines.
|
|
6164 * When a <code>LineStyleListener</code> is used a redraw call
|
|
6165 * is the only notification to the widget that styles have changed
|
|
6166 * and that the content width may have changed.
|
|
6167 * </p>
|
|
6168 *
|
|
6169 * @param x the x coordinate of the area to draw
|
|
6170 * @param y the y coordinate of the area to draw
|
|
6171 * @param width the width of the area to draw
|
|
6172 * @param height the height of the area to draw
|
|
6173 * @param all <code>true</code> if children should redraw, and <code>false</code> otherwise
|
|
6174 *
|
|
6175 * @exception SWTException <ul>
|
|
6176 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
6177 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
6178 * </ul>
|
|
6179 *
|
|
6180 * @see Control#update()
|
|
6181 */
|
|
6182 public override void redraw(int x, int y, int width, int height, bool all) {
|
|
6183 super.redraw(x, y, width, height, all);
|
|
6184 if (height > 0) {
|
|
6185 int firstLine = getLineIndex(y);
|
|
6186 int lastLine = getLineIndex(y + height);
|
|
6187 resetCache(firstLine, lastLine - firstLine + 1);
|
|
6188 }
|
|
6189 }
|
|
6190 void redrawLines(int startLine, int lineCount) {
|
|
6191 // do nothing if redraw range is completely invisible
|
|
6192 int partialBottomIndex = getPartialBottomIndex();
|
|
6193 if (startLine > partialBottomIndex || startLine + lineCount - 1 < topIndex) {
|
|
6194 return;
|
|
6195 }
|
|
6196 // only redraw visible lines
|
|
6197 if (startLine < topIndex) {
|
|
6198 lineCount -= topIndex - startLine;
|
|
6199 startLine = topIndex;
|
|
6200 }
|
|
6201 if (startLine + lineCount - 1 > partialBottomIndex) {
|
|
6202 lineCount = partialBottomIndex - startLine + 1;
|
|
6203 }
|
|
6204 startLine -= topIndex;
|
|
6205 int redrawTop = getLinePixel(startLine);
|
|
6206 int redrawBottom = getLinePixel(startLine + lineCount);
|
|
6207 int redrawWidth = clientAreaWidth - leftMargin - rightMargin;
|
|
6208 super.redraw(leftMargin, redrawTop, redrawWidth, redrawBottom - redrawTop, true);
|
|
6209 }
|
|
6210 void redrawLinesBullet (int[] redrawLines) {
|
|
6211 if (redrawLines is null) return;
|
|
6212 int topIndex = getPartialTopIndex();
|
|
6213 int bottomIndex = getPartialBottomIndex();
|
|
6214 for (int i = 0; i < redrawLines.length; i++) {
|
|
6215 int lineIndex = redrawLines[i];
|
|
6216 if (!(topIndex <= lineIndex && lineIndex <= bottomIndex)) continue;
|
|
6217 int width = -1;
|
|
6218 Bullet bullet = renderer.getLineBullet(lineIndex, null);
|
|
6219 if (bullet !is null) {
|
|
6220 StyleRange style = bullet.style;
|
|
6221 GlyphMetrics metrics = style.metrics;
|
|
6222 width = metrics.width;
|
|
6223 }
|
|
6224 if (width is -1) width = getClientArea().width;
|
|
6225 int height = renderer.getLineHeight(lineIndex);
|
|
6226 int y = getLinePixel(lineIndex);
|
|
6227 super.redraw(0, y, width, height, false);
|
|
6228 }
|
|
6229 }
|
|
6230 /**
|
|
6231 * Redraws the specified text range.
|
|
6232 *
|
|
6233 * @param start offset of the first character to redraw
|
|
6234 * @param length number of characters to redraw
|
|
6235 * @param clearBackground true if the background should be cleared as
|
|
6236 * part of the redraw operation. If true, the entire redraw range will
|
|
6237 * be cleared before anything is redrawn. If the redraw range includes
|
|
6238 * the last character of a line (i.e., the entire line is redrawn) the
|
|
6239 * line is cleared all the way to the right border of the widget.
|
|
6240 * The redraw operation will be faster and smoother if clearBackground
|
|
6241 * is set to false. Whether or not the flag can be set to false depends
|
|
6242 * on the type of change that has taken place. If font styles or
|
|
6243 * background colors for the redraw range have changed, clearBackground
|
|
6244 * should be set to true. If only foreground colors have changed for
|
|
6245 * the redraw range, clearBackground can be set to false.
|
|
6246 * @exception SWTException <ul>
|
|
6247 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
6248 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
6249 * </ul>
|
|
6250 * @exception IllegalArgumentException <ul>
|
|
6251 * <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
|
|
6252 * </ul>
|
|
6253 */
|
|
6254 public void redrawRange(int start, int length, bool clearBackground) {
|
|
6255 checkWidget();
|
|
6256 int end = start + length;
|
|
6257 int contentLength = content.getCharCount();
|
|
6258 if (start > end || start < 0 || end > contentLength) {
|
|
6259 SWT.error(SWT.ERROR_INVALID_RANGE);
|
|
6260 }
|
|
6261 int firstLine = content.getLineAtOffset(start);
|
|
6262 int lastLine = content.getLineAtOffset(end);
|
|
6263 resetCache(firstLine, lastLine - firstLine + 1);
|
|
6264 internalRedrawRange(start, length);
|
|
6265 }
|
|
6266 /**
|
|
6267 * Removes the specified bidirectional segment listener.
|
|
6268 *
|
|
6269 * @param listener the listener which should no longer be notified
|
|
6270 *
|
|
6271 * @exception SWTException <ul>
|
|
6272 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
6273 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
6274 * </ul>
|
|
6275 * @exception IllegalArgumentException <ul>
|
|
6276 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
|
|
6277 * </ul>
|
|
6278 *
|
|
6279 * @since 2.0
|
|
6280 */
|
|
6281 public void removeBidiSegmentListener(BidiSegmentListener listener) {
|
|
6282 checkWidget();
|
|
6283 if (listener is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
6284 removeListener(LineGetSegments, listener);
|
|
6285 }
|
|
6286 /**
|
|
6287 * Removes the specified extended modify listener.
|
|
6288 *
|
|
6289 * @param extendedModifyListener the listener which should no longer be notified
|
|
6290 *
|
|
6291 * @exception SWTException <ul>
|
|
6292 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
6293 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
6294 * </ul>
|
|
6295 * @exception IllegalArgumentException <ul>
|
|
6296 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
|
|
6297 * </ul>
|
|
6298 */
|
|
6299 public void removeExtendedModifyListener(ExtendedModifyListener extendedModifyListener) {
|
|
6300 checkWidget();
|
|
6301 if (extendedModifyListener is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
6302 removeListener(ExtendedModify, extendedModifyListener);
|
|
6303 }
|
|
6304 /**
|
|
6305 * Removes the specified line background listener.
|
|
6306 *
|
|
6307 * @param listener the listener which should no longer be notified
|
|
6308 *
|
|
6309 * @exception SWTException <ul>
|
|
6310 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
6311 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
6312 * </ul>
|
|
6313 * @exception IllegalArgumentException <ul>
|
|
6314 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
|
|
6315 * </ul>
|
|
6316 */
|
|
6317 public void removeLineBackgroundListener(LineBackgroundListener listener) {
|
|
6318 checkWidget();
|
|
6319 if (listener is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
6320 removeListener(LineGetBackground, listener);
|
|
6321 }
|
|
6322 /**
|
|
6323 * Removes the specified line style listener.
|
|
6324 *
|
|
6325 * @param listener the listener which should no longer be notified
|
|
6326 *
|
|
6327 * @exception SWTException <ul>
|
|
6328 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
6329 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
6330 * </ul>
|
|
6331 * @exception IllegalArgumentException <ul>
|
|
6332 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
|
|
6333 * </ul>
|
|
6334 */
|
|
6335 public void removeLineStyleListener(LineStyleListener listener) {
|
|
6336 checkWidget();
|
|
6337 if (listener is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
6338 removeListener(LineGetStyle, listener);
|
|
6339 }
|
|
6340 /**
|
|
6341 * Removes the specified modify listener.
|
|
6342 *
|
|
6343 * @param modifyListener the listener which should no longer be notified
|
|
6344 *
|
|
6345 * @exception SWTException <ul>
|
|
6346 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
6347 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
6348 * </ul>
|
|
6349 * @exception IllegalArgumentException <ul>
|
|
6350 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
|
|
6351 * </ul>
|
|
6352 */
|
|
6353 public void removeModifyListener(ModifyListener modifyListener) {
|
|
6354 checkWidget();
|
|
6355 if (modifyListener is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
6356 removeListener(SWT.Modify, modifyListener);
|
|
6357 }
|
|
6358 /**
|
|
6359 * Removes the specified listener.
|
|
6360 *
|
|
6361 * @param listener the listener which should no longer be notified
|
|
6362 *
|
|
6363 * @exception SWTException <ul>
|
|
6364 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
6365 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
6366 * </ul>
|
|
6367 * @exception IllegalArgumentException <ul>
|
|
6368 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
|
|
6369 * </ul>
|
|
6370 * @since 3.2
|
|
6371 */
|
|
6372 public void removePaintObjectListener(PaintObjectListener listener) {
|
|
6373 checkWidget();
|
|
6374 if (listener is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
6375 removeListener(PaintObject, listener);
|
|
6376 }
|
|
6377 /**
|
|
6378 * Removes the listener from the collection of listeners who will
|
|
6379 * be notified when the user changes the receiver's selection.
|
|
6380 *
|
|
6381 * @param listener the listener which should no longer be notified
|
|
6382 *
|
|
6383 * @exception IllegalArgumentException <ul>
|
|
6384 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
|
|
6385 * </ul>
|
|
6386 * @exception SWTException <ul>
|
|
6387 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
6388 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
6389 * </ul>
|
|
6390 *
|
|
6391 * @see SelectionListener
|
|
6392 * @see #addSelectionListener
|
|
6393 */
|
|
6394 public void removeSelectionListener(SelectionListener listener) {
|
|
6395 checkWidget();
|
|
6396 if (listener is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
6397 removeListener(SWT.Selection, listener);
|
|
6398 }
|
|
6399 /**
|
|
6400 * Removes the specified verify listener.
|
|
6401 *
|
|
6402 * @param verifyListener the listener which should no longer be notified
|
|
6403 *
|
|
6404 * @exception SWTException <ul>
|
|
6405 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
6406 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
6407 * </ul>
|
|
6408 * @exception IllegalArgumentException <ul>
|
|
6409 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
|
|
6410 * </ul>
|
|
6411 */
|
|
6412 public void removeVerifyListener(VerifyListener verifyListener) {
|
|
6413 checkWidget();
|
|
6414 if (verifyListener is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
6415 removeListener(SWT.Verify, verifyListener);
|
|
6416 }
|
|
6417 /**
|
|
6418 * Removes the specified key verify listener.
|
|
6419 *
|
|
6420 * @param listener the listener which should no longer be notified
|
|
6421 *
|
|
6422 * @exception SWTException <ul>
|
|
6423 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
6424 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
6425 * </ul>
|
|
6426 * @exception IllegalArgumentException <ul>
|
|
6427 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
|
|
6428 * </ul>
|
|
6429 */
|
|
6430 public void removeVerifyKeyListener(VerifyKeyListener listener) {
|
|
6431 if (listener is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
6432 removeListener(VerifyKey, listener);
|
|
6433 }
|
|
6434 /**
|
|
6435 * Removes the specified word movement listener.
|
|
6436 *
|
|
6437 * @param listener the listener which should no longer be notified
|
|
6438 *
|
|
6439 * @exception SWTException <ul>
|
|
6440 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
6441 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
6442 * </ul>
|
|
6443 * @exception IllegalArgumentException <ul>
|
|
6444 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
|
|
6445 * </ul>
|
|
6446 *
|
|
6447 * @see MovementEvent
|
|
6448 * @see MovementListener
|
|
6449 * @see #addWordMovementListener
|
|
6450 *
|
|
6451 * @since 3.3
|
|
6452 */
|
|
6453
|
|
6454 public void removeWordMovementListener(MovementListener listener) {
|
|
6455 checkWidget();
|
|
6456 if (listener is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
6457 removeListener(WordNext, listener);
|
|
6458 removeListener(WordPrevious, listener);
|
|
6459 }
|
|
6460 /**
|
|
6461 * Replaces the styles in the given range with new styles. This method
|
|
6462 * effectively deletes the styles in the given range and then adds the
|
|
6463 * the new styles.
|
|
6464 * <p>
|
|
6465 * Note: Because a StyleRange includes the start and length, the
|
|
6466 * same instance cannot occur multiple times in the array of styles.
|
|
6467 * If the same style attributes, such as font and color, occur in
|
|
6468 * multiple StyleRanges, <code>setStyleRanges(int, int, int[], StyleRange[])</code>
|
|
6469 * can be used to share styles and reduce memory usage.
|
|
6470 * </p><p>
|
|
6471 * Should not be called if a LineStyleListener has been set since the
|
|
6472 * listener maintains the styles.
|
|
6473 * </p>
|
|
6474 *
|
|
6475 * @param start offset of first character where styles will be deleted
|
|
6476 * @param length length of the range to delete styles in
|
|
6477 * @param ranges StyleRange objects containing the new style information.
|
|
6478 * The ranges should not overlap and should be within the specified start
|
|
6479 * and length. The style rendering is undefined if the ranges do overlap
|
|
6480 * or are ill-defined. Must not be null.
|
|
6481 * @exception SWTException <ul>
|
|
6482 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
6483 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
6484 * </ul>
|
|
6485 * @exception IllegalArgumentException <ul>
|
|
6486 * <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 <= offset <= getCharCount())</li>
|
|
6487 * </ul>
|
|
6488 *
|
|
6489 * @since 2.0
|
|
6490 *
|
|
6491 * @see #setStyleRanges(int, int, int[], StyleRange[])
|
|
6492 */
|
|
6493 public void replaceStyleRanges(int start, int length, StyleRange[] ranges) {
|
|
6494 checkWidget();
|
|
6495 if (isListening(LineGetStyle)) return;
|
|
6496 // SWT extension: allow null for zero length string
|
|
6497 //if (ranges is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
6498 setStyleRanges(start, length, null, ranges, false);
|
|
6499 }
|
|
6500 /**
|
|
6501 * Replaces the given text range with new text.
|
|
6502 * If the widget has the SWT.SINGLE style and "text" contains more than
|
|
6503 * one line, only the first line is rendered but the text is stored
|
|
6504 * unchanged. A subsequent call to getText will return the same text
|
|
6505 * that was set. Note that only a single line of text should be set when
|
|
6506 * the SWT.SINGLE style is used.
|
|
6507 * <p>
|
|
6508 * <b>NOTE:</b> During the replace operation the current selection is
|
|
6509 * changed as follows:
|
|
6510 * <ul>
|
|
6511 * <li>selection before replaced text: selection unchanged
|
|
6512 * <li>selection after replaced text: adjust the selection so that same text
|
|
6513 * remains selected
|
|
6514 * <li>selection intersects replaced text: selection is cleared and caret
|
|
6515 * is placed after inserted text
|
|
6516 * </ul>
|
|
6517 * </p>
|
|
6518 *
|
|
6519 * @param start offset of first character to replace
|
|
6520 * @param length number of characters to replace. Use 0 to insert text
|
|
6521 * @param text new text. May be empty to delete text.
|
|
6522 * @exception SWTException <ul>
|
|
6523 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
6524 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
6525 * </ul>
|
|
6526 * @exception IllegalArgumentException <ul>
|
|
6527 * <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 <= offset <= getCharCount())</li>
|
|
6528 * <li>ERROR_INVALID_ARGUMENT when either start or end is inside a multi byte line delimiter.
|
|
6529 * 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>
|
|
6530 * </ul>
|
|
6531 */
|
|
6532 public void replaceTextRange(int start, int length, String text) {
|
|
6533 checkWidget();
|
|
6534 // SWT extension: allow null for zero length string
|
|
6535 // if (text is null) {
|
|
6536 // SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
6537 // }
|
|
6538 int contentLength = getCharCount();
|
|
6539 int end = start + length;
|
|
6540 if (start > end || start < 0 || end > contentLength) {
|
|
6541 SWT.error(SWT.ERROR_INVALID_RANGE);
|
|
6542 }
|
|
6543 Event event = new Event();
|
|
6544 event.start = start;
|
|
6545 event.end = end;
|
|
6546 event.text = text;
|
|
6547 modifyContent(event, false);
|
|
6548 }
|
|
6549 /**
|
|
6550 * Resets the caret position, selection and scroll offsets. Recalculate
|
|
6551 * the content width and scroll bars. Redraw the widget.
|
|
6552 */
|
|
6553 void reset() {
|
|
6554 ScrollBar verticalBar = getVerticalBar();
|
|
6555 ScrollBar horizontalBar = getHorizontalBar();
|
|
6556 caretOffset = 0;
|
|
6557 topIndex = 0;
|
|
6558 topIndexY = 0;
|
|
6559 verticalScrollOffset = 0;
|
|
6560 horizontalScrollOffset = 0;
|
|
6561 resetSelection();
|
|
6562 renderer.setContent(content);
|
|
6563 if (verticalBar !is null) {
|
|
6564 verticalBar.setSelection(0);
|
|
6565 }
|
|
6566 if (horizontalBar !is null) {
|
|
6567 horizontalBar.setSelection(0);
|
|
6568 }
|
|
6569 resetCache(0, 0);
|
|
6570 setCaretLocation();
|
|
6571 super.redraw();
|
|
6572 }
|
|
6573 void resetCache(int firstLine, int count) {
|
|
6574 int maxLineIndex = renderer.maxWidthLineIndex;
|
|
6575 renderer.reset(firstLine, count);
|
|
6576 renderer.calculateClientArea();
|
|
6577 if (0 <= maxLineIndex && maxLineIndex < content.getLineCount()) {
|
|
6578 renderer.calculate(maxLineIndex, 1);
|
|
6579 }
|
|
6580 setScrollBars(true);
|
|
6581 if (!isFixedLineHeight()) {
|
|
6582 if (topIndex > firstLine) {
|
|
6583 verticalScrollOffset = -1;
|
|
6584 }
|
|
6585 renderer.calculateIdle();
|
|
6586 }
|
|
6587 }
|
|
6588 /**
|
|
6589 * Resets the selection.
|
|
6590 */
|
|
6591 void resetSelection() {
|
|
6592 selection.x = selection.y = caretOffset;
|
|
6593 selectionAnchor = -1;
|
|
6594 }
|
|
6595
|
|
6596 public override void scroll(int destX, int destY, int x, int y, int width, int height, bool all) {
|
|
6597 super.scroll(destX, destY, x, y, width, height, false);
|
|
6598 if (all) {
|
|
6599 int deltaX = destX - x, deltaY = destY - y;
|
|
6600 Control[] children = getChildren();
|
|
6601 for (int i=0; i<children.length; i++) {
|
|
6602 Control child = children[i];
|
|
6603 Rectangle rect = child.getBounds();
|
|
6604 child.setLocation(rect.x + deltaX, rect.y + deltaY);
|
|
6605 }
|
|
6606 }
|
|
6607 }
|
|
6608
|
|
6609 /**
|
|
6610 * Scrolls the widget horizontally.
|
|
6611 *
|
|
6612 * @param pixels number of pixels to scroll, > 0 = scroll left,
|
|
6613 * < 0 scroll right
|
|
6614 * @param adjustScrollBar
|
|
6615 * true= the scroll thumb will be moved to reflect the new scroll offset.
|
|
6616 * false = the scroll thumb will not be moved
|
|
6617 * @return
|
|
6618 * true=the widget was scrolled
|
|
6619 * false=the widget was not scrolled, the given offset is not valid.
|
|
6620 */
|
|
6621 bool scrollHorizontal(int pixels, bool adjustScrollBar) {
|
|
6622 if (pixels is 0) {
|
|
6623 return false;
|
|
6624 }
|
|
6625 ScrollBar horizontalBar = getHorizontalBar();
|
|
6626 if (horizontalBar !is null && adjustScrollBar) {
|
|
6627 horizontalBar.setSelection(horizontalScrollOffset + pixels);
|
|
6628 }
|
|
6629 int scrollHeight = clientAreaHeight - topMargin - bottomMargin;
|
|
6630 if (pixels > 0) {
|
|
6631 int sourceX = leftMargin + pixels;
|
|
6632 int scrollWidth = clientAreaWidth - sourceX - rightMargin;
|
|
6633 if (scrollWidth > 0) {
|
|
6634 scroll(leftMargin, topMargin, sourceX, topMargin, scrollWidth, scrollHeight, true);
|
|
6635 }
|
|
6636 if (sourceX > scrollWidth) {
|
|
6637 super.redraw(leftMargin + scrollWidth, topMargin, pixels - scrollWidth, scrollHeight, true);
|
|
6638 }
|
|
6639 } else {
|
|
6640 int destinationX = leftMargin - pixels;
|
|
6641 int scrollWidth = clientAreaWidth - destinationX - rightMargin;
|
|
6642 if (scrollWidth > 0) {
|
|
6643 scroll(destinationX, topMargin, leftMargin, topMargin, scrollWidth, scrollHeight, true);
|
|
6644 }
|
|
6645 if (destinationX > scrollWidth) {
|
|
6646 super.redraw(leftMargin + scrollWidth, topMargin, -pixels - scrollWidth, scrollHeight, true);
|
|
6647 }
|
|
6648 }
|
|
6649 horizontalScrollOffset += pixels;
|
|
6650 setCaretLocation();
|
|
6651 return true;
|
|
6652 }
|
|
6653 /**
|
|
6654 * Scrolls the widget vertically.
|
|
6655 *
|
|
6656 * @param pixel the new vertical scroll offset
|
|
6657 * @param adjustScrollBar
|
|
6658 * true= the scroll thumb will be moved to reflect the new scroll offset.
|
|
6659 * false = the scroll thumb will not be moved
|
|
6660 * @return
|
|
6661 * true=the widget was scrolled
|
|
6662 * false=the widget was not scrolled
|
|
6663 */
|
|
6664 bool scrollVertical(int pixels, bool adjustScrollBar) {
|
|
6665 if (pixels is 0) {
|
|
6666 return false;
|
|
6667 }
|
|
6668 if (verticalScrollOffset !is -1) {
|
|
6669 ScrollBar verticalBar = getVerticalBar();
|
|
6670 if (verticalBar !is null && adjustScrollBar) {
|
|
6671 verticalBar.setSelection(verticalScrollOffset + pixels);
|
|
6672 }
|
|
6673 int scrollWidth = clientAreaWidth - leftMargin - rightMargin;
|
|
6674 if (pixels > 0) {
|
|
6675 int sourceY = topMargin + pixels;
|
|
6676 int scrollHeight = clientAreaHeight - sourceY - bottomMargin;
|
|
6677 if (scrollHeight > 0) {
|
|
6678 scroll(leftMargin, topMargin, leftMargin, sourceY, scrollWidth, scrollHeight, true);
|
|
6679 }
|
|
6680 if (sourceY > scrollHeight) {
|
|
6681 int redrawY = Math.max(0, topMargin + scrollHeight);
|
|
6682 int redrawHeight = Math.min(clientAreaHeight, pixels - scrollHeight);
|
|
6683 super.redraw(leftMargin, redrawY, scrollWidth, redrawHeight, true);
|
|
6684 }
|
|
6685 } else {
|
|
6686 int destinationY = topMargin - pixels;
|
|
6687 int scrollHeight = clientAreaHeight - destinationY - bottomMargin;
|
|
6688 if (scrollHeight > 0) {
|
|
6689 scroll(leftMargin, destinationY, leftMargin, topMargin, scrollWidth, scrollHeight, true);
|
|
6690 }
|
|
6691 if (destinationY > scrollHeight) {
|
|
6692 int redrawY = Math.max(0, topMargin + scrollHeight);
|
|
6693 int redrawHeight = Math.min(clientAreaHeight, -pixels - scrollHeight);
|
|
6694 super.redraw(leftMargin, redrawY, scrollWidth, redrawHeight, true);
|
|
6695 }
|
|
6696 }
|
|
6697 verticalScrollOffset += pixels;
|
|
6698 calculateTopIndex(pixels);
|
|
6699 } else {
|
|
6700 calculateTopIndex(pixels);
|
|
6701 super.redraw();
|
|
6702 }
|
|
6703 setCaretLocation();
|
|
6704 return true;
|
|
6705 }
|
|
6706 void scrollText(int srcY, int destY) {
|
|
6707 if (srcY is destY) return;
|
|
6708 int deltaY = destY - srcY;
|
|
6709 int scrollWidth = clientAreaWidth - leftMargin - rightMargin, scrollHeight;
|
|
6710 if (deltaY > 0) {
|
|
6711 scrollHeight = clientAreaHeight - srcY - bottomMargin;
|
|
6712 } else {
|
|
6713 scrollHeight = clientAreaHeight - destY - bottomMargin;
|
|
6714 }
|
|
6715 scroll(leftMargin, destY, leftMargin, srcY, scrollWidth, scrollHeight, true);
|
|
6716 if ((0 < srcY + scrollHeight) && (topMargin > srcY)) {
|
|
6717 super.redraw(leftMargin, deltaY, scrollWidth, topMargin, false);
|
|
6718 }
|
|
6719 if ((0 < destY + scrollHeight) && (topMargin > destY)) {
|
|
6720 super.redraw(leftMargin, 0, scrollWidth, topMargin, false);
|
|
6721 }
|
|
6722 if ((clientAreaHeight - bottomMargin < srcY + scrollHeight) && (clientAreaHeight > srcY)) {
|
|
6723 super.redraw(leftMargin, clientAreaHeight - bottomMargin + deltaY, scrollWidth, bottomMargin, false);
|
|
6724 }
|
|
6725 if ((clientAreaHeight - bottomMargin < destY + scrollHeight) && (clientAreaHeight > destY)) {
|
|
6726 super.redraw(leftMargin, clientAreaHeight - bottomMargin, scrollWidth, bottomMargin, false);
|
|
6727 }
|
|
6728 }
|
|
6729 /**
|
|
6730 * Selects all the text.
|
|
6731 *
|
|
6732 * @exception SWTException <ul>
|
|
6733 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
6734 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
6735 * </ul>
|
|
6736 */
|
|
6737 public void selectAll() {
|
|
6738 checkWidget();
|
|
6739 setSelection(0, Math.max(getCharCount(),0));
|
|
6740 }
|
|
6741 /**
|
|
6742 * Replaces/inserts text as defined by the event.
|
|
6743 *
|
|
6744 * @param event the text change event.
|
|
6745 * <ul>
|
|
6746 * <li>event.start - the replace start offset</li>
|
|
6747 * <li>event.end - the replace end offset</li>
|
|
6748 * <li>event.text - the new text</li>
|
|
6749 * </ul>
|
|
6750 */
|
|
6751 void sendKeyEvent(Event event) {
|
|
6752 if (editable) {
|
|
6753 modifyContent(event, true);
|
|
6754 }
|
|
6755 }
|
|
6756 /**
|
|
6757 * Returns a StyledTextEvent that can be used to request data such
|
|
6758 * as styles and background color for a line.
|
|
6759 * <p>
|
|
6760 * The specified line may be a visual (wrapped) line if in word
|
|
6761 * wrap mode. The returned object will always be for a logical
|
|
6762 * (unwrapped) line.
|
|
6763 * </p>
|
|
6764 *
|
|
6765 * @param lineOffset offset of the line. This may be the offset of
|
|
6766 * a visual line if the widget is in word wrap mode.
|
|
6767 * @param line line text. This may be the text of a visual line if
|
|
6768 * the widget is in word wrap mode.
|
|
6769 * @return StyledTextEvent that can be used to request line data
|
|
6770 * for the given line.
|
|
6771 */
|
|
6772 StyledTextEvent sendLineEvent(int eventType, int lineOffset, String line) {
|
|
6773 StyledTextEvent event = null;
|
|
6774 if (isListening(eventType)) {
|
|
6775 event = new StyledTextEvent(content);
|
|
6776 event.detail = lineOffset;
|
|
6777 event.text = line;
|
|
6778 event.alignment = alignment;
|
|
6779 event.indent = indent;
|
|
6780 event.justify = justify;
|
|
6781 notifyListeners(eventType, event);
|
|
6782 }
|
|
6783 return event;
|
|
6784 }
|
|
6785 void sendModifyEvent(Event event) {
|
|
6786 Accessible accessible = getAccessible();
|
|
6787 if (event.text.length is 0) {
|
|
6788 accessible.textChanged(ACC.TEXT_DELETE, event.start, event.end - event.start);
|
|
6789 } else {
|
|
6790 if (event.start is event.end) {
|
|
6791 accessible.textChanged(ACC.TEXT_INSERT, event.start, event.text.length);
|
|
6792 } else {
|
|
6793 accessible.textChanged(ACC.TEXT_DELETE, event.start, event.end - event.start);
|
|
6794 accessible.textChanged(ACC.TEXT_INSERT, event.start, event.text.length);
|
|
6795 }
|
|
6796 }
|
|
6797 notifyListeners(SWT.Modify, event);
|
|
6798 }
|
|
6799 /**
|
|
6800 * Sends the specified selection event.
|
|
6801 */
|
|
6802 void sendSelectionEvent() {
|
|
6803 getAccessible().textSelectionChanged();
|
|
6804 Event event = new Event();
|
|
6805 event.x = selection.x;
|
|
6806 event.y = selection.y;
|
|
6807 notifyListeners(SWT.Selection, event);
|
|
6808 }
|
|
6809 int sendWordBoundaryEvent(int eventType, int movement, int offset, int newOffset, String lineText, int lineOffset) {
|
|
6810 if (isListening(eventType)) {
|
|
6811 StyledTextEvent event = new StyledTextEvent(content);
|
|
6812 event.detail = lineOffset;
|
|
6813 event.text = lineText;
|
|
6814 event.count = movement;
|
|
6815 event.start = offset;
|
|
6816 event.end = newOffset;
|
|
6817 notifyListeners(eventType, event);
|
|
6818 offset = event.end;
|
|
6819 if (offset !is newOffset) {
|
|
6820 int length = getCharCount();
|
|
6821 if (offset < 0) {
|
|
6822 offset = 0;
|
|
6823 } else if (offset > length) {
|
|
6824 offset = length;
|
|
6825 } else {
|
|
6826 if (isLineDelimiter(offset)) {
|
|
6827 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
|
|
6828 }
|
|
6829 }
|
|
6830 }
|
|
6831 return offset;
|
|
6832 }
|
|
6833 return newOffset;
|
|
6834 }
|
|
6835 /**
|
|
6836 * Sets the alignment of the widget. The argument should be one of <code>SWT.LEFT</code>,
|
|
6837 * <code>SWT.CENTER</code> or <code>SWT.RIGHT</code>. The alignment applies for all lines.
|
|
6838 * </p><p>
|
|
6839 * Note that if <code>SWT.MULTI</code> is set, then <code>SWT.WRAP</code> must also be set
|
|
6840 * in order to stabilize the right edge before setting alignment.
|
|
6841 * </p>
|
|
6842 *
|
|
6843 * @param alignment the new alignment
|
|
6844 *
|
|
6845 * @exception SWTException <ul>
|
|
6846 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
6847 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
6848 * </ul>
|
|
6849 *
|
|
6850 * @see #setLineAlignment(int, int, int)
|
|
6851 *
|
|
6852 * @since 3.2
|
|
6853 */
|
|
6854 public void setAlignment(int alignment) {
|
|
6855 checkWidget();
|
|
6856 alignment &= (SWT.LEFT | SWT.RIGHT | SWT.CENTER);
|
|
6857 if (alignment is 0 || this.alignment is alignment) return;
|
|
6858 this.alignment = alignment;
|
|
6859 resetCache(0, content.getLineCount());
|
|
6860 setCaretLocation();
|
|
6861 super.redraw();
|
|
6862 }
|
|
6863 /**
|
|
6864 * @see Control#setBackground(Color)
|
|
6865 */
|
|
6866 public override void setBackground(Color color) {
|
|
6867 checkWidget();
|
|
6868 background = color;
|
|
6869 super.setBackground(color);
|
|
6870 super.redraw();
|
|
6871 }
|
|
6872 /**
|
|
6873 * Sets the receiver's caret. Set the caret's height and location.
|
|
6874 *
|
|
6875 * </p>
|
|
6876 * @param caret the new caret for the receiver
|
|
6877 *
|
|
6878 * @exception SWTException <ul>
|
|
6879 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
6880 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
6881 * </ul>
|
|
6882 */
|
|
6883 public override void setCaret(Caret caret) {
|
|
6884 checkWidget ();
|
|
6885 super.setCaret(caret);
|
|
6886 caretDirection = SWT.NULL;
|
|
6887 if (caret !is null) {
|
|
6888 setCaretLocation();
|
|
6889 }
|
|
6890 }
|
|
6891 /**
|
|
6892 * Sets the BIDI coloring mode. When true the BIDI text display
|
|
6893 * algorithm is applied to segments of text that are the same
|
|
6894 * color.
|
|
6895 *
|
|
6896 * @param mode the new coloring mode
|
|
6897 * @exception SWTException <ul>
|
|
6898 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
6899 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
6900 * </ul>
|
|
6901 *
|
|
6902 * @deprecated use BidiSegmentListener instead.
|
|
6903 */
|
|
6904 public void setBidiColoring(bool mode) {
|
|
6905 checkWidget();
|
|
6906 bidiColoring = mode;
|
|
6907 }
|
|
6908 /**
|
|
6909 * Moves the Caret to the current caret offset.
|
|
6910 */
|
|
6911 void setCaretLocation() {
|
|
6912 Point newCaretPos = getPointAtOffset(caretOffset);
|
|
6913 setCaretLocation(newCaretPos, getCaretDirection());
|
|
6914 }
|
|
6915 void setCaretLocation(Point location, int direction) {
|
|
6916 Caret caret = getCaret();
|
|
6917 if (caret !is null) {
|
|
6918 bool isDefaultCaret = caret is defaultCaret;
|
|
6919 int lineHeight = renderer.getLineHeight();
|
|
6920 int caretHeight = lineHeight;
|
|
6921 if (!isFixedLineHeight() && isDefaultCaret) {
|
|
6922 caretHeight = getBoundsAtOffset(caretOffset).height;
|
|
6923 if (caretHeight !is lineHeight) {
|
|
6924 direction = SWT.DEFAULT;
|
|
6925 }
|
|
6926 }
|
|
6927 int imageDirection = direction;
|
|
6928 if (isMirrored()) {
|
|
6929 if (imageDirection is SWT.LEFT) {
|
|
6930 imageDirection = SWT.RIGHT;
|
|
6931 } else if (imageDirection is SWT.RIGHT) {
|
|
6932 imageDirection = SWT.LEFT;
|
|
6933 }
|
|
6934 }
|
|
6935 if (isDefaultCaret && imageDirection is SWT.RIGHT) {
|
|
6936 location.x -= (caret.getSize().x - 1);
|
|
6937 }
|
|
6938 if (isDefaultCaret) {
|
|
6939 caret.setBounds(location.x, location.y, caretWidth, caretHeight);
|
|
6940 } else {
|
|
6941 caret.setLocation(location);
|
|
6942 }
|
|
6943 getAccessible().textCaretMoved(getCaretOffset());
|
|
6944 if (direction !is caretDirection) {
|
|
6945 caretDirection = direction;
|
|
6946 if (isDefaultCaret) {
|
|
6947 if (imageDirection is SWT.DEFAULT) {
|
|
6948 defaultCaret.setImage(null);
|
|
6949 } else if (imageDirection is SWT.LEFT) {
|
|
6950 defaultCaret.setImage(leftCaretBitmap);
|
|
6951 } else if (imageDirection is SWT.RIGHT) {
|
|
6952 defaultCaret.setImage(rightCaretBitmap);
|
|
6953 }
|
|
6954 }
|
|
6955 if (caretDirection is SWT.LEFT) {
|
|
6956 BidiUtil.setKeyboardLanguage(BidiUtil.KEYBOARD_NON_BIDI);
|
|
6957 } else if (caretDirection is SWT.RIGHT) {
|
|
6958 BidiUtil.setKeyboardLanguage(BidiUtil.KEYBOARD_BIDI);
|
|
6959 }
|
|
6960 }
|
|
6961 }
|
|
6962 columnX = location.x;
|
|
6963 }
|
|
6964 /**
|
|
6965 * Sets the caret offset.
|
|
6966 *
|
|
6967 * @param offset caret offset, relative to the first character in the text.
|
|
6968 * @exception SWTException <ul>
|
|
6969 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
6970 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
6971 * </ul>
|
|
6972 * @exception IllegalArgumentException <ul>
|
|
6973 * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
|
|
6974 * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
|
|
6975 * </ul>
|
|
6976 */
|
|
6977 public void setCaretOffset(int offset) {
|
|
6978 checkWidget();
|
|
6979 int length = getCharCount();
|
|
6980 if (length > 0 && offset !is caretOffset) {
|
|
6981 if (offset < 0) {
|
|
6982 caretOffset = 0;
|
|
6983 } else if (offset > length) {
|
|
6984 caretOffset = length;
|
|
6985 } else {
|
|
6986 if (isLineDelimiter(offset)) {
|
|
6987 // offset is inside a multi byte line delimiter. This is an
|
|
6988 // illegal operation and an exception is thrown. Fixes 1GDKK3R
|
|
6989 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
|
|
6990 }
|
|
6991 caretOffset = offset;
|
|
6992 }
|
|
6993 caretAlignment = PREVIOUS_OFFSET_TRAILING;
|
|
6994 // clear the selection if the caret is moved.
|
|
6995 // don't notify listeners about the selection change.
|
|
6996 clearSelection(false);
|
|
6997 }
|
|
6998 setCaretLocation();
|
|
6999 }
|
|
7000 /**
|
|
7001 * Copies the specified text range to the clipboard. The text will be placed
|
|
7002 * in the clipboard in plain text format and RTF format.
|
|
7003 *
|
|
7004 * @param start start index of the text
|
|
7005 * @param length length of text to place in clipboard
|
|
7006 *
|
|
7007 * @exception SWTError, see Clipboard.setContents
|
|
7008 * @see org.eclipse.swt.dnd.Clipboard#setContents
|
|
7009 */
|
|
7010 void setClipboardContent(int start, int length, int clipboardType) {
|
|
7011 if (clipboardType is DND.SELECTION_CLIPBOARD && !(IS_MOTIF || IS_GTK)) return;
|
|
7012 TextTransfer plainTextTransfer = TextTransfer.getInstance();
|
|
7013 TextWriter plainTextWriter = new TextWriter(start, length);
|
|
7014 String plainText = getPlatformDelimitedText(plainTextWriter);
|
|
7015 Object[] data;
|
|
7016 Transfer[] types;
|
|
7017 if (clipboardType is DND.SELECTION_CLIPBOARD) {
|
|
7018 data = [ cast(Object) new ArrayWrapperString(plainText) ];
|
|
7019 types = [plainTextTransfer];
|
|
7020 } else {
|
|
7021 RTFTransfer rtfTransfer = RTFTransfer.getInstance();
|
|
7022 RTFWriter rtfWriter = new RTFWriter(start, length);
|
|
7023 String rtfText = getPlatformDelimitedText(rtfWriter);
|
|
7024 data = [ cast(Object) new ArrayWrapperString(rtfText), new ArrayWrapperString(plainText) ];
|
|
7025 types = [ cast(Transfer)rtfTransfer, plainTextTransfer];
|
|
7026 }
|
|
7027 clipboard.setContents(data, types, clipboardType);
|
|
7028 }
|
|
7029 /**
|
|
7030 * Sets the content implementation to use for text storage.
|
|
7031 *
|
|
7032 * @param newContent StyledTextContent implementation to use for text storage.
|
|
7033 * @exception SWTException <ul>
|
|
7034 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7035 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7036 * </ul>
|
|
7037 * @exception IllegalArgumentException <ul>
|
|
7038 * <li>ERROR_NULL_ARGUMENT when listener is null</li>
|
|
7039 * </ul>
|
|
7040 */
|
|
7041 public void setContent(StyledTextContent newContent) {
|
|
7042 checkWidget();
|
|
7043 if (newContent is null) {
|
|
7044 SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
7045 }
|
|
7046 if (content !is null) {
|
|
7047 content.removeTextChangeListener(textChangeListener);
|
|
7048 }
|
|
7049 content = newContent;
|
|
7050 content.addTextChangeListener(textChangeListener);
|
|
7051 reset();
|
|
7052 }
|
|
7053 /**
|
|
7054 * Sets the receiver's cursor to the cursor specified by the
|
|
7055 * argument. Overridden to handle the null case since the
|
|
7056 * StyledText widget uses an ibeam as its default cursor.
|
|
7057 *
|
|
7058 * @see Control#setCursor(Cursor)
|
|
7059 */
|
|
7060 public override void setCursor (Cursor cursor) {
|
|
7061 if (cursor is null) {
|
|
7062 Display display = getDisplay();
|
|
7063 super.setCursor(display.getSystemCursor(SWT.CURSOR_IBEAM));
|
|
7064 } else {
|
|
7065 super.setCursor(cursor);
|
|
7066 }
|
|
7067 }
|
|
7068 /**
|
|
7069 * Sets whether the widget : double click mouse behavior.
|
|
7070 * </p>
|
|
7071 *
|
|
7072 * @param enable if true double clicking a word selects the word, if false
|
|
7073 * double clicks have the same effect as regular mouse clicks.
|
|
7074 * @exception SWTException <ul>
|
|
7075 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7076 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7077 * </ul>
|
|
7078 */
|
|
7079 public void setDoubleClickEnabled(bool enable) {
|
|
7080 checkWidget();
|
|
7081 doubleClickEnabled = enable;
|
|
7082 }
|
|
7083 public override void setDragDetect (bool dragDetect_) {
|
|
7084 checkWidget ();
|
|
7085 this.dragDetect_ = dragDetect_;
|
|
7086 }
|
|
7087 /**
|
|
7088 * Sets whether the widget content can be edited.
|
|
7089 * </p>
|
|
7090 *
|
|
7091 * @param editable if true content can be edited, if false content can not be
|
|
7092 * edited
|
|
7093 * @exception SWTException <ul>
|
|
7094 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7095 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7096 * </ul>
|
|
7097 */
|
|
7098 public void setEditable(bool editable) {
|
|
7099 checkWidget();
|
|
7100 this.editable = editable;
|
|
7101 }
|
|
7102 /**
|
|
7103 * Sets a new font to render text with.
|
|
7104 * <p>
|
|
7105 * <b>NOTE:</b> Italic fonts are not supported unless they have no overhang
|
|
7106 * and the same baseline as regular fonts.
|
|
7107 * </p>
|
|
7108 *
|
|
7109 * @param font new font
|
|
7110 * @exception SWTException <ul>
|
|
7111 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7112 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7113 * </ul>
|
|
7114 */
|
|
7115 public override void setFont(Font font) {
|
|
7116 checkWidget();
|
|
7117 int oldLineHeight = renderer.getLineHeight();
|
|
7118 super.setFont(font);
|
|
7119 renderer.setFont(getFont(), tabLength);
|
|
7120 // keep the same top line visible. fixes 5815
|
|
7121 if (isFixedLineHeight()) {
|
|
7122 int lineHeight = renderer.getLineHeight();
|
|
7123 if (lineHeight !is oldLineHeight) {
|
|
7124 int vscroll = (getVerticalScrollOffset() * lineHeight / oldLineHeight) - getVerticalScrollOffset();
|
|
7125 scrollVertical(vscroll, true);
|
|
7126 }
|
|
7127 }
|
|
7128 resetCache(0, content.getLineCount());
|
|
7129 claimBottomFreeSpace();
|
|
7130 calculateScrollBars();
|
|
7131 if (isBidiCaret()) createCaretBitmaps();
|
|
7132 caretDirection = SWT.NULL;
|
|
7133 setCaretLocation();
|
|
7134 super.redraw();
|
|
7135 }
|
|
7136 public override void setForeground(Color color) {
|
|
7137 checkWidget();
|
|
7138 foreground = color;
|
|
7139 super.setForeground(getForeground());
|
|
7140 super.redraw();
|
|
7141 }
|
|
7142 /**
|
|
7143 * Sets the horizontal scroll offset relative to the start of the line.
|
|
7144 * Do nothing if there is no text set.
|
|
7145 * <p>
|
|
7146 * <b>NOTE:</b> The horizontal index is reset to 0 when new text is set in the
|
|
7147 * widget.
|
|
7148 * </p>
|
|
7149 *
|
|
7150 * @param offset horizontal scroll offset relative to the start
|
|
7151 * of the line, measured in character increments starting at 0, if
|
|
7152 * equal to 0 the content is not scrolled, if > 0 = the content is scrolled.
|
|
7153 * @exception SWTException <ul>
|
|
7154 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7155 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7156 * </ul>
|
|
7157 */
|
|
7158 public void setHorizontalIndex(int offset) {
|
|
7159 checkWidget();
|
|
7160 if (getCharCount() is 0) {
|
|
7161 return;
|
|
7162 }
|
|
7163 if (offset < 0) {
|
|
7164 offset = 0;
|
|
7165 }
|
|
7166 offset *= getHorizontalIncrement();
|
|
7167 // allow any value if client area width is unknown or 0.
|
|
7168 // offset will be checked in resize handler.
|
|
7169 // don't use isVisible since width is known even if widget
|
|
7170 // is temporarily invisible
|
|
7171 if (clientAreaWidth > 0) {
|
|
7172 int width = renderer.getWidth();
|
|
7173 // prevent scrolling if the content fits in the client area.
|
|
7174 // align end of longest line with right border of client area
|
|
7175 // if offset is out of range.
|
|
7176 if (offset > width - clientAreaWidth) {
|
|
7177 offset = Math.max(0, width - clientAreaWidth);
|
|
7178 }
|
|
7179 }
|
|
7180 scrollHorizontal(offset - horizontalScrollOffset, true);
|
|
7181 }
|
|
7182 /**
|
|
7183 * Sets the horizontal pixel offset relative to the start of the line.
|
|
7184 * Do nothing if there is no text set.
|
|
7185 * <p>
|
|
7186 * <b>NOTE:</b> The horizontal pixel offset is reset to 0 when new text
|
|
7187 * is set in the widget.
|
|
7188 * </p>
|
|
7189 *
|
|
7190 * @param pixel horizontal pixel offset relative to the start
|
|
7191 * of the line.
|
|
7192 * @exception SWTException <ul>
|
|
7193 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7194 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7195 * </ul>
|
|
7196 * @since 2.0
|
|
7197 */
|
|
7198 public void setHorizontalPixel(int pixel) {
|
|
7199 checkWidget();
|
|
7200 if (getCharCount() is 0) {
|
|
7201 return;
|
|
7202 }
|
|
7203 if (pixel < 0) {
|
|
7204 pixel = 0;
|
|
7205 }
|
|
7206 // allow any value if client area width is unknown or 0.
|
|
7207 // offset will be checked in resize handler.
|
|
7208 // don't use isVisible since width is known even if widget
|
|
7209 // is temporarily invisible
|
|
7210 if (clientAreaWidth > 0) {
|
|
7211 int width = renderer.getWidth();
|
|
7212 // prevent scrolling if the content fits in the client area.
|
|
7213 // align end of longest line with right border of client area
|
|
7214 // if offset is out of range.
|
|
7215 if (pixel > width - clientAreaWidth) {
|
|
7216 pixel = Math.max(0, width - clientAreaWidth);
|
|
7217 }
|
|
7218 }
|
|
7219 scrollHorizontal(pixel - horizontalScrollOffset, true);
|
|
7220 }
|
|
7221 /**
|
|
7222 * Sets the line indentation of the widget.
|
|
7223 * <p>
|
|
7224 * It is the amount of blank space, in pixels, at the beginning of each line.
|
|
7225 * When a line wraps in several lines only the first one is indented.
|
|
7226 * </p>
|
|
7227 *
|
|
7228 * @param indent the new indent
|
|
7229 *
|
|
7230 * @exception SWTException <ul>
|
|
7231 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7232 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7233 * </ul>
|
|
7234 *
|
|
7235 * @see #setLineIndent(int, int, int)
|
|
7236 *
|
|
7237 * @since 3.2
|
|
7238 */
|
|
7239 public void setIndent(int indent) {
|
|
7240 checkWidget();
|
|
7241 if (this.indent is indent || indent < 0) return;
|
|
7242 this.indent = indent;
|
|
7243 resetCache(0, content.getLineCount());
|
|
7244 setCaretLocation();
|
|
7245 super.redraw();
|
|
7246 }
|
|
7247 /**
|
|
7248 * Sets whether the widget should justify lines.
|
|
7249 *
|
|
7250 * @param justify whether lines should be justified
|
|
7251 *
|
|
7252 * @exception SWTException <ul>
|
|
7253 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7254 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7255 * </ul>
|
|
7256 *
|
|
7257 * @see #setLineJustify(int, int, bool)
|
|
7258 *
|
|
7259 * @since 3.2
|
|
7260 */
|
|
7261 public void setJustify(bool justify) {
|
|
7262 checkWidget();
|
|
7263 if (this.justify is justify) return;
|
|
7264 this.justify = justify;
|
|
7265 resetCache(0, content.getLineCount());
|
|
7266 setCaretLocation();
|
|
7267 super.redraw();
|
|
7268 }
|
|
7269 /**
|
|
7270 * Maps a key to an action.
|
|
7271 * <p>
|
|
7272 * One action can be associated with N keys. However, each key can only
|
|
7273 * have one action (key:action is N:1 relation).
|
|
7274 * </p>
|
|
7275 *
|
|
7276 * @param key a key code defined in SWT.java or a character.
|
|
7277 * Optionally ORd with a state mask. Preferred state masks are one or more of
|
|
7278 * SWT.MOD1, SWT.MOD2, SWT.MOD3, since these masks account for modifier platform
|
|
7279 * differences. However, there may be cases where using the specific state masks
|
|
7280 * (i.e., SWT.CTRL, SWT.SHIFT, SWT.ALT, SWT.COMMAND) makes sense.
|
|
7281 * @param action one of the predefined actions defined in ST.java.
|
|
7282 * Use SWT.NULL to remove a key binding.
|
|
7283 * @exception SWTException <ul>
|
|
7284 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7285 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7286 * </ul>
|
|
7287 */
|
|
7288 public void setKeyBinding(int key, int action) {
|
|
7289 checkWidget();
|
|
7290 int modifierValue = key & SWT.MODIFIER_MASK;
|
|
7291 char keyChar = cast(char)(key & SWT.KEY_MASK);
|
|
7292 if (Compatibility.isLetter(keyChar)) {
|
|
7293 // make the keybinding case insensitive by adding it
|
|
7294 // in its upper and lower case form
|
|
7295 char ch = CharacterToUpper(keyChar);
|
|
7296 int newKey = ch | modifierValue;
|
|
7297 if (action is SWT.NULL) {
|
|
7298 keyActionMap.remove(newKey);
|
|
7299 } else {
|
|
7300 keyActionMap[newKey] = action;
|
|
7301 }
|
|
7302 ch = CharacterToLower(keyChar);
|
|
7303 newKey = ch | modifierValue;
|
|
7304 if (action is SWT.NULL) {
|
|
7305 keyActionMap.remove(newKey);
|
|
7306 } else {
|
|
7307 keyActionMap[newKey] = action;
|
|
7308 }
|
|
7309 } else {
|
|
7310 if (action is SWT.NULL) {
|
|
7311 keyActionMap.remove(key);
|
|
7312 } else {
|
|
7313 keyActionMap[key]=action;
|
|
7314 }
|
|
7315 }
|
|
7316 }
|
|
7317 /**
|
|
7318 * Sets the alignment of the specified lines. The argument should be one of <code>SWT.LEFT</code>,
|
|
7319 * <code>SWT.CENTER</code> or <code>SWT.RIGHT</code>.
|
|
7320 * <p><p>
|
|
7321 * Note that if <code>SWT.MULTI</code> is set, then <code>SWT.WRAP</code> must also be set
|
|
7322 * in order to stabilize the right edge before setting alignment.
|
|
7323 * </p>
|
|
7324 * Should not be called if a LineStyleListener has been set since the listener
|
|
7325 * maintains the line attributes.
|
|
7326 * </p><p>
|
|
7327 * All line attributes are maintained relative to the line text, not the
|
|
7328 * line index that is specified in this method call.
|
|
7329 * During text changes, when entire lines are inserted or removed, the line
|
|
7330 * attributes that are associated with the lines after the change
|
|
7331 * will "move" with their respective text. An entire line is defined as
|
|
7332 * extending from the first character on a line to the last and including the
|
|
7333 * line delimiter.
|
|
7334 * </p><p>
|
|
7335 * When two lines are joined by deleting a line delimiter, the top line
|
|
7336 * attributes take precedence and the attributes of the bottom line are deleted.
|
|
7337 * For all other text changes line attributes will remain unchanged.
|
|
7338 *
|
|
7339 * @param startLine first line the alignment is applied to, 0 based
|
|
7340 * @param lineCount number of lines the alignment applies to.
|
|
7341 * @param alignment line alignment
|
|
7342 *
|
|
7343 * @exception SWTException <ul>
|
|
7344 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7345 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7346 * </ul>
|
|
7347 * @exception IllegalArgumentException <ul>
|
|
7348 * <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
|
|
7349 * </ul>
|
|
7350 * @see #setAlignment(int)
|
|
7351 * @since 3.2
|
|
7352 */
|
|
7353 public void setLineAlignment(int startLine, int lineCount, int alignment) {
|
|
7354 checkWidget();
|
|
7355 if (isListening(LineGetStyle)) return;
|
|
7356 if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
|
|
7357 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
|
|
7358 }
|
|
7359
|
|
7360 renderer.setLineAlignment(startLine, lineCount, alignment);
|
|
7361 resetCache(startLine, lineCount);
|
|
7362 redrawLines(startLine, lineCount);
|
|
7363 int caretLine = getCaretLine();
|
|
7364 if (startLine <= caretLine && caretLine < startLine + lineCount) {
|
|
7365 setCaretLocation();
|
|
7366 }
|
|
7367 }
|
|
7368 /**
|
|
7369 * Sets the background color of the specified lines.
|
|
7370 * <p>
|
|
7371 * The background color is drawn for the width of the widget. All
|
|
7372 * line background colors are discarded when setText is called.
|
|
7373 * The text background color if defined in a StyleRange overlays the
|
|
7374 * line background color.
|
|
7375 * </p><p>
|
|
7376 * Should not be called if a LineBackgroundListener has been set since the
|
|
7377 * listener maintains the line backgrounds.
|
|
7378 * </p><p>
|
|
7379 * All line attributes are maintained relative to the line text, not the
|
|
7380 * line index that is specified in this method call.
|
|
7381 * During text changes, when entire lines are inserted or removed, the line
|
|
7382 * attributes that are associated with the lines after the change
|
|
7383 * will "move" with their respective text. An entire line is defined as
|
|
7384 * extending from the first character on a line to the last and including the
|
|
7385 * line delimiter.
|
|
7386 * </p><p>
|
|
7387 * When two lines are joined by deleting a line delimiter, the top line
|
|
7388 * attributes take precedence and the attributes of the bottom line are deleted.
|
|
7389 * For all other text changes line attributes will remain unchanged.
|
|
7390 * </p>
|
|
7391 *
|
|
7392 * @param startLine first line the color is applied to, 0 based
|
|
7393 * @param lineCount number of lines the color applies to.
|
|
7394 * @param background line background color
|
|
7395 * @exception SWTException <ul>
|
|
7396 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7397 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7398 * </ul>
|
|
7399 * @exception IllegalArgumentException <ul>
|
|
7400 * <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
|
|
7401 * </ul>
|
|
7402 */
|
|
7403 public void setLineBackground(int startLine, int lineCount, Color background) {
|
|
7404 checkWidget();
|
|
7405 if (isListening(LineGetBackground)) return;
|
|
7406 if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
|
|
7407 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
|
|
7408 }
|
|
7409 if (background !is null) {
|
|
7410 renderer.setLineBackground(startLine, lineCount, background);
|
|
7411 } else {
|
|
7412 renderer.clearLineBackground(startLine, lineCount);
|
|
7413 }
|
|
7414 redrawLines(startLine, lineCount);
|
|
7415 }
|
|
7416 /**
|
|
7417 * Sets the bullet of the specified lines.
|
|
7418 * <p>
|
|
7419 * Should not be called if a LineStyleListener has been set since the listener
|
|
7420 * maintains the line attributes.
|
|
7421 * </p><p>
|
|
7422 * All line attributes are maintained relative to the line text, not the
|
|
7423 * line index that is specified in this method call.
|
|
7424 * During text changes, when entire lines are inserted or removed, the line
|
|
7425 * attributes that are associated with the lines after the change
|
|
7426 * will "move" with their respective text. An entire line is defined as
|
|
7427 * extending from the first character on a line to the last and including the
|
|
7428 * line delimiter.
|
|
7429 * </p><p>
|
|
7430 * When two lines are joined by deleting a line delimiter, the top line
|
|
7431 * attributes take precedence and the attributes of the bottom line are deleted.
|
|
7432 * For all other text changes line attributes will remain unchanged.
|
|
7433 * </p>
|
|
7434 *
|
|
7435 * @param startLine first line the bullet is applied to, 0 based
|
|
7436 * @param lineCount number of lines the bullet applies to.
|
|
7437 * @param bullet line bullet
|
|
7438 *
|
|
7439 * @exception SWTException <ul>
|
|
7440 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7441 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7442 * </ul>
|
|
7443 * @exception IllegalArgumentException <ul>
|
|
7444 * <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
|
|
7445 * </ul>
|
|
7446 * @since 3.2
|
|
7447 */
|
|
7448 public void setLineBullet(int startLine, int lineCount, Bullet bullet) {
|
|
7449 checkWidget();
|
|
7450 if (isListening(LineGetStyle)) return;
|
|
7451 if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
|
|
7452 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
|
|
7453 }
|
|
7454
|
|
7455 renderer.setLineBullet(startLine, lineCount, bullet);
|
|
7456 resetCache(startLine, lineCount);
|
|
7457 redrawLines(startLine, lineCount);
|
|
7458 int caretLine = getCaretLine();
|
|
7459 if (startLine <= caretLine && caretLine < startLine + lineCount) {
|
|
7460 setCaretLocation();
|
|
7461 }
|
|
7462 }
|
|
7463 void setVariableLineHeight () {
|
|
7464 if (!fixedLineHeight) return;
|
|
7465 fixedLineHeight = false;
|
|
7466 renderer.calculateIdle();
|
|
7467 }
|
|
7468 /**
|
|
7469 * Sets the indent of the specified lines.
|
|
7470 * <p>
|
|
7471 * Should not be called if a LineStyleListener has been set since the listener
|
|
7472 * maintains the line attributes.
|
|
7473 * </p><p>
|
|
7474 * All line attributes are maintained relative to the line text, not the
|
|
7475 * line index that is specified in this method call.
|
|
7476 * During text changes, when entire lines are inserted or removed, the line
|
|
7477 * attributes that are associated with the lines after the change
|
|
7478 * will "move" with their respective text. An entire line is defined as
|
|
7479 * extending from the first character on a line to the last and including the
|
|
7480 * line delimiter.
|
|
7481 * </p><p>
|
|
7482 * When two lines are joined by deleting a line delimiter, the top line
|
|
7483 * attributes take precedence and the attributes of the bottom line are deleted.
|
|
7484 * For all other text changes line attributes will remain unchanged.
|
|
7485 * </p>
|
|
7486 *
|
|
7487 * @param startLine first line the indent is applied to, 0 based
|
|
7488 * @param lineCount number of lines the indent applies to.
|
|
7489 * @param indent line indent
|
|
7490 *
|
|
7491 * @exception SWTException <ul>
|
|
7492 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7493 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7494 * </ul>
|
|
7495 * @exception IllegalArgumentException <ul>
|
|
7496 * <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
|
|
7497 * </ul>
|
|
7498 * @see #setIndent(int)
|
|
7499 * @since 3.2
|
|
7500 */
|
|
7501 public void setLineIndent(int startLine, int lineCount, int indent) {
|
|
7502 checkWidget();
|
|
7503 if (isListening(LineGetStyle)) return;
|
|
7504 if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
|
|
7505 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
|
|
7506 }
|
|
7507
|
|
7508 renderer.setLineIndent(startLine, lineCount, indent);
|
|
7509 resetCache(startLine, lineCount);
|
|
7510 redrawLines(startLine, lineCount);
|
|
7511 int caretLine = getCaretLine();
|
|
7512 if (startLine <= caretLine && caretLine < startLine + lineCount) {
|
|
7513 setCaretLocation();
|
|
7514 }
|
|
7515 }
|
|
7516 /**
|
|
7517 * Sets the justify of the specified lines.
|
|
7518 * <p>
|
|
7519 * Should not be called if a LineStyleListener has been set since the listener
|
|
7520 * maintains the line attributes.
|
|
7521 * </p><p>
|
|
7522 * All line attributes are maintained relative to the line text, not the
|
|
7523 * line index that is specified in this method call.
|
|
7524 * During text changes, when entire lines are inserted or removed, the line
|
|
7525 * attributes that are associated with the lines after the change
|
|
7526 * will "move" with their respective text. An entire line is defined as
|
|
7527 * extending from the first character on a line to the last and including the
|
|
7528 * line delimiter.
|
|
7529 * </p><p>
|
|
7530 * When two lines are joined by deleting a line delimiter, the top line
|
|
7531 * attributes take precedence and the attributes of the bottom line are deleted.
|
|
7532 * For all other text changes line attributes will remain unchanged.
|
|
7533 * </p>
|
|
7534 *
|
|
7535 * @param startLine first line the justify is applied to, 0 based
|
|
7536 * @param lineCount number of lines the justify applies to.
|
|
7537 * @param justify true if lines should be justified
|
|
7538 *
|
|
7539 * @exception SWTException <ul>
|
|
7540 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7541 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7542 * </ul>
|
|
7543 * @exception IllegalArgumentException <ul>
|
|
7544 * <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
|
|
7545 * </ul>
|
|
7546 * @see #setJustify(bool)
|
|
7547 * @since 3.2
|
|
7548 */
|
|
7549 public void setLineJustify(int startLine, int lineCount, bool justify) {
|
|
7550 checkWidget();
|
|
7551 if (isListening(LineGetStyle)) return;
|
|
7552 if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
|
|
7553 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
|
|
7554 }
|
|
7555
|
|
7556 renderer.setLineJustify(startLine, lineCount, justify);
|
|
7557 resetCache(startLine, lineCount);
|
|
7558 redrawLines(startLine, lineCount);
|
|
7559 int caretLine = getCaretLine();
|
|
7560 if (startLine <= caretLine && caretLine < startLine + lineCount) {
|
|
7561 setCaretLocation();
|
|
7562 }
|
|
7563 }
|
|
7564 /**
|
|
7565 * Sets the line spacing of the widget. The line spacing applies for all lines.
|
|
7566 *
|
|
7567 * @param lineSpacing the line spacing
|
|
7568 * @exception SWTException <ul>
|
|
7569 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7570 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7571 * </ul>
|
|
7572 * @since 3.2
|
|
7573 */
|
|
7574 public void setLineSpacing(int lineSpacing) {
|
|
7575 checkWidget();
|
|
7576 if (this.lineSpacing is lineSpacing || lineSpacing < 0) return;
|
|
7577 this.lineSpacing = lineSpacing;
|
|
7578 setVariableLineHeight();
|
|
7579 resetCache(0, content.getLineCount());
|
|
7580 setCaretLocation();
|
|
7581 super.redraw();
|
|
7582 }
|
|
7583 void setMargins (int leftMargin, int topMargin, int rightMargin, int bottomMargin) {
|
|
7584 checkWidget();
|
|
7585 this.leftMargin = leftMargin;
|
|
7586 this.topMargin = topMargin;
|
|
7587 this.rightMargin = rightMargin;
|
|
7588 this.bottomMargin = bottomMargin;
|
|
7589 setCaretLocation();
|
|
7590 }
|
|
7591 /**
|
|
7592 * Flips selection anchor based on word selection direction.
|
|
7593 */
|
|
7594 void setMouseWordSelectionAnchor() {
|
|
7595 if (clickCount > 1) {
|
|
7596 if (caretOffset < doubleClickSelection.x) {
|
|
7597 selectionAnchor = doubleClickSelection.y;
|
|
7598 } else if (caretOffset > doubleClickSelection.y) {
|
|
7599 selectionAnchor = doubleClickSelection.x;
|
|
7600 }
|
|
7601 }
|
|
7602 }
|
|
7603 /**
|
|
7604 * Sets the orientation of the receiver, which must be one
|
|
7605 * of the constants <code>SWT.LEFT_TO_RIGHT</code> or <code>SWT.RIGHT_TO_LEFT</code>.
|
|
7606 *
|
|
7607 * @param orientation new orientation style
|
|
7608 *
|
|
7609 * @exception SWTException <ul>
|
|
7610 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7611 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7612 * </ul>
|
|
7613 *
|
|
7614 * @since 2.1.2
|
|
7615 */
|
|
7616 public void setOrientation(int orientation) {
|
|
7617 if ((orientation & (SWT.RIGHT_TO_LEFT | SWT.LEFT_TO_RIGHT)) is 0) {
|
|
7618 return;
|
|
7619 }
|
|
7620 if ((orientation & SWT.RIGHT_TO_LEFT) !is 0 && (orientation & SWT.LEFT_TO_RIGHT) !is 0) {
|
|
7621 return;
|
|
7622 }
|
|
7623 if ((orientation & SWT.RIGHT_TO_LEFT) !is 0 && isMirrored()) {
|
|
7624 return;
|
|
7625 }
|
|
7626 if ((orientation & SWT.LEFT_TO_RIGHT) !is 0 && !isMirrored()) {
|
|
7627 return;
|
|
7628 }
|
|
7629 if (!BidiUtil.setOrientation(this, orientation)) {
|
|
7630 return;
|
|
7631 }
|
|
7632 isMirrored_ = (orientation & SWT.RIGHT_TO_LEFT) !is 0;
|
|
7633 caretDirection = SWT.NULL;
|
|
7634 resetCache(0, content.getLineCount());
|
|
7635 setCaretLocation();
|
|
7636 keyActionMap = null;
|
|
7637 createKeyBindings();
|
|
7638 super.redraw();
|
|
7639 }
|
|
7640 /**
|
|
7641 * Adjusts the maximum and the page size of the scroll bars to
|
|
7642 * reflect content width/length changes.
|
|
7643 *
|
|
7644 * @param vertical indicates if the vertical scrollbar also needs to be set
|
|
7645 */
|
|
7646 void setScrollBars(bool vertical) {
|
|
7647 int inactive = 1;
|
|
7648 if (vertical || !isFixedLineHeight()) {
|
|
7649 ScrollBar verticalBar = getVerticalBar();
|
|
7650 if (verticalBar !is null) {
|
|
7651 int maximum = renderer.getHeight();
|
|
7652 // only set the real values if the scroll bar can be used
|
|
7653 // (ie. because the thumb size is less than the scroll maximum)
|
|
7654 // avoids flashing on Motif, fixes 1G7RE1J and 1G5SE92
|
|
7655 if (clientAreaHeight < maximum) {
|
|
7656 verticalBar.setMaximum(maximum);
|
|
7657 verticalBar.setThumb(clientAreaHeight);
|
|
7658 verticalBar.setPageIncrement(clientAreaHeight);
|
|
7659 } else if (verticalBar.getThumb() !is inactive || verticalBar.getMaximum() !is inactive) {
|
|
7660 verticalBar.setValues(
|
|
7661 verticalBar.getSelection(),
|
|
7662 verticalBar.getMinimum(),
|
|
7663 inactive,
|
|
7664 inactive,
|
|
7665 verticalBar.getIncrement(),
|
|
7666 inactive);
|
|
7667 }
|
|
7668 }
|
|
7669 }
|
|
7670 ScrollBar horizontalBar = getHorizontalBar();
|
|
7671 if (horizontalBar !is null && horizontalBar.getVisible()) {
|
|
7672 int maximum = renderer.getWidth();
|
|
7673 // only set the real values if the scroll bar can be used
|
|
7674 // (ie. because the thumb size is less than the scroll maximum)
|
|
7675 // avoids flashing on Motif, fixes 1G7RE1J and 1G5SE92
|
|
7676 if (clientAreaWidth < maximum) {
|
|
7677 horizontalBar.setMaximum(maximum);
|
|
7678 horizontalBar.setThumb(clientAreaWidth - leftMargin - rightMargin);
|
|
7679 horizontalBar.setPageIncrement(clientAreaWidth - leftMargin - rightMargin);
|
|
7680 } else if (horizontalBar.getThumb() !is inactive || horizontalBar.getMaximum() !is inactive) {
|
|
7681 horizontalBar.setValues(
|
|
7682 horizontalBar.getSelection(),
|
|
7683 horizontalBar.getMinimum(),
|
|
7684 inactive,
|
|
7685 inactive,
|
|
7686 horizontalBar.getIncrement(),
|
|
7687 inactive);
|
|
7688 }
|
|
7689 }
|
|
7690 }
|
|
7691 /**
|
|
7692 * Sets the selection to the given position and scrolls it into view. Equivalent to setSelection(start,start).
|
|
7693 *
|
|
7694 * @param start new caret position
|
|
7695 * @see #setSelection(int,int)
|
|
7696 * @exception SWTException <ul>
|
|
7697 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7698 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7699 * </ul>
|
|
7700 * @exception IllegalArgumentException <ul>
|
|
7701 * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
|
|
7702 * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
|
|
7703 * </ul>
|
|
7704 */
|
|
7705 public void setSelection(int start) {
|
|
7706 // checkWidget test done in setSelectionRange
|
|
7707 setSelection(start, start);
|
|
7708 }
|
|
7709 /**
|
|
7710 * Sets the selection and scrolls it into view.
|
|
7711 * <p>
|
|
7712 * Indexing is zero based. Text selections are specified in terms of
|
|
7713 * caret positions. In a text widget that contains N characters, there are
|
|
7714 * N+1 caret positions, ranging from 0..N
|
|
7715 * </p>
|
|
7716 *
|
|
7717 * @param point x=selection start offset, y=selection end offset
|
|
7718 * The caret will be placed at the selection start when x > y.
|
|
7719 * @see #setSelection(int,int)
|
|
7720 * @exception SWTException <ul>
|
|
7721 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7722 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7723 * </ul>
|
|
7724 * @exception IllegalArgumentException <ul>
|
|
7725 * <li>ERROR_NULL_ARGUMENT when point is null</li>
|
|
7726 * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
|
|
7727 * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
|
|
7728 * </ul>
|
|
7729 */
|
|
7730 public void setSelection(Point point) {
|
|
7731 checkWidget();
|
|
7732 if (point is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
|
|
7733 setSelection(point.x, point.y);
|
|
7734 }
|
|
7735 /**
|
|
7736 * Sets the receiver's selection background color to the color specified
|
|
7737 * by the argument, or to the default system color for the control
|
|
7738 * if the argument is null.
|
|
7739 *
|
|
7740 * @param color the new color (or null)
|
|
7741 *
|
|
7742 * @exception IllegalArgumentException <ul>
|
|
7743 * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
|
|
7744 * </ul>
|
|
7745 * @exception SWTException <ul>
|
|
7746 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7747 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7748 * </ul>
|
|
7749 * @since 2.1
|
|
7750 */
|
|
7751 public void setSelectionBackground (Color color) {
|
|
7752 checkWidget ();
|
|
7753 if (color !is null) {
|
|
7754 if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
|
|
7755 }
|
|
7756 selectionBackground = color;
|
|
7757 super.redraw();
|
|
7758 }
|
|
7759 /**
|
|
7760 * Sets the receiver's selection foreground color to the color specified
|
|
7761 * by the argument, or to the default system color for the control
|
|
7762 * if the argument is null.
|
|
7763 * <p>
|
|
7764 * Note that this is a <em>HINT</em>. Some platforms do not allow the application
|
|
7765 * to change the selection foreground color.
|
|
7766 * </p>
|
|
7767 * @param color the new color (or null)
|
|
7768 *
|
|
7769 * @exception IllegalArgumentException <ul>
|
|
7770 * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
|
|
7771 * </ul>
|
|
7772 * @exception SWTException <ul>
|
|
7773 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7774 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7775 * </ul>
|
|
7776 * @since 2.1
|
|
7777 */
|
|
7778 public void setSelectionForeground (Color color) {
|
|
7779 checkWidget ();
|
|
7780 if (color !is null) {
|
|
7781 if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
|
|
7782 }
|
|
7783 selectionForeground = color;
|
|
7784 super.redraw();
|
|
7785 }
|
|
7786 /**
|
|
7787 * Sets the selection and scrolls it into view.
|
|
7788 * <p>
|
|
7789 * Indexing is zero based. Text selections are specified in terms of
|
|
7790 * caret positions. In a text widget that contains N characters, there are
|
|
7791 * N+1 caret positions, ranging from 0..N
|
|
7792 * </p>
|
|
7793 *
|
|
7794 * @param start selection start offset. The caret will be placed at the
|
|
7795 * selection start when start > end.
|
|
7796 * @param end selection end offset
|
|
7797 * @see #setSelectionRange(int,int)
|
|
7798 * @exception SWTException <ul>
|
|
7799 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7800 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7801 * </ul>
|
|
7802 * @exception IllegalArgumentException <ul>
|
|
7803 * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
|
|
7804 * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
|
|
7805 * </ul>
|
|
7806 */
|
|
7807 public void setSelection(int start, int end) {
|
|
7808 setSelectionRange(start, end - start);
|
|
7809 showSelection();
|
|
7810 }
|
|
7811 /**
|
|
7812 * Sets the selection.
|
|
7813 * <p>
|
|
7814 * The new selection may not be visible. Call showSelection to scroll
|
|
7815 * the selection into view.
|
|
7816 * </p>
|
|
7817 *
|
|
7818 * @param start offset of the first selected character, start >= 0 must be true.
|
|
7819 * @param length number of characters to select, 0 <= start + length
|
|
7820 * <= getCharCount() must be true.
|
|
7821 * A negative length places the caret at the selection start.
|
|
7822 * @param sendEvent a Selection event is sent when set to true and when
|
|
7823 * the selection is reset.
|
|
7824 */
|
|
7825 void setSelection(int start, int length, bool sendEvent) {
|
|
7826 int end = start + length;
|
|
7827 start = content.utf8AdjustOffset(start);
|
|
7828 end = content.utf8AdjustOffset(end);
|
|
7829 if (start > end) {
|
|
7830 int temp = end;
|
|
7831 end = start;
|
|
7832 start = temp;
|
|
7833 }
|
|
7834 // is the selection range different or is the selection direction
|
|
7835 // different?
|
|
7836 if (selection.x !is start || selection.y !is end ||
|
|
7837 (length > 0 && selectionAnchor !is selection.x) ||
|
|
7838 (length < 0 && selectionAnchor !is selection.y)) {
|
|
7839 clearSelection(sendEvent);
|
|
7840 if (length < 0) {
|
|
7841 selectionAnchor = selection.y = end;
|
|
7842 caretOffset = selection.x = start;
|
|
7843 } else {
|
|
7844 selectionAnchor = selection.x = start;
|
|
7845 caretOffset = selection.y = end;
|
|
7846 }
|
|
7847 caretAlignment = PREVIOUS_OFFSET_TRAILING;
|
|
7848 internalRedrawRange(selection.x, selection.y - selection.x);
|
|
7849 }
|
|
7850 }
|
|
7851 /**
|
|
7852 * Sets the selection.
|
|
7853 * <p>
|
|
7854 * The new selection may not be visible. Call showSelection to scroll the selection
|
|
7855 * into view. A negative length places the caret at the visual start of the selection.
|
|
7856 * </p>
|
|
7857 *
|
|
7858 * @param start offset of the first selected character
|
|
7859 * @param length number of characters to select
|
|
7860 *
|
|
7861 * @exception SWTException <ul>
|
|
7862 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7863 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7864 * </ul>
|
|
7865 * @exception IllegalArgumentException <ul>
|
|
7866 * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
|
|
7867 * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
|
|
7868 * </ul>
|
|
7869 */
|
|
7870 public void setSelectionRange(int start, int length) {
|
|
7871 checkWidget();
|
|
7872 int contentLength = getCharCount();
|
|
7873 start = Math.max(0, Math.min (start, contentLength));
|
|
7874 int end = start + length;
|
|
7875 if (end < 0) {
|
|
7876 length = -start;
|
|
7877 } else {
|
|
7878 if (end > contentLength) length = contentLength - start;
|
|
7879 }
|
|
7880 if (isLineDelimiter(start) || isLineDelimiter(start + length)) {
|
|
7881 // the start offset or end offset of the selection range is inside a
|
|
7882 // multi byte line delimiter. This is an illegal operation and an exception
|
|
7883 // is thrown. Fixes 1GDKK3R
|
|
7884 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
|
|
7885 }
|
|
7886 setSelection(start, length, false);
|
|
7887 setCaretLocation();
|
|
7888 }
|
|
7889 /**
|
|
7890 * Adds the specified style.
|
|
7891 * <p>
|
|
7892 * The new style overwrites existing styles for the specified range.
|
|
7893 * Existing style ranges are adjusted if they partially overlap with
|
|
7894 * the new style. To clear an individual style, call setStyleRange
|
|
7895 * with a StyleRange that has null attributes.
|
|
7896 * </p><p>
|
|
7897 * Should not be called if a LineStyleListener has been set since the
|
|
7898 * listener maintains the styles.
|
|
7899 * </p>
|
|
7900 *
|
|
7901 * @param range StyleRange object containing the style information.
|
|
7902 * Overwrites the old style in the given range. May be null to delete
|
|
7903 * all styles.
|
|
7904 * @exception SWTException <ul>
|
|
7905 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7906 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7907 * </ul>
|
|
7908 * @exception IllegalArgumentException <ul>
|
|
7909 * <li>ERROR_INVALID_RANGE when the style range is outside the valid range (> getCharCount())</li>
|
|
7910 * </ul>
|
|
7911 */
|
|
7912 public void setStyleRange(StyleRange range) {
|
|
7913 checkWidget();
|
|
7914 if (isListening(LineGetStyle)) return;
|
|
7915 if (range !is null) {
|
|
7916 if (range.isUnstyled()) {
|
|
7917 setStyleRanges(range.start, range.length, null, null, false);
|
|
7918 } else {
|
|
7919 setStyleRanges(range.start, 0, null, [range], false);
|
|
7920 }
|
|
7921 } else {
|
|
7922 setStyleRanges(0, 0, null, null, true);
|
|
7923 }
|
|
7924 }
|
|
7925 /**
|
|
7926 * Clears the styles in the range specified by <code>start</code> and
|
|
7927 * <code>length</code> and adds the new styles.
|
|
7928 * <p>
|
|
7929 * The ranges array contains start and length pairs. Each pair refers to
|
|
7930 * the corresponding style in the styles array. For example, the pair
|
|
7931 * that starts at ranges[n] with length ranges[n+1] uses the style
|
|
7932 * at styles[n/2]. The range fields within each StyleRange are ignored.
|
|
7933 * If ranges or styles is null, the specified range is cleared.
|
|
7934 * </p><p>
|
|
7935 * Note: It is expected that the same instance of a StyleRange will occur
|
|
7936 * multiple times within the styles array, reducing memory usage.
|
|
7937 * </p><p>
|
|
7938 * Should not be called if a LineStyleListener has been set since the
|
|
7939 * listener maintains the styles.
|
|
7940 * </p>
|
|
7941 *
|
|
7942 * @param start offset of first character where styles will be deleted
|
|
7943 * @param length length of the range to delete styles in
|
|
7944 * @param ranges the array of ranges. The ranges must not overlap and must be in order.
|
|
7945 * @param styles the array of StyleRanges. The range fields within the StyleRange are unused.
|
|
7946 *
|
|
7947 * @exception SWTException <ul>
|
|
7948 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7949 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7950 * </ul>
|
|
7951 * @exception IllegalArgumentException <ul>
|
|
7952 * <li>ERROR_NULL_ARGUMENT when an element in the styles array is null</li>
|
|
7953 * <li>ERROR_INVALID_RANGE when the number of ranges and style do not match (ranges.length * 2 is styles.length)</li>
|
|
7954 * <li>ERROR_INVALID_RANGE when a range is outside the valid range (> getCharCount() or less than zero)</li>
|
|
7955 * <li>ERROR_INVALID_RANGE when a range overlaps</li>
|
|
7956 * </ul>
|
|
7957 *
|
|
7958 * @since 3.2
|
|
7959 */
|
|
7960 public void setStyleRanges(int start, int length, int[] ranges, StyleRange[] styles) {
|
|
7961 checkWidget();
|
|
7962 if (isListening(LineGetStyle)) return;
|
|
7963 if (ranges is null || styles is null) {
|
|
7964 setStyleRanges(start, length, null, null, false);
|
|
7965 } else {
|
|
7966 setStyleRanges(start, length, ranges, styles, false);
|
|
7967 }
|
|
7968 }
|
|
7969 /**
|
|
7970 * Sets styles to be used for rendering the widget content.
|
|
7971 * <p>
|
|
7972 * All styles in the widget will be replaced with the given set of ranges and styles.
|
|
7973 * The ranges array contains start and length pairs. Each pair refers to
|
|
7974 * the corresponding style in the styles array. For example, the pair
|
|
7975 * that starts at ranges[n] with length ranges[n+1] uses the style
|
|
7976 * at styles[n/2]. The range fields within each StyleRange are ignored.
|
|
7977 * If either argument is null, the styles are cleared.
|
|
7978 * </p><p>
|
|
7979 * Note: It is expected that the same instance of a StyleRange will occur
|
|
7980 * multiple times within the styles array, reducing memory usage.
|
|
7981 * </p><p>
|
|
7982 * Should not be called if a LineStyleListener has been set since the
|
|
7983 * listener maintains the styles.
|
|
7984 * </p>
|
|
7985 *
|
|
7986 * @param ranges the array of ranges. The ranges must not overlap and must be in order.
|
|
7987 * @param styles the array of StyleRanges. The range fields within the StyleRange are unused.
|
|
7988 *
|
|
7989 * @exception SWTException <ul>
|
|
7990 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
7991 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
7992 * </ul>
|
|
7993 * @exception IllegalArgumentException <ul>
|
|
7994 * <li>ERROR_NULL_ARGUMENT when an element in the styles array is null</li>
|
|
7995 * <li>ERROR_INVALID_RANGE when the number of ranges and style do not match (ranges.length * 2 is styles.length)</li>
|
|
7996 * <li>ERROR_INVALID_RANGE when a range is outside the valid range (> getCharCount() or less than zero)</li>
|
|
7997 * <li>ERROR_INVALID_RANGE when a range overlaps</li>
|
|
7998 * </ul>
|
|
7999 *
|
|
8000 * @since 3.2
|
|
8001 */
|
|
8002 public void setStyleRanges(int[] ranges, StyleRange[] styles) {
|
|
8003 checkWidget();
|
|
8004 if (isListening(LineGetStyle)) return;
|
|
8005 if (ranges is null || styles is null) {
|
|
8006 setStyleRanges(0, 0, null, null, true);
|
|
8007 } else {
|
|
8008 setStyleRanges(0, 0, ranges, styles, true);
|
|
8009 }
|
|
8010 }
|
|
8011 void setStyleRanges(int start, int length, int[] ranges, StyleRange[] styles, bool reset) {
|
|
8012 int charCount = content.getCharCount();
|
|
8013 int end = start + length;
|
|
8014 if (start > end || start < 0) {
|
|
8015 SWT.error(SWT.ERROR_INVALID_RANGE);
|
|
8016 }
|
|
8017 if (styles !is null) {
|
|
8018 if (end > charCount) {
|
|
8019 SWT.error(SWT.ERROR_INVALID_RANGE);
|
|
8020 }
|
|
8021 if (ranges !is null) {
|
|
8022 if (ranges.length !is styles.length << 1) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
|
|
8023 }
|
|
8024 int lastOffset = 0;
|
|
8025 bool variableHeight = false;
|
|
8026 for (int i = 0; i < styles.length; i ++) {
|
|
8027 if (styles[i] is null) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
|
|
8028 int rangeStart, rangeLength;
|
|
8029 if (ranges !is null) {
|
|
8030 rangeStart = ranges[i << 1];
|
|
8031 rangeLength = ranges[(i << 1) + 1];
|
|
8032 } else {
|
|
8033 rangeStart = styles[i].start;
|
|
8034 rangeLength = styles[i].length;
|
|
8035 }
|
|
8036 if (rangeLength < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
|
|
8037 if (!(0 <= rangeStart && rangeStart + rangeLength <= charCount)) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
|
|
8038 if (lastOffset > rangeStart) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
|
|
8039 variableHeight |= styles[i].isVariableHeight();
|
|
8040 lastOffset = rangeStart + rangeLength;
|
|
8041 }
|
|
8042 if (variableHeight) setVariableLineHeight();
|
|
8043 }
|
|
8044 int rangeStart = start, rangeEnd = end;
|
|
8045 if (styles !is null && styles.length > 0) {
|
|
8046 if (ranges !is null) {
|
|
8047 rangeStart = ranges[0];
|
|
8048 rangeEnd = ranges[ranges.length - 2] + ranges[ranges.length - 1];
|
|
8049 } else {
|
|
8050 rangeStart = styles[0].start;
|
|
8051 rangeEnd = styles[styles.length - 1].start + styles[styles.length - 1].length;
|
|
8052 }
|
|
8053 }
|
|
8054 int lastLineBottom = 0;
|
|
8055 if (!isFixedLineHeight() && !reset) {
|
|
8056 int lineEnd = content.getLineAtOffset(Math.max(end, rangeEnd));
|
|
8057 int partialTopIndex = getPartialTopIndex();
|
|
8058 int partialBottomIndex = getPartialBottomIndex();
|
|
8059 if (partialTopIndex <= lineEnd && lineEnd <= partialBottomIndex) {
|
|
8060 lastLineBottom = getLinePixel(lineEnd + 1);
|
|
8061 }
|
|
8062 }
|
|
8063 if (reset) {
|
|
8064 renderer.setStyleRanges(null, null);
|
|
8065 } else {
|
|
8066 renderer.updateRanges(start, length, length);
|
|
8067 }
|
|
8068 if (styles !is null && styles.length > 0) {
|
|
8069 renderer.setStyleRanges(ranges, styles);
|
|
8070 }
|
|
8071 if (reset) {
|
|
8072 resetCache(0, content.getLineCount());
|
|
8073 super.redraw();
|
|
8074 } else {
|
|
8075 int lineStart = content.getLineAtOffset(Math.min(start, rangeStart));
|
|
8076 int lineEnd = content.getLineAtOffset(Math.max(end, rangeEnd));
|
|
8077 resetCache(lineStart, lineEnd - lineStart + 1);
|
|
8078 int partialTopIndex = getPartialTopIndex();
|
|
8079 int partialBottomIndex = getPartialBottomIndex();
|
|
8080 if (!(lineStart > partialBottomIndex || lineEnd < partialTopIndex)) {
|
|
8081 int y = 0;
|
|
8082 int height = clientAreaHeight;
|
|
8083 if (partialTopIndex <= lineStart && lineStart <= partialBottomIndex) {
|
|
8084 int lineTop = Math.max(y, getLinePixel(lineStart));
|
|
8085 y = lineTop;
|
|
8086 height -= lineTop;
|
|
8087 }
|
|
8088 if (partialTopIndex <= lineEnd && lineEnd <= partialBottomIndex) {
|
|
8089 int newLastLineBottom = getLinePixel(lineEnd + 1);
|
|
8090 if (!isFixedLineHeight()) {
|
|
8091 scrollText(lastLineBottom, newLastLineBottom);
|
|
8092 }
|
|
8093 height = newLastLineBottom - y;
|
|
8094 }
|
|
8095 super.redraw(0, y, clientAreaWidth, height, false);
|
|
8096 }
|
|
8097 }
|
|
8098 setCaretLocation();
|
|
8099 }
|
|
8100 /**
|
|
8101 * Sets styles to be used for rendering the widget content. All styles
|
|
8102 * in the widget will be replaced with the given set of styles.
|
|
8103 * <p>
|
|
8104 * Note: Because a StyleRange includes the start and length, the
|
|
8105 * same instance cannot occur multiple times in the array of styles.
|
|
8106 * If the same style attributes, such as font and color, occur in
|
|
8107 * multiple StyleRanges, <code>setStyleRanges(int[], StyleRange[])</code>
|
|
8108 * can be used to share styles and reduce memory usage.
|
|
8109 * </p><p>
|
|
8110 * Should not be called if a LineStyleListener has been set since the
|
|
8111 * listener maintains the styles.
|
|
8112 * </p>
|
|
8113 *
|
|
8114 * @param ranges StyleRange objects containing the style information.
|
|
8115 * The ranges should not overlap. The style rendering is undefined if
|
|
8116 * the ranges do overlap. Must not be null. The styles need to be in order.
|
|
8117 * @exception SWTException <ul>
|
|
8118 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
8119 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
8120 * </ul>
|
|
8121 * @exception IllegalArgumentException <ul>
|
|
8122 * <li>ERROR_INVALID_RANGE when the last of the style ranges is outside the valid range (> getCharCount())</li>
|
|
8123 * </ul>
|
|
8124 *
|
|
8125 * @see #setStyleRanges(int[], StyleRange[])
|
|
8126 */
|
|
8127 public void setStyleRanges(StyleRange[] ranges) {
|
|
8128 checkWidget();
|
|
8129 if (isListening(LineGetStyle)) return;
|
|
8130 // SWT extension: allow null for zero length string
|
|
8131 //if (ranges is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
8132 setStyleRanges(0, 0, null, ranges, true);
|
|
8133 }
|
|
8134 /**
|
|
8135 * Sets the tab width.
|
|
8136 *
|
|
8137 * @param tabs tab width measured in characters.
|
|
8138 * @exception SWTException <ul>
|
|
8139 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
8140 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
8141 * </ul>
|
|
8142 */
|
|
8143 public void setTabs(int tabs) {
|
|
8144 checkWidget();
|
|
8145 tabLength = tabs;
|
|
8146 renderer.setFont(null, tabs);
|
|
8147 resetCache(0, content.getLineCount());
|
|
8148 setCaretLocation();
|
|
8149 super.redraw();
|
|
8150 }
|
|
8151 /**
|
|
8152 * Sets the widget content.
|
|
8153 * If the widget has the SWT.SINGLE style and "text" contains more than
|
|
8154 * one line, only the first line is rendered but the text is stored
|
|
8155 * unchanged. A subsequent call to getText will return the same text
|
|
8156 * that was set.
|
|
8157 * <p>
|
|
8158 * <b>Note:</b> Only a single line of text should be set when the SWT.SINGLE
|
|
8159 * style is used.
|
|
8160 * </p>
|
|
8161 *
|
|
8162 * @param text new widget content. Replaces existing content. Line styles
|
|
8163 * that were set using StyledText API are discarded. The
|
|
8164 * current selection is also discarded.
|
|
8165 * @exception SWTException <ul>
|
|
8166 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
8167 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
8168 * </ul>
|
|
8169 */
|
|
8170 public void setText(String text) {
|
|
8171 checkWidget();
|
|
8172 // SWT extension: allow null for zero length string
|
|
8173 // if (text is null) {
|
|
8174 // SWT.error(SWT.ERROR_NULL_ARGUMENT);
|
|
8175 // }
|
|
8176 Event event = new Event();
|
|
8177 event.start = 0;
|
|
8178 event.end = getCharCount();
|
|
8179 event.text = text;
|
|
8180 event.doit = true;
|
|
8181 notifyListeners(SWT.Verify, event);
|
|
8182 if (event.doit) {
|
|
8183 StyledTextEvent styledTextEvent = null;
|
|
8184 if (isListening(ExtendedModify)) {
|
|
8185 styledTextEvent = new StyledTextEvent(content);
|
|
8186 styledTextEvent.start = event.start;
|
|
8187 styledTextEvent.end = event.start + event.text.length;
|
|
8188 styledTextEvent.text = content.getTextRange(event.start, event.end - event.start);
|
|
8189 }
|
|
8190 content.setText(event.text);
|
|
8191 sendModifyEvent(event);
|
|
8192 if (styledTextEvent !is null) {
|
|
8193 notifyListeners(ExtendedModify, styledTextEvent);
|
|
8194 }
|
|
8195 }
|
|
8196 }
|
|
8197 /**
|
|
8198 * Sets the text limit to the specified number of characters.
|
|
8199 * <p>
|
|
8200 * The text limit specifies the amount of text that
|
|
8201 * the user can type into the widget.
|
|
8202 * </p>
|
|
8203 *
|
|
8204 * @param limit the new text limit.
|
|
8205 * @exception SWTException <ul>
|
|
8206 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
8207 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
8208 * </ul>
|
|
8209 * @exception IllegalArgumentException <ul>
|
|
8210 * <li>ERROR_CANNOT_BE_ZERO when limit is 0</li>
|
|
8211 * </ul>
|
|
8212 */
|
|
8213 public void setTextLimit(int limit) {
|
|
8214 checkWidget();
|
|
8215 if (limit is 0) {
|
|
8216 SWT.error(SWT.ERROR_CANNOT_BE_ZERO);
|
|
8217 }
|
|
8218 textLimit = limit;
|
|
8219 }
|
|
8220 /**
|
|
8221 * Sets the top index. Do nothing if there is no text set.
|
|
8222 * <p>
|
|
8223 * The top index is the index of the line that is currently at the top
|
|
8224 * of the widget. The top index changes when the widget is scrolled.
|
|
8225 * Indexing starts from zero.
|
|
8226 * Note: The top index is reset to 0 when new text is set in the widget.
|
|
8227 * </p>
|
|
8228 *
|
|
8229 * @param topIndex new top index. Must be between 0 and
|
|
8230 * getLineCount() - fully visible lines per page. If no lines are fully
|
|
8231 * visible the maximum value is getLineCount() - 1. An out of range
|
|
8232 * index will be adjusted accordingly.
|
|
8233 * @exception SWTException <ul>
|
|
8234 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
8235 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
8236 * </ul>
|
|
8237 */
|
|
8238 public void setTopIndex(int topIndex) {
|
|
8239 checkWidget();
|
|
8240 if (getCharCount() is 0) {
|
|
8241 return;
|
|
8242 }
|
|
8243 int lineCount = content.getLineCount(), pixel;
|
|
8244 if (isFixedLineHeight()) {
|
|
8245 int pageSize = Math.max(1, Math.min(lineCount, getLineCountWhole()));
|
|
8246 if (topIndex < 0) {
|
|
8247 topIndex = 0;
|
|
8248 } else if (topIndex > lineCount - pageSize) {
|
|
8249 topIndex = lineCount - pageSize;
|
|
8250 }
|
|
8251 pixel = getLinePixel(topIndex);
|
|
8252 } else {
|
|
8253 topIndex = Math.max(0, Math.min(lineCount - 1, topIndex));
|
|
8254 pixel = getLinePixel(topIndex);
|
|
8255 if (pixel > 0) {
|
|
8256 pixel = getAvailableHeightBellow(pixel);
|
|
8257 } else {
|
|
8258 pixel = getAvailableHeightAbove(pixel);
|
|
8259 }
|
|
8260 }
|
|
8261 scrollVertical(pixel, true);
|
|
8262 }
|
|
8263 /**
|
|
8264 * Sets the top pixel offset. Do nothing if there is no text set.
|
|
8265 * <p>
|
|
8266 * The top pixel offset is the vertical pixel offset of the widget. The
|
|
8267 * widget is scrolled so that the given pixel position is at the top.
|
|
8268 * The top index is adjusted to the corresponding top line.
|
|
8269 * Note: The top pixel is reset to 0 when new text is set in the widget.
|
|
8270 * </p>
|
|
8271 *
|
|
8272 * @param pixel new top pixel offset. Must be between 0 and
|
|
8273 * (getLineCount() - visible lines per page) / getLineHeight()). An out
|
|
8274 * of range offset will be adjusted accordingly.
|
|
8275 * @exception SWTException <ul>
|
|
8276 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
8277 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
8278 * </ul>
|
|
8279 * @since 2.0
|
|
8280 */
|
|
8281 public void setTopPixel(int pixel) {
|
|
8282 checkWidget();
|
|
8283 if (getCharCount() is 0) {
|
|
8284 return;
|
|
8285 }
|
|
8286 if (pixel < 0) pixel = 0;
|
|
8287 int lineCount = content.getLineCount();
|
|
8288 int height = clientAreaHeight - topMargin - bottomMargin;
|
|
8289 int verticalOffset = getVerticalScrollOffset();
|
|
8290 if (isFixedLineHeight()) {
|
|
8291 int maxTopPixel = Math.max(0, lineCount * getVerticalIncrement() - height);
|
|
8292 if (pixel > maxTopPixel) pixel = maxTopPixel;
|
|
8293 pixel -= verticalOffset;
|
|
8294 } else {
|
|
8295 pixel -= verticalOffset;
|
|
8296 if (pixel > 0) {
|
|
8297 pixel = getAvailableHeightBellow(pixel);
|
|
8298 }
|
|
8299 }
|
|
8300 scrollVertical(pixel, true);
|
|
8301 }
|
|
8302 /**
|
|
8303 * Sets whether the widget wraps lines.
|
|
8304 * <p>
|
|
8305 * This overrides the creation style bit SWT.WRAP.
|
|
8306 * </p>
|
|
8307 *
|
|
8308 * @param wrap true=widget wraps lines, false=widget does not wrap lines
|
|
8309 * @since 2.0
|
|
8310 */
|
|
8311 public void setWordWrap(bool wrap) {
|
|
8312 checkWidget();
|
|
8313 if ((getStyle() & SWT.SINGLE) !is 0) return;
|
|
8314 if (wordWrap is wrap) return;
|
|
8315 wordWrap = wrap;
|
|
8316 setVariableLineHeight();
|
|
8317 resetCache(0, content.getLineCount());
|
|
8318 horizontalScrollOffset = 0;
|
|
8319 ScrollBar horizontalBar = getHorizontalBar();
|
|
8320 if (horizontalBar !is null) {
|
|
8321 horizontalBar.setVisible(!wordWrap);
|
|
8322 }
|
|
8323 setScrollBars(true);
|
|
8324 setCaretLocation();
|
|
8325 super.redraw();
|
|
8326 }
|
|
8327 // SWT: If necessary, scroll to show the location
|
|
8328 bool showLocation(Rectangle rect, bool scrollPage) {
|
|
8329 int clientAreaWidth = this.clientAreaWidth - leftMargin - rightMargin;
|
|
8330 int clientAreaHeight = this.clientAreaHeight - topMargin - bottomMargin;
|
|
8331 bool scrolled = false;
|
|
8332 if (rect.y <= topMargin) {
|
|
8333 scrolled = scrollVertical(rect.y - topMargin, true);
|
|
8334 } else if (rect.y + rect.height > clientAreaHeight) {
|
|
8335 if (clientAreaHeight is 0) {
|
|
8336 scrolled = scrollVertical(rect.y, true);
|
|
8337 } else {
|
|
8338 scrolled = scrollVertical(rect.y + rect.height - clientAreaHeight, true);
|
|
8339 }
|
|
8340 }
|
|
8341 if (clientAreaWidth > 0) {
|
|
8342 int minScroll = scrollPage ? clientAreaWidth / 4 : 0;
|
|
8343 if (rect.x < leftMargin) {
|
|
8344 int scrollWidth = Math.max(leftMargin - rect.x, minScroll);
|
|
8345 int maxScroll = horizontalScrollOffset;
|
|
8346 scrolled = scrollHorizontal(-Math.min(maxScroll, scrollWidth), true);
|
|
8347 } else if (rect.x + rect.width > clientAreaWidth) {
|
|
8348 int scrollWidth = Math.max(rect.x + rect.width - clientAreaWidth, minScroll);
|
|
8349 int maxScroll = renderer.getWidth() - horizontalScrollOffset - this.clientAreaWidth;
|
|
8350 scrolled = scrollHorizontal(Math.min(maxScroll, scrollWidth), true);
|
|
8351 }
|
|
8352 }
|
|
8353 return scrolled;
|
|
8354 }
|
|
8355 /**
|
|
8356 * Sets the caret location and scrolls the caret offset into view.
|
|
8357 */
|
|
8358 void showCaret() {
|
|
8359 Rectangle bounds = getBoundsAtOffset(caretOffset);
|
|
8360 if (!showLocation(bounds, true)) {
|
|
8361 setCaretLocation();
|
|
8362 }
|
|
8363 }
|
|
8364 /**
|
|
8365 * Scrolls the selection into view.
|
|
8366 * <p>
|
|
8367 * The end of the selection will be scrolled into view.
|
|
8368 * Note that if a right-to-left selection exists, the end of the selection is
|
|
8369 * the visual beginning of the selection (i.e., where the caret is located).
|
|
8370 * </p>
|
|
8371 *
|
|
8372 * @exception SWTException <ul>
|
|
8373 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
|
|
8374 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
|
|
8375 * </ul>
|
|
8376 */
|
|
8377 public void showSelection() {
|
|
8378 checkWidget();
|
|
8379 // is selection from right-to-left?
|
|
8380 bool rightToLeft = caretOffset is selection.x;
|
|
8381 int startOffset, endOffset;
|
|
8382 if (rightToLeft) {
|
|
8383 startOffset = selection.y;
|
|
8384 endOffset = selection.x;
|
|
8385 } else {
|
|
8386 startOffset = selection.x;
|
|
8387 endOffset = selection.y;
|
|
8388 }
|
|
8389
|
|
8390 Rectangle startBounds = getBoundsAtOffset(startOffset);
|
|
8391 Rectangle endBounds = getBoundsAtOffset(endOffset);
|
|
8392
|
|
8393 // can the selection be fully displayed within the widget's visible width?
|
|
8394 int w = clientAreaWidth - leftMargin - rightMargin;
|
|
8395 bool selectionFits = rightToLeft ? startBounds.x - endBounds.x <= w : endBounds.x - startBounds.x <= w;
|
|
8396 if (selectionFits) {
|
|
8397 // show as much of the selection as possible by first showing
|
|
8398 // the start of the selection
|
|
8399 if (showLocation(startBounds, false)) {
|
|
8400 // endX value could change if showing startX caused a scroll to occur
|
|
8401 endBounds = getBoundsAtOffset(endOffset);
|
|
8402 }
|
|
8403 // the character at endOffset is not part of the selection
|
|
8404 endBounds.width = 0;
|
|
8405 showLocation(endBounds, false);
|
|
8406 } else {
|
|
8407 // just show the end of the selection since the selection start
|
|
8408 // will not be visible
|
|
8409 showLocation(endBounds, true);
|
|
8410 }
|
|
8411 }
|
|
8412 /**
|
|
8413 * Updates the selection and caret position depending on the text change.
|
|
8414 * <p>
|
|
8415 * If the selection intersects with the replaced text, the selection is
|
|
8416 * reset and the caret moved to the end of the new text.
|
|
8417 * If the selection is behind the replaced text it is moved so that the
|
|
8418 * same text remains selected. If the selection is before the replaced text
|
|
8419 * it is left unchanged.
|
|
8420 * </p>
|
|
8421 *
|
|
8422 * @param startOffset offset of the text change
|
|
8423 * @param replacedLength length of text being replaced
|
|
8424 * @param newLength length of new text
|
|
8425 */
|
|
8426 void updateSelection(int startOffset, int replacedLength, int newLength) {
|
|
8427 if (selection.y <= startOffset) {
|
|
8428 // selection ends before text change
|
|
8429 if (wordWrap) setCaretLocation();
|
|
8430 return;
|
|
8431 }
|
|
8432 if (selection.x < startOffset) {
|
|
8433 // clear selection fragment before text change
|
|
8434 internalRedrawRange(selection.x, startOffset - selection.x);
|
|
8435 }
|
|
8436 if (selection.y > startOffset + replacedLength && selection.x < startOffset + replacedLength) {
|
|
8437 // clear selection fragment after text change.
|
|
8438 // do this only when the selection is actually affected by the
|
|
8439 // change. Selection is only affected if it intersects the change (1GDY217).
|
|
8440 int netNewLength = newLength - replacedLength;
|
|
8441 int redrawStart = startOffset + newLength;
|
|
8442 internalRedrawRange(redrawStart, selection.y + netNewLength - redrawStart);
|
|
8443 }
|
|
8444 if (selection.y > startOffset && selection.x < startOffset + replacedLength) {
|
|
8445 // selection intersects replaced text. set caret behind text change
|
|
8446 setSelection(startOffset + newLength, 0, true);
|
|
8447 } else {
|
|
8448 // move selection to keep same text selected
|
|
8449 setSelection(selection.x + newLength - replacedLength, selection.y - selection.x, true);
|
|
8450 }
|
|
8451 setCaretLocation();
|
|
8452 }
|
|
8453 }
|