In a master/detail view I have a series of text fields (and one or two other controls) that all pertain to the detail of the currently selected item. They all share the same DocumentListener
so if you change any of them a pair of "save"/"discard" buttons become enabled. The buttons invoke a method and I can happily save/discard items.
However, when I use InputMap
and ActionMap
to attach a shared saveAction to the enter key and a shared discardAction to the escape key the discardAction only works on some fields (saveAction works for them all).
When logging I can see that for the fields that work the discardAction is triggered first, followed by the appropriate combination of removeUpdate and insertUpdate.
For the fields that don't work the discardAction is never triggered. Enough chitter, chatter - here is the pertinent code (copy and paste, not paraphrase):
docChangeListener = new DocumentListener() {
public void insertUpdate(DocumentEvent de) {
System.out.println("\t insertUpdate just got triggered");
memberDetailsChanged(de);
}
public void removeUpdate(DocumentEvent de) {
System.out.println("\t removeUpdate just got triggered");
memberDetailsChanged(de);
}
public void changedUpdate(DocumentEvent de) {
// Not a styled document, safely ignore
}
};
saveAction = new AbstractAction() {
public void actionPerformed(ActionEvent ae) {
System.out.println("\t saveAction just got triggered");
saveChanges();
}
};
discardAction = new AbstractAction() {
public void actionPerformed(ActionEvent ae) {
System.out.println("\t discardAction just got triggered");
discardChanges();
}
};
private void registerDetailField(final JTextField field) {
field.getDocument().putProperty("field", field);
field.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "saveActionKey");
field.getActionMap().put("saveActionKey", saveAction);
field.getInputMap().put(KeyStroke.getKeyStroke("ESCAPE"), "discardActionKey");
field.getActionMap().put("discardActionKey", discardAction);
field.getDocument().addDocumentListener(docChangeListener);
}
All the text fields are registered the same way (using registerDetailField()
). They also have putClientProperty
called on them to allocate them a type for validation (see below).
The ONLY difference between the fields that work and the fields that don't is the actual validation process. I'll cut it down because it is so long but I feel I have to include it. discardAction SEEMS to fire first for the fields that work but the fields that don't work all have custom validation in common.
private void verifyField(final JTextField field) {
int fieldType = ((Integer)field.getClientProperty("type")).intValue();
String fieldValue = field.getText();
switch (fieldType) {
case STANDARD_FIELD:
return; // No validation at the moment
case MEMBER_NUMBER_FIELD:
if (fieldValue.length() == 0) { // Field is required
field.setBackground(REQUIRED_COLOUR);
field.setToolTipText("This is a required field");
invalidFields.add(field);
return;
}
// Check proposed value is valid
if (customTableModel.memberNumStringIsValid(fieldValue,
selectedMember.getMemberNumber())) {
field.setBackground(NORMAL_COLOUR);
field.setToolTipText(null);
invalidFields.remove(field);
} else {
field.setBackground(ERROR_COLOUR);
field.setToolTipText("This value must be a unique,
positive number");
invalidFields.add(field);
}
return;
/* SNIP */
default:
return;
}
}
Hopefully it's a simple problem with my verifyField method that I'm overlooking due to lack of sleep but at the moment I'm completely stumped.