package com.michaelbaranov.microba.calendar; import java.text.DateFormat; import java.util.Date; import java.util.Locale; import java.util.Map; import java.util.TimeZone; import com.michaelbaranov.microba.calendar.ui.DatePickerUI; /** * A concrete implementation of JComponent. Capable of displaying and selecting * dates, much like an editable combo-box with a calendar dropdown. * <p> * This implementatin allows for specifying time component along with the date. * Make sure that: 1) keepTime property is true; 2) stripTime is false; 3) * dateFormat has time fields; * * @author Michael Baranov */ public class DatePicker extends CalendarPane { /** * The name of a "dateFormat" property. */ public static final String PROPERTY_NAME_DATE_FORMAT = "dateFormat"; /** * The name of a "fieldEditable" property. */ public static final String PROPERTY_NAME_FIELD_EDITABLE = "fieldEditable"; /** * The name of a "keepTime" property. */ public static final String PROPERTY_NAME_KEEP_TIME = "keepTime"; /** * The name of a "pickerStyle" property. */ public static final String PROPERTY_NAME_PICKER_STYLE = "pickerStyle"; /** * The name of a "popupFocusable" property. */ public static final String PROPERTY_NAME_DROPDOWN_FOCUSABLE = "dropdownFocusable"; /** * A constant for the "pickerStyle" property. */ public static final int PICKER_STYLE_FIELD_AND_BUTTON = 0x110; /** * A constant for the "pickerStyle" property. */ public static final int PICKER_STYLE_BUTTON = 0x120; private static final String uiClassID = "microba.DatePickerUI"; private DateFormat dateFormat; private boolean fieldEditable; private boolean keepTime; private int pickerStyle; private boolean dropdownFocusable; /** * Constructor. */ public DatePicker() { this(new Date(), DateFormat.MEDIUM, Locale.getDefault(), TimeZone .getDefault()); } /** * Constructor. */ public DatePicker(Date initialDate) { this(initialDate, DateFormat.MEDIUM, Locale.getDefault(), TimeZone .getDefault()); } /** * Constructor. */ public DatePicker(Date initialDate, int dateStyle) { this(initialDate, dateStyle, Locale.getDefault(), TimeZone.getDefault()); } /** * Constructor. */ public DatePicker(Date initialDate, DateFormat dateFormat) { this(initialDate, dateFormat, Locale.getDefault(), TimeZone .getDefault()); } /** * Constructor. */ public DatePicker(Date initialDate, int dateStyle, Locale locale) { this(initialDate, dateStyle, locale, TimeZone.getDefault()); } /** * Constructor. */ public DatePicker(Date initialDate, DateFormat dateFormat, Locale locale) { this(initialDate, dateFormat, locale, TimeZone.getDefault()); } /** * Constructor. */ public DatePicker(Date initialDate, int dateStyle, Locale locale, TimeZone zone) { super(initialDate, CalendarPane.STYLE_CLASSIC, locale, zone); checkDateStyle(dateStyle); this.dateFormat = dateFormatFromStyle(dateStyle); this.fieldEditable = true; this.keepTime = true; this.pickerStyle = PICKER_STYLE_FIELD_AND_BUTTON; this.setStripTime(false); this.dropdownFocusable = true; updateUI(); } /** * Constructor. */ public DatePicker(Date initialDate, DateFormat dateFormat, Locale locale, TimeZone zone) { super(initialDate, CalendarPane.STYLE_CLASSIC, locale, zone); checkDateFormat(dateFormat); this.dateFormat = dateFormat; this.fieldEditable = true; this.keepTime = true; this.pickerStyle = PICKER_STYLE_FIELD_AND_BUTTON; this.setStripTime(false); this.dropdownFocusable = true; updateUI(); } public String getUIClassID() { return uiClassID; } /** * Returns the date format. * <p> * * @return current date format * @see #setDateFormat(DateFormat) */ public DateFormat getDateFormat() { return dateFormat; } /** * Sets the date format constant defined by {@link DateFormat} and updates * the control to reflect new date style. * <p> * * @param dateFormat * the date format constant to set * @see #getDateFormat() * @see DateFormat */ public void setDateFormat(DateFormat dateFormat) { checkDateFormat(dateFormat); Object oldValue = this.dateFormat; this.dateFormat = dateFormat; firePropertyChange(PROPERTY_NAME_DATE_FORMAT, oldValue, dateFormat); } /** * Is the edit field of the control editable by the user? * <p> * If not editable, the user can not type in the date and can only use * calendar drop-down to select dates. * * @return <code>true</code> if the edit field is editable, * <code>false</code> otherwise * * @see #setFieldEditable(boolean) */ public boolean isFieldEditable() { return fieldEditable; } /** * Enables or disables editing of the edit field by the user. * <p> * If not editable, the user can not type in the date and can only use * calendar drop-down to select dates. * * @param fieldEditable * the editable value to set * * @see #isFieldEditable() */ public void setFieldEditable(boolean fieldEditable) { boolean old = this.fieldEditable; this.fieldEditable = fieldEditable; firePropertyChange(PROPERTY_NAME_FIELD_EDITABLE, old, fieldEditable); } /** * Is the dropdown focusable? * <p> * If not focusable, the dropdown calendar will lack some keyboard input * capabilities. * * @return <code>true</code> if the dropdown is focusable, * <code>false</code> otherwise * * @see #setDropdownFocusable(boolean) */ public boolean isDropdownFocusable() { return dropdownFocusable; } /** * Enables or disables focusability of the dropdown calendar. * <p> * If not focusable, the dropdown calendar will lack some keyboard input * capabilities. * * @param popupFocusable * the focusable value to set * * @see #isDropdownFocusable() */ public void setDropdownFocusable(boolean popupFocusable) { boolean old = this.dropdownFocusable; this.dropdownFocusable = popupFocusable; firePropertyChange(PROPERTY_NAME_DROPDOWN_FOCUSABLE, old, popupFocusable); } /** * Does UI try to preserve time components entered in the edit field? * <p> * If <code>true</code> and if the date format has some time fields * (hours, minutes, seconds, fraction of second), the UI tries to respect * the time fields' values entered by user as much as possible. * <p> * Note: to be able to receive time portion of the date, make sure * {@link #isStripTime()} is <code>false</code> (the dafualt). * * @return <code>true</code> if the UI respects time fields, * <code>false</code> otherwise * @see #setKeepTime(boolean) * @see #setStripTime(boolean) * @see #isStripTime() * */ public boolean isKeepTime() { return keepTime; } /** * Determines if the UI should try to preserve time components entered in * the edit field. * <p> * If <code>true</code> and if the date format has some time fields * (hours, minutes, seconds, fraction of second), the UI tries to respect * the time fields' values entered by user as much as possible. * <p> * Note: to be able to receive time portion of the date, make sure * {@link #isStripTime()} is <code>false</code> (the dafualt). * * @param keepTime * <code>true</code> to make the UI respects time fields, * <code>false</code> otherwise * @see #isKeepTime() * @see #setStripTime(boolean) * @see #isStripTime() */ public void setKeepTime(boolean keepTime) { boolean old = this.keepTime; this.keepTime = keepTime; firePropertyChange(PROPERTY_NAME_KEEP_TIME, old, keepTime); } /** * Returns current visual style of the picker control. * <p> * NOTE: do not confuse with {@link #getStyle()}. * * @return current visual style constant. */ public int getPickerStyle() { return pickerStyle; } /** * Sets the current visual style of the picker control. * <p> * The control is then updated to reflect the new style. * <p> * NOTE: do not confuse with {@link #getStyle()}. * * @param pickerStyle * the style to set * @see #PICKER_STYLE_BUTTON * @see #PICKER_STYLE_FIELD_AND_BUTTON */ public void setPickerStyle(int pickerStyle) { pickerStyle = checkPickerStyle(pickerStyle); int oldValue = this.pickerStyle; this.pickerStyle = pickerStyle; firePropertyChange(PROPERTY_NAME_PICKER_STYLE, oldValue, pickerStyle); } /** * A shortucut method to switch picker style between * {@link #PICKER_STYLE_FIELD_AND_BUTTON} and {@link #PICKER_STYLE_BUTTON} * * @param buttonOnly * <code>true</code> to set {@link #PICKER_STYLE_BUTTON}, * <code>false</code> to set * {@link #PICKER_STYLE_FIELD_AND_BUTTON} */ public void showButtonOnly(boolean buttonOnly) { if (buttonOnly) setPickerStyle(PICKER_STYLE_BUTTON); else setPickerStyle(PICKER_STYLE_FIELD_AND_BUTTON); } /** * Displays the calendar dropdown. */ public void showPopup() { ((DatePickerUI) getUI()).showPopup(true); } /** * Hides the calendar dropdown without selecting a date. */ public void hidePopup() { ((DatePickerUI) getUI()).showPopup(false); } public boolean commitEdit() { try { ((DatePickerUI) getUI()).commit(); fireCommitEvent(true); return true; } catch (Exception e) { return false; } } public void revertEdit() { ((DatePickerUI) getUI()).revert(); fireCommitEvent(false); } private void checkDateStyle(int style) { if (style != DateFormat.SHORT && style != DateFormat.MEDIUM && style != DateFormat.LONG) throw new IllegalArgumentException("dateStyle: unrecognized style"); } private int checkPickerStyle(int style) { if (style == 0) style = PICKER_STYLE_FIELD_AND_BUTTON; if (style != PICKER_STYLE_FIELD_AND_BUTTON && style != PICKER_STYLE_BUTTON) throw new IllegalArgumentException(PROPERTY_NAME_PICKER_STYLE + ": unrecognized style"); return style; } private void checkDateFormat(DateFormat dateFormat) { if (dateFormat == null) throw new IllegalArgumentException("dateFormat: null value"); } private DateFormat dateFormatFromStyle(int dateStyle) { DateFormat df = DateFormat.getDateInstance(dateStyle, this.getLocale()); df.setTimeZone(this.getZone()); return df; } }