2

開いているさまざまなファイルを循環できるようにすると がJTextAreaあります。別のファイルを選択すると、内容が変化します。ファイルごとに異なる Undo バッファを維持しようとしており、ファイルごとに別のバッファを定義しました。JComboBoxJTextAreaUndoManager

私は、「One」と「Two」と呼ぶ 2 つのバッファーを使用して問題を示すための、より単純な SSCCE を作成しました。それらを切り替えるための単純なボタンがあります。がUndoableEdit発生すると、アクティブなバッファをチェックaddEdit()し、それぞれに対して を実行しますUndoManager。「元に戻す」ボタンが押されると、それぞれの をチェックcanUndo()して実行します。と呼ばれるフラグがあります。これは、バッファーを切り替えて、それらの編集が記録されないようにするときに使用されます。undo()UndoManagerignoreEdit

バッファーを切り替えなければ問題はありません。Undo は期待どおりに機能します。バッファを切り替えてドキュメントを「壊す」ように見える場合にのみ、失敗します。次の手順を使用して、問題を再現できます。

バッファ「One」に、次のように入力します。

THIS
IS ONE
EXAMPLE

バッファ「Two」に切り替えて、次のように入力します。

THIS
IS ANOTHER
EXAMPLE

バッファ「One」に切り替え、「元に戻す」ボタンを複数回押します。元に戻す操作を数回行った後、バッファは次のようになります (カーソルが最初の 2 行を選択する方法はありません)。しかし、 の内容は のtextArea.getText()とおり正しいSystem.out.println()ので、レンダリングの問題のように見えますか?

THIS

THISIS ONE

誰かがファイルごとに独立した Undo バッファを実装しようとしたのはこれが初めてではないでしょうか? 私は明らかに Document モデルで何か間違ったことをしており、本質的にそれを壊していますが、これを修正する最善の方法についてアドバイスを探していますか?

SSCCE のコードは次のとおりです。

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.undo.*;

public class SSCCE extends JFrame implements ActionListener, UndoableEditListener {
  private final JLabel labTextArea;
  private final JTextArea textArea;
  private final JScrollPane scrollTextArea;
  private final Document docTextArea;
  private final JButton bOne, bTwo, bUndo;
  private final UndoManager uOne, uTwo;
  private String sOne, sTwo;
  private boolean ignoreEdit = false;

  public SSCCE(String[] args) {
    setTitle("SSCCE - Short, Self Contained, Correct Example");
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setSize(300, 200);
    setLocationRelativeTo(null);

    labTextArea = new JLabel("One");
    getContentPane().add(labTextArea, BorderLayout.PAGE_START);

    uOne = new UndoManager();
    uTwo = new UndoManager();
    sOne = new String();
    sTwo = new String();

    textArea = new JTextArea();
    docTextArea = textArea.getDocument();
    docTextArea.addUndoableEditListener(this);
    scrollTextArea = new JScrollPane(textArea);
    getContentPane().add(scrollTextArea, BorderLayout.CENTER);

    JPanel pButtons = new JPanel();
    bOne = new JButton("One");
    bOne.addActionListener(this);
    bOne.setFocusable(false);
    pButtons.add(bOne, BorderLayout.LINE_START);
    bTwo = new JButton("Two");
    bTwo.addActionListener(this);
    bTwo.setFocusable(false);
    pButtons.add(bTwo, BorderLayout.LINE_END);
    bUndo = new JButton("Undo");
    bUndo.addActionListener(this);
    bUndo.setFocusable(false);
    pButtons.add(bUndo, BorderLayout.LINE_END);
    getContentPane().add(pButtons, BorderLayout.PAGE_END);

    setVisible(true);
  }

