com.google.gwt.user.datepicker.client.DatePicker Maven / Gradle / Ivy
/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.user.datepicker.client;
import com.google.gwt.editor.client.IsEditor;
import com.google.gwt.editor.client.LeafValueEditor;
import com.google.gwt.editor.client.adapters.TakesValueEditor;
import com.google.gwt.event.logical.shared.HasHighlightHandlers;
import com.google.gwt.event.logical.shared.HasShowRangeHandlers;
import com.google.gwt.event.logical.shared.HighlightEvent;
import com.google.gwt.event.logical.shared.HighlightHandler;
import com.google.gwt.event.logical.shared.ShowRangeEvent;
import com.google.gwt.event.logical.shared.ShowRangeHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.VerticalPanel;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* Standard GWT date picker.
*
* CSS Style Rules
*
*
*
* - .gwt-DatePicker { }
*
* - .datePickerMonthSelector { the month selector widget }
*
* - .datePickerMonth { the month in the month selector widget }
-
*
*
- .datePickerYear { the year in the month selector widget }
-
*
*
- .datePickerPreviousButton { the previous month button }
-
*
*
- .datePickerNextButton { the next month button }
-
*
*
- .datePickerPreviousYearButton { the previous year button }
-
*
*
- .datePickerNextYearButton { the next year button }
-
*
*
- .datePickerDays { the portion of the picker that shows the days }
*
* - .datePickerWeekdayLabel { the label over weekdays }
*
* - .datePickerWeekendLabel { the label over weekends }
*
* - .datePickerDay { a single day }
*
* - .datePickerDayIsToday { today's date }
*
* - .datePickerDayIsWeekend { a weekend day }
*
* - .datePickerDayIsFiller { a day in another month }
*
* - .datePickerDayIsValue { the selected day }
*
* - .datePickerDayIsDisabled { a disabled day }
*
* - .datePickerDayIsHighlighted { the currently highlighted day }
*
* - .datePickerDayIsValueAndHighlighted { the highlighted day if it is also
* selected }
*
*
*
*
*
Example
* {@example com.google.gwt.examples.DatePickerExample}
*
*/
public class DatePicker extends Composite implements
HasHighlightHandlers, HasShowRangeHandlers, HasValue,
IsEditor> {
/**
* Convenience class to group css style names.
*/
static class StandardCss {
static StandardCss DEFAULT = new StandardCss("gwt-DatePicker", "datePicker");
private String baseName;
private String widgetName;
public StandardCss(String widgetName, String baseName) {
this.widgetName = widgetName;
this.baseName = baseName;
}
public String datePicker() {
return getWidgetStyleName();
}
public String day() {
return wrap("Day");
}
public String day(String dayModifier) {
return day() + "Is" + dayModifier;
}
public String dayIsDisabled() {
return day("Disabled");
}
public String dayIsFiller() {
return day("Filler");
}
public String dayIsHighlighted() {
return day("Highlighted");
}
public String dayIsToday() {
return day("Today");
}
public String dayIsValue() {
return day("Value");
}
public String dayIsValueAndHighlighted() {
return dayIsValue() + "AndHighlighted";
}
public String dayIsWeekend() {
return day("Weekend");
}
public String days() {
return wrap("Days");
}
public String daysLabel() {
return wrap("DaysLabel");
}
public String getBaseStyleName() {
return baseName;
}
public String getWidgetStyleName() {
return widgetName;
}
public String month() {
return wrap("Month");
}
public String monthSelector() {
return wrap("MonthSelector");
}
public String nextButton() {
return wrap("NextButton");
}
public String nextYearButton() {
return wrap("NextYearButton");
}
public String previousButton() {
return wrap("PreviousButton");
}
public String previousYearButton() {
return wrap("PreviousYearButton");
}
public String weekdayLabel() {
return wrap("WeekdayLabel");
}
public String weekendLabel() {
return wrap("WeekendLabel");
}
public String year() {
return wrap("Year");
}
/**
* Prepends the base name to the given style.
*
* @param style style name
* @return style name
*/
protected String wrap(String style) {
return baseName + style;
}
}
/**
* A date highlighted event that copied on read.
*/
private static class DateHighlightEvent extends HighlightEvent {
protected DateHighlightEvent(Date highlighted) {
super(highlighted);
}
@Override
public Date getHighlighted() {
return CalendarUtil.copyDate(super.getHighlighted());
}
}
private static class DateStyler {
private Map info = new HashMap();
public String getStyleName(Date d) {
return info.get(genKey(d));
}
public void setStyleName(Date d, String styleName, boolean add) {
// Code is easier to maintain if surrounded by " ", and on all browsers
// this is a no-op.
styleName = " " + styleName + " ";
String key = genKey(d);
String current = info.get(key);
if (add) {
if (current == null) {
info.put(key, styleName);
} else if (current.indexOf(styleName) == -1) {
info.put(key, current + styleName);
}
} else {
if (current != null) {
String newValue = current.replaceAll(styleName, "");
if (newValue.trim().length() == 0) {
info.remove(key);
} else {
info.put(key, newValue);
}
}
}
}
@SuppressWarnings("deprecation")
private String genKey(Date d) {
return d.getYear() + "/" + d.getMonth() + "/" + d.getDate();
}
}
private static final int DEFAULT_VISIBLE_YEAR_COUNT = 21;
private final DateStyler styler = new DateStyler();
private final MonthSelector monthAndYearSelector;
private final CalendarView view;
private final CalendarModel model;
private Date value;
private Date highlighted;
private StandardCss css = StandardCss.DEFAULT;
private LeafValueEditor editor;
private int visibleYearCount = DEFAULT_VISIBLE_YEAR_COUNT;
private boolean yearArrowsVisible;
private boolean yearAndMonthDropdownVisible;
/**
* Create a new date picker.
*/
public DatePicker() {
this(new DefaultMonthSelector(), new DefaultCalendarView(),
new CalendarModel());
}
/**
* Creates a new date picker.
*
* @param monthAndYearSelector the month selector
* @param view the view
* @param model the model
*/
protected DatePicker(MonthSelector monthAndYearSelector, CalendarView view,
CalendarModel model) {
this.model = model;
this.monthAndYearSelector = monthAndYearSelector;
monthAndYearSelector.setDatePicker(this);
this.view = view;
view.setDatePicker(this);
view.setup();
monthAndYearSelector.setup();
this.setup();
setCurrentMonth(new Date());
addStyleToDates(css().dayIsToday(), new Date());
}
public HandlerRegistration addHighlightHandler(HighlightHandler handler) {
return addHandler(handler, HighlightEvent.getType());
}
public HandlerRegistration addShowRangeHandler(ShowRangeHandler handler) {
return addHandler(handler, ShowRangeEvent.getType());
}
/**
* Adds a show range handler and immediately activate the handler on the
* current view.
*
* @param handler the handler
* @return the handler registration
*/
public HandlerRegistration addShowRangeHandlerAndFire(
ShowRangeHandler handler) {
ShowRangeEvent event = new ShowRangeEvent(
getView().getFirstDate(), getView().getLastDate()) {
};
handler.onShowRange(event);
return addShowRangeHandler(handler);
}
/**
* Add a style name to the given dates.
*/
public void addStyleToDates(String styleName, Date date) {
styler.setStyleName(date, styleName, true);
if (isDateVisible(date)) {
getView().addStyleToDate(styleName, date);
}
}
/**
* Add a style name to the given dates.
*/
public void addStyleToDates(String styleName, Date date, Date... moreDates) {
addStyleToDates(styleName, date);
for (Date d : moreDates) {
addStyleToDates(styleName, d);
}
}
/**
* Add a style name to the given dates.
*/
public void addStyleToDates(String styleName, Iterable dates) {
for (Date d : dates) {
addStyleToDates(styleName, d);
}
}
/**
* Adds the given style name to the specified dates, which must be visible.
* This is only set until the next time the DatePicker is refreshed.
*/
public void addTransientStyleToDates(String styleName, Date date) {
assert isDateVisible(date) : date + " must be visible";
getView().addStyleToDate(styleName, date);
}
/**
* Adds the given style name to the specified dates, which must be visible.
* This is only set until the next time the DatePicker is refreshed.
*/
public final void addTransientStyleToDates(String styleName, Date date,
Date... moreDates) {
addTransientStyleToDates(styleName, date);
for (Date d : moreDates) {
addTransientStyleToDates(styleName, d);
}
}
/**
* Adds the given style name to the specified dates, which must be visible.
* This is only set until the next time the DatePicker is refreshed.
*/
public final void addTransientStyleToDates(String styleName,
Iterable dates) {
for (Date d : dates) {
addTransientStyleToDates(styleName, d);
}
}
public HandlerRegistration addValueChangeHandler(
ValueChangeHandler handler) {
return addHandler(handler, ValueChangeEvent.getType());
}
/**
* Returns a {@link TakesValueEditor} backed by the DatePicker.
*/
public LeafValueEditor asEditor() {
if (editor == null) {
editor = TakesValueEditor.of(this);
}
return editor;
}
/**
* Gets the current month the date picker is showing.
*
*
* A datepicker may show days not in the current month. It
* must show all days in the current month.
*
*
* @return the current month
*
*/
public Date getCurrentMonth() {
return getModel().getCurrentMonth();
}
/**
* Returns the first shown date.
*
* @return the first date.
*/
// Final because the view should always control the value of the first date.
public final Date getFirstDate() {
return view.getFirstDate();
}
/**
* Gets the highlighted date (the one the mouse is hovering over), if any.
*
* @return the highlighted date
*/
public final Date getHighlightedDate() {
return CalendarUtil.copyDate(highlighted);
}
/**
* Returns the last shown date.
*
* @return the last date.
*/
// Final because the view should always control the value of the last date.
public final Date getLastDate() {
return view.getLastDate();
}
/**
* Returns the number of year to display in the years selection dropdown.
*/
public int getVisibleYearCount() {
return visibleYearCount;
}
/**
* Gets the style associated with a date (does not include styles set via
* {@link #addTransientStyleToDates}).
*
* @param date the date
* @return the styles associated with this date
*/
public String getStyleOfDate(Date date) {
return styler.getStyleName(date);
}
/**
* Returns the selected date, or null if none is selected.
*
* @return the selected date, or null
*/
public final Date getValue() {
return CalendarUtil.copyDate(value);
}
/**
* Is the visible date enabled?
*
* @param date the date, which must be visible
*
* @return is the date enabled?
*/
public boolean isDateEnabled(Date date) {
assert isDateVisible(date) : date + " is not visible";
return getView().isDateEnabled(date);
}
/**
* Is the date currently shown in the date picker?
*
* @param date
*
* @return is the date currently shown
*/
public boolean isDateVisible(Date date) {
CalendarView r = getView();
Date first = r.getFirstDate();
Date last = r.getLastDate();
return (date != null && (CalendarUtil.isSameDate(first, date)
|| CalendarUtil.isSameDate(last, date) || (first.before(date) && last.after(date))));
}
/**
* Can the user navigate through the years?
*
* @return is the year navigation is enabled
*/
public boolean isYearArrowsVisible() {
return yearArrowsVisible;
}
/**
* Is the year and month selectable via a dropdown?
*/
public boolean isYearAndMonthDropdownVisible() {
return yearAndMonthDropdownVisible;
}
@Override
public void onLoad() {
ShowRangeEvent.fire(this, getFirstDate(), getLastDate());
}
/**
* Removes the styleName from the given dates (even if it is transient).
*/
public void removeStyleFromDates(String styleName, Date date) {
styler.setStyleName(date, styleName, false);
if (isDateVisible(date)) {
getView().removeStyleFromDate(styleName, date);
}
}
/**
* Removes the styleName from the given dates (even if it is transient).
*/
public void removeStyleFromDates(String styleName, Date date,
Date... moreDates) {
removeStyleFromDates(styleName, date);
for (Date d : moreDates) {
removeStyleFromDates(styleName, d);
}
}
/**
* Removes the styleName from the given dates (even if it is transient).
*/
public void removeStyleFromDates(String styleName, Iterable dates) {
for (Date d : dates) {
removeStyleFromDates(styleName, d);
}
}
/**
* Sets the date picker to show the given month, use {@link #getFirstDate()}
* and {@link #getLastDate()} to access the exact date range the date picker
* chose to display.
*
* A datepicker may show days not in the current month. It
* must show all days in the current month.
*
*
* @param month the month to show
*/
public void setCurrentMonth(Date month) {
getModel().setCurrentMonth(month);
refreshAll();
}
/**
* Set the number of years to display in the years selection dropdown. The range of years will be
* centered on the selected date.
*/
public void setVisibleYearCount(int numberOfYears) {
if (numberOfYears <= 0) {
throw new IllegalArgumentException("The number of years to display must be positive");
}
visibleYearCount = numberOfYears;
getMonthSelector().refresh();
}
/**
* Set if the user can navigate through the years via a set of backward and forward buttons.
*/
public void setYearArrowsVisible(boolean yearArrowsVisible) {
this.yearArrowsVisible = yearArrowsVisible;
getMonthSelector().refresh();
}
/**
* If the dropdownVisible
is equal to true, the user will be able to change the current month and
* the current year of the date picker via two dropdown lists.
*/
public void setYearAndMonthDropdownVisible(boolean dropdownVisible) {
this.yearAndMonthDropdownVisible = dropdownVisible;
getMonthSelector().refresh();
}
/**
* Sets the date picker style name.
*/
@Override
public void setStyleName(String styleName) {
css = new StandardCss(styleName, "datePicker");
super.setStyleName(styleName);
}
/**
* Sets a visible date to be enabled or disabled. This is only set until the
* next time the DatePicker is refreshed.
*/
public final void setTransientEnabledOnDates(boolean enabled, Date date) {
assert isDateVisible(date) : date + " must be visible";
getView().setEnabledOnDate(enabled, date);
}
/**
* Sets a visible date to be enabled or disabled. This is only set until the
* next time the DatePicker is refreshed.
*/
public final void setTransientEnabledOnDates(boolean enabled, Date date,
Date... moreDates) {
setTransientEnabledOnDates(enabled, date);
for (Date d : moreDates) {
setTransientEnabledOnDates(enabled, d);
}
}
/**
* Sets a group of visible dates to be enabled or disabled. This is only set
* until the next time the DatePicker is refreshed.
*/
public final void setTransientEnabledOnDates(boolean enabled,
Iterable dates) {
for (Date d : dates) {
setTransientEnabledOnDates(enabled, d);
}
}
/**
* Sets the {@link DatePicker}'s value.
*
* @param newValue the new value
*/
public final void setValue(Date newValue) {
setValue(newValue, false);
}
/**
* Sets the {@link DatePicker}'s value.
*
* @param newValue the new value for this date picker
* @param fireEvents should events be fired.
*/
public final void setValue(Date newValue, boolean fireEvents) {
Date oldValue = value;
if (oldValue != null) {
removeStyleFromDates(css().dayIsValue(), oldValue);
}
value = CalendarUtil.copyDate(newValue);
if (value != null) {
addStyleToDates(css().dayIsValue(), value);
}
getView().setAriaSelectedCell(newValue);
if (fireEvents) {
DateChangeEvent.fireIfNotEqualDates(this, oldValue, newValue);
}
}
/**
* Gets the {@link CalendarModel} associated with this date picker.
*
* @return the model
*/
protected final CalendarModel getModel() {
return model;
}
/**
* Gets the {@link MonthSelector} associated with this date picker.
*
* @return the month selector
*/
protected final MonthSelector getMonthSelector() {
return monthAndYearSelector;
}
/**
* Gets the {@link CalendarView} associated with this date picker.
*
* @return the view
*/
protected final CalendarView getView() {
return view;
}
/**
* Refreshes all components of this date picker.
*/
protected final void refreshAll() {
highlighted = null;
getModel().refresh();
getView().refresh();
getMonthSelector().refresh();
if (isAttached()) {
ShowRangeEvent.fire(this, getFirstDate(), getLastDate());
}
getView().setAriaSelectedCell(value);
}
/**
* Sets up the date picker.
*/
protected void setup() {
/*
* Use a table (VerticalPanel) to get shrink-to-fit behavior. Divs expand to
* fill the available width, so we'd need to give it a size.
*/
VerticalPanel panel = new VerticalPanel();
initWidget(panel);
setStyleName(panel.getElement(), css.datePicker());
setStyleName(css().datePicker());
panel.add(this.getMonthSelector());
panel.add(this.getView());
}
/**
* Gets the css associated with this date picker for use by extended month and
* cell grids.
*
* @return the css.
*/
final StandardCss css() {
return css;
}
/**
* Sets the highlighted date.
*
* @param highlighted highlighted date
*/
void setHighlightedDate(Date highlighted) {
this.highlighted = highlighted;
fireEvent(new DateHighlightEvent(highlighted));
}
}