/******************************************************************************* * Copyright (c) 2010 Haifeng Li * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ package smile.swing.table; import javax.swing.AbstractAction; import javax.swing.DefaultCellEditor; import javax.swing.JFormattedTextField; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.KeyStroke; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.Component; import java.awt.Toolkit; import java.text.ParseException; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.text.DefaultFormatter; import javax.swing.text.DefaultFormatterFactory; /** * Implements a cell editor that uses a formatted text field * to edit double[] values. */ @SuppressWarnings("serial") public class DoubleArrayCellEditor extends DefaultCellEditor { private final static Logger LOGGER = Logger.getLogger(DoubleArrayCellEditor.class.getName()); JFormattedTextField textField; /** * Constructor. */ public DoubleArrayCellEditor() { super(new JFormattedTextField()); textField = (JFormattedTextField) getComponent(); DefaultFormatter formatter = new DefaultFormatter() { @Override public Object stringToValue(String string) throws ParseException { string = string.trim(); if (string.isEmpty()) { throw new ParseException("Empty string", 0); } int begin = 0; char ch = string.charAt(0); if (ch == '[' || ch == '{' || ch == '<') { begin = 1; } int end = string.length(); ch = string.charAt(end - 1); if (ch == ']' || ch == '}' || ch == '>') { end -= 1; } string = string.substring(begin, end); String[] items = string.split("\\s*[ ,;:]\\s*"); double[] data = new double[items.length]; for (int i = 0; i < data.length; i++) { data[i] = Double.valueOf(items[i].trim()); } return data; } @Override public String valueToString(Object value) throws ParseException { if (value == null) { return ""; } StringBuilder builder = new StringBuilder(); if (value instanceof float[]) { float[] data = (float[]) value; if (data.length > 0) { builder.append("[").append(data[0]); } for (int i = 1; i < data.length; i++) { builder.append(", ").append(data[i]); } builder.append("]"); } else if (value instanceof double[]) { double[] data = (double[]) value; if (data.length > 0) { builder.append("[").append(data[0]); } for (int i = 1; i < data.length; i++) { builder.append(", ").append(data[i]); } builder.append("]"); } else { throw new ParseException("Unsupport data type: " + value.getClass(), 0); } return builder.toString(); } }; formatter.setOverwriteMode(false); textField.setFormatterFactory(new DefaultFormatterFactory(formatter)); textField.setHorizontalAlignment(JTextField.TRAILING); textField.setFocusLostBehavior(JFormattedTextField.PERSIST); // React when the user presses Enter while the editor is // active. (Tab is handled as specified by // JFormattedTextField's focusLostBehavior property.) textField.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "check"); textField.getActionMap().put("check", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { if (!textField.isEditValid()) { //The text is invalid. Toolkit.getDefaultToolkit().beep(); textField.selectAll(); } else { try { //The text is valid, textField.commitEdit(); //so use it. textField.postActionEvent(); //stop editing } catch (java.text.ParseException ex) { } } } }); } @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { textField.setValue(value); return textField; } @Override public Object getCellEditorValue() { JFormattedTextField ftf = (JFormattedTextField) getComponent(); Object o = ftf.getValue(); if (o instanceof double[]) { return o; } else { LOGGER.log(Level.FINE, "getCellEditorValue: can't parse o{0}", 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 ex) { } } else { //text is invalid Toolkit.getDefaultToolkit().beep(); textField.selectAll(); return false; //don't let the editor go away } return super.stopCellEditing(); } }