  @Override
  public void actionPerformed(ActionEvent e) {
    if (e.getSource().equals(bOne)) {
      if (!labTextArea.getText().equals("One")) {
        sTwo = textArea.getText();
        ignoreEdit = true;
        textArea.setText(sOne);
        ignoreEdit = false;
        labTextArea.setText("One");
      }
    }
    else if (e.getSource().equals(bTwo)) {
      if (!labTextArea.getText().equals("Two")) {
        sOne = textArea.getText();
        ignoreEdit = true;
        textArea.setText(sTwo);
        ignoreEdit = false;
        labTextArea.setText("Two");
      }
    }
    else if (e.getSource().equals(bUndo)) {
      if (labTextArea.getText().equals("One")) {
        try {
          if (uOne.canUndo()) {
            System.out.println("Performing Undo for One");
            uOne.undo();
            System.out.println("Buffer One is now:\n" + textArea.getText() + "\n");
          }
          else {
            System.out.println("Nothing to Undo for One");
          }
        }
        catch (CannotUndoException ex) {
          ex.printStackTrace();
        }
      }
      else if (labTextArea.getText().equals("Two")) {
        try {
          if (uTwo.canUndo()) {
            System.out.println("Performing Undo for Two");
            uTwo.undo();
            System.out.println("Buffer Two is now:\n" + textArea.getText() + "\n");
          }
          else {
            System.out.println("Nothing to Undo for Two");
          }
        }
        catch (CannotUndoException ex) {
          ex.printStackTrace();
        }
      }
    }
  }

  @Override
  public void undoableEditHappened(UndoableEditEvent e) {
    if (!ignoreEdit) {
      if (labTextArea.getText().equals("One")) {
        System.out.println("Adding Edit for One");
        uOne.addEdit(e.getEdit());
      }
      else if (labTextArea.getText().equals("Two")) {
        System.out.println("Adding Edit for Two");
        uTwo.addEdit(e.getEdit());
      }
    }
  }

  public static void main(final String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
        new SSCCE(args);
      }
    });
  }
}
4

2 に答える 2

2

以前、Documentクラスの新しいインスタンス (それぞれが同じ Undo リスナーを参照)を作成しようとして、JTextArea.setDocument()代わりにJTextArea.setText(). ただし、Documentインターフェイスであり、インスタンス化することはできませんが、mKorbel が投稿したリファレンスを読んだ後、PlainDocument代わりにクラスを使用してこれを試してみましたが、うまくいきました。

クラスを保持し、それらを切り替えるHashMap<String, Document>ためにを維持することにしました。Documentを切り替えるとDocument、元に戻す/やり直しの問題が表示されませんDocument

以下の SSCCE を更新し、JTextArea.setDocument()代わりに を使用するようになりJTextArea.setText()ました。ignoreEditこれには、ブール値を必要としsetDocument()ないという利点もありUndoableEditEventますsetText()。次に、それぞれDocumentがローカル クラスを参照しますUndoableEditListener

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.undo.*;

public class SSCCE extends JFrame implements ActionListener, UndoableEditListener {
  private final JLabel labTextArea;
  private final JTextArea textArea;
  private final JScrollPane scrollTextArea;
  private final Document docTextArea;
  private final JButton bOne, bTwo, bUndo;
  private final UndoManager uOne, uTwo;
  private Document dOne, dTwo;

  public SSCCE(String[] args) {
    setTitle("SSCCE - Short, Self Contained, Correct Example");
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setSize(300, 200);
    setLocationRelativeTo(null);

    labTextArea = new JLabel("One");
    getContentPane().add(labTextArea, BorderLayout.PAGE_START);

    uOne = new UndoManager();
    uTwo = new UndoManager();
    dOne = new PlainDocument();
    dTwo = new PlainDocument();
    dOne.addUndoableEditListener(this);
    dTwo.addUndoableEditListener(this);

    textArea = new JTextArea();
    docTextArea = textArea.getDocument();
    docTextArea.addUndoableEditListener(this);
    textArea.setDocument(dOne);
    scrollTextArea = new JScrollPane(textArea);
    getContentPane().add(scrollTextArea, BorderLayout.CENTER);

    JPanel pButtons = new JPanel();
    bOne = new JButton("One");
    bOne.addActionListener(this);
    bOne.setFocusable(false);
    pButtons.add(bOne, BorderLayout.LINE_START);
    bTwo = new JButton("Two");
    bTwo.addActionListener(this);
    bTwo.setFocusable(false);
    pButtons.add(bTwo, BorderLayout.LINE_END);
    bUndo = new JButton("Undo");
    bUndo.addActionListener(this);
    bUndo.setFocusable(false);
    pButtons.add(bUndo, BorderLayout.LINE_END);
    getContentPane().add(pButtons, BorderLayout.PAGE_END);

    setVisible(true);
  }

