package org.vaadin.touchkit.gwt.client.ui;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import org.vaadin.touchkit.gwt.client.ui.DatePicker.Resolution;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
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.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.datepicker.client.CalendarUtil;
import com.vaadin.client.ui.VOverlay;
/**
* Overlay with calendar, used by DatePicker.
*/
public class CalendarOverlay extends VOverlay implements
HasValueChangeHandlers<java.util.Date>, ClickHandler {
private static final String CLASSNAME = "v-touchkit-datepopover";
private com.google.gwt.user.datepicker.client.DatePicker calendarWidget = null;
private TextBox timeBox;
private final Label okButton;
private final Label cancelButton;
private final Resolution resolution;
private final Date min;
private final Date max;
public CalendarOverlay(Resolution resolution, final Date min, final Date max) {
this.resolution = resolution;
this.min = min;
this.max = max;
setAutoHideEnabled(true);
FlowPanel panel = new FlowPanel();
add(panel);
calendarWidget = GWT
.create(com.google.gwt.user.datepicker.client.DatePicker.class);
calendarWidget.addShowRangeHandlerAndFire(showRangeHandler);
panel.add(calendarWidget);
if (resolution == Resolution.TIME) {
SimplePanel p = new SimplePanel();
p.setStyleName(CLASSNAME + "-time");
timeBox = GWT.create(TextBox.class);
p.add(timeBox);
panel.add(p);
}
okButton = new Label("✓");
okButton.addStyleName("v-touchkit-date-ok");
okButton.addClickHandler(CalendarOverlay.this);
cancelButton = new Label("✕");
cancelButton.addStyleName("v-touchkit-date-cancel");
cancelButton.addClickHandler(CalendarOverlay.this);
panel.add(cancelButton);
panel.add(okButton);
addStyleName(CLASSNAME);
if (resolution == Resolution.MONTH) {
addStyleName(CLASSNAME + "-hidedays");
}
}
private final ShowRangeHandler<Date> showRangeHandler = new ShowRangeHandler<Date>() {
@Override
public void onShowRange(ShowRangeEvent<Date> event) {
// Verify min and max values
Date startDay = justDay(event.getStart());
Date endDay = justDay(event.getEnd());
Date currentMonth = justMonth(calendarWidget.getCurrentMonth());
if (min != null) {
if (currentMonth.before(justMonth(min))) {
calendarWidget.setCurrentMonth(min);
return;
}
Date minDay = justDay(min);
if (endDay.before(minDay)) {
calendarWidget.setCurrentMonth(minDay);
return;
}
}
if (max != null) {
if (currentMonth.after(justMonth(max))) {
calendarWidget.setCurrentMonth(max);
return;
}
Date maxDay = justDay(max);
if (startDay.after(maxDay)) {
calendarWidget.setCurrentMonth(maxDay);
return;
}
}
// Disable all dates that are in current view but are out of scope
disableDaysNotInCurrentMonth(startDay, endDay);
// Now update browsing button enable state
updatePrevNextButtons();
}
private void disableDaysNotInCurrentMonth(Date startDay, Date endDay) {
List<Date> disableDates = new LinkedList<Date>();
Date firstDayOfMonth = firstDayOfMonth(calendarWidget
.getCurrentMonth());
Date lastDayOfMonth = lastDayOfMonth(calendarWidget
.getCurrentMonth());
Date dayAfterEnd = (Date) endDay.clone();
CalendarUtil.addDaysToDate(dayAfterEnd, 1);
for (Date day = startDay; day.before(dayAfterEnd); CalendarUtil
.addDaysToDate(day, 1)) {
if (day.before(firstDayOfMonth) || day.after(lastDayOfMonth)) {
disableDates.add((Date) day.clone());
}
}
if (!disableDates.isEmpty()) {
calendarWidget.setTransientEnabledOnDates(false, disableDates);
}
}
private void updatePrevNextButtons() {
Date currentMonth;
currentMonth = justMonth(calendarWidget.getCurrentMonth());
if (min != null) {
setPrevButtonEnabled(currentMonth.after(min));
} else {
setPrevButtonEnabled(true);
}
if (max != null) {
setNextButtonEnabled(currentMonth.before(max));
} else {
setNextButtonEnabled(true);
}
}
};
private Date firstDayOfMonth(Date month) {
Date firstDay = (Date) month.clone();
CalendarUtil.setToFirstDayOfMonth(firstDay);
return justDay(firstDay);
}
private Date lastDayOfMonth(Date month) {
Date lastDay = (Date) month.clone();
CalendarUtil.addMonthsToDate(lastDay, 1);
CalendarUtil.setToFirstDayOfMonth(lastDay);
CalendarUtil.addDaysToDate(lastDay, -1);
return justDay(lastDay);
}
@Override
public HandlerRegistration addValueChangeHandler(
ValueChangeHandler<Date> handler) {
return addHandler(handler, ValueChangeEvent.getType());
}
public void setDate(Date date) {
calendarWidget.setValue(date, false);
calendarWidget.setCurrentMonth(date);
if (resolution == Resolution.TIME) {
timeBox.setText(timeOnlyFormat.format(date));
}
}
@Override
public void onClick(ClickEvent event) {
if (event.getSource() == okButton) {
Date value = calendarWidget.getValue();
if (resolution == Resolution.MONTH) {
value = calendarWidget.getCurrentMonth();
} else if (resolution == Resolution.TIME) {
value = trySetTimeFromTimeBoxText(value);
}
ValueChangeEvent.fire(CalendarOverlay.this, value);
this.hide();
} else if (event.getSource() == cancelButton) {
this.hide(false);
}
}
@SuppressWarnings("deprecation")
protected Date trySetTimeFromTimeBoxText(Date value) {
try {
Date time = timeOnlyFormat.parse(timeBox.getText());
value.setHours(time.getHours());
value.setMinutes(time.getMinutes());
} catch (IllegalArgumentException e) {
// Couldn't parse hours, just ignore them and use the old,
// unmodified value.
}
return value;
}
private final static DateTimeFormat timeOnlyFormat = DateTimeFormat
.getFormat(DateTimeFormat.PredefinedFormat.HOUR24_MINUTE);
protected final static DateTimeFormat dropTimeFormat = DateTimeFormat
.getFormat("yyyyMMdd");
protected Date justDay(Date date) {
return dropTimeFormat.parse(dropTimeFormat.format(date));
}
protected final static DateTimeFormat dropDayFormat = DateTimeFormat
.getFormat("yyyyMM");
protected Date justMonth(Date date) {
return dropDayFormat.parse(dropDayFormat.format(date));
}
protected void setPrevButtonEnabled(boolean enabled) {
if (!enabled) {
addStyleName(CLASSNAME + "-noprev");
} else {
removeStyleName(CLASSNAME + "-noprev");
}
}
protected void setNextButtonEnabled(boolean enabled) {
if (!enabled) {
addStyleName(CLASSNAME + "-nonext");
} else {
removeStyleName(CLASSNAME + "-nonext");
}
}
}