From 5abcfae71f8c32d5209f1d9cc6870f50b69dd6fa Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Mon, 22 Mar 2010 11:34:39 +0000 Subject: Fix for #3492 - DateField should have an option to show week numbers svn changeset:12004/svn branch:6.3 --- .../terminal/gwt/client/DateTimeService.java | 31 ++++ .../terminal/gwt/client/ui/VCalendarPanel.java | 187 ++++++++++++++------- .../vaadin/terminal/gwt/client/ui/VDateField.java | 24 +++ src/com/vaadin/ui/DateField.java | 31 +++- 4 files changed, 213 insertions(+), 60 deletions(-) diff --git a/src/com/vaadin/terminal/gwt/client/DateTimeService.java b/src/com/vaadin/terminal/gwt/client/DateTimeService.java index e16d9b078d..7bc8e8eba4 100644 --- a/src/com/vaadin/terminal/gwt/client/DateTimeService.java +++ b/src/com/vaadin/terminal/gwt/client/DateTimeService.java @@ -234,4 +234,35 @@ public class DateTimeService { return ((y + 1900) * 10000 + m * 100 + d) * 1000000000; } + /** + * Returns the ISO-8601 week number of the given date. + * + * @param date + * The date for which the week number should be resolved + * @return The ISO-8601 week number for {@literal date} + */ + public static int getISOWeekNumber(Date date) { + final long MILLISECONDS_PER_DAY = 24 * 3600 * 1000; + int dayOfWeek = date.getDay(); // 0 == sunday + + // ISO 8601 use weeks that start on monday so we use + // mon=1,tue=2,...sun=7; + if (dayOfWeek == 0) { + dayOfWeek = 7; + } + // Find nearest thursday (defines the week in ISO 8601). The week number + // for the nearest thursday is the same as for the target date. + int nearestThursdayDiff = 4 - dayOfWeek; // 4 is thursday + Date nearestThursday = new Date(date.getTime() + nearestThursdayDiff + * MILLISECONDS_PER_DAY); + + Date firstOfJanuary = new Date(nearestThursday.getYear(), 0, 1); + long timeDiff = nearestThursday.getTime() - firstOfJanuary.getTime(); + int daysSinceFirstOfJanuary = (int) (timeDiff / MILLISECONDS_PER_DAY); + + int weekNumber = (daysSinceFirstOfJanuary) / 7 + 1; + + return weekNumber; + } + } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VCalendarPanel.java b/src/com/vaadin/terminal/gwt/client/ui/VCalendarPanel.java index 74c1a683e2..34c596ac20 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VCalendarPanel.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VCalendarPanel.java @@ -83,8 +83,12 @@ public class VCalendarPanel extends FlexTable implements MouseListener { private void clearCalendarBody(boolean remove) { if (!remove) { + // Leave the cells in place but clear their contents + + // This has the side effect of ensuring that the calendar always + // contain 7 rows. for (int row = 1; row < 7; row++) { - for (int col = 0; col < 7; col++) { + for (int col = 0; col < 8; col++) { days.setHTML(row, col, " "); } } @@ -157,16 +161,30 @@ public class VCalendarPanel extends FlexTable implements MouseListener { private void buildCalendarBody() { + final boolean showISOWeekNumbers = datefield.isShowISOWeekNumbers(); + final int columns = 1 + 5; + final int weekColumn = 0; + final int firstWeekdayColumn = 1; + final int headerRow = 0; + setWidget(1, 0, days); setCellPadding(0); setCellSpacing(0); - getFlexCellFormatter().setColSpan(1, 0, 5); + getFlexCellFormatter().setColSpan(1, 0, columns); getFlexCellFormatter().setStyleName(1, 0, VDateField.CLASSNAME + "-calendarpanel-body"); - days.getFlexCellFormatter().setStyleName(0, 0, "v-first"); - days.getFlexCellFormatter().setStyleName(0, 6, "v-last"); - days.getRowFormatter().setStyleName(0, + days.getFlexCellFormatter().setStyleName(headerRow, weekColumn, + "v-week"); + // Hide the week column if week numbers are not to be displayed. + days.getFlexCellFormatter().setVisible(headerRow, weekColumn, + showISOWeekNumbers); + + days.getFlexCellFormatter().setStyleName(headerRow, firstWeekdayColumn, + "v-first"); + days.getFlexCellFormatter().setStyleName(headerRow, + firstWeekdayColumn + 6, "v-last"); + days.getRowFormatter().setStyleName(headerRow, VDateField.CLASSNAME + "-calendarpanel-weekdays"); // Print weekday names @@ -177,78 +195,129 @@ public class VCalendarPanel extends FlexTable implements MouseListener { day = 0; } if (datefield.getCurrentResolution() > VDateField.RESOLUTION_MONTH) { - days.setHTML(0, i, "" + days.setHTML(headerRow, firstWeekdayColumn + i, "" + datefield.getDateTimeService().getShortDay(day) + ""); } else { - days.setHTML(0, i, ""); + days.setHTML(headerRow, firstWeekdayColumn + i, ""); } } // date actually selected? - Date currentDate = datefield.getCurrentDate(); + Date selectedDate = datefield.getCurrentDate(); + // Showing is the date (year/month+year) that is currently shown in the + // panel Date showing = datefield.getShowingDate(); - boolean selected = (currentDate != null - && currentDate.getMonth() == showing.getMonth() && currentDate - .getYear() == showing.getYear()); + + // 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) + int dayOfMonthSelected = -1; + // The day of month that is today, -1 if no day of this month is today + // (i.e., showing another month/year than current) + int dayOfMonthToday = -1; + + // Find out a day this month is selected + if (selectedDate != null + && selectedDate.getMonth() == showing.getMonth() + && selectedDate.getYear() == showing.getYear()) { + dayOfMonthSelected = selectedDate.getDate(); + } + + // Find out if today is in this month + final Date today = new Date(); + if (today.getMonth() == showing.getMonth() + && today.getYear() == showing.getYear()) { + dayOfMonthToday = today.getDate(); + } final int startWeekDay = datefield.getDateTimeService() - .getStartWeekDay(datefield.getShowingDate()); - final int numDays = DateTimeService.getNumberOfDaysInMonth(datefield - .getShowingDate()); + .getStartWeekDay(showing); + final int daysInMonth = DateTimeService.getNumberOfDaysInMonth(showing); + int dayCount = 0; - final Date today = new Date(); - final Date curr = new Date(datefield.getShowingDate().getTime()); - for (int row = 1; row < 7; row++) { - for (int col = 0; col < 7; col++) { - if (!(row == 1 && col < startWeekDay)) { - if (dayCount < numDays) { - final int selectedDate = ++dayCount; - String title = ""; - if (entrySource != null) { - curr.setDate(dayCount); - 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); - } + final Date curr = new Date(showing.getTime()); + + // No month has more than 6 weeks so 6 is a safe maximum for rows. + for (int weekOfMonth = 1; weekOfMonth < 7; weekOfMonth++) { + boolean weekNumberProcessed[] = new boolean[] { false, false, + false, false, false, false, false }; + + for (int dayOfWeek = 0; dayOfWeek < 7; dayOfWeek++) { + if (!(weekOfMonth == 1 && dayOfWeek < startWeekDay)) { + + if (dayCount >= daysInMonth) { + // All days printed and we are done + break; + } + + 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); } } - final String baseclass = VDateField.CLASSNAME - + "-calendarpanel-day"; - String cssClass = baseclass; - if (!isEnabledDate(curr)) { - cssClass += " " + baseclass + "-disabled"; - } - if (selected - && datefield.getShowingDate().getDate() == dayCount) { - cssClass += " " + baseclass + "-selected"; - } - if (today.getDate() == dayCount - && today.getMonth() == datefield - .getShowingDate().getMonth() - && today.getYear() == datefield - .getShowingDate().getYear()) { - cssClass += " " + baseclass + "-today"; - } - if (title.length() > 0) { - cssClass += " " + baseclass + "-entry"; - } - days.setHTML(row, col, "" - + selectedDate + ""); - } else { - break; } + // Add CSS classes according to state + String cssClass = baseclass; + + if (!isEnabledDate(curr)) { + cssClass += " " + baseclass + "-disabled"; + } + + if (dayOfMonthSelected == dayOfMonth) { + cssClass += " " + baseclass + "-selected"; + } + + if (dayOfMonthToday == dayOfMonth) { + cssClass += " " + baseclass + "-today"; + } + if (title.length() > 0) { + cssClass += " " + baseclass + "-entry"; + } + + // Actually write the day of month + days.setHTML(weekOfMonth, firstWeekdayColumn + dayOfWeek, + "" + dayOfMonth + ""); + + // ISO week numbers if requested + if (!weekNumberProcessed[weekOfMonth]) { + days.getCellFormatter().setVisible(weekOfMonth, + weekColumn, showISOWeekNumbers); + if (showISOWeekNumbers) { + final String baseCssClass = VDateField.CLASSNAME + + "-calendarpanel-weeknumber"; + String weekCssClass = baseCssClass; + + int weekNumber = DateTimeService + .getISOWeekNumber(curr); + + days.setHTML(weekOfMonth, 0, "" + weekNumber + + ""); + weekNumberProcessed[weekOfMonth] = true; + } + + } } } } + } private void buildTime(boolean forceRedraw) { diff --git a/src/com/vaadin/terminal/gwt/client/ui/VDateField.java b/src/com/vaadin/terminal/gwt/client/ui/VDateField.java index 1ce367e587..4afe0e31e2 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VDateField.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VDateField.java @@ -34,6 +34,8 @@ public class VDateField extends FlowPanel implements Paintable, Field { public static final int RESOLUTION_SEC = 5; public static final int RESOLUTION_MSEC = 6; + public static final String WEEK_NUMBERS = "wn"; + static String resolutionToString(int res) { if (res > RESOLUTION_DAY) { return "full"; @@ -55,12 +57,17 @@ public class VDateField extends FlowPanel implements Paintable, Field { protected boolean enabled; + /** + * The date that is selected in the date field. + */ protected Date date = null; // e.g when paging a calendar, before actually selecting protected Date showingDate = new Date(); protected DateTimeService dts; + private boolean showISOWeekNumbers = false; + public VDateField() { setStyleName(CLASSNAME); dts = new DateTimeService(); @@ -103,6 +110,11 @@ public class VDateField extends FlowPanel implements Paintable, Field { } } + // We show week numbers only if the week starts with Monday, as ISO 8601 + // specifies + showISOWeekNumbers = uidl.getBooleanAttribute(WEEK_NUMBERS) + && dts.getFirstDayOfWeek() == 1; + int newResolution; if (uidl.hasVariable("msec")) { newResolution = RESOLUTION_MSEC; @@ -246,4 +258,16 @@ public class VDateField extends FlowPanel implements Paintable, Field { public ApplicationConnection getClient() { return client; } + + /** + * Returns whether ISO 8601 week numbers should be shown in the date + * selector or not. ISO 8601 defines that a week always starts with a Monday + * so if the week starts with another day this will return false. + * + * @return true if week number should be shown, false otherwise + */ + public boolean isShowISOWeekNumbers() { + return showISOWeekNumbers; + } + } diff --git a/src/com/vaadin/ui/DateField.java b/src/com/vaadin/ui/DateField.java index 3c21630f0e..4f69b37de6 100644 --- a/src/com/vaadin/ui/DateField.java +++ b/src/com/vaadin/ui/DateField.java @@ -19,6 +19,7 @@ import com.vaadin.event.FieldEvents.FocusEvent; import com.vaadin.event.FieldEvents.FocusListener; import com.vaadin.terminal.PaintException; import com.vaadin.terminal.PaintTarget; +import com.vaadin.terminal.gwt.client.ui.VDateField; import com.vaadin.terminal.gwt.client.ui.VPopupCalendar; import com.vaadin.terminal.gwt.client.ui.VTextualDate; @@ -122,6 +123,11 @@ public class DateField extends AbstractField implements private boolean lenient = false; + /** + * Determines if week numbers are shown in the date selector. + */ + private boolean showISOWeekNumbers = false; + /* Constructors */ /** @@ -213,6 +219,7 @@ public class DateField extends AbstractField implements } target.addAttribute("type", type); + target.addAttribute(VDateField.WEEK_NUMBERS, isShowISOWeekNumbers()); // Gets the calendar final Calendar calendar = getCalendar(); @@ -557,7 +564,7 @@ public class DateField extends AbstractField implements } /** - * Specifies whether or not date/time interpretation is to be lenient. + * Returns whether date/time interpretation is to be lenient. Lenient * * @see #setLenient(boolean) * @@ -586,4 +593,26 @@ public class DateField extends AbstractField implements removeListener(BLUR_EVENT, BlurEvent.class, listener); } + /** + * Checks whether ISO 8601 week numbers are shown in the date selector. + * + * @return true if week numbers are shown, false otherwise. + */ + public boolean isShowISOWeekNumbers() { + return showISOWeekNumbers; + } + + /** + * Sets the visibility of ISO 8601 week numbers in the date selector. ISO + * 8601 defines that a week always starts with a Monday so the week numbers + * are only shown if this is the case. + * + * @param showWeekNumbers + * true if week numbers should be shown, false otherwise. + */ + public void setShowISOWeekNumbers(boolean showWeekNumbers) { + showISOWeekNumbers = showWeekNumbers; + requestRepaint(); + } + } -- cgit v1.2.3