\r
import com.google.gwt.event.dom.client.ClickEvent;\r
import com.google.gwt.event.dom.client.ClickHandler;\r
+import com.google.gwt.event.dom.client.KeyCodes;\r
+import com.google.gwt.event.dom.client.KeyDownEvent;\r
+import com.google.gwt.event.dom.client.KeyDownHandler;\r
+import com.google.gwt.event.dom.client.KeyPressEvent;\r
+import com.google.gwt.event.dom.client.KeyPressHandler;\r
import com.google.gwt.user.client.DOM;\r
import com.google.gwt.user.client.Event;\r
import com.google.gwt.user.client.Timer;\r
import com.google.gwt.user.client.ui.FlexTable;\r
+import com.google.gwt.user.client.ui.InlineHTML;\r
import com.google.gwt.user.client.ui.MouseListener;\r
import com.google.gwt.user.client.ui.MouseListenerCollection;\r
import com.google.gwt.user.client.ui.SourcesMouseEvents;\r
import com.google.gwt.user.client.ui.Widget;\r
+import com.vaadin.terminal.gwt.client.BrowserInfo;\r
import com.vaadin.terminal.gwt.client.DateTimeService;\r
import com.vaadin.terminal.gwt.client.LocaleService;\r
\r
-public class VCalendarPanel extends FlexTable implements MouseListener {\r
+public class VCalendarPanel extends FocusableFlexTable implements\r
+ MouseListener, KeyDownHandler, KeyPressHandler {\r
+\r
+ /**\r
+ * Represents a Date button in the calendar\r
+ */\r
+ private class VEventButton extends VNativeButton implements\r
+ SourcesMouseEvents {\r
+\r
+ private MouseListenerCollection mouseListeners;\r
+\r
+ /**\r
+ * Default constructor\r
+ */\r
+ public VEventButton() {\r
+ super();\r
+ sinkEvents(Event.FOCUSEVENTS | Event.KEYEVENTS | Event.ONCLICK\r
+ | Event.MOUSEEVENTS);\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see\r
+ * com.google.gwt.user.client.ui.FocusWidget#addMouseListener(com.google\r
+ * .gwt.user.client.ui.MouseListener)\r
+ */\r
+ @Override\r
+ public void addMouseListener(MouseListener listener) {\r
+ if (mouseListeners == null) {\r
+ mouseListeners = new MouseListenerCollection();\r
+ }\r
+ mouseListeners.add(listener);\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see\r
+ * com.google.gwt.user.client.ui.FocusWidget#removeMouseListener(com\r
+ * .google.gwt.user.client.ui.MouseListener)\r
+ */\r
+ @Override\r
+ public void removeMouseListener(MouseListener listener) {\r
+ if (mouseListeners != null) {\r
+ mouseListeners.remove(listener);\r
+ }\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see\r
+ * com.vaadin.terminal.gwt.client.ui.VNativeButton#onBrowserEvent(com\r
+ * .google.gwt.user.client.Event)\r
+ */\r
+ @Override\r
+ public void onBrowserEvent(Event event) {\r
+ super.onBrowserEvent(event);\r
+ switch (DOM.eventGetType(event)) {\r
+ case Event.ONMOUSEDOWN:\r
+ case Event.ONMOUSEUP:\r
+ case Event.ONMOUSEOUT:\r
+ if (mouseListeners != null) {\r
+ mouseListeners.fireMouseEvent(this, event);\r
+ }\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Represents a click handler for when a user selects a date by using the\r
+ * mouse\r
+ */\r
+ private class DateClickHandler implements ClickHandler {\r
+\r
+ private final VCalendarPanel cal;\r
+\r
+ public DateClickHandler(VCalendarPanel panel) {\r
+ cal = panel;\r
+ }\r
+\r
+ public void selectDate(String date) {\r
+ try {\r
+ final Integer day = new Integer(date);\r
+ final Date newDate = cal.datefield.getShowingDate();\r
+ newDate.setDate(day.intValue());\r
+ if (!isEnabledDate(newDate)) {\r
+ return;\r
+ }\r
+ if (cal.datefield.getCurrentDate() == null) {\r
+ cal.datefield.setCurrentDate(new Date(newDate.getTime()));\r
+\r
+ // Init variables with current time\r
+ datefield.getClient().updateVariable(cal.datefield.getId(),\r
+ "hour", newDate.getHours(), false);\r
+ datefield.getClient().updateVariable(cal.datefield.getId(),\r
+ "min", newDate.getMinutes(), false);\r
+ datefield.getClient().updateVariable(cal.datefield.getId(),\r
+ "sec", newDate.getSeconds(), false);\r
+ datefield.getClient().updateVariable(cal.datefield.getId(),\r
+ "msec", datefield.getMilliseconds(), false);\r
+ }\r
+\r
+ cal.datefield.getCurrentDate().setTime(newDate.getTime());\r
+ cal.datefield.getClient().updateVariable(cal.datefield.getId(),\r
+ "day", cal.datefield.getCurrentDate().getDate(), false);\r
+ cal.datefield.getClient().updateVariable(cal.datefield.getId(),\r
+ "month", cal.datefield.getCurrentDate().getMonth() + 1,\r
+ false);\r
+ cal.datefield.getClient().updateVariable(cal.datefield.getId(),\r
+ "year",\r
+ cal.datefield.getCurrentDate().getYear() + 1900,\r
+ cal.datefield.isImmediate());\r
+\r
+ if (datefield instanceof VTextualDate) {\r
+ ((VOverlay) getParent()).hide();\r
+ } else {\r
+ updateCalendar();\r
+ }\r
+\r
+ } catch (final NumberFormatException e) {\r
+ // Not a number, ignore and stop here\r
+ return;\r
+ }\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see\r
+ * com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt\r
+ * .event.dom.client.ClickEvent)\r
+ */\r
+ public void onClick(ClickEvent event) {\r
+ Object sender = event.getSource();\r
+ Cell cell = cal.days.getCellForEvent(event);\r
+ if (sender != cal.days || cell == null || cell.getRowIndex() < 1\r
+ || cell.getRowIndex() > 6 || !cal.datefield.isEnabled()\r
+ || cal.datefield.isReadonly() || cell.getCellIndex() < 1) {\r
+ return;\r
+ }\r
+\r
+ final String text = cal.days.getText(cell.getRowIndex(), cell\r
+ .getCellIndex());\r
+ if (text.equals(" ")) {\r
+ return;\r
+ }\r
+\r
+ selectDate(text);\r
+ }\r
+ }\r
\r
private final VDateField datefield;\r
\r
/* Needed to identify locale changes */\r
private String locale = LocaleService.getDefaultLocale();\r
\r
+ private int selectedRow;\r
+ private int selectedColumn;\r
+\r
+ private boolean changingView = false;\r
+\r
+ private final DateClickHandler dateClickHandler;\r
+\r
+ private Timer mouseTimer;\r
+\r
public VCalendarPanel(VDateField parent) {\r
+ super();\r
+\r
datefield = parent;\r
setStyleName(VDateField.CLASSNAME + "-calendarpanel");\r
// buildCalendar(true);\r
- days.addClickHandler(new DateClickHandler(this));\r
+\r
+ dateClickHandler = new DateClickHandler(this);\r
+ days.addClickHandler(dateClickHandler);\r
+\r
+ /*\r
+ * Firefox auto-repeat works correctly only if we use a key press\r
+ * handler, other browsers handle it correctly when using a key down\r
+ * handler\r
+ */\r
+ if (BrowserInfo.get().isGecko()) {\r
+ addKeyPressHandler(this);\r
+ } else {\r
+ addKeyDownHandler(this);\r
+ }\r
+\r
}\r
\r
public VCalendarPanel(VDateField parent, Date min, Date max) {\r
+ super();\r
+\r
datefield = parent;\r
setStyleName(VDateField.CLASSNAME + "-calendarpanel");\r
- days.addClickHandler(new DateClickHandler(this));\r
\r
+ dateClickHandler = new DateClickHandler(this);\r
+ days.addClickHandler(dateClickHandler);\r
+\r
+ /*\r
+ * Firefox auto-repeat works correctly only if we use a key press\r
+ * handler, other browsers handle it correctly when using a key down\r
+ * handler\r
+ */\r
+ if (BrowserInfo.get().isGecko()) {\r
+ addKeyPressHandler(this);\r
+ } else {\r
+ addKeyDownHandler(this);\r
+ }\r
}\r
\r
private void buildCalendar(boolean forceRedraw) {\r
prevYear = new VEventButton();\r
prevYear.setHTML("«");\r
prevYear.setStyleName("v-button-prevyear");\r
+ prevYear.getElement().setTabIndex(-1);\r
nextYear = new VEventButton();\r
nextYear.setHTML("»");\r
nextYear.setStyleName("v-button-nextyear");\r
+ nextYear.getElement().setTabIndex(-1);\r
prevYear.addMouseListener(this);\r
nextYear.addMouseListener(this);\r
setWidget(0, 0, prevYear);\r
prevMonth = new VEventButton();\r
prevMonth.setHTML("‹");\r
prevMonth.setStyleName("v-button-prevmonth");\r
+ prevMonth.getElement().setTabIndex(-1);\r
nextMonth = new VEventButton();\r
nextMonth.setHTML("›");\r
nextMonth.setStyleName("v-button-nextmonth");\r
+ nextMonth.getElement().setTabIndex(-1);\r
prevMonth.addMouseListener(this);\r
nextMonth.addMouseListener(this);\r
setWidget(0, 3, nextMonth);\r
\r
// date actually selected?\r
Date selectedDate = datefield.getCurrentDate();\r
+\r
// Showing is the date (year/month+year) that is currently shown in the\r
// panel\r
Date showing = datefield.getShowingDate();\r
\r
+ // Always show something\r
+ if (showing == null) {\r
+ showing = new Date();\r
+ datefield.setShowingDate(showing);\r
+ }\r
+\r
// The day of month that is selected, -1 if no day of this month is\r
// selected (i.e, showing another month/year than selected or nothing is\r
// selected)\r
int dayOfMonthToday = -1;\r
\r
// Find out a day this month is selected\r
- if (selectedDate != null\r
+ if (showing != null) {\r
+ dayOfMonthSelected = showing.getDate();\r
+ } else if (selectedDate != null\r
&& selectedDate.getMonth() == showing.getMonth()\r
&& selectedDate.getYear() == showing.getYear()) {\r
dayOfMonthSelected = selectedDate.getDate();\r
}\r
}\r
\r
- // Add CSS classes according to state\r
- String cssClass = baseclass;\r
+ // Actually write the day of month\r
+ InlineHTML html = new InlineHTML(String.valueOf(dayOfMonth));\r
+ html.setStylePrimaryName(baseclass);\r
+ html.setTitle(title);\r
\r
+ // Add CSS classes according to state\r
if (!isEnabledDate(curr)) {\r
- cssClass += " " + baseclass + "-disabled";\r
+ html.addStyleDependentName("disabled");\r
}\r
\r
if (dayOfMonthSelected == dayOfMonth) {\r
- cssClass += " " + baseclass + "-selected";\r
+ html.addStyleDependentName("selected");\r
+ selectedRow = weekOfMonth;\r
+ selectedColumn = firstWeekdayColumn + dayOfWeek;\r
}\r
\r
if (dayOfMonthToday == dayOfMonth) {\r
- cssClass += " " + baseclass + "-today";\r
+ html.addStyleDependentName("today");\r
}\r
if (title.length() > 0) {\r
- cssClass += " " + baseclass + "-entry";\r
+ html.addStyleDependentName("entry");\r
}\r
\r
- // Actually write the day of month\r
- days.setHTML(weekOfMonth, firstWeekdayColumn + dayOfWeek,\r
- "<span title=\"" + title + "\" class=\"" + cssClass\r
- + "\">" + dayOfMonth + "</span>");\r
+ days.setWidget(weekOfMonth, firstWeekdayColumn + dayOfWeek,\r
+ html);\r
\r
// ISO week numbers if requested\r
if (!weekNumberProcessed[weekOfMonth]) {\r
\r
private void buildTime(boolean forceRedraw) {\r
if (time == null) {\r
- time = new VTime(datefield);\r
+ time = new VTime(datefield, this);\r
setWidget(2, 0, time);\r
getFlexCellFormatter().setColSpan(2, 0, 5);\r
getFlexCellFormatter().setStyleName(2, 0,\r
}\r
\r
/**\r
- * \r
- * @param forceRedraw\r
- * Build all from scratch, in case of e.g. locale changes\r
+ * Updates the calendar and text field with the selected dates *\r
*/\r
public void updateCalendar() {\r
- // Locale and resolution changes force a complete redraw\r
- buildCalendar(locale != datefield.getCurrentLocale()\r
- || resolution != datefield.getCurrentResolution());\r
+ updateCalendarOnly();\r
if (datefield instanceof VTextualDate) {\r
((VTextualDate) datefield).buildDate();\r
}\r
- locale = datefield.getCurrentLocale();\r
- resolution = datefield.getCurrentResolution();\r
+ }\r
+\r
+ /**\r
+ * Updates the popup only, does not affect the text field\r
+ */\r
+ private void updateCalendarOnly() {\r
+ if (!changingView) {\r
+ changingView = true;\r
+ // Locale and resolution changes force a complete redraw\r
+ buildCalendar(locale != datefield.getCurrentLocale()\r
+ || resolution != datefield.getCurrentResolution());\r
+ locale = datefield.getCurrentLocale();\r
+ resolution = datefield.getCurrentResolution();\r
+\r
+ /*\r
+ * Wait a while before releasing the lock, so auto-repeated events\r
+ * isn't processed after the calendar is updated\r
+ */\r
+ Timer changeTimer = new Timer() {\r
+ @Override\r
+ public void run() {\r
+ changingView = false;\r
+ }\r
+ };\r
+ changeTimer.schedule(100);\r
+ }\r
}\r
\r
private boolean isEnabledDate(Date date) {\r
return true;\r
}\r
\r
+ /**\r
+ * Selects the next month\r
+ */\r
+ private void selectNextMonth() {\r
+ Date showingDate = datefield.getShowingDate();\r
+ int currentMonth = showingDate.getMonth();\r
+ showingDate.setMonth(currentMonth + 1);\r
+ int requestedMonth = (currentMonth + 1) % 12;\r
+\r
+ /*\r
+ * If the selected date was e.g. 31.3 the new date would be 31.4 but\r
+ * this date is invalid so the new date will be 1.5. This is taken care\r
+ * of by decreasing the date until we have the correct month.\r
+ */\r
+ while (showingDate.getMonth() != requestedMonth) {\r
+ showingDate.setDate(showingDate.getDate() - 1);\r
+ }\r
+\r
+ updateCalendar();\r
+ }\r
+\r
+ /**\r
+ * Selects the previous month\r
+ */\r
+ private void selectPreviousMonth() {\r
+ Date showingDate = datefield.getShowingDate();\r
+ int currentMonth = showingDate.getMonth();\r
+ showingDate.setMonth(currentMonth - 1);\r
+\r
+ /*\r
+ * If the selected date was e.g. 31.12 the new date would be 31.11 but\r
+ * this date is invalid so the new date will be 1.12. This is taken care\r
+ * of by decreasing the date until we have the correct month.\r
+ */\r
+ while (showingDate.getMonth() == currentMonth) {\r
+ showingDate.setDate(showingDate.getDate() - 1);\r
+ }\r
+\r
+ updateCalendar();\r
+ }\r
+\r
+ /**\r
+ * Selects the previous year\r
+ */\r
+ private void selectPreviousYear(int years) {\r
+ Date showingDate = datefield.getShowingDate();\r
+ showingDate.setYear(showingDate.getYear() - years);\r
+ updateCalendar();\r
+ }\r
+\r
+ /**\r
+ * Selects the next year\r
+ */\r
+ private void selectNextYear(int years) {\r
+ Date showingDate = datefield.getShowingDate();\r
+ showingDate.setYear(showingDate.getYear() + years);\r
+ updateCalendar();\r
+ }\r
+\r
+ /**\r
+ * Handles a user click on the component\r
+ * \r
+ * @param sender\r
+ * The component that was clicked\r
+ * @param updateVariable\r
+ * Should the date field be updated\r
+ * \r
+ */\r
private void processClickEvent(Widget sender, boolean updateVariable) {\r
if (!datefield.isEnabled() || datefield.isReadonly()) {\r
return;\r
}\r
- Date showingDate = datefield.getShowingDate();\r
if (!updateVariable) {\r
if (sender == prevYear) {\r
- showingDate.setYear(showingDate.getYear() - 1);\r
- updateCalendar();\r
+ selectPreviousYear(1);\r
} else if (sender == nextYear) {\r
- showingDate.setYear(showingDate.getYear() + 1);\r
- updateCalendar();\r
+ selectNextYear(1);\r
} else if (sender == prevMonth) {\r
- int currentMonth = showingDate.getMonth();\r
- showingDate.setMonth(currentMonth - 1);\r
-\r
- /*\r
- * If the selected date was e.g. 31.12 the new date would be\r
- * 31.11 but this date is invalid so the new date will be 1.12.\r
- * This is taken care of by decreasing the date until we have\r
- * the correct month.\r
- */\r
- while (showingDate.getMonth() == currentMonth) {\r
- showingDate.setDate(showingDate.getDate() - 1);\r
- }\r
-\r
- updateCalendar();\r
+ selectPreviousMonth();\r
} else if (sender == nextMonth) {\r
- int currentMonth = showingDate.getMonth();\r
- showingDate.setMonth(currentMonth + 1);\r
- int requestedMonth = (currentMonth + 1) % 12;\r
-\r
- /*\r
- * If the selected date was e.g. 31.3 the new date would be 31.4\r
- * but this date is invalid so the new date will be 1.5. This is\r
- * taken care of by decreasing the date until we have the\r
- * correct month.\r
- */\r
- while (showingDate.getMonth() != requestedMonth) {\r
- showingDate.setDate(showingDate.getDate() - 1);\r
- }\r
-\r
- updateCalendar();\r
+ selectNextMonth();\r
}\r
} else {\r
if (datefield.getCurrentResolution() == VDateField.RESOLUTION_YEAR\r
|| datefield.getCurrentResolution() == VDateField.RESOLUTION_MONTH) {\r
- // Due to current UI, update variable if res=year/month\r
- datefield.setCurrentDate(new Date(showingDate.getTime()));\r
- if (datefield.getCurrentResolution() == VDateField.RESOLUTION_MONTH) {\r
- datefield.getClient().updateVariable(datefield.getId(),\r
- "month", datefield.getCurrentDate().getMonth() + 1,\r
- false);\r
- }\r
- datefield.getClient().updateVariable(datefield.getId(), "year",\r
- datefield.getCurrentDate().getYear() + 1900,\r
- datefield.isImmediate());\r
-\r
- /* Must update the value in the textfield also */\r
- updateCalendar();\r
+ notifyServerOfChanges();\r
}\r
}\r
}\r
\r
- private Timer timer;\r
+ /**\r
+ * Send the date to the server\r
+ */\r
+ private void notifyServerOfChanges() {\r
+ Date showingDate = datefield.getShowingDate();\r
+\r
+ // Due to current UI, update variable if res=year/month\r
+ datefield.setCurrentDate(new Date(showingDate.getTime()));\r
+ if (datefield.getCurrentResolution() == VDateField.RESOLUTION_MONTH) {\r
+ datefield.getClient().updateVariable(datefield.getId(), "month",\r
+ datefield.getCurrentDate().getMonth() + 1, false);\r
+ }\r
+ datefield.getClient().updateVariable(datefield.getId(), "year",\r
+ datefield.getCurrentDate().getYear() + 1900,\r
+ datefield.isImmediate());\r
+\r
+ /* Must update the value in the textfield also */\r
+ updateCalendar();\r
+ }\r
\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see\r
+ * com.google.gwt.user.client.ui.MouseListener#onMouseDown(com.google.gwt\r
+ * .user.client.ui.Widget, int, int)\r
+ */\r
public void onMouseDown(final Widget sender, int x, int y) {\r
// Allow user to click-n-hold for fast-forward or fast-rewind.\r
// Timer is first used for a 500ms delay after mousedown. After that has\r
// timers are cancelled on mouseup or mouseout.\r
if (sender instanceof VEventButton) {\r
processClickEvent(sender, false);\r
- timer = new Timer() {\r
+ mouseTimer = new Timer() {\r
@Override\r
public void run() {\r
- timer = new Timer() {\r
+ mouseTimer = new Timer() {\r
@Override\r
public void run() {\r
processClickEvent(sender, false);\r
}\r
};\r
- timer.scheduleRepeating(150);\r
+ mouseTimer.scheduleRepeating(150);\r
}\r
};\r
- timer.schedule(500);\r
+ mouseTimer.schedule(500);\r
}\r
}\r
\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see\r
+ * com.google.gwt.user.client.ui.MouseListener#onMouseEnter(com.google.gwt\r
+ * .user.client.ui.Widget)\r
+ */\r
public void onMouseEnter(Widget sender) {\r
}\r
\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see\r
+ * com.google.gwt.user.client.ui.MouseListener#onMouseLeave(com.google.gwt\r
+ * .user.client.ui.Widget)\r
+ */\r
public void onMouseLeave(Widget sender) {\r
- if (timer != null) {\r
- timer.cancel();\r
+ if (mouseTimer != null) {\r
+ mouseTimer.cancel();\r
}\r
}\r
\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see\r
+ * com.google.gwt.user.client.ui.MouseListener#onMouseMove(com.google.gwt\r
+ * .user.client.ui.Widget, int, int)\r
+ */\r
public void onMouseMove(Widget sender, int x, int y) {\r
}\r
\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see\r
+ * com.google.gwt.user.client.ui.MouseListener#onMouseUp(com.google.gwt.\r
+ * user.client.ui.Widget, int, int)\r
+ */\r
public void onMouseUp(Widget sender, int x, int y) {\r
- if (timer != null) {\r
- timer.cancel();\r
+ if (mouseTimer != null) {\r
+ mouseTimer.cancel();\r
}\r
processClickEvent(sender, true);\r
}\r
\r
- private class VEventButton extends VNativeButton implements\r
- SourcesMouseEvents {\r
\r
- private MouseListenerCollection mouseListeners;\r
+ public void setLimits(Date min, Date max) {\r
+ if (min != null) {\r
+ final Date d = new Date(min.getTime());\r
+ d.setHours(0);\r
+ d.setMinutes(0);\r
+ d.setSeconds(1);\r
+ minDate = d;\r
+ } else {\r
+ minDate = null;\r
+ }\r
+ if (max != null) {\r
+ final Date d = new Date(max.getTime());\r
+ d.setHours(24);\r
+ d.setMinutes(59);\r
+ d.setSeconds(59);\r
+ maxDate = d;\r
+ } else {\r
+ maxDate = null;\r
+ }\r
+ }\r
\r
- public VEventButton() {\r
- super();\r
- sinkEvents(Event.FOCUSEVENTS | Event.KEYEVENTS | Event.ONCLICK\r
- | Event.MOUSEEVENTS);\r
+ public void setCalendarEntrySource(CalendarEntrySource entrySource) {\r
+ this.entrySource = entrySource;\r
+ }\r
+\r
+ public CalendarEntrySource getCalendarEntrySource() {\r
+ return entrySource;\r
+ }\r
+\r
+ public interface CalendarEntrySource {\r
+ public List getEntries(Date date, int resolution);\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see\r
+ * com.google.gwt.event.dom.client.KeyDownHandler#onKeyDown(com.google.gwt\r
+ * .event.dom.client.KeyDownEvent)\r
+ */\r
+ public void onKeyDown(KeyDownEvent event) {\r
+ int keycode = event.getNativeKeyCode();\r
+\r
+ if (handleNavigation(keycode, event\r
+ .isControlKeyDown()\r
+ || event.isMetaKeyDown(), event.isShiftKeyDown())) {\r
+ event.preventDefault();\r
}\r
+ }\r
\r
- @Override\r
- public void addMouseListener(MouseListener listener) {\r
- if (mouseListeners == null) {\r
- mouseListeners = new MouseListenerCollection();\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see\r
+ * com.google.gwt.event.dom.client.KeyPressHandler#onKeyPress(com.google\r
+ * .gwt.event.dom.client.KeyPressEvent)\r
+ */\r
+ public void onKeyPress(KeyPressEvent event) {\r
+ if (handleNavigation(event.getNativeEvent().getKeyCode(), event\r
+ .isControlKeyDown()\r
+ || event.isMetaKeyDown(), event.isShiftKeyDown())) {\r
+ event.preventDefault();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Get the first valid weekday column index of this month\r
+ * \r
+ * @return A column index or zero if not found\r
+ */\r
+ private int getFirstWeekdayColumn() {\r
+ Widget day = null;\r
+ for (int col = 1; col <= 7; col++) {\r
+ day = days.getWidget(1, col);\r
+ if (day != null) {\r
+ return col;\r
}\r
- mouseListeners.add(listener);\r
}\r
+ return 0;\r
+ }\r
\r
- @Override\r
- public void removeMouseListener(MouseListener listener) {\r
- if (mouseListeners != null) {\r
- mouseListeners.remove(listener);\r
+ /**\r
+ * Get the last valid weekday column index of this month\r
+ * \r
+ * @return A column index or zero if not found\r
+ */\r
+ private int[] getLastWeekdayColumn() {\r
+ Widget day = null;\r
+ for (int row = days.getRowCount() - 1; row >= 1; row--) {\r
+ for (int col = 7; col >= 1; col--) {\r
+ day = days.getWidget(row, col);\r
+ if (day != null) {\r
+ return new int[] { row, col };\r
+ }\r
}\r
}\r
+ return new int[] { 0, 0 };\r
+ }\r
\r
- @Override\r
- public void onBrowserEvent(Event event) {\r
- super.onBrowserEvent(event);\r
- switch (DOM.eventGetType(event)) {\r
- case Event.ONMOUSEDOWN:\r
- case Event.ONMOUSEUP:\r
- case Event.ONMOUSEOUT:\r
- if (mouseListeners != null) {\r
- mouseListeners.fireMouseEvent(this, event);\r
+ /**\r
+ * Handles the keyboard navigation when the resolution is set to years.\r
+ * \r
+ * @param keycode\r
+ * The keycode to process\r
+ * @param ctrl\r
+ * Is ctrl pressed?\r
+ * @param shift\r
+ * is shift pressed\r
+ * @return Returns true if the keycode was processed, else false\r
+ */\r
+ protected boolean handleNavigationYearMode(int keycode, boolean ctrl,\r
+ boolean shift) {\r
+\r
+ // Ctrl and Shift selection not supported\r
+ if (ctrl || shift) {\r
+ return false;\r
+ }\r
+\r
+ else if (keycode == getPreviousKey()) {\r
+ selectNextYear(10); // Add 10 years\r
+ return true;\r
+ }\r
+\r
+ else if (keycode == getForwardKey()) {\r
+ selectNextYear(1); // Add 1 year\r
+ return true;\r
+ }\r
+\r
+ else if (keycode == getNextKey()) {\r
+ selectPreviousYear(10); // Subtract 10 years\r
+ return true;\r
+ }\r
+\r
+ else if (keycode == getBackwardKey()) {\r
+ selectPreviousYear(1); // Subtract 1 year\r
+ return true;\r
+\r
+ } else if (keycode == getSelectKey()) {\r
+\r
+ // We need to notify the clickhandler of the selection\r
+ dateClickHandler.selectDate(String.valueOf(datefield\r
+ .getShowingDate().getDay()));\r
+\r
+ // Send changes to server\r
+ notifyServerOfChanges();\r
+\r
+ if (datefield instanceof VTextualDate) {\r
+ ((VTextualDate) datefield).focus();\r
+ }\r
+ return true;\r
+\r
+ } else if (keycode == getResetKey()) {\r
+ // Restore showing date the selected date\r
+ Date showing = datefield.getShowingDate();\r
+ Date current = datefield.getCurrentDate();\r
+ showing.setTime(current.getTime());\r
+ updateCalendar();\r
+ return true;\r
+\r
+ } else if (keycode == getCloseKey()) {\r
+ if (datefield instanceof VPopupCalendar) {\r
+\r
+ // Restore showing date the selected date\r
+ Date showing = datefield.getShowingDate();\r
+ Date current = datefield.getCurrentDate();\r
+\r
+ if (current != null && current != null) {\r
+ showing.setTime(current.getTime());\r
}\r
- break;\r
+\r
+ // Close popup..\r
+ ((VPopupCalendar) datefield).closeCalendarPanel();\r
+\r
+ // ..and focus the textfield\r
+ ((VPopupCalendar) datefield).focus();\r
}\r
+\r
+ return true;\r
}\r
+ return false;\r
}\r
\r
- private class DateClickHandler implements ClickHandler {\r
+ /**\r
+ * Handle the keyboard navigation when the resolution is set to MONTH\r
+ * \r
+ * @param keycode\r
+ * The keycode to handle\r
+ * @param ctrl\r
+ * Was the ctrl key pressed?\r
+ * @param shift\r
+ * Was the shift key pressed?\r
+ * @return\r
+ */\r
+ protected boolean handleNavigationMonthMode(int keycode, boolean ctrl,\r
+ boolean shift) {\r
\r
- private final VCalendarPanel cal;\r
+ // Ctrl selection not supported\r
+ if (ctrl) {\r
+ return false;\r
\r
- public DateClickHandler(VCalendarPanel panel) {\r
- cal = panel;\r
+ } else if (keycode == getPreviousKey()) {\r
+ selectNextYear(1); // Add 1 year\r
+ return true;\r
+\r
+ } else if (keycode == getForwardKey()) {\r
+ selectNextMonth(); // Add 1 month\r
+ return true;\r
+\r
+ } else if (keycode == getNextKey()) {\r
+ selectPreviousYear(1); // Subtract 1 year\r
+ return true;\r
+\r
+ } else if (keycode == getBackwardKey()) {\r
+ selectPreviousMonth(); // Subtract 1 month\r
+ return true;\r
+\r
+ } else if (keycode == getSelectKey()) {\r
+\r
+ // We need to notify the clickhandler of the selection\r
+ dateClickHandler.selectDate(String.valueOf(datefield\r
+ .getShowingDate().getDay()));\r
+\r
+ // Send changes to server\r
+ notifyServerOfChanges();\r
+\r
+ if (datefield instanceof VTextualDate) {\r
+ ((VTextualDate) datefield).focus();\r
+ }\r
+\r
+ return true;\r
+\r
+ } else if (keycode == getResetKey()) {\r
+ // Restore showing date the selected date\r
+ Date showing = datefield.getShowingDate();\r
+ Date current = datefield.getCurrentDate();\r
+ showing.setTime(current.getTime());\r
+ updateCalendar();\r
+ return true;\r
+\r
+ } else if (keycode == getCloseKey() || keycode == KeyCodes.KEY_TAB) {\r
+ if (datefield instanceof VPopupCalendar) {\r
+\r
+ // Restore showing date the selected date\r
+ Date showing = datefield.getShowingDate();\r
+ Date current = datefield.getCurrentDate();\r
+\r
+ if (current != null && current != null) {\r
+ showing.setTime(current.getTime());\r
+ }\r
+\r
+ // Close popup..\r
+ ((VPopupCalendar) datefield).closeCalendarPanel();\r
+\r
+ // ..and focus the textfield\r
+ ((VPopupCalendar) datefield).focus();\r
+ }\r
+\r
+ return true;\r
}\r
\r
- public void onClick(ClickEvent event) {\r
- Object sender = event.getSource();\r
- Cell cell = cal.days.getCellForEvent(event);\r
- if (sender != cal.days || cell == null || cell.getRowIndex() < 1\r
- || cell.getRowIndex() > 6 || !cal.datefield.isEnabled()\r
- || cal.datefield.isReadonly() || cell.getCellIndex() < 1) {\r
- return;\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Handle keyboard navigation what the resolution is set to DAY\r
+ * \r
+ * @param keycode\r
+ * The keycode to handle\r
+ * @param ctrl\r
+ * Was the ctrl key pressed?\r
+ * @param shift\r
+ * Was the shift key pressed?\r
+ * @return Return true if the key press was handled by the method, else\r
+ * return false.\r
+ */\r
+ protected boolean handleNavigationDayMode(int keycode, boolean ctrl,\r
+ boolean shift) {\r
+\r
+ // Ctrl key is not in use\r
+ if (ctrl) {\r
+ return false;\r
+ }\r
+\r
+ Date showingDate = datefield.getShowingDate();\r
+ Widget currentSelection = days.getWidget(selectedRow, selectedColumn);\r
+\r
+ /*\r
+ * Jumps to the next day.\r
+ */\r
+ if (keycode == getForwardKey() && !shift) {\r
+ // Calculate new showing date\r
+ Date newCurrentDate = new Date(showingDate.getYear(), showingDate\r
+ .getMonth(), showingDate.getDate(), showingDate.getHours(),\r
+ showingDate.getMinutes(), showingDate.getSeconds());\r
+ newCurrentDate.setDate(newCurrentDate.getDate() + 1);\r
+\r
+ if (newCurrentDate.getMonth() == showingDate.getMonth()) {\r
+ // Month did not change, only move the selection\r
+\r
+ showingDate.setDate(showingDate.getDate() + 1);\r
+\r
+ if (currentSelection != null) {\r
+ currentSelection.removeStyleDependentName("selected");\r
+\r
+ // Calculate new selection\r
+ if (selectedColumn == 7) {\r
+ selectedColumn = 1;\r
+ selectedRow++;\r
+ } else {\r
+ selectedColumn++;\r
+ }\r
+ }\r
+\r
+ // Set new selection\r
+ currentSelection = days.getWidget(selectedRow, selectedColumn);\r
+ currentSelection.addStyleDependentName("selected");\r
+\r
+ } else {\r
+ // If the month changed we need to re-render the calendar\r
+ showingDate.setDate(showingDate.getDate() + 1);\r
+ updateCalendarOnly();\r
+\r
+ selectedRow = 1;\r
+ selectedColumn = getFirstWeekdayColumn();\r
+\r
+ // Set new selection\r
+ currentSelection = days.getWidget(selectedRow, selectedColumn);\r
+ currentSelection.addStyleDependentName("selected");\r
}\r
\r
- final String text = cal.days.getText(cell.getRowIndex(), cell\r
- .getCellIndex());\r
- if (text.equals(" ")) {\r
- return;\r
+ return true;\r
+\r
+ /*\r
+ * Jumps to the previous day\r
+ */\r
+ } else if (keycode == getBackwardKey() && !shift) {\r
+ // Calculate new showing date\r
+ Date newCurrentDate = new Date(showingDate.getYear(), showingDate\r
+ .getMonth(), showingDate.getDate(), showingDate.getHours(),\r
+ showingDate.getMinutes(), showingDate.getSeconds());\r
+ newCurrentDate.setDate(newCurrentDate.getDate() - 1);\r
+\r
+ if (newCurrentDate.getMonth() == showingDate.getMonth()) {\r
+ // Month did not change, only move the selection\r
+\r
+ showingDate.setDate(showingDate.getDate() - 1);\r
+\r
+ if (currentSelection != null) {\r
+ currentSelection.removeStyleDependentName("selected");\r
+\r
+ // Calculate new selection\r
+ if (selectedColumn == 1) {\r
+ selectedColumn = 7;\r
+ selectedRow--;\r
+ } else {\r
+ selectedColumn--;\r
+ }\r
+\r
+ }\r
+ // Set new selection\r
+ currentSelection = days.getWidget(selectedRow, selectedColumn);\r
+ currentSelection.addStyleDependentName("selected");\r
+\r
+ } else {\r
+ // If the month changed we need to re-render the calendar\r
+ showingDate.setDate(showingDate.getDate() - 1);\r
+ updateCalendarOnly();\r
+\r
+ int[] pos = getLastWeekdayColumn();\r
+ selectedRow = pos[0];\r
+ selectedColumn = pos[1];\r
+\r
+ // Set new selection\r
+ currentSelection = days.getWidget(selectedRow, selectedColumn);\r
+ currentSelection.addStyleDependentName("selected");\r
}\r
\r
- try {\r
- final Integer day = new Integer(text);\r
- final Date newDate = cal.datefield.getShowingDate();\r
- newDate.setDate(day.intValue());\r
- if (!isEnabledDate(newDate)) {\r
- return;\r
+ return true;\r
+\r
+ /*\r
+ * Jumps one week back in the calendar\r
+ */\r
+ } else if (keycode == getPreviousKey() && !shift) {\r
+ // Calculate new showing date\r
+ Date newCurrentDate = new Date(showingDate.getYear(), showingDate\r
+ .getMonth(), showingDate.getDate(), showingDate.getHours(),\r
+ showingDate.getMinutes(), showingDate.getSeconds());\r
+ newCurrentDate.setDate(newCurrentDate.getDate() - 7);\r
+\r
+ if (newCurrentDate.getMonth() == showingDate.getMonth()\r
+ && selectedRow > 1) {\r
+ // Month did not change, only move the selection\r
+\r
+ showingDate.setDate(showingDate.getDate() - 7);\r
+\r
+ if (currentSelection != null) {\r
+ currentSelection.removeStyleDependentName("selected");\r
}\r
- if (cal.datefield.getCurrentDate() == null) {\r
- cal.datefield.setCurrentDate(new Date(newDate.getTime()));\r
\r
- // Init variables with current time\r
- datefield.getClient().updateVariable(cal.datefield.getId(),\r
- "hour", newDate.getHours(), false);\r
- datefield.getClient().updateVariable(cal.datefield.getId(),\r
- "min", newDate.getMinutes(), false);\r
- datefield.getClient().updateVariable(cal.datefield.getId(),\r
- "sec", newDate.getSeconds(), false);\r
- datefield.getClient().updateVariable(cal.datefield.getId(),\r
- "msec", datefield.getMilliseconds(), false);\r
+ selectedRow--;\r
+\r
+ // Set new selection\r
+ currentSelection = days.getWidget(selectedRow, selectedColumn);\r
+ currentSelection.addStyleDependentName("selected");\r
+\r
+ } else {\r
+ // If the month changed we need to re-render the calendar\r
+ showingDate.setDate(showingDate.getDate() - 7);\r
+ updateCalendarOnly();\r
+\r
+ int[] pos = getLastWeekdayColumn();\r
+ selectedRow = pos[0];\r
+\r
+ // Set new selection\r
+ currentSelection = days.getWidget(selectedRow, selectedColumn);\r
+ if (currentSelection == null) {\r
+ selectedRow--;\r
+ currentSelection = days.getWidget(selectedRow,\r
+ selectedColumn);\r
}\r
\r
- cal.datefield.getCurrentDate().setTime(newDate.getTime());\r
- cal.datefield.getClient().updateVariable(cal.datefield.getId(),\r
- "day", cal.datefield.getCurrentDate().getDate(), false);\r
- cal.datefield.getClient().updateVariable(cal.datefield.getId(),\r
- "month", cal.datefield.getCurrentDate().getMonth() + 1,\r
- false);\r
- cal.datefield.getClient().updateVariable(cal.datefield.getId(),\r
- "year",\r
- cal.datefield.getCurrentDate().getYear() + 1900,\r
- cal.datefield.isImmediate());\r
+ currentSelection.addStyleDependentName("selected");\r
+ }\r
\r
- if (datefield instanceof VTextualDate\r
- && resolution < VDateField.RESOLUTION_HOUR) {\r
- ((VOverlay) getParent()).hide();\r
- } else {\r
- updateCalendar();\r
+ return true;\r
+\r
+ /*\r
+ * Jumps one week forward in the calendar\r
+ */\r
+ } else if (keycode == getNextKey() && !ctrl && !shift) {\r
+ // Calculate new showing date\r
+ Date newCurrentDate = new Date(showingDate.getYear(), showingDate\r
+ .getMonth(), showingDate.getDate(), showingDate.getHours(),\r
+ showingDate.getMinutes(), showingDate.getSeconds());\r
+ newCurrentDate.setDate(newCurrentDate.getDate() + 7);\r
+\r
+ if (newCurrentDate.getMonth() == showingDate.getMonth()) {\r
+ // Month did not change, only move the selection\r
+\r
+ showingDate.setDate(showingDate.getDate() + 7);\r
+\r
+ if (currentSelection != null) {\r
+ currentSelection.removeStyleDependentName("selected");\r
}\r
\r
- } catch (final NumberFormatException e) {\r
- // Not a number, ignore and stop here\r
- return;\r
+ selectedRow++;\r
+\r
+ // Set new selection\r
+ currentSelection = days.getWidget(selectedRow, selectedColumn);\r
+ currentSelection.addStyleDependentName("selected");\r
+\r
+ } else {\r
+ // If the month changed we need to re-render the calendar\r
+ showingDate.setDate(showingDate.getDate() + 7);\r
+ updateCalendarOnly();\r
+\r
+ selectedRow = 1;\r
+\r
+ // Set new selection\r
+ currentSelection = days.getWidget(selectedRow, selectedColumn);\r
+ if (currentSelection == null) {\r
+ selectedRow++;\r
+ currentSelection = days.getWidget(selectedRow,\r
+ selectedColumn);\r
+ }\r
+\r
+ currentSelection.addStyleDependentName("selected");\r
}\r
- }\r
\r
+ return true;\r
+\r
+ /*\r
+ * Selects the date that is chosen\r
+ */\r
+ } else if (keycode == getSelectKey() && !shift) {\r
+ InlineHTML selection = (InlineHTML) days.getWidget(selectedRow,\r
+ selectedColumn);\r
+ dateClickHandler.selectDate(selection.getText());\r
+\r
+ if (datefield instanceof VTextualDate) {\r
+ ((VTextualDate) datefield).focus();\r
+ }\r
+\r
+ return true;\r
+\r
+ /*\r
+ * Closes the date popup\r
+ */\r
+ } else if (keycode == getCloseKey() || keycode == KeyCodes.KEY_TAB) {\r
+ if (datefield instanceof VPopupCalendar) {\r
+\r
+ // Restore showing date the selected date\r
+ Date showing = datefield.getShowingDate();\r
+ Date current = datefield.getCurrentDate();\r
+\r
+ if (current != null && current != null) {\r
+ showing.setTime(current.getTime());\r
+ }\r
+\r
+ // Close popup..\r
+ ((VPopupCalendar) datefield).closeCalendarPanel();\r
+\r
+ // ..and focus the textfield\r
+ ((VPopupCalendar) datefield).focus();\r
+\r
+ }\r
+\r
+ return true;\r
+\r
+ /*\r
+ * Selects the next month\r
+ */\r
+ } else if (shift && keycode == getForwardKey()) {\r
+ selectNextMonth();\r
+ return true;\r
+\r
+ /*\r
+ * Selects the previous month\r
+ */\r
+ } else if (shift && keycode == getBackwardKey()) {\r
+ selectPreviousMonth();\r
+ return true;\r
+\r
+ /*\r
+ * Selects the next year\r
+ */\r
+ } else if (shift && keycode == getPreviousKey()) {\r
+ selectNextYear(1);\r
+ return true;\r
+\r
+ /*\r
+ * Selects the previous year\r
+ */\r
+ } else if (shift && keycode == getNextKey()) {\r
+ selectPreviousYear(1);\r
+ return true;\r
+\r
+ /*\r
+ * Resets the selection\r
+ */\r
+ } else if (keycode == getResetKey() && !shift) {\r
+ // Restore showing date the selected date\r
+ Date showing = datefield.getShowingDate();\r
+ Date current = datefield.getCurrentDate();\r
+ showing.setTime(current.getTime());\r
+ updateCalendar();\r
+ return true;\r
+ }\r
+ \r
+ return false;\r
}\r
\r
- public void setLimits(Date min, Date max) {\r
- if (min != null) {\r
- final Date d = new Date(min.getTime());\r
- d.setHours(0);\r
- d.setMinutes(0);\r
- d.setSeconds(1);\r
- minDate = d;\r
- } else {\r
- minDate = null;\r
+ /**\r
+ * Handles the keyboard navigation\r
+ * \r
+ * @param keycode\r
+ * The key code that was pressed\r
+ * @param ctrl\r
+ * Was the ctrl key pressed\r
+ * @param shift\r
+ * Was the shift key pressed\r
+ * @return Return true if key press was handled by the component, else\r
+ * return false\r
+ */\r
+ @SuppressWarnings("deprecation")\r
+ protected boolean handleNavigation(int keycode, boolean ctrl, boolean shift) {\r
+ if (!datefield.isEnabled() || datefield.isReadonly() || changingView) {\r
+ return false;\r
}\r
- if (max != null) {\r
- final Date d = new Date(max.getTime());\r
- d.setHours(24);\r
- d.setMinutes(59);\r
- d.setSeconds(59);\r
- maxDate = d;\r
- } else {\r
- maxDate = null;\r
+\r
+ else if (resolution == VDateField.RESOLUTION_YEAR) {\r
+ return handleNavigationYearMode(keycode, ctrl, shift);\r
+ }\r
+\r
+ else if (resolution == VDateField.RESOLUTION_MONTH) {\r
+ return handleNavigationMonthMode(keycode, ctrl, shift);\r
}\r
+\r
+ else if (resolution == VDateField.RESOLUTION_DAY) {\r
+ return handleNavigationDayMode(keycode, ctrl, shift);\r
+ }\r
+\r
+ else {\r
+ // Do not close the window on tab key but move the\r
+ // focus down to the time\r
+ if (keycode == KeyCodes.KEY_TAB) {\r
+ time.setFocus(true);\r
+ return true;\r
+ }\r
+\r
+ return handleNavigationDayMode(keycode, ctrl, shift);\r
+ }\r
+\r
}\r
\r
- public void setCalendarEntrySource(CalendarEntrySource entrySource) {\r
- this.entrySource = entrySource;\r
+ /**\r
+ * Returns the reset key which will reset the calendar to the previous\r
+ * selection. By default this is backspace but it can be overriden to change\r
+ * the key to whatever you want.\r
+ * \r
+ * @return\r
+ */\r
+ protected int getResetKey() {\r
+ return KeyCodes.KEY_BACKSPACE;\r
}\r
\r
- public CalendarEntrySource getCalendarEntrySource() {\r
- return entrySource;\r
+ /**\r
+ * Returns the select key which selects the date. By default this is the\r
+ * enter key but it can be changed to whatever you like by overriding this\r
+ * method.\r
+ * \r
+ * @return\r
+ */\r
+ protected int getSelectKey() {\r
+ return KeyCodes.KEY_ENTER;\r
}\r
\r
- public interface CalendarEntrySource {\r
- public List getEntries(Date date, int resolution);\r
+ /**\r
+ * Returns the key that closes the popup window if this is a VPopopCalendar.\r
+ * Else this does nothing. By default this is the Escape key but you can\r
+ * change the key to whatever you want by overriding this method.\r
+ * \r
+ * @return\r
+ */\r
+ protected int getCloseKey() {\r
+ return KeyCodes.KEY_ESCAPE;\r
+ }\r
+\r
+ /**\r
+ * The key that selects the next day in the calendar. By default this is the\r
+ * right arrow key but by overriding this method it can be changed to\r
+ * whatever you like.\r
+ * \r
+ * @return\r
+ */\r
+ protected int getForwardKey() {\r
+ return KeyCodes.KEY_RIGHT;\r
+ }\r
+\r
+ /**\r
+ * The key that selects the previous day in the calendar. By default this is\r
+ * the left arrow key but by overriding this method it can be changed to\r
+ * whatever you like.\r
+ * \r
+ * @return\r
+ */\r
+ protected int getBackwardKey() {\r
+ return KeyCodes.KEY_LEFT;\r
+ }\r
+\r
+ /**\r
+ * The key that selects the next week in the calendar. By default this is\r
+ * the down arrow key but by overriding this method it can be changed to\r
+ * whatever you like.\r
+ * \r
+ * @return\r
+ */\r
+ protected int getNextKey() {\r
+ return KeyCodes.KEY_DOWN;\r
}\r
\r
/**\r
- * Sets focus to Calendar panel.\r
+ * The key that selects the previous week in the calendar. By default this\r
+ * is the up arrow key but by overriding this method it can be changed to\r
+ * whatever you like.\r
* \r
- * @param focus\r
+ * @return\r
*/\r
- public void setFocus(boolean focus) {\r
- nextYear.setFocus(focus);\r
+ protected int getPreviousKey() {\r
+ return KeyCodes.KEY_UP;\r
}\r
\r
}\r
\r
import com.google.gwt.event.dom.client.ChangeEvent;\r
import com.google.gwt.event.dom.client.ChangeHandler;\r
-import com.google.gwt.user.client.ui.FlowPanel;\r
+import com.google.gwt.event.dom.client.FocusEvent;\r
+import com.google.gwt.event.dom.client.FocusHandler;\r
+import com.google.gwt.event.dom.client.KeyCodes;\r
+import com.google.gwt.event.dom.client.KeyDownEvent;\r
+import com.google.gwt.event.dom.client.KeyDownHandler;\r
+import com.google.gwt.event.dom.client.KeyPressEvent;\r
+import com.google.gwt.event.dom.client.KeyPressHandler;\r
+import com.google.gwt.user.client.Event;\r
+import com.google.gwt.user.client.Timer;\r
import com.google.gwt.user.client.ui.ListBox;\r
+import com.vaadin.terminal.gwt.client.BrowserInfo;\r
\r
-public class VTime extends FlowPanel implements ChangeHandler {\r
+public class VTime extends FocusableFlowPanel implements ChangeHandler,\r
+ KeyPressHandler, KeyDownHandler, FocusHandler {\r
\r
private final VDateField datefield;\r
\r
+ private final VCalendarPanel calendar;\r
+\r
private ListBox hours;\r
\r
private ListBox mins;\r
\r
private ListBox msec;\r
\r
- private ListBox ampm;\r
+ private AMPMListBox ampm;\r
\r
private int resolution = VDateField.RESOLUTION_HOUR;\r
\r
private boolean readonly;\r
\r
- public VTime(VDateField parent) {\r
+ /**\r
+ * The AM/PM Listbox yields the keyboard focus to the calendar\r
+ */\r
+ private class AMPMListBox extends ListBox {\r
+ public AMPMListBox() {\r
+ super();\r
+ sinkEvents(Event.ONKEYDOWN);\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see\r
+ * com.google.gwt.user.client.ui.Widget#onBrowserEvent(com.google.gwt\r
+ * .user.client.Event)\r
+ */\r
+ @Override\r
+ public void onBrowserEvent(Event event) {\r
+ super.onBrowserEvent(event);\r
+\r
+ if (event.getKeyCode() == KeyCodes.KEY_TAB) {\r
+ event.preventDefault();\r
+\r
+ /*\r
+ * Wait until the current event has been processed. If the timer\r
+ * is left out the focus will move to the VTime-class not the\r
+ * panel itself. Weird.\r
+ */\r
+ Timer t = new Timer() {\r
+ @Override\r
+ public void run() {\r
+ calendar.setFocus(true);\r
+ }\r
+ };\r
+ t.schedule(1);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Constructor\r
+ * \r
+ * @param parent\r
+ * The DateField related to this instance\r
+ * @param panel\r
+ * The panel where this this instance is embedded\r
+ */\r
+ public VTime(VDateField parent, VCalendarPanel panel) {\r
super();\r
datefield = parent;\r
+ calendar = panel;\r
setStyleName(VDateField.CLASSNAME + "-time");\r
+\r
+ /*\r
+ * Firefox auto-repeat works correctly only if we use a key press\r
+ * handler, other browsers handle it correctly when using a key down\r
+ * handler\r
+ */\r
+ if (BrowserInfo.get().isGecko()) {\r
+ addKeyPressHandler(this);\r
+ } else {\r
+ addKeyDownHandler(this);\r
+ }\r
+\r
+ addFocusHandler(this);\r
}\r
\r
+ /**\r
+ * Constructs the ListBoxes and updates their value\r
+ * \r
+ * @param redraw\r
+ * Should new instances of the listboxes be created\r
+ */\r
private void buildTime(boolean redraw) {\r
final boolean thc = datefield.getDateTimeService().isTwelveHourClock();\r
if (redraw) {\r
}\r
hours.addChangeHandler(this);\r
if (thc) {\r
- ampm = new ListBox();\r
+ ampm = new AMPMListBox();\r
ampm.setStyleName(VNativeSelect.CLASSNAME);\r
final String[] ampmText = datefield.getDateTimeService()\r
.getAmPmStrings();\r
+ calendar.setFocus(true);\r
ampm.addItem(ampmText[0]);\r
ampm.addItem(ampmText[1]);\r
ampm.addChangeHandler(this);\r
}\r
\r
// Update times\r
- Date cdate = datefield.getCurrentDate();\r
+ Date cdate = datefield.getShowingDate();\r
boolean selected = true;\r
if (cdate == null) {\r
cdate = new Date();\r
\r
}\r
\r
+ /**\r
+ * Update the time ListBoxes\r
+ * \r
+ * @param redraw\r
+ * Should new instances of the listboxes be created\r
+ */\r
public void updateTime(boolean redraw) {\r
buildTime(redraw || resolution != datefield.getCurrentResolution()\r
|| readonly != datefield.isReadonly());\r
readonly = datefield.isReadonly();\r
}\r
\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see\r
+ * com.google.gwt.event.dom.client.ChangeHandler#onChange(com.google.gwt\r
+ * .event.dom.client.ChangeEvent)\r
+ */\r
public void onChange(ChangeEvent event) {\r
if (datefield.getCurrentDate() == null) {\r
// was null on server, need to set\r
datefield.setCurrentDate(new Date(now.getTime()));\r
\r
// Init variables with current time\r
- datefield.getClient().updateVariable(datefield.getId(), "year",\r
- now.getYear() + 1900, false);\r
- datefield.getClient().updateVariable(datefield.getId(), "month",\r
- now.getMonth() + 1, false);\r
- datefield.getClient().updateVariable(datefield.getId(), "day",\r
- now.getDate(), false);\r
- datefield.getClient().updateVariable(datefield.getId(), "hour",\r
- now.getHours(), false);\r
- datefield.getClient().updateVariable(datefield.getId(), "min",\r
- now.getMinutes(), false);\r
- datefield.getClient().updateVariable(datefield.getId(), "sec",\r
- now.getSeconds(), false);\r
- datefield.getClient().updateVariable(datefield.getId(), "msec",\r
- datefield.getMilliseconds(), false);\r
+ notifyServerOfChanges();\r
}\r
if (event.getSource() == hours) {\r
int h = hours.getSelectedIndex();\r
if (datefield.getDateTimeService().isTwelveHourClock()) {\r
h = h + ampm.getSelectedIndex() * 12;\r
}\r
- datefield.getCurrentDate().setHours(h);\r
datefield.getShowingDate().setHours(h);\r
- datefield.getClient().updateVariable(datefield.getId(), "hour", h,\r
- datefield.isImmediate());\r
updateTime(false);\r
} else if (event.getSource() == mins) {\r
final int m = mins.getSelectedIndex();\r
- datefield.getCurrentDate().setMinutes(m);\r
datefield.getShowingDate().setMinutes(m);\r
- datefield.getClient().updateVariable(datefield.getId(), "min", m,\r
- datefield.isImmediate());\r
updateTime(false);\r
} else if (event.getSource() == sec) {\r
final int s = sec.getSelectedIndex();\r
- datefield.getCurrentDate().setSeconds(s);\r
datefield.getShowingDate().setSeconds(s);\r
- datefield.getClient().updateVariable(datefield.getId(), "sec", s,\r
- datefield.isImmediate());\r
updateTime(false);\r
} else if (event.getSource() == msec) {\r
final int ms = msec.getSelectedIndex();\r
- datefield.setMilliseconds(ms);\r
datefield.setShowingMilliseconds(ms);\r
- datefield.getClient().updateVariable(datefield.getId(), "msec", ms,\r
- datefield.isImmediate());\r
updateTime(false);\r
} else if (event.getSource() == ampm) {\r
- final int h = hours.getSelectedIndex() + ampm.getSelectedIndex()\r
- * 12;\r
- datefield.getCurrentDate().setHours(h);\r
+ final int h = hours.getSelectedIndex()\r
+ + (ampm.getSelectedIndex() * 12);\r
datefield.getShowingDate().setHours(h);\r
- datefield.getClient().updateVariable(datefield.getId(), "hour", h,\r
- datefield.isImmediate());\r
updateTime(false);\r
}\r
}\r
\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see\r
+ * com.google.gwt.event.dom.client.KeyPressHandler#onKeyPress(com.google\r
+ * .gwt.event.dom.client.KeyPressEvent)\r
+ */\r
+ public void onKeyPress(KeyPressEvent event) {\r
+ int keycode = event.getNativeEvent().getKeyCode();\r
+\r
+ if (calendar != null) {\r
+ if (keycode == calendar.getSelectKey()\r
+ || keycode == calendar.getCloseKey()) {\r
+ if (keycode == calendar.getSelectKey()) {\r
+ notifyServerOfChanges();\r
+ }\r
+\r
+ calendar.handleNavigation(keycode, event.getNativeEvent()\r
+ .getCtrlKey()\r
+ || event.getNativeEvent().getMetaKey(), event\r
+ .getNativeEvent().getShiftKey());\r
+ return;\r
+ }\r
+ }\r
+\r
+ event.stopPropagation();\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see\r
+ * com.google.gwt.event.dom.client.KeyDownHandler#onKeyDown(com.google.gwt\r
+ * .event.dom.client.KeyDownEvent)\r
+ */\r
+ public void onKeyDown(KeyDownEvent event) {\r
+ int keycode = event.getNativeEvent().getKeyCode();\r
+ if (keycode != calendar.getCloseKey()\r
+ && keycode != calendar.getSelectKey()) {\r
+ event.stopPropagation();\r
+ }\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see\r
+ * com.google.gwt.event.dom.client.FocusHandler#onFocus(com.google.gwt.event\r
+ * .dom.client.FocusEvent)\r
+ */\r
+ public void onFocus(FocusEvent event) {\r
+ event.preventDefault();\r
+\r
+ // Delegate focus to the hour select\r
+ hours.setFocus(true);\r
+ }\r
+\r
+ /**\r
+ * Update the variables server side\r
+ */\r
+ public void notifyServerOfChanges() {\r
+ /*\r
+ * Just update the variables, don't send any thing. The calendar panel\r
+ * will make the request when the panel is closed.\r
+ */\r
+ Date now = datefield.getCurrentDate();\r
+ datefield.getClient().updateVariable(datefield.getId(), "year",\r
+ now.getYear() + 1900, false);\r
+ datefield.getClient().updateVariable(datefield.getId(), "month",\r
+ now.getMonth() + 1, false);\r
+ datefield.getClient().updateVariable(datefield.getId(), "day",\r
+ now.getDate(), false);\r
+ datefield.getClient().updateVariable(datefield.getId(), "hour",\r
+ now.getHours(), false);\r
+ datefield.getClient().updateVariable(datefield.getId(), "min",\r
+ now.getMinutes(), false);\r
+ datefield.getClient().updateVariable(datefield.getId(), "sec",\r
+ now.getSeconds(), false);\r
+ datefield.getClient().updateVariable(datefield.getId(), "msec",\r
+ datefield.getShowingMilliseconds(), false);\r
+ }\r
+\r
}\r