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