001/* 002 * $Id: JTextFieldDateEditor.java,v 1.2 2011/03/24 16:06:33 davep Exp $ 003 * 004 * This file is part of McIDAS-V 005 * 006 * Copyright 2007-2011 007 * Space Science and Engineering Center (SSEC) 008 * University of Wisconsin - Madison 009 * 1225 W. Dayton Street, Madison, WI 53706, USA 010 * https://www.ssec.wisc.edu/mcidas 011 * 012 * All Rights Reserved 013 * 014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 015 * some McIDAS-V source code is based on IDV and VisAD source code. 016 * 017 * McIDAS-V is free software; you can redistribute it and/or modify 018 * it under the terms of the GNU Lesser Public License as published by 019 * the Free Software Foundation; either version 3 of the License, or 020 * (at your option) any later version. 021 * 022 * McIDAS-V is distributed in the hope that it will be useful, 023 * but WITHOUT ANY WARRANTY; without even the implied warranty of 024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 025 * GNU Lesser Public License for more details. 026 * 027 * You should have received a copy of the GNU Lesser Public License 028 * along with this program. If not, see http://www.gnu.org/licenses. 029 */ 030 031package edu.wisc.ssec.mcidasv.data.dateChooser; 032 033import java.awt.Color; 034import java.awt.Dimension; 035import java.awt.event.ActionEvent; 036import java.awt.event.ActionListener; 037import java.awt.event.FocusEvent; 038import java.awt.event.FocusListener; 039import java.text.DateFormat; 040import java.text.ParseException; 041import java.text.SimpleDateFormat; 042import java.util.Calendar; 043import java.util.Date; 044import java.util.Locale; 045 046import javax.swing.JComponent; 047import javax.swing.JFormattedTextField; 048import javax.swing.JFrame; 049import javax.swing.JTextField; 050import javax.swing.UIManager; 051import javax.swing.event.CaretEvent; 052import javax.swing.event.CaretListener; 053import javax.swing.text.MaskFormatter; 054 055/** 056 * JTextFieldDateEditor is the default editor used by JDateChooser. It is a 057 * formatted text field, that colores valid dates green/black and invalid dates 058 * red. The date format patten and mask can be set manually. If not set, the 059 * MEDIUM pattern of a SimpleDateFormat with regards to the actual locale is 060 * used. 061 * 062 * @author Kai Toedter 063 * @version $LastChangedRevision: 97 $ 064 * @version $LastChangedDate: 2006-05-24 17:30:41 +0200 (Mi, 24 Mai 2006) $ 065 */ 066public class JTextFieldDateEditor extends JFormattedTextField implements IDateEditor, 067 CaretListener, FocusListener, ActionListener { 068 069 private static final long serialVersionUID = -8901842591101625304L; 070 071 protected Date date; 072 073 protected SimpleDateFormat dateFormatter; 074 075 protected MaskFormatter maskFormatter; 076 077 protected String datePattern; 078 079 protected String maskPattern; 080 081 protected char placeholder; 082 083 protected Color darkGreen; 084 085 protected DateUtil dateUtil; 086 087 private boolean isMaskVisible; 088 089 private boolean ignoreDatePatternChange; 090 091 private int hours; 092 093 private int minutes; 094 095 private int seconds; 096 097 private int millis; 098 099 private Calendar calendar; 100 101 public JTextFieldDateEditor() { 102 this(false, null, null, ' '); 103 } 104 105 public JTextFieldDateEditor(String datePattern, String maskPattern, char placeholder) { 106 this(true, datePattern, maskPattern, placeholder); 107 } 108 109 public JTextFieldDateEditor(boolean showMask, String datePattern, String maskPattern, 110 char placeholder) { 111 dateFormatter = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.MEDIUM); 112 dateFormatter.setLenient(false); 113 114 setDateFormatString(datePattern); 115 if (datePattern != null) { 116 ignoreDatePatternChange = true; 117 } 118 119 this.placeholder = placeholder; 120 121 if (maskPattern == null) { 122 this.maskPattern = createMaskFromDatePattern(this.datePattern); 123 } else { 124 this.maskPattern = maskPattern; 125 } 126 127 setToolTipText(this.datePattern); 128 setMaskVisible(showMask); 129 130 addCaretListener(this); 131 addFocusListener(this); 132 addActionListener(this); 133 darkGreen = new Color(0, 150, 0); 134 135 calendar = Calendar.getInstance(); 136 137 dateUtil = new DateUtil(); 138 } 139 140 /* 141 * (non-Javadoc) 142 * 143 * @see com.toedter.calendar.IDateEditor#getDate() 144 */ 145 public Date getDate() { 146 try { 147 calendar.setTime(dateFormatter.parse(getText())); 148 calendar.set(Calendar.HOUR_OF_DAY, hours); 149 calendar.set(Calendar.MINUTE, minutes); 150 calendar.set(Calendar.SECOND, seconds); 151 calendar.set(Calendar.MILLISECOND, millis); 152 date = calendar.getTime(); 153 } catch (ParseException e) { 154 date = null; 155 } 156 return date; 157 } 158 159 /* 160 * (non-Javadoc) 161 * 162 * @see com.toedter.calendar.IDateEditor#setDate(java.util.Date) 163 */ 164 public void setDate(Date date) { 165 setDate(date, true); 166 } 167 168 /** 169 * Sets the date. 170 * 171 * @param date 172 * the date 173 * @param firePropertyChange 174 * true, if the date property should be fired. 175 */ 176 protected void setDate(Date date, boolean firePropertyChange) { 177 Date oldDate = this.date; 178 this.date = date; 179 180 if (date == null) { 181 setText(""); 182 } else { 183 calendar.setTime(date); 184 hours = calendar.get(Calendar.HOUR_OF_DAY); 185 minutes = calendar.get(Calendar.MINUTE); 186 seconds = calendar.get(Calendar.SECOND); 187 millis = calendar.get(Calendar.MILLISECOND); 188 189 String formattedDate = dateFormatter.format(date); 190 try { 191 setText(formattedDate); 192 } catch (RuntimeException e) { 193 e.printStackTrace(); 194 } 195 } 196 if (date != null && dateUtil.checkDate(date)) { 197 setForeground(Color.BLACK); 198 199 } 200 201 if (firePropertyChange) { 202 firePropertyChange("date", oldDate, date); 203 } 204 } 205 206 /* 207 * (non-Javadoc) 208 * 209 * @see com.toedter.calendar.IDateEditor#setDateFormatString(java.lang.String) 210 */ 211 public void setDateFormatString(String dateFormatString) { 212 if (ignoreDatePatternChange) { 213 return; 214 } 215 216 try { 217 dateFormatter.applyPattern(dateFormatString); 218 } catch (RuntimeException e) { 219 dateFormatter = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.MEDIUM); 220 dateFormatter.setLenient(false); 221 } 222 this.datePattern = dateFormatter.toPattern(); 223 setToolTipText(this.datePattern); 224 setDate(date, false); 225 } 226 227 /* 228 * (non-Javadoc) 229 * 230 * @see com.toedter.calendar.IDateEditor#getDateFormatString() 231 */ 232 public String getDateFormatString() { 233 return datePattern; 234 } 235 236 /* 237 * (non-Javadoc) 238 * 239 * @see com.toedter.calendar.IDateEditor#getUiComponent() 240 */ 241 public JComponent getUiComponent() { 242 return this; 243 } 244 245 /** 246 * After any user input, the value of the textfield is proofed. Depending on 247 * being a valid date, the value is colored green or red. 248 * 249 * @param event 250 * the caret event 251 */ 252 public void caretUpdate(CaretEvent event) { 253 String text = getText().trim(); 254 String emptyMask = maskPattern.replace('#', placeholder); 255 256 if (text.length() == 0 || text.equals(emptyMask)) { 257 setForeground(Color.BLACK); 258 return; 259 } 260 261 try { 262 Date date = dateFormatter.parse(getText()); 263 if (dateUtil.checkDate(date)) { 264 setForeground(darkGreen); 265 } else { 266 setForeground(Color.RED); 267 } 268 } catch (Exception e) { 269 setForeground(Color.RED); 270 } 271 } 272 273 /* 274 * (non-Javadoc) 275 * 276 * @see java.awt.event.FocusListener#focusLost(java.awt.event.FocusEvent) 277 */ 278 public void focusLost(FocusEvent focusEvent) { 279 checkText(); 280 } 281 282 private void checkText() { 283 try { 284 Date date = dateFormatter.parse(getText()); 285 setDate(date, true); 286 } catch (Exception e) { 287 // ignore 288 } 289 } 290 291 /* 292 * (non-Javadoc) 293 * 294 * @see java.awt.event.FocusListener#focusGained(java.awt.event.FocusEvent) 295 */ 296 public void focusGained(FocusEvent e) { 297 } 298 299 /* 300 * (non-Javadoc) 301 * 302 * @see java.awt.Component#setLocale(java.util.Locale) 303 */ 304 public void setLocale(Locale locale) { 305 if (locale == getLocale() || ignoreDatePatternChange) { 306 return; 307 } 308 309 super.setLocale(locale); 310 dateFormatter = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.MEDIUM, locale); 311 setToolTipText(dateFormatter.toPattern()); 312 313 setDate(date, false); 314 doLayout(); 315 } 316 317 /** 318 * Creates a mask from a date pattern. This is a very simple (and 319 * incomplete) implementation thet works only with numbers. A date pattern 320 * of "MM/dd/yy" will result in the mask "##/##/##". Probably you want to 321 * override this method if it does not fit your needs. 322 * 323 * @param datePattern 324 * the date pattern 325 * @return the mask 326 */ 327 public String createMaskFromDatePattern(String datePattern) { 328 String symbols = "GyMdkHmsSEDFwWahKzZ"; 329 String mask = ""; 330 for (int i = 0; i < datePattern.length(); i++) { 331 char ch = datePattern.charAt(i); 332 boolean symbolFound = false; 333 for (int n = 0; n < symbols.length(); n++) { 334 if (symbols.charAt(n) == ch) { 335 mask += "#"; 336 symbolFound = true; 337 break; 338 } 339 } 340 if (!symbolFound) { 341 mask += ch; 342 } 343 } 344 return mask; 345 } 346 347 /** 348 * Returns true, if the mask is visible. 349 * 350 * @return true, if the mask is visible 351 */ 352 public boolean isMaskVisible() { 353 return isMaskVisible; 354 } 355 356 /** 357 * Sets the mask visible. 358 * 359 * @param isMaskVisible 360 * true, if the mask should be visible 361 */ 362 public void setMaskVisible(boolean isMaskVisible) { 363 this.isMaskVisible = isMaskVisible; 364 if (isMaskVisible) { 365 if (maskFormatter == null) { 366 try { 367 maskFormatter = new MaskFormatter(createMaskFromDatePattern(datePattern)); 368 maskFormatter.setPlaceholderCharacter(this.placeholder); 369 maskFormatter.install(this); 370 } catch (ParseException e) { 371 e.printStackTrace(); 372 } 373 } 374 } 375 } 376 377 /** 378 * Returns the preferred size. If a date pattern is set, it is the size the 379 * date pattern would take. 380 */ 381 public Dimension getPreferredSize() { 382 383 Dimension ret = null; 384 if (datePattern != null) { 385 ret =(new JTextField(datePattern)).getPreferredSize(); 386 //return new JTextField(datePattern).getPreferredSize(); 387 } else { 388 ret = super.getPreferredSize(); 389 //return super.getPreferredSize(); 390 } 391 ret.setSize(90, 19); 392 return ret; 393 } 394 395 /** 396 * Validates the typed date and sets it (only if it is valid). 397 */ 398 public void actionPerformed(ActionEvent e) { 399 checkText(); 400 } 401 402 /** 403 * Enables and disabled the compoment. It also fixes the background bug 404 * 4991597 and sets the background explicitely to a 405 * TextField.inactiveBackground. 406 */ 407 public void setEnabled(boolean b) { 408 super.setEnabled(b); 409 if (!b) { 410 super.setBackground(UIManager.getColor("TextField.inactiveBackground")); 411 } 412 } 413 414 /* 415 * (non-Javadoc) 416 * 417 * @see com.toedter.calendar.IDateEditor#getMaxSelectableDate() 418 */ 419 public Date getMaxSelectableDate() { 420 return dateUtil.getMaxSelectableDate(); 421 } 422 423 /* 424 * (non-Javadoc) 425 * 426 * @see com.toedter.calendar.IDateEditor#getMinSelectableDate() 427 */ 428 public Date getMinSelectableDate() { 429 return dateUtil.getMinSelectableDate(); 430 } 431 432 /* 433 * (non-Javadoc) 434 * 435 * @see com.toedter.calendar.IDateEditor#setMaxSelectableDate(java.util.Date) 436 */ 437 public void setMaxSelectableDate(Date max) { 438 dateUtil.setMaxSelectableDate(max); 439 checkText(); 440 } 441 442 /* 443 * (non-Javadoc) 444 * 445 * @see com.toedter.calendar.IDateEditor#setMinSelectableDate(java.util.Date) 446 */ 447 public void setMinSelectableDate(Date min) { 448 dateUtil.setMinSelectableDate(min); 449 checkText(); 450 } 451 452 /* 453 * (non-Javadoc) 454 * 455 * @see com.toedter.calendar.IDateEditor#setSelectableDateRange(java.util.Date, 456 * java.util.Date) 457 */ 458 public void setSelectableDateRange(Date min, Date max) { 459 dateUtil.setSelectableDateRange(min, max); 460 checkText(); 461 } 462 463 /** 464 * Creates a JFrame with a JCalendar inside and can be used for testing. 465 * 466 * @param s 467 * The command line arguments 468 */ 469 public static void main(String[] s) { 470 JFrame frame = new JFrame("JTextFieldDateEditor"); 471 JTextFieldDateEditor jTextFieldDateEditor = new JTextFieldDateEditor(); 472 jTextFieldDateEditor.setDate(new Date()); 473 frame.getContentPane().add(jTextFieldDateEditor); 474 frame.pack(); 475 frame.setVisible(true); 476 } 477}