From 9a1c8fdc0db24b14a0e53ce73bd5f2be030bb65d Mon Sep 17 00:00:00 2001 From: John Alhroos Date: Fri, 6 Aug 2010 12:05:55 +0000 Subject: DateField client side refactoring. svn changeset:14424/svn branch:6.4 --- .../terminal/gwt/client/DateTimeService.java | 31 +- .../terminal/gwt/client/ui/VCalendarPanel.java | 1734 +++++++++++--------- .../vaadin/terminal/gwt/client/ui/VDateField.java | 47 +- .../terminal/gwt/client/ui/VDateFieldCalendar.java | 81 +- .../terminal/gwt/client/ui/VPopupCalendar.java | 126 +- .../terminal/gwt/client/ui/VTextualDate.java | 77 +- src/com/vaadin/terminal/gwt/client/ui/VTime.java | 464 ------ 7 files changed, 1210 insertions(+), 1350 deletions(-) delete mode 100644 src/com/vaadin/terminal/gwt/client/ui/VTime.java (limited to 'src/com') diff --git a/src/com/vaadin/terminal/gwt/client/DateTimeService.java b/src/com/vaadin/terminal/gwt/client/DateTimeService.java index 0693994a9e..e7b93475bc 100644 --- a/src/com/vaadin/terminal/gwt/client/DateTimeService.java +++ b/src/com/vaadin/terminal/gwt/client/DateTimeService.java @@ -6,6 +6,8 @@ package com.vaadin.terminal.gwt.client; import java.util.Date; +import com.vaadin.terminal.gwt.client.ui.VDateField; + /** * This class provides date/time parsing services to all components on the * client side. @@ -15,13 +17,6 @@ import java.util.Date; */ @SuppressWarnings("deprecation") public class DateTimeService { - public static int RESOLUTION_YEAR = 0; - public static int RESOLUTION_MONTH = 1; - public static int RESOLUTION_DAY = 2; - public static int RESOLUTION_HOUR = 3; - public static int RESOLUTION_MIN = 4; - public static int RESOLUTION_SEC = 5; - public static int RESOLUTION_MSEC = 6; private String currentLocale; @@ -150,6 +145,18 @@ public class DateTimeService { return start; } + public static void setMilliseconds(Date date, int ms) { + date.setTime(date.getTime() / 1000 * 1000 + ms); + } + + public static int getMilliseconds(Date date) { + if (date == null) { + return 0; + } + + return (int) (date.getTime() - date.getTime() / 1000 * 1000); + } + public static int getNumberOfDaysInMonth(Date date) { final int month = date.getMonth(); if (month == 1 && true == isLeapYear(date)) { @@ -192,31 +199,31 @@ public class DateTimeService { long end = e.getYear() * 10000000000l; long target = date.getYear() * 10000000000l; - if (resolution == RESOLUTION_YEAR) { + if (resolution == VDateField.RESOLUTION_YEAR) { return (start <= target && end >= target); } start += s.getMonth() * 100000000l; end += e.getMonth() * 100000000l; target += date.getMonth() * 100000000l; - if (resolution == RESOLUTION_MONTH) { + if (resolution == VDateField.RESOLUTION_MONTH) { return (start <= target && end >= target); } start += s.getDate() * 1000000l; end += e.getDate() * 1000000l; target += date.getDate() * 1000000l; - if (resolution == RESOLUTION_DAY) { + if (resolution == VDateField.RESOLUTION_DAY) { return (start <= target && end >= target); } start += s.getHours() * 10000l; end += e.getHours() * 10000l; target += date.getHours() * 10000l; - if (resolution == RESOLUTION_HOUR) { + if (resolution == VDateField.RESOLUTION_HOUR) { return (start <= target && end >= target); } start += s.getMinutes() * 100l; end += e.getMinutes() * 100l; target += date.getMinutes() * 100l; - if (resolution == RESOLUTION_MIN) { + if (resolution == VDateField.RESOLUTION_MIN) { return (start <= target && end >= target); } start += s.getSeconds(); diff --git a/src/com/vaadin/terminal/gwt/client/ui/VCalendarPanel.java b/src/com/vaadin/terminal/gwt/client/ui/VCalendarPanel.java index 56811d1ac4..eb03e302c8 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VCalendarPanel.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VCalendarPanel.java @@ -5,157 +5,104 @@ package com.vaadin.terminal.gwt.client.ui; import java.util.Date; -import java.util.Iterator; import java.util.List; +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.user.client.DOM; -import com.google.gwt.user.client.Event; +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.Command; +import com.google.gwt.user.client.DeferredCommand; 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.MouseListener; -import com.google.gwt.user.client.ui.MouseListenerCollection; -import com.google.gwt.user.client.ui.SourcesMouseEvents; +import com.google.gwt.user.client.ui.ListBox; import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; import com.vaadin.terminal.gwt.client.BrowserInfo; import com.vaadin.terminal.gwt.client.DateTimeService; -import com.vaadin.terminal.gwt.client.LocaleService; +@SuppressWarnings("deprecation") public class VCalendarPanel extends FocusableFlexTable implements - MouseListener, KeyDownHandler, KeyPressHandler { + KeyDownHandler, KeyPressHandler, MouseOutHandler, MouseDownHandler, + MouseUpHandler, BlurHandler, FocusHandler { - /** - * Represents a Date button in the calendar - */ - private class VEventButton extends VNativeButton implements - SourcesMouseEvents { - - private MouseListenerCollection mouseListeners; + public interface SubmitListener { /** - * Default constructor + * Called when calendar user triggers a submitting operation in calendar + * panel. Eg. clicking on day or hitting enter. */ - public VEventButton() { - super(); - sinkEvents(Event.FOCUSEVENTS | Event.KEYEVENTS | Event.ONCLICK - | Event.MOUSEEVENTS); - } + void onSubmit(); - /* - * (non-Javadoc) - * - * @see - * com.google.gwt.user.client.ui.FocusWidget#addMouseListener(com.google - * .gwt.user.client.ui.MouseListener) + /** + * On eg. ESC key. */ - @Override - public void addMouseListener(MouseListener listener) { - if (mouseListeners == null) { - mouseListeners = new MouseListenerCollection(); - } - mouseListeners.add(listener); - } + void onCancel(); + } - /* - * (non-Javadoc) - * - * @see - * com.google.gwt.user.client.ui.FocusWidget#removeMouseListener(com - * .google.gwt.user.client.ui.MouseListener) + /** + * 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 */ - @Override - public void removeMouseListener(MouseListener listener) { - if (mouseListeners != null) { - mouseListeners.remove(listener); - } - } + boolean onFocusOut(DomEvent event); + } - /* - * (non-Javadoc) + /** + * Dispatches an event when the panel changes its _focused_ value. + */ + public interface ValueChangeListener { + /** * - * @see - * com.vaadin.terminal.gwt.client.ui.VNativeButton#onBrowserEvent(com - * .google.gwt.user.client.Event) + * @return true if the calendar panel will not be used anymore */ - @Override - public void onBrowserEvent(Event event) { - super.onBrowserEvent(event); - switch (DOM.eventGetType(event)) { - case Event.ONMOUSEDOWN: - case Event.ONMOUSEUP: - case Event.ONMOUSEOUT: - if (mouseListeners != null) { - mouseListeners.fireMouseEvent(this, event); - } - break; - } - } + void changed(Date date); } /** - * Represents a click handler for when a user selects a date by using the - * mouse + * Represents a Date button in the calendar */ - private class DateClickHandler implements ClickHandler { - - private final VCalendarPanel cal; - - public DateClickHandler(VCalendarPanel panel) { - cal = panel; + private class VEventButton extends Button { + public VEventButton() { + addMouseDownHandler(VCalendarPanel.this); + addMouseOutHandler(VCalendarPanel.this); + addMouseUpHandler(VCalendarPanel.this); } + } - public void selectDate(String date) { - try { - final Integer day = new Integer(date); - final Date newDate = cal.datefield.getShowingDate(); - newDate.setDate(day.intValue()); - if (!isEnabledDate(newDate)) { - return; - } - if (cal.datefield.getCurrentDate() == null) { - cal.datefield.setCurrentDate(new Date(newDate.getTime())); - - // Init variables with current time - datefield.getClient().updateVariable(cal.datefield.getId(), - "hour", newDate.getHours(), false); - datefield.getClient().updateVariable(cal.datefield.getId(), - "min", newDate.getMinutes(), false); - datefield.getClient().updateVariable(cal.datefield.getId(), - "sec", newDate.getSeconds(), false); - datefield.getClient().updateVariable(cal.datefield.getId(), - "msec", datefield.getMilliseconds(), false); - } + private static final String CN_FOCUSED = "focused"; - cal.datefield.getCurrentDate().setTime(newDate.getTime()); - cal.datefield.getClient().updateVariable(cal.datefield.getId(), - "day", cal.datefield.getCurrentDate().getDate(), false); - cal.datefield.getClient().updateVariable(cal.datefield.getId(), - "month", cal.datefield.getCurrentDate().getMonth() + 1, - false); - cal.datefield.getClient().updateVariable(cal.datefield.getId(), - "year", - cal.datefield.getCurrentDate().getYear() + 1900, - cal.datefield.isImmediate()); - - if (datefield instanceof VTextualDate) { - ((VOverlay) getParent()).hide(); - } else { - updateCalendar(); - } + private static final String CN_TODAY = "today"; - } catch (final NumberFormatException e) { - // Not a number, ignore and stop here - return; - } - } + private static final String CN_SELECTED = "selected"; + /** + * Represents a click handler for when a user selects a value by using the + * mouse + */ + private ClickHandler dayClickHandler = new ClickHandler() { /* * (non-Javadoc) * @@ -164,25 +111,12 @@ public class VCalendarPanel extends FocusableFlexTable implements * .event.dom.client.ClickEvent) */ public void onClick(ClickEvent event) { - Object sender = event.getSource(); - Cell cell = cal.days.getCellForEvent(event); - if (sender != cal.days || cell == null || cell.getRowIndex() < 1 - || cell.getRowIndex() > 6 || !cal.datefield.isEnabled() - || cal.datefield.isReadonly() || cell.getCellIndex() < 1) { - return; - } - - final String text = cal.days.getText(cell.getRowIndex(), cell - .getCellIndex()); - if (text.equals(" ")) { - return; - } - - selectDate(text); + Day day = (Day) event.getSource(); + focusDay(day.getDay()); + selectFocused(); + onSubmit(); } - } - - private final VDateField datefield; + }; private VEventButton prevYear; @@ -194,38 +128,44 @@ public class VCalendarPanel extends FocusableFlexTable implements private VTime time; - private Date minDate = null; - - private Date maxDate = null; - - private CalendarEntrySource entrySource; - private FlexTable days = new FlexTable(); /* Needed to identify resolution changes */ + private int oldResolution = 0; private int resolution = VDateField.RESOLUTION_YEAR; - /* Needed to identify locale changes */ - private String locale = LocaleService.getDefaultLocale(); + private int focusedRow; + private int focusedColumn; - private int selectedRow; - private int selectedColumn; + private Timer mouseTimer; - private boolean changingView = false; + private Date value; - private final DateClickHandler dateClickHandler; + private boolean enabled = true; - private Timer mouseTimer; + private boolean readonly = false; - public VCalendarPanel(VDateField parent) { - super(); + private DateTimeService dateTimeService; - datefield = parent; - setStyleName(VDateField.CLASSNAME + "-calendarpanel"); - // buildCalendar(true); + private boolean showISOWeekNumbers; - dateClickHandler = new DateClickHandler(this); - days.addClickHandler(dateClickHandler); + private Date focusedDate; + + private Day selectedDay; + + private Day focusedDay; + + private FocusOutListener focusOutListener; + + private SubmitListener submitListener; + + private ValueChangeListener valueChangeListener; + + private boolean hasFocus = false; + + public VCalendarPanel() { + + setStyleName(VDateField.CLASSNAME + "-calendarpanel"); /* * Firefox auto-repeat works correctly only if we use a key press @@ -237,48 +177,125 @@ public class VCalendarPanel extends FocusableFlexTable implements } else { addKeyDownHandler(this); } + addFocusHandler(this); + addBlurHandler(this); } - public VCalendarPanel(VDateField parent, Date min, Date max) { - super(); + /** + * Sets the focus to given day of current time. Used when moving in the + * calender with the keyboard. + * + * @param day + * The day number from by Date.getDate() + */ + private void focusDay(int day) { + // Only used when calender body is present + if (resolution > VDateField.RESOLUTION_MONTH) { + if (focusedDay != null) + focusedDay.removeStyleDependentName(CN_FOCUSED); + + if (day > 0 && focusedDate != null) { + focusedDate.setDate(day); + 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.getDay() == day) { + curday.addStyleDependentName(CN_FOCUSED); + focusedDay = curday; + focusedColumn = j; + focusedRow = i; + return; + } + } + } + } + } + } + } - datefield = parent; - setStyleName(VDateField.CLASSNAME + "-calendarpanel"); + /** + * Sets the selection hightlight to a given date of current time + * + * @param day + */ + private void selectDate(int day) { + + value.setDate(day); + + if (selectedDay != null) + selectedDay.removeStyleDependentName(CN_SELECTED); + + 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.getDay() == day) { + curday.addStyleDependentName(CN_SELECTED); + selectedDay = curday; + return; + } + } + } + } + } - dateClickHandler = new DateClickHandler(this); - days.addClickHandler(dateClickHandler); + /** + * Updates year, month, day from focusedDate to value + */ + private void selectFocused() { + if (focusedDate != null) { + int changedFields = 0; + if (value.getYear() != focusedDate.getYear()) { + value.setYear(focusedDate.getYear()); + changedFields += VDateField.RESOLUTION_YEAR; + } + if (value.getMonth() != focusedDate.getMonth()) { + value.setMonth(focusedDate.getMonth()); + changedFields += VDateField.RESOLUTION_MONTH; + } + if (value.getDate() != focusedDate.getDate()) { + value.setDate(focusedDate.getDate()); + changedFields += VDateField.RESOLUTION_DAY; + } - /* - * 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); + selectDate(focusedDate.getDate()); } else { - addKeyDownHandler(this); + ApplicationConnection.getConsole().log( + "Trying to select a the focused date which is NULL!"); } } - private void buildCalendar(boolean forceRedraw) { - final boolean needsMonth = datefield.getCurrentResolution() > VDateField.RESOLUTION_YEAR; - boolean needsBody = datefield.getCurrentResolution() >= VDateField.RESOLUTION_DAY; - final boolean needsTime = datefield.getCurrentResolution() >= VDateField.RESOLUTION_HOUR; - forceRedraw = prevYear == null ? true : forceRedraw; - buildCalendarHeader(forceRedraw, needsMonth); - clearCalendarBody(!needsBody); - if (needsBody) { - buildCalendarBody(); - } - if (needsTime) { - buildTime(forceRedraw); - } else if (time != null) { - remove(time); - time = null; + protected boolean onValueChange() { + return false; + } + + private int getResolution() { + return resolution; + } + + public void setResolution(int resolution) { + if (resolution != this.resolution) { + this.oldResolution = this.resolution; + this.resolution = resolution; } } + private boolean isReadonly() { + return readonly; + } + + private boolean isEnabled() { + return enabled; + } + private void clearCalendarBody(boolean remove) { if (!remove) { // Leave the cells in place but clear their contents @@ -293,10 +310,17 @@ public class VCalendarPanel extends FocusableFlexTable implements } else if (getRowCount() > 1) { removeRow(1); days.clear(); - } } + /** + * Builds the top buttons and current month and year header. + * + * @param forceRedraw + * Forces the builder to recreate the instances of the buttons. + * @param needsMonth + * Should the month buttons be visible? + */ private void buildCalendarHeader(boolean forceRedraw, boolean needsMonth) { if (forceRedraw) { if (prevMonth == null) { @@ -316,13 +340,11 @@ public class VCalendarPanel extends FocusableFlexTable implements prevYear = new VEventButton(); prevYear.setHTML("«"); prevYear.setStyleName("v-button-prevyear"); - prevYear.getElement().setTabIndex(-1); + prevYear.setTabIndex(-1); nextYear = new VEventButton(); nextYear.setHTML("»"); nextYear.setStyleName("v-button-nextyear"); - nextYear.getElement().setTabIndex(-1); - prevYear.addMouseListener(this); - nextYear.addMouseListener(this); + nextYear.setTabIndex(-1); setWidget(0, 0, prevYear); setWidget(0, 4, nextYear); @@ -330,20 +352,16 @@ public class VCalendarPanel extends FocusableFlexTable implements prevMonth = new VEventButton(); prevMonth.setHTML("‹"); prevMonth.setStyleName("v-button-prevmonth"); - prevMonth.getElement().setTabIndex(-1); + prevMonth.setTabIndex(-1); nextMonth = new VEventButton(); nextMonth.setHTML("›"); nextMonth.setStyleName("v-button-nextmonth"); - nextMonth.getElement().setTabIndex(-1); - prevMonth.addMouseListener(this); - nextMonth.addMouseListener(this); + nextMonth.setTabIndex(-1); setWidget(0, 3, nextMonth); setWidget(0, 1, prevMonth); } } else if (!needsMonth) { // Remove month traverse buttons - prevMonth.removeMouseListener(this); - nextMonth.removeMouseListener(this); remove(prevMonth); remove(nextMonth); prevMonth = null; @@ -351,9 +369,9 @@ public class VCalendarPanel extends FocusableFlexTable implements } } - final String monthName = needsMonth ? datefield.getDateTimeService() - .getMonth(datefield.getShowingDate().getMonth()) : ""; - final int year = datefield.getShowingDate().getYear() + 1900; + final String monthName = needsMonth ? getDateTimeService().getMonth( + focusedDate.getMonth()) : ""; + final int year = focusedDate.getYear() + 1900; getFlexCellFormatter().setStyleName(0, 2, VDateField.CLASSNAME + "-calendarpanel-month"); setHTML(0, 2, ""); } + 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; + } + + /** + * Builds the day and time selectors of the calendar. + */ private void buildCalendarBody() { - final boolean showISOWeekNumbers = datefield.isShowISOWeekNumbers(); final int weekColumn = 0; final int firstWeekdayColumn = 1; final int headerRow = 0; @@ -380,12 +423,12 @@ public class VCalendarPanel extends FocusableFlexTable implements days.setHTML(headerRow, weekColumn, ""); // Hide the week column if week numbers are not to be displayed. days.getFlexCellFormatter().setVisible(headerRow, weekColumn, - showISOWeekNumbers); + isShowISOWeekNumbers()); days.getRowFormatter().setStyleName(headerRow, VDateField.CLASSNAME + "-calendarpanel-weekdays"); - if (showISOWeekNumbers) { + if (isShowISOWeekNumbers()) { days.getFlexCellFormatter().setStyleName(headerRow, weekColumn, "v-first"); days.getFlexCellFormatter().setStyleName(headerRow, @@ -402,34 +445,20 @@ public class VCalendarPanel extends FocusableFlexTable implements firstWeekdayColumn + 6, "v-last"); // Print weekday names - final int firstDay = datefield.getDateTimeService().getFirstDayOfWeek(); + final int firstDay = getDateTimeService().getFirstDayOfWeek(); for (int i = 0; i < 7; i++) { int day = i + firstDay; if (day > 6) { day = 0; } - if (datefield.getCurrentResolution() > VDateField.RESOLUTION_MONTH) { + if (getResolution() > VDateField.RESOLUTION_MONTH) { days.setHTML(headerRow, firstWeekdayColumn + i, "" - + datefield.getDateTimeService().getShortDay(day) - + ""); + + getDateTimeService().getShortDay(day) + ""); } else { days.setHTML(headerRow, firstWeekdayColumn + i, ""); } } - // date actually selected? - Date selectedDate = datefield.getCurrentDate(); - - // Showing is the date (year/month+year) that is currently shown in the - // panel - Date showing = datefield.getShowingDate(); - - // Always show something - if (showing == null) { - showing = new Date(); - datefield.setShowingDate(showing); - } - // The day of month that is selected, -1 if no day of this month is // selected (i.e, showing another month/year than selected or nothing is // selected) @@ -438,28 +467,25 @@ public class VCalendarPanel extends FocusableFlexTable implements // (i.e., showing another month/year than current) int dayOfMonthToday = -1; - // Find out a day this month is selected - if (showing != null) { - dayOfMonthSelected = showing.getDate(); - } else if (selectedDate != null - && selectedDate.getMonth() == showing.getMonth() - && selectedDate.getYear() == showing.getYear()) { - dayOfMonthSelected = selectedDate.getDate(); - } + boolean initiallyNull = value == null; - // Find out if today is in this month + if (!initiallyNull && value.getMonth() == focusedDate.getMonth() + && value.getYear() == focusedDate.getYear()) { + dayOfMonthSelected = value.getDate(); + } final Date today = new Date(); - if (today.getMonth() == showing.getMonth() - && today.getYear() == showing.getYear()) { + if (today.getMonth() == focusedDate.getMonth() + && today.getYear() == focusedDate.getYear()) { dayOfMonthToday = today.getDate(); } - final int startWeekDay = datefield.getDateTimeService() - .getStartWeekDay(showing); - final int daysInMonth = DateTimeService.getNumberOfDaysInMonth(showing); + final int startWeekDay = getDateTimeService().getStartWeekDay( + focusedDate); + final int daysInMonth = DateTimeService + .getNumberOfDaysInMonth(focusedDate); int dayCount = 0; - final Date curr = new Date(showing.getTime()); + final Date curr = new Date(focusedDate.getTime()); // No month has more than 6 weeks so 6 is a safe maximum for rows. for (int weekOfMonth = 1; weekOfMonth < 7; weekOfMonth++) { @@ -475,57 +501,36 @@ public class VCalendarPanel extends FocusableFlexTable implements } final int dayOfMonth = ++dayCount; - final String baseclass = VDateField.CLASSNAME - + "-calendarpanel-day"; - String title = ""; curr.setDate(dayCount); - if (entrySource != null) { - final List entries = entrySource.getEntries(curr, - VDateField.RESOLUTION_DAY); - if (entries != null) { - for (final Iterator it = entries.iterator(); it - .hasNext();) { - final CalendarEntry entry = (CalendarEntry) it - .next(); - title += (title.length() > 0 ? ", " : "") - + entry.getStringForDate(curr); - } - } - } - // Actually write the day of month - InlineHTML html = new InlineHTML(String.valueOf(dayOfMonth)); - html.setStyleName(baseclass); - html.setTitle(title); - - // Add CSS classes according to state - if (!isEnabledDate(curr)) { - html.addStyleDependentName("disabled"); - } + Day day = new Day(dayOfMonth); if (dayOfMonthSelected == dayOfMonth) { - html.addStyleDependentName("selected"); - selectedRow = weekOfMonth; - selectedColumn = firstWeekdayColumn + dayOfWeek; + day.addStyleDependentName(CN_SELECTED); + selectedDay = day; } if (dayOfMonthToday == dayOfMonth) { - html.addStyleDependentName("today"); + day.addStyleDependentName(CN_TODAY); } - if (title.length() > 0) { - html.addStyleDependentName("entry"); + + if (dayOfMonth == focusedDate.getDate()) { + day.addStyleDependentName(CN_FOCUSED); + focusedDay = day; + focusedRow = weekOfMonth; + focusedColumn = firstWeekdayColumn + dayOfWeek; } days.setWidget(weekOfMonth, firstWeekdayColumn + dayOfWeek, - html); + day); // ISO week numbers if requested if (!weekNumberProcessed[weekOfMonth]) { days.getCellFormatter().setVisible(weekOfMonth, - weekColumn, showISOWeekNumbers); - if (showISOWeekNumbers) { + weekColumn, isShowISOWeekNumbers()); + if (isShowISOWeekNumbers()) { final String baseCssClass = VDateField.CLASSNAME + "-calendarpanel-weeknumber"; String weekCssClass = baseCssClass; @@ -546,120 +551,112 @@ public class VCalendarPanel extends FocusableFlexTable implements } - private void buildTime(boolean forceRedraw) { - if (time == null) { - time = new VTime(datefield, this); - setWidget(2, 0, time); - getFlexCellFormatter().setColSpan(2, 0, 5); - getFlexCellFormatter().setStyleName(2, 0, - VDateField.CLASSNAME + "-calendarpanel-time"); - } - time.updateTime(forceRedraw); - } - /** - * Updates the calendar and text field with the selected dates * + * Do we need the time selector + * + * @return True if it is required */ - public void updateCalendar() { - updateCalendarOnly(); - if (datefield instanceof VPopupCalendar) { - ((VPopupCalendar) datefield).buildDate(true); - } else if (datefield instanceof VTextualDate) { - ((VTextualDate) datefield).buildDate(); - } + private boolean isTimeSelectorNeeded() { + return getResolution() > VDateField.RESOLUTION_DAY; } /** - * Updates the popup only, does not affect the text field + * Updates the calendar and text field with the selected dates. */ - private void updateCalendarOnly() { - if (!changingView) { - changingView = true; - // Locale and resolution changes force a complete redraw - buildCalendar(locale != datefield.getCurrentLocale() - || resolution != datefield.getCurrentResolution()); - locale = datefield.getCurrentLocale(); - resolution = datefield.getCurrentResolution(); + public void renderCalendar() { + if (focusedDate == null) { + focusedDate = new Date(); + } - /* - * Wait a while before releasing the lock, so auto-repeated events - * isn't processed after the calendar is updated - */ - Timer changeTimer = new Timer() { - @Override - public void run() { - changingView = false; - } - }; - changeTimer.schedule(100); + if (getResolution() <= VDateField.RESOLUTION_MONTH + && valueChangeListener != null) { + valueChangeListener.changed(focusedDate); } - } - private boolean isEnabledDate(Date date) { - if ((minDate != null && date.before(minDate)) - || (maxDate != null && date.after(maxDate))) { - return false; + Date start = new Date(); + final boolean needsMonth = getResolution() > VDateField.RESOLUTION_YEAR; + boolean needsBody = getResolution() >= VDateField.RESOLUTION_DAY; + buildCalendarHeader(true, needsMonth); + clearCalendarBody(!needsBody); + if (needsBody) { + buildCalendarBody(); + } + + if (isTimeSelectorNeeded() + && (time == null || this.resolution != this.oldResolution)) { + time = new VTime(); + setWidget(2, 0, time); + getFlexCellFormatter().setColSpan(2, 0, 5); + getFlexCellFormatter().setStyleName(2, 0, + VDateField.CLASSNAME + "-calendarpanel-time"); + this.oldResolution = this.resolution; + } else if (isTimeSelectorNeeded()) { + time.updateTimes(); + } else if (time != null) { + remove(time); } - return true; + + Date end = new Date(); + ApplicationConnection.getConsole().error( + "Rendering calendar panel for(ms) " + + (end.getTime() - start.getTime())); + } /** * Selects the next month */ - private void selectNextMonth() { - Date showingDate = datefield.getShowingDate(); - int currentMonth = showingDate.getMonth(); - showingDate.setMonth(currentMonth + 1); + private void focusNextMonth() { + + int currentMonth = focusedDate.getMonth(); + focusedDate.setMonth(currentMonth + 1); int requestedMonth = (currentMonth + 1) % 12; /* - * If the selected date was e.g. 31.3 the new date would be 31.4 but - * this date is invalid so the new date will be 1.5. This is taken care - * of by decreasing the date until we have the correct month. + * 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 (showingDate.getMonth() != requestedMonth) { - showingDate.setDate(showingDate.getDate() - 1); + while (focusedDate.getMonth() != requestedMonth) { + focusedDate.setDate(focusedDate.getDate() - 1); } - updateCalendar(); + renderCalendar(); } /** * Selects the previous month */ - private void selectPreviousMonth() { - Date showingDate = datefield.getShowingDate(); - int currentMonth = showingDate.getMonth(); - showingDate.setMonth(currentMonth - 1); + private void focusPreviousMonth() { + int currentMonth = focusedDate.getMonth(); + focusedDate.setMonth(currentMonth - 1); /* - * If the selected date was e.g. 31.12 the new date would be 31.11 but - * this date is invalid so the new date will be 1.12. This is taken care - * of by decreasing the date until we have the correct month. + * 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 (showingDate.getMonth() == currentMonth) { - showingDate.setDate(showingDate.getDate() - 1); + while (focusedDate.getMonth() == currentMonth) { + focusedDate.setDate(focusedDate.getDate() - 1); } - updateCalendar(); + renderCalendar(); } /** * Selects the previous year */ - private void selectPreviousYear(int years) { - Date showingDate = datefield.getShowingDate(); - showingDate.setYear(showingDate.getYear() - years); - updateCalendar(); + private void focusPreviousYear(int years) { + focusedDate.setYear(focusedDate.getYear() - years); + renderCalendar(); } /** * Selects the next year */ - private void selectNextYear(int years) { - Date showingDate = datefield.getShowingDate(); - showingDate.setYear(showingDate.getYear() + years); - updateCalendar(); + private void focusNextYear(int years) { + focusedDate.setYear(focusedDate.getYear() + years); + renderCalendar(); } /** @@ -668,224 +665,106 @@ public class VCalendarPanel extends FocusableFlexTable implements * @param sender * The component that was clicked * @param updateVariable - * Should the date field be updated + * Should the value field be updated * */ - private void processClickEvent(Widget sender, boolean updateVariable) { - if (!datefield.isEnabled() || datefield.isReadonly()) { + private void processClickEvent(Widget sender) { + if (!isEnabled() || isReadonly()) { return; } - if (!updateVariable) { - if (sender == prevYear) { - selectPreviousYear(1); - } else if (sender == nextYear) { - selectNextYear(1); - } else if (sender == prevMonth) { - selectPreviousMonth(); - } else if (sender == nextMonth) { - selectNextMonth(); - } - } else { - if (datefield.getCurrentResolution() == VDateField.RESOLUTION_YEAR - || datefield.getCurrentResolution() == VDateField.RESOLUTION_MONTH) { - notifyServerOfChanges(); - } + if (sender == prevYear) { + focusPreviousYear(1); + } else if (sender == nextYear) { + focusNextYear(1); + } else if (sender == prevMonth) { + focusPreviousMonth(); + } else if (sender == nextMonth) { + focusNextMonth(); } } - /** - * Send the date to the server - */ - private void notifyServerOfChanges() { - Date showingDate = datefield.getShowingDate(); - - // Due to current UI, update variable if res=year/month - datefield.setCurrentDate(new Date(showingDate.getTime())); - if (datefield.getCurrentResolution() == VDateField.RESOLUTION_MONTH) { - datefield.getClient().updateVariable(datefield.getId(), "month", - datefield.getCurrentDate().getMonth() + 1, false); - } - datefield.getClient().updateVariable(datefield.getId(), "year", - datefield.getCurrentDate().getYear() + 1900, - datefield.isImmediate()); - - /* Must update the value in the textfield also */ - updateCalendar(); + public interface CalendarEntrySource { + public List getEntries(Date date, int resolution); } /* * (non-Javadoc) * * @see - * com.google.gwt.user.client.ui.MouseListener#onMouseDown(com.google.gwt - * .user.client.ui.Widget, int, int) + * com.google.gwt.event.dom.client.KeyDownHandler#onKeyDown(com.google.gwt + * .event.dom.client.KeyDownEvent) */ - public void onMouseDown(final Widget sender, int x, int y) { - // Allow user to click-n-hold 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 (sender instanceof VEventButton) { - processClickEvent(sender, false); - mouseTimer = new Timer() { - @Override - public void run() { - mouseTimer = new Timer() { - @Override - public void run() { - processClickEvent(sender, false); - } - }; - mouseTimer.scheduleRepeating(150); - } - }; - mouseTimer.schedule(500); - } + public void onKeyDown(KeyDownEvent event) { + handleKeyPress(event); } /* * (non-Javadoc) * * @see - * com.google.gwt.user.client.ui.MouseListener#onMouseEnter(com.google.gwt - * .user.client.ui.Widget) + * com.google.gwt.event.dom.client.KeyPressHandler#onKeyPress(com.google + * .gwt.event.dom.client.KeyPressEvent) */ - public void onMouseEnter(Widget sender) { + public void onKeyPress(KeyPressEvent event) { + handleKeyPress(event); } - /* - * (non-Javadoc) + /** + * Handles the keypress from both the onKeyPress event and the onKeyDown + * event * - * @see - * com.google.gwt.user.client.ui.MouseListener#onMouseLeave(com.google.gwt - * .user.client.ui.Widget) + * @param event + * The keydown/keypress event */ - public void onMouseLeave(Widget sender) { - if (mouseTimer != null) { - mouseTimer.cancel(); + private void handleKeyPress(DomEvent event) { + if (time != null + && time.getElement().isOrHasChild( + (Node) event.getNativeEvent().getEventTarget().cast())) { + int nativeKeyCode = event.getNativeEvent().getKeyCode(); + if (nativeKeyCode == getSelectKey()) { + ApplicationConnection.getConsole().log( + "keydown on listselects" + + event.getNativeEvent().getKeyCode()); + onSubmit(); // submit happens if enter key hit down on listboxes + event.preventDefault(); + event.stopPropagation(); + } + return; } - } - - /* - * (non-Javadoc) - * - * @see - * com.google.gwt.user.client.ui.MouseListener#onMouseMove(com.google.gwt - * .user.client.ui.Widget, int, int) - */ - public void onMouseMove(Widget sender, int x, int y) { - } - /* - * (non-Javadoc) - * - * @see - * com.google.gwt.user.client.ui.MouseListener#onMouseUp(com.google.gwt. - * user.client.ui.Widget, int, int) - */ - public void onMouseUp(Widget sender, int x, int y) { - if (mouseTimer != null) { - mouseTimer.cancel(); + // Check tabs + int keycode = event.getNativeEvent().getKeyCode(); + if (keycode == KeyCodes.KEY_TAB && event.getNativeEvent().getShiftKey()) { + if (onTabOut(event)) { + return; + } } - processClickEvent(sender, true); - } - public void setLimits(Date min, Date max) { - if (min != null) { - final Date d = new Date(min.getTime()); - d.setHours(0); - d.setMinutes(0); - d.setSeconds(1); - minDate = d; - } else { - minDate = null; - } - if (max != null) { - final Date d = new Date(max.getTime()); - d.setHours(24); - d.setMinutes(59); - d.setSeconds(59); - maxDate = d; - } else { - maxDate = null; + // Handle the navigation + if (handleNavigation(keycode, event.getNativeEvent().getCtrlKey() + || event.getNativeEvent().getMetaKey(), event.getNativeEvent() + .getShiftKey())) { + event.preventDefault(); } - } - public void setCalendarEntrySource(CalendarEntrySource entrySource) { - this.entrySource = entrySource; } - public CalendarEntrySource getCalendarEntrySource() { - return entrySource; + /** + * Notifies submit-listeners of a submit event + */ + private void onSubmit() { + if (getSubmitListener() != null) { + getSubmitListener().onSubmit(); + } } - public interface CalendarEntrySource { - public List getEntries(Date date, int resolution); - } - - /* - * (non-Javadoc) - * - * @see - * com.google.gwt.event.dom.client.KeyDownHandler#onKeyDown(com.google.gwt - * .event.dom.client.KeyDownEvent) - */ - public void onKeyDown(KeyDownEvent event) { - int keycode = event.getNativeKeyCode(); - - if (handleNavigation(keycode, event.isControlKeyDown() - || event.isMetaKeyDown(), event.isShiftKeyDown())) { - event.preventDefault(); - } - } - - /* - * (non-Javadoc) - * - * @see - * com.google.gwt.event.dom.client.KeyPressHandler#onKeyPress(com.google - * .gwt.event.dom.client.KeyPressEvent) - */ - public void onKeyPress(KeyPressEvent event) { - if (handleNavigation(event.getNativeEvent().getKeyCode(), event - .isControlKeyDown() - || event.isMetaKeyDown(), event.isShiftKeyDown())) { - event.preventDefault(); - } - } - - /** - * Get the first valid weekday column index of this month - * - * @return A column index or zero if not found - */ - private int getFirstWeekdayColumn() { - Widget day = null; - for (int col = 1; col <= 7; col++) { - day = days.getWidget(1, col); - if (day != null) { - return col; - } - } - return 0; - } - - /** - * Get the last valid weekday column index of this month - * - * @return A column index or zero if not found - */ - private int[] getLastWeekdayColumn() { - Widget day = null; - for (int row = days.getRowCount() - 1; row >= 1; row--) { - for (int col = 7; col >= 1; col--) { - day = days.getWidget(row, col); - if (day != null) { - return new int[] { row, col }; - } - } - } - return new int[] { 0, 0 }; + /** + * Notifies submit-listeners of a cancel event + */ + private void onCancel() { + if (getSubmitListener() != null) { + getSubmitListener().onCancel(); + } } /** @@ -908,63 +787,37 @@ public class VCalendarPanel extends FocusableFlexTable implements } else if (keycode == getPreviousKey()) { - selectNextYear(10); // Add 10 years + focusNextYear(10); // Add 10 years return true; } else if (keycode == getForwardKey()) { - selectNextYear(1); // Add 1 year + focusNextYear(1); // Add 1 year return true; } else if (keycode == getNextKey()) { - selectPreviousYear(10); // Subtract 10 years + focusPreviousYear(10); // Subtract 10 years return true; } else if (keycode == getBackwardKey()) { - selectPreviousYear(1); // Subtract 1 year + focusPreviousYear(1); // Subtract 1 year return true; } else if (keycode == getSelectKey()) { - - // We need to notify the clickhandler of the selection - dateClickHandler.selectDate(String.valueOf(datefield - .getShowingDate().getDay())); - - // Send changes to server - notifyServerOfChanges(); - - if (datefield instanceof VTextualDate) { - ((VTextualDate) datefield).focus(); - } + value = (Date) focusedDate.clone(); + onSubmit(); return true; } else if (keycode == getResetKey()) { - // Restore showing date the selected date - Date showing = datefield.getShowingDate(); - Date current = datefield.getCurrentDate(); - showing.setTime(current.getTime()); - updateCalendar(); + // Restore showing value the selected value + focusedDate.setTime(value.getTime()); + renderCalendar(); return true; } else if (keycode == getCloseKey()) { - if (datefield instanceof VPopupCalendar) { - - // Restore showing date the selected date - Date showing = datefield.getShowingDate(); - Date current = datefield.getCurrentDate(); - - if (current != null && current != null) { - showing.setTime(current.getTime()); - } - - // Close popup.. - ((VPopupCalendar) datefield).closeCalendarPanel(); - - // ..and focus the textfield - ((VPopupCalendar) datefield).focus(); - } + // TODO fire listener, on users responsibility?? return true; } @@ -990,61 +843,35 @@ public class VCalendarPanel extends FocusableFlexTable implements return false; } else if (keycode == getPreviousKey()) { - selectNextYear(1); // Add 1 year + focusNextYear(1); // Add 1 year return true; } else if (keycode == getForwardKey()) { - selectNextMonth(); // Add 1 month + focusNextMonth(); // Add 1 month return true; } else if (keycode == getNextKey()) { - selectPreviousYear(1); // Subtract 1 year + focusPreviousYear(1); // Subtract 1 year return true; } else if (keycode == getBackwardKey()) { - selectPreviousMonth(); // Subtract 1 month + focusPreviousMonth(); // Subtract 1 month return true; } else if (keycode == getSelectKey()) { - - // We need to notify the clickhandler of the selection - dateClickHandler.selectDate(String.valueOf(datefield - .getShowingDate().getDay())); - - // Send changes to server - notifyServerOfChanges(); - - if (datefield instanceof VTextualDate) { - ((VTextualDate) datefield).focus(); - } - + value = (Date) focusedDate.clone(); + onSubmit(); return true; } else if (keycode == getResetKey()) { - // Restore showing date the selected date - Date showing = datefield.getShowingDate(); - Date current = datefield.getCurrentDate(); - showing.setTime(current.getTime()); - updateCalendar(); + // Restore showing value the selected value + focusedDate.setTime(value.getTime()); + renderCalendar(); return true; } else if (keycode == getCloseKey() || keycode == KeyCodes.KEY_TAB) { - if (datefield instanceof VPopupCalendar) { - // Restore showing date the selected date - Date showing = datefield.getShowingDate(); - Date current = datefield.getCurrentDate(); - - if (current != null && current != null) { - showing.setTime(current.getTime()); - } - - // Close popup.. - ((VPopupCalendar) datefield).closeCalendarPanel(); - - // ..and focus the textfield - ((VPopupCalendar) datefield).focus(); - } + // TODO fire close event return true; } @@ -1072,51 +899,23 @@ public class VCalendarPanel extends FocusableFlexTable implements return false; } - Date showingDate = datefield.getShowingDate(); - Widget currentSelection = days.getWidget(selectedRow, selectedColumn); - /* * Jumps to the next day. */ if (keycode == getForwardKey() && !shift) { - // Calculate new showing date - Date newCurrentDate = new Date(showingDate.getYear(), showingDate - .getMonth(), showingDate.getDate(), showingDate.getHours(), - showingDate.getMinutes(), showingDate.getSeconds()); - newCurrentDate.setDate(newCurrentDate.getDate() + 1); - - if (newCurrentDate.getMonth() == showingDate.getMonth()) { - // Month did not change, only move the selection - - showingDate.setDate(showingDate.getDate() + 1); - - if (currentSelection != null) { - currentSelection.removeStyleDependentName("selected"); + // Calculate new showing value - // Calculate new selection - if (selectedColumn == 7) { - selectedColumn = 1; - selectedRow++; - } else { - selectedColumn++; - } - } + Date newCurrentDate = (Date) focusedDate.clone(); - // Set new selection - currentSelection = days.getWidget(selectedRow, selectedColumn); - currentSelection.addStyleDependentName("selected"); + newCurrentDate.setDate(newCurrentDate.getDate() + 1); + if (newCurrentDate.getMonth() == focusedDate.getMonth()) { + // Month did not change, only move the selection + focusDay(focusedDate.getDate() + 1); } else { // If the month changed we need to re-render the calendar - showingDate.setDate(showingDate.getDate() + 1); - updateCalendarOnly(); - - selectedRow = 1; - selectedColumn = getFirstWeekdayColumn(); - - // Set new selection - currentSelection = days.getWidget(selectedRow, selectedColumn); - currentSelection.addStyleDependentName("selected"); + focusedDate.setDate(focusedDate.getDate() + 1); + renderCalendar(); } return true; @@ -1125,45 +924,17 @@ public class VCalendarPanel extends FocusableFlexTable implements * Jumps to the previous day */ } else if (keycode == getBackwardKey() && !shift) { - // Calculate new showing date - Date newCurrentDate = new Date(showingDate.getYear(), showingDate - .getMonth(), showingDate.getDate(), showingDate.getHours(), - showingDate.getMinutes(), showingDate.getSeconds()); + // Calculate new showing value + Date newCurrentDate = (Date) focusedDate.clone(); newCurrentDate.setDate(newCurrentDate.getDate() - 1); - if (newCurrentDate.getMonth() == showingDate.getMonth()) { + if (newCurrentDate.getMonth() == focusedDate.getMonth()) { // Month did not change, only move the selection - - showingDate.setDate(showingDate.getDate() - 1); - - if (currentSelection != null) { - currentSelection.removeStyleDependentName("selected"); - - // Calculate new selection - if (selectedColumn == 1) { - selectedColumn = 7; - selectedRow--; - } else { - selectedColumn--; - } - - } - // Set new selection - currentSelection = days.getWidget(selectedRow, selectedColumn); - currentSelection.addStyleDependentName("selected"); - + focusDay(focusedDate.getDate() - 1); } else { // If the month changed we need to re-render the calendar - showingDate.setDate(showingDate.getDate() - 1); - updateCalendarOnly(); - - int[] pos = getLastWeekdayColumn(); - selectedRow = pos[0]; - selectedColumn = pos[1]; - - // Set new selection - currentSelection = days.getWidget(selectedRow, selectedColumn); - currentSelection.addStyleDependentName("selected"); + focusedDate.setDate(focusedDate.getDate() - 1); + renderCalendar(); } return true; @@ -1172,45 +943,18 @@ public class VCalendarPanel extends FocusableFlexTable implements * Jumps one week back in the calendar */ } else if (keycode == getPreviousKey() && !shift) { - // Calculate new showing date - Date newCurrentDate = new Date(showingDate.getYear(), showingDate - .getMonth(), showingDate.getDate(), showingDate.getHours(), - showingDate.getMinutes(), showingDate.getSeconds()); + // Calculate new showing value + Date newCurrentDate = (Date) focusedDate.clone(); newCurrentDate.setDate(newCurrentDate.getDate() - 7); - if (newCurrentDate.getMonth() == showingDate.getMonth() - && selectedRow > 1) { + if (newCurrentDate.getMonth() == focusedDate.getMonth() + && focusedRow > 1) { // Month did not change, only move the selection - - showingDate.setDate(showingDate.getDate() - 7); - - if (currentSelection != null) { - currentSelection.removeStyleDependentName("selected"); - } - - selectedRow--; - - // Set new selection - currentSelection = days.getWidget(selectedRow, selectedColumn); - currentSelection.addStyleDependentName("selected"); - + focusDay(focusedDate.getDate() - 7); } else { // If the month changed we need to re-render the calendar - showingDate.setDate(showingDate.getDate() - 7); - updateCalendarOnly(); - - int[] pos = getLastWeekdayColumn(); - selectedRow = pos[0]; - - // Set new selection - currentSelection = days.getWidget(selectedRow, selectedColumn); - if (currentSelection == null) { - selectedRow--; - currentSelection = days.getWidget(selectedRow, - selectedColumn); - } - - currentSelection.addStyleDependentName("selected"); + focusedDate.setDate(focusedDate.getDate() - 7); + renderCalendar(); } return true; @@ -1219,122 +963,70 @@ public class VCalendarPanel extends FocusableFlexTable implements * Jumps one week forward in the calendar */ } else if (keycode == getNextKey() && !ctrl && !shift) { - // Calculate new showing date - Date newCurrentDate = new Date(showingDate.getYear(), showingDate - .getMonth(), showingDate.getDate(), showingDate.getHours(), - showingDate.getMinutes(), showingDate.getSeconds()); + // Calculate new showing value + Date newCurrentDate = (Date) focusedDate.clone(); newCurrentDate.setDate(newCurrentDate.getDate() + 7); - if (newCurrentDate.getMonth() == showingDate.getMonth()) { + if (newCurrentDate.getMonth() == focusedDate.getMonth()) { // Month did not change, only move the selection - - showingDate.setDate(showingDate.getDate() + 7); - - if (currentSelection != null) { - currentSelection.removeStyleDependentName("selected"); - } - - selectedRow++; - - // Set new selection - currentSelection = days.getWidget(selectedRow, selectedColumn); - currentSelection.addStyleDependentName("selected"); - + focusDay(focusedDate.getDate() + 7); } else { // If the month changed we need to re-render the calendar - showingDate.setDate(showingDate.getDate() + 7); - updateCalendarOnly(); - - selectedRow = 1; + focusedDate.setDate(focusedDate.getDate() + 7); + renderCalendar(); - // Set new selection - currentSelection = days.getWidget(selectedRow, selectedColumn); - if (currentSelection == null) { - selectedRow++; - currentSelection = days.getWidget(selectedRow, - selectedColumn); - } - - currentSelection.addStyleDependentName("selected"); } return true; /* - * Selects the date that is chosen + * Selects the value that is chosen */ } else if (keycode == getSelectKey() && !shift) { - InlineHTML selection = (InlineHTML) days.getWidget(selectedRow, - selectedColumn); - dateClickHandler.selectDate(selection.getText()); - - if (datefield instanceof VTextualDate) { - ((VTextualDate) datefield).focus(); - } - + selectFocused(); + onSubmit(); // submit return true; - - /* - * Closes the date popup - */ - } else if (keycode == getCloseKey() || keycode == KeyCodes.KEY_TAB) { - if (datefield instanceof VPopupCalendar) { - - // Restore showing date the selected date - Date showing = datefield.getShowingDate(); - Date current = datefield.getCurrentDate(); - - if (current != null && current != null) { - showing.setTime(current.getTime()); - } - - // Close popup.. - ((VPopupCalendar) datefield).closeCalendarPanel(); - - // ..and focus the textfield - ((VPopupCalendar) datefield).focus(); - - } + } else if (keycode == getCloseKey()) { + onCancel(); + // TODO close event return true; /* - * Selects the next month + * Jumps to the next month */ } else if (shift && keycode == getForwardKey()) { - selectNextMonth(); + focusNextMonth(); return true; /* - * Selects the previous month + * Jumps to the previous month */ } else if (shift && keycode == getBackwardKey()) { - selectPreviousMonth(); + focusPreviousMonth(); return true; /* - * Selects the next year + * Jumps to the next year */ } else if (shift && keycode == getPreviousKey()) { - selectNextYear(1); + focusNextYear(1); return true; /* - * Selects the previous year + * Jumps to the previous year */ } else if (shift && keycode == getNextKey()) { - selectPreviousYear(1); + focusPreviousYear(1); return true; /* * Resets the selection */ } else if (keycode == getResetKey() && !shift) { - // Restore showing date the selected date - Date showing = datefield.getShowingDate(); - Date current = datefield.getCurrentDate(); - showing.setTime(current.getTime()); - updateCalendar(); + // Restore showing value the selected value + focusedDate.setTime(value.getTime()); + renderCalendar(); return true; } @@ -1353,9 +1045,8 @@ public class VCalendarPanel extends FocusableFlexTable implements * @return Return true if key press was handled by the component, else * return false */ - @SuppressWarnings("deprecation") protected boolean handleNavigation(int keycode, boolean ctrl, boolean shift) { - if (!datefield.isEnabled() || datefield.isReadonly() || changingView) { + if (!isEnabled() || isReadonly()) { return false; } @@ -1372,13 +1063,6 @@ public class VCalendarPanel extends FocusableFlexTable implements } else { - // Do not close the window on tab key but move the - // focus down to the time - if (keycode == KeyCodes.KEY_TAB) { - time.setFocus(true); - return true; - } - return handleNavigationDayMode(keycode, ctrl, shift); } @@ -1396,7 +1080,7 @@ public class VCalendarPanel extends FocusableFlexTable implements } /** - * Returns the select key which selects the date. By default this is the + * 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. * @@ -1461,4 +1145,492 @@ public class VCalendarPanel extends FocusableFlexTable implements return KeyCodes.KEY_UP; } + /* + * (non-Javadoc) + * + * @see + * com.google.gwt.event.dom.client.MouseOutHandler#onMouseOut(com.google + * .gwt.event.dom.client.MouseOutEvent) + */ + 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) + */ + public void onMouseDown(MouseDownEvent event) { + // Allow user to click-n-hold 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.getSource() instanceof VEventButton) { + final Widget sender = (Widget) 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) + */ + public void onMouseUp(MouseUpEvent event) { + if (mouseTimer != null) { + mouseTimer.cancel(); + } + } + + /** + * Sets the data of the Panel. + * + * @param currentDate + * The date to set + */ + public void setDate(Date currentDate) { + + // Check that we are not re-rendering a already active date if + if (currentDate == value && currentDate != null) { + return; + } + + Date oldValue = value; + value = currentDate; + + if (focusedDate == null && value != null) { + focusedDate = (Date) value.clone(); + } + + // Re-render calendar if the month or year has changed + if (oldValue == null || value == null + || oldValue.getYear() != value.getYear() + || oldValue.getMonth() != value.getMonth()) { + renderCalendar(); + } else { + focusDay(currentDate.getDate()); + selectFocused(); + } + + if (!hasFocus) + focusDay(-1); + } + + /** + * 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 msec; + + private ListBox ampm; + + private ListBox lastField; + + /** + * Constructor + */ + public VTime() { + super(); + setStyleName(VDateField.CLASSNAME + "-time"); + buildTime(); + } + + private ListBox createListBox() { + ListBox lb = new ListBox(); + lb.setStyleName(VNativeSelect.CLASSNAME); + 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() >= VDateField.RESOLUTION_MIN) { + mins = createListBox(); + for (int i = 0; i < 60; i++) { + mins.addItem((i < 10) ? "0" + i : "" + i); + } + mins.addChangeHandler(this); + } + if (getResolution() >= VDateField.RESOLUTION_SEC) { + sec = createListBox(); + for (int i = 0; i < 60; i++) { + sec.addItem((i < 10) ? "0" + i : "" + i); + } + sec.addChangeHandler(this); + } + if (getResolution() == VDateField.RESOLUTION_MSEC) { + msec = createListBox(); + for (int i = 0; i < 1000; i++) { + if (i < 10) { + msec.addItem("00" + i); + } else if (i < 100) { + msec.addItem("0" + i); + } else { + msec.addItem("" + i); + } + } + msec.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); + lastField = hours; + } + + if (getResolution() >= VDateField.RESOLUTION_MIN) { + add(new VLabel(delimiter)); + if (isReadonly()) { + final int m = mins.getSelectedIndex(); + add(new VLabel(m < 10 ? "0" + m : "" + m)); + } else { + add(mins); + lastField = mins; + } + } + if (getResolution() >= VDateField.RESOLUTION_SEC) { + add(new VLabel(delimiter)); + if (isReadonly()) { + final int s = sec.getSelectedIndex(); + add(new VLabel(s < 10 ? "0" + s : "" + s)); + } else { + add(sec); + lastField = sec; + } + } + if (getResolution() == VDateField.RESOLUTION_MSEC) { + add(new VLabel(".")); + if (isReadonly()) { + final int m = getMilliseconds(); + final String ms = m < 100 ? "0" + m : "" + m; + add(new VLabel(m < 10 ? "0" + ms : ms)); + } else { + add(msec); + lastField = msec; + } + } + if (getResolution() == VDateField.RESOLUTION_HOUR) { + add(new VLabel(delimiter + "00")); // o'clock + } + if (getDateTimeService().isTwelveHourClock()) { + add(new VLabel(" ")); + if (isReadonly()) { + int i = 0; + if (value != null) { + i = (value.getHours() < 12) ? 0 : 1; + } + add(new VLabel(ampm.getItemText(i))); + } else { + add(ampm); + lastField = ampm; + } + } + + if (isReadonly()) { + return; + } + + // Update times + updateTimes(); + + ListBox lastDropDown = (ListBox) getWidget(getWidgetCount() - 1); + lastDropDown.addKeyDownHandler(new KeyDownHandler() { + 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); + } + } + } + }); + + } + + /** + * Updates the valus to correspond to the values in value + */ + public void updateTimes() { + boolean selected = true; + if (value == null) { + value = new Date(); + selected = false; + } + 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() >= VDateField.RESOLUTION_MIN) { + mins.setSelectedIndex(value.getMinutes()); + } + if (getResolution() >= VDateField.RESOLUTION_SEC) { + sec.setSelectedIndex(value.getSeconds()); + } + if (getResolution() == VDateField.RESOLUTION_MSEC) { + if (selected) { + msec.setSelectedIndex(getMilliseconds()); + } else { + msec.setSelectedIndex(0); + } + } + 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 (msec != null) { + msec.setEnabled(isEnabled()); + } + if (ampm != null) { + ampm.setEnabled(isEnabled()); + } + + } + + private int getMilliseconds() { + return DateTimeService.getMilliseconds(value); + } + + private DateTimeService getDateTimeService() { + if (dateTimeService == null) { + dateTimeService = new DateTimeService(); + } + return dateTimeService; + } + + /* + * (non-Javadoc) + * + * @see + * com.google.gwt.event.dom.client.ChangeHandler#onChange(com.google.gwt + * .event.dom.client.ChangeEvent) + */ + 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); + } else if (event.getSource() == mins) { + final int m = mins.getSelectedIndex(); + value.setMinutes(m); + } else if (event.getSource() == sec) { + final int s = sec.getSelectedIndex(); + value.setSeconds(s); + } else if (event.getSource() == msec) { + final int ms = msec.getSelectedIndex(); + DateTimeService.setMilliseconds(value, ms); + } else if (event.getSource() == ampm) { + final int h = hours.getSelectedIndex() + + (ampm.getSelectedIndex() * 12); + value.setHours(h); + } + } + + } + + private class Day extends InlineHTML { + private static final String BASECLASS = VDateField.CLASSNAME + + "-calendarpanel-day"; + private final int day; + + Day(int dayOfMonth) { + super("" + dayOfMonth); + setStyleName(BASECLASS); + day = dayOfMonth; + addClickHandler(dayClickHandler); + } + + public int getDay() { + return day; + } + } + + 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 value change listener is triggered when the focused date changes by + * user either clicking on a new date or by using the keyboard. + * + * @param listener + * The listener to trigger + */ + public void setValueChangeListener(ValueChangeListener listener) { + this.valueChangeListener = 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) + */ + public void onBlur(final BlurEvent event) { + if (isAttached()) { + DeferredCommand.addCommand(new Command() { + public void execute() { + if (!hasFocus) { + onTabOut(event); + } + } + }); + } + + if (event.getSource() instanceof VCalendarPanel) { + hasFocus = false; + focusDay(-1); + } + } + + /* + * (non-Javadoc) + * + * @see + * com.google.gwt.event.dom.client.FocusHandler#onFocus(com.google.gwt.event + * .dom.client.FocusEvent) + */ + public void onFocus(FocusEvent event) { + if (event.getSource() instanceof VCalendarPanel) { + hasFocus = true; + + // Focuses the current day if the calendar shows the days + focusDay(focusedDay.getDay()); + } + } + } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VDateField.java b/src/com/vaadin/terminal/gwt/client/ui/VDateField.java index 17fe4f93ba..83079d4ed9 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VDateField.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VDateField.java @@ -20,19 +20,19 @@ public class VDateField extends FlowPanel implements Paintable, Field { public static final String CLASSNAME = "v-datefield"; - protected String id; + private String id; - protected ApplicationConnection client; + private ApplicationConnection client; protected boolean immediate; - public static final int RESOLUTION_YEAR = 0; - public static final int RESOLUTION_MONTH = 1; - public static final int RESOLUTION_DAY = 2; - public static final int RESOLUTION_HOUR = 3; - public static final int RESOLUTION_MIN = 4; - public static final int RESOLUTION_SEC = 5; - public static final int RESOLUTION_MSEC = 6; + public static final int RESOLUTION_YEAR = 1; + public static final int RESOLUTION_MONTH = 2; + public static final int RESOLUTION_DAY = 4; + public static final int RESOLUTION_HOUR = 8; + public static final int RESOLUTION_MIN = 16; + public static final int RESOLUTION_SEC = 32; + public static final int RESOLUTION_MSEC = 64; public static final String WEEK_NUMBERS = "wn"; @@ -62,8 +62,6 @@ public class VDateField extends FlowPanel implements Paintable, Field { * specified. */ protected Date date = null; - // e.g when paging a calendar, before actually selecting - protected Date showingDate = new Date(); protected DateTimeService dts; @@ -156,12 +154,9 @@ public class VDateField extends FlowPanel implements Paintable, Field { if (year > -1) { date = new Date((long) getTime(year, month, day, hour, min, sec, msec)); - showingDate.setTime(date.getTime()); } else { date = null; - showingDate = new Date(); } - } /* @@ -189,23 +184,11 @@ public class VDateField extends FlowPanel implements Paintable, Field { }-*/; public int getMilliseconds() { - if (date == null) { - return 0; - } - - return (int) (date.getTime() - date.getTime() / 1000 * 1000); + return DateTimeService.getMilliseconds(date); } public void setMilliseconds(int ms) { - date.setTime(date.getTime() / 1000 * 1000 + ms); - } - - public int getShowingMilliseconds() { - return (int) (showingDate.getTime() - showingDate.getTime() / 1000 * 1000); - } - - public void setShowingMilliseconds(int ms) { - showingDate.setTime(showingDate.getTime() / 1000 * 1000 + ms); + DateTimeService.setMilliseconds(date, ms); } public int getCurrentResolution() { @@ -232,14 +215,6 @@ public class VDateField extends FlowPanel implements Paintable, Field { this.date = date; } - public Date getShowingDate() { - return showingDate; - } - - public void setShowingDate(Date date) { - showingDate = date; - } - public boolean isImmediate() { return immediate; } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VDateFieldCalendar.java b/src/com/vaadin/terminal/gwt/client/ui/VDateFieldCalendar.java index fec6756da6..e3eee5de44 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VDateFieldCalendar.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VDateFieldCalendar.java @@ -4,23 +4,96 @@ package com.vaadin.terminal.gwt.client.ui; +import java.util.Date; + +import com.google.gwt.event.dom.client.DomEvent; import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.DateTimeService; import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.ui.VCalendarPanel.FocusOutListener; +import com.vaadin.terminal.gwt.client.ui.VCalendarPanel.SubmitListener; public class VDateFieldCalendar extends VDateField { - private final VCalendarPanel date; + private final VCalendarPanel caleandarPanel; public VDateFieldCalendar() { super(); - date = new VCalendarPanel(this); - add(date); + caleandarPanel = new VCalendarPanel(); + add(caleandarPanel); + caleandarPanel.setSubmitListener(new SubmitListener() { + public void onSubmit() { + updateValueFromPanel(); + } + + public void onCancel() { + // TODO Auto-generated method stub + + } + }); + caleandarPanel.setFocusOutListener(new FocusOutListener() { + public boolean onFocusOut(DomEvent event) { + updateValueFromPanel(); + return false; + } + }); } @Override public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { super.updateFromUIDL(uidl, client); - date.updateCalendar(); + caleandarPanel.setShowISOWeekNumbers(isShowISOWeekNumbers()); + caleandarPanel.setDateTimeService(getDateTimeService()); + caleandarPanel.setResolution(getCurrentResolution()); + Date currentDate = getCurrentDate(); + if (currentDate != null) { + caleandarPanel.setDate(new Date(currentDate.getTime())); + } else { + caleandarPanel.setDate(null); + } + + // Update possible changes + caleandarPanel.renderCalendar(); } + private void updateValueFromPanel() { + Date date2 = caleandarPanel.getDate(); + Date currentDate = getCurrentDate(); + if (currentDate == null || date2.getTime() != currentDate.getTime()) { + setCurrentDate(date2); + getClient().updateVariable(getId(), "year", date2.getYear() + 1900, + false); + if (getCurrentResolution() > VDateField.RESOLUTION_YEAR) { + getClient().updateVariable(getId(), "month", + date2.getMonth() + 1, false); + if (getCurrentResolution() > RESOLUTION_MONTH) { + getClient().updateVariable(getId(), "day", date2.getDate(), + false); + if (getCurrentResolution() > RESOLUTION_DAY) { + getClient().updateVariable(getId(), "hour", + date2.getHours(), false); + if (getCurrentResolution() > RESOLUTION_HOUR) { + getClient().updateVariable(getId(), "min", + date2.getMinutes(), false); + if (getCurrentResolution() > RESOLUTION_MIN) { + getClient().updateVariable(getId(), "sec", + date2.getSeconds(), false); + if (getCurrentResolution() > RESOLUTION_MSEC) { + getClient().updateVariable( + getId(), + "msec", + DateTimeService + .getMilliseconds(date2), + false); + } + } + } + } + } + } + if (isImmediate()) { + getClient().sendPendingVariableChanges(); + } + } + } } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VPopupCalendar.java b/src/com/vaadin/terminal/gwt/client/ui/VPopupCalendar.java index 857e52e0b6..b60705264e 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VPopupCalendar.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VPopupCalendar.java @@ -4,8 +4,12 @@ package com.vaadin.terminal.gwt.client.ui; +import java.util.Date; + +import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.event.dom.client.DomEvent; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.logical.shared.CloseEvent; import com.google.gwt.event.logical.shared.CloseHandler; @@ -17,8 +21,12 @@ import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.PopupPanel; import com.google.gwt.user.client.ui.PopupPanel.PositionCallback; import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.DateTimeService; import com.vaadin.terminal.gwt.client.Paintable; import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.ui.VCalendarPanel.FocusOutListener; +import com.vaadin.terminal.gwt.client.ui.VCalendarPanel.SubmitListener; +import com.vaadin.terminal.gwt.client.ui.VCalendarPanel.ValueChangeListener; /** * Represents a date selection component with a text field and a popup date @@ -51,7 +59,28 @@ public class VPopupCalendar extends VTextualDate implements Paintable, Field, calendarToggle.getElement().setTabIndex(-1); add(calendarToggle); - calendar = new VCalendarPanel(this); + calendar = GWT.create(VCalendarPanel.class); + calendar.setFocusOutListener(new FocusOutListener() { + public boolean onFocusOut(DomEvent event) { + ApplicationConnection.getConsole().log( + "Focus out event, due to " + + event.getNativeEvent().getType()); + event.preventDefault(); + closeCalendarPanel(); + return true; + } + }); + + calendar.setSubmitListener(new SubmitListener() { + public void onSubmit() { + updateValue(calendar.getDate()); + closeCalendarPanel(); + } + + public void onCancel() { + closeCalendarPanel(); + } + }); popup = new VOverlay(true, true, true); popup.setStyleName(VDateField.CLASSNAME + "-popup"); @@ -65,6 +94,46 @@ public class VPopupCalendar extends VTextualDate implements Paintable, Field, } + private void updateValue(Date newDate) { + Date currentDate = getCurrentDate(); + if (currentDate == null || newDate.getTime() != currentDate.getTime()) { + setCurrentDate(newDate); + getClient().updateVariable(getId(), "year", + newDate.getYear() + 1900, false); + if (getCurrentResolution() > VDateField.RESOLUTION_YEAR) { + getClient().updateVariable(getId(), "month", + newDate.getMonth() + 1, false); + if (getCurrentResolution() > RESOLUTION_MONTH) { + getClient().updateVariable(getId(), "day", + newDate.getDate(), false); + if (getCurrentResolution() > RESOLUTION_DAY) { + getClient().updateVariable(getId(), "hour", + newDate.getHours(), false); + if (getCurrentResolution() > RESOLUTION_HOUR) { + getClient().updateVariable(getId(), "min", + newDate.getMinutes(), false); + if (getCurrentResolution() > RESOLUTION_MIN) { + getClient().updateVariable(getId(), "sec", + newDate.getSeconds(), false); + if (getCurrentResolution() == RESOLUTION_MSEC) { + getClient().updateVariable( + getId(), + "msec", + DateTimeService + .getMilliseconds(newDate), + false); + } + } + } + } + } + } + if (isImmediate()) { + getClient().sendPendingVariableChanges(); + } + } + } + /* * (non-Javadoc) * @@ -83,11 +152,20 @@ public class VPopupCalendar extends VTextualDate implements Paintable, Field, popup.setStyleName(VDateField.CLASSNAME + "-popup " + VDateField.CLASSNAME + "-" + resolutionToString(currentResolution)); - if (date != null) { - calendar.updateCalendar(); - } + calendar.setDateTimeService(getDateTimeService()); + calendar.setShowISOWeekNumbers(isShowISOWeekNumbers()); + calendar.setResolution(currentResolution); calendarToggle.setEnabled(enabled); + if (currentResolution <= RESOLUTION_MONTH) { + calendar.setValueChangeListener(new ValueChangeListener() { + public void changed(Date date) { + setCurrentDate(date); + buildDate(); + } + }); + } + if (readonly) { calendarToggle.addStyleName(CLASSNAME + "-button-readonly"); } else { @@ -113,21 +191,6 @@ public class VPopupCalendar extends VTextualDate implements Paintable, Field, super.setStyleName(style + " " + CLASSNAME + "-popupcalendar"); } - /** - * Set the popup panel to be displayed when clicking the calendar button. - * This is usually used when we want to extend the VCalendarPanel and use - * new keyboard bindings. - * - * @param panel - * The custom calendar panel - */ - public void setCalendarPanel(VCalendarPanel panel) { - if (panel != null) { - calendar = panel; - calendar.updateCalendar(); - } - } - /** * Opens the calendar panel popup */ @@ -135,7 +198,13 @@ public class VPopupCalendar extends VTextualDate implements Paintable, Field, if (!open && !readonly) { open = true; - calendar.updateCalendar(); + final Date start = new Date(); + + if (getCurrentDate() != null) { + calendar.setDate((Date) getCurrentDate().clone()); + } else { + calendar.setDate(new Date()); + } // clear previous values popup.setWidth(""); @@ -183,6 +252,12 @@ public class VPopupCalendar extends VTextualDate implements Paintable, Field, popup.setPopupPosition(l, t + calendarToggle.getOffsetHeight() + 2); + Date end = new Date(); + + ApplicationConnection.getConsole().log( + "Rendering VCalendar took " + + (end.getTime() - start.getTime() + "ms")); + /* * We have to wait a while before focusing since the popup * needs to be opened before we can focus @@ -197,6 +272,9 @@ public class VPopupCalendar extends VTextualDate implements Paintable, Field, focusTimer.schedule(100); } }); + } else { + ApplicationConnection.getConsole().error( + "Cannot reopen popup, it is already open!"); } } @@ -222,7 +300,15 @@ public class VPopupCalendar extends VTextualDate implements Paintable, Field, */ public void onClose(CloseEvent event) { if (event.getSource() == popup) { + if (getCurrentResolution() <= VDateField.RESOLUTION_MONTH) { + // due to UI limitations always fetch date from popup if + // resolution is month or year + date = calendar.getDate(); + } buildDate(); + focus(); + + // TODO resolve what the "Sigh." is all about and document it here // Sigh. Timer t = new Timer() { @Override diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTextualDate.java b/src/com/vaadin/terminal/gwt/client/ui/VTextualDate.java index 92e3aad39b..19ae40fae5 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VTextualDate.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VTextualDate.java @@ -4,8 +4,6 @@ package com.vaadin.terminal.gwt.client.ui; -import java.util.Date; - import com.google.gwt.event.dom.client.BlurEvent; import com.google.gwt.event.dom.client.BlurHandler; import com.google.gwt.event.dom.client.ChangeEvent; @@ -66,10 +64,11 @@ public class VTextualDate extends VDateField implements Paintable, Field, text.setText(""); setPrompting(false); } - if (client != null - && client.hasEventListeners(VTextualDate.this, + if (getClient() != null + && getClient().hasEventListeners(VTextualDate.this, EventId.FOCUS)) { - client.updateVariable(id, EventId.FOCUS, "", true); + getClient() + .updateVariable(getId(), EventId.FOCUS, "", true); } } }); @@ -83,10 +82,10 @@ public class VTextualDate extends VDateField implements Paintable, Field, if (prompting) { text.setText(readonly ? "" : inputPrompt); } - if (client != null - && client.hasEventListeners(VTextualDate.this, + if (getClient() != null + && getClient().hasEventListeners(VTextualDate.this, EventId.BLUR)) { - client.updateVariable(id, EventId.BLUR, "", true); + getClient().updateVariable(getId(), EventId.BLUR, "", true); } } }); @@ -232,8 +231,8 @@ public class VTextualDate extends VDateField implements Paintable, Field, addStyleName(PARSE_ERROR_CLASSNAME); // this is a hack that may eventually be removed - client.updateVariable(id, "lastInvalidDateString", text - .getText(), false); + getClient().updateVariable(getId(), "lastInvalidDateString", + text.getText(), false); date = null; } } else { @@ -242,49 +241,61 @@ public class VTextualDate extends VDateField implements Paintable, Field, removeStyleName(PARSE_ERROR_CLASSNAME); } // always send the date string - client.updateVariable(id, "dateString", text.getText(), false); - - if (date != null) { - showingDate = new Date(date.getTime()); - } + getClient() + .updateVariable(getId(), "dateString", text.getText(), false); // Update variables // (only the smallest defining resolution needs to be // immediate) - client.updateVariable(id, "year", date != null ? date.getYear() + 1900 - : -1, currentResolution == VDateField.RESOLUTION_YEAR - && immediate); + getClient().updateVariable(getId(), "year", + date != null ? date.getYear() + 1900 : -1, + currentResolution == VDateField.RESOLUTION_YEAR && immediate); if (currentResolution >= VDateField.RESOLUTION_MONTH) { - client.updateVariable(id, "month", + getClient().updateVariable( + getId(), + "month", date != null ? date.getMonth() + 1 : -1, currentResolution == VDateField.RESOLUTION_MONTH && immediate); } if (currentResolution >= VDateField.RESOLUTION_DAY) { - client - .updateVariable(id, "day", date != null ? date.getDate() - : -1, + getClient() + .updateVariable( + getId(), + "day", + date != null ? date.getDate() : -1, currentResolution == VDateField.RESOLUTION_DAY && immediate); } if (currentResolution >= VDateField.RESOLUTION_HOUR) { - client.updateVariable(id, "hour", date != null ? date.getHours() - : -1, currentResolution == VDateField.RESOLUTION_HOUR - && immediate); + getClient().updateVariable( + getId(), + "hour", + date != null ? date.getHours() : -1, + currentResolution == VDateField.RESOLUTION_HOUR + && immediate); } if (currentResolution >= VDateField.RESOLUTION_MIN) { - client.updateVariable(id, "min", date != null ? date.getMinutes() - : -1, currentResolution == VDateField.RESOLUTION_MIN - && immediate); + getClient() + .updateVariable( + getId(), + "min", + date != null ? date.getMinutes() : -1, + currentResolution == VDateField.RESOLUTION_MIN + && immediate); } if (currentResolution >= VDateField.RESOLUTION_SEC) { - client.updateVariable(id, "sec", date != null ? date.getSeconds() - : -1, currentResolution == VDateField.RESOLUTION_SEC - && immediate); + getClient() + .updateVariable( + getId(), + "sec", + date != null ? date.getSeconds() : -1, + currentResolution == VDateField.RESOLUTION_SEC + && immediate); } if (currentResolution == VDateField.RESOLUTION_MSEC) { - client.updateVariable(id, "msec", date != null ? getMilliseconds() - : -1, immediate); + getClient().updateVariable(getId(), "msec", + date != null ? getMilliseconds() : -1, immediate); } } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTime.java b/src/com/vaadin/terminal/gwt/client/ui/VTime.java deleted file mode 100644 index 99cf94b4c4..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/VTime.java +++ /dev/null @@ -1,464 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.Date; - -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.event.dom.client.KeyPressEvent; -import com.google.gwt.event.dom.client.KeyPressHandler; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.Timer; -import com.google.gwt.user.client.ui.ListBox; -import com.vaadin.terminal.gwt.client.BrowserInfo; - -public class VTime extends FocusableFlowPanel implements ChangeHandler, - KeyPressHandler, KeyDownHandler, FocusHandler { - - private final VDateField datefield; - - private final VCalendarPanel calendar; - - private ListBox hours; - - private ListBox mins; - - private ListBox sec; - - private ListBox msec; - - private AMPMListBox ampm; - - private int resolution = VDateField.RESOLUTION_HOUR; - - private boolean readonly; - - /** - * The AM/PM Listbox yields the keyboard focus to the calendar - */ - private class AMPMListBox extends ListBox { - public AMPMListBox() { - super(); - sinkEvents(Event.ONKEYDOWN); - } - - /* - * (non-Javadoc) - * - * @see - * com.google.gwt.user.client.ui.Widget#onBrowserEvent(com.google.gwt - * .user.client.Event) - */ - @Override - public void onBrowserEvent(Event event) { - super.onBrowserEvent(event); - - if (event.getKeyCode() == KeyCodes.KEY_TAB) { - event.preventDefault(); - - /* - * Wait until the current event has been processed. If the timer - * is left out the focus will move to the VTime-class not the - * panel itself. Weird. - */ - Timer t = new Timer() { - @Override - public void run() { - calendar.setFocus(true); - } - }; - t.schedule(1); - } - } - } - - /** - * Constructor - * - * @param parent - * The DateField related to this instance - * @param panel - * The panel where this this instance is embedded - */ - public VTime(VDateField parent, VCalendarPanel panel) { - super(); - datefield = parent; - calendar = panel; - setStyleName(VDateField.CLASSNAME + "-time"); - - /* - * 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); - } - - /** - * Constructs the ListBoxes and updates their value - * - * @param redraw - * Should new instances of the listboxes be created - */ - private void buildTime(boolean redraw) { - final boolean thc = datefield.getDateTimeService().isTwelveHourClock(); - if (redraw) { - clear(); - final int numHours = thc ? 12 : 24; - hours = new ListBox(); - hours.setStyleName(VNativeSelect.CLASSNAME); - for (int i = 0; i < numHours; i++) { - hours.addItem((i < 10) ? "0" + i : "" + i); - } - hours.addChangeHandler(this); - if (thc) { - ampm = new AMPMListBox(); - ampm.setStyleName(VNativeSelect.CLASSNAME); - final String[] ampmText = datefield.getDateTimeService() - .getAmPmStrings(); - calendar.setFocus(true); - ampm.addItem(ampmText[0]); - ampm.addItem(ampmText[1]); - ampm.addChangeHandler(this); - } - - if (datefield.getCurrentResolution() >= VDateField.RESOLUTION_MIN) { - mins = new ListBox(); - mins.setStyleName(VNativeSelect.CLASSNAME); - for (int i = 0; i < 60; i++) { - mins.addItem((i < 10) ? "0" + i : "" + i); - } - mins.addChangeHandler(this); - } - if (datefield.getCurrentResolution() >= VDateField.RESOLUTION_SEC) { - sec = new ListBox(); - sec.setStyleName(VNativeSelect.CLASSNAME); - for (int i = 0; i < 60; i++) { - sec.addItem((i < 10) ? "0" + i : "" + i); - } - sec.addChangeHandler(this); - } - if (datefield.getCurrentResolution() == VDateField.RESOLUTION_MSEC) { - msec = new ListBox(); - msec.setStyleName(VNativeSelect.CLASSNAME); - for (int i = 0; i < 1000; i++) { - if (i < 10) { - msec.addItem("00" + i); - } else if (i < 100) { - msec.addItem("0" + i); - } else { - msec.addItem("" + i); - } - } - msec.addChangeHandler(this); - } - - final String delimiter = datefield.getDateTimeService() - .getClockDelimeter(); - final boolean ro = datefield.isReadonly(); - - if (ro) { - int h = 0; - if (datefield.getCurrentDate() != null) { - h = datefield.getCurrentDate().getHours(); - } - if (thc) { - h -= h < 12 ? 0 : 12; - } - add(new VLabel(h < 10 ? "0" + h : "" + h)); - } else { - add(hours); - } - - if (datefield.getCurrentResolution() >= VDateField.RESOLUTION_MIN) { - add(new VLabel(delimiter)); - if (ro) { - final int m = mins.getSelectedIndex(); - add(new VLabel(m < 10 ? "0" + m : "" + m)); - } else { - add(mins); - } - } - if (datefield.getCurrentResolution() >= VDateField.RESOLUTION_SEC) { - add(new VLabel(delimiter)); - if (ro) { - final int s = sec.getSelectedIndex(); - add(new VLabel(s < 10 ? "0" + s : "" + s)); - } else { - add(sec); - } - } - if (datefield.getCurrentResolution() == VDateField.RESOLUTION_MSEC) { - add(new VLabel(".")); - if (ro) { - final int m = datefield.getMilliseconds(); - final String ms = m < 100 ? "0" + m : "" + m; - add(new VLabel(m < 10 ? "0" + ms : ms)); - } else { - add(msec); - } - } - if (datefield.getCurrentResolution() == VDateField.RESOLUTION_HOUR) { - add(new VLabel(delimiter + "00")); // o'clock - } - if (thc) { - add(new VLabel(" ")); - if (ro) { - add(new VLabel(ampm.getItemText(datefield.getCurrentDate() - .getHours() < 12 ? 0 : 1))); - } else { - add(ampm); - } - } - - if (ro) { - return; - } - } - - // Update times - Date cdate = datefield.getShowingDate(); - boolean selected = true; - if (cdate == null) { - cdate = new Date(); - selected = false; - } - if (thc) { - int h = cdate.getHours(); - ampm.setSelectedIndex(h < 12 ? 0 : 1); - h -= ampm.getSelectedIndex() * 12; - hours.setSelectedIndex(h); - } else { - hours.setSelectedIndex(cdate.getHours()); - } - if (datefield.getCurrentResolution() >= VDateField.RESOLUTION_MIN) { - mins.setSelectedIndex(cdate.getMinutes()); - } - if (datefield.getCurrentResolution() >= VDateField.RESOLUTION_SEC) { - sec.setSelectedIndex(cdate.getSeconds()); - } - if (datefield.getCurrentResolution() == VDateField.RESOLUTION_MSEC) { - if (selected) { - msec.setSelectedIndex(datefield.getMilliseconds()); - } else { - msec.setSelectedIndex(0); - } - } - if (thc) { - ampm.setSelectedIndex(cdate.getHours() < 12 ? 0 : 1); - } - - if (datefield.isReadonly() && !redraw) { - // Do complete redraw when in read-only status - clear(); - final String delimiter = datefield.getDateTimeService() - .getClockDelimeter(); - - int h = cdate.getHours(); - if (thc) { - h -= h < 12 ? 0 : 12; - } - add(new VLabel(h < 10 ? "0" + h : "" + h)); - - if (datefield.getCurrentResolution() >= VDateField.RESOLUTION_MIN) { - add(new VLabel(delimiter)); - final int m = mins.getSelectedIndex(); - add(new VLabel(m < 10 ? "0" + m : "" + m)); - } - if (datefield.getCurrentResolution() >= VDateField.RESOLUTION_SEC) { - add(new VLabel(delimiter)); - final int s = sec.getSelectedIndex(); - add(new VLabel(s < 10 ? "0" + s : "" + s)); - } - if (datefield.getCurrentResolution() == VDateField.RESOLUTION_MSEC) { - add(new VLabel(".")); - final int m = datefield.getMilliseconds(); - final String ms = m < 100 ? "0" + m : "" + m; - add(new VLabel(m < 10 ? "0" + ms : ms)); - } - if (datefield.getCurrentResolution() == VDateField.RESOLUTION_HOUR) { - add(new VLabel(delimiter + "00")); // o'clock - } - if (thc) { - add(new VLabel(" ")); - add(new VLabel(ampm.getItemText(cdate.getHours() < 12 ? 0 : 1))); - } - } - - final boolean enabled = datefield.isEnabled(); - hours.setEnabled(enabled); - if (mins != null) { - mins.setEnabled(enabled); - } - if (sec != null) { - sec.setEnabled(enabled); - } - if (msec != null) { - msec.setEnabled(enabled); - } - if (ampm != null) { - ampm.setEnabled(enabled); - } - - } - - /** - * Update the time ListBoxes - * - * @param redraw - * Should new instances of the listboxes be created - */ - public void updateTime(boolean redraw) { - buildTime(redraw || resolution != datefield.getCurrentResolution() - || readonly != datefield.isReadonly()); - if (datefield instanceof VTextualDate) { - ((VTextualDate) datefield).buildDate(); - } - resolution = datefield.getCurrentResolution(); - readonly = datefield.isReadonly(); - } - - /* - * (non-Javadoc) - * - * @see - * com.google.gwt.event.dom.client.ChangeHandler#onChange(com.google.gwt - * .event.dom.client.ChangeEvent) - */ - public void onChange(ChangeEvent event) { - if (datefield.getCurrentDate() == null) { - // was null on server, need to set - Date now = datefield.getShowingDate(); - if (now == null) { - now = new Date(); - datefield.setShowingDate(now); - } - datefield.setCurrentDate(new Date(now.getTime())); - - // Init variables with current time - notifyServerOfChanges(); - } - if (event.getSource() == hours) { - int h = hours.getSelectedIndex(); - if (datefield.getDateTimeService().isTwelveHourClock()) { - h = h + ampm.getSelectedIndex() * 12; - } - datefield.getShowingDate().setHours(h); - updateTime(false); - } else if (event.getSource() == mins) { - final int m = mins.getSelectedIndex(); - datefield.getShowingDate().setMinutes(m); - updateTime(false); - } else if (event.getSource() == sec) { - final int s = sec.getSelectedIndex(); - datefield.getShowingDate().setSeconds(s); - updateTime(false); - } else if (event.getSource() == msec) { - final int ms = msec.getSelectedIndex(); - datefield.setShowingMilliseconds(ms); - updateTime(false); - } else if (event.getSource() == ampm) { - final int h = hours.getSelectedIndex() - + (ampm.getSelectedIndex() * 12); - datefield.getShowingDate().setHours(h); - updateTime(false); - } - } - - /* - * (non-Javadoc) - * - * @see - * com.google.gwt.event.dom.client.KeyPressHandler#onKeyPress(com.google - * .gwt.event.dom.client.KeyPressEvent) - */ - public void onKeyPress(KeyPressEvent event) { - int keycode = event.getNativeEvent().getKeyCode(); - - if (calendar != null) { - if (keycode == calendar.getSelectKey() - || keycode == calendar.getCloseKey()) { - if (keycode == calendar.getSelectKey()) { - notifyServerOfChanges(); - } - - calendar.handleNavigation(keycode, event.getNativeEvent() - .getCtrlKey() - || event.getNativeEvent().getMetaKey(), event - .getNativeEvent().getShiftKey()); - return; - } - } - - event.stopPropagation(); - } - - /* - * (non-Javadoc) - * - * @see - * com.google.gwt.event.dom.client.KeyDownHandler#onKeyDown(com.google.gwt - * .event.dom.client.KeyDownEvent) - */ - public void onKeyDown(KeyDownEvent event) { - int keycode = event.getNativeEvent().getKeyCode(); - if (keycode != calendar.getCloseKey() - && keycode != calendar.getSelectKey()) { - event.stopPropagation(); - } - } - - /* - * (non-Javadoc) - * - * @see - * com.google.gwt.event.dom.client.FocusHandler#onFocus(com.google.gwt.event - * .dom.client.FocusEvent) - */ - public void onFocus(FocusEvent event) { - event.preventDefault(); - - // Delegate focus to the hour select - hours.setFocus(true); - } - - /** - * Update the variables server side - */ - public void notifyServerOfChanges() { - /* - * Just update the variables, don't send any thing. The calendar panel - * will make the request when the panel is closed. - */ - Date now = datefield.getCurrentDate(); - datefield.getClient().updateVariable(datefield.getId(), "year", - now.getYear() + 1900, false); - datefield.getClient().updateVariable(datefield.getId(), "month", - now.getMonth() + 1, false); - datefield.getClient().updateVariable(datefield.getId(), "day", - now.getDate(), false); - datefield.getClient().updateVariable(datefield.getId(), "hour", - now.getHours(), false); - datefield.getClient().updateVariable(datefield.getId(), "min", - now.getMinutes(), false); - datefield.getClient().updateVariable(datefield.getId(), "sec", - now.getSeconds(), false); - datefield.getClient().updateVariable(datefield.getId(), "msec", - datefield.getShowingMilliseconds(), false); - } - -} -- cgit v1.2.3