  @Override
  public void actionPerformed(ActionEvent e) {
    if (e.getSource().equals(bOne)) {
      if (!labTextArea.getText().equals("One")) {
        textArea.setDocument(dOne);
        labTextArea.setText("One");
      }
    }
    else if (e.getSource().equals(bTwo)) {
      if (!labTextArea.getText().equals("Two")) {
        textArea.setDocument(dTwo);
        labTextArea.setText("Two");
      }
    }
    else if (e.getSource().equals(bUndo)) {
      if (labTextArea.getText().equals("One")) {
        try {
          if (uOne.canUndo()) {
            System.out.println("Performing Undo for One");
            uOne.undo();
            System.out.println("Buffer One is now:\n" + textArea.getText() + "\n");
          }
          else {
            System.out.println("Nothing to Undo for One");
          }
        }
        catch (CannotUndoException ex) {
          ex.printStackTrace();
        }
      }
      else if (labTextArea.getText().equals("Two")) {
        try {
          if (uTwo.canUndo()) {
            System.out.println("Performing Undo for Two");
            uTwo.undo();
            System.out.println("Buffer Two is now:\n" + textArea.getText() + "\n");
          }
          else {
            System.out.println("Nothing to Undo for Two");
          }
        }
        catch (CannotUndoException ex) {
          ex.printStackTrace();
        }
      }
    }
  }

  @Override
  public void undoableEditHappened(UndoableEditEvent e) {
    if (labTextArea.getText().equals("One")) {
      System.out.println("Adding Edit for One");
      uOne.addEdit(e.getEdit());
    }
    else if (labTextArea.getText().equals("Two")) {
      System.out.println("Adding Edit for Two");
      uTwo.addEdit(e.getEdit());
    }
  }

  public static void main(final String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
        new SSCCE(args);
      }
    });
  }
}
于 2014-05-27T14:02:46.073 に答える
1

これはそれ自体が答えではありませんが、同じ問題にアプローチする別の方法のデモンストレーションです。

これが行うことは、独自のとUndoableEditListenerを持つ自己管理プロキシに をラップすることです。UndoManagerDocument

これをJava 7およびJava 8でテストしました:

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import javax.swing.text.PlainDocument;
import javax.swing.undo.UndoManager;

public class UndoExample {

    public static void main(String[] args) {
        new UndoExample();
    }

    private int index = 0;
    private Map<String, Undoer> mapUndoers;
    private JTextArea ta;
    private Undoer current;

    public UndoExample() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                mapUndoers = new HashMap<>(2);
                mapUndoers.put("One", new Undoer());
                mapUndoers.put("Two", new Undoer());

                ta = new JTextArea(4, 20);
                ta.setWrapStyleWord(true);
                ta.setLineWrap(true);

                JButton btnOne = new JButton("One");
                JButton btnTwo = new JButton("Two");
                ActionListener al = new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        install(e.getActionCommand());
                    }
                };
                btnOne.addActionListener(al);
                btnTwo.addActionListener(al);

                JButton undo = new JButton("Undo");
                undo.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if (current != null) {
                            current.undo();
                        }
                    }
                });

                JPanel panel = new JPanel(new GridBagLayout());
                panel.add(btnOne);
                panel.add(btnTwo);
                panel.add(undo);

                install("One");

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new JScrollPane(ta));
                frame.add(panel, BorderLayout.SOUTH);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    protected void install(String name) {
        Undoer undoer = mapUndoers.get(name);
        if (undoer != null) {
            current = undoer;
            undoer.install(ta);
        }
    }

    public class Undoer implements UndoableEditListener {

        private UndoManager undoManager;
        private Document doc;

        public Undoer() {
            undoManager = new UndoManager();
            doc = createDocument();
            doc.addUndoableEditListener(this);
        }

        public void undo() {
            undoManager.undo();
        }

        public void undoOrRedo() {
            undoManager.undoOrRedo();
        }

        protected Document createDocument() {
            return new PlainDocument();
        }

        public void install(JTextComponent comp) {
            comp.setDocument(doc);
        }

        @Override
        public void undoableEditHappened(UndoableEditEvent e) {
            undoManager.addEdit(e.getEdit());
        }

    }

}
于 2014-05-27T23:57:24.957 に答える