]> source.dussan.org Git - vaadin-framework.git/commitdiff
Fix for #3492 - DateField should have an option to show week numbers
authorArtur Signell <artur.signell@itmill.com>
Mon, 22 Mar 2010 11:34:39 +0000 (11:34 +0000)
committerArtur Signell <artur.signell@itmill.com>
Mon, 22 Mar 2010 11:34:39 +0000 (11:34 +0000)
svn changeset:12004/svn branch:6.3

src/com/vaadin/terminal/gwt/client/DateTimeService.java
src/com/vaadin/terminal/gwt/client/ui/VCalendarPanel.java
src/com/vaadin/terminal/gwt/client/ui/VDateField.java
src/com/vaadin/ui/DateField.java

index e16d9b078d173e3d7b4cddc8399b28b1e319997f..7bc8e8eba4d2af0f40a4b67acab2363ab56ea066 100644 (file)
@@ -234,4 +234,35 @@ public class DateTimeService {
         return ((y + 1900) * 10000 + m * 100 + d) * 1000000000;\r
     }\r
 \r
+    /**\r
+     * Returns the ISO-8601 week number of the given date.\r
+     * \r
+     * @param date\r
+     *            The date for which the week number should be resolved\r
+     * @return The ISO-8601 week number for {@literal date}\r
+     */\r
+    public static int getISOWeekNumber(Date date) {\r
+        final long MILLISECONDS_PER_DAY = 24 * 3600 * 1000;\r
+        int dayOfWeek = date.getDay(); // 0 == sunday\r
+\r
+        // ISO 8601 use weeks that start on monday so we use\r
+        // mon=1,tue=2,...sun=7;\r
+        if (dayOfWeek == 0) {\r
+            dayOfWeek = 7;\r
+        }\r
+        // Find nearest thursday (defines the week in ISO 8601). The week number\r
+        // for the nearest thursday is the same as for the target date.\r
+        int nearestThursdayDiff = 4 - dayOfWeek; // 4 is thursday\r
+        Date nearestThursday = new Date(date.getTime() + nearestThursdayDiff\r
+                * MILLISECONDS_PER_DAY);\r
+\r
+        Date firstOfJanuary = new Date(nearestThursday.getYear(), 0, 1);\r
+        long timeDiff = nearestThursday.getTime() - firstOfJanuary.getTime();\r
+        int daysSinceFirstOfJanuary = (int) (timeDiff / MILLISECONDS_PER_DAY);\r
+\r
+        int weekNumber = (daysSinceFirstOfJanuary) / 7 + 1;\r
+\r
+        return weekNumber;\r
+    }\r
+\r
 }\r
