JTextArea setText() & UndoManager

前端 未结 5 471
夕颜
夕颜 2020-12-10 21:43

I\'m using an UndoManager to capture changes in my JTextArea.

The method setText() however deletes everything and then pastes

5条回答
  •  一向
    一向 (楼主)
    2020-12-10 21:43

    I needed a solution that combined grouping the remove/insert of replace with a single undo (aterai's answer) plus treating continuous insertion/deletions of single characters as a single undo (similar to http://java-sl.com/tip_merge_undo_edits.html).

    The combined code is:

       /*##################*/
       /* TextCompoundEdit */
       /*##################*/
       class TextCompoundEdit extends CompoundEdit
         {
          private boolean isUnDone = false;
    
          /*************/
          /* getLength */
          /*************/
          public int getLength()
            {
             return edits.size();
            }
    
          /********/
          /* undo */
          /********/
          public void undo() throws CannotUndoException
            {
             super.undo();
             isUnDone = true;
            }
    
          /********/
          /* redo */
          /********/
          public void redo() throws CannotUndoException
            {
             super.redo();
             isUnDone = false;
            }
    
          /***********/
          /* canUndo */
          /***********/
          public boolean canUndo()
            {
             return (edits.size() > 0) && (! isUnDone);
            }
    
          /***********/
          /* canRedo */
          /***********/
          public boolean canRedo()
            {
             return (edits.size() > 0) && isUnDone;
            }
         }
    
       /*#################*/
       /* TextUndoManager */
       /*#################*/
       class TextUndoManager extends AbstractUndoableEdit 
                             implements UndoableEditListener
         {     
          private String lastEditName = null;
          private int lastStart = 0;
          private ArrayList edits = new ArrayList();
          private TextCompoundEdit current;
          private int pointer = -1;
          private int groupIndex = 0;
          private String groupName = null;
    
          /************************/
          /* undoableEditHappened */
          /************************/
          public void undoableEditHappened(
            UndoableEditEvent e)
            {
             boolean isNeedStart = false;
             UndoableEdit edit = e.getEdit();
    
             if (! (edit instanceof AbstractDocument.DefaultDocumentEvent))
               { return; }
    
             AbstractDocument.DefaultDocumentEvent event = (AbstractDocument.DefaultDocumentEvent) edit;
    
             int start = event.getOffset();
    
             String editName;
    
             /*============================================*/
             /* If an explicit group name is not present,  */
             /* use the INSERT/REMOVE name from the event. */
             /*============================================*/
    
             if (groupName != null)
               { editName = groupName; }
             else
               { editName = event.getType().toString(); }
    
             /*============================*/
             /* Create a new compound edit */
             /* for the very first edit.   */
             /*============================*/
    
             if (current == null)
               { isNeedStart = true; }
    
             /*============================*/
             /* Create a new compound edit */
             /* for a different operation. */
             /*============================*/
    
             else if ((lastEditName == null) || 
                      (! lastEditName.equals(editName)))
               { isNeedStart = true; }
    
             /*================================================*/
             /* Only group continuous single character inserts */
             /* and deletes. Create a new edit if the user has */
             /* moved the caret from its prior position.       */
             /*================================================*/
    
             else if (groupName == null)
               {            
                if ((event.getType() == DocumentEvent.EventType.INSERT) &&
                         (start != (lastStart + 1)))
                  { isNeedStart = true; }
                else if ((event.getType() == DocumentEvent.EventType.REMOVE) &&
                         (start != (lastStart - 1)))
                  { isNeedStart = true; }
               }
    
             /*=========================================*/
             /* Adding a new edit will clear all of the */
             /* redos forward of the current position.  */
             /*=========================================*/
    
             while (pointer < edits.size() - 1)
               {
                edits.remove(edits.size() - 1);
                isNeedStart = true;
               }
    
             /*===================*/
             /* Add the new edit. */
             /*===================*/
    
             if (isNeedStart)
               { createCompoundEdit(); }
    
             current.addEdit(edit);
    
             /*=====================================*/
             /* Remember prior state for next edit. */
             /*=====================================*/
    
             lastEditName = editName;
             lastStart = start;
            }
    
          /*********************/
          /* startEditGrouping */
          /*********************/
          public void startEditGrouping()
            {
             groupName = "Group-" + groupIndex++;
            }
    
          /********************/
          /* stopEditGrouping */
          /********************/
          public void stopEditGrouping()
            {
             groupName = null;
            }
    
          /**********************/
          /* createCompoundEdit */
          /**********************/
          private void createCompoundEdit()
            {
             if (current == null)
               { current = new TextCompoundEdit(); }
             else if (current.getLength() > 0)
               { current = new TextCompoundEdit(); }
    
             edits.add(current);
             pointer++;
            }
    
          /********/
          /* undo */
          /********/ 
          public void undo() throws CannotUndoException
            {
             if (! canUndo())
               { throw new CannotUndoException(); }
    
             TextCompoundEdit u = edits.get(pointer);
             u.undo();
             pointer--;
            }
    
          /********/
          /* redo */
          /********/
          public void redo() throws CannotUndoException
            {
             if (! canRedo())
               { throw new CannotUndoException(); }
    
             pointer++;
             TextCompoundEdit u = edits.get(pointer);
             u.redo();
            }
    
          /***********/
          /* canUndo */
          /***********/
          public boolean canUndo()
            { 
             return pointer >= 0; 
            }
    
          /***********/
          /* canRedo */
          /***********/
          public boolean canRedo()
            {
             return (edits.size() > 0) && (pointer < (edits.size() - 1));
            }
         }
    
       /*#######################*/
       /* TextUndoPlainDocument */
       /*#######################*/
       class TextUndoPlainDocument extends PlainDocument 
         {    
          private TextUndoManager undoManager;
    
          /*************************/
          /* TextUndoPlainDocument */
          /*************************/
          TextUndoPlainDocument(
            TextUndoManager theManager)
            {
             super();
             undoManager = theManager;
             this.addUndoableEditListener(undoManager);
            }
    
          /***********/
          /* replace */
          /***********/
          @Override 
          public void replace(
            int offset, 
            int length,
            String text, 
            AttributeSet attrs) throws BadLocationException
            {
             if (length == 0)
               { super.replace(offset,length,text,attrs); }
             else
               {
                undoManager.startEditGrouping();
                super.replace(offset,length,text,attrs); 
                undoManager.stopEditGrouping();
               }
            }
         }
    

    I invoke it in this way:

    JTextArea textArea = new JTextArea(); 
    TextUndoManager textAreaUndo = new TextUndoManager();
    textArea.setDocument(new TextUndoPlainDocument(textAreaUndo));
    

提交回复
热议问题