diff options
3 files changed, 344 insertions, 308 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/DisplayRenderer.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/DisplayRenderer.java index 424413010b..539a813102 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/DisplayRenderer.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/DisplayRenderer.java @@ -76,6 +76,7 @@ protected void drawLineSelectionBackground(String line, int lineOffset, StyleRan int lineEndSpaceWidth = getLineEndSpaceWidth(); int lineHeight = getLineHeight(); boolean wordWrap = parent.internalGetWordWrap(); + boolean isRightOriented = (parent.getStyle() & SWT.RIGHT_TO_LEFT) != 0; if (selectionEnd == selectionStart || selectionEnd < 0 || selectionStart > lineLength) { return; @@ -129,7 +130,7 @@ protected void drawLineSelectionBackground(String line, int lineOffset, StyleRan // if the selection extends past this line, render an additional // whitespace background at the end of the line to represent the // selected line break - if (bidi != null && selectionEnd > 0 && bidi.isRightToLeft(selectionEnd - 1)) { + if (bidi != null && selectionEnd > 0 && (bidi.isRightToLeft(selectionEnd - 1) || (isRightOriented && bidi.isRightToLeft(selectionEnd - 1) == false))) { int lineEndX = bidi.getTextWidth(); gc.fillRectangle(lineEndX - horizontalScrollOffset + leftMargin, paintY, lineEndSpaceWidth, lineHeight); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledText.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledText.java index d3055db63d..aab59f553c 100755 --- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledText.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledText.java @@ -1596,7 +1596,8 @@ public StyledText(Composite parent, int style) { super.setForeground(getForeground()); super.setBackground(getBackground()); Display display = getDisplay(); - isBidi = StyledTextBidi.isBidiPlatform(); + boolean isRightOriented = (getStyle() & SWT.RIGHT_TO_LEFT) != 0; + isBidi = StyledTextBidi.isBidiPlatform() || isRightOriented; if ((style & SWT.READ_ONLY) != 0) { setEditable(false); } @@ -1618,6 +1619,9 @@ public StyledText(Composite parent, int style) { } else { createCaretBitmaps(); + if (isRightOriented) { + BidiUtil.setKeyboardLanguage(BidiUtil.KEYBOARD_BIDI); + } new Caret(this, SWT.NULL); setBidiCaretDirection(); Runnable runnable = new Runnable() { @@ -2237,32 +2241,49 @@ void createKeyBindings() { setKeyBinding(SWT.ARROW_DOWN, ST.LINE_DOWN); setKeyBinding(SWT.HOME, ST.LINE_START); setKeyBinding(SWT.END, ST.LINE_END); - setKeyBinding(SWT.ARROW_LEFT, ST.COLUMN_PREVIOUS); - setKeyBinding(SWT.ARROW_RIGHT, ST.COLUMN_NEXT); setKeyBinding(SWT.PAGE_UP, ST.PAGE_UP); setKeyBinding(SWT.PAGE_DOWN, ST.PAGE_DOWN); - setKeyBinding(SWT.ARROW_LEFT | SWT.MOD1, ST.WORD_PREVIOUS); - setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD1, ST.WORD_NEXT); setKeyBinding(SWT.HOME | SWT.MOD1, ST.TEXT_START); setKeyBinding(SWT.END | SWT.MOD1, ST.TEXT_END); setKeyBinding(SWT.PAGE_UP | SWT.MOD1, ST.WINDOW_START); setKeyBinding(SWT.PAGE_DOWN | SWT.MOD1, ST.WINDOW_END); + if ((getStyle() & SWT.RIGHT_TO_LEFT) == 0) { + setKeyBinding(SWT.ARROW_LEFT, ST.COLUMN_PREVIOUS); + setKeyBinding(SWT.ARROW_RIGHT, ST.COLUMN_NEXT); + setKeyBinding(SWT.ARROW_LEFT | SWT.MOD1, ST.WORD_PREVIOUS); + setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD1, ST.WORD_NEXT); + } + else { + setKeyBinding(SWT.ARROW_LEFT, ST.COLUMN_NEXT); + setKeyBinding(SWT.ARROW_RIGHT, ST.COLUMN_PREVIOUS); + setKeyBinding(SWT.ARROW_LEFT | SWT.MOD1, ST.WORD_NEXT); + setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD1, ST.WORD_PREVIOUS); + } + // Selection setKeyBinding(SWT.ARROW_UP | SWT.MOD2, ST.SELECT_LINE_UP); setKeyBinding(SWT.ARROW_DOWN | SWT.MOD2, ST.SELECT_LINE_DOWN); setKeyBinding(SWT.HOME | SWT.MOD2, ST.SELECT_LINE_START); setKeyBinding(SWT.END | SWT.MOD2, ST.SELECT_LINE_END); - setKeyBinding(SWT.ARROW_LEFT | SWT.MOD2, ST.SELECT_COLUMN_PREVIOUS); - setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD2, ST.SELECT_COLUMN_NEXT); setKeyBinding(SWT.PAGE_UP | SWT.MOD2, ST.SELECT_PAGE_UP); setKeyBinding(SWT.PAGE_DOWN | SWT.MOD2, ST.SELECT_PAGE_DOWN); - setKeyBinding(SWT.ARROW_LEFT | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_PREVIOUS); - setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_NEXT); setKeyBinding(SWT.HOME | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_START); setKeyBinding(SWT.END | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_END); setKeyBinding(SWT.PAGE_UP | SWT.MOD1 | SWT.MOD2, ST.SELECT_WINDOW_START); setKeyBinding(SWT.PAGE_DOWN | SWT.MOD1 | SWT.MOD2, ST.SELECT_WINDOW_END); - + if ((getStyle() & SWT.RIGHT_TO_LEFT) == 0) { + setKeyBinding(SWT.ARROW_LEFT | SWT.MOD2, ST.SELECT_COLUMN_PREVIOUS); + setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD2, ST.SELECT_COLUMN_NEXT); + setKeyBinding(SWT.ARROW_LEFT | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_PREVIOUS); + setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_NEXT); + } + else { + setKeyBinding(SWT.ARROW_LEFT | SWT.MOD2, ST.SELECT_COLUMN_NEXT); + setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD2, ST.SELECT_COLUMN_PREVIOUS); + setKeyBinding(SWT.ARROW_LEFT | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_NEXT); + setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_PREVIOUS); + } + // Modification // Cut, Copy, Paste setKeyBinding('X' | SWT.MOD1, ST.CUT); @@ -2289,6 +2310,7 @@ void createKeyBindings() { */ void createCaretBitmaps() { int caretWidth = BIDI_CARET_WIDTH; + int gcStyle = getStyle() & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT); Display display = getDisplay(); if (caretPalette == null) { @@ -2299,7 +2321,9 @@ void createCaretBitmaps() { } ImageData imageData = new ImageData(caretWidth, lineHeight, 1, caretPalette); leftCaretBitmap = new Image(display, imageData); - GC gc = new GC (leftCaretBitmap); + // mirror the caret gc because when the bitmap is rendered on the screen it will be + // mirrored since the GC for the canvas is mirrored + GC gc = new GC (leftCaretBitmap, gcStyle); gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE)); gc.drawLine(0,0,0,lineHeight); gc.drawLine(0,0,caretWidth-1,0); @@ -2310,7 +2334,9 @@ void createCaretBitmaps() { rightCaretBitmap.dispose(); } rightCaretBitmap = new Image(display, imageData); - gc = new GC (rightCaretBitmap); + // mirror the caret gc because when the bitmap is rendered on the screen it will be + // mirrored since the GC for the canvas is mirrored + gc = new GC (rightCaretBitmap, gcStyle); gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE)); gc.drawLine(caretWidth-1,0,caretWidth-1,lineHeight); gc.drawLine(0,0,caretWidth-1,0); @@ -2367,11 +2393,11 @@ void doAutoScroll(Event event) { } else if (event.x < leftMargin && wordWrap == false) { - doAutoScroll(SWT.LEFT); + doAutoScroll(ST.COLUMN_PREVIOUS); } else if (event.x > area.width - leftMargin - rightMargin && wordWrap == false) { - doAutoScroll(SWT.RIGHT); + doAutoScroll(ST.COLUMN_NEXT); } else { endAutoScroll(); @@ -2381,7 +2407,7 @@ void doAutoScroll(Event event) { * Initiates autoscrolling. * <p> * - * @param direction SWT.UP, SWT.DOWN, SWT.RIGHT, SWT.LEFT + * @param direction SWT.UP, SWT.DOWN, SWT.COLUMN_NEXT, SWT.COLUMN_PREVIOUS */ void doAutoScroll(int direction) { Runnable timer = null; @@ -2413,24 +2439,24 @@ void doAutoScroll(int direction) { } } }; - } else if (direction == SWT.RIGHT) { + } else if (direction == ST.COLUMN_NEXT) { timer = new Runnable() { public void run() { - if (autoScrollDirection == SWT.RIGHT) { - doColumnRight(); + if (autoScrollDirection == ST.COLUMN_NEXT) { + doVisualNext(); setMouseWordSelectionAnchor(); - doSelection(SWT.RIGHT); + doMouseSelection(); display.timerExec(TIMER_INTERVAL, this); } } }; - } else if (direction == SWT.LEFT) { + } else if (direction == ST.COLUMN_PREVIOUS) { timer = new Runnable() { public void run() { - if (autoScrollDirection == SWT.LEFT) { - doColumnLeft(); + if (autoScrollDirection == ST.COLUMN_PREVIOUS) { + doVisualPrevious(); setMouseWordSelectionAnchor(); - doSelection(SWT.LEFT); + doMouseSelection(); display.timerExec(TIMER_INTERVAL, this); } } @@ -2471,208 +2497,6 @@ void doBackspace() { } } /** - * Moves the caret one character to the left. Do not go to the previous line. - * When in a bidi locale and at a R2L character the caret is moved to the - * beginning of the R2L segment (visually right) and then one character to the - * left (visually left because it's now in a L2R segment). - */ -void doColumnLeft() { - int line = content.getLineAtOffset(caretOffset); - int lineOffset = content.getOffsetAtLine(line); - int offsetInLine = caretOffset - lineOffset; - - if (isBidi()) { - String lineText = content.getLine(line); - int lineLength = lineText.length(); - GC gc = getGC(); - StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc); - - if (horizontalScrollOffset > 0 || offsetInLine > 0) { - if (offsetInLine < lineLength && bidi.isRightToLeft(offsetInLine)) { - // advance caret logically if in R2L segment (move visually left) - caretOffset++; - doSelection(SWT.RIGHT); - if (caretOffset - lineOffset == lineLength) { - // if the line end is reached in a R2L segment, make the - // caret position (visual left border) visible before - // jumping to segment start - showCaret(); - } - // end of R2L segment reached (visual left side)? - if (bidi.isRightToLeft(caretOffset - lineOffset) == false) { - if (bidi.getTextPosition(caretOffset - lineOffset) < horizontalScrollOffset) { - // make beginning of R2L segment visible before going - // left, to L2R segment important if R2L segment ends - // at visual left in order to scroll all the way to the - // left. Fixes 1GKM3XS - showCaret(); - } - // go to beginning of R2L segment (visually end of next L2R - // segment)/beginning of line - caretOffset--; - while (caretOffset - lineOffset > 0 && - bidi.isRightToLeft(caretOffset - lineOffset)) { - caretOffset--; - } - } - } - else - if (offsetInLine == lineLength && - bidi.getTextPosition(lineLength) != XINSET) { - // at logical line end in R2L segment but there's more text (a - // L2R segment) go to end of R2L segment (visually left of next - // L2R segment)/end of line - caretOffset--; - while (caretOffset - lineOffset > 0 && - bidi.isRightToLeft(caretOffset - lineOffset)) { - caretOffset--; - } - } - else - if (offsetInLine > 0 && bidi.isRightToLeft(offsetInLine) == false) { - // decrease caret logically if in L2R segment (move visually left) - caretOffset--; - doSelection(SWT.LEFT); - // end of L2R segment reached (visual left side of preceeding R2L - // segment)? - if (caretOffset - lineOffset > 0 && - bidi.isRightToLeft(caretOffset - lineOffset - 1)) { - // go to beginning of R2L segment (visually start of next L2R - // segment)/beginning of line - caretOffset--; - while (caretOffset - lineOffset > 0 && - bidi.isRightToLeft(caretOffset - lineOffset - 1)) { - caretOffset--; - } - } - } - // if new caret position is to the left of the client area - if (bidi.getTextPosition(caretOffset - lineOffset) < horizontalScrollOffset) { - // scroll to the caret position - showCaret(); - } - else { - // otherwise just update caret position without scrolling it into view - setBidiCaretLocation(null); - setBidiKeyboardLanguage(); - } - // Beginning of line reached (auto scroll finished) but not scrolled - // completely to the left? Fixes 1GKM193 - if (caretOffset - lineOffset == 0 && horizontalScrollOffset > 0 && - horizontalScrollOffset <= XINSET) { - scrollHorizontalBar(-horizontalScrollOffset); - } - } - gc.dispose(); - } - else - if (offsetInLine > 0) { - caretOffset--; - showCaret(); - } -} -/** - * Moves the caret one character to the right. Do not go to the next line. - * When in a bidi locale and at a R2L character the caret is moved to the - * end of the R2L segment (visually left) and then one character to the - * right (visually right because it's now in a L2R segment). - */ -void doColumnRight() { - int line = content.getLineAtOffset(caretOffset); - int lineOffset = content.getOffsetAtLine(line); - int offsetInLine = caretOffset - lineOffset; - String lineText = content.getLine(line); - int lineLength = lineText.length(); - - if (isBidi()) { - GC gc = getGC(); - StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc); - if (bidi.getTextWidth() + leftMargin > horizontalScrollOffset + getClientArea().width || - offsetInLine < lineLength) { - if (bidi.isRightToLeft(offsetInLine) == false && - offsetInLine < lineLength) { - // advance caret logically if in L2R segment (move visually right) - caretOffset++; - doSelection(SWT.RIGHT); - // end of L2R segment reached (visual right side)? - if (bidi.isRightToLeft(caretOffset - lineOffset)) { - // go to end of R2L segment (visually left of next R2L segment)/ - // end of line - caretOffset++; - while (caretOffset < lineOffset + lineLength && - bidi.isRightToLeft(caretOffset - lineOffset)) { - caretOffset++; - } - } - } - else - if (offsetInLine > 0 && - (bidi.isRightToLeft(offsetInLine) || - bidi.getTextWidth() + leftMargin > horizontalScrollOffset + getClientArea().width || - offsetInLine < lineLength)) { - // advance caret visually if in R2L segment or logically at line end - // but right end of line is not fully visible yet - caretOffset--; - doSelection(SWT.LEFT); - offsetInLine = caretOffset - lineOffset; - // end of R2L segment reached (visual right side)? - if (offsetInLine > 0 && bidi.isRightToLeft(offsetInLine) == false) { - // go to end of R2L segment (visually left of next L2R segment)/ - // end of line - caretOffset++; - while (caretOffset < lineOffset + lineLength && - bidi.isRightToLeft(caretOffset - lineOffset)) { - caretOffset++; - } - } - } - else - if (offsetInLine == 0 && bidi.getTextPosition(0) != bidi.getTextWidth()) { - // at logical line start in R2L segment but there's more text (a L2R - // segment) go to end of R2L segment (visually left of next L2R - // segment)/end of line - caretOffset++; - while (caretOffset < lineOffset + lineLength && - bidi.isRightToLeft(caretOffset - lineOffset - 1)) { - caretOffset++; - } - } - offsetInLine = caretOffset - lineOffset; - // if new caret position is to the right of the client area - if (bidi.getTextPosition(offsetInLine) >= horizontalScrollOffset) { - // scroll to the caret position - showCaret(); - } - else { - // otherwise just update caret position without scrolling it into view - setBidiCaretLocation(null); - setBidiKeyboardLanguage(); - } - if (offsetInLine > 0 && offsetInLine < lineLength - 1) { - int clientAreaEnd = horizontalScrollOffset + getClientArea().width; - boolean directionChange = bidi.isRightToLeft(offsetInLine - 1) == false && bidi.isRightToLeft(offsetInLine); - int textWidth = bidi.getTextWidth() + leftMargin; - // between L2R and R2L segment and second character of R2L segment is - // left of right border and logical line end is left of right border - // but visual line end is not left of right border - if (directionChange && - bidi.isRightToLeft(offsetInLine + 1) && - bidi.getTextPosition(offsetInLine + 1) + leftMargin < clientAreaEnd && - bidi.getTextPosition(lineLength) + leftMargin < clientAreaEnd && textWidth > clientAreaEnd) { - // make visual line end visible - scrollHorizontalBar(textWidth - clientAreaEnd); - } - } - } - gc.dispose(); - } - else - if (offsetInLine < lineLength) { - caretOffset++; - showCaret(); - } -} -/** * Replaces the selection with the character or insert the character at the * current caret position if no selection exists. * If a carriage return was typed replace it with the line break character @@ -2864,7 +2688,9 @@ int doLineDown() { if (caretLine < content.getLineCount() - 1) { caretLine++; if (isBidi()) { - caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine); + int offsetDirection[] = getBidiOffsetAtMouseLocation(columnX, caretLine); + caretOffset = offsetDirection[0]; + lastCaretDirection = offsetDirection[1]; } else { caretOffset = getOffsetAtMouseLocation(columnX, caretLine); @@ -2911,7 +2737,9 @@ int doLineUp() { if (caretLine > 0) { caretLine--; if (isBidi()) { - caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine); + int offsetDirection[] = getBidiOffsetAtMouseLocation(columnX, caretLine); + caretOffset = offsetDirection[0]; + lastCaretDirection = offsetDirection[1]; } else { caretOffset = getOffsetAtMouseLocation(columnX, caretLine); @@ -2933,6 +2761,7 @@ void doMouseLocationChange(int x, int y, boolean select) { int lineCount = content.getLineCount(); int newCaretOffset; int newCaretLine; + int newCaretDirection = lastCaretDirection; if (line > lineCount - 1) { line = lineCount - 1; @@ -2943,7 +2772,9 @@ void doMouseLocationChange(int x, int y, boolean select) { return; } if (isBidi()) { - newCaretOffset = getBidiOffsetAtMouseLocation(x, line); + int offsetDirection[] = getBidiOffsetAtMouseLocation(x, line); + newCaretOffset = offsetDirection[0]; + newCaretDirection = offsetDirection[1]; } else { newCaretOffset = getOffsetAtMouseLocation(x, line); @@ -2957,8 +2788,9 @@ void doMouseLocationChange(int x, int y, boolean select) { // a different line? If not the autoscroll selection // could be incorrectly reset. Fixes 1GKM3XS if (y >= 0 && y < getClientArea().height && - (x >= 0 || newCaretLine != content.getLineAtOffset(caretOffset))) { + (x >= 0 && x < getClientArea().width || newCaretLine != content.getLineAtOffset(caretOffset))) { if (newCaretOffset != caretOffset) { + lastCaretDirection = newCaretDirection; caretOffset = newCaretOffset; if (select) { doMouseSelection(); @@ -2967,6 +2799,7 @@ void doMouseLocationChange(int x, int y, boolean select) { } } if (select == false) { + lastCaretDirection = newCaretDirection; clearSelection(true); } } @@ -2977,10 +2810,10 @@ void doMouseSelection() { if (caretOffset <= selection.x || (caretOffset > selection.x && caretOffset < selection.y && selectionAnchor == selection.x)) { - doSelection(SWT.LEFT); + doSelection(ST.COLUMN_PREVIOUS); } else { - doSelection(SWT.RIGHT); + doSelection(ST.COLUMN_NEXT); } } /** @@ -3057,13 +2890,15 @@ void doPageDown(boolean select) { scrollLines = Math.max(1, scrollLines); caretLine += scrollLines; if (isBidi()) { - caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine); + int offsetDirection[] = getBidiOffsetAtMouseLocation(columnX, caretLine); + caretOffset = offsetDirection[0]; + lastCaretDirection = offsetDirection[1]; } else { caretOffset = getOffsetAtMouseLocation(columnX, caretLine); } if (select) { - doSelection(SWT.RIGHT); + doSelection(ST.COLUMN_NEXT); } // scroll one page down or to the bottom scrollOffset = verticalScrollOffset + scrollLines * getVerticalIncrement(); @@ -3129,7 +2964,9 @@ void doPageUp() { caretLine -= scrollLines; if (isBidi()) { - caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine); + int offsetDirection[] = getBidiOffsetAtMouseLocation(columnX, caretLine); + caretOffset = offsetDirection[0]; + lastCaretDirection = offsetDirection[1]; } else { caretOffset = getOffsetAtMouseLocation(columnX, caretLine); @@ -3156,7 +2993,7 @@ void doSelection(int direction) { if (selectionAnchor == -1) { selectionAnchor = selection.x; } - if (direction == SWT.LEFT) { + if (direction == ST.COLUMN_PREVIOUS) { if (caretOffset < selection.x) { // grow selection redrawEnd = selection.x; @@ -3285,7 +3122,7 @@ void doSelectionLineDown() { setMouseWordSelectionAnchor(); // select first and then scroll to reduce flash when key // repeat scrolls lots of lines - doSelection(SWT.RIGHT); + doSelection(ST.COLUMN_NEXT); // explicitly go to the calculated caret line. may be different // from content.getLineAtOffset(caretOffset) when in word wrap mode showCaret(caretLine); @@ -3320,7 +3157,7 @@ void doSelectionLineUp() { // explicitly go to the calculated caret line. may be different // from content.getLineAtOffset(caretOffset) when in word wrap mode showCaret(caretLine); - doSelection(SWT.LEFT); + doSelection(ST.COLUMN_PREVIOUS); // save the original horizontal caret position columnX = oldColumnX; } @@ -3405,6 +3242,158 @@ void doSelectionWordPrevious() { showCaret(caretLine); } /** + * Moves the caret one character to the left. Do not go to the previous line. + * When in a bidi locale and at a R2L character the caret is moved to the + * beginning of the R2L segment (visually right) and then one character to the + * left (visually left because it's now in a L2R segment). + */ +void doVisualPrevious() { + int line = content.getLineAtOffset(caretOffset); + int lineOffset = content.getOffsetAtLine(line); + int offsetInLine = caretOffset - lineOffset; + + if (isBidi()) { + // check if caret location is at the visual beginning of the line + if (columnX <= XINSET && horizontalScrollOffset == 0) { + return; + } + String lineText = content.getLine(line); + int lineLength = lineText.length(); + GC gc = getGC(); + StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc); + int visualOffset = -1; + + if (offsetInLine == lineLength) { + //logical end of line may not be visual end, setup visualOffset to process as usual + visualOffset = bidi.getVisualOffset(offsetInLine - 1); + } + else + if (offsetInLine < lineLength) { + visualOffset = bidi.getVisualOffset(offsetInLine); + } + if (visualOffset != -1) { + if (visualOffset > 0) { + visualOffset--; + offsetInLine = bidi.getLogicalOffset(visualOffset); + } + else + if (visualOffset == 0) { + boolean isRightOriented = (getStyle() & SWT.RIGHT_TO_LEFT) != 0; + + //move to visual line end (i.e., behind L2R character/in front of R2L character at visual 0) + if ((isRightOriented && bidi.isRightToLeft(offsetInLine) == false) || + (isRightOriented == false && bidi.isRightToLeft(offsetInLine))) { + offsetInLine++; + } + + if (offsetInLine > 0 && offsetInLine < lineLength) { + if (isRightOriented) { + boolean rightToLeftStart = bidi.isRightToLeft(offsetInLine) && bidi.isRightToLeft(offsetInLine - 1) == false; + if (rightToLeftStart) { + //moving from LtoR segment to RtoL segment + lastCaretDirection = ST.COLUMN_NEXT; + } + } + else { + boolean leftToRightStart = bidi.isRightToLeft(offsetInLine) == false && bidi.isRightToLeft(offsetInLine - 1); + if (bidi.isLatinNumber(offsetInLine) && bidi.isRightToLeftInput(offsetInLine - 1)) { + //moving from LtoR segment to latin number + lastCaretDirection = ST.COLUMN_PREVIOUS; + } + else + if (leftToRightStart) { + //moving from RtoL segment to LtoR segment + lastCaretDirection = ST.COLUMN_NEXT; + } + } + } + } + caretOffset = lineOffset + offsetInLine; + showCaret(); + } + if (bidi.getTextPosition(offsetInLine, ST.COLUMN_NEXT) == XINSET) { + //scroll to origin if caret is at origin + scrollHorizontalBar(-horizontalScrollOffset); + } + gc.dispose(); + } + else + if (offsetInLine > 0) { + caretOffset--; + showCaret(); + } +} +/** + * Moves the caret one character to the right. Do not go to the next line. + * When in a bidi locale and at a R2L character the caret is moved to the + * end of the R2L segment (visually left) and then one character to the + * right (visually right because it's now in a L2R segment). + */ +void doVisualNext() { + int line = content.getLineAtOffset(caretOffset); + int lineOffset = content.getOffsetAtLine(line); + int offsetInLine = caretOffset - lineOffset; + String lineText = content.getLine(line); + int lineLength = lineText.length(); + + if (isBidi()) { + GC gc = getGC(); + StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc); + int lineEndPixel = bidi.getTextWidth() + leftMargin; + + // check if caret location is at the visual end of the line (can't use + // caret location here since it's location is dependent on current keyboard + // language direction) + if (bidi.getTextPosition(offsetInLine, lastCaretDirection) == lineEndPixel) { + gc.dispose(); + return; + } + int visualOffset = -1; + if (offsetInLine == lineLength) { + //logical end of line may not be visual end, setup visualOffset to process as usual + visualOffset = bidi.getVisualOffset(offsetInLine - 1); + } + else + if (offsetInLine < lineLength) { + visualOffset = bidi.getVisualOffset(offsetInLine); + } + if (visualOffset != -1) { + visualOffset++; + offsetInLine = bidi.getLogicalOffset(visualOffset); + if (offsetInLine > 0 && offsetInLine < lineLength) { + boolean isRightOriented = (getStyle() & SWT.RIGHT_TO_LEFT) != 0; + if (isRightOriented) { + boolean leftToRightStart = bidi.isRightToLeft(offsetInLine) == false && bidi.isRightToLeft(offsetInLine - 1); + if (leftToRightStart) { + //moving from RtoL segment to LtoR segment + lastCaretDirection = ST.COLUMN_PREVIOUS; + } + } + else { + boolean rightToLeftStart = bidi.isRightToLeft(offsetInLine) && bidi.isRightToLeft(offsetInLine - 1) == false; + if (bidi.isRightToLeftInput(offsetInLine) && bidi.isLatinNumber(offsetInLine - 1)) { + //moving from latin number to RtoL segment + lastCaretDirection = ST.COLUMN_NEXT; + } + else + if (rightToLeftStart) { + //moving from LtoR segment to RtoL segment + lastCaretDirection = ST.COLUMN_PREVIOUS; + } + } + } + caretOffset = lineOffset + offsetInLine; + showCaret(); + } + gc.dispose(); + } + else + if (offsetInLine < lineLength) { + caretOffset++; + showCaret(); + } +} +/** * Moves the caret to the end of the next word. * If a selection exists, move the caret to the end of the selection * and remove the selection. @@ -3515,30 +3504,28 @@ public boolean getBidiColoring() { return bidiColoring; } /** - * Returns the offset at the specified x location in the specified line. - * Also sets the caret direction so that the caret is placed correctly - * depending on whether the mouse location is in a R2L or L2R segment. + * Returns the offset and caret direction at the specified x location + * in the specified line. + * The returned caret direction needs to be set in order to place the + * caret correctly based on whether the mouse location is in a R2L + * or L2R segment. * <p> * * @param x x location of the mouse location * @param line line the mouse location is in - * @return the offset at the specified x location in the specified line, - * relative to the beginning of the document + * @return int array, first element is the offset at the specified x + * location in the specified line, relative to the beginning of the + * document. second element is the caret direction. */ -int getBidiOffsetAtMouseLocation(int x, int line) { +int[] getBidiOffsetAtMouseLocation(int x, int line) { String lineText = content.getLine(line); int lineOffset = content.getOffsetAtLine(line); GC gc = getGC(); StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc); - int[] values; - int offsetInLine; - x += horizontalScrollOffset; - values = bidi.getCaretOffsetAndDirectionAtX(x - leftMargin); - offsetInLine = values[0]; - lastCaretDirection = values[1]; - gc.dispose(); + int[] values = bidi.getCaretOffsetAndDirectionAtX(x + horizontalScrollOffset - leftMargin); - return lineOffset + offsetInLine; + gc.dispose(); + return new int[] {lineOffset + values[0], values[1]}; } /** * Returns the x position of the character at the specified offset @@ -5565,50 +5552,50 @@ public void invokeAction(int action) { break; case ST.SELECT_LINE_START: doLineStart(); - doSelection(SWT.LEFT); + doSelection(ST.COLUMN_PREVIOUS); break; case ST.SELECT_LINE_END: doLineEnd(); - doSelection(SWT.RIGHT); + doSelection(ST.COLUMN_NEXT); break; case ST.SELECT_COLUMN_PREVIOUS: doSelectionCursorPrevious(); - doSelection(SWT.LEFT); + doSelection(ST.COLUMN_PREVIOUS); break; case ST.SELECT_COLUMN_NEXT: doSelectionCursorNext(); - doSelection(SWT.RIGHT); + doSelection(ST.COLUMN_NEXT); break; case ST.SELECT_PAGE_UP: doSelectionPageUp(); - doSelection(SWT.LEFT); + doSelection(ST.COLUMN_PREVIOUS); break; case ST.SELECT_PAGE_DOWN: doSelectionPageDown(); break; case ST.SELECT_WORD_PREVIOUS: doSelectionWordPrevious(); - doSelection(SWT.LEFT); + doSelection(ST.COLUMN_PREVIOUS); break; case ST.SELECT_WORD_NEXT: doSelectionWordNext(); - doSelection(SWT.RIGHT); + doSelection(ST.COLUMN_NEXT); break; case ST.SELECT_TEXT_START: doContentStart(); - doSelection(SWT.LEFT); + doSelection(ST.COLUMN_PREVIOUS); break; case ST.SELECT_TEXT_END: doContentEnd(); - doSelection(SWT.RIGHT); + doSelection(ST.COLUMN_NEXT); break; case ST.SELECT_WINDOW_START: doPageStart(); - doSelection(SWT.LEFT); + doSelection(ST.COLUMN_PREVIOUS); break; case ST.SELECT_WINDOW_END: doPageEnd(); - doSelection(SWT.RIGHT); + doSelection(ST.COLUMN_NEXT); break; // Modification case ST.CUT: @@ -5898,6 +5885,7 @@ void performPaint(GC gc,int startLine,int startY, int renderHeight) { Color foreground = getForeground(); int lineCount = content.getLineCount(); int paintY = 0; + int gcStyle = getStyle() & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT); if (isSingleLine()) { lineCount = 1; @@ -5906,7 +5894,7 @@ void performPaint(GC gc,int startLine,int startY, int renderHeight) { } } Image lineBuffer = new Image(getDisplay(), clientArea.width, renderHeight); - GC lineGC = new GC(lineBuffer); + GC lineGC = new GC(lineBuffer, gcStyle); lineGC.setFont(getFont()); renderer.setCurrentFontStyle(SWT.NORMAL); @@ -6060,7 +6048,12 @@ public void redraw() { * @see Control#update */ public void redraw(int x, int y, int width, int height, boolean all) { - super.redraw(x, y, width, height, all); + if (isBidi()) { + // workaround for bug 4776 + super.redraw(x, y, width + 1, height, all); + } else { + super.redraw(x, y, width, height, all); + } if (height > 0) { int lineCount = content.getLineCount(); int startLine = (getTopPixel() + y) / lineHeight; @@ -6096,9 +6089,9 @@ void redrawBidiLines(int firstLine, int offsetInFirstLine, int lastLine, int end String line = content.getLine(firstLine); GC gc = getGC(); StyledTextBidi bidi = getStyledTextBidi(line, firstLineOffset, gc); - + bidi.redrawRange( - this, offsetInFirstLine, + this, offsetInFirstLine, Math.min(line.length(), endOffset) - offsetInFirstLine, leftMargin - horizontalScrollOffset, redrawY + topMargin, lineHeight); // redraw line break marker (either space or full client area width) @@ -6907,6 +6900,7 @@ void setBidiCaretLocation(StyledTextBidi bidi, int caretLine) { int lineStartOffset = content.getOffsetAtLine(caretLine); int offsetInLine = caretOffset - lineStartOffset; GC gc = null; + boolean isRightOriented = (getStyle() & SWT.RIGHT_TO_LEFT) != 0; if (bidi == null) { gc = getGC(); @@ -6917,7 +6911,10 @@ void setBidiCaretLocation(StyledTextBidi bidi, int caretLine) { } else { columnX = bidi.getTextPosition(offsetInLine, lastCaretDirection) + leftMargin - horizontalScrollOffset; } - if (StyledTextBidi.getKeyboardLanguageDirection() == SWT.RIGHT) { + // take the width of the caret into account + int keyboardDirection = StyledTextBidi.getKeyboardLanguageDirection(); + if ((keyboardDirection == SWT.RIGHT && isRightOriented == false) || + (keyboardDirection == SWT.LEFT && isRightOriented)){ columnX -= (getCaretWidth() - 1); } if (caret != null) { diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextBidi.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextBidi.java index c443b65772..e76ba3a1d0 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextBidi.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextBidi.java @@ -31,6 +31,7 @@ class StyledTextBidi { private int[] dx; // distance between character cells. in visual order. renderPositions[iV + 1] = renderPositions[iV] + dx[iV] private byte[] classBuffer; // the character types in logical order, see BidiUtil for the possible types private char[] glyphBuffer; // the glyphs in visual order as they will be rendered on screen. + private boolean isRightOriented;// writing orientation /** * This class describes a text segment of a single direction, either @@ -106,6 +107,7 @@ class StyledTextBidi { */ public StyledTextBidi(GC gc, int tabWidth, String text, StyleRange[] ranges, Font boldFont, int[] offsets) { int length = text.length(); + isRightOriented = (gc.getStyle() & SWT.MIRRORED) != 0; this.gc = gc; bidiSegments = offsets; @@ -155,6 +157,7 @@ public StyledTextBidi(GC gc, int tabWidth, String text, StyleRange[] ranges, Fon */ public StyledTextBidi(GC gc, String text, int[] offsets) { int length = text.length(); + isRightOriented = (gc.getStyle() & SWT.MIRRORED) != 0; this.gc = gc; bidiSegments = offsets; order = new int[length]; @@ -381,8 +384,6 @@ void fillBackground(int logicalStart, int length, int xOffset, int yOffset, int */ int[] getCaretOffsetAndDirectionAtX(int x) { int lineLength = getTextLength(); - int offset; - int direction; if (lineLength == 0) { return new int[] {0, 0}; @@ -395,9 +396,16 @@ int[] getCaretOffsetAndDirectionAtX(int x) { int visualOffset = getVisualOffsetAtX(x); // figure out if the character was clicked on the right or left int halfway = renderPositions[visualOffset] + dx[visualOffset] / 2; - boolean visualLeft = (x <= halfway); - offset = getLogicalOffset(visualOffset); + boolean visualLeft; + int offset = getLogicalOffset(visualOffset); + int direction; + if (isRightOriented) { + visualLeft = (x > halfway); + } + else { + visualLeft = (x <= halfway); + } if (isRightToLeft(offset)) { if (visualLeft) { if (isLigated(gc)) { @@ -413,23 +421,23 @@ int[] getCaretOffsetAndDirectionAtX(int x) { } else { // position the caret as if the caret is to the left - // of the character at location x and the PREVIOUS key is - // pressed - direction = ST.COLUMN_PREVIOUS; + // of the character at location x and the PREVIOUS + // key is pressed + direction = ST.COLUMN_PREVIOUS; } } else { if (visualLeft) { - // position the caret as if the caret is to the right - // of the character at location x and the PREVIOUS key is - // pressed - direction = ST.COLUMN_PREVIOUS; + // position the caret as if the caret is to the left + // of the character at location x and the PREVIOUS + // key is pressed + direction = ST.COLUMN_PREVIOUS; } else { - // position the caret as if the caret is to the left + offset++; + // position the caret as if the caret is to the right // of the character at location x and the NEXT key is // pressed - offset++; direction = ST.COLUMN_NEXT; } } @@ -457,6 +465,7 @@ private Vector getDirectionRuns(int logicalStart, int length) { int logicalEnd = logicalStart + length - 1; int segmentLogicalStart = logicalStart; int segmentLogicalEnd = segmentLogicalStart; + int checkSide = isRightOriented ? -1 : 1; if (logicalEnd < getTextLength()) { int bidiSegmentIndex = 0; @@ -481,8 +490,9 @@ private Vector getDirectionRuns(int logicalStart, int length) { // is no direction change. // If our segment type is LtoR, the order index for the next character will be one more if there is // no direction change. - ((isRightToLeftSegment && (order[segmentLogicalEnd + 1]+ 1 == order[segmentLogicalEnd])) || - (isRightToLeftSegment == false && (order[segmentLogicalEnd + 1]- 1 == order[segmentLogicalEnd]))) && + (order[segmentLogicalEnd + 1] == order[segmentLogicalEnd] || // treat ligatures as part of the direction segment + (isRightToLeftSegment && (order[segmentLogicalEnd + 1] + checkSide == order[segmentLogicalEnd])) || + (isRightToLeftSegment == false && (order[segmentLogicalEnd + 1] - checkSide == order[segmentLogicalEnd]))) && segmentLogicalEnd + 1 < bidiSegmentEnd) { segmentLogicalEnd++; } @@ -555,7 +565,7 @@ int getLigatureStartOffset(int offset) { * @param visualOffset the visual offset * @return the logical offset of the character at <code>visualOffset</code>. */ -private int getLogicalOffset(int visualOffset) { +int getLogicalOffset(int visualOffset) { int logicalOffset = 0; while (logicalOffset < order.length && order[logicalOffset] != visualOffset) { @@ -697,26 +707,29 @@ int getTextPosition(int logicalOffset, int direction) { if (getTextLength() == 0 || logicalOffset < 0) { return StyledText.XINSET; } + + boolean isRightToLeft = isRightToLeft(logicalOffset); // at or past end of line? if (logicalOffset >= order.length) { logicalOffset = Math.min(logicalOffset, order.length - 1); + isRightToLeft = isRightToLeft(logicalOffset); int visualOffset = order[logicalOffset]; - if (isRightToLeft(logicalOffset)) { - caretX = renderPositions[visualOffset]; + if ((!isRightOriented && !isRightToLeft) || (isRightOriented && isRightToLeft)) { + caretX = renderPositions[visualOffset] + dx[visualOffset]; } else { - caretX = renderPositions[visualOffset] + dx[visualOffset]; + caretX = renderPositions[visualOffset]; } } else // at beginning of line? if (logicalOffset == 0) { int visualOffset = order[logicalOffset]; - if (isRightToLeft(logicalOffset)) { - caretX = renderPositions[visualOffset] + dx[visualOffset]; + if ((!isRightOriented && !isRightToLeft) || (isRightOriented && isRightToLeft)) { + caretX = renderPositions[visualOffset]; } else { - caretX = renderPositions[visualOffset]; + caretX = renderPositions[visualOffset] + dx[visualOffset]; } } else @@ -733,14 +746,12 @@ int getTextPosition(int logicalOffset, int direction) { // do not consider local numbers as R2L here, to determine position, // because local numbers are navigated L2R and we want the caret to // be to the right of the number. see 1GK9API - if (isRightToLeft(logicalOffset - 1)) { - // moving from RtoL to LtoR - caretX = renderPositions[visualOffset]; - } - else { - // moving from LtoR to RtoL + isRightToLeft = isRightToLeft(logicalOffset - 1); + if ((!isRightOriented && !isRightToLeft) || (isRightOriented && isRightToLeft)) { caretX = renderPositions[visualOffset] + dx[visualOffset]; - } + } else { + caretX = renderPositions[visualOffset]; + } } else // consider local numbers as R2L in determining direction boundaries. @@ -752,23 +763,22 @@ int getTextPosition(int logicalOffset, int direction) { // consider local numbers as R2L here, to determine position, because // we want to stay in L2R segment and place the cursor to the left of // first L2R character. see 1GK9API - if (isRightToLeftInput(logicalOffset - 1)) { - // moving from LtoR to RtoL + isRightToLeft = isRightToLeft(logicalOffset - 1); + if ((!isRightOriented && !isRightToLeft) || (isRightOriented && isRightToLeft)) { + caretX = renderPositions[visualOffset] + dx[visualOffset]; + } else { + caretX = renderPositions[visualOffset]; + } + } + else { + int visualOffset = order[logicalOffset]; + if ((!isRightOriented && !isRightToLeft) || (isRightOriented && isRightToLeft)) { caretX = renderPositions[visualOffset]; } else { - // moving from RtoL to LtoR caretX = renderPositions[visualOffset] + dx[visualOffset]; } } - else - if (isRightToLeft(logicalOffset)) { - int visualOffset = order[logicalOffset]; - caretX = renderPositions[visualOffset] + dx[visualOffset]; - } - else { - caretX = renderPositions[order[logicalOffset]]; - } return caretX; } /** @@ -786,6 +796,17 @@ int getTextWidth() { return width; } /** + * Returns the visual offset of the character at the specified + * logical offset. + * <p> + * + * @param logicalOffset the logical offset + * @return the visual offset of the character at <code>logicalOffset</code>. + */ +int getVisualOffset(int logicalOffset) { + return order[logicalOffset]; +} +/** * Returns the visual offset of the character at the specified x * location. * <p> @@ -820,6 +841,23 @@ private int getVisualOffsetAtX(int x) { return high; } /** + * Returns if the character at the given offset is a latin number. + * <p> + * + * @param logicalIndex the index of the character + * @return + * true=the character at the specified index is a latin number + * false=the character at the specified index is not a latin number + */ +boolean isLatinNumber(int logicalIndex) { + boolean isLatinNumber = false; + + if (logicalIndex >= 0 && logicalIndex < classBuffer.length) { + isLatinNumber = classBuffer[logicalIndex] == BidiUtil.CLASS_LATINNUMBER; + } + return isLatinNumber; +} +/** * Returns if the character at the given offset is a local number. * <p> * |