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