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 }