/* * Copyright (C) 2006 Kai Toedter * kai@toedter.com * www.toedter.com * * This program 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 * of the License, or (at your option) any later version. * * This program 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 program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package com.toedter.calendar; import java.awt.Color; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.Locale; import javax.swing.JComponent; import javax.swing.JFormattedTextField; import javax.swing.JFrame; import javax.swing.JTextField; import javax.swing.UIManager; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; import javax.swing.text.MaskFormatter; /** * JTextFieldDateEditor is the default editor used by JDateChooser. It is a * formatted text field, that colores valid dates green/black and invalid dates * red. The date format patten and mask can be set manually. If not set, the * MEDIUM pattern of a SimpleDateFormat with regards to the actual locale is * used. * * @author Kai Toedter * @version $LastChangedRevision: 97 $ * @version $LastChangedDate: 2006-05-24 17:30:41 +0200 (Mi, 24 Mai 2006) $ */ public class JTextFieldDateEditor extends JFormattedTextField implements IDateEditor, CaretListener, FocusListener, ActionListener { private static final long serialVersionUID = -8901842591101625304L; protected Date date; protected SimpleDateFormat dateFormatter; protected MaskFormatter maskFormatter; protected String datePattern; protected String maskPattern; protected char placeholder; protected Color darkGreen; protected DateUtil dateUtil; private boolean isMaskVisible; private boolean ignoreDatePatternChange; private int hours; private int minutes; private int seconds; private int millis; private Calendar calendar; public JTextFieldDateEditor() { this(false, null, null, ' '); } public JTextFieldDateEditor(String datePattern, String maskPattern, char placeholder) { this(true, datePattern, maskPattern, placeholder); } public JTextFieldDateEditor(boolean showMask, String datePattern, String maskPattern, char placeholder) { dateFormatter = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.MEDIUM); dateFormatter.setLenient(false); setDateFormatString(datePattern); if (datePattern != null) { ignoreDatePatternChange = true; } this.placeholder = placeholder; if (maskPattern == null) { this.maskPattern = createMaskFromDatePattern(this.datePattern); } else { this.maskPattern = maskPattern; } setToolTipText(this.datePattern); setMaskVisible(showMask); addCaretListener(this); addFocusListener(this); addActionListener(this); darkGreen = new Color(0, 150, 0); calendar = Calendar.getInstance(); dateUtil = new DateUtil(); } /* * (non-Javadoc) * * @see com.toedter.calendar.IDateEditor#getDate() */ public Date getDate() { try { calendar.setTime(dateFormatter.parse(getText())); calendar.set(Calendar.HOUR_OF_DAY, hours); calendar.set(Calendar.MINUTE, minutes); calendar.set(Calendar.SECOND, seconds); calendar.set(Calendar.MILLISECOND, millis); date = calendar.getTime(); } catch (ParseException e) { date = null; } return date; } /* * (non-Javadoc) * * @see com.toedter.calendar.IDateEditor#setDate(java.util.Date) */ public void setDate(Date date) { setDate(date, true); } /** * Sets the date. * * @param date * the date * @param firePropertyChange * true, if the date property should be fired. */ protected void setDate(Date date, boolean firePropertyChange) { Date oldDate = this.date; this.date = date; if (date == null) { setText(""); } else { calendar.setTime(date); hours = calendar.get(Calendar.HOUR_OF_DAY); minutes = calendar.get(Calendar.MINUTE); seconds = calendar.get(Calendar.SECOND); millis = calendar.get(Calendar.MILLISECOND); String formattedDate = dateFormatter.format(date); try { setText(formattedDate); } catch (RuntimeException e) { e.printStackTrace(); } } if (date != null && dateUtil.checkDate(date)) { setForeground(Color.BLACK); } if (firePropertyChange) { firePropertyChange("date", oldDate, date); } } /* * (non-Javadoc) * * @see com.toedter.calendar.IDateEditor#setDateFormatString(java.lang.String) */ public void setDateFormatString(String dateFormatString) { if (ignoreDatePatternChange) { return; } try { dateFormatter.applyPattern(dateFormatString); } catch (RuntimeException e) { dateFormatter = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.MEDIUM); dateFormatter.setLenient(false); } this.datePattern = dateFormatter.toPattern(); setToolTipText(this.datePattern); setDate(date, false); } /* * (non-Javadoc) * * @see com.toedter.calendar.IDateEditor#getDateFormatString() */ public String getDateFormatString() { return datePattern; } /* * (non-Javadoc) * * @see com.toedter.calendar.IDateEditor#getUiComponent() */ public JComponent getUiComponent() { return this; } /** * After any user input, the value of the textfield is proofed. Depending on * being a valid date, the value is colored green or red. * * @param event * the caret event */ public void caretUpdate(CaretEvent event) { String text = getText().trim(); String emptyMask = maskPattern.replace('#', placeholder); if (text.length() == 0 || text.equals(emptyMask)) { setForeground(Color.BLACK); return; } try { Date date = dateFormatter.parse(getText()); if (dateUtil.checkDate(date)) { setForeground(darkGreen); } else { setForeground(Color.RED); } } catch (Exception e) { setForeground(Color.RED); } } /* * (non-Javadoc) * * @see java.awt.event.FocusListener#focusLost(java.awt.event.FocusEvent) */ public void focusLost(FocusEvent focusEvent) { checkText(); } private void checkText() { try { Date date = dateFormatter.parse(getText()); setDate(date, true); } catch (Exception e) { // ignore } } /* * (non-Javadoc) * * @see java.awt.event.FocusListener#focusGained(java.awt.event.FocusEvent) */ public void focusGained(FocusEvent e) { } /* * (non-Javadoc) * * @see java.awt.Component#setLocale(java.util.Locale) */ public void setLocale(Locale locale) { if (locale == getLocale() || ignoreDatePatternChange) { return; } super.setLocale(locale); dateFormatter = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.MEDIUM, locale); setToolTipText(dateFormatter.toPattern()); setDate(date, false); doLayout(); } /** * Creates a mask from a date pattern. This is a very simple (and * incomplete) implementation thet works only with numbers. A date pattern * of "MM/dd/yy" will result in the mask "##/##/##". Probably you want to * override this method if it does not fit your needs. * * @param datePattern * the date pattern * @return the mask */ public String createMaskFromDatePattern(String datePattern) { String symbols = "GyMdkHmsSEDFwWahKzZ"; String mask = ""; for (int i = 0; i < datePattern.length(); i++) { char ch = datePattern.charAt(i); boolean symbolFound = false; for (int n = 0; n < symbols.length(); n++) { if (symbols.charAt(n) == ch) { mask += "#"; symbolFound = true; break; } } if (!symbolFound) { mask += ch; } } return mask; } /** * Returns true, if the mask is visible. * * @return true, if the mask is visible */ public boolean isMaskVisible() { return isMaskVisible; } /** * Sets the mask visible. * * @param isMaskVisible * true, if the mask should be visible */ public void setMaskVisible(boolean isMaskVisible) { this.isMaskVisible = isMaskVisible; if (isMaskVisible) { if (maskFormatter == null) { try { maskFormatter = new MaskFormatter(createMaskFromDatePattern(datePattern)); maskFormatter.setPlaceholderCharacter(this.placeholder); maskFormatter.install(this); } catch (ParseException e) { e.printStackTrace(); } } } } /** * Returns the preferred size. If a date pattern is set, it is the size the * date pattern would take. */ public Dimension getPreferredSize() { if (datePattern != null) { return new JTextField(datePattern).getPreferredSize(); } return super.getPreferredSize(); } /** * Validates the typed date and sets it (only if it is valid). */ public void actionPerformed(ActionEvent e) { checkText(); } /** * Enables and disabled the compoment. It also fixes the background bug * 4991597 and sets the background explicitely to a * TextField.inactiveBackground. */ public void setEnabled(boolean b) { super.setEnabled(b); if (!b) { super.setBackground(UIManager.getColor("TextField.inactiveBackground")); } } /* * (non-Javadoc) * * @see com.toedter.calendar.IDateEditor#getMaxSelectableDate() */ public Date getMaxSelectableDate() { return dateUtil.getMaxSelectableDate(); } /* * (non-Javadoc) * * @see com.toedter.calendar.IDateEditor#getMinSelectableDate() */ public Date getMinSelectableDate() { return dateUtil.getMinSelectableDate(); } /* * (non-Javadoc) * * @see com.toedter.calendar.IDateEditor#setMaxSelectableDate(java.util.Date) */ public void setMaxSelectableDate(Date max) { dateUtil.setMaxSelectableDate(max); checkText(); } /* * (non-Javadoc) * * @see com.toedter.calendar.IDateEditor#setMinSelectableDate(java.util.Date) */ public void setMinSelectableDate(Date min) { dateUtil.setMinSelectableDate(min); checkText(); } /* * (non-Javadoc) * * @see com.toedter.calendar.IDateEditor#setSelectableDateRange(java.util.Date, * java.util.Date) */ public void setSelectableDateRange(Date min, Date max) { dateUtil.setSelectableDateRange(min, max); checkText(); } /** * Creates a JFrame with a JCalendar inside and can be used for testing. * * @param s * The command line arguments */ public static void main(String[] s) { JFrame frame = new JFrame("JTextFieldDateEditor"); JTextFieldDateEditor jTextFieldDateEditor = new JTextFieldDateEditor(); jTextFieldDateEditor.setDate(new Date()); frame.getContentPane().add(jTextFieldDateEditor); frame.pack(); frame.setVisible(true); } }