开发者

How to prevent the cursor from moving after undoing a Document.Replace

If you have a text component in Java and you do a replace the cursor will not move, if 开发者_运维百科you undo and redo that replace using a standard undo manager the cursor will move to the beginning or end of that insertion or deletion.

How would I prevent this behavior?

I was able to trigger this with the Java TextComponentDemo, where I added a simple replace action that did this:

doc.replace(doc.getText(0, doc.getLength()).indexOf("mouse"), 5, "cat", null);

If I then use the demo's undo's and redo's the cursor will move.


Java Swing isn't the only API that has this behaviour in the standard undo manager. Sometimes XUL (which is the base for Firefox and Thunderbird) does the same thing with text areas in 3rd-party extensions. Essentially, even though the original text and the replacement text are similar in this case, the text area has to treat the document as an entirely new one, just like if you had done a select all and paste to overwrite the old text. In general, restoring the same cursor position to the new document would be useless, and if the document is shorter, it might not even be possible.

I think the easiest way around this would be to create your own custom actions for replacing text. Listen for them and perform the actions instead of performing the default action. Your custom action should be a compound action which scans the document manually, replacing substrings in the existing document - scan and replace all the way to the end by performing a number of document changes. When you override the undo method, just go through the list of modifications you made, undoing each one in reverse order. As long as each action in the compound action sets the text and the cursor position correctly, and its undo method works properly, the whole compound action will also undo properly.

This guide should hopefully explain this concept a little more clearly. The example merges actions into groups while the user types. You just need to do the same, but with procedural edits instead.


Check out the caret update policy in the class DefaultCaret.

The following update policies are allowed:

  • NEVER_UPDATE: the caret stays at the same absolute position in the document regardless of any document updates, except when document length becomes less than the current caret position due to removal. In that case caret position is adjusted to the end of the document. The caret doesn't try to keep itself visible by scrolling the associated view when using this policy.
  • ALWAYS_UPDATE: the caret always tracks document changes. For regular changes it increases its position if an insertion occurs before or at its current position, and decreases position if a removal occurs before its current position. For undo/redo updates it is always moved to the position where update occurred. The caret also tries to keep itself visible by calling adjustVisibility method.
  • UPDATE_WHEN_ON_EDT: acts like ALWAYS_UPDATE if the document updates are performed on the Event Dispatching Thread and like NEVER_UPDATE if updates are performed on other thread.

You can set the update policy to NEVER_UPDATE before an undo or redo action, then set it back to what it was after the action.

public void actionPerformed(ActionEvent e) {
    int updatePolicy = caret.getUpdatePolicy();
    caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
    undoManager.undo();
    caret.setUpdatePolicy(updatePolicy);
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