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