I want two textfields (from now A and B) sharing the same content as the user inputs on any of them. I can make one mirror the other (B mirrors A) or the opposite (A mirrors B). But when I keep both DocumentListeners the execution starts to throw Exceptions.
According to the Oracle's Docs I can't use a DocumentListener to mutate the content of a document from within the Listener itself. Which I find weird since I already did it in the first case (B mirrors A) or the opposite case. Anyway the code still "works" but with an Exception thrown every two events triggered.
KeyListeners are not reliable for this particular case and I refuse to use buttons because I like the real-time look DocumentListener gives.
Any suggestions?
Here's my code:
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
public class Mirror {
private JTextField oriText;
private JTextField mirrorText;
private static int debugCounter; //Counts the times an Event is Triggered.
public static void main(String[] args) {
Mirror gui = new Mirror();
gui.build();
}
public void build(){
JFrame frame = new JFrame();
JPanel panel = new JPanel();
panel.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
frame.getContentPane().add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
c.gridx = 0;
c.gridy = 0;
JLabel original = new JLabel("Original");
panel.add(original, c);
c.gridy = 1;
oriText = new JTextField(10);
panel.add(oriText,c);
c.gridy = 2;
JLabel mirror = new JLabel("Mirror");
panel.add(mirror, c);
c.gridy = 3;
mirrorText = new JTextField(10);
panel.add(mirrorText, c);
mirrorText.getDocument().addDocumentListener(new MyDocumentListenerII()); // Comment this line to see only the 1st Case (B mirrors A)
oriText.getDocument().addDocumentListener(new MyDocumentListener()); // Comment this line to see only the 2nd Case (A mirrors B)
frame.pack();
frame.setVisible(true);
}
class MyDocumentListener implements DocumentListener{
@Override
public void changedUpdate(DocumentEvent e) {
//Does nothing.
}
@Override
public void insertUpdate(DocumentEvent e) {
mirror();
}
@Override
public void removeUpdate(DocumentEvent e) {
mirror();
}
}
class MyDocumentListenerII implements DocumentListener{
@Override
public void changedUpdate(DocumentEvent e) {
// Does nothing.
}
@Override
public void insertUpdate(DocumentEvent e) {
mirror1();
}
@Override
public void removeUpdate(DocumentEvent e) {
mirror1();
}
}
public void mirror(){
if (!oriText.getText().equals(mirrorText.getText())){ //Without this each Event trigger the other in some sort of Paradoxical cycle.
mirrorText.setText(oriText.getText());
debugCounter++;
System.out.println(debugCounter+" events triggered");
}
}
public void mirror1(){
if (!mirrorText.getText().equals(oriText.getText())){
oriText.setText(mirrorText.getText());
debugCounter++;
System.out.println(debugCounter+" events triggered");
}
}
}
The problem you're having is that since both
JTextFields need to be sync, each field'sDocumentListenerneeds to update the other field. However, that update causes the otherDocumentListenerto attempt to update the first field, causing the thrownIllegalStateException, since something is attempting to modify the field while aDocumentListenerfor it is executing.The solution is to block calls to
setText(String)when aDocumentListeneris being executed for that field. This can be done withbooleanvariables. Below is the code for one of theDocumentListeners:where
blockAandblockBarebooleaninstance fields (or possiblyfinalvariables in the method). Now, when a method fires intextFieldA'sDocumentListener, a flag is set to indicate not to usetextFieldA.setText(String). We, also see howtextFieldB.setText(String)is blocked whenblockBis set. TheDocumentListenerfortextFieldBlooks about the same.Now,
textFieldAwon't be set during a call inside itsDocumentListener, same for the other field. Such a call would be redundant anyway: the text of each field would be same at that point.