001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2016 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 182 protected void setDate(Date date, boolean firePropertyChange) { 183 Date oldDate = this.date; 184 this.date = date; 185 186 if (date == null) { 187 setText(""); 188 } else { 189 calendar.setTime(date); 190 hours = calendar.get(Calendar.HOUR_OF_DAY); 191 minutes = calendar.get(Calendar.MINUTE); 192 seconds = calendar.get(Calendar.SECOND); 193 millis = calendar.get(Calendar.MILLISECOND); 194 195 String formattedDate = dateFormatter.format(date); 196 try { 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 218 public void setDateFormatString(String dateFormatString) { 219 if (ignoreDatePatternChange) { 220 return; 221 } 222 223 try { 224 dateFormatter.applyPattern(dateFormatString); 225 } catch (RuntimeException e) { 226 dateFormatter = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.MEDIUM); 227 dateFormatter.setLenient(false); 228 } 229 this.datePattern = dateFormatter.toPattern(); 230 setToolTipText(this.datePattern); 231 setDate(date, false); 232 } 233 234 /* 235 * (non-Javadoc) 236 * 237 * @see com.toedter.calendar.IDateEditor#getDateFormatString() 238 */ 239 240 public String getDateFormatString() { 241 return datePattern; 242 } 243 244 /* 245 * (non-Javadoc) 246 * 247 * @see com.toedter.calendar.IDateEditor#getUiComponent() 248 */ 249 250 public JComponent getUiComponent() { 251 return this; 252 } 253 254 /** 255 * After any user input, the value of the textfield is proofed. Depending on 256 * being a valid date, the value is colored green or red. 257 * 258 * @param event 259 * the caret event 260 */ 261 262 public void caretUpdate(CaretEvent event) { 263 String text = getText().trim(); 264 String emptyMask = maskPattern.replace('#', placeholder); 265 266 if (text.length() == 0 || text.equals(emptyMask)) { 267 setForeground(Color.BLACK); 268 return; 269 } 270 271 try { 272 Date date = dateFormatter.parse(getText()); 273 if (dateUtil.checkDate(date)) { 274 setForeground(darkGreen); 275 } else { 276 setForeground(Color.RED); 277 } 278 } catch (Exception e) { 279 setForeground(Color.RED); 280 } 281 } 282 283 /* 284 * (non-Javadoc) 285 * 286 * @see java.awt.event.FocusListener#focusLost(java.awt.event.FocusEvent) 287 */ 288 289 public void focusLost(FocusEvent focusEvent) { 290 checkText(); 291 } 292 293 private void checkText() { 294 try { 295 Date date = dateFormatter.parse(getText()); 296 setDate(date, true); 297 } catch (Exception e) { 298 // ignore 299 } 300 } 301 302 /* 303 * (non-Javadoc) 304 * 305 * @see java.awt.event.FocusListener#focusGained(java.awt.event.FocusEvent) 306 */ 307 308 public void focusGained(FocusEvent e) { 309 } 310 311 /* 312 * (non-Javadoc) 313 * 314 * @see java.awt.Component#setLocale(java.util.Locale) 315 */ 316 317 public void setLocale(Locale locale) { 318 if (locale == getLocale() || ignoreDatePatternChange) { 319 return; 320 } 321 322 super.setLocale(locale); 323 dateFormatter = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.MEDIUM, locale); 324 setToolTipText(dateFormatter.toPattern()); 325 326 setDate(date, false); 327 doLayout(); 328 } 329 330 /** 331 * Creates a mask from a date pattern. This is a very simple (and 332 * incomplete) implementation that works only with numbers. A date pattern 333 * of "MM/dd/yy" will result in the mask "##/##/##". Probably you want to 334 * override this method if it does not fit your needs. 335 * 336 * @param datePattern 337 * the date pattern 338 * @return the mask 339 */ 340 341 public String createMaskFromDatePattern(String datePattern) { 342 String symbols = "GyMdkHmsSEDFwWahKzZ"; 343 String mask = ""; 344 for (int i = 0; i < datePattern.length(); i++) { 345 char ch = datePattern.charAt(i); 346 boolean symbolFound = false; 347 for (int n = 0; n < symbols.length(); n++) { 348 if (symbols.charAt(n) == ch) { 349 mask += "#"; 350 symbolFound = true; 351 break; 352 } 353 } 354 if (!symbolFound) { 355 mask += ch; 356 } 357 } 358 return mask; 359 } 360 361 /** 362 * Returns true, if the mask is visible. 363 * 364 * @return true, if the mask is visible 365 */ 366 367 public boolean isMaskVisible() { 368 return isMaskVisible; 369 } 370 371 /** 372 * Sets the mask visible. 373 * 374 * @param isMaskVisible 375 * true, if the mask should be visible 376 */ 377 378 public void setMaskVisible(boolean isMaskVisible) { 379 this.isMaskVisible = isMaskVisible; 380 if (isMaskVisible) { 381 if (maskFormatter == null) { 382 try { 383 maskFormatter = new MaskFormatter(createMaskFromDatePattern(datePattern)); 384 maskFormatter.setPlaceholderCharacter(this.placeholder); 385 maskFormatter.install(this); 386 } catch (ParseException e) { 387 e.printStackTrace(); 388 } 389 } 390 } 391 } 392 393 /** 394 * Returns the preferred size. If a date pattern is set, it is the size the 395 * date pattern would take. 396 */ 397 398 public Dimension getPreferredSize() { 399 400 Dimension ret = null; 401 if (datePattern != null) { 402 ret = (new JTextField(datePattern)).getPreferredSize(); 403 // return new JTextField(datePattern).getPreferredSize(); 404 } else { 405 ret = super.getPreferredSize(); 406 // return super.getPreferredSize(); 407 } 408 ret.setSize(90, 19); 409 return ret; 410 411 } 412 413 /** 414 * Validates the typed date and sets it (only if it is valid). 415 */ 416 public void actionPerformed(ActionEvent e) { 417 checkText(); 418 } 419 420 /** 421 * Enables and disabled the component. It also fixes the background bug 422 * 4991597 and sets the background explicitly to a 423 * TextField.inactiveBackground. 424 */ 425 426 public void setEnabled(boolean b) { 427 super.setEnabled(b); 428 if (!b) { 429 super.setBackground(UIManager.getColor("TextField.inactiveBackground")); 430 } 431 } 432 433 /* 434 * (non-Javadoc) 435 * 436 * @see com.toedter.calendar.IDateEditor#getMaxSelectableDate() 437 */ 438 public Date getMaxSelectableDate() { 439 return dateUtil.getMaxSelectableDate(); 440 } 441 442 /* 443 * (non-Javadoc) 444 * 445 * @see com.toedter.calendar.IDateEditor#getMinSelectableDate() 446 */ 447 public Date getMinSelectableDate() { 448 return dateUtil.getMinSelectableDate(); 449 } 450 451 /* 452 * (non-Javadoc) 453 * 454 * @see com.toedter.calendar.IDateEditor#setMaxSelectableDate(java.util.Date) 455 */ 456 public void setMaxSelectableDate(Date max) { 457 dateUtil.setMaxSelectableDate(max); 458 checkText(); 459 } 460 461 /* 462 * (non-Javadoc) 463 * 464 * @see com.toedter.calendar.IDateEditor#setMinSelectableDate(java.util.Date) 465 */ 466 public void setMinSelectableDate(Date min) { 467 dateUtil.setMinSelectableDate(min); 468 checkText(); 469 } 470 471 /* 472 * (non-Javadoc) 473 * 474 * @see com.toedter.calendar.IDateEditor#setSelectableDateRange(java.util.Date, 475 * java.util.Date) 476 */ 477 public void setSelectableDateRange(Date min, Date max) { 478 dateUtil.setSelectableDateRange(min, max); 479 checkText(); 480 } 481 482 /** 483 * Creates a JFrame with a JCalendar inside and can be used for testing. 484 * 485 * @param s 486 * The command line arguments 487 */ 488 public static void main(String[] s) { 489 JFrame frame = new JFrame("JTextFieldDateEditor"); 490 JTextFieldDateEditor jTextFieldDateEditor = new JTextFieldDateEditor(); 491 jTextFieldDateEditor.setDate(new Date()); 492 frame.getContentPane().add(jTextFieldDateEditor); 493 frame.pack(); 494 frame.setVisible(true); 495 } 496}