index 74c1a683e236aa4427db9d06ed54313bcab6bb96..34c596ac206d581af66a223cbd2b2e77b5edd0c3 100644 (file)
@@ -83,8 +83,12 @@ public class VCalendarPanel extends FlexTable implements MouseListener {
 \r
     private void clearCalendarBody(boolean remove) {\r
         if (!remove) {\r
+            // Leave the cells in place but clear their contents\r
+\r
+            // This has the side effect of ensuring that the calendar always\r
+            // contain 7 rows.\r
             for (int row = 1; row < 7; row++) {\r
-                for (int col = 0; col < 7; col++) {\r
+                for (int col = 0; col < 8; col++) {\r
                     days.setHTML(row, col, "&nbsp;");\r
                 }\r
             }\r
@@ -157,16 +161,30 @@ public class VCalendarPanel extends FlexTable implements MouseListener {
 \r
     private void buildCalendarBody() {\r
 \r
+        final boolean showISOWeekNumbers = datefield.isShowISOWeekNumbers();\r
+        final int columns = 1 + 5;\r
+        final int weekColumn = 0;\r
+        final int firstWeekdayColumn = 1;\r
+        final int headerRow = 0;\r
+\r
         setWidget(1, 0, days);\r
         setCellPadding(0);\r
         setCellSpacing(0);\r
-        getFlexCellFormatter().setColSpan(1, 0, 5);\r
+        getFlexCellFormatter().setColSpan(1, 0, columns);\r
         getFlexCellFormatter().setStyleName(1, 0,\r
                 VDateField.CLASSNAME + "-calendarpanel-body");\r
 \r
-        days.getFlexCellFormatter().setStyleName(0, 0, "v-first");\r
-        days.getFlexCellFormatter().setStyleName(0, 6, "v-last");\r
-        days.getRowFormatter().setStyleName(0,\r
+        days.getFlexCellFormatter().setStyleName(headerRow, weekColumn,\r
+                "v-week");\r
+        // Hide the week column if week numbers are not to be displayed.\r
+        days.getFlexCellFormatter().setVisible(headerRow, weekColumn,\r
+                showISOWeekNumbers);\r
+\r
+        days.getFlexCellFormatter().setStyleName(headerRow, firstWeekdayColumn,\r
+                "v-first");\r
+        days.getFlexCellFormatter().setStyleName(headerRow,\r
+                firstWeekdayColumn + 6, "v-last");\r
+        days.getRowFormatter().setStyleName(headerRow,\r
                 VDateField.CLASSNAME + "-calendarpanel-weekdays");\r
 \r
         // Print weekday names\r
@@ -177,78 +195,129 @@ public class VCalendarPanel extends FlexTable implements MouseListener {
                 day = 0;\r
             }\r
             if (datefield.getCurrentResolution() > VDateField.RESOLUTION_MONTH) {\r
-                days.setHTML(0, i, "<strong>"\r
+                days.setHTML(headerRow, firstWeekdayColumn + i, "<strong>"\r
                         + datefield.getDateTimeService().getShortDay(day)\r
                         + "</strong>");\r
             } else {\r
-                days.setHTML(0, i, "");\r
+                days.setHTML(headerRow, firstWeekdayColumn + i, "");\r
             }\r
         }\r
 \r
         // date actually selected?\r
-        Date currentDate = datefield.getCurrentDate();\r
+        Date selectedDate = datefield.getCurrentDate();\r
+        // Showing is the date (year/month+year) that is currently shown in the\r
+        // panel\r
         Date showing = datefield.getShowingDate();\r
-        boolean selected = (currentDate != null\r
-                && currentDate.getMonth() == showing.getMonth() && currentDate\r
-                .getYear() == showing.getYear());\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 dayOfMonthSelected = -1;\r
+        // The day of month that is today, -1 if no day of this month is today\r
+        // (i.e., showing another month/year than current)\r
+        int dayOfMonthToday = -1;\r
+\r
+        // Find out a day this month is selected\r
+        if (selectedDate != null\r
+                && selectedDate.getMonth() == showing.getMonth()\r
+                && selectedDate.getYear() == showing.getYear()) {\r
+            dayOfMonthSelected = selectedDate.getDate();\r
+        }\r
+\r
+        // Find out if today is in this month\r
+        final Date today = new Date();\r
+        if (today.getMonth() == showing.getMonth()\r
+                && today.getYear() == showing.getYear()) {\r
+            dayOfMonthToday = today.getDate();\r
+        }\r
 \r
         final int startWeekDay = datefield.getDateTimeService()\r
-                .getStartWeekDay(datefield.getShowingDate());\r
-        final int numDays = DateTimeService.getNumberOfDaysInMonth(datefield\r
-                .getShowingDate());\r
+                .getStartWeekDay(showing);\r
+        final int daysInMonth = DateTimeService.getNumberOfDaysInMonth(showing);\r
+\r
         int dayCount = 0;\r
-        final Date today = new Date();\r
-        final Date curr = new Date(datefield.getShowingDate().getTime());\r
-        for (int row = 1; row < 7; row++) {\r
-            for (int col = 0; col < 7; col++) {\r
-                if (!(row == 1 && col < startWeekDay)) {\r
-                    if (dayCount < numDays) {\r
-                        final int selectedDate = ++dayCount;\r
-                        String title = "";\r
-                        if (entrySource != null) {\r
-                            curr.setDate(dayCount);\r
-                            final List entries = entrySource.getEntries(curr,\r
-                                    VDateField.RESOLUTION_DAY);\r
-                            if (entries != null) {\r
-                                for (final Iterator it = entries.iterator(); it\r
-                                        .hasNext();) {\r
-                                    final CalendarEntry entry = (CalendarEntry) it\r
-                                            .next();\r
-                                    title += (title.length() > 0 ? ", " : "")\r
-                                            + entry.getStringForDate(curr);\r
-                                }\r
+        final Date curr = new Date(showing.getTime());\r
+\r
+        // No month has more than 6 weeks so 6 is a safe maximum for rows.\r
+        for (int weekOfMonth = 1; weekOfMonth < 7; weekOfMonth++) {\r
+            boolean weekNumberProcessed[] = new boolean[] { false, false,\r
+                    false, false, false, false, false };\r
+\r
+            for (int dayOfWeek = 0; dayOfWeek < 7; dayOfWeek++) {\r
+                if (!(weekOfMonth == 1 && dayOfWeek < startWeekDay)) {\r
+\r
+                    if (dayCount >= daysInMonth) {\r
+                        // All days printed and we are done\r
+                        break;\r
+                    }\r
+\r
+                    final int dayOfMonth = ++dayCount;\r
+                    final String baseclass = VDateField.CLASSNAME\r
+                            + "-calendarpanel-day";\r
+\r
+                    String title = "";\r
+                    curr.setDate(dayCount);\r
+\r
+                    if (entrySource != null) {\r
+                        final List entries = entrySource.getEntries(curr,\r
+                                VDateField.RESOLUTION_DAY);\r
+                        if (entries != null) {\r
+                            for (final Iterator it = entries.iterator(); it\r
+                                    .hasNext();) {\r
+                                final CalendarEntry entry = (CalendarEntry) it\r
+                                        .next();\r
+                                title += (title.length() > 0 ? ", " : "")\r
+                                        + entry.getStringForDate(curr);\r
                             }\r
                         }\r
-                        final String baseclass = VDateField.CLASSNAME\r
-                                + "-calendarpanel-day";\r
-                        String cssClass = baseclass;\r
-                        if (!isEnabledDate(curr)) {\r
-                            cssClass += " " + baseclass + "-disabled";\r
-                        }\r
-                        if (selected\r
-                                && datefield.getShowingDate().getDate() == dayCount) {\r
-                            cssClass += " " + baseclass + "-selected";\r
-                        }\r
-                        if (today.getDate() == dayCount\r
-                                && today.getMonth() == datefield\r
-                                        .getShowingDate().getMonth()\r
-                                && today.getYear() == datefield\r
-                                        .getShowingDate().getYear()) {\r
-                            cssClass += " " + baseclass + "-today";\r
-                        }\r
-                        if (title.length() > 0) {\r
-                            cssClass += " " + baseclass + "-entry";\r
-                        }\r
-                        days.setHTML(row, col, "<span title=\"" + title\r
-                                + "\" class=\"" + cssClass + "\">"\r
-                                + selectedDate + "</span>");\r
-                    } else {\r
-                        break;\r
                     }\r
 \r
+                    // Add CSS classes according to state\r
+                    String cssClass = baseclass;\r
+\r
+                    if (!isEnabledDate(curr)) {\r
+                        cssClass += " " + baseclass + "-disabled";\r
+                    }\r
+\r
+                    if (dayOfMonthSelected == dayOfMonth) {\r
+                        cssClass += " " + baseclass + "-selected";\r
+                    }\r
+\r
+                    if (dayOfMonthToday == dayOfMonth) {\r
+                        cssClass += " " + baseclass + "-today";\r
+                    }\r
+                    if (title.length() > 0) {\r
+                        cssClass += " " + baseclass + "-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
+\r
+                    // ISO week numbers if requested\r
+                    if (!weekNumberProcessed[weekOfMonth]) {\r
+                        days.getCellFormatter().setVisible(weekOfMonth,\r
+                                weekColumn, showISOWeekNumbers);\r
+                        if (showISOWeekNumbers) {\r
+                            final String baseCssClass = VDateField.CLASSNAME\r
+                                    + "-calendarpanel-weeknumber";\r
+                            String weekCssClass = baseCssClass;\r
+\r
+                            int weekNumber = DateTimeService\r
+                                    .getISOWeekNumber(curr);\r
+\r
+                            days.setHTML(weekOfMonth, 0, "<span class=\""\r
+                                    + weekCssClass + "\"" + ">" + weekNumber\r
+                                    + "</span>");\r
+                            weekNumberProcessed[weekOfMonth] = true;\r
+                        }\r
+\r
+                    }\r
                 }\r
             }\r
         }\r
+\r
     }\r
 \r
     private void buildTime(boolean forceRedraw) {\r
index 1ce367e5872ee0fcb0571e8603a209c3c4011476..4afe0e31e29481c0be7999fd4d3dc0a71dc88f09 100644 (file)
@@ -34,6 +34,8 @@ public class VDateField extends FlowPanel implements Paintable, Field {
     public static final int RESOLUTION_SEC = 5;\r
     public static final int RESOLUTION_MSEC = 6;\r
 \r
+    public static final String WEEK_NUMBERS = "wn";\r
+\r
     static String resolutionToString(int res) {\r
         if (res > RESOLUTION_DAY) {\r
             return "full";\r
@@ -55,12 +57,17 @@ public class VDateField extends FlowPanel implements Paintable, Field {
 \r
     protected boolean enabled;\r
 \r
+    /**\r
+     * The date that is selected in the date field.\r
+     */\r
     protected Date date = null;\r
     // e.g when paging a calendar, before actually selecting\r
     protected Date showingDate = new Date();\r
 \r
     protected DateTimeService dts;\r
 \r
+    private boolean showISOWeekNumbers = false;\r
+\r
     public VDateField() {\r
         setStyleName(CLASSNAME);\r
         dts = new DateTimeService();\r
@@ -103,6 +110,11 @@ public class VDateField extends FlowPanel implements Paintable, Field {
             }\r
         }\r
 \r
+        // We show week numbers only if the week starts with Monday, as ISO 8601\r
+        // specifies\r
+        showISOWeekNumbers = uidl.getBooleanAttribute(WEEK_NUMBERS)\r
+                && dts.getFirstDayOfWeek() == 1;\r
+\r
         int newResolution;\r
         if (uidl.hasVariable("msec")) {\r
             newResolution = RESOLUTION_MSEC;\r
@@ -246,4 +258,16 @@ public class VDateField extends FlowPanel implements Paintable, Field {
     public ApplicationConnection getClient() {\r
         return client;\r
     }\r
+\r
+    /**\r
+     * Returns whether ISO 8601 week numbers should be shown in the date\r
+     * selector or not. ISO 8601 defines that a week always starts with a Monday\r
+     * so if the week starts with another day this will return false.\r
+     * \r
+     * @return true if week number should be shown, false otherwise\r
+     */\r
+    public boolean isShowISOWeekNumbers() {\r
+        return showISOWeekNumbers;\r
+    }\r
+\r
 }\r
index 3c21630f0efd25d074191e56e9ea986deeb7c19e..4f69b37de66e61fe66c12fe4ed7a8c64a84952d6 100644 (file)
@@ -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();
+    }
+
 }