1

I have a DoubleEditor that I have modified from an Integer Editor that I pulled from the net. I am using this to validate double values entered on a JXTable cell meant to take values from 0.00 to 100.00. However, I have this strange problem: If I enter 1.999 in the cell, I press enter nothing happens. (I expected it to convert this to 2.00). If I click to edit the cell with the value 1.999, it then performs the conversions. If I enter 1.9999 it performs the conversion immediately!

All I need from this editor is to: 1) Represent my values as 10.00 and 2) it must round off to 2d.p any entered value

Here is the code:

public class DoubleEditor extends DefaultCellEditor {

JFormattedTextField ftf;
DecimalFormat doubleFormat;
private Double minimum, maximum;
private boolean DEBUG = false;

public DoubleEditor(double min, double max) {
    super(new JFormattedTextField());
    ftf = (JFormattedTextField) getComponent();
    minimum = new Double(min);
    maximum = new Double(max);

    //Set up the editor for the integer cells.
    doubleFormat = new DecimalFormat("###.##");//Setting out the formatter here
    doubleFormat.setMaximumFractionDigits(2);//2dp
    NumberFormatter doubleFormatter = new NumberFormatter(doubleFormat);
    doubleFormatter.setFormat(doubleFormat);
    doubleFormatter.setMinimum(minimum);
    doubleFormatter.setMaximum(maximum);

    ftf.setFormatterFactory(
            new DefaultFormatterFactory(doubleFormatter));
    ftf.setValue(minimum);
    ftf.setHorizontalAlignment(JTextField.CENTER);
    ftf.setFocusLostBehavior(JFormattedTextField.PERSIST);

    //React when the user presses Enter while the editor is
    //active.  (Tab is handled as specified by
    //JFormattedTextField's focusLostBehavior property.)
    ftf.getInputMap().put(KeyStroke.getKeyStroke(
            KeyEvent.VK_ENTER, 0),
            "check");
    ftf.getActionMap().put("check", new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent e) {
            if (!ftf.isEditValid()) { //The text is invalid.
                if (userSaysRevert()) { //reverted
                    ftf.postActionEvent(); //inform the editor
                }
            } else {
                try {              //The text is valid,
                    ftf.commitEdit();     //so use it.
                    ftf.postActionEvent(); //stop editing
                } catch (java.text.ParseException exc) {
                }
            }
        }
    });
}

//Override to invoke setValue on the formatted text field.
@Override
public Component getTableCellEditorComponent(JTable table,
        Object value, boolean isSelected,
        int row, int column) {
    JFormattedTextField ftf
            = (JFormattedTextField) super.getTableCellEditorComponent(
                    table, value, isSelected, row, column);
    ftf.setValue(value);
    return ftf;
}

//Override to ensure that the value remains an Double.
@Override
public Object getCellEditorValue() {
    JFormattedTextField ftf = (JFormattedTextField) getComponent();
    Object o = ftf.getValue();
    if (o instanceof Double) {//Watch out !!!
        return o;
    } else if (o instanceof Number) {

        return new Double(((Number) o).doubleValue());
    } else {
        if (DEBUG) {
            System.out.println("getCellEditorValue: o isn't a Number");
        }
        try {
            return doubleFormat.parseObject(o.toString());
        } catch (ParseException exc) {
            System.err.println("getCellEditorValue: can't parse o: " + o);
            return null;
        }
    }
}

//Override to check whether the edit is valid,
//setting the value if it is and complaining if
//it isn't.  If it's OK for the editor to go
//away, we need to invoke the superclass's version 
//of this method so that everything gets cleaned up.
@Override
public boolean stopCellEditing() {
    JFormattedTextField ftf = (JFormattedTextField) getComponent();
    if (ftf.isEditValid()) {
        try {
            ftf.commitEdit();
        } catch (java.text.ParseException exc) {
        }

    } else { //text is invalid
        if (!userSaysRevert()) { //user wants to edit
            return false; //don't let the editor go away
        }
    }
    return super.stopCellEditing();
}

/**
 * Lets the user know that the text they entered is bad. Returns true if the
 * user elects to revert to the last good value. Otherwise, returns false,
 * indicating that the user wants to continue editing.
 */
protected boolean userSaysRevert() {
    Toolkit.getDefaultToolkit().beep();
    ftf.selectAll();
    Object[] options = {"Edit",
        "Revert"};
    int answer = JOptionPane.showOptionDialog(
            SwingUtilities.getWindowAncestor(ftf),
            "The value must be an integer between "
            + minimum + " and "
            + maximum + ".\n"
            + "You can either continue editing "
            + "or revert to the last valid value.",
            "Invalid Text Entered",
            JOptionPane.YES_NO_OPTION,
            JOptionPane.ERROR_MESSAGE,
            null,
            options,
            options[1]);

    if (answer == 1) { //Revert!
        ftf.setValue(ftf.getValue());
        return true;
    }
    return false;
}
}

What is wrong with this code?

CN1002
  • 1,115
  • 3
  • 20
  • 40

1 Answers1

1

Based on @trashgod's comment I was able to come up with an answer. I did not now I was missing the Table Cell Renderer, instead I was focusing on the Editor. I learnt that these two things work differently and are meant for different things. Answer based on Advice welcomed on creating my own Swing component.

My Custom Renderer for columns taking double values.

public class DoubleRenderer extends DefaultTableCellRenderer {

DecimalFormat df;

public DoubleRenderer(DecimalFormat df) {
    this.df = df;
    this.setHorizontalAlignment(JLabel.CENTER);
    this.setBackground(Color.lightGray);
    this.df.setParseBigDecimal(true);
}

@Override
protected void setValue(Object value) {
    setText((value == null) ? "" : df.format(value));
}
 }
CN1002
  • 1,115
  • 3
  • 20
  • 40