/* * $Id$ * * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, * Santa Clara, California 95054, U.S.A. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package org.jdesktop.swingx.table; import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.text.DateFormat; import java.text.ParseException; import java.util.Date; import java.util.EventObject; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.AbstractCellEditor; import javax.swing.BorderFactory; import javax.swing.JTable; import javax.swing.JTree; import javax.swing.UIManager; import javax.swing.table.TableCellEditor; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreeCellEditor; import org.jdesktop.swingx.JXDatePicker; import org.jdesktop.swingx.treetable.AbstractMutableTreeTableNode; /** * A CellEditor using a JXDatePicker as editor component.<p> * * NOTE: this class will be moved! * * @author Richard Osbald * @author Jeanette Winzenburg */ public class DatePickerCellEditor extends AbstractCellEditor implements TableCellEditor, TreeCellEditor { protected JXDatePicker datePicker; protected DateFormat dateFormat; protected int clickCountToStart = 2; private ActionListener pickerActionListener; protected boolean ignoreAction; private static Logger logger = Logger.getLogger(DatePickerCellEditor.class .getName()); private static final long serialVersionUID = -1L; /** * Instantiates a editor with the default dateFormat. * * PENDING: always override default from DatePicker? * */ public DatePickerCellEditor() { this(null); } /** * Instantiates an editor with the given dateFormat. If * null, the datePickers default is used. * * @param dateFormat */ public DatePickerCellEditor(DateFormat dateFormat) { // JW: the copy is used to synchronize .. can // we use something else? this.dateFormat = dateFormat != null ? dateFormat : DateFormat.getDateInstance(); datePicker = new JXDatePicker(); // default border crushes the editor/combo datePicker.getEditor().setBorder( BorderFactory.createEmptyBorder(0, 1, 0, 1)); // should be fixed by j2se 6.0 datePicker.setFont(UIManager.getDefaults().getFont("TextField.font")); if (dateFormat != null) { datePicker.setFormats(dateFormat); } datePicker.addActionListener(getPickerActionListener()); } //-------------------- CellEditor /** * Returns the pickers date. * * Note: the date is only meaningful after a stopEditing and * before the next call to getTableCellEditorComponent. */ @Override public Date getCellEditorValue() { return datePicker.getDate(); } @Override public boolean isCellEditable(EventObject anEvent) { if (anEvent instanceof MouseEvent) { return ((MouseEvent) anEvent).getClickCount() >= getClickCountToStart(); } return super.isCellEditable(anEvent); } /** * {@inheritDoc} * <p> * * Overridden to commit pending edits. If commit successful, returns super, * else returns false. * * */ @Override public boolean stopCellEditing() { ignoreAction = true; boolean canCommit = commitChange(); ignoreAction = false; if (canCommit) { return super.stopCellEditing(); } return false; } /** * Specifies the number of clicks needed to start editing. * * @param count an int specifying the number of clicks needed to start * editing * @see #getClickCountToStart */ public void setClickCountToStart(int count) { clickCountToStart = count; } /** * Returns the number of clicks needed to start editing. * * @return the number of clicks needed to start editing */ public int getClickCountToStart() { return clickCountToStart; } //------------------------ TableCellEditor @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { // PENDING JW: can remove the ignore flags here? // the picker learnde to behave ... ignoreAction = true; datePicker.setDate(getValueAsDate(value)); // todo how to.. // SwingUtilities.invokeLater(new Runnable() { // public void run() { // datePicker.getEditor().selectAll(); // } // }); ignoreAction = false; return datePicker; } //------------------------- TreeCellEditor @Override public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) { // PENDING JW: can remove the ignore flags here? // the picker learnde to behave ... ignoreAction = true; datePicker.setDate(getValueAsDate(value)); // todo how to.. // SwingUtilities.invokeLater(new Runnable() { // public void run() { // datePicker.getEditor().selectAll(); // } // }); ignoreAction = false; return datePicker; } //-------------------- helpers /** * Returns the given value as Date. * * PENDING: abstract into something pluggable (like StringValue * in ComponentProvider?) * * @param value the value to map as Date * @return the value as Date or null, if not successful. * */ protected Date getValueAsDate(Object value) { if (isEmpty(value)) return null; if (value instanceof Date) { return (Date) value; } if (value instanceof Long) { return new Date((Long) value); } if (value instanceof String) { try { // JW: why was the parsing synchronized? // synchronized (dateFormat) { // datePicker.setDate(dateFormat.parse((String) value)); // } return dateFormat.parse((String) value); } catch (ParseException e) { handleParseException(e); } } if (value instanceof DefaultMutableTreeNode) { return getValueAsDate(((DefaultMutableTreeNode) value).getUserObject()); } if (value instanceof AbstractMutableTreeTableNode) { return getValueAsDate(((AbstractMutableTreeTableNode) value).getUserObject()); } return null; } /** * @param e */ protected void handleParseException(ParseException e) { logger.log(Level.SEVERE, e.getMessage(), e.getMessage()); } protected boolean isEmpty(Object value) { return value == null || value instanceof String && ((String) value).length() == 0; } //--------------- picker specifics /** * Commits any pending edits and returns a boolean indicating whether the * commit was successful. * * @return true if the edit was valid, false otherwise. */ protected boolean commitChange() { try { datePicker.commitEdit(); return true; } catch (ParseException e) { } return false; } /** * * @return the DatePicker's formats. * * @see org.jdesktop.swingx.JXDatePicker#getFormats(). */ public DateFormat[] getFormats() { return datePicker.getFormats(); } /** * * @param formats the formats to use in the datepicker. * * @see org.jdesktop.swingx.JXDatePicker#setFormats(DateFormat...) * */ public void setFormats(DateFormat... formats) { datePicker.setFormats(formats); } /** * Returns the ActionListener to add to the datePicker. * * @return the action listener to listen for datePicker's * action events. */ protected ActionListener getPickerActionListener() { if (pickerActionListener == null) { pickerActionListener = createPickerActionListener(); } return pickerActionListener; } /** * Creates and returns the ActionListener for the Picker. * @return the ActionListener to listen for Picker's action events. */ protected ActionListener createPickerActionListener() { ActionListener l = new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { // avoid duplicate trigger from // commit in stopCellEditing if (ignoreAction) return; // still need to invoke .. hmm // no ... with the table cooperating the // invoke is contra-productive! terminateEdit(e); } /** * @param e */ private void terminateEdit(final ActionEvent e) { if ((e != null) && (JXDatePicker.COMMIT_KEY.equals(e.getActionCommand()))) { stopCellEditing(); } else { cancelCellEditing(); } } }; return l; } }