aboutsummaryrefslogtreecommitdiffstats
path: root/compatibility-client
diff options
context:
space:
mode:
authorArtur Signell <artur@vaadin.com>2016-08-30 14:18:12 +0300
committerArtur Signell <artur@vaadin.com>2016-08-30 17:19:35 +0300
commita23bf9481cedb7f981a5ff95f6acc19a7c46ffd2 (patch)
treed53b3b569d50e9ccfcb60de5b07a642f7845e3c1 /compatibility-client
parentb31a71ae635d431c258d387a90bacb27d62a6bbf (diff)
downloadvaadin-framework-a23bf9481cedb7f981a5ff95f6acc19a7c46ffd2.tar.gz
vaadin-framework-a23bf9481cedb7f981a5ff95f6acc19a7c46ffd2.zip
Move and duplicate client side and state to compatibility package
* DateField * PopupDateField * InlineDateField Change-Id: I7d6c0253435dcdf424b7914d025e81af504be11d
Diffstat (limited to 'compatibility-client')
-rw-r--r--compatibility-client/src/main/java/com/vaadin/v7/client/ui/VCalendarPanel.java2267
-rw-r--r--compatibility-client/src/main/java/com/vaadin/v7/client/ui/VDateField.java230
-rw-r--r--compatibility-client/src/main/java/com/vaadin/v7/client/ui/VDateFieldCalendar.java131
-rw-r--r--compatibility-client/src/main/java/com/vaadin/v7/client/ui/VPopupCalendar.java745
-rw-r--r--compatibility-client/src/main/java/com/vaadin/v7/client/ui/VTextualDate.java410
-rw-r--r--compatibility-client/src/main/java/com/vaadin/v7/client/ui/datefield/AbstractDateFieldConnector.java4
-rw-r--r--compatibility-client/src/main/java/com/vaadin/v7/client/ui/datefield/DateFieldConnector.java8
-rw-r--r--compatibility-client/src/main/java/com/vaadin/v7/client/ui/datefield/InlineDateFieldConnector.java8
-rw-r--r--compatibility-client/src/main/java/com/vaadin/v7/client/ui/datefield/TextualDateConnector.java4
9 files changed, 3795 insertions, 12 deletions
diff --git a/compatibility-client/src/main/java/com/vaadin/v7/client/ui/VCalendarPanel.java b/compatibility-client/src/main/java/com/vaadin/v7/client/ui/VCalendarPanel.java
new file mode 100644
index 0000000000..782cd1ca44
--- /dev/null
+++ b/compatibility-client/src/main/java/com/vaadin/v7/client/ui/VCalendarPanel.java
@@ -0,0 +1,2267 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * 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.vaadin.v7.client.ui;
+
+import java.util.Date;
+import java.util.Iterator;
+
+import com.google.gwt.aria.client.Roles;
+import com.google.gwt.aria.client.SelectedValue;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.dom.client.Node;
+import com.google.gwt.event.dom.client.BlurEvent;
+import com.google.gwt.event.dom.client.BlurHandler;
+import com.google.gwt.event.dom.client.ChangeEvent;
+import com.google.gwt.event.dom.client.ChangeHandler;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.DomEvent;
+import com.google.gwt.event.dom.client.FocusEvent;
+import com.google.gwt.event.dom.client.FocusHandler;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.event.dom.client.KeyDownEvent;
+import com.google.gwt.event.dom.client.KeyDownHandler;
+import com.google.gwt.event.dom.client.KeyPressEvent;
+import com.google.gwt.event.dom.client.KeyPressHandler;
+import com.google.gwt.event.dom.client.MouseDownEvent;
+import com.google.gwt.event.dom.client.MouseDownHandler;
+import com.google.gwt.event.dom.client.MouseOutEvent;
+import com.google.gwt.event.dom.client.MouseOutHandler;
+import com.google.gwt.event.dom.client.MouseUpEvent;
+import com.google.gwt.event.dom.client.MouseUpHandler;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.FlexTable;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.InlineHTML;
+import com.google.gwt.user.client.ui.ListBox;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.client.BrowserInfo;
+import com.vaadin.client.DateTimeService;
+import com.vaadin.client.VConsole;
+import com.vaadin.client.WidgetUtil;
+import com.vaadin.client.ui.FocusableFlexTable;
+import com.vaadin.client.ui.SubPartAware;
+import com.vaadin.shared.ui.datefield.Resolution;
+import com.vaadin.shared.util.SharedUtil;
+import com.vaadin.v7.client.ui.VPopupCalendar;
+
+@SuppressWarnings("deprecation")
+public class VCalendarPanel extends FocusableFlexTable implements
+ KeyDownHandler, KeyPressHandler, MouseOutHandler, MouseDownHandler,
+ MouseUpHandler, BlurHandler, FocusHandler, SubPartAware {
+
+ public interface SubmitListener {
+
+ /**
+ * Called when calendar user triggers a submitting operation in calendar
+ * panel. Eg. clicking on day or hitting enter.
+ */
+ void onSubmit();
+
+ /**
+ * On eg. ESC key.
+ */
+ void onCancel();
+ }
+
+ /**
+ * Blur listener that listens to blur event from the panel
+ */
+ public interface FocusOutListener {
+ /**
+ * @return true if the calendar panel is not used after focus moves out
+ */
+ boolean onFocusOut(DomEvent<?> event);
+ }
+
+ /**
+ * FocusChangeListener is notified when the panel changes its _focused_
+ * value.
+ */
+ public interface FocusChangeListener {
+ void focusChanged(Date focusedDate);
+ }
+
+ /**
+ * Dispatches an event when the panel when time is changed
+ */
+ public interface TimeChangeListener {
+
+ void changed(int hour, int min, int sec, int msec);
+ }
+
+ /**
+ * Represents a Date button in the calendar
+ */
+ private class VEventButton extends Button {
+ public VEventButton() {
+ addMouseDownHandler(VCalendarPanel.this);
+ addMouseOutHandler(VCalendarPanel.this);
+ addMouseUpHandler(VCalendarPanel.this);
+ }
+ }
+
+ private static final String CN_FOCUSED = "focused";
+
+ private static final String CN_TODAY = "today";
+
+ private static final String CN_SELECTED = "selected";
+
+ private static final String CN_OFFMONTH = "offmonth";
+
+ private static final String CN_OUTSIDE_RANGE = "outside-range";
+
+ /**
+ * Represents a click handler for when a user selects a value by using the
+ * mouse
+ */
+ private ClickHandler dayClickHandler = new ClickHandler() {
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt
+ * .event.dom.client.ClickEvent)
+ */
+ @Override
+ public void onClick(ClickEvent event) {
+ if (!isEnabled() || isReadonly()) {
+ return;
+ }
+
+ Date newDate = ((Day) event.getSource()).getDate();
+ if (!isDateInsideRange(newDate, Resolution.DAY)) {
+ return;
+ }
+ if (newDate.getMonth() != displayedMonth.getMonth()
+ || newDate.getYear() != displayedMonth.getYear()) {
+ // If an off-month date was clicked, we must change the
+ // displayed month and re-render the calendar (#8931)
+ displayedMonth.setMonth(newDate.getMonth());
+ displayedMonth.setYear(newDate.getYear());
+ renderCalendar();
+ }
+ focusDay(newDate);
+ selectFocused();
+ onSubmit();
+ }
+ };
+
+ private VEventButton prevYear;
+
+ private VEventButton nextYear;
+
+ private VEventButton prevMonth;
+
+ private VEventButton nextMonth;
+
+ private VTime time;
+
+ private FlexTable days = new FlexTable();
+
+ private Resolution resolution = Resolution.YEAR;
+
+ private Timer mouseTimer;
+
+ private Date value;
+
+ private DateTimeService dateTimeService;
+
+ private boolean showISOWeekNumbers;
+
+ private FocusedDate displayedMonth;
+
+ private FocusedDate focusedDate;
+
+ private Day selectedDay;
+
+ private Day focusedDay;
+
+ private FocusOutListener focusOutListener;
+
+ private SubmitListener submitListener;
+
+ private FocusChangeListener focusChangeListener;
+
+ private TimeChangeListener timeChangeListener;
+
+ private boolean hasFocus = false;
+
+ private VDateField parent;
+
+ private boolean initialRenderDone = false;
+
+ public VCalendarPanel() {
+ getElement().setId(DOM.createUniqueId());
+ setStyleName(VDateField.CLASSNAME + "-calendarpanel");
+ Roles.getGridRole().set(getElement());
+
+ /*
+ * Firefox auto-repeat works correctly only if we use a key press
+ * handler, other browsers handle it correctly when using a key down
+ * handler
+ */
+ if (BrowserInfo.get().isGecko()) {
+ addKeyPressHandler(this);
+ } else {
+ addKeyDownHandler(this);
+ }
+ addFocusHandler(this);
+ addBlurHandler(this);
+ }
+
+ public void setParentField(VDateField parent) {
+ this.parent = parent;
+ }
+
+ /**
+ * Sets the focus to given date in the current view. Used when moving in the
+ * calendar with the keyboard.
+ *
+ * @param date
+ * A Date representing the day of month to be focused. Must be
+ * one of the days currently visible.
+ */
+ private void focusDay(Date date) {
+ // Only used when calender body is present
+ if (resolution.getCalendarField() > Resolution.MONTH
+ .getCalendarField()) {
+ if (focusedDay != null) {
+ focusedDay.removeStyleDependentName(CN_FOCUSED);
+ }
+
+ if (date != null && focusedDate != null) {
+ focusedDate.setTime(date.getTime());
+ int rowCount = days.getRowCount();
+ for (int i = 0; i < rowCount; i++) {
+ int cellCount = days.getCellCount(i);
+ for (int j = 0; j < cellCount; j++) {
+ Widget widget = days.getWidget(i, j);
+ if (widget != null && widget instanceof Day) {
+ Day curday = (Day) widget;
+ if (curday.getDate().equals(date)) {
+ curday.addStyleDependentName(CN_FOCUSED);
+ focusedDay = curday;
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Sets the selection highlight to a given day in the current view
+ *
+ * @param date
+ * A Date representing the day of month to be selected. Must be
+ * one of the days currently visible.
+ *
+ */
+ private void selectDate(Date date) {
+ if (selectedDay != null) {
+ selectedDay.removeStyleDependentName(CN_SELECTED);
+ Roles.getGridcellRole()
+ .removeAriaSelectedState(selectedDay.getElement());
+ }
+
+ int rowCount = days.getRowCount();
+ for (int i = 0; i < rowCount; i++) {
+ int cellCount = days.getCellCount(i);
+ for (int j = 0; j < cellCount; j++) {
+ Widget widget = days.getWidget(i, j);
+ if (widget != null && widget instanceof Day) {
+ Day curday = (Day) widget;
+ if (curday.getDate().equals(date)) {
+ curday.addStyleDependentName(CN_SELECTED);
+ selectedDay = curday;
+ Roles.getGridcellRole().setAriaSelectedState(
+ selectedDay.getElement(), SelectedValue.TRUE);
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Updates year, month, day from focusedDate to value
+ */
+ private void selectFocused() {
+ if (focusedDate != null && isDateInsideRange(focusedDate, resolution)) {
+ if (value == null) {
+ // No previously selected value (set to null on server side).
+ // Create a new date using current date and time
+ value = new Date();
+ }
+ /*
+ * #5594 set Date (day) to 1 in order to prevent any kind of
+ * wrapping of months when later setting the month. (e.g. 31 ->
+ * month with 30 days -> wraps to the 1st of the following month,
+ * e.g. 31st of May -> 31st of April = 1st of May)
+ */
+ value.setDate(1);
+ if (value.getYear() != focusedDate.getYear()) {
+ value.setYear(focusedDate.getYear());
+ }
+ if (value.getMonth() != focusedDate.getMonth()) {
+ value.setMonth(focusedDate.getMonth());
+ }
+ if (value.getDate() != focusedDate.getDate()) {
+ }
+ // We always need to set the date, even if it hasn't changed, since
+ // it was forced to 1 above.
+ value.setDate(focusedDate.getDate());
+
+ selectDate(focusedDate);
+ } else {
+ VConsole.log("Trying to select a the focused date which is NULL!");
+ }
+ }
+
+ protected boolean onValueChange() {
+ return false;
+ }
+
+ public Resolution getResolution() {
+ return resolution;
+ }
+
+ public void setResolution(Resolution resolution) {
+ this.resolution = resolution;
+ if (time != null) {
+ time.removeFromParent();
+ time = null;
+ }
+ }
+
+ private boolean isReadonly() {
+ return parent.isReadonly();
+ }
+
+ private boolean isEnabled() {
+ return parent.isEnabled();
+ }
+
+ @Override
+ public void setStyleName(String style) {
+ super.setStyleName(style);
+ if (initialRenderDone) {
+ // Dynamic updates to the stylename needs to render the calendar to
+ // update the inner element stylenames
+ renderCalendar();
+ }
+ }
+
+ @Override
+ public void setStylePrimaryName(String style) {
+ super.setStylePrimaryName(style);
+ if (initialRenderDone) {
+ // Dynamic updates to the stylename needs to render the calendar to
+ // update the inner element stylenames
+ renderCalendar();
+ }
+ }
+
+ private void clearCalendarBody(boolean remove) {
+ if (!remove) {
+ // Leave the cells in place but clear their contents
+
+ // This has the side effect of ensuring that the calendar always
+ // contain 7 rows.
+ for (int row = 1; row < 7; row++) {
+ for (int col = 0; col < 8; col++) {
+ days.setHTML(row, col, "&nbsp;");
+ }
+ }
+ } else if (getRowCount() > 1) {
+ removeRow(1);
+ days.clear();
+ }
+ }
+
+ /**
+ * Builds the top buttons and current month and year header.
+ *
+ * @param needsMonth
+ * Should the month buttons be visible?
+ */
+ private void buildCalendarHeader(boolean needsMonth) {
+
+ getRowFormatter().addStyleName(0,
+ parent.getStylePrimaryName() + "-calendarpanel-header");
+
+ if (prevMonth == null && needsMonth) {
+ prevMonth = new VEventButton();
+ prevMonth.setHTML("&lsaquo;");
+ prevMonth.setStyleName("v-button-prevmonth");
+
+ prevMonth.setTabIndex(-1);
+
+ nextMonth = new VEventButton();
+ nextMonth.setHTML("&rsaquo;");
+ nextMonth.setStyleName("v-button-nextmonth");
+
+ nextMonth.setTabIndex(-1);
+
+ setWidget(0, 3, nextMonth);
+ setWidget(0, 1, prevMonth);
+ } else if (prevMonth != null && !needsMonth) {
+ // Remove month traverse buttons
+ remove(prevMonth);
+ remove(nextMonth);
+ prevMonth = null;
+ nextMonth = null;
+ }
+
+ if (prevYear == null) {
+
+ prevYear = new VEventButton();
+ prevYear.setHTML("&laquo;");
+ prevYear.setStyleName("v-button-prevyear");
+
+ prevYear.setTabIndex(-1);
+ nextYear = new VEventButton();
+ nextYear.setHTML("&raquo;");
+ nextYear.setStyleName("v-button-nextyear");
+
+ nextYear.setTabIndex(-1);
+ setWidget(0, 0, prevYear);
+ setWidget(0, 4, nextYear);
+ }
+
+ updateControlButtonRangeStyles(needsMonth);
+
+ final String monthName = needsMonth
+ ? getDateTimeService().getMonth(displayedMonth.getMonth()) : "";
+ final int year = displayedMonth.getYear() + 1900;
+
+ getFlexCellFormatter().setStyleName(0, 2,
+ parent.getStylePrimaryName() + "-calendarpanel-month");
+ getFlexCellFormatter().setStyleName(0, 0,
+ parent.getStylePrimaryName() + "-calendarpanel-prevyear");
+ getFlexCellFormatter().setStyleName(0, 4,
+ parent.getStylePrimaryName() + "-calendarpanel-nextyear");
+ getFlexCellFormatter().setStyleName(0, 3,
+ parent.getStylePrimaryName() + "-calendarpanel-nextmonth");
+ getFlexCellFormatter().setStyleName(0, 1,
+ parent.getStylePrimaryName() + "-calendarpanel-prevmonth");
+
+ setHTML(0, 2,
+ "<span class=\"" + parent.getStylePrimaryName()
+ + "-calendarpanel-month\">" + monthName + " " + year
+ + "</span>");
+ }
+
+ private void updateControlButtonRangeStyles(boolean needsMonth) {
+
+ if (focusedDate == null) {
+ return;
+ }
+
+ if (needsMonth) {
+ Date prevMonthDate = (Date) focusedDate.clone();
+ removeOneMonth(prevMonthDate);
+
+ if (!isDateInsideRange(prevMonthDate, Resolution.MONTH)) {
+ prevMonth.addStyleName(CN_OUTSIDE_RANGE);
+ } else {
+ prevMonth.removeStyleName(CN_OUTSIDE_RANGE);
+ }
+ Date nextMonthDate = (Date) focusedDate.clone();
+ addOneMonth(nextMonthDate);
+ if (!isDateInsideRange(nextMonthDate, Resolution.MONTH)) {
+ nextMonth.addStyleName(CN_OUTSIDE_RANGE);
+ } else {
+ nextMonth.removeStyleName(CN_OUTSIDE_RANGE);
+ }
+ }
+
+ Date prevYearDate = (Date) focusedDate.clone();
+ prevYearDate.setYear(prevYearDate.getYear() - 1);
+ if (!isDateInsideRange(prevYearDate, Resolution.YEAR)) {
+ prevYear.addStyleName(CN_OUTSIDE_RANGE);
+ } else {
+ prevYear.removeStyleName(CN_OUTSIDE_RANGE);
+ }
+
+ Date nextYearDate = (Date) focusedDate.clone();
+ nextYearDate.setYear(nextYearDate.getYear() + 1);
+ if (!isDateInsideRange(nextYearDate, Resolution.YEAR)) {
+ nextYear.addStyleName(CN_OUTSIDE_RANGE);
+ } else {
+ nextYear.removeStyleName(CN_OUTSIDE_RANGE);
+ }
+
+ }
+
+ private DateTimeService getDateTimeService() {
+ return dateTimeService;
+ }
+
+ public void setDateTimeService(DateTimeService dateTimeService) {
+ this.dateTimeService = dateTimeService;
+ }
+
+ /**
+ * Returns whether ISO 8601 week numbers should be shown in the value
+ * selector or not. ISO 8601 defines that a week always starts with a Monday
+ * so the week numbers are only shown if this is the case.
+ *
+ * @return true if week number should be shown, false otherwise
+ */
+ public boolean isShowISOWeekNumbers() {
+ return showISOWeekNumbers;
+ }
+
+ public void setShowISOWeekNumbers(boolean showISOWeekNumbers) {
+ this.showISOWeekNumbers = showISOWeekNumbers;
+ }
+
+ /**
+ * Checks inclusively whether a date is inside a range of dates or not.
+ *
+ * @param date
+ * @return
+ */
+ private boolean isDateInsideRange(Date date, Resolution minResolution) {
+ assert (date != null);
+
+ return isAcceptedByRangeEnd(date, minResolution)
+ && isAcceptedByRangeStart(date, minResolution);
+ }
+
+ /**
+ * Accepts dates greater than or equal to rangeStart, depending on the
+ * resolution. If the resolution is set to DAY, the range will compare on a
+ * day-basis. If the resolution is set to YEAR, only years are compared. So
+ * even if the range is set to one millisecond in next year, also next year
+ * will be included.
+ *
+ * @param date
+ * @param minResolution
+ * @return
+ */
+ private boolean isAcceptedByRangeStart(Date date,
+ Resolution minResolution) {
+ assert (date != null);
+
+ // rangeStart == null means that we accept all values below rangeEnd
+ if (rangeStart == null) {
+ return true;
+ }
+
+ Date valueDuplicate = (Date) date.clone();
+ Date rangeStartDuplicate = (Date) rangeStart.clone();
+
+ if (minResolution == Resolution.YEAR) {
+ return valueDuplicate.getYear() >= rangeStartDuplicate.getYear();
+ }
+ if (minResolution == Resolution.MONTH) {
+ valueDuplicate = clearDateBelowMonth(valueDuplicate);
+ rangeStartDuplicate = clearDateBelowMonth(rangeStartDuplicate);
+ } else {
+ valueDuplicate = clearDateBelowDay(valueDuplicate);
+ rangeStartDuplicate = clearDateBelowDay(rangeStartDuplicate);
+ }
+
+ return !rangeStartDuplicate.after(valueDuplicate);
+ }
+
+ /**
+ * Accepts dates earlier than or equal to rangeStart, depending on the
+ * resolution. If the resolution is set to DAY, the range will compare on a
+ * day-basis. If the resolution is set to YEAR, only years are compared. So
+ * even if the range is set to one millisecond in next year, also next year
+ * will be included.
+ *
+ * @param date
+ * @param minResolution
+ * @return
+ */
+ private boolean isAcceptedByRangeEnd(Date date, Resolution minResolution) {
+ assert (date != null);
+
+ // rangeEnd == null means that we accept all values above rangeStart
+ if (rangeEnd == null) {
+ return true;
+ }
+
+ Date valueDuplicate = (Date) date.clone();
+ Date rangeEndDuplicate = (Date) rangeEnd.clone();
+
+ if (minResolution == Resolution.YEAR) {
+ return valueDuplicate.getYear() <= rangeEndDuplicate.getYear();
+ }
+ if (minResolution == Resolution.MONTH) {
+ valueDuplicate = clearDateBelowMonth(valueDuplicate);
+ rangeEndDuplicate = clearDateBelowMonth(rangeEndDuplicate);
+ } else {
+ valueDuplicate = clearDateBelowDay(valueDuplicate);
+ rangeEndDuplicate = clearDateBelowDay(rangeEndDuplicate);
+ }
+
+ return !rangeEndDuplicate.before(valueDuplicate);
+
+ }
+
+ private static Date clearDateBelowMonth(Date date) {
+ date.setDate(1);
+ return clearDateBelowDay(date);
+ }
+
+ private static Date clearDateBelowDay(Date date) {
+ date.setHours(0);
+ date.setMinutes(0);
+ date.setSeconds(0);
+ // Clearing milliseconds
+ long time = date.getTime() / 1000;
+ date = new Date(time * 1000);
+ return date;
+ }
+
+ /**
+ * Builds the day and time selectors of the calendar.
+ */
+ private void buildCalendarBody() {
+
+ final int weekColumn = 0;
+ final int firstWeekdayColumn = 1;
+ final int headerRow = 0;
+
+ setWidget(1, 0, days);
+ setCellPadding(0);
+ setCellSpacing(0);
+ getFlexCellFormatter().setColSpan(1, 0, 5);
+ getFlexCellFormatter().setStyleName(1, 0,
+ parent.getStylePrimaryName() + "-calendarpanel-body");
+
+ days.getFlexCellFormatter().setStyleName(headerRow, weekColumn,
+ "v-week");
+ days.setHTML(headerRow, weekColumn, "<strong></strong>");
+ // Hide the week column if week numbers are not to be displayed.
+ days.getFlexCellFormatter().setVisible(headerRow, weekColumn,
+ isShowISOWeekNumbers());
+
+ days.getRowFormatter().setStyleName(headerRow,
+ parent.getStylePrimaryName() + "-calendarpanel-weekdays");
+
+ if (isShowISOWeekNumbers()) {
+ days.getFlexCellFormatter().setStyleName(headerRow, weekColumn,
+ "v-first");
+ days.getFlexCellFormatter().setStyleName(headerRow,
+ firstWeekdayColumn, "");
+ days.getRowFormatter().addStyleName(headerRow,
+ parent.getStylePrimaryName()
+ + "-calendarpanel-weeknumbers");
+ } else {
+ days.getFlexCellFormatter().setStyleName(headerRow, weekColumn, "");
+ days.getFlexCellFormatter().setStyleName(headerRow,
+ firstWeekdayColumn, "v-first");
+ }
+
+ days.getFlexCellFormatter().setStyleName(headerRow,
+ firstWeekdayColumn + 6, "v-last");
+
+ // Print weekday names
+ final int firstDay = getDateTimeService().getFirstDayOfWeek();
+ for (int i = 0; i < 7; i++) {
+ int day = i + firstDay;
+ if (day > 6) {
+ day = 0;
+ }
+ if (getResolution().getCalendarField() > Resolution.MONTH
+ .getCalendarField()) {
+ days.setHTML(headerRow, firstWeekdayColumn + i, "<strong>"
+ + getDateTimeService().getShortDay(day) + "</strong>");
+ } else {
+ days.setHTML(headerRow, firstWeekdayColumn + i, "");
+ }
+
+ Roles.getColumnheaderRole().set(days.getCellFormatter()
+ .getElement(headerRow, firstWeekdayColumn + i));
+ }
+
+ // Zero out hours, minutes, seconds, and milliseconds to compare dates
+ // without time part
+ final Date tmp = new Date();
+ final Date today = new Date(tmp.getYear(), tmp.getMonth(),
+ tmp.getDate());
+
+ final Date selectedDate = value == null ? null
+ : new Date(value.getYear(), value.getMonth(), value.getDate());
+
+ final int startWeekDay = getDateTimeService()
+ .getStartWeekDay(displayedMonth);
+ final Date curr = (Date) displayedMonth.clone();
+ // Start from the first day of the week that at least partially belongs
+ // to the current month
+ curr.setDate(1 - startWeekDay);
+
+ // No month has more than 6 weeks so 6 is a safe maximum for rows.
+ for (int weekOfMonth = 1; weekOfMonth < 7; weekOfMonth++) {
+ for (int dayOfWeek = 0; dayOfWeek < 7; dayOfWeek++) {
+
+ // Actually write the day of month
+ Date dayDate = (Date) curr.clone();
+ Day day = new Day(dayDate);
+
+ day.setStyleName(
+ parent.getStylePrimaryName() + "-calendarpanel-day");
+
+ if (!isDateInsideRange(dayDate, Resolution.DAY)) {
+ day.addStyleDependentName(CN_OUTSIDE_RANGE);
+ }
+
+ if (curr.equals(selectedDate)) {
+ day.addStyleDependentName(CN_SELECTED);
+ Roles.getGridcellRole().setAriaSelectedState(
+ day.getElement(), SelectedValue.TRUE);
+ selectedDay = day;
+ }
+ if (curr.equals(today)) {
+ day.addStyleDependentName(CN_TODAY);
+ }
+ if (curr.equals(focusedDate)) {
+ focusedDay = day;
+ if (hasFocus) {
+ day.addStyleDependentName(CN_FOCUSED);
+ }
+ }
+ if (curr.getMonth() != displayedMonth.getMonth()) {
+ day.addStyleDependentName(CN_OFFMONTH);
+ }
+
+ days.setWidget(weekOfMonth, firstWeekdayColumn + dayOfWeek,
+ day);
+ Roles.getGridcellRole().set(days.getCellFormatter().getElement(
+ weekOfMonth, firstWeekdayColumn + dayOfWeek));
+
+ // ISO week numbers if requested
+ days.getCellFormatter().setVisible(weekOfMonth, weekColumn,
+ isShowISOWeekNumbers());
+
+ if (isShowISOWeekNumbers()) {
+ final String baseCssClass = parent.getStylePrimaryName()
+ + "-calendarpanel-weeknumber";
+ String weekCssClass = baseCssClass;
+
+ int weekNumber = DateTimeService.getISOWeekNumber(curr);
+
+ days.setHTML(weekOfMonth, 0, "<span class=\"" + weekCssClass
+ + "\"" + ">" + weekNumber + "</span>");
+ }
+ curr.setDate(curr.getDate() + 1);
+ }
+ }
+ }
+
+ /**
+ * Do we need the time selector
+ *
+ * @return True if it is required
+ */
+ private boolean isTimeSelectorNeeded() {
+ return getResolution().getCalendarField() > Resolution.DAY
+ .getCalendarField();
+ }
+
+ /**
+ * Updates the calendar and text field with the selected dates.
+ */
+ public void renderCalendar() {
+ renderCalendar(true);
+ }
+
+ /**
+ * For internal use only. May be removed or replaced in the future.
+ *
+ * Updates the calendar and text field with the selected dates.
+ *
+ * @param updateDate
+ * The value false prevents setting the selected date of the
+ * calendar based on focusedDate. That can be used when only the
+ * resolution of the calendar is changed and no date has been
+ * selected.
+ */
+ public void renderCalendar(boolean updateDate) {
+
+ super.setStylePrimaryName(
+ parent.getStylePrimaryName() + "-calendarpanel");
+
+ if (focusedDate == null) {
+ Date now = new Date();
+ // focusedDate must have zero hours, mins, secs, millisecs
+ focusedDate = new FocusedDate(now.getYear(), now.getMonth(),
+ now.getDate());
+ displayedMonth = new FocusedDate(now.getYear(), now.getMonth(), 1);
+ }
+
+ if (updateDate && getResolution().getCalendarField() <= Resolution.MONTH
+ .getCalendarField() && focusChangeListener != null) {
+ focusChangeListener.focusChanged(new Date(focusedDate.getTime()));
+ }
+
+ final boolean needsMonth = getResolution()
+ .getCalendarField() > Resolution.YEAR.getCalendarField();
+ boolean needsBody = getResolution().getCalendarField() >= Resolution.DAY
+ .getCalendarField();
+ buildCalendarHeader(needsMonth);
+ clearCalendarBody(!needsBody);
+ if (needsBody) {
+ buildCalendarBody();
+ }
+
+ if (isTimeSelectorNeeded()) {
+ time = new VTime();
+ setWidget(2, 0, time);
+ getFlexCellFormatter().setColSpan(2, 0, 5);
+ getFlexCellFormatter().setStyleName(2, 0,
+ parent.getStylePrimaryName() + "-calendarpanel-time");
+ } else if (time != null) {
+ remove(time);
+ }
+
+ initialRenderDone = true;
+ }
+
+ /**
+ * Moves the focus forward the given number of days.
+ */
+ private void focusNextDay(int days) {
+ if (focusedDate == null) {
+ return;
+ }
+
+ Date focusCopy = ((Date) focusedDate.clone());
+ focusCopy.setDate(focusedDate.getDate() + days);
+ if (!isDateInsideRange(focusCopy, resolution)) {
+ // If not inside allowed range, then do not move anything
+ return;
+ }
+
+ int oldMonth = focusedDate.getMonth();
+ int oldYear = focusedDate.getYear();
+ focusedDate.setDate(focusedDate.getDate() + days);
+
+ if (focusedDate.getMonth() == oldMonth
+ && focusedDate.getYear() == oldYear) {
+ // Month did not change, only move the selection
+ focusDay(focusedDate);
+ } else {
+
+ // If the month changed we need to re-render the calendar
+ displayedMonth.setMonth(focusedDate.getMonth());
+ displayedMonth.setYear(focusedDate.getYear());
+ renderCalendar();
+ }
+ }
+
+ /**
+ * Moves the focus backward the given number of days.
+ */
+ private void focusPreviousDay(int days) {
+ focusNextDay(-days);
+ }
+
+ /**
+ * Selects the next month
+ */
+ private void focusNextMonth() {
+
+ if (focusedDate == null) {
+ return;
+ }
+ // Trying to request next month
+ Date requestedNextMonthDate = (Date) focusedDate.clone();
+ addOneMonth(requestedNextMonthDate);
+
+ if (!isDateInsideRange(requestedNextMonthDate, Resolution.MONTH)) {
+ return;
+ }
+
+ // Now also checking whether the day is inside the range or not. If not
+ // inside,
+ // correct it
+ if (!isDateInsideRange(requestedNextMonthDate, Resolution.DAY)) {
+ requestedNextMonthDate = adjustDateToFitInsideRange(
+ requestedNextMonthDate);
+ }
+
+ focusedDate.setTime(requestedNextMonthDate.getTime());
+ displayedMonth.setMonth(displayedMonth.getMonth() + 1);
+
+ renderCalendar();
+ }
+
+ private static void addOneMonth(Date date) {
+ int currentMonth = date.getMonth();
+ int requestedMonth = (currentMonth + 1) % 12;
+
+ date.setMonth(date.getMonth() + 1);
+
+ /*
+ * If the selected value was e.g. 31.3 the new value would be 31.4 but
+ * this value is invalid so the new value will be 1.5. This is taken
+ * care of by decreasing the value until we have the correct month.
+ */
+ while (date.getMonth() != requestedMonth) {
+ date.setDate(date.getDate() - 1);
+ }
+ }
+
+ private static void removeOneMonth(Date date) {
+ int currentMonth = date.getMonth();
+
+ date.setMonth(date.getMonth() - 1);
+
+ /*
+ * If the selected value was e.g. 31.12 the new value would be 31.11 but
+ * this value is invalid so the new value will be 1.12. This is taken
+ * care of by decreasing the value until we have the correct month.
+ */
+ while (date.getMonth() == currentMonth) {
+ date.setDate(date.getDate() - 1);
+ }
+ }
+
+ /**
+ * Selects the previous month
+ */
+ private void focusPreviousMonth() {
+
+ if (focusedDate == null) {
+ return;
+ }
+ Date requestedPreviousMonthDate = (Date) focusedDate.clone();
+ removeOneMonth(requestedPreviousMonthDate);
+
+ if (!isDateInsideRange(requestedPreviousMonthDate, Resolution.MONTH)) {
+ return;
+ }
+
+ if (!isDateInsideRange(requestedPreviousMonthDate, Resolution.DAY)) {
+ requestedPreviousMonthDate = adjustDateToFitInsideRange(
+ requestedPreviousMonthDate);
+ }
+ focusedDate.setTime(requestedPreviousMonthDate.getTime());
+ displayedMonth.setMonth(displayedMonth.getMonth() - 1);
+
+ renderCalendar();
+ }
+
+ /**
+ * Selects the previous year
+ */
+ private void focusPreviousYear(int years) {
+
+ if (focusedDate == null) {
+ return;
+ }
+ Date previousYearDate = (Date) focusedDate.clone();
+ previousYearDate.setYear(previousYearDate.getYear() - years);
+ // Do not focus if not inside range
+ if (!isDateInsideRange(previousYearDate, Resolution.YEAR)) {
+ return;
+ }
+ // If we remove one year, but have to roll back a bit, fit it
+ // into the calendar. Also the months have to be changed
+ if (!isDateInsideRange(previousYearDate, Resolution.DAY)) {
+ previousYearDate = adjustDateToFitInsideRange(previousYearDate);
+
+ focusedDate.setYear(previousYearDate.getYear());
+ focusedDate.setMonth(previousYearDate.getMonth());
+ focusedDate.setDate(previousYearDate.getDate());
+ displayedMonth.setYear(previousYearDate.getYear());
+ displayedMonth.setMonth(previousYearDate.getMonth());
+ } else {
+
+ int currentMonth = focusedDate.getMonth();
+ focusedDate.setYear(focusedDate.getYear() - years);
+ displayedMonth.setYear(displayedMonth.getYear() - years);
+ /*
+ * If the focused date was a leap day (Feb 29), the new date becomes
+ * Mar 1 if the new year is not also a leap year. Set it to Feb 28
+ * instead.
+ */
+ if (focusedDate.getMonth() != currentMonth) {
+ focusedDate.setDate(0);
+ }
+ }
+
+ renderCalendar();
+ }
+
+ /**
+ * Selects the next year
+ */
+ private void focusNextYear(int years) {
+
+ if (focusedDate == null) {
+ return;
+ }
+ Date nextYearDate = (Date) focusedDate.clone();
+ nextYearDate.setYear(nextYearDate.getYear() + years);
+ // Do not focus if not inside range
+ if (!isDateInsideRange(nextYearDate, Resolution.YEAR)) {
+ return;
+ }
+ // If we add one year, but have to roll back a bit, fit it
+ // into the calendar. Also the months have to be changed
+ if (!isDateInsideRange(nextYearDate, Resolution.DAY)) {
+ nextYearDate = adjustDateToFitInsideRange(nextYearDate);
+
+ focusedDate.setYear(nextYearDate.getYear());
+ focusedDate.setMonth(nextYearDate.getMonth());
+ focusedDate.setDate(nextYearDate.getDate());
+ displayedMonth.setYear(nextYearDate.getYear());
+ displayedMonth.setMonth(nextYearDate.getMonth());
+ } else {
+
+ int currentMonth = focusedDate.getMonth();
+ focusedDate.setYear(focusedDate.getYear() + years);
+ displayedMonth.setYear(displayedMonth.getYear() + years);
+ /*
+ * If the focused date was a leap day (Feb 29), the new date becomes
+ * Mar 1 if the new year is not also a leap year. Set it to Feb 28
+ * instead.
+ */
+ if (focusedDate.getMonth() != currentMonth) {
+ focusedDate.setDate(0);
+ }
+ }
+
+ renderCalendar();
+ }
+
+ /**
+ * Handles a user click on the component
+ *
+ * @param sender
+ * The component that was clicked
+ * @param updateVariable
+ * Should the value field be updated
+ *
+ */
+ private void processClickEvent(Widget sender) {
+ if (!isEnabled() || isReadonly()) {
+ return;
+ }
+ if (sender == prevYear) {
+ focusPreviousYear(1);
+ } else if (sender == nextYear) {
+ focusNextYear(1);
+ } else if (sender == prevMonth) {
+ focusPreviousMonth();
+ } else if (sender == nextMonth) {
+ focusNextMonth();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.google.gwt.event.dom.client.KeyDownHandler#onKeyDown(com.google.gwt
+ * .event.dom.client.KeyDownEvent)
+ */
+ @Override
+ public void onKeyDown(KeyDownEvent event) {
+ handleKeyPress(event);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.google.gwt.event.dom.client.KeyPressHandler#onKeyPress(com.google
+ * .gwt.event.dom.client.KeyPressEvent)
+ */
+ @Override
+ public void onKeyPress(KeyPressEvent event) {
+ handleKeyPress(event);
+ }
+
+ /**
+ * Handles the keypress from both the onKeyPress event and the onKeyDown
+ * event
+ *
+ * @param event
+ * The keydown/keypress event
+ */
+ private void handleKeyPress(DomEvent<?> event) {
+ // Special handling for events from time ListBoxes.
+ if (time != null && time.getElement().isOrHasChild(
+ (Node) event.getNativeEvent().getEventTarget().cast())) {
+ int nativeKeyCode = event.getNativeEvent().getKeyCode();
+ if (nativeKeyCode == getSelectKey()) {
+ onSubmit(); // submit if enter key hit down on listboxes
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ if (nativeKeyCode == getCloseKey()) {
+ onCancel(); // cancel if ESC key hit down on listboxes
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ return;
+ }
+
+ // Check tabs
+ int keycode = event.getNativeEvent().getKeyCode();
+ if (keycode == KeyCodes.KEY_TAB
+ && event.getNativeEvent().getShiftKey()) {
+ if (onTabOut(event)) {
+ return;
+ }
+ }
+
+ // Handle the navigation
+ if (handleNavigation(keycode,
+ event.getNativeEvent().getCtrlKey()
+ || event.getNativeEvent().getMetaKey(),
+ event.getNativeEvent().getShiftKey())) {
+ event.preventDefault();
+ }
+
+ }
+
+ /**
+ * Notifies submit-listeners of a submit event
+ */
+ private void onSubmit() {
+ if (getSubmitListener() != null) {
+ getSubmitListener().onSubmit();
+ }
+ }
+
+ /**
+ * Notifies submit-listeners of a cancel event
+ */
+ private void onCancel() {
+ if (getSubmitListener() != null) {
+ getSubmitListener().onCancel();
+ }
+ }
+
+ /**
+ * Handles the keyboard navigation when the resolution is set to years.
+ *
+ * @param keycode
+ * The keycode to process
+ * @param ctrl
+ * Is ctrl pressed?
+ * @param shift
+ * is shift pressed
+ * @return Returns true if the keycode was processed, else false
+ */
+ protected boolean handleNavigationYearMode(int keycode, boolean ctrl,
+ boolean shift) {
+
+ // Ctrl and Shift selection not supported
+ if (ctrl || shift) {
+ return false;
+ }
+
+ else if (keycode == getPreviousKey()) {
+ focusNextYear(10); // Add 10 years
+ return true;
+ }
+
+ else if (keycode == getForwardKey()) {
+ focusNextYear(1); // Add 1 year
+ return true;
+ }
+
+ else if (keycode == getNextKey()) {
+ focusPreviousYear(10); // Subtract 10 years
+ return true;
+ }
+
+ else if (keycode == getBackwardKey()) {
+ focusPreviousYear(1); // Subtract 1 year
+ return true;
+
+ } else if (keycode == getSelectKey()) {
+ value = (Date) focusedDate.clone();
+ onSubmit();
+ return true;
+
+ } else if (keycode == getResetKey()) {
+ // Restore showing value the selected value
+ focusedDate.setTime(value.getTime());
+ renderCalendar();
+ return true;
+
+ } else if (keycode == getCloseKey()) {
+ // TODO fire listener, on users responsibility??
+
+ onCancel();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Handle the keyboard navigation when the resolution is set to MONTH
+ *
+ * @param keycode
+ * The keycode to handle
+ * @param ctrl
+ * Was the ctrl key pressed?
+ * @param shift
+ * Was the shift key pressed?
+ * @return
+ */
+ protected boolean handleNavigationMonthMode(int keycode, boolean ctrl,
+ boolean shift) {
+
+ // Ctrl selection not supported
+ if (ctrl) {
+ return false;
+
+ } else if (keycode == getPreviousKey()) {
+ focusNextYear(1); // Add 1 year
+ return true;
+
+ } else if (keycode == getForwardKey()) {
+ focusNextMonth(); // Add 1 month
+ return true;
+
+ } else if (keycode == getNextKey()) {
+ focusPreviousYear(1); // Subtract 1 year
+ return true;
+
+ } else if (keycode == getBackwardKey()) {
+ focusPreviousMonth(); // Subtract 1 month
+ return true;
+
+ } else if (keycode == getSelectKey()) {
+ value = (Date) focusedDate.clone();
+ onSubmit();
+ return true;
+
+ } else if (keycode == getResetKey()) {
+ // Restore showing value the selected value
+ focusedDate.setTime(value.getTime());
+ renderCalendar();
+ return true;
+
+ } else if (keycode == getCloseKey() || keycode == KeyCodes.KEY_TAB) {
+ onCancel();
+
+ // TODO fire close event
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Handle keyboard navigation what the resolution is set to DAY
+ *
+ * @param keycode
+ * The keycode to handle
+ * @param ctrl
+ * Was the ctrl key pressed?
+ * @param shift
+ * Was the shift key pressed?
+ * @return Return true if the key press was handled by the method, else
+ * return false.
+ */
+ protected boolean handleNavigationDayMode(int keycode, boolean ctrl,
+ boolean shift) {
+
+ // Ctrl key is not in use
+ if (ctrl) {
+ return false;
+ }
+
+ /*
+ * Jumps to the next day.
+ */
+ if (keycode == getForwardKey() && !shift) {
+ focusNextDay(1);
+ return true;
+
+ /*
+ * Jumps to the previous day
+ */
+ } else if (keycode == getBackwardKey() && !shift) {
+ focusPreviousDay(1);
+ return true;
+
+ /*
+ * Jumps one week forward in the calendar
+ */
+ } else if (keycode == getNextKey() && !shift) {
+ focusNextDay(7);
+ return true;
+
+ /*
+ * Jumps one week back in the calendar
+ */
+ } else if (keycode == getPreviousKey() && !shift) {
+ focusPreviousDay(7);
+ return true;
+
+ /*
+ * Selects the value that is chosen
+ */
+ } else if (keycode == getSelectKey() && !shift) {
+ selectFocused();
+ onSubmit(); // submit
+ return true;
+
+ } else if (keycode == getCloseKey()) {
+ onCancel();
+ // TODO close event
+
+ return true;
+
+ /*
+ * Jumps to the next month
+ */
+ } else if (shift && keycode == getForwardKey()) {
+ focusNextMonth();
+ return true;
+
+ /*
+ * Jumps to the previous month
+ */
+ } else if (shift && keycode == getBackwardKey()) {
+ focusPreviousMonth();
+ return true;
+
+ /*
+ * Jumps to the next year
+ */
+ } else if (shift && keycode == getPreviousKey()) {
+ focusNextYear(1);
+ return true;
+
+ /*
+ * Jumps to the previous year
+ */
+ } else if (shift && keycode == getNextKey()) {
+ focusPreviousYear(1);
+ return true;
+
+ /*
+ * Resets the selection
+ */
+ } else if (keycode == getResetKey() && !shift) {
+ // Restore showing value the selected value
+ focusedDate = new FocusedDate(value.getYear(), value.getMonth(),
+ value.getDate());
+ displayedMonth = new FocusedDate(value.getYear(), value.getMonth(),
+ 1);
+ renderCalendar();
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Handles the keyboard navigation
+ *
+ * @param keycode
+ * The key code that was pressed
+ * @param ctrl
+ * Was the ctrl key pressed
+ * @param shift
+ * Was the shift key pressed
+ * @return Return true if key press was handled by the component, else
+ * return false
+ */
+ protected boolean handleNavigation(int keycode, boolean ctrl,
+ boolean shift) {
+ if (!isEnabled() || isReadonly()) {
+ return false;
+ }
+
+ else if (resolution == Resolution.YEAR) {
+ return handleNavigationYearMode(keycode, ctrl, shift);
+ }
+
+ else if (resolution == Resolution.MONTH) {
+ return handleNavigationMonthMode(keycode, ctrl, shift);
+ }
+
+ else if (resolution == Resolution.DAY) {
+ return handleNavigationDayMode(keycode, ctrl, shift);
+ }
+
+ else {
+ return handleNavigationDayMode(keycode, ctrl, shift);
+ }
+
+ }
+
+ /**
+ * Returns the reset key which will reset the calendar to the previous
+ * selection. By default this is backspace but it can be overriden to change
+ * the key to whatever you want.
+ *
+ * @return
+ */
+ protected int getResetKey() {
+ return KeyCodes.KEY_BACKSPACE;
+ }
+
+ /**
+ * Returns the select key which selects the value. By default this is the
+ * enter key but it can be changed to whatever you like by overriding this
+ * method.
+ *
+ * @return
+ */
+ protected int getSelectKey() {
+ return KeyCodes.KEY_ENTER;
+ }
+
+ /**
+ * Returns the key that closes the popup window if this is a VPopopCalendar.
+ * Else this does nothing. By default this is the Escape key but you can
+ * change the key to whatever you want by overriding this method.
+ *
+ * @return
+ */
+ protected int getCloseKey() {
+ return KeyCodes.KEY_ESCAPE;
+ }
+
+ /**
+ * The key that selects the next day in the calendar. By default this is the
+ * right arrow key but by overriding this method it can be changed to
+ * whatever you like.
+ *
+ * @return
+ */
+ protected int getForwardKey() {
+ return KeyCodes.KEY_RIGHT;
+ }
+
+ /**
+ * The key that selects the previous day in the calendar. By default this is
+ * the left arrow key but by overriding this method it can be changed to
+ * whatever you like.
+ *
+ * @return
+ */
+ protected int getBackwardKey() {
+ return KeyCodes.KEY_LEFT;
+ }
+
+ /**
+ * The key that selects the next week in the calendar. By default this is
+ * the down arrow key but by overriding this method it can be changed to
+ * whatever you like.
+ *
+ * @return
+ */
+ protected int getNextKey() {
+ return KeyCodes.KEY_DOWN;
+ }
+
+ /**
+ * The key that selects the previous week in the calendar. By default this
+ * is the up arrow key but by overriding this method it can be changed to
+ * whatever you like.
+ *
+ * @return
+ */
+ protected int getPreviousKey() {
+ return KeyCodes.KEY_UP;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.google.gwt.event.dom.client.MouseOutHandler#onMouseOut(com.google
+ * .gwt.event.dom.client.MouseOutEvent)
+ */
+ @Override
+ public void onMouseOut(MouseOutEvent event) {
+ if (mouseTimer != null) {
+ mouseTimer.cancel();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.google.gwt.event.dom.client.MouseDownHandler#onMouseDown(com.google
+ * .gwt.event.dom.client.MouseDownEvent)
+ */
+ @Override
+ public void onMouseDown(MouseDownEvent event) {
+ // Click-n-hold the left mouse button for fast-forward or fast-rewind.
+ // Timer is first used for a 500ms delay after mousedown. After that has
+ // elapsed, another timer is triggered to go off every 150ms. Both
+ // timers are cancelled on mouseup or mouseout.
+ if (event.getNativeButton() == NativeEvent.BUTTON_LEFT
+ && event.getSource() instanceof VEventButton) {
+ final VEventButton sender = (VEventButton) event.getSource();
+ processClickEvent(sender);
+ mouseTimer = new Timer() {
+ @Override
+ public void run() {
+ mouseTimer = new Timer() {
+ @Override
+ public void run() {
+ processClickEvent(sender);
+ }
+ };
+ mouseTimer.scheduleRepeating(150);
+ }
+ };
+ mouseTimer.schedule(500);
+ }
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.google.gwt.event.dom.client.MouseUpHandler#onMouseUp(com.google.gwt
+ * .event.dom.client.MouseUpEvent)
+ */
+ @Override
+ public void onMouseUp(MouseUpEvent event) {
+ if (mouseTimer != null) {
+ mouseTimer.cancel();
+ }
+ }
+
+ /**
+ * Adjusts a date to fit inside the range, only if outside
+ *
+ * @param date
+ */
+ private Date adjustDateToFitInsideRange(Date date) {
+ if (rangeStart != null && rangeStart.after(date)) {
+ date = (Date) rangeStart.clone();
+ } else if (rangeEnd != null && rangeEnd.before(date)) {
+ date = (Date) rangeEnd.clone();
+ }
+ return date;
+ }
+
+ /**
+ * Sets the data of the Panel.
+ *
+ * @param currentDate
+ * The date to set
+ */
+ public void setDate(Date currentDate) {
+
+ // Check that we are not re-rendering an already active date
+ if (currentDate == value && currentDate != null) {
+ return;
+ }
+ boolean currentDateWasAdjusted = false;
+ // Check that selected date is inside the allowed range
+ if (currentDate != null
+ && !isDateInsideRange(currentDate, resolution)) {
+ currentDate = adjustDateToFitInsideRange(currentDate);
+ currentDateWasAdjusted = true;
+ }
+
+ Date oldDisplayedMonth = displayedMonth;
+ value = currentDate;
+
+ // If current date was adjusted, we will not select any date,
+ // since that will look like a date is selected. Instead we
+ // only focus on the adjusted value
+ if (value == null || currentDateWasAdjusted) {
+ // If ranges enabled, we may need to focus on a different view to
+ // potentially not get stuck
+ if (rangeStart != null || rangeEnd != null) {
+ Date dateThatFitsInsideRange = adjustDateToFitInsideRange(
+ new Date());
+ focusedDate = new FocusedDate(dateThatFitsInsideRange.getYear(),
+ dateThatFitsInsideRange.getMonth(),
+ dateThatFitsInsideRange.getDate());
+ displayedMonth = new FocusedDate(
+ dateThatFitsInsideRange.getYear(),
+ dateThatFitsInsideRange.getMonth(), 1);
+ // value was adjusted. Set selected to null to not cause
+ // confusion, but this is only needed (and allowed) when we have
+ // a day
+ // resolution
+ if (getResolution().getCalendarField() >= Resolution.DAY
+ .getCalendarField()) {
+ value = null;
+ }
+ } else {
+ focusedDate = displayedMonth = null;
+ }
+ } else {
+ focusedDate = new FocusedDate(value.getYear(), value.getMonth(),
+ value.getDate());
+ displayedMonth = new FocusedDate(value.getYear(), value.getMonth(),
+ 1);
+ }
+
+ // Re-render calendar if the displayed month is changed,
+ // or if a time selector is needed but does not exist.
+ if ((isTimeSelectorNeeded() && time == null)
+ || oldDisplayedMonth == null || value == null
+ || oldDisplayedMonth.getYear() != value.getYear()
+ || oldDisplayedMonth.getMonth() != value.getMonth()) {
+ renderCalendar();
+ } else {
+ focusDay(focusedDate);
+ selectFocused();
+ if (isTimeSelectorNeeded()) {
+ time.updateTimes();
+ }
+ }
+
+ if (!hasFocus) {
+ focusDay(null);
+ }
+ }
+
+ /**
+ * TimeSelector is a widget consisting of list boxes that modifie the Date
+ * object that is given for.
+ *
+ */
+ public class VTime extends FlowPanel implements ChangeHandler {
+
+ private ListBox hours;
+
+ private ListBox mins;
+
+ private ListBox sec;
+
+ private ListBox ampm;
+
+ /**
+ * Constructor
+ */
+ public VTime() {
+ super();
+ setStyleName(VDateField.CLASSNAME + "-time");
+ buildTime();
+ }
+
+ private ListBox createListBox() {
+ ListBox lb = new ListBox();
+ lb.setStyleName("v-select");
+ lb.addChangeHandler(this);
+ lb.addBlurHandler(VCalendarPanel.this);
+ lb.addFocusHandler(VCalendarPanel.this);
+ return lb;
+ }
+
+ /**
+ * Constructs the ListBoxes and updates their value
+ *
+ * @param redraw
+ * Should new instances of the listboxes be created
+ */
+ private void buildTime() {
+ clear();
+
+ hours = createListBox();
+ if (getDateTimeService().isTwelveHourClock()) {
+ hours.addItem("12");
+ for (int i = 1; i < 12; i++) {
+ hours.addItem((i < 10) ? "0" + i : "" + i);
+ }
+ } else {
+ for (int i = 0; i < 24; i++) {
+ hours.addItem((i < 10) ? "0" + i : "" + i);
+ }
+ }
+
+ hours.addChangeHandler(this);
+ if (getDateTimeService().isTwelveHourClock()) {
+ ampm = createListBox();
+ final String[] ampmText = getDateTimeService().getAmPmStrings();
+ ampm.addItem(ampmText[0]);
+ ampm.addItem(ampmText[1]);
+ ampm.addChangeHandler(this);
+ }
+
+ if (getResolution().getCalendarField() >= Resolution.MINUTE
+ .getCalendarField()) {
+ mins = createListBox();
+ for (int i = 0; i < 60; i++) {
+ mins.addItem((i < 10) ? "0" + i : "" + i);
+ }
+ mins.addChangeHandler(this);
+ }
+ if (getResolution().getCalendarField() >= Resolution.SECOND
+ .getCalendarField()) {
+ sec = createListBox();
+ for (int i = 0; i < 60; i++) {
+ sec.addItem((i < 10) ? "0" + i : "" + i);
+ }
+ sec.addChangeHandler(this);
+ }
+
+ final String delimiter = getDateTimeService().getClockDelimeter();
+ if (isReadonly()) {
+ int h = 0;
+ if (value != null) {
+ h = value.getHours();
+ }
+ if (getDateTimeService().isTwelveHourClock()) {
+ h -= h < 12 ? 0 : 12;
+ }
+ add(new VLabel(h < 10 ? "0" + h : "" + h));
+ } else {
+ add(hours);
+ }
+
+ if (getResolution().getCalendarField() >= Resolution.MINUTE
+ .getCalendarField()) {
+ add(new VLabel(delimiter));
+ if (isReadonly()) {
+ final int m = mins.getSelectedIndex();
+ add(new VLabel(m < 10 ? "0" + m : "" + m));
+ } else {
+ add(mins);
+ }
+ }
+ if (getResolution().getCalendarField() >= Resolution.SECOND
+ .getCalendarField()) {
+ add(new VLabel(delimiter));
+ if (isReadonly()) {
+ final int s = sec.getSelectedIndex();
+ add(new VLabel(s < 10 ? "0" + s : "" + s));
+ } else {
+ add(sec);
+ }
+ }
+ if (getResolution() == Resolution.HOUR) {
+ add(new VLabel(delimiter + "00")); // o'clock
+ }
+ if (getDateTimeService().isTwelveHourClock()) {
+ add(new VLabel("&nbsp;"));
+ if (isReadonly()) {
+ int i = 0;
+ if (value != null) {
+ i = (value.getHours() < 12) ? 0 : 1;
+ }
+ add(new VLabel(ampm.getItemText(i)));
+ } else {
+ add(ampm);
+ }
+ }
+
+ if (isReadonly()) {
+ return;
+ }
+
+ // Update times
+ updateTimes();
+
+ ListBox lastDropDown = getLastDropDown();
+ lastDropDown.addKeyDownHandler(new KeyDownHandler() {
+ @Override
+ public void onKeyDown(KeyDownEvent event) {
+ boolean shiftKey = event.getNativeEvent().getShiftKey();
+ if (shiftKey) {
+ return;
+ } else {
+ int nativeKeyCode = event.getNativeKeyCode();
+ if (nativeKeyCode == KeyCodes.KEY_TAB) {
+ onTabOut(event);
+ }
+ }
+ }
+ });
+
+ }
+
+ private ListBox getLastDropDown() {
+ int i = getWidgetCount() - 1;
+ while (i >= 0) {
+ Widget widget = getWidget(i);
+ if (widget instanceof ListBox) {
+ return (ListBox) widget;
+ }
+ i--;
+ }
+ return null;
+ }
+
+ /**
+ * Updates the valus to correspond to the values in value
+ */
+ public void updateTimes() {
+ if (value == null) {
+ value = new Date();
+ }
+ if (getDateTimeService().isTwelveHourClock()) {
+ int h = value.getHours();
+ ampm.setSelectedIndex(h < 12 ? 0 : 1);
+ h -= ampm.getSelectedIndex() * 12;
+ hours.setSelectedIndex(h);
+ } else {
+ hours.setSelectedIndex(value.getHours());
+ }
+ if (getResolution().getCalendarField() >= Resolution.MINUTE
+ .getCalendarField()) {
+ mins.setSelectedIndex(value.getMinutes());
+ }
+ if (getResolution().getCalendarField() >= Resolution.SECOND
+ .getCalendarField()) {
+ sec.setSelectedIndex(value.getSeconds());
+ }
+ if (getDateTimeService().isTwelveHourClock()) {
+ ampm.setSelectedIndex(value.getHours() < 12 ? 0 : 1);
+ }
+
+ hours.setEnabled(isEnabled());
+ if (mins != null) {
+ mins.setEnabled(isEnabled());
+ }
+ if (sec != null) {
+ sec.setEnabled(isEnabled());
+ }
+ if (ampm != null) {
+ ampm.setEnabled(isEnabled());
+ }
+
+ }
+
+ private DateTimeService getDateTimeService() {
+ if (dateTimeService == null) {
+ dateTimeService = new DateTimeService();
+ }
+ return dateTimeService;
+ }
+
+ /*
+ * (non-Javadoc) VT
+ *
+ * @see
+ * com.google.gwt.event.dom.client.ChangeHandler#onChange(com.google.gwt
+ * .event.dom.client.ChangeEvent)
+ */
+ @Override
+ public void onChange(ChangeEvent event) {
+ /*
+ * Value from dropdowns gets always set for the value. Like year and
+ * month when resolution is month or year.
+ */
+ if (event.getSource() == hours) {
+ int h = hours.getSelectedIndex();
+ if (getDateTimeService().isTwelveHourClock()) {
+ h = h + ampm.getSelectedIndex() * 12;
+ }
+ value.setHours(h);
+ if (timeChangeListener != null) {
+ timeChangeListener.changed(h, value.getMinutes(),
+ value.getSeconds(),
+ DateTimeService.getMilliseconds(value));
+ }
+ event.preventDefault();
+ event.stopPropagation();
+ } else if (event.getSource() == mins) {
+ final int m = mins.getSelectedIndex();
+ value.setMinutes(m);
+ if (timeChangeListener != null) {
+ timeChangeListener.changed(value.getHours(), m,
+ value.getSeconds(),
+ DateTimeService.getMilliseconds(value));
+ }
+ event.preventDefault();
+ event.stopPropagation();
+ } else if (event.getSource() == sec) {
+ final int s = sec.getSelectedIndex();
+ value.setSeconds(s);
+ if (timeChangeListener != null) {
+ timeChangeListener.changed(value.getHours(),
+ value.getMinutes(), s,
+ DateTimeService.getMilliseconds(value));
+ }
+ event.preventDefault();
+ event.stopPropagation();
+ } else if (event.getSource() == ampm) {
+ final int h = hours.getSelectedIndex()
+ + (ampm.getSelectedIndex() * 12);
+ value.setHours(h);
+ if (timeChangeListener != null) {
+ timeChangeListener.changed(h, value.getMinutes(),
+ value.getSeconds(),
+ DateTimeService.getMilliseconds(value));
+ }
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+
+ }
+
+ /**
+ * A widget representing a single day in the calendar panel.
+ */
+ private class Day extends InlineHTML {
+ private final Date date;
+
+ Day(Date date) {
+ super("" + date.getDate());
+ this.date = date;
+ addClickHandler(dayClickHandler);
+ }
+
+ public Date getDate() {
+ return date;
+ }
+ }
+
+ public Date getDate() {
+ return value;
+ }
+
+ /**
+ * If true should be returned if the panel will not be used after this
+ * event.
+ *
+ * @param event
+ * @return
+ */
+ protected boolean onTabOut(DomEvent<?> event) {
+ if (focusOutListener != null) {
+ return focusOutListener.onFocusOut(event);
+ }
+ return false;
+ }
+
+ /**
+ * A focus out listener is triggered when the panel loosed focus. This can
+ * happen either after a user clicks outside the panel or tabs out.
+ *
+ * @param listener
+ * The listener to trigger
+ */
+ public void setFocusOutListener(FocusOutListener listener) {
+ focusOutListener = listener;
+ }
+
+ /**
+ * The submit listener is called when the user selects a value from the
+ * calender either by clicking the day or selects it by keyboard.
+ *
+ * @param submitListener
+ * The listener to trigger
+ */
+ public void setSubmitListener(SubmitListener submitListener) {
+ this.submitListener = submitListener;
+ }
+
+ /**
+ * The given FocusChangeListener is notified when the focused date changes
+ * by user either clicking on a new date or by using the keyboard.
+ *
+ * @param listener
+ * The FocusChangeListener to be notified
+ */
+ public void setFocusChangeListener(FocusChangeListener listener) {
+ focusChangeListener = listener;
+ }
+
+ /**
+ * The time change listener is triggered when the user changes the time.
+ *
+ * @param listener
+ */
+ public void setTimeChangeListener(TimeChangeListener listener) {
+ timeChangeListener = listener;
+ }
+
+ /**
+ * Returns the submit listener that listens to selection made from the panel
+ *
+ * @return The listener or NULL if no listener has been set
+ */
+ public SubmitListener getSubmitListener() {
+ return submitListener;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.google.gwt.event.dom.client.BlurHandler#onBlur(com.google.gwt.event
+ * .dom.client.BlurEvent)
+ */
+ @Override
+ public void onBlur(final BlurEvent event) {
+ if (event.getSource() instanceof VCalendarPanel) {
+ hasFocus = false;
+ focusDay(null);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.google.gwt.event.dom.client.FocusHandler#onFocus(com.google.gwt.event
+ * .dom.client.FocusEvent)
+ */
+ @Override
+ public void onFocus(FocusEvent event) {
+ if (event.getSource() instanceof VCalendarPanel) {
+ hasFocus = true;
+
+ // Focuses the current day if the calendar shows the days
+ if (focusedDay != null) {
+ focusDay(focusedDate);
+ }
+ }
+ }
+
+ private static final String SUBPART_NEXT_MONTH = "nextmon";
+ private static final String SUBPART_PREV_MONTH = "prevmon";
+
+ private static final String SUBPART_NEXT_YEAR = "nexty";
+ private static final String SUBPART_PREV_YEAR = "prevy";
+ private static final String SUBPART_HOUR_SELECT = "h";
+ private static final String SUBPART_MINUTE_SELECT = "m";
+ private static final String SUBPART_SECS_SELECT = "s";
+ private static final String SUBPART_AMPM_SELECT = "ampm";
+ private static final String SUBPART_DAY = "day";
+ private static final String SUBPART_MONTH_YEAR_HEADER = "header";
+
+ private Date rangeStart;
+
+ private Date rangeEnd;
+
+ @Override
+ public String getSubPartName(
+ com.google.gwt.user.client.Element subElement) {
+ if (contains(nextMonth, subElement)) {
+ return SUBPART_NEXT_MONTH;
+ } else if (contains(prevMonth, subElement)) {
+ return SUBPART_PREV_MONTH;
+ } else if (contains(nextYear, subElement)) {
+ return SUBPART_NEXT_YEAR;
+ } else if (contains(prevYear, subElement)) {
+ return SUBPART_PREV_YEAR;
+ } else if (contains(days, subElement)) {
+ // Day, find out which dayOfMonth and use that as the identifier
+ Day day = WidgetUtil.findWidget(subElement, Day.class);
+ if (day != null) {
+ Date date = day.getDate();
+ int id = date.getDate();
+ // Zero or negative ids map to days of the preceding month,
+ // past-the-end-of-month ids to days of the following month
+ if (date.getMonth() < displayedMonth.getMonth()) {
+ id -= DateTimeService.getNumberOfDaysInMonth(date);
+ } else if (date.getMonth() > displayedMonth.getMonth()) {
+ id += DateTimeService
+ .getNumberOfDaysInMonth(displayedMonth);
+ }
+ return SUBPART_DAY + id;
+ }
+ } else if (time != null) {
+ if (contains(time.hours, subElement)) {
+ return SUBPART_HOUR_SELECT;
+ } else if (contains(time.mins, subElement)) {
+ return SUBPART_MINUTE_SELECT;
+ } else if (contains(time.sec, subElement)) {
+ return SUBPART_SECS_SELECT;
+ } else if (contains(time.ampm, subElement)) {
+ return SUBPART_AMPM_SELECT;
+
+ }
+ } else if (getCellFormatter().getElement(0, 2)
+ .isOrHasChild(subElement)) {
+ return SUBPART_MONTH_YEAR_HEADER;
+ }
+
+ return null;
+ }
+
+ /**
+ * Checks if subElement is inside the widget DOM hierarchy.
+ *
+ * @param w
+ * @param subElement
+ * @return true if {@code w} is a parent of subElement, false otherwise.
+ */
+ private boolean contains(Widget w, Element subElement) {
+ if (w == null || w.getElement() == null) {
+ return false;
+ }
+
+ return w.getElement().isOrHasChild(subElement);
+ }
+
+ @Override
+ public com.google.gwt.user.client.Element getSubPartElement(
+ String subPart) {
+ if (SUBPART_NEXT_MONTH.equals(subPart)) {
+ return nextMonth.getElement();
+ }
+ if (SUBPART_PREV_MONTH.equals(subPart)) {
+ return prevMonth.getElement();
+ }
+ if (SUBPART_NEXT_YEAR.equals(subPart)) {
+ return nextYear.getElement();
+ }
+ if (SUBPART_PREV_YEAR.equals(subPart)) {
+ return prevYear.getElement();
+ }
+ if (SUBPART_HOUR_SELECT.equals(subPart)) {
+ return time.hours.getElement();
+ }
+ if (SUBPART_MINUTE_SELECT.equals(subPart)) {
+ return time.mins.getElement();
+ }
+ if (SUBPART_SECS_SELECT.equals(subPart)) {
+ return time.sec.getElement();
+ }
+ if (SUBPART_AMPM_SELECT.equals(subPart)) {
+ return time.ampm.getElement();
+ }
+ if (subPart.startsWith(SUBPART_DAY)) {
+ // Zero or negative ids map to days in the preceding month,
+ // past-the-end-of-month ids to days in the following month
+ int dayOfMonth = Integer
+ .parseInt(subPart.substring(SUBPART_DAY.length()));
+ Date date = new Date(displayedMonth.getYear(),
+ displayedMonth.getMonth(), dayOfMonth);
+ Iterator<Widget> iter = days.iterator();
+ while (iter.hasNext()) {
+ Widget w = iter.next();
+ if (w instanceof Day) {
+ Day day = (Day) w;
+ if (day.getDate().equals(date)) {
+ return day.getElement();
+ }
+ }
+ }
+ }
+
+ if (SUBPART_MONTH_YEAR_HEADER.equals(subPart)) {
+ return DOM.asOld(
+ (Element) getCellFormatter().getElement(0, 2).getChild(0));
+ }
+ return null;
+ }
+
+ @Override
+ protected void onDetach() {
+ super.onDetach();
+ if (mouseTimer != null) {
+ mouseTimer.cancel();
+ }
+ }
+
+ /**
+ * Helper class to inform the screen reader that the user changed the
+ * selected date. It sets the value of a field that is outside the view, and
+ * is defined as a live area. That way the screen reader recognizes the
+ * change and reads it to the user.
+ */
+ public class FocusedDate extends Date {
+
+ public FocusedDate(int year, int month, int date) {
+ super(year, month, date);
+ }
+
+ @Override
+ public void setTime(long time) {
+ super.setTime(time);
+ setLabel();
+ }
+
+ @Override
+ @Deprecated
+ public void setDate(int date) {
+ super.setDate(date);
+ setLabel();
+ }
+
+ @Override
+ @Deprecated
+ public void setMonth(int month) {
+ super.setMonth(month);
+ setLabel();
+ }
+
+ @Override
+ @Deprecated
+ public void setYear(int year) {
+ super.setYear(year);
+ setLabel();
+ }
+
+ private void setLabel() {
+ if (parent instanceof VPopupCalendar) {
+ ((VPopupCalendar) parent).setFocusedDate(this);
+ }
+ }
+ }
+
+ /**
+ * Sets the start range for this component. The start range is inclusive,
+ * and it depends on the current resolution, what is considered inside the
+ * range.
+ *
+ * @param startDate
+ * - the allowed range's start date
+ */
+ public void setRangeStart(Date newRangeStart) {
+ if (!SharedUtil.equals(rangeStart, newRangeStart)) {
+ rangeStart = newRangeStart;
+ if (initialRenderDone) {
+ // Dynamic updates to the range needs to render the calendar to
+ // update the element stylenames
+ renderCalendar();
+ }
+ }
+
+ }
+
+ /**
+ * Sets the end range for this component. The end range is inclusive, and it
+ * depends on the current resolution, what is considered inside the range.
+ *
+ * @param endDate
+ * - the allowed range's end date
+ */
+ public void setRangeEnd(Date newRangeEnd) {
+ if (!SharedUtil.equals(rangeEnd, newRangeEnd)) {
+ rangeEnd = newRangeEnd;
+ if (initialRenderDone) {
+ // Dynamic updates to the range needs to render the calendar to
+ // update the element stylenames
+ renderCalendar();
+ }
+ }
+ }
+}
diff --git a/compatibility-client/src/main/java/com/vaadin/v7/client/ui/VDateField.java b/compatibility-client/src/main/java/com/vaadin/v7/client/ui/VDateField.java
new file mode 100644
index 0000000000..eca4273e24
--- /dev/null
+++ b/compatibility-client/src/main/java/com/vaadin/v7/client/ui/VDateField.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * 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.vaadin.v7.client.ui;
+
+import java.util.Date;
+
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.HasEnabled;
+import com.vaadin.client.ApplicationConnection;
+import com.vaadin.client.DateTimeService;
+import com.vaadin.client.ui.Field;
+import com.vaadin.shared.ui.datefield.Resolution;
+
+public class VDateField extends FlowPanel implements Field, HasEnabled {
+
+ public static final String CLASSNAME = "v-datefield";
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public String paintableId;
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public ApplicationConnection client;
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public boolean immediate;
+
+ @Deprecated
+ public static final Resolution RESOLUTION_YEAR = Resolution.YEAR;
+ @Deprecated
+ public static final Resolution RESOLUTION_MONTH = Resolution.MONTH;
+ @Deprecated
+ public static final Resolution RESOLUTION_DAY = Resolution.DAY;
+ @Deprecated
+ public static final Resolution RESOLUTION_HOUR = Resolution.HOUR;
+ @Deprecated
+ public static final Resolution RESOLUTION_MIN = Resolution.MINUTE;
+ @Deprecated
+ public static final Resolution RESOLUTION_SEC = Resolution.SECOND;
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public static String resolutionToString(Resolution res) {
+ if (res.getCalendarField() > Resolution.DAY.getCalendarField()) {
+ return "full";
+ }
+ if (res == Resolution.DAY) {
+ return "day";
+ }
+ if (res == Resolution.MONTH) {
+ return "month";
+ }
+ return "year";
+ }
+
+ protected Resolution currentResolution = Resolution.YEAR;
+
+ protected String currentLocale;
+
+ protected boolean readonly;
+
+ protected boolean enabled;
+
+ /**
+ * The date that is selected in the date field. Null if an invalid date is
+ * specified.
+ */
+ private Date date = null;
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public DateTimeService dts;
+
+ protected boolean showISOWeekNumbers = false;
+
+ public VDateField() {
+ setStyleName(CLASSNAME);
+ dts = new DateTimeService();
+ }
+
+ /**
+ * We need this redundant native function because Java's Date object doesn't
+ * have a setMilliseconds method.
+ * <p>
+ * For internal use only. May be removed or replaced in the future.
+ */
+ public static native double getTime(int y, int m, int d, int h, int mi,
+ int s, int ms)
+ /*-{
+ try {
+ var date = new Date(2000,1,1,1); // don't use current date here
+ if(y && y >= 0) date.setFullYear(y);
+ if(m && m >= 1) date.setMonth(m-1);
+ if(d && d >= 0) date.setDate(d);
+ if(h >= 0) date.setHours(h);
+ if(mi >= 0) date.setMinutes(mi);
+ if(s >= 0) date.setSeconds(s);
+ if(ms >= 0) date.setMilliseconds(ms);
+ return date.getTime();
+ } catch (e) {
+ // TODO print some error message on the console
+ //console.log(e);
+ return (new Date()).getTime();
+ }
+ }-*/;
+
+ public int getMilliseconds() {
+ return DateTimeService.getMilliseconds(date);
+ }
+
+ public void setMilliseconds(int ms) {
+ DateTimeService.setMilliseconds(date, ms);
+ }
+
+ public Resolution getCurrentResolution() {
+ return currentResolution;
+ }
+
+ public void setCurrentResolution(Resolution currentResolution) {
+ this.currentResolution = currentResolution;
+ }
+
+ public String getCurrentLocale() {
+ return currentLocale;
+ }
+
+ public void setCurrentLocale(String currentLocale) {
+ this.currentLocale = currentLocale;
+ }
+
+ public Date getCurrentDate() {
+ return date;
+ }
+
+ public void setCurrentDate(Date date) {
+ this.date = date;
+ }
+
+ public boolean isImmediate() {
+ return immediate;
+ }
+
+ public void setImmediate(boolean immediate) {
+ this.immediate = immediate;
+ }
+
+ public boolean isReadonly() {
+ return readonly;
+ }
+
+ public void setReadonly(boolean readonly) {
+ this.readonly = readonly;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public DateTimeService getDateTimeService() {
+ return dts;
+ }
+
+ public String getId() {
+ return paintableId;
+ }
+
+ public ApplicationConnection getClient() {
+ return client;
+ }
+
+ /**
+ * Returns whether ISO 8601 week numbers should be shown in the date
+ * selector or not. ISO 8601 defines that a week always starts with a Monday
+ * so the week numbers are only shown if this is the case.
+ *
+ * @return true if week number should be shown, false otherwise
+ */
+ public boolean isShowISOWeekNumbers() {
+ return showISOWeekNumbers;
+ }
+
+ public void setShowISOWeekNumbers(boolean showISOWeekNumbers) {
+ this.showISOWeekNumbers = showISOWeekNumbers;
+ }
+
+ /**
+ * Returns a copy of the current date. Modifying the returned date will not
+ * modify the value of this VDateField. Use {@link #setDate(Date)} to change
+ * the current date.
+ * <p>
+ * For internal use only. May be removed or replaced in the future.
+ *
+ * @return A copy of the current date
+ */
+ public Date getDate() {
+ Date current = getCurrentDate();
+ if (current == null) {
+ return null;
+ } else {
+ return (Date) getCurrentDate().clone();
+ }
+ }
+
+ /**
+ * Sets the current date for this VDateField.
+ *
+ * @param date
+ * The new date to use
+ */
+ protected void setDate(Date date) {
+ this.date = date;
+ }
+}
diff --git a/compatibility-client/src/main/java/com/vaadin/v7/client/ui/VDateFieldCalendar.java b/compatibility-client/src/main/java/com/vaadin/v7/client/ui/VDateFieldCalendar.java
new file mode 100644
index 0000000000..16f6b90a5d
--- /dev/null
+++ b/compatibility-client/src/main/java/com/vaadin/v7/client/ui/VDateFieldCalendar.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * 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.vaadin.v7.client.ui;
+
+import java.util.Date;
+
+import com.google.gwt.event.dom.client.DomEvent;
+import com.vaadin.client.DateTimeService;
+import com.vaadin.shared.ui.datefield.Resolution;
+import com.vaadin.v7.client.ui.VCalendarPanel.FocusOutListener;
+import com.vaadin.v7.client.ui.VCalendarPanel.SubmitListener;
+
+/**
+ * A client side implementation for InlineDateField
+ */
+public class VDateFieldCalendar extends VDateField {
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public final VCalendarPanel calendarPanel;
+
+ public VDateFieldCalendar() {
+ super();
+ calendarPanel = new VCalendarPanel();
+ calendarPanel.setParentField(this);
+ add(calendarPanel);
+ calendarPanel.setSubmitListener(new SubmitListener() {
+ @Override
+ public void onSubmit() {
+ updateValueFromPanel();
+ }
+
+ @Override
+ public void onCancel() {
+ // TODO Auto-generated method stub
+
+ }
+ });
+ calendarPanel.setFocusOutListener(new FocusOutListener() {
+ @Override
+ public boolean onFocusOut(DomEvent<?> event) {
+ updateValueFromPanel();
+ return false;
+ }
+ });
+ }
+
+ /**
+ * TODO refactor: almost same method as in VPopupCalendar.updateValue
+ * <p>
+ * For internal use only. May be removed or replaced in the future.
+ */
+
+ @SuppressWarnings("deprecation")
+ public void updateValueFromPanel() {
+
+ // If field is invisible at the beginning, client can still be null when
+ // this function is called.
+ if (getClient() == null) {
+ return;
+ }
+
+ Date date2 = calendarPanel.getDate();
+ Date currentDate = getCurrentDate();
+ if (currentDate == null || date2.getTime() != currentDate.getTime()) {
+ setCurrentDate((Date) date2.clone());
+ getClient().updateVariable(getId(), "year", date2.getYear() + 1900,
+ false);
+ if (getCurrentResolution().getCalendarField() > Resolution.YEAR
+ .getCalendarField()) {
+ getClient().updateVariable(getId(), "month",
+ date2.getMonth() + 1, false);
+ if (getCurrentResolution().getCalendarField() > Resolution.MONTH
+ .getCalendarField()) {
+ getClient().updateVariable(getId(), "day", date2.getDate(),
+ false);
+ if (getCurrentResolution()
+ .getCalendarField() > Resolution.DAY
+ .getCalendarField()) {
+ getClient().updateVariable(getId(), "hour",
+ date2.getHours(), false);
+ if (getCurrentResolution()
+ .getCalendarField() > Resolution.HOUR
+ .getCalendarField()) {
+ getClient().updateVariable(getId(), "min",
+ date2.getMinutes(), false);
+ if (getCurrentResolution()
+ .getCalendarField() > Resolution.MINUTE
+ .getCalendarField()) {
+ getClient().updateVariable(getId(), "sec",
+ date2.getSeconds(), false);
+ if (getCurrentResolution()
+ .getCalendarField() > Resolution.SECOND
+ .getCalendarField()) {
+ getClient().updateVariable(getId(),
+ "msec", DateTimeService
+ .getMilliseconds(date2),
+ false);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (isImmediate()) {
+ getClient().sendPendingVariableChanges();
+ }
+ }
+ }
+
+ public void setTabIndex(int tabIndex) {
+ calendarPanel.getElement().setTabIndex(tabIndex);
+ }
+
+ public int getTabIndex() {
+ return calendarPanel.getElement().getTabIndex();
+ }
+}
diff --git a/compatibility-client/src/main/java/com/vaadin/v7/client/ui/VPopupCalendar.java b/compatibility-client/src/main/java/com/vaadin/v7/client/ui/VPopupCalendar.java
new file mode 100644
index 0000000000..a21cfea4ab
--- /dev/null
+++ b/compatibility-client/src/main/java/com/vaadin/v7/client/ui/VPopupCalendar.java
@@ -0,0 +1,745 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * 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.vaadin.v7.client.ui;
+
+import java.util.Date;
+
+import com.google.gwt.aria.client.Id;
+import com.google.gwt.aria.client.LiveValue;
+import com.google.gwt.aria.client.Roles;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.DomEvent;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.event.dom.client.MouseOutEvent;
+import com.google.gwt.event.dom.client.MouseOutHandler;
+import com.google.gwt.event.dom.client.MouseOverEvent;
+import com.google.gwt.event.dom.client.MouseOverHandler;
+import com.google.gwt.event.logical.shared.CloseEvent;
+import com.google.gwt.event.logical.shared.CloseHandler;
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.PopupPanel;
+import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.client.BrowserInfo;
+import com.vaadin.client.ComputedStyle;
+import com.vaadin.client.VConsole;
+import com.vaadin.client.ui.Field;
+import com.vaadin.client.ui.SubPartAware;
+import com.vaadin.client.ui.VOverlay;
+import com.vaadin.client.ui.aria.AriaHelper;
+import com.vaadin.shared.ui.datefield.Resolution;
+import com.vaadin.v7.client.ui.VCalendarPanel.FocusOutListener;
+import com.vaadin.v7.client.ui.VCalendarPanel.SubmitListener;
+import com.vaadin.v7.shared.ui.datefield.PopupDateFieldState;
+
+/**
+ * Represents a date selection component with a text field and a popup date
+ * selector.
+ *
+ * <b>Note:</b> To change the keyboard assignments used in the popup dialog you
+ * should extend <code>com.vaadin.client.ui.VCalendarPanel</code> and then pass
+ * set it by calling the <code>setCalendarPanel(VCalendarPanel panel)</code>
+ * method.
+ *
+ */
+public class VPopupCalendar extends VTextualDate
+ implements Field, ClickHandler, CloseHandler<PopupPanel>, SubPartAware {
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public final Button calendarToggle = new Button();
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public VCalendarPanel calendar;
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public final VOverlay popup;
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public boolean parsable = true;
+
+ private boolean open = false;
+
+ /*
+ * #14857: If calendarToggle button is clicked when calendar popup is
+ * already open we should prevent calling openCalendarPanel() in onClick,
+ * since we don't want to reopen it again right after it closes.
+ */
+ private boolean preventOpenPopupCalendar = false;
+ private boolean cursorOverCalendarToggleButton = false;
+ private boolean toggleButtonClosesWithGuarantee = false;
+
+ private boolean textFieldEnabled = true;
+
+ private String captionId;
+
+ private Label selectedDate;
+
+ private Element descriptionForAssisitveDevicesElement;
+
+ public VPopupCalendar() {
+ super();
+
+ calendarToggle.setText("");
+ calendarToggle.addClickHandler(this);
+
+ calendarToggle.addDomHandler(new MouseOverHandler() {
+ @Override
+ public void onMouseOver(MouseOverEvent event) {
+ cursorOverCalendarToggleButton = true;
+ }
+ }, MouseOverEvent.getType());
+
+ calendarToggle.addDomHandler(new MouseOutHandler() {
+ @Override
+ public void onMouseOut(MouseOutEvent event) {
+ cursorOverCalendarToggleButton = false;
+ }
+ }, MouseOutEvent.getType());
+
+ // -2 instead of -1 to avoid FocusWidget.onAttach to reset it
+ calendarToggle.getElement().setTabIndex(-2);
+
+ Roles.getButtonRole().set(calendarToggle.getElement());
+ Roles.getButtonRole().setAriaHiddenState(calendarToggle.getElement(),
+ true);
+
+ add(calendarToggle);
+
+ // Description of the usage of the widget for assisitve device users
+ descriptionForAssisitveDevicesElement = DOM.createDiv();
+ descriptionForAssisitveDevicesElement.setInnerText(
+ PopupDateFieldState.DESCRIPTION_FOR_ASSISTIVE_DEVICES);
+ AriaHelper.ensureHasId(descriptionForAssisitveDevicesElement);
+ Roles.getTextboxRole().setAriaDescribedbyProperty(text.getElement(),
+ Id.of(descriptionForAssisitveDevicesElement));
+ AriaHelper.setVisibleForAssistiveDevicesOnly(
+ descriptionForAssisitveDevicesElement, true);
+
+ calendar = GWT.create(VCalendarPanel.class);
+ calendar.setParentField(this);
+ calendar.setFocusOutListener(new FocusOutListener() {
+ @Override
+ public boolean onFocusOut(DomEvent<?> event) {
+ event.preventDefault();
+ closeCalendarPanel();
+ return true;
+ }
+ });
+
+ // FIXME: Problem is, that the element with the provided id does not
+ // exist yet in html. This is the same problem as with the context menu.
+ // Apply here the same fix (#11795)
+ Roles.getTextboxRole().setAriaControlsProperty(text.getElement(),
+ Id.of(calendar.getElement()));
+ Roles.getButtonRole().setAriaControlsProperty(
+ calendarToggle.getElement(), Id.of(calendar.getElement()));
+
+ calendar.setSubmitListener(new SubmitListener() {
+ @Override
+ public void onSubmit() {
+ // Update internal value and send valuechange event if immediate
+ updateValue(calendar.getDate());
+
+ // Update text field (a must when not immediate).
+ buildDate(true);
+
+ closeCalendarPanel();
+ }
+
+ @Override
+ public void onCancel() {
+ closeCalendarPanel();
+ }
+ });
+
+ popup = new VOverlay(true, false);
+ popup.setOwner(this);
+
+ FlowPanel wrapper = new FlowPanel();
+ selectedDate = new Label();
+ selectedDate.setStyleName(getStylePrimaryName() + "-selecteddate");
+ AriaHelper.setVisibleForAssistiveDevicesOnly(selectedDate.getElement(),
+ true);
+
+ Roles.getTextboxRole().setAriaLiveProperty(selectedDate.getElement(),
+ LiveValue.ASSERTIVE);
+ Roles.getTextboxRole().setAriaAtomicProperty(selectedDate.getElement(),
+ true);
+ wrapper.add(selectedDate);
+ wrapper.add(calendar);
+
+ popup.setWidget(wrapper);
+ popup.addCloseHandler(this);
+
+ DOM.setElementProperty(calendar.getElement(), "id",
+ "PID_VAADIN_POPUPCAL");
+
+ sinkEvents(Event.ONKEYDOWN);
+
+ updateStyleNames();
+ }
+
+ @Override
+ protected void onAttach() {
+ super.onAttach();
+ DOM.appendChild(RootPanel.get().getElement(),
+ descriptionForAssisitveDevicesElement);
+ }
+
+ @Override
+ protected void onDetach() {
+ super.onDetach();
+ descriptionForAssisitveDevicesElement.removeFromParent();
+ closeCalendarPanel();
+ }
+
+ @SuppressWarnings("deprecation")
+ public void updateValue(Date newDate) {
+ Date currentDate = getCurrentDate();
+ if (currentDate == null || newDate.getTime() != currentDate.getTime()) {
+ setCurrentDate((Date) newDate.clone());
+ getClient().updateVariable(getId(), "year",
+ newDate.getYear() + 1900, false);
+ if (getCurrentResolution().getCalendarField() > Resolution.YEAR
+ .getCalendarField()) {
+ getClient().updateVariable(getId(), "month",
+ newDate.getMonth() + 1, false);
+ if (getCurrentResolution().getCalendarField() > Resolution.MONTH
+ .getCalendarField()) {
+ getClient().updateVariable(getId(), "day",
+ newDate.getDate(), false);
+ if (getCurrentResolution()
+ .getCalendarField() > Resolution.DAY
+ .getCalendarField()) {
+ getClient().updateVariable(getId(), "hour",
+ newDate.getHours(), false);
+ if (getCurrentResolution()
+ .getCalendarField() > Resolution.HOUR
+ .getCalendarField()) {
+ getClient().updateVariable(getId(), "min",
+ newDate.getMinutes(), false);
+ if (getCurrentResolution()
+ .getCalendarField() > Resolution.MINUTE
+ .getCalendarField()) {
+ getClient().updateVariable(getId(), "sec",
+ newDate.getSeconds(), false);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks whether the text field is enabled.
+ *
+ * @see VPopupCalendar#setTextFieldEnabled(boolean)
+ * @return The current state of the text field.
+ */
+ public boolean isTextFieldEnabled() {
+ return textFieldEnabled;
+ }
+
+ /**
+ * Sets the state of the text field of this component. By default the text
+ * field is enabled. Disabling it causes only the button for date selection
+ * to be active, thus preventing the user from entering invalid dates. See
+ * {@link http://dev.vaadin.com/ticket/6790}.
+ *
+ * @param state
+ */
+ public void setTextFieldEnabled(boolean textFieldEnabled) {
+ this.textFieldEnabled = textFieldEnabled;
+ updateTextFieldEnabled();
+ }
+
+ protected void updateTextFieldEnabled() {
+ boolean reallyEnabled = isEnabled() && isTextFieldEnabled();
+ // IE has a non input disabled themeing that can not be overridden so we
+ // must fake the functionality using readonly and unselectable
+ if (BrowserInfo.get().isIE()) {
+ if (!reallyEnabled) {
+ text.getElement().setAttribute("unselectable", "on");
+ text.getElement().setAttribute("readonly", "");
+ text.setTabIndex(-2);
+ } else if (reallyEnabled
+ && text.getElement().hasAttribute("unselectable")) {
+ text.getElement().removeAttribute("unselectable");
+ text.getElement().removeAttribute("readonly");
+ text.setTabIndex(0);
+ }
+ } else {
+ text.setEnabled(reallyEnabled);
+ }
+
+ if (reallyEnabled) {
+ calendarToggle.setTabIndex(-1);
+ Roles.getButtonRole()
+ .setAriaHiddenState(calendarToggle.getElement(), true);
+ } else {
+ calendarToggle.setTabIndex(0);
+ Roles.getButtonRole()
+ .setAriaHiddenState(calendarToggle.getElement(), false);
+ }
+
+ handleAriaAttributes();
+ }
+
+ /**
+ * Set correct tab index for disabled text field in IE as the value set in
+ * setTextFieldEnabled(...) gets overridden in
+ * TextualDateConnection.updateFromUIDL(...)
+ *
+ * @since 7.3.1
+ */
+ public void setTextFieldTabIndex() {
+ if (BrowserInfo.get().isIE() && !textFieldEnabled) {
+ // index needs to be -2 because FocusWidget updates -1 to 0 onAttach
+ text.setTabIndex(-2);
+ }
+ }
+
+ @Override
+ public void bindAriaCaption(
+ com.google.gwt.user.client.Element captionElement) {
+ if (captionElement == null) {
+ captionId = null;
+ } else {
+ captionId = captionElement.getId();
+ }
+
+ if (isTextFieldEnabled()) {
+ super.bindAriaCaption(captionElement);
+ } else {
+ AriaHelper.bindCaption(calendarToggle, captionElement);
+ }
+
+ handleAriaAttributes();
+ }
+
+ private void handleAriaAttributes() {
+ Widget removeFromWidget;
+ Widget setForWidget;
+
+ if (isTextFieldEnabled()) {
+ setForWidget = text;
+ removeFromWidget = calendarToggle;
+ } else {
+ setForWidget = calendarToggle;
+ removeFromWidget = text;
+ }
+
+ Roles.getFormRole()
+ .removeAriaLabelledbyProperty(removeFromWidget.getElement());
+ if (captionId == null) {
+ Roles.getFormRole()
+ .removeAriaLabelledbyProperty(setForWidget.getElement());
+ } else {
+ Roles.getFormRole().setAriaLabelledbyProperty(
+ setForWidget.getElement(), Id.of(captionId));
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.google.gwt.user.client.ui.UIObject#setStyleName(java.lang.String)
+ */
+ @Override
+ public void setStyleName(String style) {
+ super.setStyleName(style);
+ updateStyleNames();
+ }
+
+ @Override
+ public void setStylePrimaryName(String style) {
+ removeStyleName(getStylePrimaryName() + "-popupcalendar");
+ super.setStylePrimaryName(style);
+ updateStyleNames();
+ }
+
+ @Override
+ protected void updateStyleNames() {
+ super.updateStyleNames();
+ if (getStylePrimaryName() != null && calendarToggle != null) {
+ addStyleName(getStylePrimaryName() + "-popupcalendar");
+ calendarToggle.setStyleName(getStylePrimaryName() + "-button");
+ popup.setStyleName(getStylePrimaryName() + "-popup");
+ calendar.setStyleName(getStylePrimaryName() + "-calendarpanel");
+ }
+ }
+
+ /**
+ * Opens the calendar panel popup
+ */
+ public void openCalendarPanel() {
+
+ if (!open && !readonly && isEnabled()) {
+ open = true;
+
+ if (getCurrentDate() != null) {
+ calendar.setDate((Date) getCurrentDate().clone());
+ } else {
+ calendar.setDate(new Date());
+ }
+
+ // clear previous values
+ popup.setWidth("");
+ popup.setHeight("");
+ popup.setPopupPositionAndShow(new PopupPositionCallback());
+ } else {
+ VConsole.error("Cannot reopen popup, it is already open!");
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event
+ * .dom.client.ClickEvent)
+ */
+ @Override
+ public void onClick(ClickEvent event) {
+ if (event.getSource() == calendarToggle && isEnabled()) {
+ if (!preventOpenPopupCalendar) {
+ openCalendarPanel();
+ }
+ preventOpenPopupCalendar = false;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.google.gwt.event.logical.shared.CloseHandler#onClose(com.google.gwt
+ * .event.logical.shared.CloseEvent)
+ */
+ @Override
+ public void onClose(CloseEvent<PopupPanel> event) {
+ if (event.getSource() == popup) {
+ buildDate();
+ if (!BrowserInfo.get().isTouchDevice() && textFieldEnabled) {
+ /*
+ * Move focus to textbox, unless on touch device (avoids opening
+ * virtual keyboard) or if textField is disabled.
+ */
+ focus();
+ }
+
+ open = false;
+
+ if (cursorOverCalendarToggleButton
+ && !toggleButtonClosesWithGuarantee) {
+ preventOpenPopupCalendar = true;
+ }
+
+ toggleButtonClosesWithGuarantee = false;
+ }
+ }
+
+ /**
+ * Sets focus to Calendar panel.
+ *
+ * @param focus
+ */
+ public void setFocus(boolean focus) {
+ calendar.setFocus(focus);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ updateTextFieldEnabled();
+ calendarToggle.setEnabled(enabled);
+ Roles.getButtonRole().setAriaDisabledState(calendarToggle.getElement(),
+ !enabled);
+ }
+
+ /**
+ * Sets the content of a special field for assistive devices, so that they
+ * can recognize the change and inform the user (reading out in case of
+ * screen reader)
+ *
+ * @param selectedDate
+ * Date that is currently selected
+ */
+ public void setFocusedDate(Date selectedDate) {
+ this.selectedDate.setText(DateTimeFormat.getFormat("dd, MMMM, yyyy")
+ .format(selectedDate));
+ }
+
+ /**
+ * For internal use only. May be removed or replaced in the future.
+ *
+ * @see com.vaadin.client.ui.VTextualDate#buildDate()
+ */
+ @Override
+ public void buildDate() {
+ // Save previous value
+ String previousValue = getText();
+ super.buildDate();
+
+ // Restore previous value if the input could not be parsed
+ if (!parsable) {
+ setText(previousValue);
+ }
+ updateTextFieldEnabled();
+ }
+
+ /**
+ * Update the text field contents from the date. See {@link #buildDate()}.
+ *
+ * @param forceValid
+ * true to force the text field to be updated, false to only
+ * update if the parsable flag is true.
+ */
+ protected void buildDate(boolean forceValid) {
+ if (forceValid) {
+ parsable = true;
+ }
+ buildDate();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.client.ui.VDateField#onBrowserEvent(com.google
+ * .gwt.user.client.Event)
+ */
+ @Override
+ public void onBrowserEvent(com.google.gwt.user.client.Event event) {
+ super.onBrowserEvent(event);
+ if (DOM.eventGetType(event) == Event.ONKEYDOWN
+ && event.getKeyCode() == getOpenCalenderPanelKey()) {
+ openCalendarPanel();
+ event.preventDefault();
+ }
+ }
+
+ /**
+ * Get the key code that opens the calendar panel. By default it is the down
+ * key but you can override this to be whatever you like
+ *
+ * @return
+ */
+ protected int getOpenCalenderPanelKey() {
+ return KeyCodes.KEY_DOWN;
+ }
+
+ /**
+ * Closes the open popup panel
+ */
+ public void closeCalendarPanel() {
+ if (open) {
+ toggleButtonClosesWithGuarantee = true;
+ popup.hide(true);
+ }
+ }
+
+ private final String CALENDAR_TOGGLE_ID = "popupButton";
+
+ @Override
+ public com.google.gwt.user.client.Element getSubPartElement(
+ String subPart) {
+ if (subPart.equals(CALENDAR_TOGGLE_ID)) {
+ return calendarToggle.getElement();
+ }
+
+ return super.getSubPartElement(subPart);
+ }
+
+ @Override
+ public String getSubPartName(
+ com.google.gwt.user.client.Element subElement) {
+ if (calendarToggle.getElement().isOrHasChild(subElement)) {
+ return CALENDAR_TOGGLE_ID;
+ }
+
+ return super.getSubPartName(subElement);
+ }
+
+ /**
+ * Set a description that explains the usage of the Widget for users of
+ * assistive devices.
+ *
+ * @param descriptionForAssistiveDevices
+ * String with the description
+ */
+ public void setDescriptionForAssistiveDevices(
+ String descriptionForAssistiveDevices) {
+ descriptionForAssisitveDevicesElement
+ .setInnerText(descriptionForAssistiveDevices);
+ }
+
+ /**
+ * Get the description that explains the usage of the Widget for users of
+ * assistive devices.
+ *
+ * @return String with the description
+ */
+ public String getDescriptionForAssistiveDevices() {
+ return descriptionForAssisitveDevicesElement.getInnerText();
+ }
+
+ /**
+ * Sets the start range for this component. The start range is inclusive,
+ * and it depends on the current resolution, what is considered inside the
+ * range.
+ *
+ * @param startDate
+ * - the allowed range's start date
+ */
+ public void setRangeStart(Date rangeStart) {
+ calendar.setRangeStart(rangeStart);
+ }
+
+ /**
+ * Sets the end range for this component. The end range is inclusive, and it
+ * depends on the current resolution, what is considered inside the range.
+ *
+ * @param endDate
+ * - the allowed range's end date
+ */
+ public void setRangeEnd(Date rangeEnd) {
+ calendar.setRangeEnd(rangeEnd);
+ }
+
+ private class PopupPositionCallback implements PositionCallback {
+
+ @Override
+ public void setPosition(int offsetWidth, int offsetHeight) {
+ final int width = offsetWidth;
+ final int height = offsetHeight;
+ final int browserWindowWidth = Window.getClientWidth()
+ + Window.getScrollLeft();
+ final int windowHeight = Window.getClientHeight()
+ + Window.getScrollTop();
+ int left = calendarToggle.getAbsoluteLeft();
+
+ // Add a little extra space to the right to avoid
+ // problems with IE7 scrollbars and to make it look
+ // nicer.
+ int extraSpace = 30;
+
+ boolean overflow = left + width + extraSpace > browserWindowWidth;
+ if (overflow) {
+ // Part of the popup is outside the browser window
+ // (to the right)
+ left = browserWindowWidth - width - extraSpace;
+ }
+
+ int top = calendarToggle.getAbsoluteTop();
+ int extraHeight = 2;
+ boolean verticallyRepositioned = false;
+ ComputedStyle style = new ComputedStyle(popup.getElement());
+ int[] margins = style.getMargin();
+ int desiredPopupBottom = top + height
+ + calendarToggle.getOffsetHeight() + margins[0]
+ + margins[2];
+
+ if (desiredPopupBottom > windowHeight) {
+ int updatedLeft = left;
+ left = getLeftPosition(left, width, style, overflow);
+
+ // if position has not been changed then it means there is no
+ // space to make popup fully visible
+ if (updatedLeft == left) {
+ // let's try to show popup on the top of the field
+ int updatedTop = top - extraHeight - height - margins[0]
+ - margins[2];
+ verticallyRepositioned = updatedTop >= 0;
+ if (verticallyRepositioned) {
+ top = updatedTop;
+ }
+ }
+ // Part of the popup is outside the browser window
+ // (below)
+ if (!verticallyRepositioned) {
+ verticallyRepositioned = true;
+ top = windowHeight - height - extraSpace + extraHeight;
+ }
+ }
+ if (verticallyRepositioned) {
+ popup.setPopupPosition(left, top);
+ } else {
+ popup.setPopupPosition(left,
+ top + calendarToggle.getOffsetHeight() + extraHeight);
+ }
+ doSetFocus();
+ }
+
+ private int getLeftPosition(int left, int width, ComputedStyle style,
+ boolean overflow) {
+ if (positionRightSide()) {
+ // Show to the right of the popup button unless we
+ // are in the lower right corner of the screen
+ if (overflow) {
+ return left;
+ } else {
+ return left + calendarToggle.getOffsetWidth();
+ }
+ } else {
+ int[] margins = style.getMargin();
+ int desiredLeftPosition = calendarToggle.getAbsoluteLeft()
+ - width - margins[1] - margins[3];
+ if (desiredLeftPosition >= 0) {
+ return desiredLeftPosition;
+ } else {
+ return left;
+ }
+ }
+ }
+
+ private boolean positionRightSide() {
+ int buttonRightSide = calendarToggle.getAbsoluteLeft()
+ + calendarToggle.getOffsetWidth();
+ int textRightSide = text.getAbsoluteLeft() + text.getOffsetWidth();
+ return buttonRightSide >= textRightSide;
+ }
+
+ private void doSetFocus() {
+ /*
+ * We have to wait a while before focusing since the popup needs to
+ * be opened before we can focus
+ */
+ Timer focusTimer = new Timer() {
+ @Override
+ public void run() {
+ setFocus(true);
+ }
+ };
+
+ focusTimer.schedule(100);
+ }
+ }
+
+}
diff --git a/compatibility-client/src/main/java/com/vaadin/v7/client/ui/VTextualDate.java b/compatibility-client/src/main/java/com/vaadin/v7/client/ui/VTextualDate.java
new file mode 100644
index 0000000000..329382d605
--- /dev/null
+++ b/compatibility-client/src/main/java/com/vaadin/v7/client/ui/VTextualDate.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * 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.vaadin.v7.client.ui;
+
+import java.util.Date;
+
+import com.google.gwt.aria.client.Roles;
+import com.google.gwt.event.dom.client.BlurEvent;
+import com.google.gwt.event.dom.client.BlurHandler;
+import com.google.gwt.event.dom.client.ChangeEvent;
+import com.google.gwt.event.dom.client.ChangeHandler;
+import com.google.gwt.event.dom.client.FocusEvent;
+import com.google.gwt.event.dom.client.FocusHandler;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.event.dom.client.KeyDownEvent;
+import com.google.gwt.event.dom.client.KeyDownHandler;
+import com.google.gwt.user.client.ui.TextBox;
+import com.vaadin.client.BrowserInfo;
+import com.vaadin.client.Focusable;
+import com.vaadin.client.LocaleNotLoadedException;
+import com.vaadin.client.LocaleService;
+import com.vaadin.client.VConsole;
+import com.vaadin.client.ui.Field;
+import com.vaadin.client.ui.SubPartAware;
+import com.vaadin.client.ui.aria.AriaHelper;
+import com.vaadin.client.ui.aria.HandlesAriaCaption;
+import com.vaadin.client.ui.aria.HandlesAriaInvalid;
+import com.vaadin.client.ui.aria.HandlesAriaRequired;
+import com.vaadin.shared.EventId;
+import com.vaadin.shared.ui.datefield.Resolution;
+
+public class VTextualDate extends VDateField implements Field, ChangeHandler,
+ Focusable, SubPartAware, HandlesAriaCaption, HandlesAriaInvalid,
+ HandlesAriaRequired, KeyDownHandler {
+
+ private static final String PARSE_ERROR_CLASSNAME = "-parseerror";
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public final TextBox text;
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public String formatStr;
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public boolean lenient;
+
+ private static final String CLASSNAME_PROMPT = "prompt";
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public static final String ATTR_INPUTPROMPT = "prompt";
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public String inputPrompt = "";
+
+ private boolean prompting = false;
+
+ public VTextualDate() {
+ super();
+ text = new TextBox();
+ text.addChangeHandler(this);
+ text.addFocusHandler(new FocusHandler() {
+ @Override
+ public void onFocus(FocusEvent event) {
+ text.addStyleName(VTextField.CLASSNAME + "-"
+ + VTextField.CLASSNAME_FOCUS);
+ if (prompting) {
+ text.setText("");
+ setPrompting(false);
+ }
+ if (getClient() != null && getClient()
+ .hasEventListeners(VTextualDate.this, EventId.FOCUS)) {
+ getClient().updateVariable(getId(), EventId.FOCUS, "",
+ true);
+ }
+
+ // Needed for tooltip event handling
+ VTextualDate.this.fireEvent(event);
+ }
+ });
+ text.addBlurHandler(new BlurHandler() {
+ @Override
+ public void onBlur(BlurEvent event) {
+ text.removeStyleName(VTextField.CLASSNAME + "-"
+ + VTextField.CLASSNAME_FOCUS);
+ String value = getText();
+ setPrompting(inputPrompt != null
+ && (value == null || "".equals(value)));
+ if (prompting) {
+ text.setText(readonly ? "" : inputPrompt);
+ }
+ if (getClient() != null && getClient()
+ .hasEventListeners(VTextualDate.this, EventId.BLUR)) {
+ getClient().updateVariable(getId(), EventId.BLUR, "", true);
+ }
+
+ // Needed for tooltip event handling
+ VTextualDate.this.fireEvent(event);
+ }
+ });
+ if (BrowserInfo.get().isIE()) {
+ addDomHandler(this, KeyDownEvent.getType());
+ }
+ add(text);
+ }
+
+ protected void updateStyleNames() {
+ if (text != null) {
+ text.setStyleName(VTextField.CLASSNAME);
+ text.addStyleName(getStylePrimaryName() + "-textfield");
+ }
+ }
+
+ protected String getFormatString() {
+ if (formatStr == null) {
+ if (currentResolution == Resolution.YEAR) {
+ formatStr = "yyyy"; // force full year
+ } else {
+
+ try {
+ String frmString = LocaleService
+ .getDateFormat(currentLocale);
+ frmString = cleanFormat(frmString);
+ // String delim = LocaleService
+ // .getClockDelimiter(currentLocale);
+ if (currentResolution.getCalendarField() >= Resolution.HOUR
+ .getCalendarField()) {
+ if (dts.isTwelveHourClock()) {
+ frmString += " hh";
+ } else {
+ frmString += " HH";
+ }
+ if (currentResolution
+ .getCalendarField() >= Resolution.MINUTE
+ .getCalendarField()) {
+ frmString += ":mm";
+ if (currentResolution
+ .getCalendarField() >= Resolution.SECOND
+ .getCalendarField()) {
+ frmString += ":ss";
+ }
+ }
+ if (dts.isTwelveHourClock()) {
+ frmString += " aaa";
+ }
+
+ }
+
+ formatStr = frmString;
+ } catch (LocaleNotLoadedException e) {
+ // TODO should die instead? Can the component survive
+ // without format string?
+ VConsole.error(e);
+ }
+ }
+ }
+ return formatStr;
+ }
+
+ @Override
+ public void bindAriaCaption(
+ com.google.gwt.user.client.Element captionElement) {
+ AriaHelper.bindCaption(text, captionElement);
+ }
+
+ @Override
+ public void setAriaRequired(boolean required) {
+ AriaHelper.handleInputRequired(text, required);
+ }
+
+ @Override
+ public void setAriaInvalid(boolean invalid) {
+ AriaHelper.handleInputInvalid(text, invalid);
+ }
+
+ /**
+ * Updates the text field according to the current date (provided by
+ * {@link #getDate()}). Takes care of updating text, enabling and disabling
+ * the field, setting/removing readonly status and updating readonly styles.
+ * <p>
+ * For internal use only. May be removed or replaced in the future.
+ * <p>
+ * TODO: Split part of this into a method that only updates the text as this
+ * is what usually is needed except for updateFromUIDL.
+ */
+ public void buildDate() {
+ removeStyleName(getStylePrimaryName() + PARSE_ERROR_CLASSNAME);
+ // Create the initial text for the textfield
+ String dateText;
+ Date currentDate = getDate();
+ if (currentDate != null) {
+ dateText = getDateTimeService().formatDate(currentDate,
+ getFormatString());
+ } else {
+ dateText = "";
+ }
+
+ setText(dateText);
+ text.setEnabled(enabled);
+ text.setReadOnly(readonly);
+
+ if (readonly) {
+ text.addStyleName("v-readonly");
+ Roles.getTextboxRole().setAriaReadonlyProperty(text.getElement(),
+ true);
+ } else {
+ text.removeStyleName("v-readonly");
+ Roles.getTextboxRole()
+ .removeAriaReadonlyProperty(text.getElement());
+ }
+
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ text.setEnabled(enabled);
+ }
+
+ protected void setPrompting(boolean prompting) {
+ this.prompting = prompting;
+ if (prompting) {
+ addStyleDependentName(CLASSNAME_PROMPT);
+ } else {
+ removeStyleDependentName(CLASSNAME_PROMPT);
+ }
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public void onChange(ChangeEvent event) {
+ if (!text.getText().equals("")) {
+ try {
+ String enteredDate = text.getText();
+
+ setDate(getDateTimeService().parseDate(enteredDate,
+ getFormatString(), lenient));
+
+ if (lenient) {
+ // If date value was leniently parsed, normalize text
+ // presentation.
+ // FIXME: Add a description/example here of when this is
+ // needed
+ text.setValue(getDateTimeService().formatDate(getDate(),
+ getFormatString()), false);
+ }
+
+ // remove possibly added invalid value indication
+ removeStyleName(getStylePrimaryName() + PARSE_ERROR_CLASSNAME);
+ } catch (final Exception e) {
+ VConsole.log(e);
+
+ addStyleName(getStylePrimaryName() + PARSE_ERROR_CLASSNAME);
+ // this is a hack that may eventually be removed
+ getClient().updateVariable(getId(), "lastInvalidDateString",
+ text.getText(), false);
+ setDate(null);
+ }
+ } else {
+ setDate(null);
+ // remove possibly added invalid value indication
+ removeStyleName(getStylePrimaryName() + PARSE_ERROR_CLASSNAME);
+ }
+ // always send the date string
+ getClient().updateVariable(getId(), "dateString", text.getText(),
+ false);
+
+ // Update variables
+ // (only the smallest defining resolution needs to be
+ // immediate)
+ Date currentDate = getDate();
+ getClient().updateVariable(getId(), "year",
+ currentDate != null ? currentDate.getYear() + 1900 : -1,
+ currentResolution == Resolution.YEAR && immediate);
+ if (currentResolution.getCalendarField() >= Resolution.MONTH
+ .getCalendarField()) {
+ getClient().updateVariable(getId(), "month",
+ currentDate != null ? currentDate.getMonth() + 1 : -1,
+ currentResolution == Resolution.MONTH && immediate);
+ }
+ if (currentResolution.getCalendarField() >= Resolution.DAY
+ .getCalendarField()) {
+ getClient().updateVariable(getId(), "day",
+ currentDate != null ? currentDate.getDate() : -1,
+ currentResolution == Resolution.DAY && immediate);
+ }
+ if (currentResolution.getCalendarField() >= Resolution.HOUR
+ .getCalendarField()) {
+ getClient().updateVariable(getId(), "hour",
+ currentDate != null ? currentDate.getHours() : -1,
+ currentResolution == Resolution.HOUR && immediate);
+ }
+ if (currentResolution.getCalendarField() >= Resolution.MINUTE
+ .getCalendarField()) {
+ getClient().updateVariable(getId(), "min",
+ currentDate != null ? currentDate.getMinutes() : -1,
+ currentResolution == Resolution.MINUTE && immediate);
+ }
+ if (currentResolution.getCalendarField() >= Resolution.SECOND
+ .getCalendarField()) {
+ getClient().updateVariable(getId(), "sec",
+ currentDate != null ? currentDate.getSeconds() : -1,
+ currentResolution == Resolution.SECOND && immediate);
+ }
+
+ }
+
+ private String cleanFormat(String format) {
+ // Remove unnecessary d & M if resolution is too low
+ if (currentResolution.getCalendarField() < Resolution.DAY
+ .getCalendarField()) {
+ format = format.replaceAll("d", "");
+ }
+ if (currentResolution.getCalendarField() < Resolution.MONTH
+ .getCalendarField()) {
+ format = format.replaceAll("M", "");
+ }
+
+ // Remove unsupported patterns
+ // TODO support for 'G', era designator (used at least in Japan)
+ format = format.replaceAll("[GzZwWkK]", "");
+
+ // Remove extra delimiters ('/' and '.')
+ while (format.startsWith("/") || format.startsWith(".")
+ || format.startsWith("-")) {
+ format = format.substring(1);
+ }
+ while (format.endsWith("/") || format.endsWith(".")
+ || format.endsWith("-")) {
+ format = format.substring(0, format.length() - 1);
+ }
+
+ // Remove duplicate delimiters
+ format = format.replaceAll("//", "/");
+ format = format.replaceAll("\\.\\.", ".");
+ format = format.replaceAll("--", "-");
+
+ return format.trim();
+ }
+
+ @Override
+ public void focus() {
+ text.setFocus(true);
+ }
+
+ protected String getText() {
+ if (prompting) {
+ return "";
+ }
+ return text.getText();
+ }
+
+ protected void setText(String text) {
+ if (inputPrompt != null && (text == null || "".equals(text))
+ && !this.text.getStyleName().contains(VTextField.CLASSNAME + "-"
+ + VTextField.CLASSNAME_FOCUS)) {
+ text = readonly ? "" : inputPrompt;
+ setPrompting(true);
+ } else {
+ setPrompting(false);
+ }
+
+ this.text.setText(text);
+ }
+
+ private final String TEXTFIELD_ID = "field";
+
+ @Override
+ public com.google.gwt.user.client.Element getSubPartElement(
+ String subPart) {
+ if (subPart.equals(TEXTFIELD_ID)) {
+ return text.getElement();
+ }
+
+ return null;
+ }
+
+ @Override
+ public String getSubPartName(
+ com.google.gwt.user.client.Element subElement) {
+ if (text.getElement().isOrHasChild(subElement)) {
+ return TEXTFIELD_ID;
+ }
+
+ return null;
+ }
+
+ @Override
+ public void onKeyDown(KeyDownEvent event) {
+ if (BrowserInfo.get().isIE()
+ && event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
+ // IE does not send change events when pressing enter in a text
+ // input so we handle it using a key listener instead
+ onChange(null);
+ }
+ }
+}
diff --git a/compatibility-client/src/main/java/com/vaadin/v7/client/ui/datefield/AbstractDateFieldConnector.java b/compatibility-client/src/main/java/com/vaadin/v7/client/ui/datefield/AbstractDateFieldConnector.java
index 0b110d66a4..bd9ba1c90d 100644
--- a/compatibility-client/src/main/java/com/vaadin/v7/client/ui/datefield/AbstractDateFieldConnector.java
+++ b/compatibility-client/src/main/java/com/vaadin/v7/client/ui/datefield/AbstractDateFieldConnector.java
@@ -22,10 +22,10 @@ import com.vaadin.client.LocaleNotLoadedException;
import com.vaadin.client.Paintable;
import com.vaadin.client.UIDL;
import com.vaadin.client.VConsole;
-import com.vaadin.client.ui.VDateField;
-import com.vaadin.shared.ui.datefield.DateFieldConstants;
import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.v7.client.ui.AbstractFieldConnector;
+import com.vaadin.v7.client.ui.VDateField;
+import com.vaadin.v7.shared.ui.datefield.DateFieldConstants;
public class AbstractDateFieldConnector extends AbstractFieldConnector
implements Paintable {
diff --git a/compatibility-client/src/main/java/com/vaadin/v7/client/ui/datefield/DateFieldConnector.java b/compatibility-client/src/main/java/com/vaadin/v7/client/ui/datefield/DateFieldConnector.java
index 7d81df987b..2b961cf9ac 100644
--- a/compatibility-client/src/main/java/com/vaadin/v7/client/ui/datefield/DateFieldConnector.java
+++ b/compatibility-client/src/main/java/com/vaadin/v7/client/ui/datefield/DateFieldConnector.java
@@ -25,12 +25,12 @@ import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.DateTimeService;
import com.vaadin.client.UIDL;
import com.vaadin.client.communication.StateChangeEvent;
-import com.vaadin.client.ui.VCalendarPanel.FocusChangeListener;
-import com.vaadin.client.ui.VCalendarPanel.TimeChangeListener;
-import com.vaadin.client.ui.VPopupCalendar;
import com.vaadin.shared.ui.Connect;
-import com.vaadin.shared.ui.datefield.PopupDateFieldState;
import com.vaadin.shared.ui.datefield.Resolution;
+import com.vaadin.v7.client.ui.VCalendarPanel.FocusChangeListener;
+import com.vaadin.v7.client.ui.VCalendarPanel.TimeChangeListener;
+import com.vaadin.v7.client.ui.VPopupCalendar;
+import com.vaadin.v7.shared.ui.datefield.PopupDateFieldState;
import com.vaadin.v7.ui.DateField;
@Connect(DateField.class)
diff --git a/compatibility-client/src/main/java/com/vaadin/v7/client/ui/datefield/InlineDateFieldConnector.java b/compatibility-client/src/main/java/com/vaadin/v7/client/ui/datefield/InlineDateFieldConnector.java
index fed8730e48..39d8e575d7 100644
--- a/compatibility-client/src/main/java/com/vaadin/v7/client/ui/datefield/InlineDateFieldConnector.java
+++ b/compatibility-client/src/main/java/com/vaadin/v7/client/ui/datefield/InlineDateFieldConnector.java
@@ -21,12 +21,12 @@ import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.DateTimeService;
import com.vaadin.client.UIDL;
import com.vaadin.client.communication.StateChangeEvent;
-import com.vaadin.client.ui.VCalendarPanel.FocusChangeListener;
-import com.vaadin.client.ui.VCalendarPanel.TimeChangeListener;
-import com.vaadin.client.ui.VDateFieldCalendar;
import com.vaadin.shared.ui.Connect;
-import com.vaadin.shared.ui.datefield.InlineDateFieldState;
import com.vaadin.shared.ui.datefield.Resolution;
+import com.vaadin.v7.client.ui.VCalendarPanel.FocusChangeListener;
+import com.vaadin.v7.client.ui.VCalendarPanel.TimeChangeListener;
+import com.vaadin.v7.client.ui.VDateFieldCalendar;
+import com.vaadin.v7.shared.ui.datefield.InlineDateFieldState;
import com.vaadin.v7.ui.InlineDateField;
@Connect(InlineDateField.class)
diff --git a/compatibility-client/src/main/java/com/vaadin/v7/client/ui/datefield/TextualDateConnector.java b/compatibility-client/src/main/java/com/vaadin/v7/client/ui/datefield/TextualDateConnector.java
index 6eb5f900ea..85b3664923 100644
--- a/compatibility-client/src/main/java/com/vaadin/v7/client/ui/datefield/TextualDateConnector.java
+++ b/compatibility-client/src/main/java/com/vaadin/v7/client/ui/datefield/TextualDateConnector.java
@@ -18,9 +18,9 @@ package com.vaadin.v7.client.ui.datefield;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.UIDL;
-import com.vaadin.client.ui.VTextualDate;
import com.vaadin.shared.ui.datefield.Resolution;
-import com.vaadin.shared.ui.datefield.TextualDateFieldState;
+import com.vaadin.v7.client.ui.VTextualDate;
+import com.vaadin.v7.shared.ui.datefield.TextualDateFieldState;
public class TextualDateConnector extends AbstractDateFieldConnector {