comparison dwt/custom/StyledText.d @ 213:36f5cb12e1a2

Update to SWT 3.4M7
author Frank Benoit <benoit@tionex.de>
date Sat, 17 May 2008 17:34:28 +0200
parents ab60f3309436
children a8fed3e56433
comparison
equal deleted inserted replaced
212:ab60f3309436 213:36f5cb12e1a2
54 import dwt.widgets.Caret; 54 import dwt.widgets.Caret;
55 import dwt.widgets.Composite; 55 import dwt.widgets.Composite;
56 import dwt.widgets.Control; 56 import dwt.widgets.Control;
57 import dwt.widgets.Display; 57 import dwt.widgets.Display;
58 import dwt.widgets.Event; 58 import dwt.widgets.Event;
59 import dwt.widgets.IME;
59 import dwt.widgets.Label; 60 import dwt.widgets.Label;
60 import dwt.widgets.Listener; 61 import dwt.widgets.Listener;
61 import dwt.widgets.ScrollBar; 62 import dwt.widgets.ScrollBar;
62 import dwt.widgets.TypedListener; 63 import dwt.widgets.TypedListener;
63 import dwt.custom.StyledTextContent; 64 import dwt.custom.StyledTextContent;
194 Point clipboardSelection; // x and y are start and end caret offsets of previous selection 195 Point clipboardSelection; // x and y are start and end caret offsets of previous selection
195 int selectionAnchor; // position of selection anchor. 0 based offset from beginning of text 196 int selectionAnchor; // position of selection anchor. 0 based offset from beginning of text
196 Point doubleClickSelection; // selection after last mouse double click 197 Point doubleClickSelection; // selection after last mouse double click
197 bool editable = true; 198 bool editable = true;
198 bool wordWrap = false; 199 bool wordWrap = false;
199 bool doubleClickEnabled = true; // see getDoubleClickEnabled 200 bool doubleClickEnabled = true; // see getDoubleClickEnabled
200 bool overwrite = false; // insert/overwrite edit mode 201 bool overwrite = false; // insert/overwrite edit mode
201 int textLimit = -1; // limits the number of characters the user can type in the widget. Unlimited by default. 202 int textLimit = -1; // limits the number of characters the user can type in the widget. Unlimited by default.
202 int[int] keyActionMap; 203 int[int] keyActionMap;
203 Color background = null; // workaround for bug 4791 204 Color background = null; // workaround for bug 4791
204 Color foreground = null; // 205 Color foreground = null; //
205 Clipboard clipboard; 206 Clipboard clipboard;
211 int lastTextChangeNewCharCount; // event for use in the 212 int lastTextChangeNewCharCount; // event for use in the
212 int lastTextChangeReplaceLineCount; // text changed handler 213 int lastTextChangeReplaceLineCount; // text changed handler
213 int lastTextChangeReplaceCharCount; 214 int lastTextChangeReplaceCharCount;
214 int lastLineBottom; // the bottom pixel of the last line been replaced 215 int lastLineBottom; // the bottom pixel of the last line been replaced
215 bool isMirrored_; 216 bool isMirrored_;
216 bool bidiColoring = false; // apply the BIDI algorithm on text segments of the same color 217 bool bidiColoring = false; // apply the BIDI algorithm on text segments of the same color
217 Image leftCaretBitmap = null; 218 Image leftCaretBitmap = null;
218 Image rightCaretBitmap = null; 219 Image rightCaretBitmap = null;
219 int caretDirection = DWT.NULL; 220 int caretDirection = DWT.NULL;
221 int caretWidth = 0;
220 Caret defaultCaret = null; 222 Caret defaultCaret = null;
221 bool updateCaretDirection = true; 223 bool updateCaretDirection = true;
222 bool fixedLineHeight; 224 bool fixedLineHeight;
223 bool dragDetect_ = true; 225 bool dragDetect_ = true;
226 IME ime;
224 227
225 int alignment; 228 int alignment;
226 bool justify; 229 bool justify;
227 int indent; 230 int indent;
228 int lineSpacing; 231 int lineSpacing;
258 int pageWidth; // width of a printer page in pixels 261 int pageWidth; // width of a printer page in pixels
259 int startPage; // first page to print 262 int startPage; // first page to print
260 int endPage; // last page to print 263 int endPage; // last page to print
261 int startLine; // first (wrapped) line to print 264 int startLine; // first (wrapped) line to print
262 int endLine; // last (wrapped) line to print 265 int endLine; // last (wrapped) line to print
263 bool singleLine; // widget single line mode 266 bool singleLine; // widget single line mode
264 Point selection = null; // selected text 267 Point selection = null; // selected text
265 bool mirrored; // indicates the printing gc should be mirrored 268 bool mirrored; // indicates the printing gc should be mirrored
266 int lineSpacing; 269 int lineSpacing;
267 int printMargin; 270 int printMargin;
268 271
269 /** 272 /**
270 * Creates an instance of <code>Printing</code>. 273 * Creates an instance of <code>Printing</code>.
524 if (printOptions.printLineNumbers || printOptions.header !is null || printOptions.footer !is null) { 527 if (printOptions.printLineNumbers || printOptions.header !is null || printOptions.footer !is null) {
525 printLayout = new TextLayout(printer); 528 printLayout = new TextLayout(printer);
526 printLayout.setFont(printerFont); 529 printLayout.setFont(printerFont);
527 } 530 }
528 if (printOptions.printLineNumbers) { 531 if (printOptions.printLineNumbers) {
532 int numberingWidth = 0;
529 int count = endLine - startLine + 1; 533 int count = endLine - startLine + 1;
530 StringBuffer buffer = new StringBuffer("0"); 534 String[] lineLabels = printOptions.lineLabels;
531 while ((count /= 10) > 0) buffer.append("0"); 535 if (lineLabels !is null) {
532 printLayout.setText(buffer.toString()); 536 for (int i = startLine; i < Math.min(count, lineLabels.length); i++) {
533 int numberingWidth = printLayout.getBounds().width + printMargin; 537 if (lineLabels[i] !is null) {
538 printLayout.setText(lineLabels[i]);
539 int lineWidth = printLayout.getBounds().width;
540 numberingWidth = Math.max(numberingWidth, lineWidth);
541 }
542 }
543 } else {
544 StringBuffer buffer = new StringBuffer("0");
545 while ((count /= 10) > 0) buffer.append("0");
546 printLayout.setText(buffer.toString());
547 numberingWidth = printLayout.getBounds().width;
548 }
549 numberingWidth += printMargin;
534 if (numberingWidth > width) numberingWidth = width; 550 if (numberingWidth > width) numberingWidth = width;
535 paintX += numberingWidth; 551 paintX += numberingWidth;
536 width -= numberingWidth; 552 width -= numberingWidth;
537 } 553 }
538 for (int i = startLine; i <= endLine && page <= endPage; i++) { 554 for (int i = startLine; i <= endLine && page <= endPage; i++) {
566 paintY += layout.getBounds().height; 582 paintY += layout.getBounds().height;
567 } 583 }
568 } else { 584 } else {
569 //draw paragraph top in the current page and paragraph bottom in the next 585 //draw paragraph top in the current page and paragraph bottom in the next
570 int height = paragraphBottom - paintY; 586 int height = paragraphBottom - paintY;
571 gc.setClipping(paintX, paintY, width, height); 587 gc.setClipping(clientArea.x, paintY, clientArea.width, height);
572 printLine(paintX, paintY, gc, foreground, lineBackground, layout, printLayout, i); 588 printLine(paintX, paintY, gc, foreground, lineBackground, layout, printLayout, i);
589 gc.setClipping(cast(Rectangle)null);
573 printDecoration(page, false, printLayout); 590 printDecoration(page, false, printLayout);
574 printer.endPage(); 591 printer.endPage();
575 page++; 592 page++;
576 if (page <= endPage) { 593 if (page <= endPage) {
577 printer.startPage(); 594 printer.startPage();
578 printDecoration(page, true, printLayout); 595 printDecoration(page, true, printLayout);
579 paintY = clientArea.y - height; 596 paintY = clientArea.y - height;
580 int layoutHeight = layout.getBounds().height; 597 int layoutHeight = layout.getBounds().height;
581 gc.setClipping(paintX, clientArea.y, width, layoutHeight - height); 598 gc.setClipping(clientArea.x, clientArea.y, clientArea.width, layoutHeight - height);
582 printLine(paintX, paintY, gc, foreground, lineBackground, layout, printLayout, i); 599 printLine(paintX, paintY, gc, foreground, lineBackground, layout, printLayout, i);
600 gc.setClipping(cast(Rectangle)null);
583 paintY += layoutHeight; 601 paintY += layoutHeight;
584 } 602 }
585 gc.setClipping(cast(Rectangle)null);
586 } 603 }
587 } 604 }
588 printerRenderer.disposeTextLayout(layout); 605 printerRenderer.disposeTextLayout(layout);
589 } 606 }
590 if (paintY > clientArea.y) { 607 if (page <= endPage && paintY > clientArea.y) {
591 // close partial page 608 // close partial page
592 printDecoration(page, false, printLayout); 609 printDecoration(page, false, printLayout);
593 printer.endPage(); 610 printer.endPage();
594 } 611 }
595 if (printLayout !is null) printLayout.dispose(); 612 if (printLayout !is null) printLayout.dispose();
672 // gc.fillRectangle(rect); 689 // gc.fillRectangle(rect);
673 // } 690 // }
674 } 691 }
675 if (printOptions.printLineNumbers) { 692 if (printOptions.printLineNumbers) {
676 FontMetrics metrics = layout.getLineMetrics(0); 693 FontMetrics metrics = layout.getLineMetrics(0);
677 printLayout.setAscent(metrics.getAscent() + metrics.getDescent()); 694 printLayout.setAscent(metrics.getAscent() + metrics.getLeading());
678 printLayout.setDescent(metrics.getDescent()); 695 printLayout.setDescent(metrics.getDescent());
679 printLayout.setText( to!(String)(index) ); 696 String[] lineLabels = printOptions.lineLabels;
697 if (lineLabels !is null) {
698 if (0 <= index && index < lineLabels.length && lineLabels[index] !is null) {
699 printLayout.setText(lineLabels[index]);
700 } else {
701 printLayout.setText("");
702 }
703 } else {
704 printLayout.setText( to!(String)(index) );
705 }
680 int paintX = x - printMargin - printLayout.getBounds().width; 706 int paintX = x - printMargin - printLayout.getBounds().width;
681 printLayout.draw(gc, paintX, y); 707 printLayout.draw(gc, paintX, y);
682 printLayout.setAscent(-1); 708 printLayout.setAscent(-1);
683 printLayout.setDescent(-1); 709 printLayout.setDescent(-1);
684 } 710 }
1310 clipboard = new Clipboard(display); 1336 clipboard = new Clipboard(display);
1311 installDefaultContent(); 1337 installDefaultContent();
1312 renderer = new StyledTextRenderer(getDisplay(), this); 1338 renderer = new StyledTextRenderer(getDisplay(), this);
1313 renderer.setContent(content); 1339 renderer.setContent(content);
1314 renderer.setFont(getFont(), tabLength); 1340 renderer.setFont(getFont(), tabLength);
1315 defaultCaret = new Caret(this, DWT.NULL); 1341 ime = new IME(this, DWT.NONE);
1342 defaultCaret = new Caret(this, DWT.NONE);
1316 if ((style & DWT.WRAP) !is 0) { 1343 if ((style & DWT.WRAP) !is 0) {
1317 setWordWrap(true); 1344 setWordWrap(true);
1318 } 1345 }
1319 if (isBidiCaret()) { 1346 if (isBidiCaret()) {
1320 createCaretBitmaps(); 1347 createCaretBitmaps();
1325 if (getCaret() !is defaultCaret) return; 1352 if (getCaret() !is defaultCaret) return;
1326 Point newCaretPos = getPointAtOffset(caretOffset); 1353 Point newCaretPos = getPointAtOffset(caretOffset);
1327 setCaretLocation(newCaretPos, direction); 1354 setCaretLocation(newCaretPos, direction);
1328 } 1355 }
1329 }; 1356 };
1330 BidiUtil.addLanguageListener(handle, runnable); 1357 BidiUtil.addLanguageListener(this, runnable);
1331 } 1358 }
1332 setCaret(defaultCaret); 1359 setCaret(defaultCaret);
1333 calculateScrollBars(); 1360 calculateScrollBars();
1334 createKeyBindings(); 1361 createKeyBindings();
1335 setCursor(display.getSystemCursor(DWT.CURSOR_IBEAM)); 1362 setCursor(display.getSystemCursor(DWT.CURSOR_IBEAM));
1749 if (wHint is DWT.DEFAULT || hHint is DWT.DEFAULT) { 1776 if (wHint is DWT.DEFAULT || hHint is DWT.DEFAULT) {
1750 Display display = getDisplay(); 1777 Display display = getDisplay();
1751 int maxHeight = display.getClientArea().height; 1778 int maxHeight = display.getClientArea().height;
1752 for (int lineIndex = 0; lineIndex < lineCount; lineIndex++) { 1779 for (int lineIndex = 0; lineIndex < lineCount; lineIndex++) {
1753 TextLayout layout = renderer.getTextLayout(lineIndex); 1780 TextLayout layout = renderer.getTextLayout(lineIndex);
1781 int wrapWidth = layout.getWidth();
1754 if (wordWrap) layout.setWidth(wHint is 0 ? 1 : wHint); 1782 if (wordWrap) layout.setWidth(wHint is 0 ? 1 : wHint);
1755 Rectangle rect = layout.getBounds(); 1783 Rectangle rect = layout.getBounds();
1756 height += rect.height; 1784 height += rect.height;
1757 width = Math.max(width, rect.width); 1785 width = Math.max(width, rect.width);
1786 layout.setWidth(wrapWidth);
1758 renderer.disposeTextLayout(layout); 1787 renderer.disposeTextLayout(layout);
1759 if (isFixedLineHeight() && height > maxHeight) break; 1788 if (isFixedLineHeight() && height > maxHeight) break;
1760 } 1789 }
1761 if (isFixedLineHeight()) { 1790 if (isFixedLineHeight()) {
1762 height = lineCount * renderer.getLineHeight(); 1791 height = lineCount * renderer.getLineHeight();
1941 if (event.button !is 2) return false; 1970 if (event.button !is 2) return false;
1942 } else { 1971 } else {
1943 if (event.button !is 1) return false; 1972 if (event.button !is 1) return false;
1944 } 1973 }
1945 if (selection.x is selection.y) return false; 1974 if (selection.x is selection.y) return false;
1946 int offset = getOffsetAtPoint(event.x, event.y); 1975 int offset = getOffsetAtPoint(event.x, event.y, null, true);
1947 if (offset > selection.x && offset < selection.y) { 1976 if (selection.x <= offset && offset < selection.y) {
1948 return dragDetect(event); 1977 return dragDetect(event);
1949 } 1978 }
1950 return false; 1979 return false;
1951 } 1980 }
1952 /** 1981 /**
2234 * </p> 2263 * </p>
2235 * 2264 *
2236 * @param key the character typed by the user 2265 * @param key the character typed by the user
2237 */ 2266 */
2238 void doContent(char key) { 2267 void doContent(char key) {
2239 if (textLimit > 0 &&
2240 content.getCharCount() - (selection.y - selection.x) >= textLimit) {
2241 return;
2242 }
2243 Event event = new Event(); 2268 Event event = new Event();
2244 event.start = selection.x; 2269 event.start = selection.x;
2245 event.end = selection.y; 2270 event.end = selection.y;
2246 // replace a CR line break with the widget line break 2271 // replace a CR line break with the widget line break
2247 // CR does not make sense on Windows since most (all?) applications 2272 // CR does not make sense on Windows since most (all?) applications
2264 event.text = [key]; 2289 event.text = [key];
2265 } else { 2290 } else {
2266 event.text = [key]; 2291 event.text = [key];
2267 } 2292 }
2268 if (event.text !is null) { 2293 if (event.text !is null) {
2294 if (textLimit > 0 && content.getCharCount() - (event.end - event.start) >= textLimit) {
2295 return;
2296 }
2269 sendKeyEvent(event); 2297 sendKeyEvent(event);
2270 } 2298 }
2271 } 2299 }
2272 /** 2300 /**
2273 * Moves the caret after the last character of the widget content. 2301 * Moves the caret after the last character of the widget content.
2528 return; 2556 return;
2529 } 2557 }
2530 int oldCaretAlignment = caretAlignment; 2558 int oldCaretAlignment = caretAlignment;
2531 int newCaretOffset = getOffsetAtPoint(x, y); 2559 int newCaretOffset = getOffsetAtPoint(x, y);
2532 2560
2533 if (clickCount > 1) { 2561 if (doubleClickEnabled && clickCount > 1) {
2534 // double click word select the previous/next word. fixes bug 15610
2535 newCaretOffset = doMouseWordSelect(x, newCaretOffset, line); 2562 newCaretOffset = doMouseWordSelect(x, newCaretOffset, line);
2536 } 2563 }
2537 2564
2538 int newCaretLine = content.getLineAtOffset(newCaretOffset); 2565 int newCaretLine = content.getLineAtOffset(newCaretOffset);
2539 2566
3244 } 3271 }
3245 return bottomIndex; 3272 return bottomIndex;
3246 } 3273 }
3247 Rectangle getBoundsAtOffset(int offset) { 3274 Rectangle getBoundsAtOffset(int offset) {
3248 int lineIndex = content.getLineAtOffset(offset); 3275 int lineIndex = content.getLineAtOffset(offset);
3276 int lineOffset = content.getOffsetAtLine(lineIndex);
3249 String line = content.getLine(lineIndex); 3277 String line = content.getLine(lineIndex);
3250 Rectangle bounds; 3278 Rectangle bounds;
3251 if (line.length !is 0) { 3279 if (line.length !is 0) {
3252 int offsetInLine = offset - content.getOffsetAtLine(lineIndex); 3280 int offsetInLine = offset - lineOffset;
3253 TextLayout layout = renderer.getTextLayout(lineIndex); 3281 TextLayout layout = renderer.getTextLayout(lineIndex);
3254 bounds = layout.getBounds(offsetInLine, offsetInLine); 3282 bounds = layout.getBounds(offsetInLine, offsetInLine);
3255 renderer.disposeTextLayout(layout); 3283 renderer.disposeTextLayout(layout);
3256 } else { 3284 } else {
3257 bounds = new Rectangle (0, 0, 0, renderer.getLineHeight()); 3285 bounds = new Rectangle (0, 0, 0, renderer.getLineHeight());
3258 } 3286 }
3287 if (offset is caretOffset) {
3288 int lineEnd = lineOffset + line.length;
3289 if (offset is lineEnd && caretAlignment is PREVIOUS_OFFSET_TRAILING) {
3290 bounds.width += getCaretWidth();
3291 }
3292 }
3259 bounds.x += leftMargin - horizontalScrollOffset; 3293 bounds.x += leftMargin - horizontalScrollOffset;
3260 bounds.y += getLinePixel(lineIndex); 3294 bounds.y += getLinePixel(lineIndex);
3261 return bounds; 3295 return bounds;
3262 } 3296 }
3263
3264 /** 3297 /**
3265 * Returns the caret position relative to the start of the text. 3298 * Returns the caret position relative to the start of the text.
3266 * 3299 *
3267 * @return the caret position relative to the start of the text. 3300 * @return the caret position relative to the start of the text.
3268 * @exception DWTException <ul> 3301 * @exception DWTException <ul>
3305 offset += lineOffset; 3338 offset += lineOffset;
3306 renderer.disposeTextLayout(layout); 3339 renderer.disposeTextLayout(layout);
3307 return offset; 3340 return offset;
3308 } 3341 }
3309 /** 3342 /**
3310 * Returns the content implementation that is used for text storage 3343 * Returns the content implementation that is used for text storage.
3311 * or null if no user defined content implementation has been set. 3344 *
3312 * 3345 * @return content the user defined content implementation that is used for
3313 * @return content implementation that is used for text storage or null 3346 * text storage or the default content implementation if no user defined
3314 * if no user defined content implementation has been set. 3347 * content implementation has been set.
3315 * @exception DWTException <ul> 3348 * @exception DWTException <ul>
3316 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3349 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3317 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3350 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3318 * </ul> 3351 * </ul>
3319 */ 3352 */
3464 * </ul> 3497 * </ul>
3465 */ 3498 */
3466 public int getCharCount() { 3499 public int getCharCount() {
3467 checkWidget(); 3500 checkWidget();
3468 return content.getCharCount(); 3501 return content.getCharCount();
3502 }
3503 /**
3504 * Returns the line at the given line index without delimiters.
3505 * Index 0 is the first line of the content. When there are not
3506 * any lines, getLine(0) is a valid call that answers an empty string.
3507 * <p>
3508 *
3509 * @param lineIndex index of the line to return.
3510 * @return the line text without delimiters
3511 *
3512 * @exception DWTException <ul>
3513 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3514 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3515 * </ul>
3516 * @exception IllegalArgumentException <ul>
3517 * <li>ERROR_INVALID_RANGE when the line index is outside the valid range (< 0 or >= getLineCount())</li>
3518 * </ul>
3519 * @since 3.4
3520 */
3521 public String getLine(int lineIndex) {
3522 checkWidget();
3523 if (lineIndex < 0 ||
3524 (lineIndex > 0 && lineIndex >= content.getLineCount())) {
3525 DWT.error(DWT.ERROR_INVALID_RANGE);
3526 }
3527 return content.getLine(lineIndex);
3469 } 3528 }
3470 /** 3529 /**
3471 * Returns the alignment of the line at the given index. 3530 * Returns the alignment of the line at the given index.
3472 * 3531 *
3473 * @param index the index of the line 3532 * @param index the index of the line
3857 * @exception DWTException <ul> 3916 * @exception DWTException <ul>
3858 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3917 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3859 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3918 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3860 * </ul> 3919 * </ul>
3861 * @exception IllegalArgumentException <ul> 3920 * @exception IllegalArgumentException <ul>
3862 * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li> 3921 * <li>ERROR_INVALID_RANGE when the line index is outside the valid range (< 0 or >= getLineCount())</li>
3863 * </ul> 3922 * </ul>
3864 * @since 2.0 3923 * @since 2.0
3865 */ 3924 */
3866 public int getOffsetAtLine(int lineIndex) { 3925 public int getOffsetAtLine(int lineIndex) {
3867 checkWidget(); 3926 checkWidget();
3897 public int getOffsetAtLocation(Point point) { 3956 public int getOffsetAtLocation(Point point) {
3898 checkWidget(); 3957 checkWidget();
3899 if (point is null) { 3958 if (point is null) {
3900 DWT.error(DWT.ERROR_NULL_ARGUMENT); 3959 DWT.error(DWT.ERROR_NULL_ARGUMENT);
3901 } 3960 }
3902 // is y above first line or is x before first column? 3961 int[] trailing = new int[1];
3903 if (point.y + getVerticalScrollOffset() < 0 || point.x + horizontalScrollOffset < 0) { 3962 int offset = getOffsetAtPoint(point.x, point.y, trailing, true);
3963 if (offset is -1) {
3904 DWT.error(DWT.ERROR_INVALID_ARGUMENT); 3964 DWT.error(DWT.ERROR_INVALID_ARGUMENT);
3905 } 3965 }
3906 int bottomIndex = getLineIndex(clientAreaHeight); 3966 return offset + trailing[0];
3907 int height = getLinePixel(bottomIndex) + renderer.getLineHeight(bottomIndex);
3908 if (point.y > height) {
3909 DWT.error(DWT.ERROR_INVALID_ARGUMENT);
3910 }
3911 int lineIndex = getLineIndex(point.y);
3912 int lineOffset = content.getOffsetAtLine(lineIndex);
3913 TextLayout layout = renderer.getTextLayout(lineIndex);
3914 int[] trailing = new int[1];
3915 int x = point.x + horizontalScrollOffset - leftMargin ;
3916 int y = point.y - getLinePixel(lineIndex);
3917 int offsetInLine = layout.getOffset(x, y, trailing);
3918 String line = content.getLine(lineIndex);
3919 if (offsetInLine !is line.length - 1) {
3920 offsetInLine = Math.min(line.length, offsetInLine + trailing[0]);
3921 }
3922 Rectangle rect = layout.getLineBounds(layout.getLineIndex(offsetInLine));
3923 renderer.disposeTextLayout(layout);
3924 if (x > rect.x + rect.width) {
3925 DWT.error(DWT.ERROR_INVALID_ARGUMENT);
3926 }
3927 return lineOffset + offsetInLine;
3928 } 3967 }
3929 int getOffsetAtPoint(int x, int y) { 3968 int getOffsetAtPoint(int x, int y) {
3930 int lineIndex = getLineIndex(y); 3969 int lineIndex = getLineIndex(y);
3931 y -= getLinePixel(lineIndex); 3970 y -= getLinePixel(lineIndex);
3932 return getOffsetAtPoint(x, y, lineIndex); 3971 return getOffsetAtPoint(x, y, lineIndex);
3945 int[] trailing = new int[1]; 3984 int[] trailing = new int[1];
3946 int offsetInLine = layout.getOffset(x, y, trailing); 3985 int offsetInLine = layout.getOffset(x, y, trailing);
3947 caretAlignment = OFFSET_LEADING; 3986 caretAlignment = OFFSET_LEADING;
3948 if (trailing[0] !is 0) { 3987 if (trailing[0] !is 0) {
3949 int lineInParagraph = layout.getLineIndex(offsetInLine + trailing[0]); 3988 int lineInParagraph = layout.getLineIndex(offsetInLine + trailing[0]);
3950 //TODO handle bidi text
3951 int lineStart = layout.getLineOffsets()[lineInParagraph]; 3989 int lineStart = layout.getLineOffsets()[lineInParagraph];
3952 if (offsetInLine + trailing[0] is lineStart) { 3990 if (offsetInLine + trailing[0] is lineStart) {
3953 offsetInLine += trailing[0]; 3991 offsetInLine += trailing[0];
3954 caretAlignment = PREVIOUS_OFFSET_TRAILING; 3992 caretAlignment = PREVIOUS_OFFSET_TRAILING;
3955 } else { 3993 } else {
3972 } 4010 }
3973 } 4011 }
3974 renderer.disposeTextLayout(layout); 4012 renderer.disposeTextLayout(layout);
3975 return offsetInLine + content.getOffsetAtLine(lineIndex); 4013 return offsetInLine + content.getOffsetAtLine(lineIndex);
3976 } 4014 }
4015 int getOffsetAtPoint(int x, int y, int[] trailing, bool inTextOnly) {
4016 if (inTextOnly && y + getVerticalScrollOffset() < 0 || x + horizontalScrollOffset < 0) {
4017 return -1;
4018 }
4019 int bottomIndex = getPartialBottomIndex();
4020 int height = getLinePixel(bottomIndex + 1);
4021 if (inTextOnly && y > height) {
4022 return -1;
4023 }
4024 int lineIndex = getLineIndex(y);
4025 int lineOffset = content.getOffsetAtLine(lineIndex);
4026 TextLayout layout = renderer.getTextLayout(lineIndex);
4027 x += horizontalScrollOffset - leftMargin ;
4028 y -= getLinePixel(lineIndex);
4029 int offset = layout.getOffset(x, y, trailing);
4030 Rectangle rect = layout.getLineBounds(layout.getLineIndex(offset));
4031 renderer.disposeTextLayout(layout);
4032 if (inTextOnly && !(rect.x <= x && x <= rect.x + rect.width)) {
4033 return -1;
4034 }
4035 return offset + lineOffset;
4036 }
3977 /** 4037 /**
3978 * Returns the orientation of the receiver. 4038 * Returns the orientation of the receiver.
3979 * 4039 *
3980 * @return the orientation style 4040 * @return the orientation style
3981 * 4041 *
3997 */ 4057 */
3998 int getPartialBottomIndex() { 4058 int getPartialBottomIndex() {
3999 if (isFixedLineHeight()) { 4059 if (isFixedLineHeight()) {
4000 int lineHeight = renderer.getLineHeight(); 4060 int lineHeight = renderer.getLineHeight();
4001 int partialLineCount = Compatibility.ceil(clientAreaHeight, lineHeight); 4061 int partialLineCount = Compatibility.ceil(clientAreaHeight, lineHeight);
4002 return Math.min(content.getLineCount(), topIndex + partialLineCount) - 1; 4062 return Math.max(0, Math.min(content.getLineCount(), topIndex + partialLineCount) - 1);
4003 } 4063 }
4004 return getLineIndex(clientAreaHeight - bottomMargin); 4064 return getLineIndex(clientAreaHeight - bottomMargin);
4005 } 4065 }
4006 /** 4066 /**
4007 * Returns the index of the first partially visible line. 4067 * Returns the index of the first partially visible line.
4594 int height = 0; 4654 int height = 0;
4595 int left = 0x7fffffff, right = 0; 4655 int left = 0x7fffffff, right = 0;
4596 for (int i = lineStart; i <= lineEnd; i++) { 4656 for (int i = lineStart; i <= lineEnd; i++) {
4597 int lineOffset = content.getOffsetAtLine(i); 4657 int lineOffset = content.getOffsetAtLine(i);
4598 TextLayout layout = renderer.getTextLayout(i); 4658 TextLayout layout = renderer.getTextLayout(i);
4599 if (layout.getText().length > 0) { 4659 int length = layout.getText().length;
4600 if (i is lineStart && i is lineEnd) { 4660 if (length > 0) {
4601 rect = layout.getBounds(start - lineOffset, end - lineOffset); 4661 if (i is lineStart) {
4602 } else if (i is lineStart) { 4662 if (i is lineEnd) {
4603 String line = content.getLine(i); 4663 rect = layout.getBounds(start - lineOffset, end - lineOffset);
4604 rect = layout.getBounds(start - lineOffset, line.length); 4664 } else {
4665 rect = layout.getBounds(start - lineOffset, length);
4666 }
4667 y += rect.y;
4605 } else if (i is lineEnd) { 4668 } else if (i is lineEnd) {
4606 rect = layout.getBounds(0, end - lineOffset); 4669 rect = layout.getBounds(0, end - lineOffset);
4607 } else { 4670 } else {
4608 rect = layout.getBounds(); 4671 rect = layout.getBounds();
4609 } 4672 }
4724 } 4787 }
4725 return lineIndex; 4788 return lineIndex;
4726 } 4789 }
4727 int getCaretDirection() { 4790 int getCaretDirection() {
4728 if (!isBidiCaret()) return DWT.DEFAULT; 4791 if (!isBidiCaret()) return DWT.DEFAULT;
4792 if (ime.getCompositionOffset() !is -1) return DWT.DEFAULT;
4729 if (!updateCaretDirection && caretDirection !is DWT.NULL) return caretDirection; 4793 if (!updateCaretDirection && caretDirection !is DWT.NULL) return caretDirection;
4730 updateCaretDirection = false; 4794 updateCaretDirection = false;
4731 int caretLine = getCaretLine(); 4795 int caretLine = getCaretLine();
4732 int lineOffset = content.getOffsetAtLine(caretLine); 4796 int lineOffset = content.getOffsetAtLine(caretLine);
4733 String line = content.getLine(caretLine); 4797 String line = content.getLine(caretLine);
4751 int getCaretLine() { 4815 int getCaretLine() {
4752 return content.getLineAtOffset(caretOffset); 4816 return content.getLineAtOffset(caretOffset);
4753 } 4817 }
4754 int getWrapWidth () { 4818 int getWrapWidth () {
4755 if (wordWrap && !isSingleLine()) { 4819 if (wordWrap && !isSingleLine()) {
4756 int width = clientAreaWidth - leftMargin - rightMargin; 4820 int width = clientAreaWidth - leftMargin - rightMargin - getCaretWidth();
4757 return width > 0 ? width : 1; 4821 return width > 0 ? width : 1;
4758 } 4822 }
4759 return -1; 4823 return -1;
4760 } 4824 }
4761 int getWordNext (int offset, int movement) { 4825 int getWordNext (int offset, int movement) {
4932 addListener(DWT.MouseUp, listener); 4996 addListener(DWT.MouseUp, listener);
4933 addListener(DWT.MouseMove, listener); 4997 addListener(DWT.MouseMove, listener);
4934 addListener(DWT.Paint, listener); 4998 addListener(DWT.Paint, listener);
4935 addListener(DWT.Resize, listener); 4999 addListener(DWT.Resize, listener);
4936 addListener(DWT.Traverse, listener); 5000 addListener(DWT.Traverse, listener);
5001 ime.addListener(DWT.ImeComposition, new class () Listener {
5002 public void handleEvent(Event event) {
5003 switch (event.detail) {
5004 case DWT.COMPOSITION_SELECTION: handleCompositionSelection(event); break;
5005 case DWT.COMPOSITION_CHANGED: handleCompositionChanged(event); break;
5006 case DWT.COMPOSITION_OFFSET: handleCompositionOffset(event); break;
5007 }
5008 }
5009 });
4937 if (verticalBar !is null) { 5010 if (verticalBar !is null) {
4938 verticalBar.addListener(DWT.Selection, new class() Listener { 5011 verticalBar.addListener(DWT.Selection, new class() Listener {
4939 public void handleEvent(Event event) { 5012 public void handleEvent(Event event) {
4940 handleVerticalScroll(event); 5013 handleVerticalScroll(event);
4941 } 5014 }
5035 int y = startRect.y + startRect.height; 5108 int y = startRect.y + startRect.height;
5036 if (endRect.y > y) { 5109 if (endRect.y > y) {
5037 super.redraw(leftMargin, y, clientAreaWidth - rightMargin - leftMargin, endRect.y - y, false); 5110 super.redraw(leftMargin, y, clientAreaWidth - rightMargin - leftMargin, endRect.y - y, false);
5038 } 5111 }
5039 } 5112 }
5113 void handleCompositionOffset (Event event) {
5114 int[] trailing = new int [1];
5115 event.index = getOffsetAtPoint(event.x, event.y, trailing, true);
5116 event.count = trailing[0];
5117 }
5118 void handleCompositionSelection (Event event) {
5119 event.start = selection.x;
5120 event.end = selection.y;
5121 event.text = getSelectionText();
5122 }
5123 void handleCompositionChanged(Event event) {
5124 String text = event.text;
5125 int start = event.start;
5126 int end = event.end;
5127 int length = text.length;
5128 if (length is ime.getCommitCount()) {
5129 content.replaceTextRange(start, end - start, "");
5130 caretOffset = start;
5131 caretWidth = 0;
5132 caretDirection = DWT.NULL;
5133 } else {
5134 content.replaceTextRange(start, end - start, text);
5135 caretOffset = ime.getCaretOffset();
5136 if (ime.getWideCaret()) {
5137 int lineIndex = getCaretLine();
5138 int lineOffset = content.getOffsetAtLine(lineIndex);
5139 TextLayout layout = renderer.getTextLayout(lineIndex);
5140 caretWidth = layout.getBounds(start - lineOffset, start + length - 1 - lineOffset).width;
5141 renderer.disposeTextLayout(layout);
5142 }
5143 }
5144 showCaret();
5145 }
5040 /** 5146 /**
5041 * Frees resources. 5147 * Frees resources.
5042 */ 5148 */
5043 void handleDispose(Event event) { 5149 void handleDispose(Event event) {
5044 removeListener(DWT.Dispose, listener); 5150 removeListener(DWT.Dispose, listener);
5065 if (rightCaretBitmap !is null) { 5171 if (rightCaretBitmap !is null) {
5066 rightCaretBitmap.dispose(); 5172 rightCaretBitmap.dispose();
5067 rightCaretBitmap = null; 5173 rightCaretBitmap = null;
5068 } 5174 }
5069 if (isBidiCaret()) { 5175 if (isBidiCaret()) {
5070 BidiUtil.removeLanguageListener(handle); 5176 BidiUtil.removeLanguageListener(this);
5071 } 5177 }
5072 selectionBackground = null; 5178 selectionBackground = null;
5073 selectionForeground = null; 5179 selectionForeground = null;
5074 textChangeListener = null; 5180 textChangeListener = null;
5075 selection = null; 5181 selection = null;
5247 end = Math.min(content.getCharCount(), getWordNext(start, DWT.MOVEMENT_WORD_END)); 5353 end = Math.min(content.getCharCount(), getWordNext(start, DWT.MOVEMENT_WORD_END));
5248 } else { 5354 } else {
5249 start = lineOffset; 5355 start = lineOffset;
5250 end = lineEnd; 5356 end = lineEnd;
5251 } 5357 }
5252 selection.x = selection.y = start; 5358 caretOffset = start;
5253 selectionAnchor = -1; 5359 resetSelection();
5254 caretOffset = end; 5360 caretOffset = end;
5255 showCaret(); 5361 showCaret();
5256 doMouseSelection(); 5362 doMouseSelection();
5257 doubleClickSelection = new Point(selection.x, selection.y); 5363 doubleClickSelection = new Point(selection.x, selection.y);
5258 } 5364 }
5393 /** 5499 /**
5394 * Updates the caret position and selection and the scroll bars to reflect 5500 * Updates the caret position and selection and the scroll bars to reflect
5395 * the content change. 5501 * the content change.
5396 */ 5502 */
5397 void handleTextChanged(TextChangedEvent event) { 5503 void handleTextChanged(TextChangedEvent event) {
5504 int offset = ime.getCompositionOffset();
5505 if (offset !is -1 && lastTextChangeStart < offset) {
5506 ime.setCompositionOffset(offset + lastTextChangeNewCharCount - lastTextChangeReplaceCharCount);
5507 }
5398 int firstLine = content.getLineAtOffset(lastTextChangeStart); 5508 int firstLine = content.getLineAtOffset(lastTextChangeStart);
5399 resetCache(firstLine, 0); 5509 resetCache(firstLine, 0);
5400 if (!isFixedLineHeight() && topIndex > firstLine) { 5510 if (!isFixedLineHeight() && topIndex > firstLine) {
5401 topIndex = firstLine; 5511 topIndex = firstLine;
5402 topIndexY = 0; 5512 topIndexY = 0;
5783 } 5893 }
5784 /** 5894 /**
5785 * Temporary until DWT provides this 5895 * Temporary until DWT provides this
5786 */ 5896 */
5787 bool isBidi() { 5897 bool isBidi() {
5788 return IS_GTK || BidiUtil.isBidiPlatform() || isMirrored_; 5898 return IS_GTK || IS_CARBON || BidiUtil.isBidiPlatform() || isMirrored_;
5789 } 5899 }
5790 bool isBidiCaret() { 5900 bool isBidiCaret() {
5791 return BidiUtil.isBidiPlatform(); 5901 return BidiUtil.isBidiPlatform();
5792 } 5902 }
5793 bool isFixedLineHeight() { 5903 bool isFixedLineHeight() {
6510 if (destinationX > scrollWidth) { 6620 if (destinationX > scrollWidth) {
6511 super.redraw(leftMargin + scrollWidth, topMargin, -pixels - scrollWidth, scrollHeight, true); 6621 super.redraw(leftMargin + scrollWidth, topMargin, -pixels - scrollWidth, scrollHeight, true);
6512 } 6622 }
6513 } 6623 }
6514 horizontalScrollOffset += pixels; 6624 horizontalScrollOffset += pixels;
6515 int oldColumnX = columnX;
6516 setCaretLocation(); 6625 setCaretLocation();
6517 columnX = oldColumnX;
6518 return true; 6626 return true;
6519 } 6627 }
6520 /** 6628 /**
6521 * Scrolls the widget vertically. 6629 * Scrolls the widget vertically.
6522 * 6630 *
6565 calculateTopIndex(pixels); 6673 calculateTopIndex(pixels);
6566 } else { 6674 } else {
6567 calculateTopIndex(pixels); 6675 calculateTopIndex(pixels);
6568 super.redraw(); 6676 super.redraw();
6569 } 6677 }
6570 int oldColumnX = columnX;
6571 setCaretLocation(); 6678 setCaretLocation();
6572 columnX = oldColumnX;
6573 return true; 6679 return true;
6574 } 6680 }
6575 void scrollText(int srcY, int destY) { 6681 void scrollText(int srcY, int destY) {
6576 if (srcY is destY) return; 6682 if (srcY is destY) return;
6577 int deltaY = destY - srcY; 6683 int deltaY = destY - srcY;
6702 return newOffset; 6808 return newOffset;
6703 } 6809 }
6704 /** 6810 /**
6705 * Sets the alignment of the widget. The argument should be one of <code>DWT.LEFT</code>, 6811 * Sets the alignment of the widget. The argument should be one of <code>DWT.LEFT</code>,
6706 * <code>DWT.CENTER</code> or <code>DWT.RIGHT</code>. The alignment applies for all lines. 6812 * <code>DWT.CENTER</code> or <code>DWT.RIGHT</code>. The alignment applies for all lines.
6813 * </p><p>
6814 * Note that if <code>DWT.MULTI</code> is set, then <code>DWT.WRAP</code> must also be set
6815 * in order to stabilize the right edge before setting alignment.
6816 * </p>
6707 * 6817 *
6708 * @param alignment the new alignment 6818 * @param alignment the new alignment
6709 * 6819 *
6710 * @exception DWTException <ul> 6820 * @exception DWTException <ul>
6711 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 6821 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
6729 * @see Control#setBackground(Color) 6839 * @see Control#setBackground(Color)
6730 */ 6840 */
6731 public override void setBackground(Color color) { 6841 public override void setBackground(Color color) {
6732 checkWidget(); 6842 checkWidget();
6733 background = color; 6843 background = color;
6844 super.setBackground(color);
6734 super.redraw(); 6845 super.redraw();
6735 } 6846 }
6736 /** 6847 /**
6737 * Sets the receiver's caret. Set the caret's height and location. 6848 * Sets the receiver's caret. Set the caret's height and location.
6738 * 6849 *
6798 } 6909 }
6799 if (isDefaultCaret && imageDirection is DWT.RIGHT) { 6910 if (isDefaultCaret && imageDirection is DWT.RIGHT) {
6800 location.x -= (caret.getSize().x - 1); 6911 location.x -= (caret.getSize().x - 1);
6801 } 6912 }
6802 if (isDefaultCaret) { 6913 if (isDefaultCaret) {
6803 caret.setBounds(location.x, location.y, 0, caretHeight); 6914 caret.setBounds(location.x, location.y, caretWidth, caretHeight);
6804 } else { 6915 } else {
6805 caret.setLocation(location); 6916 caret.setLocation(location);
6806 } 6917 }
6807 getAccessible().textCaretMoved(getCaretOffset()); 6918 getAccessible().textCaretMoved(getCaretOffset());
6808 if (direction !is caretDirection) { 6919 if (direction !is caretDirection) {
6852 // illegal operation and an exception is thrown. Fixes 1GDKK3R 6963 // illegal operation and an exception is thrown. Fixes 1GDKK3R
6853 DWT.error(DWT.ERROR_INVALID_ARGUMENT); 6964 DWT.error(DWT.ERROR_INVALID_ARGUMENT);
6854 } 6965 }
6855 caretOffset = offset; 6966 caretOffset = offset;
6856 } 6967 }
6968 caretAlignment = PREVIOUS_OFFSET_TRAILING;
6857 // clear the selection if the caret is moved. 6969 // clear the selection if the caret is moved.
6858 // don't notify listeners about the selection change. 6970 // don't notify listeners about the selection change.
6859 clearSelection(false); 6971 clearSelection(false);
6860 } 6972 }
6861 setCaretLocation(); 6973 setCaretLocation();
7181 } 7293 }
7182 } 7294 }
7183 /** 7295 /**
7184 * Sets the alignment of the specified lines. The argument should be one of <code>DWT.LEFT</code>, 7296 * Sets the alignment of the specified lines. The argument should be one of <code>DWT.LEFT</code>,
7185 * <code>DWT.CENTER</code> or <code>DWT.RIGHT</code>. 7297 * <code>DWT.CENTER</code> or <code>DWT.RIGHT</code>.
7186 * <p> 7298 * <p><p>
7299 * Note that if <code>DWT.MULTI</code> is set, then <code>DWT.WRAP</code> must also be set
7300 * in order to stabilize the right edge before setting alignment.
7301 * </p>
7187 * Should not be called if a LineStyleListener has been set since the listener 7302 * Should not be called if a LineStyleListener has been set since the listener
7188 * maintains the line attributes. 7303 * maintains the line attributes.
7189 * </p><p> 7304 * </p><p>
7190 * All line attributes are maintained relative to the line text, not the 7305 * All line attributes are maintained relative to the line text, not the
7191 * line index that is specified in this method call. 7306 * line index that is specified in this method call.
7487 return; 7602 return;
7488 } 7603 }
7489 if ((orientation & DWT.LEFT_TO_RIGHT) !is 0 && !isMirrored()) { 7604 if ((orientation & DWT.LEFT_TO_RIGHT) !is 0 && !isMirrored()) {
7490 return; 7605 return;
7491 } 7606 }
7492 if (!BidiUtil.setOrientation(handle, orientation)) { 7607 if (!BidiUtil.setOrientation(this, orientation)) {
7493 return; 7608 return;
7494 } 7609 }
7495 isMirrored_ = (orientation & DWT.RIGHT_TO_LEFT) !is 0; 7610 isMirrored_ = (orientation & DWT.RIGHT_TO_LEFT) !is 0;
7496 caretDirection = DWT.NULL; 7611 caretDirection = DWT.NULL;
7497 resetCache(0, content.getLineCount()); 7612 resetCache(0, content.getLineCount());
7703 caretOffset = selection.x = start; 7818 caretOffset = selection.x = start;
7704 } else { 7819 } else {
7705 selectionAnchor = selection.x = start; 7820 selectionAnchor = selection.x = start;
7706 caretOffset = selection.y = end; 7821 caretOffset = selection.y = end;
7707 } 7822 }
7823 caretAlignment = PREVIOUS_OFFSET_TRAILING;
7708 internalRedrawRange(selection.x, selection.y - selection.x); 7824 internalRedrawRange(selection.x, selection.y - selection.x);
7709 } 7825 }
7710 } 7826 }
7711 /** 7827 /**
7712 * Sets the selection. 7828 * Sets the selection.
8002 */ 8118 */
8003 public void setTabs(int tabs) { 8119 public void setTabs(int tabs) {
8004 checkWidget(); 8120 checkWidget();
8005 tabLength = tabs; 8121 tabLength = tabs;
8006 renderer.setFont(null, tabs); 8122 renderer.setFont(null, tabs);
8007 if (caretOffset > 0) {
8008 caretOffset = 0;
8009 showCaret();
8010 clearSelection(false);
8011 }
8012 resetCache(0, content.getLineCount()); 8123 resetCache(0, content.getLineCount());
8124 setCaretLocation();
8013 super.redraw(); 8125 super.redraw();
8014 } 8126 }
8015 /** 8127 /**
8016 * Sets the widget content. 8128 * Sets the widget content.
8017 * If the widget has the DWT.SINGLE style and "text" contains more than 8129 * If the widget has the DWT.SINGLE style and "text" contains more than