--- a/editor.actions/src/org/netbeans/modules/editor/actions/AddCaretAction.java +++ a/editor.actions/src/org/netbeans/modules/editor/actions/AddCaretAction.java @@ -115,7 +115,7 @@ } }); - editorCaret.addCarets(dots); + editorCaret.addCarets(dots, null); // TODO handle biases } }); } --- a/editor.lib2/apichanges.xml +++ a/editor.lib2/apichanges.xml @@ -107,6 +107,21 @@ + + Position.Bias support added to EditorCaret + + + + + +

+ CaretInfo.getDotBias() and CaretInfo.getMarkBias() added together + with other methods and parameters for bias manipulation to properly handle bidirectional text. +

+
+ + +
EditorUtilities.addCaretUndoableEdit added --- a/editor.lib2/manifest.mf +++ a/editor.lib2/manifest.mf @@ -1,6 +1,6 @@ Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.modules.editor.lib2/1 -OpenIDE-Module-Implementation-Version: 47 +OpenIDE-Module-Implementation-Version: 48 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/editor/lib2/Bundle.properties OpenIDE-Module-Layer: org/netbeans/modules/editor/lib2/resources/layer.xml OpenIDE-Module-Needs: org.netbeans.modules.editor.actions --- a/editor.lib2/nbproject/project.properties +++ a/editor.lib2/nbproject/project.properties @@ -43,7 +43,7 @@ is.autoload=true javac.source=1.7 javac.compilerargs=-Xlint:unchecked -spec.version.base=2.11.0 +spec.version.base=2.12.0 javadoc.arch=${basedir}/arch.xml javadoc.apichanges=${basedir}/apichanges.xml --- a/editor.lib2/src/org/netbeans/api/editor/caret/CaretInfo.java +++ a/editor.lib2/src/org/netbeans/api/editor/caret/CaretInfo.java @@ -45,6 +45,7 @@ import java.util.logging.Logger; import javax.swing.text.Position; import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.annotations.common.NonNull; /** * Immutable info about a single caret used in caret API - see {@link EditorCaret}. @@ -67,15 +68,21 @@ private final CaretItem caretItem; private final Position dotPos; + + private final Position.Bias dotBias; private final Position markPos; + + private final Position.Bias markBias; private final Point magicCaretPosition; CaretInfo(CaretItem caretItem) { this.caretItem = caretItem; this.dotPos = caretItem.getDotPosition(); + this.dotBias = caretItem.getDotBias(); this.markPos = caretItem.getMarkPosition(); + this.markBias = caretItem.getMarkBias(); this.magicCaretPosition = caretItem.getMagicCaretPosition(); } @@ -90,6 +97,20 @@ } /** + * Get a bias of the dot position which is either + * {@link Position.Bias.Forward} or {@link Position.Bias.Backward} depending + * on whether the caret biases towards the next character or previous one. + * The bias is always forward for non bidirectional text. + * + * @return either forward or backward bias. + * @since 2.12 + */ + @NonNull + public Position.Bias getDotBias() { + return dotBias; + } + + /** * Return either the same object like {@link #getDotPosition()} if there's no selection * or return position denoting the other end of an existing selection (which is either before * or after the dot position depending of how the selection was created). @@ -102,6 +123,20 @@ } /** + * Get a bias of the mark position which is either + * {@link Position.Bias.Forward} or {@link Position.Bias.Backward} depending + * on whether the caret biases towards the next character or previous one. + * The bias is always forward for non bidirectional text. + * + * @return either forward or backward bias. + * @since 2.12 + */ + @NonNull + public Position.Bias getMarkBias() { + return markBias; + } + + /** * Fetches the current position of the caret. * * @return the position >=0 --- a/editor.lib2/src/org/netbeans/api/editor/caret/CaretItem.java +++ a/editor.lib2/src/org/netbeans/api/editor/caret/CaretItem.java @@ -47,6 +47,7 @@ import javax.swing.text.JTextComponent; import javax.swing.text.Position; import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.annotations.common.NonNull; /** * A single caret inside {@link EditorCaret} handled internally by EditorCaret. @@ -75,8 +76,12 @@ private Position dotPos; + private Position.Bias dotBias; + private Position markPos; + private Position.Bias markBias; + private Point magicCaretPosition; /** @@ -96,10 +101,12 @@ */ private int statusBits; - CaretItem(EditorCaret editorCaret, Position dotPos, Position markPos) { + CaretItem(EditorCaret editorCaret, Position dotPos, Position.Bias dotBias, Position markPos, Position.Bias markBias) { this.editorCaret = editorCaret; this.dotPos = dotPos; + this.dotBias = dotBias; this.markPos = markPos; + this.markBias = markBias; this.statusBits = UPDATE_CARET_BOUNDS; // Request visual bounds updating automatically } @@ -146,6 +153,16 @@ Position getDotPosition() { return dotPos; } + + /** + * Get the bias of the dot either forward or backward bias. + * + * @return either forward or backward bias. + */ + @NonNull + Position.Bias getDotBias() { + return dotBias; + } /** * Return either the same object like {@link #getDotPosition()} if there's @@ -161,6 +178,16 @@ return markPos; } + /** + * Get the bias of the mark either forward or backward bias. + * + * @return either forward or backward bias. + */ + @NonNull + Position.Bias getMarkBias() { + return markBias; + } + int getDot() { return (dotPos != null) ? dotPos.getOffset() : 0; } --- a/editor.lib2/src/org/netbeans/api/editor/caret/CaretTransaction.java +++ a/editor.lib2/src/org/netbeans/api/editor/caret/CaretTransaction.java @@ -562,7 +562,9 @@ } } - static CaretItem[] asCaretItems(EditorCaret caret, @NonNull List dotAndSelectionStartPosPairs) { + static CaretItem[] asCaretItems(EditorCaret caret, @NonNull List dotAndSelectionStartPosPairs, + List dotAndMarkBiases) + { int size = dotAndSelectionStartPosPairs.size(); if ((size & 1) != 0) { throw new IllegalStateException("Passed list has size=" + size + " which is not an even number."); @@ -570,9 +572,13 @@ CaretItem[] addedCarets = new CaretItem[size >> 1]; int listIndex = 0; for (int j = 0; j < addedCarets.length; j++) { + Position.Bias dotBias = (dotAndMarkBiases != null) ? dotAndMarkBiases.get(listIndex) : Position.Bias.Forward; Position dotPos = dotAndSelectionStartPosPairs.get(listIndex++); + + Position.Bias markBias = (dotAndMarkBiases != null) ? dotAndMarkBiases.get(listIndex) : Position.Bias.Forward; Position selectionStartPos = dotAndSelectionStartPosPairs.get(listIndex++); - CaretItem caretItem = new CaretItem(caret, dotPos, selectionStartPos); + + CaretItem caretItem = new CaretItem(caret, dotPos, dotBias, selectionStartPos, markBias); addedCarets[j] = caretItem; } return addedCarets; --- a/editor.lib2/src/org/netbeans/api/editor/caret/EditorCaret.java +++ a/editor.lib2/src/org/netbeans/api/editor/caret/EditorCaret.java @@ -401,7 +401,7 @@ public EditorCaret() { caretItems = new GapList<>(); sortedCaretItems = new GapList<>(); - CaretItem singleCaret = new CaretItem(this, null, null); + CaretItem singleCaret = new CaretItem(this, null, Position.Bias.Forward, null, Position.Bias.Forward); caretItems.add(singleCaret); sortedCaretItems.add(singleCaret); @@ -418,6 +418,20 @@ public int getDot() { return getLastCaret().getDot(); } + + /** + * Get a bias of the dot position which is either + * {@link Position.Bias.Forward} or {@link Position.Bias.Backward} depending + * on whether the caret biases towards the next character or previous one. + * The bias is always forward for non bidirectional text document. + * + * @return either forward or backward bias. + * @since 2.12 + */ + @NonNull + public Position.Bias getDotBias() { + return getLastCaret().getDotBias(); + } /** * Get mark offset of the last created caret in the underlying document. @@ -431,6 +445,20 @@ } /** + * Get a bias of the mark position which is either + * {@link Position.Bias.Forward} or {@link Position.Bias.Backward} depending + * on whether the caret biases towards the next character or previous one. + * The bias is always forward for non bidirectional text document. + * + * @return either forward or backward bias. + * @since 2.12 + */ + @NonNull + public Position.Bias getMarkBias() { + return getLastCaret().getMarkBias(); + } + + /** * Get information about all existing carets in the order they were created. *
* The list always has at least one item. The last caret (last item of the list) @@ -530,7 +558,7 @@ * @see Caret#setDot(int) */ public @Override void setDot(final int offset) { - setDot(offset, MoveCaretsOrigin.DEFAULT); + setDot(offset, Position.Bias.Forward, MoveCaretsOrigin.DEFAULT); } /** @@ -547,11 +575,14 @@ * actions (pg up, pg down, left, right, ...). *

* @param offset new offset for the caret + * @param bias new bias for the caret. Use either {@link Position.Bias.Forward} + * or {@link Position.Bias.Backward} depending on whether the caret should bias + * towards the next character or previous one. Use forward bias for non-bidirectional text document. * @param orig specifies the operation which caused the caret to move. * @see #setDot(int) * @since 2.10 */ - public void setDot(final int offset, MoveCaretsOrigin orig) { + public void setDot(final int offset, Position.Bias bias, MoveCaretsOrigin orig) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("setDot: offset=" + offset); //NOI18N if (LOG.isLoggable(Level.FINEST)) { @@ -585,7 +616,7 @@ * @see Caret#moveDot(int) */ public @Override void moveDot(final int offset) { - moveDot(offset, MoveCaretsOrigin.DEFAULT); + moveDot(offset, Position.Bias.Forward, MoveCaretsOrigin.DEFAULT); } /** @@ -603,11 +634,14 @@ *

* * @param offset new offset for the caret + * @param bias new bias for the caret. Use either {@link Position.Bias.Forward} + * or {@link Position.Bias.Backward} depending on whether the caret should bias + * towards the next character or previous one. Use forward bias for non-bidirectional text document. * @param orig specifies the operation which caused the caret to move. * @see #moveDot(int) * @since 2.10 */ - public void moveDot(final int offset, MoveCaretsOrigin orig) { + public void moveDot(final int offset, Position.Bias bias, MoveCaretsOrigin orig) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("moveDot: offset=" + offset); //NOI18N } @@ -719,18 +753,26 @@ * * * @param dotPos position of the newly created caret. + * @param dotBias bias of the new caret. Use either {@link Position.Bias.Forward} + * or {@link Position.Bias.Backward} depending on whether the caret should bias + * towards the next character or previous one. Use forward bias for non-bidirectional text document. * @param markPos beginning of the selection (the other end is dotPos) or the same position like dotPos for no selection. * The markPos may have higher offset than dotPos to select in a backward direction. + * @param markBias bias of the begining of the selection. Use either {@link Position.Bias.Forward} + * or {@link Position.Bias.Backward} depending on whether the caret should bias + * towards the next character or previous one. Use forward bias for non-bidirectional text document. * @return difference between current count of carets and the number of carets when the operation started. - * Returns Integer.MIN_VALUE if the operation was cancelled due to the caret not being installed in any text component + * Returns Integer.MIN_VALUE if the operation was canceled due to the caret not being installed in any text component * or no document installed in the text component. *
* Note that adding a new caret to offset where another caret is already located may lead * or no document installed in the text component. */ - public int addCaret(@NonNull Position dotPos, @NonNull Position markPos) { + public int addCaret(@NonNull Position dotPos, @NonNull Position.Bias dotBias, + @NonNull Position markPos, @NonNull Position.Bias markBias) + { return runTransaction(CaretTransaction.RemoveType.NO_REMOVE, 0, - new CaretItem[] { new CaretItem(this, dotPos, markPos) }, null); + new CaretItem[] { new CaretItem(this, dotPos, dotBias, markPos, markBias) }, null); } /** @@ -758,13 +800,16 @@ * @param dotAndMarkPosPairs list of position pairs consisting of dot position * and mark position (selection start position) which may be the same position like the dot * if the particular caret has no selection. The list must have even size. + * @param dotAndMarkBiases list of position biases (corresponding to the dot and mark positions + * in the previous parameter) or null may be passed if all dotAndMarkPosPairs positions should have a forward bias. * @return difference between current count of carets and the number of carets when the operation started. - * Returns Integer.MIN_VALUE if the operation was cancelled due to the caret not being installed in any text component + * Returns Integer.MIN_VALUE if the operation was canceled due to the caret not being installed in any text component * or no document installed in the text component. + * @see #addCaret(javax.swing.text.Position, javax.swing.text.Position.Bias, javax.swing.text.Position, javax.swing.text.Position.Bias) */ - public int addCarets(@NonNull List dotAndMarkPosPairs) { + public int addCarets(@NonNull List dotAndMarkPosPairs, List dotAndMarkBiases) { return runTransaction(CaretTransaction.RemoveType.NO_REMOVE, 0, - CaretTransaction.asCaretItems(this, dotAndMarkPosPairs), null); + CaretTransaction.asCaretItems(this, dotAndMarkPosPairs, dotAndMarkBiases), null); } /** @@ -776,15 +821,17 @@ * @param dotAndMarkPosPairs list of position pairs consisting of dot position * and mark position (selection start position) which may be the same position like dot * if the particular caret has no selection. The list must have even size. + * @param dotAndMarkBiases list of position biases (corresponding to the dot and mark positions + * in the previous parameter) or null may be passed if all dotAndMarkPosPairs positions should have a forward bias. * @return difference between current count of carets and the number of carets when the operation started. * Returns Integer.MIN_VALUE if the operation was cancelled due to the caret not being installed in any text component * or no document installed in the text component. */ - public int replaceCarets(@NonNull List dotAndMarkPosPairs) { + public int replaceCarets(@NonNull List dotAndMarkPosPairs, List dotAndMarkBiases) { if (dotAndMarkPosPairs.isEmpty()) { throw new IllegalArgumentException("dotAndSelectionStartPosPairs list must not be empty"); } - CaretItem[] addedItems = CaretTransaction.asCaretItems(this, dotAndMarkPosPairs); + CaretItem[] addedItems = CaretTransaction.asCaretItems(this, dotAndMarkPosPairs, dotAndMarkBiases); return runTransaction(CaretTransaction.RemoveType.REMOVE_ALL_CARETS, 0, addedItems, null); } @@ -1784,7 +1831,8 @@ // Set caret to zero position upon document change (DefaultCaret impl does this too) runTransaction(CaretTransaction.RemoveType.REMOVE_ALL_CARETS, 0, - new CaretItem[] { new CaretItem(this, newDoc.getStartPosition(), null) }, null); + new CaretItem[] { new CaretItem(this, newDoc.getStartPosition(), Position.Bias.Forward, + null, Position.Bias.Forward ) }, null); // Leave caretPos and markPos null => offset==0 prefs = (mimeType != null) ? MimeLookup.getLookup(mimeType).lookup(Preferences.class) : null; @@ -2576,7 +2624,8 @@ try { Position pos = doc.createPosition(offset); runTransaction(CaretTransaction.RemoveType.NO_REMOVE, 0, - new CaretItem[] { new CaretItem(EditorCaret.this, pos, pos) }, null); + new CaretItem[] { new CaretItem(EditorCaret.this, pos, Position.Bias.Forward, + pos, Position.Bias.Forward) }, null); } catch (BadLocationException ex) { // Do nothing } --- a/editor.lib2/src/org/netbeans/modules/editor/lib2/CaretUndoEdit.java +++ a/editor.lib2/src/org/netbeans/modules/editor/lib2/CaretUndoEdit.java @@ -66,7 +66,7 @@ final Document doc; // (16=super)+4=20 bytes /** - * Offset of the dot to restore and last bit is a marker for undo/redo + * Offset of the dot to restore and last bit is a marker for position bias - TODO */ private int dotOffset; // 24 bytes @@ -116,7 +116,7 @@ protected void restoreEditorCaret(EditorCaret caret) throws BadLocationException { Position dotPos = doc.createPosition(getDotOffset()); - caret.replaceCarets(Arrays.asList(dotPos, dotPos)); + caret.replaceCarets(Arrays.asList(dotPos, dotPos), null); // TODO handle biases } protected void restoreLegacyCaret(Caret caret) { @@ -177,7 +177,7 @@ splitOffset = extraDotAndMarkOffsets[i++]; } } - caret.replaceCarets(dotAndMarkPosPairs); + caret.replaceCarets(dotAndMarkPosPairs, null); // TODO handle biases } @Override --- a/editor.search/src/org/netbeans/modules/editor/search/EditorFindSupport.java +++ a/editor.search/src/org/netbeans/modules/editor/search/EditorFindSupport.java @@ -451,7 +451,8 @@ if (eCaret instanceof EditorCaret) { EditorCaret caret = (EditorCaret) eCaret; try { - caret.addCaret(c.getDocument().createPosition(end), c.getDocument().createPosition(start)); + caret.addCaret(c.getDocument().createPosition(end), Position.Bias.Forward, + c.getDocument().createPosition(start), Position.Bias.Forward); } catch (BadLocationException ex) { Exceptions.printStackTrace(ex); } --- a/editor.search/src/org/netbeans/modules/editor/search/SearchBar.java +++ a/editor.search/src/org/netbeans/modules/editor/search/SearchBar.java @@ -947,7 +947,7 @@ newCarets.add(startPos); } - editorCaret.replaceCarets(newCarets); + editorCaret.replaceCarets(newCarets, null); // TODO handle biases textComponent.requestFocusInWindow(); } --- a/editor.search/src/org/netbeans/modules/editor/search/actions/AddCaretSelectAllAction.java +++ a/editor.search/src/org/netbeans/modules/editor/search/actions/AddCaretSelectAllAction.java @@ -130,7 +130,7 @@ newCarets.add(startPos); } - editorCaret.replaceCarets(newCarets); + editorCaret.replaceCarets(newCarets, null); // TODO handle biases } } catch (BadLocationException ex) { Exceptions.printStackTrace(ex);