]> source.dussan.org Git - vaadin-framework.git/commitdiff
Integrate Calendar into core #11079
authorJohn Ahlroos <john@vaadin.com>
Wed, 27 Mar 2013 14:33:28 +0000 (16:33 +0200)
committerVaadin Code Review <review@vaadin.com>
Wed, 3 Apr 2013 08:03:37 +0000 (08:03 +0000)
Everything else integrated, except TB3 tests (ticket #11090, old TB2 tests used instead)

Change-Id: If1700d7680a6c0a45f84d6e3c7b80e6536da78c8

80 files changed:
WebContent/VAADIN/themes/base/base.scss
WebContent/VAADIN/themes/base/calendar/calendar.scss [new file with mode: 0644]
WebContent/VAADIN/themes/base/calendar/img/arrows.png [new file with mode: 0644]
WebContent/VAADIN/themes/tests-calendar/styles.css [new file with mode: 0644]
client/src/com/vaadin/client/ui/VCalendar.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/CalendarConnector.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/VCalendarAction.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/CalendarDay.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/CalendarEvent.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/DateCell.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/DateCellContainer.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/DateCellGroup.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/DateUtil.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/DayToolbar.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/FocusableComplexPanel.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/FocusableGrid.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/FocusableHTML.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/HasTooltipKey.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/MonthEventLabel.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/MonthGrid.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/SimpleDayCell.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/SimpleDayToolbar.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/SimpleWeekToolbar.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/WeekGrid.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/WeekGridMinuteTimeRange.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/WeekLabel.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/WeeklyLongEvents.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/WeeklyLongEventsDateCell.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarDropHandler.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarMonthDropHandler.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarWeekDropHandler.java [new file with mode: 0644]
server/src/com/vaadin/ui/Calendar.java [new file with mode: 0644]
server/src/com/vaadin/ui/components/calendar/CalendarComponentEvent.java [new file with mode: 0644]
server/src/com/vaadin/ui/components/calendar/CalendarComponentEvents.java [new file with mode: 0644]
server/src/com/vaadin/ui/components/calendar/CalendarDateRange.java [new file with mode: 0644]
server/src/com/vaadin/ui/components/calendar/CalendarTargetDetails.java [new file with mode: 0644]
server/src/com/vaadin/ui/components/calendar/ContainerEventProvider.java [new file with mode: 0644]
server/src/com/vaadin/ui/components/calendar/event/BasicEvent.java [new file with mode: 0644]
server/src/com/vaadin/ui/components/calendar/event/BasicEventProvider.java [new file with mode: 0644]
server/src/com/vaadin/ui/components/calendar/event/CalendarEditableEventProvider.java [new file with mode: 0644]
server/src/com/vaadin/ui/components/calendar/event/CalendarEvent.java [new file with mode: 0644]
server/src/com/vaadin/ui/components/calendar/event/CalendarEventProvider.java [new file with mode: 0644]
server/src/com/vaadin/ui/components/calendar/event/EditableCalendarEvent.java [new file with mode: 0644]
server/src/com/vaadin/ui/components/calendar/handler/BasicBackwardHandler.java [new file with mode: 0644]
server/src/com/vaadin/ui/components/calendar/handler/BasicDateClickHandler.java [new file with mode: 0644]
server/src/com/vaadin/ui/components/calendar/handler/BasicEventMoveHandler.java [new file with mode: 0644]
server/src/com/vaadin/ui/components/calendar/handler/BasicEventResizeHandler.java [new file with mode: 0644]
server/src/com/vaadin/ui/components/calendar/handler/BasicForwardHandler.java [new file with mode: 0644]
server/src/com/vaadin/ui/components/calendar/handler/BasicWeekClickHandler.java [new file with mode: 0644]
server/tests/src/com/vaadin/tests/server/component/calendar/CalendarBasics.java [new file with mode: 0644]
server/tests/src/com/vaadin/tests/server/component/calendar/ContainerDataSource.java [new file with mode: 0644]
shared/src/com/vaadin/shared/ui/calendar/CalendarClientRpc.java [new file with mode: 0644]
shared/src/com/vaadin/shared/ui/calendar/CalendarEventId.java [new file with mode: 0644]
shared/src/com/vaadin/shared/ui/calendar/CalendarServerRpc.java [new file with mode: 0644]
shared/src/com/vaadin/shared/ui/calendar/CalendarState.java [new file with mode: 0644]
shared/src/com/vaadin/shared/ui/calendar/DateConstants.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/calendar/BeanItemContainerTestUI.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/calendar/CalendarActionsUI.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/calendar/CalendarTest.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/calendar/CalendarTestEvent.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/calendar/HiddenFwdBackButtons.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/calendar/NotificationTestUI.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/calendar/actions.html [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/calendar/basicNavigation.html [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/calendar/eventSizingNoOverlap.html [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/calendar/midnightEventsTest.html [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/calendar/monthlyViewNewEvent.html [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/calendar/monthlyViewNewEvents.html [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/calendar/notifications.html [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/calendar/sizeTest1000x600px.html [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/calendar/sizeTest100percentXundefined.html [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/calendar/sizeTest100x100percent.html [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/calendar/sizeTest300pxXundefined.html [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/calendar/sizeTestSizeFull.html [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/calendar/sizeTestUndefinedX100percent.html [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/calendar/sizeTestUndefinedX300px.html [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/calendar/sizeTestUndefinedXUndefined.html [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/calendar/visibleHours24H.html [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/calendar/weeklyViewNewEvents.html [new file with mode: 0644]

index 87754b6777278d4ecc4911573ff18f931fc1d5de..83e463fa0004c32fa42164ae52a86c373136e7d2 100644 (file)
@@ -4,6 +4,7 @@
 @import "button/nativebutton.scss";
 @import "button/checkbox.scss";
 @import "layout/layout.scss";
+@import "calendar/calendar.scss";
 @import "caption/caption.scss";
 @import "colorpicker/colorpicker.scss";
 @import "common/common.scss";
@@ -64,6 +65,7 @@ $line-height: normal;
        @include base-checkbox;
        @include base-caption;
        @include base-colorpicker;
+       @include base-calendar;
        
        // here for now to preserve old semantics
        @include base-common;
diff --git a/WebContent/VAADIN/themes/base/calendar/calendar.scss b/WebContent/VAADIN/themes/base/calendar/calendar.scss
new file mode 100644 (file)
index 0000000..8ff97df
--- /dev/null
@@ -0,0 +1,378 @@
+@mixin base-calendar($primaryStyleName : v-calendar) {
+
+/* Global resize style */
+.#{$primaryStyleName}-nresize DIV DIV {
+       cursor: n-resize !important;
+}
+
+.#{$primaryStyleName}-sresize DIV DIV {
+       cursor: s-resize !important;
+}
+
+/* Header bar */
+.#{$primaryStyleName} {
+       background-color: #fff;
+}
+
+.#{$primaryStyleName}-header-month,.#{$primaryStyleName}-header-week {
+       border-bottom: 1px solid #c1c1c1;
+}
+
+.#{$primaryStyleName}-header-day {
+       text-align: center;
+       color: #666;
+       font-size: 12px;
+       line-height: normal;
+}
+
+.#{$primaryStyleName}-header-week .#{$primaryStyleName}-header-day:hover {
+       cursor: pointer;
+       color: #222
+}
+
+.#{$primaryStyleName}-header-day-today {
+       font-weight: bold;
+       color: #444;
+}
+
+.#{$primaryStyleName}-header-month td:first-child {
+       padding-left: 19px;
+       /* Same as VCalendar.MONTHLY_WEEKTOOLBARWIDTH - .#{$primaryStyleName}-week-numbers border */
+}
+
+.#{$primaryStyleName}-header-week .#{$primaryStyleName}-back,.#{$primaryStyleName}-header-week .#{$primaryStyleName}-next
+       {
+       border: none;
+       padding: 0;
+       margin: 0;
+       height: 12px;
+       width: 12px;
+       overflow: hidden;
+       background: transparent url(img/arrows.png) no-repeat 50% 0;
+       opacity: .3;
+       filter: alpha(opacity = 30);
+       cursor: default;
+}
+
+.#{$primaryStyleName}-header-week .#{$primaryStyleName}-back:hover,.#{$primaryStyleName}-header-week .#{$primaryStyleName}-next:hover
+       {
+       opacity: .6;
+       filter: alpha(opacity = 60);
+}
+
+.#{$primaryStyleName}-header-week .#{$primaryStyleName}-back:active,.#{$primaryStyleName}-header-week .#{$primaryStyleName}-next:active
+       {
+       opacity: 1;
+       filter: alpha(opacity = 100);
+}
+
+.#{$primaryStyleName}-header-week .#{$primaryStyleName}-next {
+       background-position: 50% -12px;
+}
+
+/* Month grid */
+.#{$primaryStyleName}-month {
+       outline: none;
+}
+
+.#{$primaryStyleName}-week-numbers {
+       width: 20px;
+       border-right: 1px solid #ccc;
+}
+
+.#{$primaryStyleName}-week-number {
+       border: none;
+       background: transparent;
+       padding: 0;
+       margin: 0;
+       cursor: pointer;
+       opacity: .5;
+       width: 20px;
+       text-align: center;
+       border-bottom: 1px solid #ddd;
+}
+
+.#{$primaryStyleName}-week-number:hover {
+       opacity: 1;
+}
+
+.#{$primaryStyleName}-month-day {
+       border-bottom: 1px solid #ccc;
+       border-right: 1px solid #ccc;
+       outline: none;
+}
+
+.#{$primaryStyleName}-month-day-today {
+       background-color: #e7f0f5;
+}
+
+.#{$primaryStyleName}-month-day-selected {
+       background-color: #fffee7;
+}
+
+.#{$primaryStyleName}-month-day-dragemphasis {
+               background-color: #a8a8a8;
+}
+
+.#{$primaryStyleName}-month-day-scrollable {
+       overflow-y: scroll;
+}
+
+.#{$primaryStyleName}-day-number {
+       height: 18px;
+       line-height: 18px;
+       font-size: 12px;
+       text-align: right;
+       padding-right: 3px;
+       white-space: nowrap;
+}
+
+.#{$primaryStyleName}-day-number:hover {
+       cursor: pointer;
+       opacity: .6;
+       filter: alpha(opacity = 60);
+}
+
+.#{$primaryStyleName}-month .#{$primaryStyleName}-spacer,.#{$primaryStyleName}-month .#{$primaryStyleName}-bottom-spacer,.#{$primaryStyleName}-month .#{$primaryStyleName}-bottom-spacer-empty
+       {
+       /* Bottom spacer is used in GWT to measure the event height (offsetHeight) */
+       height: 15px;
+       font-size: 11px;
+}
+
+.#{$primaryStyleName}-month .#{$primaryStyleName}-bottom-spacer:hover {
+       cursor: pointer;
+       opacity: .6;
+       filter: alpha(opacity = 60);
+}
+
+.#{$primaryStyleName}-event {
+       line-height: 14px;
+       font-size: 11px;
+       padding: 0 0 0 4px;
+       cursor: pointer;
+       overflow: hidden;
+       text-overflow: ellipsis;
+       
+       outline: none;
+}
+
+.#{$primaryStyleName}-event-month {
+       margin-bottom: 1px;
+       white-space: nowrap;
+}
+
+.#{$primaryStyleName}-event-month:hover {
+       text-decoration: underline;
+}
+
+.#{$primaryStyleName}-event-all-day {
+       background: #999;
+       display: block;
+       margin-left: -2px;
+}
+
+div.#{$primaryStyleName}-event-all-day {
+       color: #fff;
+       height: 14px;
+}
+
+.#{$primaryStyleName}-event-continued-from {
+       margin-left: 0;
+}
+
+.#{$primaryStyleName}-event-start {
+       -webkit-border-top-left-radius: 6px;
+       -webkit-border-bottom-left-radius: 6px;
+       -moz-border-radius-topleft: 6px;
+       -moz-border-radius-bottomleft: 6px;
+       border-top-left-radius: 6px;
+       border-bottom-left-radius: 6px;
+       margin-left: 0;
+}
+
+.#{$primaryStyleName}-event-end {
+       -webkit-border-top-right-radius: 6px;
+       -webkit-border-bottom-right-radius: 6px;
+       -moz-border-radius-topright: 6px;
+       -moz-border-radius-bottomright: 6px;
+       border-top-right-radius: 6px;
+       border-bottom-right-radius: 6px;
+}
+
+/* Week/day view */
+.#{$primaryStyleName}-week-wrapper {
+       position: relative;
+}
+
+/*.v-ie7 .#{$primaryStyleName}-week-wrapper TABLE{
+       table-layout: fixed;
+}*/
+.#{$primaryStyleName}-times {
+       width: 51px;
+}
+
+.#{$primaryStyleName}-time {
+       padding: 0 8px 7px 0;
+       margin-top: -7px;
+       text-align: right;
+       font-size: 11px;
+       color: #666;
+       border-right: 1px solid #ccc;
+}
+
+.#{$primaryStyleName}-weekly-longevents {
+       border-left: 1px solid #ccc;
+       border-bottom: 2px solid #bbb;
+       margin-left: 50px;
+}
+
+.#{$primaryStyleName}-weekly-longevents .#{$primaryStyleName}-datecell {
+       border-right: 1px solid #ccc;
+       padding: 1px 0 0;
+}
+
+.#{$primaryStyleName}-weekly-longevents .#{$primaryStyleName}-event {
+       height: 14px;
+       margin-bottom: 1px;
+}
+
+.#{$primaryStyleName}-weekly-longevents .#{$primaryStyleName}-event:hover {
+       text-decoration: underline;
+}
+
+.#{$primaryStyleName}-day-times {
+       border-right: 1px solid #ccc;
+       outline: none;
+}
+
+.#{$primaryStyleName}-day-times .v-datecellslot,.#{$primaryStyleName}-day-times .v-datecellslot-even {
+       border-bottom: 1px solid #ccc;
+}
+
+.#{$primaryStyleName}-day-times .v-datecellslot-even {
+       border-bottom-color: #eee;
+}
+
+.#{$primaryStyleName}-day-times .v-daterange {
+       background-color: #a8a8a8;
+}
+
+.#{$primaryStyleName}-day-times .v-reserved {
+       background-color: #FF3333;
+}
+
+.#{$primaryStyleName}-day-times .dragemphasis {
+       background-color: #a8a8a8;
+}
+
+.#{$primaryStyleName}-week-wrapper .#{$primaryStyleName}-event {
+       padding: 0;
+       -webkit-border-radius: 4px;
+       -moz-border-radius: 4px;
+       border-radius: 4px;
+       margin-top: -1px;
+}
+
+.#{$primaryStyleName}-event-caption {
+       position: absolute;
+       z-index: 1;
+       top: 2px;
+       left: 4px;
+       width: 100%;
+       overflow: hidden;
+       text-overflow: ellipsis;
+       line-height: normal;
+}
+
+.#{$primaryStyleName}-event-content {
+       -webkit-border-radius: 4px;
+       -moz-border-radius: 4px;
+       border-radius: 4px;
+       border: 1px solid #777;
+       background: #eee;
+       opacity: .8;
+       filter: alpha(opacity = 80);
+       height: 14px; /* "min-height" */
+}
+
+.#{$primaryStyleName}-current-time {
+       position: absolute;
+       left: 0;
+       width: 100%;
+       height: 1px;
+       overflow: hidden;
+       background: #5a6c86;
+       opacity: .6;
+       filter: alpha(opacity = 60);
+       z-index: 2;
+}
+
+.#{$primaryStyleName}-event-resizetop {
+       position: absolute;
+       cursor: n-resize;
+       height: 5%;
+       min-height: 3px;
+       top: 0;
+       width: 100%;
+       z-index: 1;
+}
+
+.#{$primaryStyleName}-event-resizebottom {
+       position: absolute;
+       cursor: s-resize;
+       height: 5%;
+       min-height: 3px;
+       bottom: 0;
+       width: 100%;
+       z-index: 1;
+}
+
+.#{$primaryStyleName}-month-sizedheight .#{$primaryStyleName}-month-day {
+       height: 100px;
+}
+
+.#{$primaryStyleName}-month-sizedwidth .#{$primaryStyleName}-month-day {
+       width: 100px;
+}
+
+.#{$primaryStyleName}-header-month-Hsized .#{$primaryStyleName}-header-day {
+       width: 101px;
+}
+
+/* for others */
+.#{$primaryStyleName}-header-month-Hsized td:first-child {
+       padding-left: 21px;
+}
+
+.#{$primaryStyleName}-header-day-Hsized {
+       width: 200px;
+}
+
+.#{$primaryStyleName}-week-numbers-Vsized .#{$primaryStyleName}-week-number {
+       height: 100px;
+       line-height: 100px;
+}
+
+.#{$primaryStyleName}-week-wrapper-Vsized {
+       height: 400px;
+       overflow-x: hidden !important;
+}
+
+.#{$primaryStyleName}-times-Vsized .#{$primaryStyleName}-time {
+       height: 38px;
+}
+
+.#{$primaryStyleName}-times-Hsized .#{$primaryStyleName}-time {
+       width: 42px;
+}
+
+.#{$primaryStyleName}-day-times-Vsized .v-datecellslot,.#{$primaryStyleName}-day-times-Vsized .v-datecellslot-even {
+       height: 18px;
+}
+
+.#{$primaryStyleName}-day-times-Hsized, .#{$primaryStyleName}-day-times-Hsized .v-datecellslot,.#{$primaryStyleName}-day-times-Hsized .v-datecellslot-even {
+       width: 200px;
+}
+
+}
\ No newline at end of file
diff --git a/WebContent/VAADIN/themes/base/calendar/img/arrows.png b/WebContent/VAADIN/themes/base/calendar/img/arrows.png
new file mode 100644 (file)
index 0000000..9905c0b
Binary files /dev/null and b/WebContent/VAADIN/themes/base/calendar/img/arrows.png differ
diff --git a/WebContent/VAADIN/themes/tests-calendar/styles.css b/WebContent/VAADIN/themes/tests-calendar/styles.css
new file mode 100644 (file)
index 0000000..7a37fcf
--- /dev/null
@@ -0,0 +1,96 @@
+@import url(../reindeer/legacy-styles.css);
+
+.v-app {
+       background: #fff;
+       }
+
+
+/** Customized phase colors*/
+
+
+/**
+ * Green 
+ */
+
+/* For month view */
+.v-calendar .v-calendar-event-color1 {
+       color: #4f8324;
+       }
+.v-calendar .v-calendar-event-color1-all-day {
+       background-color: #61c114;
+       }
+
+/* For week/day view */
+.v-calendar .v-calendar-event-color1 .v-calendar-event-caption {
+       color: #4f8324;
+       }
+.v-calendar .v-calendar-event-color1 .v-calendar-event-content {
+       border-color: #61c114;
+       background-color: #daff70;
+       }
+
+
+/**
+ * Blue 
+ */
+
+/* For month view */
+.v-calendar .v-calendar-event-color2 {
+       color: #1c4b8b;
+       }
+.v-calendar .v-calendar-event-color2-all-day {
+       background-color: #0a56bc;
+       }
+
+/* For week/day view */
+.v-calendar .v-calendar-event-color2 .v-calendar-event-caption {
+       color: #1c4b8b;
+       }
+.v-calendar .v-calendar-event-color2 .v-calendar-event-content {
+       border-color: #0a56bc;
+       background-color: #529bff;
+       }
+
+
+/**
+ * Red 
+ */
+
+/* For month view */
+.v-calendar .v-calendar-event-color3 {
+       color: #831d1d;
+       }
+.v-calendar .v-calendar-event-color3-all-day {
+       background-color: #bd1a1a;
+       }
+
+/* For week/day view */
+.v-calendar .v-calendar-event-color3 .v-calendar-event-caption {
+       color: #831d1d;
+       }
+.v-calendar .v-calendar-event-color3 .v-calendar-event-content {
+       border-color: #bd1a1a;
+       background-color: #ff9d9d;
+       }
+
+
+/**
+ * Orange 
+ */
+
+/* For month view */
+.v-calendar .v-calendar-event-color4 {
+       color: #8b5923;
+       }
+.v-calendar .v-calendar-event-color4-all-day {
+       background-color: #cd6a00;
+       }
+
+/* For week/day view */
+.v-calendar .v-calendar-event-color4 .v-calendar-event-caption {
+       color: #8b5923;
+       }
+.v-calendar .v-calendar-event-color4 .v-calendar-event-content {
+       border-color: #cd6a00;
+       background-color: #faa345;
+       }
\ No newline at end of file
diff --git a/client/src/com/vaadin/client/ui/VCalendar.java b/client/src/com/vaadin/client/ui/VCalendar.java
new file mode 100644 (file)
index 0000000..e66a2d7
--- /dev/null
@@ -0,0 +1,1444 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+
+import com.google.gwt.event.dom.client.ContextMenuEvent;
+import com.google.gwt.event.dom.client.ContextMenuHandler;
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.DockPanel;
+import com.google.gwt.user.client.ui.ScrollPanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.client.ui.calendar.schedule.CalendarDay;
+import com.vaadin.client.ui.calendar.schedule.CalendarEvent;
+import com.vaadin.client.ui.calendar.schedule.DayToolbar;
+import com.vaadin.client.ui.calendar.schedule.MonthGrid;
+import com.vaadin.client.ui.calendar.schedule.SimpleDayCell;
+import com.vaadin.client.ui.calendar.schedule.SimpleDayToolbar;
+import com.vaadin.client.ui.calendar.schedule.SimpleWeekToolbar;
+import com.vaadin.client.ui.calendar.schedule.WeekGrid;
+import com.vaadin.client.ui.calendar.schedule.WeeklyLongEvents;
+import com.vaadin.shared.ui.calendar.DateConstants;
+
+/**
+ * Client side implementation for Calendar
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ */
+public class VCalendar extends Composite {
+
+    public static final String ATTR_FIRSTDAYOFWEEK = "firstDay";
+    public static final String ATTR_LASTDAYOFWEEK = "lastDay";
+    public static final String ATTR_FIRSTHOUROFDAY = "firstHour";
+    public static final String ATTR_LASTHOUROFDAY = "lastHour";
+
+    // private boolean hideWeekends;
+    private String[] monthNames;
+    private String[] dayNames;
+    private boolean format;
+    private final DockPanel outer = new DockPanel();
+    private int rows;
+
+    private boolean rangeSelectAllowed = true;
+    private boolean rangeMoveAllowed = true;
+    private boolean eventResizeAllowed = true;
+    private boolean eventMoveAllowed = true;
+
+    private final SimpleDayToolbar nameToolbar = new SimpleDayToolbar();
+
+    private final DayToolbar dayToolbar = new DayToolbar(this);
+    private final SimpleWeekToolbar weekToolbar;
+    private WeeklyLongEvents weeklyLongEvents;
+    private MonthGrid monthGrid;
+    private WeekGrid weekGrid;
+    private int intWidth = 0;
+    private int intHeight = 0;
+
+    protected final DateTimeFormat dateformat_datetime = DateTimeFormat
+            .getFormat("yyyy-MM-dd HH:mm:ss");
+    protected final DateTimeFormat dateformat_date = DateTimeFormat
+            .getFormat("yyyy-MM-dd");
+    protected final DateTimeFormat time12format_date = DateTimeFormat
+            .getFormat("h:mm a");
+    protected final DateTimeFormat time24format_date = DateTimeFormat
+            .getFormat("HH:mm");
+
+    private boolean readOnly = false;
+    private boolean disabled = false;
+
+    private boolean isHeightUndefined = false;
+
+    private boolean isWidthUndefined = false;
+    private int firstDay;
+    private int lastDay;
+    private int firstHour;
+    private int lastHour;
+
+    /**
+     * Listener interface for listening to event click events
+     */
+    public interface DateClickListener {
+        /**
+         * Triggered when a date was clicked
+         * 
+         * @param date
+         *            The date and time that was clicked
+         */
+        void dateClick(String date);
+    }
+
+    /**
+     * Listener interface for listening to week number click events
+     */
+    public interface WeekClickListener {
+        /**
+         * Called when a week number was selected.
+         * 
+         * @param event
+         *            The format of the vent string is "<year>w<week>"
+         */
+        void weekClick(String event);
+    }
+
+    /**
+     * Listener interface for listening to forward events
+     */
+    public interface ForwardListener {
+
+        /**
+         * Called when the calendar should move one view forward
+         */
+        void forward();
+    }
+
+    /**
+     * Listener interface for listening to backward events
+     */
+    public interface BackwardListener {
+
+        /**
+         * Called when the calendar should move one view backward
+         */
+        void backward();
+    }
+
+    /**
+     * Listener interface for listening to selection events
+     */
+    public interface RangeSelectListener {
+
+        /**
+         * Called when a user selected a new event by highlighting an area of
+         * the calendar.
+         * 
+         * FIXME Fix the value nonsense.
+         * 
+         * @param value
+         *            The format of the value string is
+         *            "<year>:<start-minutes>:<end-minutes>" if called from the
+         *            {@link SimpleWeekToolbar} and "<yyyy-MM-dd>TO<yyyy-MM-dd>"
+         *            if called from {@link MonthGrid}
+         */
+        void rangeSelected(String value);
+    }
+
+    /**
+     * Listener interface for listening to click events
+     */
+    public interface EventClickListener {
+        /**
+         * Called when an event was clicked
+         * 
+         * @param event
+         *            The event that was clicked
+         */
+        void eventClick(CalendarEvent event);
+    }
+
+    /**
+     * Listener interface for listening to event moved events. Occurs when a
+     * user drags an event to a new position
+     */
+    public interface EventMovedListener {
+        /**
+         * Triggered when an event was dragged to a new position and the start
+         * and end dates was changed
+         * 
+         * @param event
+         *            The event that was moved
+         */
+        void eventMoved(CalendarEvent event);
+    }
+
+    /**
+     * Listener interface for when an event gets resized (its start or end date
+     * changes)
+     */
+    public interface EventResizeListener {
+        /**
+         * Triggers when the time limits for the event was changed.
+         * 
+         * @param event
+         *            The event that was changed. The new time limits have been
+         *            updated in the event before calling this method
+         */
+        void eventResized(CalendarEvent event);
+    }
+
+    /**
+     * Listener interface for listening to scroll events.
+     */
+    public interface ScrollListener {
+        /**
+         * Triggered when the calendar is scrolled
+         * 
+         * @param scrollPosition
+         *            The scroll position in pixels as returned by
+         *            {@link ScrollPanel#getScrollPosition()}
+         */
+        void scroll(int scrollPosition);
+    }
+
+    /**
+     * Listener interface for listening to mouse events.
+     */
+    public interface MouseEventListener {
+        /**
+         * Triggered when a user wants an context menu
+         * 
+         * @param event
+         *            The context menu event
+         * 
+         * @param widget
+         *            The widget that the context menu should be added to
+         */
+        void contextMenu(ContextMenuEvent event, Widget widget);
+    }
+
+    /**
+     * Default constructor
+     */
+    public VCalendar() {
+        weekToolbar = new SimpleWeekToolbar(this);
+        initWidget(outer);
+        setStylePrimaryName("v-calendar");
+        blockSelect(getElement());
+    }
+
+    /**
+     * Hack for IE to not select text when dragging.
+     * 
+     * @param e
+     *            The element to apply the hack on
+     */
+    private native void blockSelect(Element e)
+    /*-{
+       e.onselectstart = function() {
+               return false;
+       }
+
+       e.ondragstart = function() {
+               return false;
+       }
+    }-*/;
+
+    private void updateEventsToWeekGrid(CalendarEvent[] events) {
+        List<CalendarEvent> allDayLong = new ArrayList<CalendarEvent>();
+        List<CalendarEvent> belowDayLong = new ArrayList<CalendarEvent>();
+
+        for (CalendarEvent e : events) {
+            if (e.isAllDay()) {
+                // Event is set on one "allDay" event or more than one.
+                allDayLong.add(e);
+
+            } else {
+                // Event is set only on one day.
+                belowDayLong.add(e);
+            }
+        }
+
+        weeklyLongEvents.addEvents(allDayLong);
+
+        for (CalendarEvent e : belowDayLong) {
+            weekGrid.addEvent(e);
+        }
+    }
+
+    /**
+     * Adds events to the month grid
+     * 
+     * @param events
+     *            The events to add
+     * @param drawImmediately
+     *            Should the grid be rendered immediately. (currently not in
+     *            use)
+     * 
+     */
+    public void updateEventsToMonthGrid(Collection<CalendarEvent> events,
+            boolean drawImmediately) {
+        for (CalendarEvent e : sortEventsByDuration(events)) {
+            // FIXME Why is drawImmediately not used ?????
+            addEventToMonthGrid(e, false);
+        }
+    }
+
+    private void addEventToMonthGrid(CalendarEvent e, boolean renderImmediately) {
+        Date when = e.getStart();
+        Date to = e.getEnd();
+        boolean eventAdded = false;
+        boolean inProgress = false; // Event adding has started
+        boolean eventMoving = false;
+        List<SimpleDayCell> dayCells = new ArrayList<SimpleDayCell>();
+        List<SimpleDayCell> timeCells = new ArrayList<SimpleDayCell>();
+        for (int row = 0; row < monthGrid.getRowCount(); row++) {
+            if (eventAdded) {
+                break;
+            }
+            for (int cell = 0; cell < monthGrid.getCellCount(row); cell++) {
+                SimpleDayCell sdc = (SimpleDayCell) monthGrid.getWidget(row,
+                        cell);
+                if (isEventInDay(when, to, sdc.getDate())
+                        && isEventInDayWithTime(when, to, sdc.getDate(),
+                                e.getEndTime(), e.isAllDay())) {
+                    if (!eventMoving) {
+                        eventMoving = sdc.getMoveEvent() != null;
+                    }
+                    long d = e.getRangeInMilliseconds();
+                    if ((d > 0 && d <= DateConstants.DAYINMILLIS)
+                            && !e.isAllDay()) {
+                        timeCells.add(sdc);
+                    } else {
+                        dayCells.add(sdc);
+                    }
+                    inProgress = true;
+                    continue;
+                } else if (inProgress) {
+                    eventAdded = true;
+                    inProgress = false;
+                    break;
+                }
+            }
+        }
+
+        updateEventSlotIndex(e, dayCells);
+        updateEventSlotIndex(e, timeCells);
+
+        for (SimpleDayCell sdc : dayCells) {
+            sdc.addCalendarEvent(e);
+        }
+        for (SimpleDayCell sdc : timeCells) {
+            sdc.addCalendarEvent(e);
+        }
+
+        if (renderImmediately) {
+            reDrawAllMonthEvents(!eventMoving);
+        }
+    }
+
+    /*
+     * We must also handle the special case when the event lasts exactly for 24
+     * hours, thus spanning two days e.g. from 1.1.2001 00:00 to 2.1.2001 00:00.
+     * That special case still should span one day when rendered.
+     */
+    @SuppressWarnings("deprecation")
+    // Date methods are not deprecated in GWT
+    private boolean isEventInDayWithTime(Date from, Date to, Date date,
+            Date endTime, boolean isAllDay) {
+        return (isAllDay || !(to.getDay() == date.getDay()
+                && from.getDay() != to.getDay() && isMidnight(endTime)));
+    }
+
+    private void updateEventSlotIndex(CalendarEvent e, List<SimpleDayCell> cells) {
+        if (cells.isEmpty()) {
+            return;
+        }
+
+        if (e.getSlotIndex() == -1) {
+            // Update slot index
+            int newSlot = -1;
+            for (SimpleDayCell sdc : cells) {
+                int slot = sdc.getEventCount();
+                if (slot > newSlot) {
+                    newSlot = slot;
+                }
+            }
+            newSlot++;
+
+            for (int i = 0; i < newSlot; i++) {
+                // check for empty slot
+                if (isSlotEmpty(e, i, cells)) {
+                    newSlot = i;
+                    break;
+                }
+            }
+            e.setSlotIndex(newSlot);
+        }
+    }
+
+    private void reDrawAllMonthEvents(boolean clearCells) {
+        for (int row = 0; row < monthGrid.getRowCount(); row++) {
+            for (int cell = 0; cell < monthGrid.getCellCount(row); cell++) {
+                SimpleDayCell sdc = (SimpleDayCell) monthGrid.getWidget(row,
+                        cell);
+                sdc.reDraw(clearCells);
+            }
+        }
+    }
+
+    private boolean isSlotEmpty(CalendarEvent addedEvent, int slotIndex,
+            List<SimpleDayCell> cells) {
+        for (SimpleDayCell sdc : cells) {
+            CalendarEvent e = sdc.getCalendarEvent(slotIndex);
+            if (e != null && !e.equals(addedEvent)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Remove a month event from the view
+     * 
+     * @param target
+     *            The event to remove
+     * 
+     * @param repaintImmediately
+     *            Should we repaint after the event was removed?
+     */
+    public void removeMonthEvent(CalendarEvent target,
+            boolean repaintImmediately) {
+        if (target != null && target.getSlotIndex() >= 0) {
+            // Remove event
+            for (int row = 0; row < monthGrid.getRowCount(); row++) {
+                for (int cell = 0; cell < monthGrid.getCellCount(row); cell++) {
+                    SimpleDayCell sdc = (SimpleDayCell) monthGrid.getWidget(
+                            row, cell);
+                    if (sdc == null) {
+                        return;
+                    }
+                    sdc.removeEvent(target, repaintImmediately);
+                }
+            }
+        }
+    }
+
+    /**
+     * Updates an event in the month grid
+     * 
+     * @param changedEvent
+     *            The event that has changed
+     */
+    public void updateEventToMonthGrid(CalendarEvent changedEvent) {
+        removeMonthEvent(changedEvent, true);
+        changedEvent.setSlotIndex(-1);
+        addEventToMonthGrid(changedEvent, true);
+    }
+
+    /**
+     * Sort the event by how long they are
+     * 
+     * @param events
+     *            The events to sort
+     * @return An array where the events has been sorted
+     */
+    public CalendarEvent[] sortEventsByDuration(Collection<CalendarEvent> events) {
+        CalendarEvent[] sorted = events
+                .toArray(new CalendarEvent[events.size()]);
+        Arrays.sort(sorted, getEventComparator());
+        return sorted;
+    }
+
+    /*
+     * Check if the given event occurs at the given date.
+     */
+    private boolean isEventInDay(Date eventWhen, Date eventTo, Date gridDate) {
+        if (eventWhen.compareTo(gridDate) <= 0
+                && eventTo.compareTo(gridDate) >= 0) {
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Re-render the week grid
+     * 
+     * @param daysCount
+     *            The amount of days to include in the week
+     * @param days
+     *            The days
+     * @param today
+     *            Todays date
+     * @param realDayNames
+     *            The names of the dates
+     */
+    @SuppressWarnings("deprecation")
+    public void updateWeekGrid(int daysCount, List<CalendarDay> days,
+            Date today, String[] realDayNames) {
+        weekGrid.setFirstHour(getFirstHourOfTheDay());
+        weekGrid.setLastHour(getLastHourOfTheDay());
+        weekGrid.getTimeBar().updateTimeBar(is24HFormat());
+
+        dayToolbar.clear();
+        dayToolbar.addBackButton();
+        dayToolbar.setVerticalSized(isHeightUndefined);
+        dayToolbar.setHorizontalSized(isWidthUndefined);
+        weekGrid.clearDates();
+        weekGrid.setDisabled(isDisabledOrReadOnly());
+
+        for (CalendarDay day : days) {
+            String date = day.getDate();
+            String localized_date_format = day.getLocalizedDateFormat();
+            Date d = dateformat_date.parse(date);
+            int dayOfWeek = day.getDayOfWeek();
+            if (dayOfWeek < getFirstDayNumber()
+                    || dayOfWeek > getLastDayNumber()) {
+                continue;
+            }
+            boolean isToday = false;
+            int dayOfMonth = d.getDate();
+            if (today.getDate() == dayOfMonth && today.getYear() == d.getYear()
+                    && today.getMonth() == d.getMonth()) {
+                isToday = true;
+            }
+            dayToolbar.add(realDayNames[dayOfWeek - 1], date,
+                    localized_date_format, isToday ? "today" : null);
+            weeklyLongEvents.addDate(d);
+            weekGrid.addDate(d);
+            if (isToday) {
+                weekGrid.setToday(d, today);
+            }
+        }
+        dayToolbar.addNextButton();
+    }
+
+    /**
+     * Updates the events in the Month view
+     * 
+     * @param daysCount
+     *            How many days there are
+     * @param daysUidl
+     * 
+     * @param today
+     *            Todays date
+     */
+    @SuppressWarnings("deprecation")
+    public void updateMonthGrid(int daysCount, List<CalendarDay> days,
+            Date today) {
+        int columns = getLastDayNumber() - getFirstDayNumber() + 1;
+        rows = (int) Math.ceil(daysCount / (double) 7);
+
+        monthGrid = new MonthGrid(this, rows, columns);
+        monthGrid.setEnabled(!isDisabledOrReadOnly());
+        weekToolbar.removeAllRows();
+        int pos = 0;
+        boolean monthNameDrawn = true;
+        boolean firstDayFound = false;
+        boolean lastDayFound = false;
+
+        for (CalendarDay day : days) {
+            String date = day.getDate();
+            Date d = dateformat_date.parse(date);
+            int dayOfWeek = day.getDayOfWeek();
+            int week = day.getWeek();
+
+            int dayOfMonth = d.getDate();
+
+            // reset at start of each month
+            if (dayOfMonth == 1) {
+                monthNameDrawn = false;
+                if (firstDayFound) {
+                    lastDayFound = true;
+                }
+                firstDayFound = true;
+            }
+
+            if (dayOfWeek < getFirstDayNumber()
+                    || dayOfWeek > getLastDayNumber()) {
+                continue;
+            }
+            int y = (pos / columns);
+            int x = pos - (y * columns);
+            if (x == 0 && daysCount > 7) {
+                // Add week to weekToolbar for navigation
+                weekToolbar.addWeek(week, d.getYear());
+            }
+            final SimpleDayCell cell = new SimpleDayCell(this, y, x);
+            cell.setMonthGrid(monthGrid);
+            cell.setDate(d);
+            cell.addDomHandler(new ContextMenuHandler() {
+                public void onContextMenu(ContextMenuEvent event) {
+                    if (mouseEventListener != null) {
+                        event.preventDefault();
+                        event.stopPropagation();
+                        mouseEventListener.contextMenu(event, cell);
+                    }
+                }
+            }, ContextMenuEvent.getType());
+
+            if (!firstDayFound) {
+                cell.addStyleDependentName("prev-month");
+            } else if (lastDayFound) {
+                cell.addStyleDependentName("next-month");
+            }
+
+            if (dayOfMonth >= 1 && !monthNameDrawn) {
+                cell.setMonthNameVisible(true);
+                monthNameDrawn = true;
+            }
+
+            if (today.getDate() == dayOfMonth && today.getYear() == d.getYear()
+                    && today.getMonth() == d.getMonth()) {
+                cell.setToday(true);
+
+            }
+            monthGrid.setWidget(y, x, cell);
+            pos++;
+        }
+    }
+
+    public void setSizeForChildren(int newWidth, int newHeight) {
+        intWidth = newWidth;
+        intHeight = newHeight;
+        isWidthUndefined = intWidth == -1;
+        dayToolbar.setVerticalSized(isHeightUndefined);
+        dayToolbar.setHorizontalSized(isWidthUndefined);
+        recalculateWidths();
+        recalculateHeights();
+    }
+
+    /**
+     * Recalculates the heights of the sub-components in the calendar
+     */
+    protected void recalculateHeights() {
+        if (monthGrid != null) {
+
+            if (intHeight == -1) {
+                monthGrid.addStyleDependentName("sizedheight");
+            } else {
+                monthGrid.removeStyleDependentName("sizedheight");
+            }
+
+            monthGrid.updateCellSizes(intWidth - weekToolbar.getOffsetWidth(),
+                    intHeight - nameToolbar.getOffsetHeight());
+            weekToolbar.setHeightPX((intHeight == -1) ? intHeight : intHeight
+                    - nameToolbar.getOffsetHeight());
+
+        } else if (weekGrid != null) {
+            weekGrid.setHeightPX((intHeight == -1) ? intHeight : intHeight
+                    - weeklyLongEvents.getOffsetHeight()
+                    - dayToolbar.getOffsetHeight());
+        }
+    }
+
+    /**
+     * Recalculates the widths of the sub-components in the calendar
+     */
+    protected void recalculateWidths() {
+        if (!isWidthUndefined) {
+            nameToolbar.setWidthPX(intWidth);
+            dayToolbar.setWidthPX(intWidth);
+
+            if (monthGrid != null) {
+                monthGrid.updateCellSizes(
+                        intWidth - weekToolbar.getOffsetWidth(), intHeight
+                                - nameToolbar.getOffsetHeight());
+            } else if (weekGrid != null) {
+                weekGrid.setWidthPX(intWidth);
+                weeklyLongEvents.setWidthPX(weekGrid.getInternalWidth());
+            }
+        } else {
+            dayToolbar.setWidthPX(intWidth);
+            nameToolbar.setWidthPX(intWidth);
+
+            if (monthGrid != null) {
+                if (intWidth == -1) {
+                    monthGrid.addStyleDependentName("sizedwidth");
+
+                } else {
+                    monthGrid.removeStyleDependentName("sizedwidth");
+                }
+            } else if (weekGrid != null) {
+                weekGrid.setWidthPX(intWidth);
+                weeklyLongEvents.setWidthPX(weekGrid.getInternalWidth());
+            }
+        }
+    }
+
+    /**
+     * Get the date format used to format dates only (excludes time)
+     * 
+     * @return
+     */
+    public DateTimeFormat getDateFormat() {
+        return dateformat_date;
+    }
+
+    /**
+     * Get the time format used to format time only (excludes date)
+     * 
+     * @return
+     */
+    public DateTimeFormat getTimeFormat() {
+        if (is24HFormat()) {
+            return time24format_date;
+        }
+        return time12format_date;
+    }
+
+    /**
+     * Get the date and time format to format the dates (includes both date and
+     * time)
+     * 
+     * @return
+     */
+    public DateTimeFormat getDateTimeFormat() {
+        return dateformat_datetime;
+    }
+
+    /**
+     * Is the calendar either disabled or readonly
+     * 
+     * @return
+     */
+    public boolean isDisabledOrReadOnly() {
+        return disabled || readOnly;
+    }
+
+    /**
+     * Is the component disabled
+     */
+    public boolean isDisabled() {
+        return disabled;
+    }
+
+    /**
+     * Is the component disabled
+     * 
+     * @param disabled
+     *            True if disabled
+     */
+    public void setDisabled(boolean disabled) {
+        this.disabled = disabled;
+    }
+
+    /**
+     * Is the component read-only
+     */
+    public boolean isReadOnly() {
+        return readOnly;
+    }
+
+    /**
+     * Is the component read-only
+     * 
+     * @param readOnly
+     *            True if component is readonly
+     */
+    public void setReadOnly(boolean readOnly) {
+        this.readOnly = readOnly;
+    }
+
+    /**
+     * Get the month grid component
+     * 
+     * @return
+     */
+    public MonthGrid getMonthGrid() {
+        return monthGrid;
+    }
+
+    /**
+     * Get he week grid component
+     * 
+     * @return
+     */
+    public WeekGrid getWeekGrid() {
+        return weekGrid;
+    }
+
+    /**
+     * Calculates correct size for all cells (size / amount of cells ) and
+     * distributes any overflow over all the cells.
+     * 
+     * @param totalSize
+     *            the total amount of size reserved for all cells
+     * @param numberOfCells
+     *            the number of cells
+     * @param sizeModifier
+     *            a modifier which is applied to all cells before distributing
+     *            the overflow
+     * @return an integer array that contains the correct size for each cell
+     */
+    public static int[] distributeSize(int totalSize, int numberOfCells,
+            int sizeModifier) {
+        int[] cellSizes = new int[numberOfCells];
+        int startingSize = totalSize / numberOfCells;
+        int cellSizeOverFlow = totalSize % numberOfCells;
+
+        for (int i = 0; i < numberOfCells; i++) {
+            cellSizes[i] = startingSize + sizeModifier;
+        }
+
+        // distribute size overflow amongst all slots
+        int j = 0;
+        while (cellSizeOverFlow > 0) {
+            cellSizes[j]++;
+            cellSizeOverFlow--;
+            j++;
+            if (j >= numberOfCells) {
+                j = 0;
+            }
+        }
+
+        // cellSizes[numberOfCells - 1] += cellSizeOverFlow;
+
+        return cellSizes;
+    }
+
+    /**
+     * Returns a comparator which can compare calendar events.
+     * 
+     * @return
+     */
+    public static Comparator<CalendarEvent> getEventComparator() {
+        return new Comparator<CalendarEvent>() {
+
+            public int compare(CalendarEvent o1, CalendarEvent o2) {
+                if (o1.isAllDay() != o2.isAllDay()) {
+                    if (o2.isAllDay()) {
+                        return 1;
+                    }
+                    return -1;
+                }
+
+                Long d1 = o1.getRangeInMilliseconds();
+                Long d2 = o2.getRangeInMilliseconds();
+                int r = 0;
+                if (!d1.equals(0L) && !d2.equals(0L)) {
+                    r = d2.compareTo(d1);
+                    return (r == 0) ? ((Integer) o2.getIndex()).compareTo(o1
+                            .getIndex()) : r;
+                }
+
+                if (d2.equals(0L) && d1.equals(0L)) {
+                    return ((Integer) o2.getIndex()).compareTo(o1.getIndex());
+                } else if (d2.equals(0L) && d1 >= DateConstants.DAYINMILLIS) {
+                    return -1;
+                } else if (d2.equals(0L) && d1 < DateConstants.DAYINMILLIS) {
+                    return 1;
+                } else if (d1.equals(0L) && d2 >= DateConstants.DAYINMILLIS) {
+                    return 1;
+                } else if (d1.equals(0L) && d2 < DateConstants.DAYINMILLIS) {
+                    return -1;
+                }
+                r = d2.compareTo(d1);
+                return (r == 0) ? ((Integer) o2.getIndex()).compareTo(o1
+                        .getIndex()) : r;
+            }
+        };
+    }
+
+    /**
+     * Is the date at midnight
+     * 
+     * @param date
+     *            The date to check
+     * 
+     * @return
+     */
+    @SuppressWarnings("deprecation")
+    public static boolean isMidnight(Date date) {
+        return (date.getHours() == 0 && date.getMinutes() == 0 && date
+                .getSeconds() == 0);
+    }
+
+    /**
+     * Are the dates equal (uses second resolution)
+     * 
+     * @param date1
+     *            The first the to compare
+     * @param date2
+     *            The second date to compare
+     * @return
+     */
+    @SuppressWarnings("deprecation")
+    public static boolean areDatesEqualToSecond(Date date1, Date date2) {
+        return date1.getYear() == date2.getYear()
+                && date1.getMonth() == date2.getMonth()
+                && date1.getDay() == date2.getDay()
+                && date1.getHours() == date2.getHours()
+                && date1.getSeconds() == date2.getSeconds();
+    }
+
+    /**
+     * Is the calendar event zero seconds long and is occurring at midnight
+     * 
+     * @param event
+     *            The event to check
+     * @return
+     */
+    public static boolean isZeroLengthMidnightEvent(CalendarEvent event) {
+        return areDatesEqualToSecond(event.getStartTime(), event.getEndTime())
+                && isMidnight(event.getEndTime());
+    }
+
+    /**
+     * Should the 24h time format be used
+     * 
+     * @param format
+     *            True if the 24h format should be used else the 12h format is
+     *            used
+     */
+    public void set24HFormat(boolean format) {
+        this.format = format;
+    }
+
+    /**
+     * Is the 24h time format used
+     */
+    public boolean is24HFormat() {
+        return format;
+    }
+
+    /**
+     * Set the names of the week days
+     * 
+     * @param names
+     *            The names of the days (Monday, Thursday,...)
+     */
+    public void setDayNames(String[] names) {
+        assert (names.length == 7);
+        dayNames = names;
+    }
+
+    /**
+     * Get the names of the week days
+     */
+    public String[] getDayNames() {
+        return dayNames;
+    }
+
+    /**
+     * Set the names of the months
+     * 
+     * @param names
+     *            The names of the months (January, February,...)
+     */
+    public void setMonthNames(String[] names) {
+        assert (names.length == 12);
+        monthNames = names;
+    }
+
+    /**
+     * Get the month names
+     */
+    public String[] getMonthNames() {
+        return monthNames;
+    }
+
+    /**
+     * Set the number when a week starts
+     * 
+     * @param dayNumber
+     *            The number of the day
+     */
+    public void setFirstDayNumber(int dayNumber) {
+        assert (dayNumber >= 1 && dayNumber <= 7);
+        firstDay = dayNumber;
+    }
+
+    /**
+     * Get the number when a week starts
+     */
+    public int getFirstDayNumber() {
+        return firstDay;
+    }
+
+    /**
+     * Set the number when a week ends
+     * 
+     * @param dayNumber
+     *            The number of the day
+     */
+    public void setLastDayNumber(int dayNumber) {
+        assert (dayNumber >= 1 && dayNumber <= 7);
+        lastDay = dayNumber;
+    }
+
+    /**
+     * Get the number when a week ends
+     */
+    public int getLastDayNumber() {
+        return lastDay;
+    }
+
+    /**
+     * Set the number when a week starts
+     * 
+     * @param dayNumber
+     *            The number of the day
+     */
+    public void setFirstHourOfTheDay(int hour) {
+        assert (hour >= 0 && hour <= 23);
+        firstHour = hour;
+    }
+
+    /**
+     * Get the number when a week starts
+     */
+    public int getFirstHourOfTheDay() {
+        return firstHour;
+    }
+
+    /**
+     * Set the number when a week ends
+     * 
+     * @param dayNumber
+     *            The number of the day
+     */
+    public void setLastHourOfTheDay(int hour) {
+        assert (hour >= 0 && hour <= 23);
+        lastHour = hour;
+    }
+
+    /**
+     * Get the number when a week ends
+     */
+    public int getLastHourOfTheDay() {
+        return lastHour;
+    }
+
+    /**
+     * Re-renders the whole week view
+     * 
+     * @param scroll
+     *            The amount of pixels to scroll the week view
+     * @param today
+     *            Todays date
+     * @param daysInMonth
+     *            How many days are there in the month
+     * @param firstDayOfWeek
+     *            The first day of the week
+     * @param events
+     *            The events to render
+     */
+    public void updateWeekView(int scroll, Date today, int daysInMonth,
+            int firstDayOfWeek, Collection<CalendarEvent> events,
+            List<CalendarDay> days) {
+
+        while (outer.getWidgetCount() > 0) {
+            outer.remove(0);
+        }
+
+        monthGrid = null;
+        String[] realDayNames = new String[getDayNames().length];
+        int j = 0;
+
+        if (firstDayOfWeek == 2) {
+            for (int i = 1; i < getDayNames().length; i++) {
+                realDayNames[j++] = getDayNames()[i];
+            }
+            realDayNames[j] = getDayNames()[0];
+        } else {
+            for (int i = 0; i < getDayNames().length; i++) {
+                realDayNames[j++] = getDayNames()[i];
+            }
+
+        }
+
+        weeklyLongEvents = new WeeklyLongEvents(this);
+        if (weekGrid == null) {
+            weekGrid = new WeekGrid(this, is24HFormat());
+        }
+        updateWeekGrid(daysInMonth, days, today, realDayNames);
+        updateEventsToWeekGrid(sortEventsByDuration(events));
+        outer.add(dayToolbar, DockPanel.NORTH);
+        outer.add(weeklyLongEvents, DockPanel.NORTH);
+        outer.add(weekGrid, DockPanel.SOUTH);
+        weekGrid.setVerticalScrollPosition(scroll);
+    }
+
+    /**
+     * Re-renders the whole month view
+     * 
+     * @param firstDayOfWeek
+     *            The first day of the week
+     * @param today
+     *            Todays date
+     * @param daysInMonth
+     *            Amount of days in the month
+     * @param events
+     *            The events to render
+     * @param days
+     *            The day information
+     */
+    public void updateMonthView(int firstDayOfWeek, Date today,
+            int daysInMonth, Collection<CalendarEvent> events,
+            List<CalendarDay> days) {
+
+        // Remove all week numbers from bar
+        while (outer.getWidgetCount() > 0) {
+            outer.remove(0);
+        }
+
+        int firstDay = getFirstDayNumber();
+        int lastDay = getLastDayNumber();
+        int daysPerWeek = lastDay - firstDay + 1;
+        int j = 0;
+
+        String[] dayNames = getDayNames();
+        String[] realDayNames = new String[daysPerWeek];
+
+        if (firstDayOfWeek == 2) {
+            for (int i = firstDay; i < lastDay + 1; i++) {
+                if (i == 7) {
+                    realDayNames[j++] = dayNames[0];
+                } else {
+                    realDayNames[j++] = dayNames[i];
+                }
+            }
+        } else {
+            for (int i = firstDay - 1; i < lastDay; i++) {
+                realDayNames[j++] = dayNames[i];
+            }
+        }
+
+        nameToolbar.setDayNames(realDayNames);
+
+        weeklyLongEvents = null;
+        weekGrid = null;
+
+        updateMonthGrid(daysInMonth, days, today);
+
+        outer.add(nameToolbar, DockPanel.NORTH);
+        outer.add(weekToolbar, DockPanel.WEST);
+        weekToolbar.updateCellHeights();
+        outer.add(monthGrid, DockPanel.CENTER);
+
+        updateEventsToMonthGrid(events, false);
+    }
+
+    private DateClickListener dateClickListener;
+
+    /**
+     * Sets the listener for listening to event clicks
+     * 
+     * @param listener
+     *            The listener to use
+     */
+    public void setListener(DateClickListener listener) {
+        dateClickListener = listener;
+    }
+
+    /**
+     * Gets the listener for listening to event clicks
+     * 
+     * @return
+     */
+    public DateClickListener getDateClickListener() {
+        return dateClickListener;
+    }
+
+    private ForwardListener forwardListener;
+
+    /**
+     * Set the listener which listens to forward events from the calendar
+     * 
+     * @param listener
+     *            The listener to use
+     */
+    public void setListener(ForwardListener listener) {
+        forwardListener = listener;
+    }
+
+    /**
+     * Get the listener which listens to forward events from the calendar
+     * 
+     * @return
+     */
+    public ForwardListener getForwardListener() {
+        return forwardListener;
+    }
+
+    private BackwardListener backwardListener;
+
+    /**
+     * Set the listener which listens to backward events from the calendar
+     * 
+     * @param listener
+     *            The listener to use
+     */
+    public void setListener(BackwardListener listener) {
+        backwardListener = listener;
+    }
+
+    /**
+     * Set the listener which listens to backward events from the calendar
+     * 
+     * @return
+     */
+    public BackwardListener getBackwardListener() {
+        return backwardListener;
+    }
+
+    private WeekClickListener weekClickListener;
+
+    /**
+     * Set the listener that listens to user clicking on the week numbers
+     * 
+     * @param listener
+     *            The listener to use
+     */
+    public void setListener(WeekClickListener listener) {
+        weekClickListener = listener;
+    }
+
+    /**
+     * Get the listener that listens to user clicking on the week numbers
+     * 
+     * @return
+     */
+    public WeekClickListener getWeekClickListener() {
+        return weekClickListener;
+    }
+
+    private RangeSelectListener rangeSelectListener;
+
+    /**
+     * Set the listener that listens to the user highlighting a region in the
+     * calendar
+     * 
+     * @param listener
+     *            The listener to use
+     */
+    public void setListener(RangeSelectListener listener) {
+        rangeSelectListener = listener;
+    }
+
+    /**
+     * Get the listener that listens to the user highlighting a region in the
+     * calendar
+     * 
+     * @return
+     */
+    public RangeSelectListener getRangeSelectListener() {
+        return rangeSelectListener;
+    }
+
+    private EventClickListener eventClickListener;
+
+    /**
+     * Get the listener that listens to the user clicking on the events
+     */
+    public EventClickListener getEventClickListener() {
+        return eventClickListener;
+    }
+
+    /**
+     * Set the listener that listens to the user clicking on the events
+     * 
+     * @param listener
+     *            The listener to use
+     */
+    public void setListener(EventClickListener listener) {
+        eventClickListener = listener;
+    }
+
+    private EventMovedListener eventMovedListener;
+
+    /**
+     * Get the listener that listens to when event is dragged to a new location
+     * 
+     * @return
+     */
+    public EventMovedListener getEventMovedListener() {
+        return eventMovedListener;
+    }
+
+    /**
+     * Set the listener that listens to when event is dragged to a new location
+     * 
+     * @param eventMovedListener
+     *            The listener to use
+     */
+    public void setListener(EventMovedListener eventMovedListener) {
+        this.eventMovedListener = eventMovedListener;
+    }
+
+    private ScrollListener scrollListener;
+
+    /**
+     * Get the listener that listens to when the calendar widget is scrolled
+     * 
+     * @return
+     */
+    public ScrollListener getScrollListener() {
+        return scrollListener;
+    }
+
+    /**
+     * Set the listener that listens to when the calendar widget is scrolled
+     * 
+     * @param scrollListener
+     *            The listener to use
+     */
+    public void setListener(ScrollListener scrollListener) {
+        this.scrollListener = scrollListener;
+    }
+
+    private EventResizeListener eventResizeListener;
+
+    /**
+     * Get the listener that listens to when an events time limits are being
+     * adjusted
+     * 
+     * @return
+     */
+    public EventResizeListener getEventResizeListener() {
+        return eventResizeListener;
+    }
+
+    /**
+     * Set the listener that listens to when an events time limits are being
+     * adjusted
+     * 
+     * @param eventResizeListener
+     *            The listener to use
+     */
+    public void setListener(EventResizeListener eventResizeListener) {
+        this.eventResizeListener = eventResizeListener;
+    }
+
+    private MouseEventListener mouseEventListener;
+    private boolean forwardNavigationEnabled = true;
+    private boolean backwardNavigationEnabled = true;
+
+    /**
+     * Get the listener that listen to mouse events
+     * 
+     * @return
+     */
+    public MouseEventListener getMouseEventListener() {
+        return mouseEventListener;
+    }
+
+    /**
+     * Set the listener that listen to mouse events
+     * 
+     * @param mouseEventListener
+     *            The listener to use
+     */
+    public void setListener(MouseEventListener mouseEventListener) {
+        this.mouseEventListener = mouseEventListener;
+    }
+
+    /**
+     * Is selecting a range allowed?
+     */
+    public boolean isRangeSelectAllowed() {
+        return rangeSelectAllowed;
+    }
+
+    /**
+     * Set selecting a range allowed
+     * 
+     * @param rangeSelectAllowed
+     *            Should selecting a range be allowed
+     */
+    public void setRangeSelectAllowed(boolean rangeSelectAllowed) {
+        this.rangeSelectAllowed = rangeSelectAllowed;
+    }
+
+    /**
+     * Is moving a range allowed
+     * 
+     * @return
+     */
+    public boolean isRangeMoveAllowed() {
+        return rangeMoveAllowed;
+    }
+
+    /**
+     * Is moving a range allowed
+     * 
+     * @param rangeMoveAllowed
+     *            Is it allowed
+     */
+    public void setRangeMoveAllowed(boolean rangeMoveAllowed) {
+        this.rangeMoveAllowed = rangeMoveAllowed;
+    }
+
+    /**
+     * Is resizing an event allowed
+     */
+    public boolean isEventResizeAllowed() {
+        return eventResizeAllowed;
+    }
+
+    /**
+     * Is resizing an event allowed
+     * 
+     * @param eventResizeAllowed
+     *            True if allowed false if not
+     */
+    public void setEventResizeAllowed(boolean eventResizeAllowed) {
+        this.eventResizeAllowed = eventResizeAllowed;
+    }
+
+    /**
+     * Is moving an event allowed
+     */
+    public boolean isEventMoveAllowed() {
+        return eventMoveAllowed;
+    }
+
+    /**
+     * Is moving an event allowed
+     * 
+     * @param eventMoveAllowed
+     *            True if moving is allowed, false if not
+     */
+    public void setEventMoveAllowed(boolean eventMoveAllowed) {
+        this.eventMoveAllowed = eventMoveAllowed;
+    }
+
+    public boolean isBackwardNavigationEnabled() {
+        return backwardNavigationEnabled;
+    }
+
+    public void setBackwardNavigationEnabled(boolean enabled) {
+        backwardNavigationEnabled = enabled;
+    }
+
+    public boolean isForwardNavigationEnabled() {
+        return forwardNavigationEnabled;
+    }
+
+    public void setForwardNavigationEnabled(boolean enabled) {
+        forwardNavigationEnabled = enabled;
+    }
+}
diff --git a/client/src/com/vaadin/client/ui/calendar/CalendarConnector.java b/client/src/com/vaadin/client/ui/calendar/CalendarConnector.java
new file mode 100644 (file)
index 0000000..120a65d
--- /dev/null
@@ -0,0 +1,637 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar;
+
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+
+import com.google.gwt.core.shared.GWT;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.event.dom.client.ContextMenuEvent;
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.client.ApplicationConnection;
+import com.vaadin.client.TooltipInfo;
+import com.vaadin.client.UIDL;
+import com.vaadin.client.Util;
+import com.vaadin.client.VConsole;
+import com.vaadin.client.communication.RpcProxy;
+import com.vaadin.client.communication.StateChangeEvent;
+import com.vaadin.client.ui.AbstractComponentConnector;
+import com.vaadin.client.ui.Action;
+import com.vaadin.client.ui.ActionOwner;
+import com.vaadin.client.ui.SimpleManagedLayout;
+import com.vaadin.client.ui.VCalendar;
+import com.vaadin.client.ui.VCalendar.BackwardListener;
+import com.vaadin.client.ui.VCalendar.DateClickListener;
+import com.vaadin.client.ui.VCalendar.EventClickListener;
+import com.vaadin.client.ui.VCalendar.EventMovedListener;
+import com.vaadin.client.ui.VCalendar.EventResizeListener;
+import com.vaadin.client.ui.VCalendar.ForwardListener;
+import com.vaadin.client.ui.VCalendar.MouseEventListener;
+import com.vaadin.client.ui.VCalendar.RangeSelectListener;
+import com.vaadin.client.ui.VCalendar.WeekClickListener;
+import com.vaadin.client.ui.calendar.schedule.CalendarDay;
+import com.vaadin.client.ui.calendar.schedule.CalendarEvent;
+import com.vaadin.client.ui.calendar.schedule.DateCell;
+import com.vaadin.client.ui.calendar.schedule.DateCell.DateCellSlot;
+import com.vaadin.client.ui.calendar.schedule.DateUtil;
+import com.vaadin.client.ui.calendar.schedule.DateCellDayEvent;
+import com.vaadin.client.ui.calendar.schedule.HasTooltipKey;
+import com.vaadin.client.ui.calendar.schedule.SimpleDayCell;
+import com.vaadin.client.ui.calendar.schedule.dd.CalendarDropHandler;
+import com.vaadin.client.ui.dd.VHasDropHandler;
+import com.vaadin.shared.ui.Connect;
+import com.vaadin.shared.ui.Connect.LoadStyle;
+import com.vaadin.shared.ui.calendar.CalendarClientRpc;
+import com.vaadin.shared.ui.calendar.CalendarEventId;
+import com.vaadin.shared.ui.calendar.CalendarServerRpc;
+import com.vaadin.shared.ui.calendar.CalendarState;
+import com.vaadin.shared.ui.calendar.DateConstants;
+import com.vaadin.ui.Calendar;
+
+/**
+ * Handles communication between Calendar on the server side and
+ * {@link VCalendar} on the client side.
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ */
+@Connect(value = Calendar.class, loadStyle = LoadStyle.LAZY)
+public class CalendarConnector extends AbstractComponentConnector implements
+        VHasDropHandler, ActionOwner, SimpleManagedLayout {
+
+    private CalendarServerRpc rpc = RpcProxy.create(CalendarServerRpc.class,
+            this);
+
+    private CalendarDropHandler dropHandler;
+
+    private final HashMap<String, String> actionMap = new HashMap<String, String>();
+    private HashMap<Object, String> tooltips = new HashMap<Object, String>();
+
+    /**
+     * 
+     */
+    public CalendarConnector() {
+
+        // Listen to events
+        registerListeners();
+    }
+
+    @Override
+    protected void init() {
+        super.init();
+        registerRpc(CalendarClientRpc.class, new CalendarClientRpc() {
+            @Override
+            public void scroll(int scrollPosition) {
+                // TODO widget scroll
+            }
+        });
+        getLayoutManager().registerDependency(this, getWidget().getElement());
+    }
+
+    @Override
+    public void onUnregister() {
+        super.onUnregister();
+        getLayoutManager().unregisterDependency(this, getWidget().getElement());
+    }
+
+    @Override
+    public VCalendar getWidget() {
+        return (VCalendar) super.getWidget();
+    }
+
+    @Override
+    public CalendarState getState() {
+        return (CalendarState) super.getState();
+    }
+
+    /**
+     * Registers listeners on the calendar so server can be notified of the
+     * events
+     */
+    protected void registerListeners() {
+        getWidget().setListener(new DateClickListener() {
+            public void dateClick(String date) {
+                if (!getWidget().isDisabledOrReadOnly()
+                        && hasEventListener(CalendarEventId.DATECLICK)) {
+                    rpc.dateClick(date);
+                }
+            }
+        });
+        getWidget().setListener(new ForwardListener() {
+            public void forward() {
+                if (hasEventListener(CalendarEventId.FORWARD)) {
+                    rpc.forward();
+                }
+            }
+        });
+        getWidget().setListener(new BackwardListener() {
+            public void backward() {
+                if (hasEventListener(CalendarEventId.BACKWARD)) {
+                    rpc.backward();
+                }
+            }
+        });
+        getWidget().setListener(new RangeSelectListener() {
+            public void rangeSelected(String value) {
+                if (hasEventListener(CalendarEventId.RANGESELECT)) {
+                    rpc.rangeSelect(value);
+                }
+            }
+        });
+        getWidget().setListener(new WeekClickListener() {
+            public void weekClick(String event) {
+                if (!getWidget().isDisabledOrReadOnly()
+                        && hasEventListener(CalendarEventId.WEEKCLICK)) {
+                    rpc.weekClick(event);
+                }
+            }
+        });
+        getWidget().setListener(new EventMovedListener() {
+            public void eventMoved(CalendarEvent event) {
+                if (hasEventListener(CalendarEventId.EVENTMOVE)) {
+                    StringBuilder sb = new StringBuilder();
+                    sb.append(DateUtil.formatClientSideDate(event.getStart()));
+                    sb.append("-");
+                    sb.append(DateUtil.formatClientSideTime(event
+                            .getStartTime()));
+                    rpc.eventMove(event.getIndex(), sb.toString());
+                }
+            }
+        });
+        getWidget().setListener(new EventResizeListener() {
+            public void eventResized(CalendarEvent event) {
+                if (hasEventListener(CalendarEventId.EVENTRESIZE)) {
+                    StringBuilder buffer = new StringBuilder();
+
+                    buffer.append(DateUtil.formatClientSideDate(event
+                            .getStart()));
+                    buffer.append("-");
+                    buffer.append(DateUtil.formatClientSideTime(event
+                            .getStartTime()));
+
+                    String newStartDate = buffer.toString();
+
+                    buffer = new StringBuilder();
+                    buffer.append(DateUtil.formatClientSideDate(event.getEnd()));
+                    buffer.append("-");
+                    buffer.append(DateUtil.formatClientSideTime(event
+                            .getEndTime()));
+
+                    String newEndDate = buffer.toString();
+
+                    rpc.eventResize(event.getIndex(), newStartDate, newEndDate);
+                }
+            }
+        });
+        getWidget().setListener(new VCalendar.ScrollListener() {
+            public void scroll(int scrollPosition) {
+                // This call is @Delayed (== non-immediate)
+                rpc.scroll(scrollPosition);
+            }
+        });
+        getWidget().setListener(new EventClickListener() {
+            public void eventClick(CalendarEvent event) {
+                if (hasEventListener(CalendarEventId.EVENTCLICK)) {
+                    rpc.eventClick(event.getIndex());
+                }
+            }
+        });
+        getWidget().setListener(new MouseEventListener() {
+            public void contextMenu(ContextMenuEvent event, final Widget widget) {
+                final NativeEvent ne = event.getNativeEvent();
+                int left = ne.getClientX();
+                int top = ne.getClientY();
+                top += Window.getScrollTop();
+                left += Window.getScrollLeft();
+                getClient().getContextMenu().showAt(new ActionOwner() {
+                    public String getPaintableId() {
+                        return CalendarConnector.this.getPaintableId();
+                    }
+
+                    public ApplicationConnection getClient() {
+                        return CalendarConnector.this.getClient();
+                    }
+
+                    @SuppressWarnings("deprecation")
+                    public Action[] getActions() {
+                        if (widget instanceof SimpleDayCell) {
+                            /*
+                             * Month view
+                             */
+                            SimpleDayCell cell = (SimpleDayCell) widget;
+                            Date start = new Date(cell.getDate().getYear(),
+                                    cell.getDate().getMonth(), cell.getDate()
+                                            .getDate(), 0, 0, 0);
+
+                            Date end = new Date(cell.getDate().getYear(), cell
+                                    .getDate().getMonth(), cell.getDate()
+                                    .getDate(), 23, 59, 59);
+
+                            return CalendarConnector.this.getActionsBetween(
+                                    start, end);
+                        } else if (widget instanceof DateCell) {
+                            /*
+                             * Week and Day view
+                             */
+                            DateCell cell = (DateCell) widget;
+                            int slotIndex = DOM.getChildIndex(
+                                    cell.getElement(), (Element) ne
+                                            .getEventTarget().cast());
+                            DateCellSlot slot = cell.getSlot(slotIndex);
+                            return CalendarConnector.this.getActionsBetween(
+                                    slot.getFrom(), slot.getTo());
+                        } else if (widget instanceof DateCellDayEvent) {
+                            /*
+                             * Context menu on event
+                             */
+                            DateCellDayEvent dayEvent = (DateCellDayEvent) widget;
+                            CalendarEvent event = dayEvent.getCalendarEvent();
+                            Action[] actions = CalendarConnector.this
+                                    .getActionsBetween(event.getStartTime(),
+                                            event.getEndTime());
+                            for (Action action : actions) {
+                                ((VCalendarAction) action).setEvent(event);
+                            }
+                            return actions;
+
+                        }
+                        return null;
+                    }
+                }, left, top);
+            }
+        });
+    }
+
+    @Override
+    public void onStateChanged(StateChangeEvent stateChangeEvent) {
+        super.onStateChanged(stateChangeEvent);
+
+        CalendarState state = getState();
+        VCalendar widget = getWidget();
+        boolean monthView = state.days.size() > 7;
+
+        // Enable or disable the forward and backward navigation buttons
+        widget.setForwardNavigationEnabled(hasEventListener(CalendarEventId.FORWARD));
+        widget.setBackwardNavigationEnabled(hasEventListener(CalendarEventId.BACKWARD));
+
+        widget.set24HFormat(state.format24H);
+        widget.setDayNames(state.dayNames);
+        widget.setMonthNames(state.monthNames);
+        widget.setFirstDayNumber(state.firstVisibleDayOfWeek);
+        widget.setLastDayNumber(state.lastVisibleDayOfWeek);
+        widget.setFirstHourOfTheDay(state.firstHourOfDay);
+        widget.setLastHourOfTheDay(state.lastHourOfDay);
+        widget.setReadOnly(state.readOnly);
+        widget.setDisabled(!state.enabled);
+
+        widget.setRangeSelectAllowed(hasEventListener(CalendarEventId.RANGESELECT));
+        widget.setRangeMoveAllowed(hasEventListener(CalendarEventId.EVENTMOVE));
+        widget.setEventMoveAllowed(hasEventListener(CalendarEventId.EVENTMOVE));
+        widget.setEventResizeAllowed(hasEventListener(CalendarEventId.EVENTRESIZE));
+
+        List<CalendarState.Day> days = state.days;
+        List<CalendarState.Event> events = state.events;
+
+        if (monthView) {
+            updateMonthView(days, events);
+        } else {
+            updateWeekView(days, events);
+        }
+
+        updateSizes();
+
+        registerEventToolTips(state.events);
+        updateActionMap(state.actions);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.terminal.gwt.client.Paintable#updateFromUIDL(com.vaadin.terminal
+     * .gwt.client.UIDL, com.vaadin.terminal.gwt.client.ApplicationConnection)
+     */
+    public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+
+        // check for DD -related access criteria
+        // Iterator<Object> childIterator = uidl.getChildIterator();
+        // while (childIterator.hasNext()) {
+        // UIDL child = (UIDL) childIterator.next();
+        //
+        // // Drag&drop
+        // if (ACCESSCRITERIA.equals(child.getTag())) {
+        // if (monthView
+        // && !(getDropHandler() instanceof CalendarMonthDropHandler)) {
+        // setDropHandler(new CalendarMonthDropHandler());
+        //
+        // } else if (!monthView
+        // && !(getDropHandler() instanceof CalendarWeekDropHandler)) {
+        // setDropHandler(new CalendarWeekDropHandler());
+        // }
+        //
+        // getDropHandler().setCalendarPaintable(this);
+        // getDropHandler().updateAcceptRules(child);
+        //
+        // } else {
+        // setDropHandler(null);
+        // }
+        //
+        // }
+    }
+
+    /**
+     * Returns the ApplicationConnection used to connect to the server side
+     */
+    @Override
+    public ApplicationConnection getClient() {
+        return getConnection();
+    }
+
+    /**
+     * Register the description of the events as tooltips. This way, any event
+     * displaying widget can use the event index as a key to display the
+     * tooltip.
+     */
+    private void registerEventToolTips(List<CalendarState.Event> events) {
+        for (CalendarState.Event e : events) {
+            if (e.description != null && !"".equals(e.description)) {
+                tooltips.put(e.index, e.description);
+            } else {
+                tooltips.remove(e.index);
+            }
+        }
+    }
+
+    @Override
+    public TooltipInfo getTooltipInfo(com.google.gwt.dom.client.Element element) {
+        TooltipInfo tooltipInfo = null;
+        Widget w = Util.findWidget((Element) element, null);
+        if (w instanceof HasTooltipKey) {
+            tooltipInfo = GWT.create(TooltipInfo.class);
+            String title = tooltips.get(((HasTooltipKey) w).getTooltipKey());
+            tooltipInfo.setTitle(title != null ? title : "");
+        }
+        if (tooltipInfo == null) {
+            tooltipInfo = super.getTooltipInfo(element);
+        }
+        return tooltipInfo;
+    }
+
+    private void updateMonthView(List<CalendarState.Day> days,
+            List<CalendarState.Event> events) {
+        CalendarState state = getState();
+        getWidget().updateMonthView(state.firstDayOfWeek,
+                getWidget().getDateTimeFormat().parse(state.now), days.size(),
+                calendarEventListOf(events, state.format24H),
+                calendarDayListOf(days));
+    }
+
+    private void updateWeekView(List<CalendarState.Day> days,
+            List<CalendarState.Event> events) {
+        CalendarState state = getState();
+        getWidget().updateWeekView(state.scroll,
+                getWidget().getDateTimeFormat().parse(state.now), days.size(),
+                state.firstDayOfWeek,
+                calendarEventListOf(events, state.format24H),
+                calendarDayListOf(days));
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.terminal.gwt.client.ui.dd.VHasDropHandler#getDropHandler()
+     */
+    public CalendarDropHandler getDropHandler() {
+        return dropHandler;
+    }
+
+    /**
+     * Set the drop handler
+     * 
+     * @param dropHandler
+     *            The drophandler to use
+     */
+    public void setDropHandler(CalendarDropHandler dropHandler) {
+        this.dropHandler = dropHandler;
+    }
+
+    private Action[] getActionsBetween(Date start, Date end) {
+        List<Action> actions = new ArrayList<Action>();
+        for (int i = 0; i < actionKeys.size(); i++) {
+            final String actionKey = actionKeys.get(i);
+            Date actionStartDate;
+            Date actionEndDate;
+            try {
+                actionStartDate = getActionStartDate(actionKey);
+                actionEndDate = getActionEndDate(actionKey);
+            } catch (ParseException pe) {
+                VConsole.error("Failed to parse action date");
+                continue;
+            }
+
+            boolean startIsValid = start.compareTo(actionStartDate) >= 0;
+            boolean endIsValid = end.compareTo(actionEndDate) <= 0;
+            if (startIsValid && endIsValid) {
+                VCalendarAction a = new VCalendarAction(this, rpc, actionKey);
+                a.setCaption(getActionCaption(actionKey));
+                a.setIconUrl(getActionIcon(actionKey));
+                a.setActionStartDate(start);
+                a.setActionEndDate(end);
+                actions.add(a);
+            }
+        }
+
+        return actions.toArray(new Action[actions.size()]);
+    }
+
+    private List<String> actionKeys = new ArrayList<String>();
+
+    private void updateActionMap(List<CalendarState.Action> actions) {
+        actionMap.clear();
+        actionKeys.clear();
+
+        if (actions == null) {
+            return;
+        }
+
+        for (CalendarState.Action action : actions) {
+            String id = action.actionKey + "-" + action.startDate + "-"
+                    + action.endDate;
+            actionMap.put(id + "_c", action.caption);
+            actionMap.put(id + "_s", action.startDate);
+            actionMap.put(id + "_e", action.endDate);
+            actionKeys.add(id);
+            if (action.iconKey != null) {
+                actionMap.put(id + "_i", getResourceUrl(action.iconKey));
+
+            } else {
+                actionMap.remove(id + "_i");
+            }
+        }
+    }
+
+    /**
+     * Get the text that is displayed for a context menu item
+     * 
+     * @param actionKey
+     *            The unique action key
+     * @return
+     */
+    public String getActionCaption(String actionKey) {
+        return actionMap.get(actionKey + "_c");
+    }
+
+    /**
+     * Get the icon url for a context menu item
+     * 
+     * @param actionKey
+     *            The unique action key
+     * @return
+     */
+    public String getActionIcon(String actionKey) {
+        return actionMap.get(actionKey + "_i");
+    }
+
+    /**
+     * Get the start date for an action item
+     * 
+     * @param actionKey
+     *            The unique action key
+     * @return
+     * @throws ParseException
+     */
+    public Date getActionStartDate(String actionKey) throws ParseException {
+        String dateStr = actionMap.get(actionKey + "_s");
+        DateTimeFormat formatter = DateTimeFormat
+                .getFormat(DateConstants.ACTION_DATE_FORMAT_PATTERN);
+        return formatter.parse(dateStr);
+    }
+
+    /**
+     * Get the end date for an action item
+     * 
+     * @param actionKey
+     *            The unique action key
+     * @return
+     * @throws ParseException
+     */
+    public Date getActionEndDate(String actionKey) throws ParseException {
+        String dateStr = actionMap.get(actionKey + "_e");
+        DateTimeFormat formatter = DateTimeFormat
+                .getFormat(DateConstants.ACTION_DATE_FORMAT_PATTERN);
+        return formatter.parse(dateStr);
+    }
+
+    /**
+     * Returns ALL currently registered events. Use {@link #getActions(Date)} to
+     * get the actions for a specific date
+     */
+    public Action[] getActions() {
+        List<Action> actions = new ArrayList<Action>();
+        for (int i = 0; i < actionKeys.size(); i++) {
+            final String actionKey = actionKeys.get(i);
+            final VCalendarAction a = new VCalendarAction(this, rpc, actionKey);
+            a.setCaption(getActionCaption(actionKey));
+            a.setIconUrl(getActionIcon(actionKey));
+
+            try {
+                a.setActionStartDate(getActionStartDate(actionKey));
+                a.setActionEndDate(getActionEndDate(actionKey));
+            } catch (ParseException pe) {
+                VConsole.error(pe);
+            }
+
+            actions.add(a);
+        }
+        return actions.toArray(new Action[actions.size()]);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.terminal.gwt.client.ui.ActionOwner#getPaintableId()
+     */
+    public String getPaintableId() {
+        return getConnectorId();
+    }
+
+    private List<CalendarEvent> calendarEventListOf(
+            List<CalendarState.Event> events, boolean format24h) {
+        List<CalendarEvent> list = new ArrayList<CalendarEvent>(events.size());
+        for (CalendarState.Event event : events) {
+            final String dateFrom = event.dateFrom;
+            final String dateTo = event.dateTo;
+            final String timeFrom = event.timeFrom;
+            final String timeTo = event.timeTo;
+            CalendarEvent calendarEvent = new CalendarEvent();
+            calendarEvent.setAllDay(event.allDay);
+            calendarEvent.setCaption(event.caption);
+            calendarEvent.setDescription(event.description);
+            calendarEvent.setStart(getWidget().getDateFormat().parse(dateFrom));
+            calendarEvent.setEnd(getWidget().getDateFormat().parse(dateTo));
+            calendarEvent.setFormat24h(format24h);
+            calendarEvent.setStartTime(getWidget().getDateTimeFormat().parse(
+                    dateFrom + " " + timeFrom));
+            calendarEvent.setEndTime(getWidget().getDateTimeFormat().parse(
+                    dateTo + " " + timeTo));
+            calendarEvent.setStyleName(event.styleName);
+            calendarEvent.setIndex(event.index);
+            list.add(calendarEvent);
+        }
+        return list;
+    }
+
+    private List<CalendarDay> calendarDayListOf(List<CalendarState.Day> days) {
+        List<CalendarDay> list = new ArrayList<CalendarDay>(days.size());
+        for (CalendarState.Day day : days) {
+            CalendarDay d = new CalendarDay(day.date, day.localizedDateFormat,
+                    day.dayOfWeek, day.week);
+
+            list.add(d);
+        }
+        return list;
+    }
+
+    @Override
+    public void layout() {
+        updateSizes();
+    }
+
+    private void updateSizes() {
+        int height = getLayoutManager()
+                .getOuterHeight(getWidget().getElement());
+        int width = getLayoutManager().getOuterWidth(getWidget().getElement());
+
+        if (isUndefinedWidth()) {
+            width = -1;
+        }
+        if (isUndefinedHeight()) {
+            height = -1;
+        }
+
+        getWidget().setSizeForChildren(width, height);
+
+    }
+}
diff --git a/client/src/com/vaadin/client/ui/calendar/VCalendarAction.java b/client/src/com/vaadin/client/ui/calendar/VCalendarAction.java
new file mode 100644 (file)
index 0000000..2a52935
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar;
+
+import java.util.Date;
+
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.vaadin.client.ui.Action;
+import com.vaadin.client.ui.calendar.schedule.CalendarEvent;
+import com.vaadin.shared.ui.calendar.CalendarServerRpc;
+import com.vaadin.shared.ui.calendar.DateConstants;
+
+/**
+ * Action performed by the calendar
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ */
+public class VCalendarAction extends Action {
+
+    private CalendarServerRpc rpc;
+
+    private String actionKey = "";
+
+    private Date actionStartDate;
+
+    private Date actionEndDate;
+
+    private CalendarEvent event;
+
+    private final DateTimeFormat dateformat_datetime = DateTimeFormat
+            .getFormat(DateConstants.ACTION_DATE_FORMAT_PATTERN);
+
+    /**
+     * 
+     * @param owner
+     */
+    public VCalendarAction(CalendarConnector owner) {
+        super(owner);
+    }
+
+    /**
+     * Constructor
+     * 
+     * @param owner
+     *            The owner who trigger this kinds of events
+     * @param rpc
+     *            The CalendarRpc which is used for executing actions
+     * @param key
+     *            The unique action key which identifies this particular action
+     */
+    public VCalendarAction(CalendarConnector owner, CalendarServerRpc rpc,
+            String key) {
+        this(owner);
+        this.rpc = rpc;
+        actionKey = key;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.terminal.gwt.client.ui.Action#execute()
+     */
+    @Override
+    public void execute() {
+        String startDate = dateformat_datetime.format(actionStartDate);
+        String endDate = dateformat_datetime.format(actionEndDate);
+
+        if (event == null) {
+            rpc.actionOnEmptyCell(actionKey.split("-")[0], startDate, endDate);
+        } else {
+            rpc.actionOnEvent(actionKey.split("-")[0], startDate, endDate,
+                    event.getIndex());
+        }
+
+        owner.getClient().getContextMenu().hide();
+    }
+
+    /**
+     * Get the date and time when the action starts
+     * 
+     * @return
+     */
+    public Date getActionStartDate() {
+        return actionStartDate;
+    }
+
+    /**
+     * Set the date when the actions start
+     * 
+     * @param actionStartDate
+     *            The date and time when the action starts
+     */
+    public void setActionStartDate(Date actionStartDate) {
+        this.actionStartDate = actionStartDate;
+    }
+
+    /**
+     * Get the date and time when the action ends
+     * 
+     * @return
+     */
+    public Date getActionEndDate() {
+        return actionEndDate;
+    }
+
+    /**
+     * Set the date and time when the action ends
+     * 
+     * @param actionEndDate
+     *            The date and time when the action ends
+     */
+    public void setActionEndDate(Date actionEndDate) {
+        this.actionEndDate = actionEndDate;
+    }
+
+    public CalendarEvent getEvent() {
+        return event;
+    }
+
+    public void setEvent(CalendarEvent event) {
+        this.event = event;
+    }
+
+}
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/CalendarDay.java b/client/src/com/vaadin/client/ui/calendar/schedule/CalendarDay.java
new file mode 100644 (file)
index 0000000..ca176c0
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule;
+
+/**
+ * Utility class used to represent a day when updating views. Only used
+ * internally.
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ */
+public class CalendarDay {
+    private String date;
+    private String localizedDateFormat;
+    private int dayOfWeek;
+    private int week;
+
+    public CalendarDay(String date, String localizedDateFormat, int dayOfWeek,
+            int week) {
+        super();
+        this.date = date;
+        this.localizedDateFormat = localizedDateFormat;
+        this.dayOfWeek = dayOfWeek;
+        this.week = week;
+    }
+
+    public String getDate() {
+        return date;
+    }
+
+    public String getLocalizedDateFormat() {
+        return localizedDateFormat;
+    }
+
+    public int getDayOfWeek() {
+        return dayOfWeek;
+    }
+
+    public int getWeek() {
+        return week;
+    }
+}
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/CalendarEvent.java b/client/src/com/vaadin/client/ui/calendar/schedule/CalendarEvent.java
new file mode 100644 (file)
index 0000000..e2c06d4
--- /dev/null
@@ -0,0 +1,313 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule;
+
+import java.util.Date;
+
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.vaadin.shared.ui.calendar.DateConstants;
+
+/**
+ * A client side implementation of a calendar event
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ */
+public class CalendarEvent {
+    private int index;
+    private String caption;
+    private Date start, end;
+    private String styleName;
+    private Date startTime, endTime;
+    private String description;
+    private int slotIndex = -1;
+    private boolean format24h;
+
+    DateTimeFormat dateformat_date = DateTimeFormat.getFormat("h:mm a");
+    DateTimeFormat dateformat_date24 = DateTimeFormat.getFormat("H:mm");
+    private boolean allDay;
+
+    /**
+     * @see com.vaadin.addon.calendar.event.CalendarEvent#getStyleName()
+     */
+    public String getStyleName() {
+        return styleName;
+    }
+
+    /**
+     * @see com.vaadin.addon.calendar.event.CalendarEvent#getStart()
+     */
+    public Date getStart() {
+        return start;
+    }
+
+    /**
+     * @see com.vaadin.addon.calendar.event.CalendarEvent#getStyleName()
+     * @param style
+     */
+    public void setStyleName(String style) {
+        styleName = style;
+    }
+
+    /**
+     * @see com.vaadin.addon.calendar.event.CalendarEvent#getStart()
+     * @param start
+     */
+    public void setStart(Date start) {
+        this.start = start;
+    }
+
+    /**
+     * @see com.vaadin.addon.calendar.event.CalendarEvent#getEnd()
+     * @return
+     */
+    public Date getEnd() {
+        return end;
+    }
+
+    /**
+     * @see com.vaadin.addon.calendar.event.CalendarEvent#getEnd()
+     * @param end
+     */
+    public void setEnd(Date end) {
+        this.end = end;
+    }
+
+    /**
+     * Returns the start time of the event
+     * 
+     * @return Time embedded in the {@link Date} object
+     */
+    public Date getStartTime() {
+        return startTime;
+    }
+
+    /**
+     * Set the start time of the event
+     * 
+     * @param startTime
+     *            The time of the event. Use the time fields in the {@link Date}
+     *            object
+     */
+    public void setStartTime(Date startTime) {
+        this.startTime = startTime;
+    }
+
+    /**
+     * Get the end time of the event
+     * 
+     * @return Time embedded in the {@link Date} object
+     */
+    public Date getEndTime() {
+        return endTime;
+    }
+
+    /**
+     * Set the end time of the event
+     * 
+     * @param endTime
+     *            Time embedded in the {@link Date} object
+     */
+    public void setEndTime(Date endTime) {
+        this.endTime = endTime;
+    }
+
+    /**
+     * Get the (server side) index of the event
+     * 
+     * @return
+     */
+    public int getIndex() {
+        return index;
+    }
+
+    /**
+     * Get the index of the slot where the event in rendered
+     * 
+     * @return
+     */
+    public int getSlotIndex() {
+        return slotIndex;
+    }
+
+    /**
+     * Set the index of the slot where the event in rendered
+     * 
+     * @param index
+     *            The index of the slot
+     */
+    public void setSlotIndex(int index) {
+        slotIndex = index;
+    }
+
+    /**
+     * Set the (server side) index of the event
+     * 
+     * @param index
+     *            The index
+     */
+    public void setIndex(int index) {
+        this.index = index;
+    }
+
+    /**
+     * Get the caption of the event. The caption is the text displayed in the
+     * calendar on the event.
+     * 
+     * @return
+     */
+    public String getCaption() {
+        return caption;
+    }
+
+    /**
+     * Set the caption of the event. The caption is the text displayed in the
+     * calendar on the event.
+     * 
+     * @param caption
+     *            The visible caption of the event
+     */
+    public void setCaption(String caption) {
+        this.caption = caption;
+    }
+
+    /**
+     * Get the description of the event. The description is the text displayed
+     * when hoovering over the event with the mouse
+     * 
+     * @return
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * Set the description of the event. The description is the text displayed
+     * when hoovering over the event with the mouse
+     * 
+     * @param description
+     */
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    /**
+     * Does the event use the 24h time format
+     * 
+     * @param format24h
+     *            True if it uses the 24h format, false if it uses the 12h time
+     *            format
+     */
+    public void setFormat24h(boolean format24h) {
+        this.format24h = format24h;
+    }
+
+    /**
+     * Is the event an all day event.
+     * 
+     * @param allDay
+     *            True if the event should be rendered all day
+     */
+    public void setAllDay(boolean allDay) {
+        this.allDay = allDay;
+    }
+
+    /**
+     * Is the event an all day event.
+     * 
+     * @return
+     */
+    public boolean isAllDay() {
+        return allDay;
+    }
+
+    /**
+     * Get the time as a formatted string
+     * 
+     * @return
+     */
+    public String getTimeAsText() {
+        if (format24h) {
+            return dateformat_date24.format(startTime);
+        } else {
+            return dateformat_date.format(startTime);
+        }
+    }
+
+    /**
+     * Get the amount of milliseconds between the start and end of the event
+     * 
+     * @return
+     */
+    public long getRangeInMilliseconds() {
+        return getEndTime().getTime() - getStartTime().getTime();
+    }
+
+    /**
+     * Get the amount of minutes between the start and end of the event
+     * 
+     * @return
+     */
+    public long getRangeInMinutes() {
+        return (getRangeInMilliseconds() / DateConstants.MINUTEINMILLIS);
+    }
+
+    /**
+     * Get the amount of minutes for the event on a specific day. This is useful
+     * if the event spans several days.
+     * 
+     * @param targetDay
+     *            The date to check
+     * @return
+     */
+    public long getRangeInMinutesForDay(Date targetDay) {
+        if (isTimeOnDifferentDays()) {
+            // Time range is on different days. Calculate the second day's
+            // range.
+            long range = (getEndTime().getTime() - getEnd().getTime())
+                    / DateConstants.MINUTEINMILLIS;
+
+            if (getEnd().compareTo(targetDay) != 0) {
+                // Calculate first day's range.
+                return getRangeInMinutes() - range;
+            }
+
+            return range;
+        } else {
+            return getRangeInMinutes();
+        }
+    }
+
+    /**
+     * Does the event span several days
+     * 
+     * @return
+     */
+    @SuppressWarnings("deprecation")
+    public boolean isTimeOnDifferentDays() {
+        if (getEndTime().getTime() - getStart().getTime() > DateConstants.DAYINMILLIS) {
+            return true;
+        }
+
+        if (getStart().compareTo(getEnd()) != 0) {
+            if (getEndTime().getHours() == 0 && getEndTime().getMinutes() == 0) {
+                return false;
+            }
+            return true;
+        }
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/DateCell.java b/client/src/com/vaadin/client/ui/calendar/schedule/DateCell.java
new file mode 100644 (file)
index 0000000..05e2a80
--- /dev/null
@@ -0,0 +1,808 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.dom.client.Node;
+import com.google.gwt.dom.client.NodeList;
+import com.google.gwt.dom.client.Style.Display;
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.event.dom.client.ContextMenuEvent;
+import com.google.gwt.event.dom.client.ContextMenuHandler;
+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.MouseDownEvent;
+import com.google.gwt.event.dom.client.MouseDownHandler;
+import com.google.gwt.event.dom.client.MouseMoveEvent;
+import com.google.gwt.event.dom.client.MouseMoveHandler;
+import com.google.gwt.event.dom.client.MouseUpEvent;
+import com.google.gwt.event.dom.client.MouseUpHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.client.Util;
+
+public class DateCell extends FocusableComplexPanel implements
+        MouseDownHandler, MouseMoveHandler, MouseUpHandler, KeyDownHandler,
+        ContextMenuHandler {
+    private static final String DRAGEMPHASISSTYLE = " dragemphasis";
+    private Date date;
+    private int width;
+    private int eventRangeStart = -1;
+    private int eventRangeStop = -1;
+    final WeekGrid weekgrid;
+    private boolean disabled = false;
+    private int height;
+    private final Element[] slotElements;
+    private final List<DateCellSlot> slots = new ArrayList<DateCell.DateCellSlot>();
+    private int[] slotElementHeights;
+    private int startingSlotHeight;
+    private Date today;
+    private Element todaybar;
+    private final List<HandlerRegistration> handlers;
+    private final int numberOfSlots;
+    private final int firstHour;
+    private final int lastHour;
+
+    public class DateCellSlot extends Widget {
+
+        private final DateCell cell;
+
+        private final Date from;
+
+        private final Date to;
+
+        public DateCellSlot(DateCell cell, Date from, Date to) {
+            setElement(DOM.createDiv());
+            getElement().setInnerHTML("&nbsp;");
+            this.cell = cell;
+            this.from = from;
+            this.to = to;
+        }
+
+        public Date getFrom() {
+            return from;
+        }
+
+        public Date getTo() {
+            return to;
+        }
+
+        public DateCell getParentCell() {
+            return cell;
+        }
+    }
+
+    public DateCell(WeekGrid parent, Date date) {
+        weekgrid = parent;
+        Element mainElement = DOM.createDiv();
+        setElement(mainElement);
+        makeFocusable();
+        setDate(date);
+
+        addStyleName("v-calendar-day-times");
+
+        handlers = new LinkedList<HandlerRegistration>();
+
+        // 2 slots / hour
+        firstHour = weekgrid.getFirstHour();
+        lastHour = weekgrid.getLastHour();
+        numberOfSlots = (lastHour - firstHour + 1) * 2;
+        long slotTime = Math.round(((lastHour - firstHour + 1) * 3600000.0)
+                / numberOfSlots);
+
+        slotElements = new Element[numberOfSlots];
+        slotElementHeights = new int[numberOfSlots];
+
+        slots.clear();
+        long start = getDate().getTime() + firstHour * 3600000;
+        long end = start + slotTime;
+        for (int i = 0; i < numberOfSlots; i++) {
+            DateCellSlot slot = new DateCellSlot(this, new Date(
+                    start), new Date(end));
+            if (i % 2 == 0) {
+                slot.setStyleName("v-datecellslot-even");
+            } else {
+                slot.setStyleName("v-datecellslot");
+            }
+            Event.sinkEvents(slot.getElement(), Event.MOUSEEVENTS);
+            mainElement.appendChild(slot.getElement());
+            slotElements[i] = slot.getElement();
+            slots.add(slot);
+            start = end;
+            end = start + slotTime;
+        }
+
+        // Sink events for tooltip handling
+        Event.sinkEvents(mainElement, Event.MOUSEEVENTS);
+    }
+
+    public int getFirstHour() {
+        return firstHour;
+    }
+
+    public int getLastHour() {
+        return lastHour;
+    }
+
+    @Override
+    protected void onAttach() {
+        super.onAttach();
+
+        handlers.add(addHandler(this, MouseDownEvent.getType()));
+        handlers.add(addHandler(this, MouseUpEvent.getType()));
+        handlers.add(addHandler(this, MouseMoveEvent.getType()));
+        handlers.add(addDomHandler(this, ContextMenuEvent.getType()));
+        handlers.add(addKeyDownHandler(this));
+    }
+
+    @Override
+    protected void onDetach() {
+        for (HandlerRegistration handler : handlers) {
+            handler.removeHandler();
+        }
+        handlers.clear();
+
+        super.onDetach();
+    }
+
+    public int getSlotIndex(Element slotElement) {
+        for (int i = 0; i < slotElements.length; i++) {
+            if (slotElement == slotElements[i]) {
+                return i;
+            }
+        }
+
+        throw new IllegalArgumentException(
+                "Element not found in this DateCell");
+    }
+
+    public DateCellSlot getSlot(int index) {
+        return slots.get(index);
+    }
+
+    public int getNumberOfSlots() {
+        return numberOfSlots;
+    }
+
+    public void setTimeBarWidth(int timebarWidth) {
+        todaybar.getStyle().setWidth(timebarWidth, Unit.PX);
+    }
+
+    /**
+     * @param isHorizontalSized
+     *            if true, this DateCell is sized with CSS and not via
+     *            {@link #setWidthPX(int)}
+     */
+    public void setHorizontalSized(boolean isHorizontalSized) {
+        if (isHorizontalSized) {
+            addStyleDependentName("Hsized");
+
+            width = getOffsetWidth()
+                    - Util.measureHorizontalBorder(getElement());
+            recalculateEventWidths();
+        } else {
+            removeStyleDependentName("Hsized");
+        }
+    }
+
+    /**
+     * @param isVerticalSized
+     *            if true, this DateCell is sized with CSS and not via
+     *            {@link #setHeightPX(int)}
+     */
+    public void setVerticalSized(boolean isVerticalSized) {
+        if (isVerticalSized) {
+            addStyleDependentName("Vsized");
+
+            // recalc heights&size for events. all other height sizes come
+            // from css
+            startingSlotHeight = slotElements[0].getOffsetHeight();
+            recalculateEventPositions();
+
+            if (isToday()) {
+                recalculateTimeBarPosition();
+            }
+
+        } else {
+            removeStyleDependentName("Vsized");
+        }
+    }
+
+    public void setDate(Date date) {
+        this.date = date;
+    }
+
+    public void setWidthPX(int cellWidth) {
+        width = cellWidth;
+        setWidth(cellWidth + "px");
+        recalculateEventWidths();
+    }
+
+    public void setHeightPX(int height, int[] cellHeights) {
+        this.height = height;
+        slotElementHeights = cellHeights;
+        setHeight(height + "px");
+        recalculateCellHeights();
+        recalculateEventPositions();
+        if (today != null) {
+            recalculateTimeBarPosition();
+        }
+    }
+
+    // date methods are not deprecated in GWT
+    @SuppressWarnings("deprecation")
+    private void recalculateTimeBarPosition() {
+        int h = today.getHours();
+        int m = today.getMinutes();
+        if (h >= firstHour && h <= lastHour) {
+            int pixelTop = weekgrid.getPixelTopFor(m + 60 * h);
+            todaybar.getStyle().clearDisplay();
+            todaybar.getStyle().setTop(pixelTop, Unit.PX);
+        } else {
+            todaybar.getStyle().setDisplay(Display.NONE);
+        }
+    }
+
+    private void recalculateEventPositions() {
+        for (int i = 0; i < getWidgetCount(); i++) {
+            DateCellDayEvent dayEvent = (DateCellDayEvent) getWidget(i);
+            updatePositionFor(dayEvent, getDate(),
+                    dayEvent.getCalendarEvent());
+        }
+    }
+
+    public void recalculateEventWidths() {
+        List<DateCellGroup> groups = new ArrayList<DateCellGroup>();
+
+        int count = getWidgetCount();
+
+        List<Integer> handled = new ArrayList<Integer>();
+
+        // Iterate through all events and group them. Events that overlaps
+        // with each other, are added to the same group.
+        for (int i = 0; i < count; i++) {
+            if (handled.contains(i)) {
+                continue;
+            }
+
+            DateCellGroup curGroup = getOverlappingEvents(i);
+            handled.addAll(curGroup.getItems());
+
+            boolean newGroup = true;
+            // No need to check other groups, if size equals the count
+            if (curGroup.getItems().size() != count) {
+                // Check other groups. When the whole group overlaps with
+                // other group, the group is merged to the other.
+                for (DateCellGroup g : groups) {
+
+                    if (WeekGridMinuteTimeRange.doesOverlap(
+                            curGroup.getDateRange(), g.getDateRange())) {
+                        newGroup = false;
+                        updateGroup(g, curGroup);
+                    }
+                }
+            } else {
+                if (newGroup) {
+                    groups.add(curGroup);
+                }
+                break;
+            }
+
+            if (newGroup) {
+                groups.add(curGroup);
+            }
+        }
+
+        drawDayEvents(groups);
+    }
+
+    private void recalculateCellHeights() {
+        startingSlotHeight = height / numberOfSlots;
+
+        for (int i = 0; i < slotElements.length; i++) {
+            slotElements[i].getStyle().setHeight(slotElementHeights[i],
+                    Unit.PX);
+        }
+
+        Iterator<Widget> it = iterator();
+        while (it.hasNext()) {
+            Widget child = it.next();
+            if (child instanceof DateCellDayEvent) {
+                ((DateCellDayEvent) child).setSlotHeightInPX(getSlotHeight());
+            }
+
+        }
+    }
+
+    public int getSlotHeight() {
+        return startingSlotHeight;
+    }
+
+    public int getSlotBorder() {
+        return Util
+                .measureVerticalBorder((com.google.gwt.user.client.Element) slotElements[0]);
+    }
+
+    private void drawDayEvents(List<DateCellGroup> groups) {
+        for (DateCellGroup g : groups) {
+            int col = 0;
+            int colCount = 0;
+            List<Integer> order = new ArrayList<Integer>();
+            Map<Integer, Integer> columns = new HashMap<Integer, Integer>();
+            for (Integer eventIndex : g.getItems()) {
+                DateCellDayEvent d = (DateCellDayEvent) getWidget(eventIndex);
+                d.setMoveWidth(width);
+
+                int freeSpaceCol = findFreeColumnSpaceOnLeft(
+                        new WeekGridMinuteTimeRange(d.getCalendarEvent()
+                                .getStartTime(), d.getCalendarEvent()
+                                .getEndTime()), order, columns);
+                if (freeSpaceCol >= 0) {
+                    col = freeSpaceCol;
+                    columns.put(eventIndex, col);
+                    int newOrderindex = 0;
+                    for (Integer i : order) {
+                        if (columns.get(i) >= col) {
+                            newOrderindex = order.indexOf(i);
+                            break;
+                        }
+                    }
+                    order.add(newOrderindex, eventIndex);
+                } else {
+                    // New column
+                    col = colCount++;
+                    columns.put(eventIndex, col);
+                    order.add(eventIndex);
+                }
+            }
+
+            // Update widths and left position
+            int eventWidth = (width / colCount);
+            for (Integer index : g.getItems()) {
+                DateCellDayEvent d = (DateCellDayEvent) getWidget(index);
+                d.getElement()
+                        .getStyle()
+                        .setMarginLeft((eventWidth * columns.get(index)),
+                                Unit.PX);
+                d.setWidth(eventWidth + "px");
+                d.setSlotHeightInPX(getSlotHeight());
+            }
+        }
+    }
+
+    private int findFreeColumnSpaceOnLeft(WeekGridMinuteTimeRange dateRange,
+            List<Integer> order, Map<Integer, Integer> columns) {
+        int freeSpot = -1;
+        int skipIndex = -1;
+        for (Integer eventIndex : order) {
+            int col = columns.get(eventIndex);
+            if (col == skipIndex) {
+                continue;
+            }
+
+            if (freeSpot != -1 && freeSpot != col) {
+                // Free spot found
+                return freeSpot;
+            }
+
+            DateCellDayEvent d = (DateCellDayEvent) getWidget(eventIndex);
+            WeekGridMinuteTimeRange nextRange = new WeekGridMinuteTimeRange(d
+                    .getCalendarEvent().getStartTime(), d
+                    .getCalendarEvent().getEndTime());
+
+            if (WeekGridMinuteTimeRange.doesOverlap(dateRange, nextRange)) {
+                skipIndex = col;
+                freeSpot = -1;
+            } else {
+                freeSpot = col;
+            }
+        }
+
+        return freeSpot;
+    }
+
+    /* Update top and bottom date range values. Add new index to the group. */
+    private void updateGroup(DateCellGroup targetGroup, DateCellGroup byGroup) {
+        Date newStart = targetGroup.getStart();
+        Date newEnd = targetGroup.getEnd();
+        if (byGroup.getStart().before(targetGroup.getStart())) {
+            newStart = byGroup.getEnd();
+        }
+        if (byGroup.getStart().after(targetGroup.getEnd())) {
+            newStart = byGroup.getStart();
+        }
+
+        targetGroup.setDateRange(new WeekGridMinuteTimeRange(newStart, newEnd));
+
+        for (Integer index : byGroup.getItems()) {
+            if (!targetGroup.getItems().contains(index)) {
+                targetGroup.add(index);
+            }
+        }
+    }
+
+    /**
+     * Returns all overlapping DayEvent indexes in the Group. Including the
+     * target.
+     * 
+     * @param targetIndex
+     *            Index of DayEvent in the current DateCell widget.
+     * @return Group that contains all Overlapping DayEvent indexes
+     */
+    public DateCellGroup getOverlappingEvents(int targetIndex) {
+        DateCellGroup g = new DateCellGroup(targetIndex);
+
+        int count = getWidgetCount();
+        DateCellDayEvent target = (DateCellDayEvent) getWidget(targetIndex);
+        WeekGridMinuteTimeRange targetRange = new WeekGridMinuteTimeRange(target
+                .getCalendarEvent().getStartTime(), target
+                .getCalendarEvent().getEndTime());
+        Date groupStart = targetRange.getStart();
+        Date groupEnd = targetRange.getEnd();
+
+        for (int i = 0; i < count; i++) {
+            if (targetIndex == i) {
+                continue;
+            }
+
+            DateCellDayEvent d = (DateCellDayEvent) getWidget(i);
+            WeekGridMinuteTimeRange nextRange = new WeekGridMinuteTimeRange(d
+                    .getCalendarEvent().getStartTime(), d
+                    .getCalendarEvent().getEndTime());
+            if (WeekGridMinuteTimeRange.doesOverlap(targetRange, nextRange)) {
+                g.add(i);
+
+                // Update top & bottom values to the greatest
+                if (nextRange.getStart().before(targetRange.getStart())) {
+                    groupStart = targetRange.getStart();
+                }
+                if (nextRange.getEnd().after(targetRange.getEnd())) {
+                    groupEnd = targetRange.getEnd();
+                }
+            }
+        }
+
+        g.setDateRange(new WeekGridMinuteTimeRange(groupStart, groupEnd));
+        return g;
+    }
+
+    public Date getDate() {
+        return date;
+    }
+
+    public void addEvent(Date targetDay, CalendarEvent calendarEvent) {
+        Element main = getElement();
+        DateCellDayEvent dayEvent = new DateCellDayEvent(this, weekgrid, calendarEvent);
+        dayEvent.setSlotHeightInPX(getSlotHeight());
+        dayEvent.setDisabled(isDisabled());
+
+        if (startingSlotHeight > 0) {
+            updatePositionFor(dayEvent, targetDay, calendarEvent);
+        }
+
+        add(dayEvent, (com.google.gwt.user.client.Element) main);
+    }
+
+    // date methods are not deprecated in GWT
+    @SuppressWarnings("deprecation")
+    private void updatePositionFor(DateCellDayEvent dayEvent, Date targetDay,
+            CalendarEvent calendarEvent) {
+        if (canDisplay(calendarEvent)) {
+
+            dayEvent.getElement().getStyle().clearDisplay();
+
+            Date fromDt = calendarEvent.getStartTime();
+            int h = fromDt.getHours();
+            int m = fromDt.getMinutes();
+            long range = calendarEvent.getRangeInMinutesForDay(targetDay);
+
+            boolean onDifferentDays = calendarEvent.isTimeOnDifferentDays();
+            if (onDifferentDays) {
+                if (calendarEvent.getStart().compareTo(targetDay) != 0) {
+                    // Current day slot is for the end date. Lets fix also
+                    // the
+                    // start & end times.
+                    h = 0;
+                    m = 0;
+                }
+            }
+
+            int startFromMinutes = (h * 60) + m;
+            dayEvent.updatePosition(startFromMinutes, range);
+
+        } else {
+            dayEvent.getElement().getStyle().setDisplay(Display.NONE);
+        }
+    }
+
+    public void addEvent(DateCellDayEvent dayEvent) {
+        Element main = getElement();
+        int index = 0;
+        List<CalendarEvent> events = new ArrayList<CalendarEvent>();
+
+        // events are the only widgets in this panel
+        // slots are just elements
+        for (; index < getWidgetCount(); index++) {
+            DateCellDayEvent dc = (DateCellDayEvent) getWidget(index);
+            dc.setDisabled(isDisabled());
+            events.add(dc.getCalendarEvent());
+        }
+        events.add(dayEvent.getCalendarEvent());
+
+        index = 0;
+        for (CalendarEvent e : weekgrid.getCalendar().sortEventsByDuration(
+                events)) {
+            if (e.equals(dayEvent.getCalendarEvent())) {
+                break;
+            }
+            index++;
+        }
+        this.insert(dayEvent, (com.google.gwt.user.client.Element) main,
+                index, true);
+    }
+
+    public void removeEvent(DateCellDayEvent dayEvent) {
+        remove(dayEvent);
+    }
+
+    /**
+     * 
+     * @param event
+     * @return
+     */
+    // Date methods not deprecated in GWT
+    @SuppressWarnings("deprecation")
+    private boolean canDisplay(CalendarEvent event) {
+        Date eventStart = event.getStartTime();
+        Date eventEnd = event.getEndTime();
+
+        int eventStartHours = eventStart.getHours();
+        int eventEndHours = eventEnd.getHours();
+
+        return (eventStartHours <= lastHour)
+                && (eventEndHours >= firstHour);
+    }
+
+    public void onKeyDown(KeyDownEvent event) {
+        int keycode = event.getNativeEvent().getKeyCode();
+        if (keycode == KeyCodes.KEY_ESCAPE && eventRangeStart > -1) {
+            cancelRangeSelect();
+        }
+    }
+
+    public void onMouseDown(MouseDownEvent event) {
+        if (event.getNativeButton() == NativeEvent.BUTTON_LEFT) {
+            Element e = Element.as(event.getNativeEvent().getEventTarget());
+            if (e.getClassName().contains("reserved") || isDisabled()
+                    || !weekgrid.getParentCalendar().isRangeSelectAllowed()) {
+                eventRangeStart = -1;
+            } else {
+                eventRangeStart = event.getY();
+                eventRangeStop = eventRangeStart;
+                Event.setCapture(getElement());
+                setFocus(true);
+            }
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public void onMouseUp(MouseUpEvent event) {
+        if (event.getNativeButton() != NativeEvent.BUTTON_LEFT) {
+            return;
+        }
+        Event.releaseCapture(getElement());
+        setFocus(false);
+        int dragDistance = Math.abs(eventRangeStart - event.getY());
+        if (dragDistance > 0 && eventRangeStart >= 0) {
+            Element main = getElement();
+            if (eventRangeStart > eventRangeStop) {
+                if (eventRangeStop <= -1) {
+                    eventRangeStop = 0;
+                }
+                int temp = eventRangeStart;
+                eventRangeStart = eventRangeStop;
+                eventRangeStop = temp;
+            }
+
+            NodeList<Node> nodes = main.getChildNodes();
+
+            int slotStart = -1;
+            int slotEnd = -1;
+
+            // iterate over all child nodes, until we find first the start,
+            // and then the end
+            for (int i = 0; i < nodes.getLength(); i++) {
+                Element element = (Element) nodes.getItem(i);
+                boolean isRangeElement = element.getClassName().contains(
+                        "v-daterange");
+
+                if (isRangeElement && slotStart == -1) {
+                    slotStart = i;
+                    slotEnd = i; // to catch one-slot selections
+
+                } else if (isRangeElement) {
+                    slotEnd = i;
+
+                } else if (slotStart != -1 && slotEnd != -1) {
+                    break;
+                }
+            }
+
+            clearSelectionRange();
+
+            int startMinutes = firstHour * 60 + slotStart * 30;
+            int endMinutes = (firstHour * 60) + (slotEnd + 1) * 30;
+            Date currentDate = getDate();
+            String yr = (currentDate.getYear() + 1900) + "-"
+                    + (currentDate.getMonth() + 1) + "-"
+                    + currentDate.getDate();
+            if (weekgrid.getCalendar().getRangeSelectListener() != null) {
+                weekgrid.getCalendar()
+                        .getRangeSelectListener()
+                        .rangeSelected(
+                                yr + ":" + startMinutes + ":" + endMinutes);
+            }
+            eventRangeStart = -1;
+        } else {
+            // Click event
+            eventRangeStart = -1;
+            cancelRangeSelect();
+
+        }
+    }
+
+    public void onMouseMove(MouseMoveEvent event) {
+        if (event.getNativeButton() != NativeEvent.BUTTON_LEFT) {
+            return;
+        }
+
+        if (eventRangeStart >= 0) {
+            int newY = event.getY();
+            int fromY = 0;
+            int toY = 0;
+            if (newY < eventRangeStart) {
+                fromY = newY;
+                toY = eventRangeStart;
+            } else {
+                fromY = eventRangeStart;
+                toY = newY;
+            }
+            Element main = getElement();
+            eventRangeStop = newY;
+            NodeList<Node> nodes = main.getChildNodes();
+            for (int i = 0; i < nodes.getLength(); i++) {
+                Element c = (Element) nodes.getItem(i);
+
+                if (todaybar != c) {
+
+                    int elemStart = c.getOffsetTop();
+                    int elemStop = elemStart + getSlotHeight();
+                    if (elemStart >= fromY && elemStart <= toY) {
+                        c.addClassName("v-daterange");
+                    } else if (elemStop >= fromY && elemStop <= toY) {
+                        c.addClassName("v-daterange");
+                    } else if (elemStop >= fromY && elemStart <= toY) {
+                        c.addClassName("v-daterange");
+                    } else {
+                        c.removeClassName("v-daterange");
+                    }
+                }
+            }
+        }
+
+        event.preventDefault();
+    }
+
+    public void cancelRangeSelect() {
+        Event.releaseCapture(getElement());
+        setFocus(false);
+
+        clearSelectionRange();
+    }
+
+    private void clearSelectionRange() {
+        if (eventRangeStart > -1) {
+            // clear all "selected" class names
+            Element main = getElement();
+            NodeList<Node> nodes = main.getChildNodes();
+
+            for (int i = 0; i <= 47; i++) {
+                Element c = (Element) nodes.getItem(i);
+                if (c == null) {
+                    continue;
+                }
+                c.removeClassName("v-daterange");
+            }
+
+            eventRangeStart = -1;
+        }
+    }
+
+    public void setToday(Date today, int width) {
+        this.today = today;
+        addStyleDependentName("today");
+        Element lastChild = (Element) getElement().getLastChild();
+        if (lastChild.getClassName().equals("v-calendar-current-time")) {
+            todaybar = lastChild;
+        } else {
+            todaybar = DOM.createDiv();
+            todaybar.setClassName("v-calendar-current-time");
+            getElement().appendChild(todaybar);
+        }
+
+        if (width != -1) {
+            todaybar.getStyle().setWidth(width, Unit.PX);
+        }
+
+        // position is calculated later, when we know the cell heights
+    }
+
+    public Element getTodaybarElement() {
+        return todaybar;
+    }
+
+    public void setDisabled(boolean disabled) {
+        this.disabled = disabled;
+    }
+
+    public boolean isDisabled() {
+        return disabled;
+    }
+
+    public void setDateColor(String styleName) {
+        this.setStyleName("v-calendar-datecell " + styleName);
+    }
+
+    public boolean isToday() {
+        return today != null;
+    }
+
+    public void addEmphasisStyle(
+            com.google.gwt.user.client.Element elementOver) {
+        String originalStylename = getStyleName(elementOver);
+        setStyleName(elementOver, originalStylename + DRAGEMPHASISSTYLE);
+    }
+
+    public void removeEmphasisStyle(
+            com.google.gwt.user.client.Element elementOver) {
+        String originalStylename = getStyleName(elementOver);
+        setStyleName(
+                elementOver,
+                originalStylename.substring(0, originalStylename.length()
+                        - DRAGEMPHASISSTYLE.length()));
+    }
+
+    public void onContextMenu(ContextMenuEvent event) {
+        if (weekgrid.getCalendar().getMouseEventListener() != null) {
+            event.preventDefault();
+            event.stopPropagation();
+            weekgrid.getCalendar().getMouseEventListener()
+                    .contextMenu(event, DateCell.this);
+        }
+    }
+}
\ No newline at end of file
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/DateCellContainer.java b/client/src/com/vaadin/client/ui/calendar/schedule/DateCellContainer.java
new file mode 100644 (file)
index 0000000..f1b45c8
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule;
+
+import java.util.Date;
+
+import com.google.gwt.event.dom.client.MouseDownEvent;
+import com.google.gwt.event.dom.client.MouseDownHandler;
+import com.google.gwt.event.dom.client.MouseUpEvent;
+import com.google.gwt.event.dom.client.MouseUpHandler;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.client.Util;
+import com.vaadin.client.ui.VCalendar;
+
+/**
+ * Internally used class by the Calendar
+ * 
+ * since 7.1
+ */
+public class DateCellContainer extends FlowPanel implements
+        MouseDownHandler, MouseUpHandler {
+
+    private Date date;
+
+    private Widget clickTargetWidget;
+
+    private VCalendar calendar;
+
+    private static int borderWidth = -1;
+
+    public DateCellContainer() {
+        setStylePrimaryName("v-calendar-datecell");
+    }
+
+    public static int measureBorderWidth(DateCellContainer dc) {
+        if (borderWidth == -1) {
+            borderWidth = Util.measureHorizontalBorder(dc.getElement());
+        }
+        return borderWidth;
+    }
+
+    public void setCalendar(VCalendar calendar) {
+        this.calendar = calendar;
+    }
+
+    public void setDate(Date date) {
+        this.date = date;
+    }
+
+    public Date getDate() {
+        return date;
+    }
+
+    public boolean hasEvent(int slotIndex) {
+        return hasDateCell(slotIndex)
+                && ((WeeklyLongEventsDateCell) getChildren().get(slotIndex)).getEvent() != null;
+    }
+
+    public boolean hasDateCell(int slotIndex) {
+        return (getChildren().size() - 1) >= slotIndex;
+    }
+
+    public WeeklyLongEventsDateCell getDateCell(int slotIndex) {
+        if (!hasDateCell(slotIndex)) {
+            addEmptyEventCells(slotIndex - (getChildren().size() - 1));
+        }
+        return (WeeklyLongEventsDateCell) getChildren().get(slotIndex);
+    }
+
+    public void addEmptyEventCells(int eventCount) {
+        for (int i = 0; i < eventCount; i++) {
+            addEmptyEventCell();
+        }
+    }
+
+    public void addEmptyEventCell() {
+        WeeklyLongEventsDateCell dateCell = new WeeklyLongEventsDateCell();
+        dateCell.addMouseDownHandler(this);
+        dateCell.addMouseUpHandler(this);
+        add(dateCell);
+    }
+
+    public void onMouseDown(MouseDownEvent event) {
+        clickTargetWidget = (Widget) event.getSource();
+
+        event.stopPropagation();
+    }
+
+    public void onMouseUp(MouseUpEvent event) {
+        if (event.getSource() == clickTargetWidget
+                && clickTargetWidget instanceof WeeklyLongEventsDateCell
+                && !calendar.isDisabledOrReadOnly()) {
+            CalendarEvent calendarEvent = ((WeeklyLongEventsDateCell) clickTargetWidget)
+                    .getEvent();
+            if (calendar.getEventClickListener() != null) {
+                calendar.getEventClickListener().eventClick(calendarEvent);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java b/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java
new file mode 100644 (file)
index 0000000..039a00e
--- /dev/null
@@ -0,0 +1,659 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule;
+
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.EventTarget;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.dom.client.Style;
+import com.google.gwt.dom.client.Style.Position;
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.event.dom.client.ContextMenuEvent;
+import com.google.gwt.event.dom.client.ContextMenuHandler;
+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.MouseDownEvent;
+import com.google.gwt.event.dom.client.MouseDownHandler;
+import com.google.gwt.event.dom.client.MouseMoveEvent;
+import com.google.gwt.event.dom.client.MouseMoveHandler;
+import com.google.gwt.event.dom.client.MouseUpEvent;
+import com.google.gwt.event.dom.client.MouseUpHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.vaadin.client.Util;
+import com.vaadin.client.ui.VCalendar;
+import com.vaadin.shared.ui.calendar.DateConstants;
+
+/**
+ * Internally used by the calendar
+ * 
+ * @since 7.1
+ */
+public class DateCellDayEvent extends FocusableHTML implements
+        MouseDownHandler, MouseUpHandler, MouseMoveHandler,
+        KeyDownHandler, ContextMenuHandler, HasTooltipKey {
+
+    private final DateCell dateCell;
+    private Element caption = null;
+    private final Element eventContent;
+    private CalendarEvent calendarEvent = null;
+    private HandlerRegistration moveRegistration;
+    private int startY = -1;
+    private int startX = -1;
+    private String moveWidth;
+    public static final int halfHourInMilliSeconds = 1800 * 1000;
+    private Date startDatetimeFrom;
+    private Date startDatetimeTo;
+    private boolean mouseMoveStarted;
+    private int top;
+    private int startYrelative;
+    private int startXrelative;
+    private boolean disabled;
+    private final WeekGrid weekGrid;
+    private com.google.gwt.user.client.Element topResizeBar;
+    private com.google.gwt.user.client.Element bottomResizeBar;
+    private Element clickTarget;
+    private final Integer eventIndex;
+    private int slotHeight;
+    private final List<HandlerRegistration> handlers;
+    private boolean mouseMoveCanceled;
+
+    public DateCellDayEvent(DateCell dateCell, WeekGrid parent, CalendarEvent event) {
+        super();
+        this.dateCell = dateCell;
+
+        handlers = new LinkedList<HandlerRegistration>();
+
+        setStylePrimaryName("v-calendar-event");
+        setCalendarEvent(event);
+
+        weekGrid = parent;
+
+        Style s = getElement().getStyle();
+        if (event.getStyleName().length() > 0) {
+            addStyleDependentName(event.getStyleName());
+        }
+        s.setPosition(Position.ABSOLUTE);
+
+        caption = DOM.createDiv();
+        caption.addClassName("v-calendar-event-caption");
+        getElement().appendChild(caption);
+
+        eventContent = DOM.createDiv();
+        eventContent.addClassName("v-calendar-event-content");
+        getElement().appendChild(eventContent);
+
+        VCalendar calendar = weekGrid.getCalendar();
+        if (weekGrid.getCalendar().isEventResizeAllowed()) {
+            topResizeBar = DOM.createDiv();
+            bottomResizeBar = DOM.createDiv();
+
+            topResizeBar.addClassName("v-calendar-event-resizetop");
+            bottomResizeBar
+                    .addClassName("v-calendar-event-resizebottom");
+
+            getElement().appendChild(topResizeBar);
+            getElement().appendChild(bottomResizeBar);
+        }
+
+        eventIndex = event.getIndex();
+    }
+
+    @Override
+    protected void onAttach() {
+        super.onAttach();
+        handlers.add(addMouseDownHandler(this));
+        handlers.add(addMouseUpHandler(this));
+        handlers.add(addKeyDownHandler(this));
+        handlers.add(addDomHandler(this, ContextMenuEvent.getType()));
+    }
+
+    @Override
+    protected void onDetach() {
+        for (HandlerRegistration handler : handlers) {
+            handler.removeHandler();
+        }
+        handlers.clear();
+        super.onDetach();
+    }
+
+    public void setSlotHeightInPX(int slotHeight) {
+        this.slotHeight = slotHeight;
+    }
+
+    public void updatePosition(long startFromMinutes,
+            long durationInMinutes) {
+        if (startFromMinutes < 0) {
+            startFromMinutes = 0;
+        }
+        top = weekGrid.getPixelTopFor((int) startFromMinutes);
+
+        getElement().getStyle().setTop(top, Unit.PX);
+        if (durationInMinutes > 0) {
+            int heightMinutes = weekGrid.getPixelLengthFor(
+                    (int) startFromMinutes, (int) durationInMinutes);
+            setHeight(heightMinutes);
+        } else {
+            setHeight(-1);
+        }
+
+        boolean multiRowCaption = (durationInMinutes > 30);
+        updateCaptions(multiRowCaption);
+    }
+
+    public int getTop() {
+        return top;
+    }
+
+    public void setMoveWidth(int width) {
+        moveWidth = width + "px";
+    }
+
+    public void setHeight(int h) {
+        if (h == -1) {
+            getElement().getStyle().setProperty("height", "");
+            eventContent.getStyle().setProperty("height", "");
+        } else {
+            getElement().getStyle().setHeight(h, Unit.PX);
+            // FIXME measure the border height (2px) from the DOM
+            eventContent.getStyle().setHeight(h - 2, Unit.PX);
+        }
+    }
+
+    /**
+     * @param bigMode
+     *            If false, event is so small that caption must be in
+     *            time-row
+     */
+    private void updateCaptions(boolean bigMode) {
+        String separator = bigMode ? "<br />" : ": ";
+        caption.setInnerHTML("<span>" + calendarEvent.getTimeAsText()
+                + "</span>" + separator
+                + Util.escapeHTML(calendarEvent.getCaption()));
+        eventContent.setInnerHTML("");
+    }
+
+    public void onKeyDown(KeyDownEvent event) {
+        int keycode = event.getNativeEvent().getKeyCode();
+        if (keycode == KeyCodes.KEY_ESCAPE && mouseMoveStarted) {
+            cancelMouseMove();
+        }
+    }
+
+    public void onMouseDown(MouseDownEvent event) {
+        startX = event.getClientX();
+        startY = event.getClientY();
+        if (isDisabled()
+                || event.getNativeButton() != NativeEvent.BUTTON_LEFT) {
+            return;
+        }
+
+        clickTarget = Element.as(event.getNativeEvent()
+                .getEventTarget());
+        mouseMoveCanceled = false;
+
+        if (weekGrid.getCalendar().isEventMoveAllowed()
+                || clickTargetsResize()) {
+            moveRegistration = addMouseMoveHandler(this);
+            setFocus(true);
+            try {
+                startYrelative = (int) ((double) event
+                        .getRelativeY(caption) % slotHeight);
+                startXrelative = (event.getRelativeX(weekGrid
+                        .getElement()) - weekGrid.timebar
+                        .getOffsetWidth())
+                        % getDateCellWidth();
+            } catch (Exception e) {
+                GWT.log("Exception calculating relative start position",
+                        e);
+            }
+            mouseMoveStarted = false;
+            Style s = getElement().getStyle();
+            s.setZIndex(1000);
+            startDatetimeFrom = (Date) calendarEvent.getStartTime()
+                    .clone();
+            startDatetimeTo = (Date) calendarEvent.getEndTime().clone();
+            Event.setCapture(getElement());
+        }
+
+        // make sure the right cursor is always displayed
+        if (clickTargetsResize()) {
+            addGlobalResizeStyle();
+        }
+
+        /*
+         * We need to stop the event propagation or else the WeekGrid
+         * range select will kick in
+         */
+        event.stopPropagation();
+        event.preventDefault();
+    }
+
+    public void onMouseUp(MouseUpEvent event) {
+        if (mouseMoveCanceled) {
+            return;
+        }
+
+        Event.releaseCapture(getElement());
+        setFocus(false);
+        if (moveRegistration != null) {
+            moveRegistration.removeHandler();
+            moveRegistration = null;
+        }
+        int endX = event.getClientX();
+        int endY = event.getClientY();
+        int xDiff = startX - endX;
+        int yDiff = startY - endY;
+        startX = -1;
+        startY = -1;
+        mouseMoveStarted = false;
+        Style s = getElement().getStyle();
+        s.setZIndex(1);
+        if (!clickTargetsResize()) {
+            // check if mouse has moved over threshold of 3 pixels
+            boolean mouseMoved = (xDiff < -3 || xDiff > 3 || yDiff < -3 || yDiff > 3);
+
+            if (!weekGrid.getCalendar().isDisabledOrReadOnly()
+                    && mouseMoved) {
+                // Event Move:
+                // - calendar must be enabled
+                // - calendar must not be in read-only mode
+                weekGrid.eventMoved(this);
+            } else if (!weekGrid.getCalendar().isDisabled()) {
+                // Event Click:
+                // - calendar must be enabled (read-only is allowed)
+                EventTarget et = event.getNativeEvent()
+                        .getEventTarget();
+                Element e = Element.as(et);
+                if (e == caption || e == eventContent
+                        || e.getParentElement() == caption) {
+                    if (weekGrid.getCalendar().getEventClickListener() != null) {
+                        weekGrid.getCalendar().getEventClickListener()
+                                .eventClick(calendarEvent);
+                    }
+                }
+            }
+
+        } else { // click targeted resize bar
+            removeGlobalResizeStyle();
+            if (weekGrid.getCalendar().getEventResizeListener() != null) {
+                weekGrid.getCalendar().getEventResizeListener()
+                        .eventResized(calendarEvent);
+            }
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public void onMouseMove(MouseMoveEvent event) {
+        if (startY < 0 && startX < 0) {
+            return;
+        }
+        if (isDisabled()) {
+            Event.releaseCapture(getElement());
+            mouseMoveStarted = false;
+            startY = -1;
+            startX = -1;
+            removeGlobalResizeStyle();
+            return;
+        }
+        int currentY = event.getClientY();
+        int currentX = event.getClientX();
+        int moveY = (currentY - startY);
+        int moveX = (currentX - startX);
+        if ((moveY < 5 && moveY > -6) && (moveX < 5 && moveX > -6)) {
+            return;
+        }
+        if (!mouseMoveStarted) {
+            setWidth(moveWidth);
+            getElement().getStyle().setMarginLeft(0, Unit.PX);
+            mouseMoveStarted = true;
+        }
+
+        HorizontalPanel parent = (HorizontalPanel) getParent()
+                .getParent();
+        int relativeX = event.getRelativeX(parent.getElement())
+                - weekGrid.timebar.getOffsetWidth();
+        int halfHourDiff = 0;
+        if (moveY > 0) {
+            halfHourDiff = (startYrelative + moveY) / slotHeight;
+        } else {
+            halfHourDiff = (moveY - startYrelative) / slotHeight;
+        }
+
+        int dateCellWidth = getDateCellWidth();
+        long dayDiff = 0;
+        if (moveX >= 0) {
+            dayDiff = (startXrelative + moveX) / dateCellWidth;
+        } else {
+            dayDiff = (moveX - (dateCellWidth - startXrelative))
+                    / dateCellWidth;
+        }
+
+        int dayOffset = relativeX / dateCellWidth;
+
+        // sanity check for right side overflow
+        int dateCellCount = weekGrid.getDateCellCount();
+        if (dayOffset >= dateCellCount) {
+            dayOffset--;
+            dayDiff--;
+        }
+
+        int dayOffsetPx = calculateDateCellOffsetPx(dayOffset)
+                + weekGrid.timebar.getOffsetWidth();
+
+        GWT.log("DateCellWidth: " + dateCellWidth + " dayDiff: "
+                + dayDiff + " dayOffset: " + dayOffset
+                + " dayOffsetPx: " + dayOffsetPx + " startXrelative: "
+                + startXrelative + " moveX: " + moveX);
+
+        if (relativeX < 0 || relativeX >= getDatesWidth()) {
+            return;
+        }
+
+        Style s = getElement().getStyle();
+
+        Date from = calendarEvent.getStartTime();
+        Date to = calendarEvent.getEndTime();
+        long duration = to.getTime() - from.getTime();
+
+        if (!clickTargetsResize()
+                && weekGrid.getCalendar().isEventMoveAllowed()) {
+            long daysMs = dayDiff * DateConstants.DAYINMILLIS;
+            from.setTime(startDatetimeFrom.getTime() + daysMs);
+            from.setTime(from.getTime()
+                    + ((long) halfHourInMilliSeconds * halfHourDiff));
+            to.setTime((from.getTime() + duration));
+
+            calendarEvent.setStartTime(from);
+            calendarEvent.setEndTime(to);
+            calendarEvent.setStart(new Date(from.getTime()));
+            calendarEvent.setEnd(new Date(to.getTime()));
+
+            // Set new position for the event
+            long startFromMinutes = (from.getHours() * 60)
+                    + from.getMinutes();
+            long range = calendarEvent.getRangeInMinutes();
+            startFromMinutes = calculateStartFromMinute(
+                    startFromMinutes, from, to, dayOffsetPx);
+            if (startFromMinutes < 0) {
+                range += startFromMinutes;
+            }
+            updatePosition(startFromMinutes, range);
+
+            s.setLeft(dayOffsetPx, Unit.PX);
+
+            if (weekGrid.getDateCellWidths() != null) {
+                s.setWidth(weekGrid.getDateCellWidths()[dayOffset],
+                        Unit.PX);
+            } else {
+                setWidth(moveWidth);
+            }
+
+        } else if (clickTarget == topResizeBar) {
+            long oldStartTime = startDatetimeFrom.getTime();
+            long newStartTime = oldStartTime
+                    + ((long) halfHourInMilliSeconds * halfHourDiff);
+
+            if (!isTimeRangeTooSmall(newStartTime,
+                    startDatetimeTo.getTime())) {
+                newStartTime = startDatetimeTo.getTime()
+                        - getMinTimeRange();
+            }
+
+            from.setTime(newStartTime);
+
+            calendarEvent.setStartTime(from);
+            calendarEvent.setStart(new Date(from.getTime()));
+
+            // Set new position for the event
+            long startFromMinutes = (from.getHours() * 60)
+                    + from.getMinutes();
+            long range = calendarEvent.getRangeInMinutes();
+
+            updatePosition(startFromMinutes, range);
+
+        } else if (clickTarget == bottomResizeBar) {
+            long oldEndTime = startDatetimeTo.getTime();
+            long newEndTime = oldEndTime
+                    + ((long) halfHourInMilliSeconds * halfHourDiff);
+
+            if (!isTimeRangeTooSmall(startDatetimeFrom.getTime(),
+                    newEndTime)) {
+                newEndTime = startDatetimeFrom.getTime()
+                        + getMinTimeRange();
+            }
+
+            to.setTime(newEndTime);
+
+            calendarEvent.setEndTime(to);
+            calendarEvent.setEnd(new Date(to.getTime()));
+
+            // Set new position for the event
+            long startFromMinutes = (startDatetimeFrom.getHours() * 60)
+                    + startDatetimeFrom.getMinutes();
+            long range = calendarEvent.getRangeInMinutes();
+            startFromMinutes = calculateStartFromMinute(
+                    startFromMinutes, from, to, dayOffsetPx);
+            if (startFromMinutes < 0) {
+                range += startFromMinutes;
+            }
+            updatePosition(startFromMinutes, range);
+        }
+    }
+
+    private void cancelMouseMove() {
+        mouseMoveCanceled = true;
+
+        // reset and remove everything related to the event handling
+        Event.releaseCapture(getElement());
+        setFocus(false);
+
+        if (moveRegistration != null) {
+            moveRegistration.removeHandler();
+            moveRegistration = null;
+        }
+
+        mouseMoveStarted = false;
+        removeGlobalResizeStyle();
+
+        Style s = getElement().getStyle();
+        s.setZIndex(1);
+
+        // reset the position of the event
+        int dateCellWidth = getDateCellWidth();
+        int dayOffset = startXrelative / dateCellWidth;
+        s.clearLeft();
+
+        calendarEvent.setStartTime(startDatetimeFrom);
+        calendarEvent.setEndTime(startDatetimeTo);
+
+        long startFromMinutes = (startDatetimeFrom.getHours() * 60)
+                + startDatetimeFrom.getMinutes();
+        long range = calendarEvent.getRangeInMinutes();
+
+        startFromMinutes = calculateStartFromMinute(startFromMinutes,
+                startDatetimeFrom, startDatetimeTo, dayOffset);
+        if (startFromMinutes < 0) {
+            range += startFromMinutes;
+        }
+
+        updatePosition(startFromMinutes, range);
+
+        startY = -1;
+        startX = -1;
+
+        // to reset the event width
+        ((DateCell) getParent()).recalculateEventWidths();
+    }
+
+    // date methods are not deprecated in GWT
+    @SuppressWarnings("deprecation")
+    private long calculateStartFromMinute(long startFromMinutes,
+            Date from, Date to, int dayOffset) {
+        boolean eventStartAtDifferentDay = from.getDate() != to
+                .getDate();
+        if (eventStartAtDifferentDay) {
+            long minutesOnPrevDay = (getTargetDateByCurrentPosition(
+                    dayOffset).getTime() - from.getTime())
+                    / DateConstants.MINUTEINMILLIS;
+            startFromMinutes = -1 * minutesOnPrevDay;
+        }
+
+        return startFromMinutes;
+    }
+
+    /**
+     * @param dateOffset
+     * @return the amount of pixels the given date is from the left side
+     */
+    private int calculateDateCellOffsetPx(int dateOffset) {
+        int dateCellOffset = 0;
+        int[] dateWidths = weekGrid.getDateCellWidths();
+
+        if (dateWidths != null) {
+            for (int i = 0; i < dateOffset; i++) {
+                dateCellOffset += dateWidths[i] + 1;
+            }
+        } else {
+            dateCellOffset = dateOffset * weekGrid.getDateCellWidth();
+        }
+
+        return dateCellOffset;
+    }
+
+    /**
+     * Check if the given time range is too small for events
+     * 
+     * @param start
+     * @param end
+     * @return
+     */
+    private boolean isTimeRangeTooSmall(long start, long end) {
+        return (end - start) >= getMinTimeRange();
+    }
+
+    /**
+     * @return the minimum amount of ms that an event must last when
+     *         resized
+     */
+    private long getMinTimeRange() {
+        return DateConstants.MINUTEINMILLIS * 30;
+    }
+
+    /**
+     * Build the string for sending resize events to server
+     * 
+     * @param event
+     * @return
+     */
+    private String buildResizeString(CalendarEvent event) {
+        StringBuilder buffer = new StringBuilder();
+        buffer.append(event.getIndex());
+        buffer.append(",");
+        buffer.append(DateUtil.formatClientSideDate(event.getStart()));
+        buffer.append("-");
+        buffer.append(DateUtil.formatClientSideTime(event
+                .getStartTime()));
+        buffer.append(",");
+        buffer.append(DateUtil.formatClientSideDate(event.getEnd()));
+        buffer.append("-");
+        buffer.append(DateUtil.formatClientSideTime(event.getEndTime()));
+
+        return buffer.toString();
+    }
+
+    private Date getTargetDateByCurrentPosition(int left) {
+        DateCell newParent = (DateCell) weekGrid.content
+                .getWidget((left / getDateCellWidth()) + 1);
+        Date targetDate = newParent.getDate();
+        return targetDate;
+    }
+
+    private int getDateCellWidth() {
+        return weekGrid.getDateCellWidth();
+    }
+
+    /* Returns total width of all date cells. */
+    private int getDatesWidth() {
+        if (weekGrid.width == -1) {
+            // Undefined width. Needs to be calculated by the known cell
+            // widths.
+            int count = weekGrid.content.getWidgetCount() - 1;
+            return count * getDateCellWidth();
+        }
+
+        return weekGrid.getInternalWidth();
+    }
+
+    /**
+     * @return true if the current mouse movement is resizing
+     */
+    private boolean clickTargetsResize() {
+        return weekGrid.getCalendar().isEventResizeAllowed()
+                && (clickTarget == topResizeBar || clickTarget == bottomResizeBar);
+    }
+
+    private void addGlobalResizeStyle() {
+        if (clickTarget == topResizeBar) {
+            weekGrid.getCalendar().addStyleDependentName("nresize");
+        } else if (clickTarget == bottomResizeBar) {
+            weekGrid.getCalendar().addStyleDependentName("sresize");
+        }
+    }
+
+    private void removeGlobalResizeStyle() {
+        weekGrid.getCalendar().removeStyleDependentName("nresize");
+        weekGrid.getCalendar().removeStyleDependentName("sresize");
+    }
+
+    public void setCalendarEvent(CalendarEvent calendarEvent) {
+        this.calendarEvent = calendarEvent;
+    }
+
+    public CalendarEvent getCalendarEvent() {
+        return calendarEvent;
+    }
+
+    public void setDisabled(boolean disabled) {
+        this.disabled = disabled;
+    }
+
+    public boolean isDisabled() {
+        return disabled;
+    }
+
+    public void onContextMenu(ContextMenuEvent event) {
+        if (this.dateCell.weekgrid.getCalendar().getMouseEventListener() != null) {
+            event.preventDefault();
+            event.stopPropagation();
+            this.dateCell.weekgrid.getCalendar().getMouseEventListener()
+                    .contextMenu(event, this);
+        }
+    }
+
+    @Override
+    public Object getTooltipKey() {
+        return eventIndex;
+    }
+}
\ No newline at end of file
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/DateCellGroup.java b/client/src/com/vaadin/client/ui/calendar/schedule/DateCellGroup.java
new file mode 100644 (file)
index 0000000..d2add53
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+
+/**
+ * Internally used by the calendar
+ * 
+ * @since 7.1
+ */
+public class DateCellGroup {
+    private WeekGridMinuteTimeRange range;
+    private final List<Integer> items;
+
+    public DateCellGroup(Integer index) {
+        items = new ArrayList<Integer>();
+        items.add(index);
+    }
+
+    public WeekGridMinuteTimeRange getDateRange() {
+        return range;
+    }
+
+    public Date getStart() {
+        return range.getStart();
+    }
+
+    public Date getEnd() {
+        return range.getEnd();
+    }
+
+    public void setDateRange(WeekGridMinuteTimeRange range) {
+        this.range = range;
+    }
+
+    public List<Integer> getItems() {
+        return items;
+    }
+
+    public void add(Integer index) {
+        items.add(index);
+    }
+}
\ No newline at end of file
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/DateUtil.java b/client/src/com/vaadin/client/ui/calendar/schedule/DateUtil.java
new file mode 100644 (file)
index 0000000..8472632
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule;
+
+import java.util.Date;
+
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.vaadin.shared.ui.calendar.DateConstants;
+
+/**
+ * Utility class for {@link Date} operations
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ */
+public class DateUtil {
+
+    /**
+     * Checks if dates are same day without checking datetimes.
+     * 
+     * @param date1
+     * @param date2
+     * @return
+     */
+    @SuppressWarnings("deprecation")
+    public static boolean compareDate(Date date1, Date date2) {
+        if (date1.getDate() == date2.getDate()
+                && date1.getYear() == date2.getYear()
+                && date1.getMonth() == date2.getMonth()) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * @param date
+     *            the date to format
+     * 
+     * @return given Date as String, for communicating to server-side
+     */
+    public static String formatClientSideDate(Date date) {
+        DateTimeFormat dateformat_date = DateTimeFormat
+                .getFormat(DateConstants.CLIENT_DATE_FORMAT);
+        return dateformat_date.format(date);
+    }
+
+    /**
+     * @param date
+     *            the date to format
+     * @return given Date as String, for communicating to server-side
+     */
+    public static String formatClientSideTime(Date date) {
+        DateTimeFormat dateformat_date = DateTimeFormat
+                .getFormat(DateConstants.CLIENT_TIME_FORMAT);
+        return dateformat_date.format(date);
+    }
+}
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/DayToolbar.java b/client/src/com/vaadin/client/ui/calendar/schedule/DayToolbar.java
new file mode 100644 (file)
index 0000000..bb0155d
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule;
+
+import java.util.Iterator;
+
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.client.ui.VCalendar;
+
+/**
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ * 
+ */
+public class DayToolbar extends HorizontalPanel implements ClickHandler {
+    private int width = 0;
+    protected static final int MARGINLEFT = 50;
+    protected static final int MARGINRIGHT = 20;
+    protected Button backLabel;
+    protected Button nextLabel;
+    private boolean verticalSized;
+    private boolean horizontalSized;
+    private VCalendar calendar;
+
+    public DayToolbar(VCalendar vcalendar) {
+        calendar = vcalendar;
+
+        setStylePrimaryName("v-calendar-header-week");
+        backLabel = new Button();
+        backLabel.setStylePrimaryName("v-calendar-back");
+        nextLabel = new Button();
+        nextLabel.addClickHandler(this);
+        nextLabel.setStylePrimaryName("v-calendar-next");
+        backLabel.addClickHandler(this);
+        setBorderWidth(0);
+        setSpacing(0);
+    }
+
+    public void setWidthPX(int width) {
+        this.width = (width - MARGINLEFT) - MARGINRIGHT;
+        // super.setWidth(this.width + "px");
+        if (getWidgetCount() == 0) {
+            return;
+        }
+        updateCellWidths();
+    }
+
+    public void updateCellWidths() {
+        int count = getWidgetCount();
+        if (count > 0) {
+            setCellWidth(backLabel, MARGINLEFT + "px");
+            setCellWidth(nextLabel, MARGINRIGHT + "px");
+            setCellHorizontalAlignment(nextLabel, ALIGN_RIGHT);
+            int cellw = width / (count - 2);
+            int remain = width % (count - 2);
+            int cellw2 = cellw + 1;
+            if (cellw > 0) {
+                int[] cellWidths = VCalendar
+                        .distributeSize(width, count - 2, 0);
+                for (int i = 1; i < count - 1; i++) {
+                    Widget widget = getWidget(i);
+                    // if (remain > 0) {
+                    // setCellWidth(widget, cellw2 + "px");
+                    // remain--;
+                    // } else {
+                    // setCellWidth(widget, cellw + "px");
+                    // }
+                    setCellWidth(widget, cellWidths[i - 1] + "px");
+                    widget.setWidth(cellWidths[i - 1] + "px");
+                }
+            }
+        }
+    }
+
+    public void add(String dayName, final String date,
+            String localized_date_format, String extraClass) {
+        Label l = new Label(dayName + " " + localized_date_format);
+        l.setStylePrimaryName("v-calendar-header-day");
+
+        if (extraClass != null) {
+            l.addStyleDependentName(extraClass);
+        }
+
+        if (verticalSized) {
+            l.addStyleDependentName("Vsized");
+        }
+        if (horizontalSized) {
+            l.addStyleDependentName("Hsized");
+        }
+
+        l.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent event) {
+                if (calendar.getDateClickListener() != null) {
+                    calendar.getDateClickListener().dateClick(date);
+                }
+            }
+        });
+
+        add(l);
+    }
+
+    public void addBackButton() {
+        if (!calendar.isBackwardNavigationEnabled()) {
+            nextLabel.getElement().getStyle().setHeight(0, Unit.PX);
+        }
+        add(backLabel);
+    }
+
+    public void addNextButton() {
+        if (!calendar.isForwardNavigationEnabled()) {
+            backLabel.getElement().getStyle().setHeight(0, Unit.PX);
+        }
+        add(nextLabel);
+    }
+
+    public void onClick(ClickEvent event) {
+        if (!calendar.isDisabledOrReadOnly()) {
+            if (event.getSource() == nextLabel) {
+                if (calendar.getForwardListener() != null) {
+                    calendar.getForwardListener().forward();
+                }
+            } else if (event.getSource() == backLabel) {
+                if (calendar.getBackwardListener() != null) {
+                    calendar.getBackwardListener().backward();
+                }
+            }
+        }
+    }
+
+    public void setVerticalSized(boolean sized) {
+        verticalSized = sized;
+        updateDayLabelSizedStyleNames();
+    }
+
+    public void setHorizontalSized(boolean sized) {
+        horizontalSized = sized;
+        updateDayLabelSizedStyleNames();
+    }
+
+    private void updateDayLabelSizedStyleNames() {
+        Iterator<Widget> it = iterator();
+        while (it.hasNext()) {
+            updateWidgetSizedStyleName(it.next());
+        }
+    }
+
+    private void updateWidgetSizedStyleName(Widget w) {
+        if (verticalSized) {
+            w.addStyleDependentName("Vsized");
+        } else {
+            w.removeStyleDependentName("VSized");
+        }
+        if (horizontalSized) {
+            w.addStyleDependentName("Hsized");
+        } else {
+            w.removeStyleDependentName("HSized");
+        }
+    }
+}
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/FocusableComplexPanel.java b/client/src/com/vaadin/client/ui/calendar/schedule/FocusableComplexPanel.java
new file mode 100644 (file)
index 0000000..6233238
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule;
+
+import com.google.gwt.event.dom.client.BlurEvent;
+import com.google.gwt.event.dom.client.BlurHandler;
+import com.google.gwt.event.dom.client.FocusEvent;
+import com.google.gwt.event.dom.client.FocusHandler;
+import com.google.gwt.event.dom.client.HasBlurHandlers;
+import com.google.gwt.event.dom.client.HasFocusHandlers;
+import com.google.gwt.event.dom.client.HasKeyDownHandlers;
+import com.google.gwt.event.dom.client.HasKeyPressHandlers;
+import com.google.gwt.event.dom.client.KeyDownEvent;
+import com.google.gwt.event.dom.client.KeyDownHandler;
+import com.google.gwt.event.dom.client.KeyPressEvent;
+import com.google.gwt.event.dom.client.KeyPressHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.ui.ComplexPanel;
+import com.google.gwt.user.client.ui.impl.FocusImpl;
+import com.vaadin.client.Focusable;
+
+/**
+ * A ComplexPanel that can be focused
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ * 
+ */
+public class FocusableComplexPanel extends ComplexPanel implements
+        HasFocusHandlers, HasBlurHandlers, HasKeyDownHandlers,
+        HasKeyPressHandlers, Focusable {
+
+    protected void makeFocusable() {
+        // make focusable, as we don't need access key magic we don't need to
+        // use FocusImpl.createFocusable
+        getElement().setTabIndex(0);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.google.gwt.event.dom.client.HasFocusHandlers#addFocusHandler(com.
+     * google.gwt.event.dom.client.FocusHandler)
+     */
+    public HandlerRegistration addFocusHandler(FocusHandler handler) {
+        return addDomHandler(handler, FocusEvent.getType());
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.google.gwt.event.dom.client.HasBlurHandlers#addBlurHandler(com.google
+     * .gwt.event.dom.client.BlurHandler)
+     */
+    public HandlerRegistration addBlurHandler(BlurHandler handler) {
+        return addDomHandler(handler, BlurEvent.getType());
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.google.gwt.event.dom.client.HasKeyDownHandlers#addKeyDownHandler(
+     * com.google.gwt.event.dom.client.KeyDownHandler)
+     */
+    public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) {
+        return addDomHandler(handler, KeyDownEvent.getType());
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.google.gwt.event.dom.client.HasKeyPressHandlers#addKeyPressHandler
+     * (com.google.gwt.event.dom.client.KeyPressHandler)
+     */
+    public HandlerRegistration addKeyPressHandler(KeyPressHandler handler) {
+        return addDomHandler(handler, KeyPressEvent.getType());
+    }
+
+    /**
+     * Sets/Removes the keyboard focus to the panel.
+     * 
+     * @param focus
+     *            If set to true then the focus is moved to the panel, if set to
+     *            false the focus is removed
+     */
+    public void setFocus(boolean focus) {
+        if (focus) {
+            FocusImpl.getFocusImplForPanel().focus(getElement());
+        } else {
+            FocusImpl.getFocusImplForPanel().blur(getElement());
+        }
+    }
+
+    /**
+     * Focus the panel
+     */
+    public void focus() {
+        setFocus(true);
+    }
+}
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/FocusableGrid.java b/client/src/com/vaadin/client/ui/calendar/schedule/FocusableGrid.java
new file mode 100644 (file)
index 0000000..d317736
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule;
+
+import com.google.gwt.event.dom.client.BlurEvent;
+import com.google.gwt.event.dom.client.BlurHandler;
+import com.google.gwt.event.dom.client.FocusEvent;
+import com.google.gwt.event.dom.client.FocusHandler;
+import com.google.gwt.event.dom.client.HasBlurHandlers;
+import com.google.gwt.event.dom.client.HasFocusHandlers;
+import com.google.gwt.event.dom.client.HasKeyDownHandlers;
+import com.google.gwt.event.dom.client.HasKeyPressHandlers;
+import com.google.gwt.event.dom.client.KeyDownEvent;
+import com.google.gwt.event.dom.client.KeyDownHandler;
+import com.google.gwt.event.dom.client.KeyPressEvent;
+import com.google.gwt.event.dom.client.KeyPressHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.impl.FocusImpl;
+import com.vaadin.client.Focusable;
+
+/**
+ * A Grid that can be focused
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ * 
+ */
+public class FocusableGrid extends Grid implements HasFocusHandlers,
+        HasBlurHandlers, HasKeyDownHandlers, HasKeyPressHandlers, Focusable {
+
+    /**
+     * Constructor
+     */
+    public FocusableGrid() {
+        super();
+        makeFocusable();
+    }
+
+    public FocusableGrid(int rows, int columns) {
+        super(rows, columns);
+        makeFocusable();
+    }
+
+    protected void makeFocusable() {
+        // make focusable, as we don't need access key magic we don't need to
+        // use FocusImpl.createFocusable
+        getElement().setTabIndex(0);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.google.gwt.event.dom.client.HasFocusHandlers#addFocusHandler(com.
+     * google.gwt.event.dom.client.FocusHandler)
+     */
+    public HandlerRegistration addFocusHandler(FocusHandler handler) {
+        return addDomHandler(handler, FocusEvent.getType());
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.google.gwt.event.dom.client.HasBlurHandlers#addBlurHandler(com.google
+     * .gwt.event.dom.client.BlurHandler)
+     */
+    public HandlerRegistration addBlurHandler(BlurHandler handler) {
+        return addDomHandler(handler, BlurEvent.getType());
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.google.gwt.event.dom.client.HasKeyDownHandlers#addKeyDownHandler(
+     * com.google.gwt.event.dom.client.KeyDownHandler)
+     */
+    public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) {
+        return addDomHandler(handler, KeyDownEvent.getType());
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.google.gwt.event.dom.client.HasKeyPressHandlers#addKeyPressHandler
+     * (com.google.gwt.event.dom.client.KeyPressHandler)
+     */
+    public HandlerRegistration addKeyPressHandler(KeyPressHandler handler) {
+        return addDomHandler(handler, KeyPressEvent.getType());
+    }
+
+    /**
+     * Sets/Removes the keyboard focus to the panel.
+     * 
+     * @param focus
+     *            If set to true then the focus is moved to the panel, if set to
+     *            false the focus is removed
+     */
+    public void setFocus(boolean focus) {
+        if (focus) {
+            FocusImpl.getFocusImplForPanel().focus(getElement());
+        } else {
+            FocusImpl.getFocusImplForPanel().blur(getElement());
+        }
+    }
+
+    /**
+     * Focus the panel
+     */
+    public void focus() {
+        setFocus(true);
+    }
+}
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/FocusableHTML.java b/client/src/com/vaadin/client/ui/calendar/schedule/FocusableHTML.java
new file mode 100644 (file)
index 0000000..c3fe195
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule;
+
+import com.google.gwt.event.dom.client.BlurEvent;
+import com.google.gwt.event.dom.client.BlurHandler;
+import com.google.gwt.event.dom.client.FocusEvent;
+import com.google.gwt.event.dom.client.FocusHandler;
+import com.google.gwt.event.dom.client.HasBlurHandlers;
+import com.google.gwt.event.dom.client.HasFocusHandlers;
+import com.google.gwt.event.dom.client.HasKeyDownHandlers;
+import com.google.gwt.event.dom.client.HasKeyPressHandlers;
+import com.google.gwt.event.dom.client.KeyDownEvent;
+import com.google.gwt.event.dom.client.KeyDownHandler;
+import com.google.gwt.event.dom.client.KeyPressEvent;
+import com.google.gwt.event.dom.client.KeyPressHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.impl.FocusImpl;
+import com.vaadin.client.Focusable;
+
+/**
+ * A HTML widget that can be focused
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ * 
+ */
+public class FocusableHTML extends HTML implements HasFocusHandlers,
+        HasBlurHandlers, HasKeyDownHandlers, HasKeyPressHandlers, Focusable {
+
+    /**
+     * Constructor
+     */
+    public FocusableHTML() {
+        // make focusable, as we don't need access key magic we don't need to
+        // use FocusImpl.createFocusable
+        getElement().setTabIndex(0);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.google.gwt.event.dom.client.HasFocusHandlers#addFocusHandler(com.
+     * google.gwt.event.dom.client.FocusHandler)
+     */
+    public HandlerRegistration addFocusHandler(FocusHandler handler) {
+        return addDomHandler(handler, FocusEvent.getType());
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.google.gwt.event.dom.client.HasBlurHandlers#addBlurHandler(com.google
+     * .gwt.event.dom.client.BlurHandler)
+     */
+    public HandlerRegistration addBlurHandler(BlurHandler handler) {
+        return addDomHandler(handler, BlurEvent.getType());
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.google.gwt.event.dom.client.HasKeyDownHandlers#addKeyDownHandler(
+     * com.google.gwt.event.dom.client.KeyDownHandler)
+     */
+    public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) {
+        return addDomHandler(handler, KeyDownEvent.getType());
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.google.gwt.event.dom.client.HasKeyPressHandlers#addKeyPressHandler
+     * (com.google.gwt.event.dom.client.KeyPressHandler)
+     */
+    public HandlerRegistration addKeyPressHandler(KeyPressHandler handler) {
+        return addDomHandler(handler, KeyPressEvent.getType());
+    }
+
+    /**
+     * Sets/Removes the keyboard focus to the panel.
+     * 
+     * @param focus
+     *            If set to true then the focus is moved to the panel, if set to
+     *            false the focus is removed
+     */
+    public void setFocus(boolean focus) {
+        if (focus) {
+            FocusImpl.getFocusImplForPanel().focus(getElement());
+        } else {
+            FocusImpl.getFocusImplForPanel().blur(getElement());
+        }
+    }
+
+    /**
+     * Focus the panel
+     */
+    public void focus() {
+        setFocus(true);
+    }
+}
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/HasTooltipKey.java b/client/src/com/vaadin/client/ui/calendar/schedule/HasTooltipKey.java
new file mode 100644 (file)
index 0000000..5827068
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule;
+
+/**
+ * For Calendar client-side internal use only.
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ * 
+ */
+public interface HasTooltipKey {
+    /**
+     * Gets the key associated for the Widget implementing this interface. This
+     * key is used for getting a tooltip title identified by the key
+     * 
+     * @return the tooltip key
+     */
+    Object getTooltipKey();
+}
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/MonthEventLabel.java b/client/src/com/vaadin/client/ui/calendar/schedule/MonthEventLabel.java
new file mode 100644 (file)
index 0000000..b7f6ee7
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule;
+
+import java.util.Date;
+
+import com.google.gwt.user.client.ui.HTML;
+import com.vaadin.client.ui.VCalendar;
+
+/**
+ * The label in a month cell
+ * 
+ * @since 7.1
+ */
+public class MonthEventLabel extends HTML implements HasTooltipKey {
+
+    private static final String STYLENAME = "v-calendar-event";
+
+    private boolean timeSpecificEvent = false;
+    private Integer eventIndex;
+    private VCalendar calendar;
+    private String caption;
+    private Date time;
+
+    /**
+     * Default constructor
+     */
+    public MonthEventLabel() {
+        setStylePrimaryName(STYLENAME);
+    }
+
+    /**
+     * Set the time of the event label
+     * 
+     * @param date
+     *            The date object that specifies the time
+     */
+    public void setTime(Date date) {
+        time = date;
+        renderCaption();
+    }
+
+    /**
+     * Set the caption of the event label
+     * 
+     * @param caption
+     *            The caption string, can be HTML
+     */
+    public void setCaption(String caption) {
+        this.caption = caption;
+        renderCaption();
+    }
+
+    /**
+     * Renders the caption in the DIV element
+     */
+    private void renderCaption() {
+        StringBuilder html = new StringBuilder();
+        if (caption != null && time != null) {
+            html.append("<span class=\"" + STYLENAME + "-time\">");
+            html.append(calendar.getTimeFormat().format(time));
+            html.append("</span> ");
+            html.append(caption);
+        } else if (caption != null) {
+            html.append(caption);
+        } else if (time != null) {
+            html.append("<span class=\"" + STYLENAME + "-time\">");
+            html.append(calendar.getTimeFormat().format(time));
+            html.append("</span>");
+        }
+        super.setHTML(html.toString());
+    }
+
+    /**
+     * Set the (server side) index of the event
+     * 
+     * @param index
+     *            The integer index
+     */
+    public void setEventIndex(int index) {
+        eventIndex = index;
+    }
+
+    /**
+     * Set the Calendar instance this label belongs to
+     * 
+     * @param calendar
+     *            The calendar instance
+     */
+    public void setCalendar(VCalendar calendar) {
+        this.calendar = calendar;
+    }
+
+    /**
+     * Is the event bound to a specific time
+     * 
+     * @return
+     */
+    public boolean isTimeSpecificEvent() {
+        return timeSpecificEvent;
+    }
+
+    /**
+     * Is the event bound to a specific time
+     * 
+     * @param timeSpecificEvent
+     *            True if the event is bound to a time, false if it is only
+     *            bound to the day
+     */
+    public void setTimeSpecificEvent(boolean timeSpecificEvent) {
+        this.timeSpecificEvent = timeSpecificEvent;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.google.gwt.user.client.ui.HTML#setHTML(java.lang.String)
+     */
+    @Override
+    public void setHTML(String html) {
+        throw new UnsupportedOperationException(
+                "Use setCaption() and setTime() instead");
+    }
+
+    @Override
+    public Object getTooltipKey() {
+        return eventIndex;
+    }
+}
\ No newline at end of file
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/MonthGrid.java b/client/src/com/vaadin/client/ui/calendar/schedule/MonthGrid.java
new file mode 100644 (file)
index 0000000..f5afd12
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule;
+
+import java.util.Date;
+
+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.shared.HandlerRegistration;
+import com.vaadin.client.ui.VCalendar;
+
+/**
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ * 
+ */
+public class MonthGrid extends FocusableGrid implements KeyDownHandler {
+
+    private SimpleDayCell selectionStart;
+    private SimpleDayCell selectionEnd;
+    private final VCalendar calendar;
+    private boolean rangeSelectDisabled;
+    private boolean disabled;
+    private boolean enabled = true;
+    private final HandlerRegistration keyDownHandler;
+
+    public MonthGrid(VCalendar parent, int rows, int columns) {
+        super(rows, columns);
+        calendar = parent;
+        setCellSpacing(0);
+        setCellPadding(0);
+        setStylePrimaryName("v-calendar-month");
+
+        keyDownHandler = addKeyDownHandler(this);
+    }
+
+    @Override
+    protected void onUnload() {
+        keyDownHandler.removeHandler();
+        super.onUnload();
+    }
+
+    public void setSelectionEnd(SimpleDayCell simpleDayCell) {
+        selectionEnd = simpleDayCell;
+        updateSelection();
+    }
+
+    public void setSelectionStart(SimpleDayCell simpleDayCell) {
+        if (!rangeSelectDisabled && isEnabled()) {
+            selectionStart = simpleDayCell;
+            setFocus(true);
+        }
+
+    }
+
+    private void updateSelection() {
+        if (selectionStart == null) {
+            return;
+        }
+        if (selectionStart != null && selectionEnd != null) {
+            Date startDate = selectionStart.getDate();
+            Date endDate = selectionEnd.getDate();
+            for (int row = 0; row < getRowCount(); row++) {
+                for (int cell = 0; cell < getCellCount(row); cell++) {
+                    SimpleDayCell sdc = (SimpleDayCell) getWidget(row, cell);
+                    if (sdc == null) {
+                        return;
+                    }
+                    Date d = sdc.getDate();
+                    if (startDate.compareTo(d) <= 0
+                            && endDate.compareTo(d) >= 0) {
+                        sdc.addStyleDependentName("selected");
+                    } else if (startDate.compareTo(d) >= 0
+                            && endDate.compareTo(d) <= 0) {
+                        sdc.addStyleDependentName("selected");
+                    } else {
+                        sdc.removeStyleDependentName("selected");
+                    }
+                }
+            }
+        }
+    }
+
+    public void setSelectionReady() {
+        if (selectionStart != null && selectionEnd != null) {
+            String value = "";
+            Date startDate = selectionStart.getDate();
+            Date endDate = selectionEnd.getDate();
+            if (startDate.compareTo(endDate) > 0) {
+                Date temp = startDate;
+                startDate = endDate;
+                endDate = temp;
+            }
+
+            if (calendar.getRangeSelectListener() != null) {
+                value = calendar.getDateFormat().format(startDate) + "TO"
+                        + calendar.getDateFormat().format(endDate);
+                calendar.getRangeSelectListener().rangeSelected(value);
+            }
+            selectionStart = null;
+            selectionEnd = null;
+            setFocus(false);
+        }
+    }
+
+    public void cancelRangeSelection() {
+        if (selectionStart != null && selectionEnd != null) {
+            for (int row = 0; row < getRowCount(); row++) {
+                for (int cell = 0; cell < getCellCount(row); cell++) {
+                    SimpleDayCell sdc = (SimpleDayCell) getWidget(row, cell);
+                    if (sdc == null) {
+                        return;
+                    }
+                    sdc.removeStyleDependentName("selected");
+                }
+            }
+        }
+        setFocus(false);
+        selectionStart = null;
+    }
+
+    public void updateCellSizes(int totalWidthPX, int totalHeightPX) {
+        boolean setHeight = totalHeightPX > 0;
+        boolean setWidth = totalWidthPX > 0;
+        int rows = getRowCount();
+        int cells = getCellCount(0);
+        int cellWidth = (totalWidthPX / cells) - 1;
+        int widthRemainder = totalWidthPX % cells;
+        // Division for cells might not be even. Distribute it evenly to
+        // will whole space.
+        int heightPX = totalHeightPX;
+        int cellHeight = heightPX / rows;
+        int heightRemainder = heightPX % rows;
+
+        for (int i = 0; i < rows; i++) {
+            for (int j = 0; j < cells; j++) {
+                SimpleDayCell sdc = (SimpleDayCell) getWidget(i, j);
+
+                if (setWidth) {
+                    if (widthRemainder > 0) {
+                        sdc.setWidth(cellWidth + 1 + "px");
+                        widthRemainder--;
+
+                    } else {
+                        sdc.setWidth(cellWidth + "px");
+                    }
+                }
+
+                if (setHeight) {
+                    if (heightRemainder > 0) {
+                        sdc.setHeightPX(cellHeight + 1, true);
+
+                    } else {
+                        sdc.setHeightPX(cellHeight, true);
+                    }
+                } else {
+                    sdc.setHeightPX(-1, true);
+                }
+            }
+            heightRemainder--;
+        }
+    }
+
+    /**
+     * Disable or enable possibility to select ranges
+     */
+    public void setRangeSelect(boolean b) {
+        rangeSelectDisabled = !b;
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public void onKeyDown(KeyDownEvent event) {
+        int keycode = event.getNativeKeyCode();
+        if (KeyCodes.KEY_ESCAPE == keycode && selectionStart != null) {
+            cancelRangeSelection();
+        }
+    }
+
+    public int getDayCellIndex(SimpleDayCell dayCell) {
+        int rows = getRowCount();
+        int cells = getCellCount(0);
+        for (int i = 0; i < rows; i++) {
+            for (int j = 0; j < cells; j++) {
+                SimpleDayCell sdc = (SimpleDayCell) getWidget(i, j);
+                if (dayCell == sdc) {
+                    return i * cells + j;
+                }
+            }
+        }
+
+        return -1;
+    }
+}
\ No newline at end of file
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/SimpleDayCell.java b/client/src/com/vaadin/client/ui/calendar/schedule/SimpleDayCell.java
new file mode 100644 (file)
index 0000000..8d1ca0f
--- /dev/null
@@ -0,0 +1,696 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule;
+
+import java.util.Date;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.NativeEvent;
+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.MouseDownEvent;
+import com.google.gwt.event.dom.client.MouseDownHandler;
+import com.google.gwt.event.dom.client.MouseMoveEvent;
+import com.google.gwt.event.dom.client.MouseMoveHandler;
+import com.google.gwt.event.dom.client.MouseOverEvent;
+import com.google.gwt.event.dom.client.MouseOverHandler;
+import com.google.gwt.event.dom.client.MouseUpEvent;
+import com.google.gwt.event.dom.client.MouseUpHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.client.ui.FocusableFlowPanel;
+import com.vaadin.client.ui.VCalendar;
+import com.vaadin.shared.ui.calendar.DateConstants;
+
+/**
+ * A class representing a single cell within the calendar in month-view
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ */
+public class SimpleDayCell extends FocusableFlowPanel implements
+        MouseUpHandler, MouseDownHandler, MouseOverHandler, MouseMoveHandler {
+
+    private static int BOTTOMSPACERHEIGHT = -1;
+    private static int EVENTHEIGHT = -1;
+    private static final int BORDERPADDINGSIZE = 1;
+
+    private final VCalendar calendar;
+    private Date date;
+    private int intHeight;
+    private final HTML bottomspacer;
+    private final Label caption;
+    private final CalendarEvent[] events = new CalendarEvent[10];
+    private final int cell;
+    private final int row;
+    private boolean monthNameVisible;
+    private HandlerRegistration mouseUpRegistration;
+    private HandlerRegistration mouseDownRegistration;
+    private HandlerRegistration mouseOverRegistration;
+    private boolean monthEventMouseDown;
+    private boolean labelMouseDown;
+    private int eventCount = 0;
+
+    private int startX = -1;
+    private int startY = -1;
+    private int startYrelative;
+    private int startXrelative;
+    private Date startDateFrom;
+    private Date startDateTo;
+    private int prevDayDiff = 0;
+    private int prevWeekDiff = 0;
+    private HandlerRegistration moveRegistration;
+    private CalendarEvent moveEvent;
+    private Widget clickedWidget;
+    private HandlerRegistration bottomSpacerMouseDownHandler;
+    private boolean scrollable = false;
+    private boolean eventCanceled;
+    private MonthGrid monthGrid;
+    private HandlerRegistration keyDownHandler;
+
+    public SimpleDayCell(VCalendar calendar, int row, int cell) {
+        this.calendar = calendar;
+        this.row = row;
+        this.cell = cell;
+        setStylePrimaryName("v-calendar-month-day");
+        caption = new Label();
+        bottomspacer = new HTML();
+        bottomspacer.setStyleName("v-calendar-bottom-spacer-empty");
+        bottomspacer.setWidth(3 + "em");
+        caption.setStyleName("v-calendar-day-number");
+        add(caption);
+        add(bottomspacer);
+        caption.addMouseDownHandler(this);
+        caption.addMouseUpHandler(this);
+    }
+
+    @Override
+    public void onLoad() {
+        BOTTOMSPACERHEIGHT = bottomspacer.getOffsetHeight();
+        EVENTHEIGHT = BOTTOMSPACERHEIGHT;
+    }
+
+    public void setMonthGrid(MonthGrid monthGrid) {
+        this.monthGrid = monthGrid;
+    }
+
+    public MonthGrid getMonthGrid() {
+        return monthGrid;
+    }
+
+    @SuppressWarnings("deprecation")
+    public void setDate(Date date) {
+        int dateOfMonth = date.getDate();
+        if (monthNameVisible) {
+            caption.setText(dateOfMonth + " "
+                    + calendar.getMonthNames()[date.getMonth()]);
+        } else {
+            caption.setText("" + dateOfMonth);
+        }
+        this.date = date;
+    }
+
+    public Date getDate() {
+        return date;
+    }
+
+    public void reDraw(boolean clear) {
+        setHeightPX(intHeight + BORDERPADDINGSIZE, clear);
+    }
+
+    /*
+     * Events and whole cell content are drawn by this method. By the
+     * clear-argument, you can choose to clear all old content. Notice that
+     * clearing will also remove all element's event handlers.
+     */
+    public void setHeightPX(int px, boolean clear) {
+        // measure from DOM if needed
+        if (px < 0) {
+            intHeight = getOffsetHeight() - BORDERPADDINGSIZE;
+        } else {
+            intHeight = px - BORDERPADDINGSIZE;
+        }
+
+        // Couldn't measure height or it ended up negative. Don't bother
+        // continuing
+        if (intHeight == -1) {
+            return;
+        }
+
+        if (clear) {
+            while (getWidgetCount() > 1) {
+                remove(1);
+            }
+        }
+
+        // How many events can be shown in UI
+        int slots = 0;
+        if (scrollable) {
+            for (int i = 0; i < events.length; i++) {
+                if (events[i] != null) {
+                    slots = i + 1;
+                }
+            }
+            setHeight(intHeight + "px"); // Fixed height
+        } else {
+            // Dynamic height by the content
+            DOM.removeElementAttribute(getElement(), "height");
+            slots = (intHeight - caption.getOffsetHeight() - BOTTOMSPACERHEIGHT)
+                    / EVENTHEIGHT;
+            if (slots > 10) {
+                slots = 10;
+            }
+        }
+
+        updateEvents(slots, clear);
+
+    }
+
+    public void updateEvents(int slots, boolean clear) {
+        int eventsAdded = 0;
+
+        for (int i = 0; i < slots; i++) {
+            CalendarEvent e = events[i];
+            if (e == null) {
+                // Empty slot
+                HTML slot = new HTML();
+                slot.setStyleName("v-calendar-spacer");
+                if (!clear) {
+                    remove(i + 1);
+                    insert(slot, i + 1);
+                } else {
+                    add(slot);
+                }
+            } else {
+                // Event slot
+                eventsAdded++;
+                if (!clear) {
+                    Widget w = getWidget(i + 1);
+                    if (!(w instanceof MonthEventLabel)) {
+                        remove(i + 1);
+                        insert(createMonthEventLabel(e), i + 1);
+                    }
+                } else {
+                    add(createMonthEventLabel(e));
+                }
+            }
+        }
+
+        int remainingSpace = intHeight
+                - ((slots * EVENTHEIGHT) + BOTTOMSPACERHEIGHT + caption
+                        .getOffsetHeight());
+        int newHeight = remainingSpace + BOTTOMSPACERHEIGHT;
+        if (newHeight < 0) {
+            newHeight = EVENTHEIGHT;
+        }
+        bottomspacer.setHeight(newHeight + "px");
+
+        if (clear) {
+            add(bottomspacer);
+        }
+
+        int more = eventCount - eventsAdded;
+        if (more > 0) {
+            if (bottomSpacerMouseDownHandler == null) {
+                bottomSpacerMouseDownHandler = bottomspacer
+                        .addMouseDownHandler(this);
+            }
+            bottomspacer.setStyleName("v-calendar-bottom-spacer");
+            bottomspacer.setText("+ " + more);
+        } else {
+            if (!scrollable && bottomSpacerMouseDownHandler != null) {
+                bottomSpacerMouseDownHandler.removeHandler();
+                bottomSpacerMouseDownHandler = null;
+            }
+
+            if (scrollable) {
+                bottomspacer.setText("[ - ]");
+            } else {
+                bottomspacer.setStyleName("v-calendar-bottom-spacer-empty");
+                bottomspacer.setText("");
+            }
+        }
+    }
+
+    private MonthEventLabel createMonthEventLabel(CalendarEvent e) {
+        long rangeInMillis = e.getRangeInMilliseconds();
+        boolean timeEvent = rangeInMillis <= DateConstants.DAYINMILLIS
+                && !e.isAllDay();
+        Date fromDatetime = e.getStartTime();
+
+        // Create a new MonthEventLabel
+        MonthEventLabel eventDiv = new MonthEventLabel();
+        eventDiv.addStyleDependentName("month");
+        eventDiv.addMouseDownHandler(this);
+        eventDiv.addMouseUpHandler(this);
+        eventDiv.setCalendar(calendar);
+        eventDiv.setEventIndex(e.getIndex());
+
+        if (timeEvent) {
+            eventDiv.setTimeSpecificEvent(true);
+            if (e.getStyleName() != null) {
+                eventDiv.addStyleDependentName(e.getStyleName());
+            }
+            eventDiv.setCaption(e.getCaption());
+            eventDiv.setTime(fromDatetime);
+
+        } else {
+            eventDiv.setTimeSpecificEvent(false);
+            Date from = e.getStart();
+            Date to = e.getEnd();
+            if (e.getStyleName().length() > 0) {
+                eventDiv.addStyleName("month-event " + e.getStyleName());
+            } else {
+                eventDiv.addStyleName("month-event");
+            }
+            int fromCompareToDate = from.compareTo(date);
+            int toCompareToDate = to.compareTo(date);
+            eventDiv.addStyleDependentName("all-day");
+            if (fromCompareToDate == 0) {
+                eventDiv.addStyleDependentName("start");
+                eventDiv.setCaption(e.getCaption());
+
+            } else if (fromCompareToDate < 0 && cell == 0) {
+                eventDiv.addStyleDependentName("continued-from");
+                eventDiv.setCaption(e.getCaption());
+            }
+            if (toCompareToDate == 0) {
+                eventDiv.addStyleDependentName("end");
+            } else if (toCompareToDate > 0
+                    && (cell + 1) == getMonthGrid().getCellCount(row)) {
+                eventDiv.addStyleDependentName("continued-to");
+            }
+            if (e.getStyleName() != null) {
+                eventDiv.addStyleDependentName(e.getStyleName() + "-all-day");
+            }
+        }
+
+        return eventDiv;
+    }
+
+    private void setUnlimitedCellHeight() {
+        scrollable = true;
+        addStyleDependentName("scrollable");
+    }
+
+    private void setLimitedCellHeight() {
+        scrollable = false;
+        removeStyleDependentName("scrollable");
+    }
+
+    public void addCalendarEvent(CalendarEvent e) {
+        eventCount++;
+        int slot = e.getSlotIndex();
+        if (slot == -1) {
+            for (int i = 0; i < events.length; i++) {
+                if (events[i] == null) {
+                    events[i] = e;
+                    e.setSlotIndex(i);
+                    break;
+                }
+            }
+        } else {
+            events[slot] = e;
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public void setMonthNameVisible(boolean b) {
+        monthNameVisible = b;
+        int dateOfMonth = date.getDate();
+        caption.setText(dateOfMonth + " "
+                + calendar.getMonthNames()[date.getMonth()]);
+    }
+
+    public HandlerRegistration addMouseMoveHandler(MouseMoveHandler handler) {
+        return addDomHandler(handler, MouseMoveEvent.getType());
+    }
+
+    @Override
+    protected void onAttach() {
+        super.onAttach();
+        mouseUpRegistration = addDomHandler(this, MouseUpEvent.getType());
+        mouseDownRegistration = addDomHandler(this, MouseDownEvent.getType());
+        mouseOverRegistration = addDomHandler(this, MouseOverEvent.getType());
+    }
+
+    @Override
+    protected void onDetach() {
+        mouseUpRegistration.removeHandler();
+        mouseDownRegistration.removeHandler();
+        mouseOverRegistration.removeHandler();
+        super.onDetach();
+    }
+
+    public void onMouseUp(MouseUpEvent event) {
+        if (event.getNativeButton() != NativeEvent.BUTTON_LEFT) {
+            return;
+        }
+
+        Widget w = (Widget) event.getSource();
+        if (moveRegistration != null) {
+            Event.releaseCapture(getElement());
+            moveRegistration.removeHandler();
+            moveRegistration = null;
+            keyDownHandler.removeHandler();
+            keyDownHandler = null;
+        }
+
+        if (w == bottomspacer && monthEventMouseDown) {
+            GWT.log("Mouse up over bottomspacer");
+
+        } else if (clickedWidget instanceof MonthEventLabel
+                && monthEventMouseDown) {
+            MonthEventLabel mel = (MonthEventLabel) clickedWidget;
+
+            int endX = event.getClientX();
+            int endY = event.getClientY();
+            int xDiff = startX - endX;
+            int yDiff = startY - endY;
+            startX = -1;
+            startY = -1;
+            prevDayDiff = 0;
+            prevWeekDiff = 0;
+
+            if (!mel.isTimeSpecificEvent()
+                    && (xDiff < -3 || xDiff > 3 || yDiff < -3 || yDiff > 3)) {
+                eventMoved(moveEvent);
+
+            } else if (calendar.getEventClickListener() != null) {
+                CalendarEvent e = getEventByWidget(mel);
+                calendar.getEventClickListener().eventClick(e);
+            }
+
+            moveEvent = null;
+        } else if (w == this) {
+            getMonthGrid().setSelectionReady();
+
+        } else if (w instanceof Label && labelMouseDown) {
+            String clickedDate = calendar.getDateFormat().format(date);
+            if (calendar.getDateClickListener() != null) {
+                calendar.getDateClickListener().dateClick(clickedDate);
+            }
+        }
+        monthEventMouseDown = false;
+        labelMouseDown = false;
+        clickedWidget = null;
+    }
+
+    public void onMouseDown(MouseDownEvent event) {
+        if (calendar.isDisabled()
+                || event.getNativeButton() != NativeEvent.BUTTON_LEFT) {
+            return;
+        }
+
+        Widget w = (Widget) event.getSource();
+        clickedWidget = w;
+
+        if (w instanceof MonthEventLabel) {
+            // event clicks should be allowed even when read-only
+            monthEventMouseDown = true;
+
+            if (w instanceof MonthEventLabel) {
+                startCalendarEventDrag(event, (MonthEventLabel) w);
+            }
+        } else if (!calendar.isReadOnly()) {
+            // these are not allowed when in read-only
+            if (w == bottomspacer) {
+                if (scrollable) {
+                    setLimitedCellHeight();
+                } else {
+                    setUnlimitedCellHeight();
+                }
+                reDraw(true);
+
+            } else if (w == this && !scrollable) {
+                MonthGrid grid = getMonthGrid();
+                if (grid.isEnabled() && calendar.isRangeSelectAllowed()) {
+                    grid.setSelectionStart(this);
+                    grid.setSelectionEnd(this);
+                }
+            } else if (w instanceof Label) {
+                labelMouseDown = true;
+            }
+        }
+
+        event.stopPropagation();
+        event.preventDefault();
+    }
+
+    public void onMouseOver(MouseOverEvent event) {
+        event.preventDefault();
+        getMonthGrid().setSelectionEnd(this);
+    }
+
+    public void onMouseMove(MouseMoveEvent event) {
+        if (clickedWidget instanceof MonthEventLabel && !monthEventMouseDown
+                || (startY < 0 && startX < 0)) {
+            return;
+        }
+
+        MonthEventLabel w = (MonthEventLabel) clickedWidget;
+
+        if (calendar.isDisabledOrReadOnly()) {
+            Event.releaseCapture(getElement());
+            monthEventMouseDown = false;
+            startY = -1;
+            startX = -1;
+            return;
+        }
+
+        int currentY = event.getClientY();
+        int currentX = event.getClientX();
+        int moveY = (currentY - startY);
+        int moveX = (currentX - startX);
+        if ((moveY < 5 && moveY > -6) && (moveX < 5 && moveX > -6)) {
+            return;
+        }
+
+        int dateCellWidth = getWidth();
+        int dateCellHeigth = getHeigth();
+
+        Element parent = getMonthGrid().getElement();
+        int relativeX = event.getRelativeX(parent);
+        int relativeY = event.getRelativeY(parent);
+        int weekDiff = 0;
+        if (moveY > 0) {
+            weekDiff = (startYrelative + moveY) / dateCellHeigth;
+        } else {
+            weekDiff = (moveY - (dateCellHeigth - startYrelative))
+                    / dateCellHeigth;
+        }
+
+        int dayDiff = 0;
+        if (moveX >= 0) {
+            dayDiff = (startXrelative + moveX) / dateCellWidth;
+        } else {
+            dayDiff = (moveX - (dateCellWidth - startXrelative))
+                    / dateCellWidth;
+        }
+        // Check boundaries
+        if (relativeY < 0
+                || relativeY >= (calendar.getMonthGrid().getRowCount() * dateCellHeigth)
+                || relativeX < 0
+                || relativeX >= (calendar.getMonthGrid().getColumnCount() * dateCellWidth)) {
+            return;
+        }
+
+        GWT.log("Event moving delta: " + weekDiff + " weeks " + dayDiff
+                + " days" + " (" + getCell() + "," + getRow() + ")");
+
+        CalendarEvent e = moveEvent;
+        if (e == null) {
+            e = getEventByWidget(w);
+        }
+
+        Date from = e.getStart();
+        Date to = e.getEnd();
+        long duration = to.getTime() - from.getTime();
+
+        long daysMs = dayDiff * DateConstants.DAYINMILLIS;
+        long weeksMs = weekDiff * DateConstants.WEEKINMILLIS;
+        from.setTime(startDateFrom.getTime() + weeksMs + daysMs);
+        to.setTime((from.getTime() + duration));
+        e.setStart(from);
+        e.setEnd(to);
+        e.setStartTime(new Date(from.getTime()));
+        e.setEndTime(new Date(to.getTime()));
+
+        updateDragPosition(w, dayDiff, weekDiff);
+    }
+
+    private void eventMoved(CalendarEvent e) {
+        calendar.updateEventToMonthGrid(e);
+        if (calendar.getEventMovedListener() != null) {
+            calendar.getEventMovedListener().eventMoved(e);
+        }
+    }
+
+    public void startCalendarEventDrag(MouseDownEvent event,
+            final MonthEventLabel w) {
+        if (w.isTimeSpecificEvent()) {
+            return;
+        }
+
+        moveRegistration = addMouseMoveHandler(this);
+        startX = event.getClientX();
+        startY = event.getClientY();
+        startYrelative = event.getRelativeY(w.getParent().getElement())
+                % getHeigth();
+        startXrelative = event.getRelativeX(w.getParent().getElement())
+                % getWidth();
+
+        CalendarEvent e = getEventByWidget(w);
+        startDateFrom = (Date) e.getStart().clone();
+        startDateTo = (Date) e.getEnd().clone();
+
+        Event.setCapture(getElement());
+        keyDownHandler = addKeyDownHandler(new KeyDownHandler() {
+
+            public void onKeyDown(KeyDownEvent event) {
+                if (event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) {
+                    cancelEventDrag(w);
+                }
+            }
+
+        });
+
+        focus();
+
+        GWT.log("Start drag");
+    }
+
+    protected void cancelEventDrag(MonthEventLabel w) {
+        if (moveRegistration != null) {
+            // reset position
+            if (moveEvent == null) {
+                moveEvent = getEventByWidget(w);
+            }
+
+            moveEvent.setStart(startDateFrom);
+            moveEvent.setEnd(startDateTo);
+            calendar.updateEventToMonthGrid(moveEvent);
+
+            // reset drag-related properties
+            Event.releaseCapture(getElement());
+            moveRegistration.removeHandler();
+            moveRegistration = null;
+            keyDownHandler.removeHandler();
+            keyDownHandler = null;
+            setFocus(false);
+            monthEventMouseDown = false;
+            startY = -1;
+            startX = -1;
+            moveEvent = null;
+            labelMouseDown = false;
+            clickedWidget = null;
+        }
+    }
+
+    public void updateDragPosition(MonthEventLabel w, int dayDiff, int weekDiff) {
+        // Draw event to its new position only when position has changed
+        if (dayDiff == prevDayDiff && weekDiff == prevWeekDiff) {
+            return;
+        }
+
+        prevDayDiff = dayDiff;
+        prevWeekDiff = weekDiff;
+
+        if (moveEvent == null) {
+            moveEvent = getEventByWidget(w);
+        }
+
+        calendar.updateEventToMonthGrid(moveEvent);
+    }
+
+    public int getRow() {
+        return row;
+    }
+
+    public int getCell() {
+        return cell;
+    }
+
+    public int getHeigth() {
+        return intHeight + BORDERPADDINGSIZE;
+    }
+
+    public int getWidth() {
+        return getOffsetWidth() - BORDERPADDINGSIZE;
+    }
+
+    public void setToday(boolean today) {
+        if (today) {
+            addStyleDependentName("today");
+        } else {
+            removeStyleDependentName("today");
+        }
+    }
+
+    public boolean removeEvent(CalendarEvent targetEvent,
+            boolean reDrawImmediately) {
+        int slot = targetEvent.getSlotIndex();
+        if (slot < 0) {
+            return false;
+        }
+
+        CalendarEvent e = getCalendarEvent(slot);
+        if (targetEvent.equals(e)) {
+            events[slot] = null;
+            eventCount--;
+            if (reDrawImmediately) {
+                reDraw(moveEvent == null);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    private CalendarEvent getEventByWidget(MonthEventLabel eventWidget) {
+        int index = getWidgetIndex(eventWidget);
+        return getCalendarEvent(index - 1);
+    }
+
+    public CalendarEvent getCalendarEvent(int i) {
+        return events[i];
+    }
+
+    public CalendarEvent[] getEvents() {
+        return events;
+    }
+
+    public int getEventCount() {
+        return eventCount;
+    }
+
+    public CalendarEvent getMoveEvent() {
+        return moveEvent;
+    }
+
+    public void addEmphasisStyle() {
+        addStyleDependentName("dragemphasis");
+    }
+
+    public void removeEmphasisStyle() {
+        removeStyleDependentName("dragemphasis");
+    }
+}
\ No newline at end of file
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/SimpleDayToolbar.java b/client/src/com/vaadin/client/ui/calendar/schedule/SimpleDayToolbar.java
new file mode 100644 (file)
index 0000000..fc75136
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule;
+
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * 
+ * @since 7.1.0
+ * @author Vaadin Ltd.
+ * 
+ */
+public class SimpleDayToolbar extends HorizontalPanel {
+    private int width = 0;
+    private boolean isWidthUndefined = false;
+
+    public SimpleDayToolbar() {
+        setStylePrimaryName("v-calendar-header-month");
+    }
+
+    public void setDayNames(String[] dayNames) {
+        clear();
+        for (int i = 0; i < dayNames.length; i++) {
+            Label l = new Label(dayNames[i]);
+            l.setStylePrimaryName("v-calendar-header-day");
+            add(l);
+        }
+        updateCellWidth();
+    }
+
+    public void setWidthPX(int width) {
+        this.width = width;
+
+        setWidthUndefined(width == -1);
+
+        if (!isWidthUndefined()) {
+            super.setWidth(this.width + "px");
+            if (getWidgetCount() == 0) {
+                return;
+            }
+        }
+        updateCellWidth();
+    }
+
+    private boolean isWidthUndefined() {
+        return isWidthUndefined;
+    }
+
+    private void setWidthUndefined(boolean isWidthUndefined) {
+        this.isWidthUndefined = isWidthUndefined;
+
+        if (isWidthUndefined) {
+            addStyleDependentName("Hsized");
+
+        } else {
+            removeStyleDependentName("Hsized");
+        }
+    }
+
+    private void updateCellWidth() {
+        int cellw = -1;
+        int widgetCount = getWidgetCount();
+        if (widgetCount <= 0) {
+            return;
+        }
+        if (isWidthUndefined()) {
+            Widget widget = getWidget(0);
+            String w = widget.getElement().getStyle().getWidth();
+            if (w.length() > 2) {
+                cellw = Integer.parseInt(w.substring(0, w.length() - 2));
+            }
+        } else {
+            cellw = width / getWidgetCount();
+        }
+        if (cellw > 0) {
+            for (int i = 0; i < getWidgetCount(); i++) {
+                Widget widget = getWidget(i);
+                setCellWidth(widget, cellw + "px");
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/SimpleWeekToolbar.java b/client/src/com/vaadin/client/ui/calendar/schedule/SimpleWeekToolbar.java
new file mode 100644 (file)
index 0000000..f86ba03
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule;
+
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.ui.FlexTable;
+import com.vaadin.client.ui.VCalendar;
+
+/**
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ * 
+ */
+public class SimpleWeekToolbar extends FlexTable implements ClickHandler {
+    private int height;
+    private VCalendar calendar;
+    private boolean isHeightUndefined;
+
+    public SimpleWeekToolbar(VCalendar parent) {
+        calendar = parent;
+        setCellSpacing(0);
+        setCellPadding(0);
+        setStyleName("v-calendar-week-numbers");
+    }
+
+    public void addWeek(int week, int year) {
+        WeekLabel l = new WeekLabel(week + "", week, year);
+        l.addClickHandler(this);
+        int rowCount = getRowCount();
+        insertRow(rowCount);
+        setWidget(rowCount, 0, l);
+        updateCellHeights();
+    }
+
+    public void updateCellHeights() {
+        if (!isHeightUndefined()) {
+            int rowCount = getRowCount();
+            if (rowCount == 0) {
+                return;
+            }
+            int cellheight = (height / rowCount) - 1;
+            int remainder = height % rowCount;
+            if (cellheight < 0) {
+                cellheight = 0;
+            }
+            for (int i = 0; i < rowCount; i++) {
+                if (remainder > 0) {
+                    getWidget(i, 0).setHeight(cellheight + 1 + "px");
+                } else {
+                    getWidget(i, 0).setHeight(cellheight + "px");
+                }
+                getWidget(i, 0).getElement().getStyle()
+                        .setProperty("lineHeight", cellheight + "px");
+                remainder--;
+            }
+        } else {
+            for (int i = 0; i < getRowCount(); i++) {
+                getWidget(i, 0).setHeight("");
+                getWidget(i, 0).getElement().getStyle()
+                        .setProperty("lineHeight", "");
+            }
+        }
+    }
+
+    public void setHeightPX(int intHeight) {
+        setHeightUndefined(intHeight == -1);
+        height = intHeight;
+        updateCellHeights();
+    }
+
+    public boolean isHeightUndefined() {
+        return isHeightUndefined;
+    }
+
+    public void setHeightUndefined(boolean isHeightUndefined) {
+        this.isHeightUndefined = isHeightUndefined;
+
+        if (isHeightUndefined) {
+            addStyleDependentName("Vsized");
+
+        } else {
+            removeStyleDependentName("Vsized");
+        }
+    }
+
+    public void onClick(ClickEvent event) {
+        WeekLabel wl = (WeekLabel) event.getSource();
+        if (calendar.getWeekClickListener() != null) {
+            calendar.getWeekClickListener().weekClick(
+                    wl.getYear() + "w" + wl.getWeek());
+        }
+    }
+}
\ No newline at end of file
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/WeekGrid.java b/client/src/com/vaadin/client/ui/calendar/schedule/WeekGrid.java
new file mode 100644 (file)
index 0000000..c5646f9
--- /dev/null
@@ -0,0 +1,677 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule;
+
+import java.util.Arrays;
+import java.util.Date;
+
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.Style;
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.event.dom.client.ScrollEvent;
+import com.google.gwt.event.dom.client.ScrollHandler;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.ScrollPanel;
+import com.google.gwt.user.client.ui.SimplePanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.client.DateTimeService;
+import com.vaadin.client.Util;
+import com.vaadin.client.ui.VCalendar;
+
+/**
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ * 
+ */
+public class WeekGrid extends SimplePanel {
+
+    int width = 0;
+    private int height = 0;
+    final HorizontalPanel content;
+    private VCalendar calendar;
+    private boolean disabled;
+    final Timebar timebar;
+    private Panel wrapper;
+    private boolean verticalScrollEnabled;
+    private boolean horizontalScrollEnabled;
+    private int[] cellHeights;
+    private final int slotInMinutes = 30;
+    private int dateCellBorder;
+    private DateCell dateCellOfToday;
+    private int[] cellWidths;
+    private int firstHour;
+    private int lastHour;
+
+    public WeekGrid(VCalendar parent, boolean format24h) {
+        setCalendar(parent);
+        content = new HorizontalPanel();
+        timebar = new Timebar(format24h);
+        content.add(timebar);
+
+        wrapper = new SimplePanel();
+        wrapper.setStylePrimaryName("v-calendar-week-wrapper");
+        wrapper.add(content);
+
+        setWidget(wrapper);
+    }
+
+    private void setVerticalScroll(boolean isVerticalScrollEnabled) {
+        if (isVerticalScrollEnabled && !(isVerticalScrollable())) {
+            verticalScrollEnabled = true;
+            horizontalScrollEnabled = false;
+            wrapper.remove(content);
+
+            final ScrollPanel scrollPanel = new ScrollPanel();
+            scrollPanel.setStylePrimaryName("v-calendar-week-wrapper");
+            scrollPanel.setWidget(content);
+
+            scrollPanel.addScrollHandler(new ScrollHandler() {
+                public void onScroll(ScrollEvent event) {
+                    if (calendar.getScrollListener() != null) {
+                        calendar.getScrollListener().scroll(
+                                scrollPanel.getVerticalScrollPosition());
+                    }
+                }
+            });
+
+            setWidget(scrollPanel);
+            wrapper = scrollPanel;
+
+        } else if (!isVerticalScrollEnabled && (isVerticalScrollable())) {
+            verticalScrollEnabled = false;
+            horizontalScrollEnabled = false;
+            wrapper.remove(content);
+
+            SimplePanel simplePanel = new SimplePanel();
+            simplePanel.setStylePrimaryName("v-calendar-week-wrapper");
+            simplePanel.setWidget(content);
+
+            setWidget(simplePanel);
+            wrapper = simplePanel;
+        }
+    }
+
+    public void setVerticalScrollPosition(int verticalScrollPosition) {
+        if (isVerticalScrollable()) {
+            ((ScrollPanel) wrapper)
+                    .setVerticalScrollPosition(verticalScrollPosition);
+        }
+    }
+
+    public int getInternalWidth() {
+        return width;
+    }
+
+    public void addDate(Date d) {
+        final DateCell dc = new DateCell(this, d);
+        dc.setDisabled(isDisabled());
+        dc.setHorizontalSized(isHorizontalScrollable() || width < 0);
+        dc.setVerticalSized(isVerticalScrollable());
+        content.add(dc);
+    }
+
+    /**
+     * @param dateCell
+     * @return get the index of the given date cell in this week, starting from
+     *         0
+     */
+    public int getDateCellIndex(DateCell dateCell) {
+        return content.getWidgetIndex(dateCell) - 1;
+    }
+
+    /**
+     * @return get the slot border in pixels
+     */
+    public int getDateSlotBorder() {
+        return ((DateCell) content.getWidget(1)).getSlotBorder();
+    }
+
+    private boolean isVerticalScrollable() {
+        return verticalScrollEnabled;
+    }
+
+    private boolean isHorizontalScrollable() {
+        return horizontalScrollEnabled;
+    }
+
+    public void setWidthPX(int width) {
+        if (isHorizontalScrollable()) {
+            updateCellWidths();
+
+            // Otherwise the scroll wrapper is somehow too narrow = horizontal
+            // scroll
+            wrapper.setWidth(content.getOffsetWidth()
+                    + Util.getNativeScrollbarSize() + "px");
+
+            this.width = content.getOffsetWidth() - timebar.getOffsetWidth();
+
+        } else {
+            this.width = (width == -1) ? width : width
+                    - timebar.getOffsetWidth();
+
+            if (isVerticalScrollable() && width != -1) {
+                this.width = this.width - Util.getNativeScrollbarSize();
+            }
+            updateCellWidths();
+        }
+    }
+
+    public void setHeightPX(int intHeight) {
+        height = intHeight;
+
+        setVerticalScroll(height <= -1);
+
+        // if not scrollable, use any height given
+        if (!isVerticalScrollable() && height > 0) {
+
+            content.setHeight(height + "px");
+            setHeight(height + "px");
+            wrapper.setHeight(height + "px");
+            wrapper.removeStyleDependentName("Vsized");
+            updateCellHeights();
+            timebar.setCellHeights(cellHeights);
+            timebar.setHeightPX(height);
+
+        } else if (isVerticalScrollable()) {
+            updateCellHeights();
+            wrapper.addStyleDependentName("Vsized");
+            timebar.setCellHeights(cellHeights);
+            timebar.setHeightPX(height);
+        }
+    }
+
+    public void clearDates() {
+        while (content.getWidgetCount() > 1) {
+            content.remove(1);
+        }
+
+        dateCellOfToday = null;
+    }
+
+    /**
+     * @return true if this weekgrid contains a date that is today
+     */
+    public boolean hasToday() {
+        return dateCellOfToday != null;
+    }
+
+    public void updateCellWidths() {
+        if (!isHorizontalScrollable() && width != -1) {
+            int count = content.getWidgetCount();
+            int datesWidth = width;
+            if (datesWidth > 0 && count > 1) {
+                cellWidths = VCalendar
+                        .distributeSize(datesWidth, count - 1, -1);
+
+                for (int i = 1; i < count; i++) {
+                    DateCell dc = (DateCell) content.getWidget(i);
+                    dc.setHorizontalSized(isHorizontalScrollable() || width < 0);
+                    dc.setWidthPX(cellWidths[i - 1]);
+                    if (dc.isToday()) {
+                        dc.setTimeBarWidth(getOffsetWidth());
+                    }
+                }
+            }
+
+        } else {
+            int count = content.getWidgetCount();
+            if (count > 1) {
+                for (int i = 1; i < count; i++) {
+                    DateCell dc = (DateCell) content.getWidget(i);
+                    dc.setHorizontalSized(isHorizontalScrollable() || width < 0);
+                }
+            }
+        }
+    }
+
+    /**
+     * @return an int-array containing the widths of the cells (days)
+     */
+    public int[] getDateCellWidths() {
+        return cellWidths;
+    }
+
+    public void updateCellHeights() {
+        if (!isVerticalScrollable()) {
+            int count = content.getWidgetCount();
+            if (count > 1) {
+                DateCell first = (DateCell) content.getWidget(1);
+                dateCellBorder = first.getSlotBorder();
+                cellHeights = VCalendar.distributeSize(height,
+                        first.getNumberOfSlots(), -dateCellBorder);
+                for (int i = 1; i < count; i++) {
+                    DateCell dc = (DateCell) content.getWidget(i);
+                    dc.setHeightPX(height, cellHeights);
+                }
+            }
+
+        } else {
+            int count = content.getWidgetCount();
+            if (count > 1) {
+                DateCell first = (DateCell) content.getWidget(1);
+                dateCellBorder = first.getSlotBorder();
+                int dateHeight = (first.getOffsetHeight() / first
+                        .getNumberOfSlots()) - dateCellBorder;
+                cellHeights = new int[48];
+                Arrays.fill(cellHeights, dateHeight);
+
+                for (int i = 1; i < count; i++) {
+                    DateCell dc = (DateCell) content.getWidget(i);
+                    dc.setVerticalSized(isVerticalScrollable());
+                }
+            }
+        }
+    }
+
+    public void addEvent(CalendarEvent e) {
+        int dateCount = content.getWidgetCount();
+        Date from = e.getStart();
+        Date toTime = e.getEndTime();
+        for (int i = 1; i < dateCount; i++) {
+            DateCell dc = (DateCell) content.getWidget(i);
+            Date dcDate = dc.getDate();
+            int comp = dcDate.compareTo(from);
+            int comp2 = dcDate.compareTo(toTime);
+            if (comp >= 0
+                    && comp2 < 0
+                    || (comp == 0 && comp2 == 0 && VCalendar
+                            .isZeroLengthMidnightEvent(e))) {
+                // Same event may be over two DateCells if event's date
+                // range floats over one day. It can't float over two days,
+                // because event which range is over 24 hours, will be handled
+                // as a "fullDay" event.
+                dc.addEvent(dcDate, e);
+            }
+        }
+    }
+
+    public int getPixelLengthFor(int startFromMinutes, int durationInMinutes) {
+        int pixelLength = 0;
+        int currentSlot = 0;
+
+        int firstHourInMinutes = firstHour * 60;
+
+        if (firstHourInMinutes > startFromMinutes) {
+            startFromMinutes = 0;
+        } else {
+            startFromMinutes -= firstHourInMinutes;
+        }
+
+        // calculate full slots to event
+        int slotsTillEvent = startFromMinutes / slotInMinutes;
+        int startOverFlowTime = slotInMinutes
+                - (startFromMinutes % slotInMinutes);
+        if (startOverFlowTime == slotInMinutes) {
+            startOverFlowTime = 0;
+            currentSlot = slotsTillEvent;
+        } else {
+            currentSlot = slotsTillEvent + 1;
+        }
+
+        int durationInSlots = 0;
+        int endOverFlowTime = 0;
+
+        if (startOverFlowTime > 0) {
+            durationInSlots = (durationInMinutes - startOverFlowTime)
+                    / slotInMinutes;
+            endOverFlowTime = (durationInMinutes - startOverFlowTime)
+                    % slotInMinutes;
+
+        } else {
+            durationInSlots = durationInMinutes / slotInMinutes;
+            endOverFlowTime = durationInMinutes % slotInMinutes;
+        }
+
+        // calculate slot overflow at start
+        if (startOverFlowTime > 0 && currentSlot < cellHeights.length) {
+            int lastSlotHeight = cellHeights[currentSlot] + dateCellBorder;
+            pixelLength += (int) (((double) lastSlotHeight / (double) slotInMinutes) * startOverFlowTime);
+        }
+
+        // calculate length in full slots
+        int lastFullSlot = currentSlot + durationInSlots;
+        for (; currentSlot < lastFullSlot && currentSlot < cellHeights.length; currentSlot++) {
+            pixelLength += cellHeights[currentSlot] + dateCellBorder;
+        }
+
+        // calculate overflow at end
+        if (endOverFlowTime > 0 && currentSlot < cellHeights.length) {
+            int lastSlotHeight = cellHeights[currentSlot] + dateCellBorder;
+            pixelLength += (int) (((double) lastSlotHeight / (double) slotInMinutes) * endOverFlowTime);
+        }
+
+        // reduce possible underflow at end
+        if (endOverFlowTime < 0) {
+            int lastSlotHeight = cellHeights[currentSlot] + dateCellBorder;
+            pixelLength += (int) (((double) lastSlotHeight / (double) slotInMinutes) * endOverFlowTime);
+        }
+
+        return pixelLength;
+    }
+
+    public int getPixelTopFor(int startFromMinutes) {
+        int pixelsToTop = 0;
+        int slotIndex = 0;
+
+        int firstHourInMinutes = firstHour * 60;
+
+        if (firstHourInMinutes > startFromMinutes) {
+            startFromMinutes = 0;
+        } else {
+            startFromMinutes -= firstHourInMinutes;
+        }
+
+        // calculate full slots to event
+        int slotsTillEvent = startFromMinutes / slotInMinutes;
+        int overFlowTime = startFromMinutes % slotInMinutes;
+        if (slotsTillEvent > 0) {
+            for (slotIndex = 0; slotIndex < slotsTillEvent; slotIndex++) {
+                pixelsToTop += cellHeights[slotIndex] + dateCellBorder;
+            }
+        }
+
+        // calculate lengths less than one slot
+        if (overFlowTime > 0) {
+            int lastSlotHeight = cellHeights[slotIndex] + dateCellBorder;
+            pixelsToTop += ((double) lastSlotHeight / (double) slotInMinutes)
+                    * overFlowTime;
+        }
+
+        return pixelsToTop;
+    }
+
+    public void eventMoved(DateCellDayEvent dayEvent) {
+        Style s = dayEvent.getElement().getStyle();
+        int left = Integer.parseInt(s.getLeft().substring(0,
+                s.getLeft().length() - 2));
+        DateCell previousParent = (DateCell) dayEvent.getParent();
+        DateCell newParent = (DateCell) content
+                .getWidget((left / getDateCellWidth()) + 1);
+        CalendarEvent se = dayEvent.getCalendarEvent();
+        previousParent.removeEvent(dayEvent);
+        newParent.addEvent(dayEvent);
+        if (!previousParent.equals(newParent)) {
+            previousParent.recalculateEventWidths();
+        }
+        newParent.recalculateEventWidths();
+        if (calendar.getEventMovedListener() != null) {
+            calendar.getEventMovedListener().eventMoved(se);
+        }
+    }
+
+    public void setToday(Date todayDate, Date todayTimestamp) {
+        int count = content.getWidgetCount();
+        if (count > 1) {
+            for (int i = 1; i < count; i++) {
+                DateCell dc = (DateCell) content.getWidget(i);
+                if (dc.getDate().getTime() == todayDate.getTime()) {
+                    if (isVerticalScrollable()) {
+                        dc.setToday(todayTimestamp, -1);
+                    } else {
+                        dc.setToday(todayTimestamp, getOffsetWidth());
+                    }
+                }
+                dateCellOfToday = dc;
+            }
+        }
+    }
+
+    public DateCell getDateCellOfToday() {
+        return dateCellOfToday;
+    }
+
+    public void setDisabled(boolean disabled) {
+        this.disabled = disabled;
+    }
+
+    public boolean isDisabled() {
+        return disabled;
+    }
+
+    public Timebar getTimeBar() {
+        return timebar;
+    }
+
+    public void setDateColor(Date when, Date to, String styleName) {
+        int dateCount = content.getWidgetCount();
+        for (int i = 1; i < dateCount; i++) {
+            DateCell dc = (DateCell) content.getWidget(i);
+            Date dcDate = dc.getDate();
+            int comp = dcDate.compareTo(when);
+            int comp2 = dcDate.compareTo(to);
+            if (comp >= 0 && comp2 <= 0) {
+                dc.setDateColor(styleName);
+            }
+        }
+    }
+
+    /**
+     * @param calendar
+     *            the calendar to set
+     */
+    public void setCalendar(VCalendar calendar) {
+        this.calendar = calendar;
+    }
+
+    /**
+     * @return the calendar
+     */
+    public VCalendar getCalendar() {
+        return calendar;
+    }
+
+    /**
+     * Get width of the single date cell
+     * 
+     * @return Date cell width
+     */
+    public int getDateCellWidth() {
+        int count = content.getWidgetCount() - 1;
+        int cellWidth = -1;
+        if (count <= 0) {
+            return cellWidth;
+        }
+
+        if (width == -1) {
+            Widget firstWidget = content.getWidget(1);
+            cellWidth = firstWidget.getElement().getOffsetWidth();
+        } else {
+            cellWidth = getInternalWidth() / count;
+        }
+        return cellWidth;
+    }
+
+    /**
+     * @return the number of day cells in this week
+     */
+    public int getDateCellCount() {
+        return content.getWidgetCount() - 1;
+    }
+
+    public void setFirstHour(int firstHour) {
+        this.firstHour = firstHour;
+        timebar.setFirstHour(firstHour);
+    }
+
+    public void setLastHour(int lastHour) {
+        this.lastHour = lastHour;
+        timebar.setLastHour(lastHour);
+    }
+
+    public int getFirstHour() {
+        return firstHour;
+    }
+
+    public int getLastHour() {
+        return lastHour;
+    }
+
+    public static class Timebar extends HTML {
+
+        private static final int[] timesFor12h = { 12, 1, 2, 3, 4, 5, 6, 7, 8,
+                9, 10, 11 };
+
+        private int height;
+
+        private final int verticalPadding = 7; // FIXME measure this from DOM
+
+        private int[] slotCellHeights;
+
+        private int firstHour;
+
+        private int lastHour;
+
+        public Timebar(boolean format24h) {
+            createTimeBar(format24h);
+        }
+
+        public void setLastHour(int lastHour) {
+            this.lastHour = lastHour;
+        }
+
+        public void setFirstHour(int firstHour) {
+            this.firstHour = firstHour;
+
+        }
+
+        public void setCellHeights(int[] cellHeights) {
+            slotCellHeights = cellHeights;
+        }
+
+        private void createTimeBar(boolean format24h) {
+            setStylePrimaryName("v-calendar-times");
+
+            // Fist "time" is empty
+            Element e = DOM.createDiv();
+            setStyleName(e, "v-calendar-time");
+            e.setInnerText("");
+            getElement().appendChild(e);
+
+            DateTimeService dts = new DateTimeService();
+
+            if (format24h) {
+                for (int i = firstHour + 1; i <= lastHour; i++) {
+                    e = DOM.createDiv();
+                    setStyleName(e, "v-calendar-time");
+                    String delimiter = dts.getClockDelimeter();
+                    e.setInnerHTML("<span>" + i + "</span>" + delimiter + "00");
+                    getElement().appendChild(e);
+                }
+            } else {
+                // FIXME Use dts.getAmPmStrings(); and make sure that
+                // DateTimeService has a some Locale set.
+                String[] ampm = new String[] { "AM", "PM" };
+
+                int amStop = (lastHour < 11) ? lastHour : 11;
+                int pmStart = (firstHour > 11) ? firstHour % 11 : 0;
+
+                if (firstHour < 12) {
+                    for (int i = firstHour + 1; i <= amStop; i++) {
+                        e = DOM.createDiv();
+                        setStyleName(e, "v-calendar-time");
+                        e.setInnerHTML("<span>" + timesFor12h[i] + "</span>"
+                                + " " + ampm[0]);
+                        getElement().appendChild(e);
+                    }
+                }
+
+                if (lastHour > 11) {
+                    for (int i = pmStart; i < lastHour - 11; i++) {
+                        e = DOM.createDiv();
+                        setStyleName(e, "v-calendar-time");
+                        e.setInnerHTML("<span>" + timesFor12h[i] + "</span>"
+                                + " " + ampm[1]);
+                        getElement().appendChild(e);
+                    }
+                }
+            }
+        }
+
+        public void updateTimeBar(boolean format24h) {
+            clear();
+            createTimeBar(format24h);
+        }
+
+        private void clear() {
+            while (getElement().getChildCount() > 0) {
+                getElement().removeChild(getElement().getChild(0));
+            }
+        }
+
+        public void setHeightPX(int pixelHeight) {
+            height = pixelHeight;
+
+            if (pixelHeight > -1) {
+                // as the negative margins on children pulls the whole element
+                // upwards, we must compensate. otherwise the element would be
+                // too short
+                super.setHeight((height + verticalPadding) + "px");
+                removeStyleDependentName("Vsized");
+                updateChildHeights();
+
+            } else {
+                addStyleDependentName("Vsized");
+                updateChildHeights();
+            }
+        }
+
+        private void updateChildHeights() {
+            int childCount = getElement().getChildCount();
+
+            if (height != -1) {
+
+                // 23 hours + first is empty
+                // we try to adjust the height of time labels to the distributed
+                // heights of the time slots
+                int hoursPerDay = lastHour - firstHour + 1;
+
+                int slotsPerHour = slotCellHeights.length / hoursPerDay;
+                int[] cellHeights = new int[slotCellHeights.length
+                        / slotsPerHour];
+
+                int slotHeightPosition = 0;
+                for (int i = 0; i < cellHeights.length; i++) {
+                    for (int j = slotHeightPosition; j < slotHeightPosition
+                            + slotsPerHour; j++) {
+                        cellHeights[i] += slotCellHeights[j] + 1;
+                        // 1px more for borders
+                        // FIXME measure from DOM
+                    }
+                    slotHeightPosition += slotsPerHour;
+                }
+
+                for (int i = 0; i < childCount; i++) {
+                    Element e = (Element) getElement().getChild(i);
+                    e.getStyle().setHeight(cellHeights[i], Unit.PX);
+                }
+
+            } else {
+                for (int i = 0; i < childCount; i++) {
+                    Element e = (Element) getElement().getChild(i);
+                    e.getStyle().setProperty("height", "");
+                }
+            }
+        }
+    }
+
+    public VCalendar getParentCalendar() {
+        return calendar;
+    }
+}
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/WeekGridMinuteTimeRange.java b/client/src/com/vaadin/client/ui/calendar/schedule/WeekGridMinuteTimeRange.java
new file mode 100644 (file)
index 0000000..27ace91
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule;
+
+import java.util.Date;
+
+/**
+ * Internally used by the calendar
+ * 
+ * @since 7.1
+ */
+public class WeekGridMinuteTimeRange {
+    private final Date start;
+    private final Date end;
+
+    /**
+     * Creates a Date time range between start and end date. Drops seconds
+     * from the range.
+     * 
+     * @param start
+     *            Start time of the range
+     * @param end
+     *            End time of the range
+     * @param clearSeconds
+     *            Boolean Indicates, if seconds should be dropped from the
+     *            range start and end
+     */
+    public WeekGridMinuteTimeRange(Date start, Date end) {
+        this.start = new Date(start.getTime());
+        this.end = new Date(end.getTime());
+        this.start.setSeconds(0);
+        this.end.setSeconds(0);
+    }
+
+    public Date getStart() {
+        return start;
+    }
+
+    public Date getEnd() {
+        return end;
+    }
+
+    public static boolean doesOverlap(WeekGridMinuteTimeRange a, WeekGridMinuteTimeRange b) {
+        boolean overlaps = a.getStart().compareTo(b.getEnd()) < 0
+                && a.getEnd().compareTo(b.getStart()) > 0;
+        return overlaps;
+    }
+}
\ No newline at end of file
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/WeekLabel.java b/client/src/com/vaadin/client/ui/calendar/schedule/WeekLabel.java
new file mode 100644 (file)
index 0000000..bde8675
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule;
+
+import com.google.gwt.user.client.ui.Label;
+
+/**
+ * A label in the {@link SimpleWeekToolbar}
+ * 
+ * @since 7.1
+ */
+public class WeekLabel extends Label {
+    private int week;
+    private int year;
+
+    public WeekLabel(String string, int week2, int year2) {
+        super(string);
+        setStylePrimaryName("v-calendar-week-number");
+        week = week2;
+        year = year2;
+    }
+
+    public int getWeek() {
+        return week;
+    }
+
+    public void setWeek(int week) {
+        this.week = week;
+    }
+
+    public int getYear() {
+        return year;
+    }
+
+    public void setYear(int year) {
+        this.year = year;
+    }
+}
\ No newline at end of file
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/WeeklyLongEvents.java b/client/src/com/vaadin/client/ui/calendar/schedule/WeeklyLongEvents.java
new file mode 100644 (file)
index 0000000..e3b7d5d
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule;
+
+import java.util.Date;
+import java.util.List;
+
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.vaadin.client.ui.VCalendar;
+
+/**
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ * 
+ */
+public class WeeklyLongEvents extends HorizontalPanel implements HasTooltipKey {
+
+    public static final int EVENT_HEIGTH = 15;
+
+    public static final int EVENT_MARGIN = 1;
+
+    private int rowCount = 0;
+
+    private VCalendar calendar;
+
+    private boolean undefinedWidth;
+
+    public WeeklyLongEvents(VCalendar calendar) {
+        setStylePrimaryName("v-calendar-weekly-longevents");
+        this.calendar = calendar;
+    }
+
+    public void addDate(Date d) {
+        DateCellContainer dcc = new DateCellContainer();
+        dcc.setDate(d);
+        dcc.setCalendar(calendar);
+        add(dcc);
+    }
+
+    public void setWidthPX(int width) {
+        if (getWidgetCount() == 0) {
+            return;
+        }
+        undefinedWidth = (width < 0);
+
+        updateCellWidths();
+    }
+
+    public void addEvents(List<CalendarEvent> events) {
+        for (CalendarEvent e : events) {
+            addEvent(e);
+        }
+    }
+
+    public void addEvent(CalendarEvent calendarEvent) {
+        updateEventSlot(calendarEvent);
+
+        int dateCount = getWidgetCount();
+        Date from = calendarEvent.getStart();
+        Date to = calendarEvent.getEnd();
+        boolean started = false;
+        for (int i = 0; i < dateCount; i++) {
+            DateCellContainer dc = (DateCellContainer) getWidget(i);
+            Date dcDate = dc.getDate();
+            int comp = dcDate.compareTo(from);
+            int comp2 = dcDate.compareTo(to);
+            WeeklyLongEventsDateCell eventLabel = dc.getDateCell(calendarEvent.getSlotIndex());
+            eventLabel.setStylePrimaryName("v-calendar-event");
+            if (comp >= 0 && comp2 <= 0) {
+                eventLabel.setEvent(calendarEvent);
+                eventLabel.setCalendar(calendar);
+
+                eventLabel.addStyleDependentName("all-day");
+                if (comp == 0) {
+                    eventLabel.addStyleDependentName("start");
+                }
+                if (comp2 == 0) {
+                    eventLabel.addStyleDependentName("end");
+                }
+                if (!started && comp > 0 && comp2 <= 0) {
+                    eventLabel.addStyleDependentName("continued-from");
+                } else if (i == (dateCount - 1)) {
+                    eventLabel.addStyleDependentName("continued-to");
+                }
+                final String extraStyle = calendarEvent.getStyleName();
+                if (extraStyle != null && extraStyle.length() > 0) {
+                    eventLabel.addStyleDependentName(extraStyle + "-all-day");
+                }
+                if (!started) {
+                    eventLabel.setText(calendarEvent.getCaption());
+                    started = true;
+                }
+            }
+        }
+    }
+
+    private void updateEventSlot(CalendarEvent e) {
+        boolean foundFreeSlot = false;
+        int slot = 0;
+        while (!foundFreeSlot) {
+            if (isSlotFree(slot, e.getStart(), e.getEnd())) {
+                e.setSlotIndex(slot);
+                foundFreeSlot = true;
+
+            } else {
+                slot++;
+            }
+        }
+    }
+
+    private boolean isSlotFree(int slot, Date start, Date end) {
+        int dateCount = getWidgetCount();
+
+        // Go over all dates this week
+        for (int i = 0; i < dateCount; i++) {
+            DateCellContainer dc = (DateCellContainer) getWidget(i);
+            Date dcDate = dc.getDate();
+            int comp = dcDate.compareTo(start);
+            int comp2 = dcDate.compareTo(end);
+
+            // check if the date is in the range we need
+            if (comp >= 0 && comp2 <= 0) {
+
+                // check if the slot is taken
+                if (dc.hasEvent(slot)) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    public int getRowCount() {
+        return rowCount;
+    }
+
+    public void updateCellWidths() {
+        int cells = getWidgetCount();
+        if (cells <= 0) {
+            return;
+        }
+
+        int cellWidth = -1;
+
+        // if width is undefined, use the width of the first cell
+        // otherwise use distributed sizes
+        if (undefinedWidth) {
+            cellWidth = calendar.getWeekGrid().getDateCellWidth()
+                    - calendar.getWeekGrid().getDateSlotBorder();
+        }
+
+        for (int i = 0; i < cells; i++) {
+            DateCellContainer dc = (DateCellContainer) getWidget(i);
+
+            if (undefinedWidth) {
+                dc.setWidth(cellWidth + "px");
+
+            } else {
+                dc.setWidth(calendar.getWeekGrid().getDateCellWidths()[i]
+                        + "px");
+            }
+        }
+    }
+
+    @Override
+    public String getTooltipKey() {
+        return null;
+    }
+}
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/WeeklyLongEventsDateCell.java b/client/src/com/vaadin/client/ui/calendar/schedule/WeeklyLongEventsDateCell.java
new file mode 100644 (file)
index 0000000..a97d352
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule;
+
+import java.util.Date;
+
+import com.google.gwt.user.client.ui.HTML;
+import com.vaadin.client.ui.VCalendar;
+
+/**
+ * Represents a cell used in {@link WeeklyLongEvents}
+ * 
+ * @since 7.1
+ */
+public class WeeklyLongEventsDateCell extends HTML implements HasTooltipKey {
+    private Date date;
+    private CalendarEvent calendarEvent;
+    private VCalendar calendar;
+
+    public WeeklyLongEventsDateCell() {
+    }
+
+    public void setDate(Date date) {
+        this.date = date;
+    }
+
+    public Date getDate() {
+        return date;
+    }
+
+    public void setEvent(CalendarEvent event) {
+        calendarEvent = event;
+    }
+
+    public CalendarEvent getEvent() {
+        return calendarEvent;
+    }
+
+    public void setCalendar(VCalendar calendar) {
+        this.calendar = calendar;
+    }
+
+    public VCalendar getCalendar() {
+        return calendar;
+    }
+
+    @Override
+    public Object getTooltipKey() {
+        if (calendarEvent != null) {
+            return calendarEvent.getIndex();
+        }
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarDropHandler.java b/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarDropHandler.java
new file mode 100644 (file)
index 0000000..03db4d0
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule.dd;
+
+import com.vaadin.client.ApplicationConnection;
+import com.vaadin.client.ui.calendar.CalendarConnector;
+import com.vaadin.client.ui.dd.VAbstractDropHandler;
+
+/**
+ * Abstract base class for calendar drop handlers.
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ * 
+ */
+public abstract class CalendarDropHandler extends VAbstractDropHandler {
+
+    protected CalendarConnector calendarConnector;
+
+    /**
+     * Set the calendar instance
+     * 
+     * @param calendarPaintable
+     */
+    public void setConnector(CalendarConnector calendarConnector) {
+        this.calendarConnector = calendarConnector;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler#getConnector()
+     */
+    @Override
+    public CalendarConnector getConnector() {
+        return calendarConnector;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.terminal.gwt.client.ui.dd.VDropHandler#getApplicationConnection
+     * ()
+     */
+    public ApplicationConnection getApplicationConnection() {
+        return calendarConnector.getClient();
+    }
+
+}
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarMonthDropHandler.java b/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarMonthDropHandler.java
new file mode 100644 (file)
index 0000000..6e57fb6
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule.dd;
+
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+import com.vaadin.client.Util;
+import com.vaadin.client.ui.calendar.schedule.SimpleDayCell;
+import com.vaadin.client.ui.dd.VAcceptCallback;
+import com.vaadin.client.ui.dd.VDragEvent;
+
+/**
+ * Handles DD when the monthly view is showing in the Calendar. In the monthly
+ * view, drops are only allowed in the the day cells. Only the day index is
+ * included in the drop details sent to the server.
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ */
+public class CalendarMonthDropHandler extends CalendarDropHandler {
+
+    private Element currentTargetElement;
+    private SimpleDayCell currentTargetDay;
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler#dragAccepted
+     * (com.vaadin.terminal.gwt.client.ui.dd.VDragEvent)
+     */
+    @Override
+    protected void dragAccepted(VDragEvent drag) {
+        deEmphasis();
+        currentTargetElement = drag.getElementOver();
+        currentTargetDay = Util.findWidget(currentTargetElement,
+                SimpleDayCell.class);
+        emphasis();
+    }
+
+    /**
+     * Removed the emphasis CSS style name from the currently emphasized day
+     */
+    private void deEmphasis() {
+        if (currentTargetElement != null && currentTargetDay != null) {
+            currentTargetDay.removeEmphasisStyle();
+            currentTargetElement = null;
+        }
+    }
+
+    /**
+     * Add CSS style name for the currently emphasized day
+     */
+    private void emphasis() {
+        if (currentTargetElement != null && currentTargetDay != null) {
+            currentTargetDay.addEmphasisStyle();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler#dragOver(com
+     * .vaadin.terminal.gwt.client.ui.dd.VDragEvent)
+     */
+    @Override
+    public void dragOver(final VDragEvent drag) {
+        if (isLocationValid(drag.getElementOver())) {
+            validate(new VAcceptCallback() {
+                public void accepted(VDragEvent event) {
+                    dragAccepted(drag);
+                }
+            }, drag);
+        }
+    }
+
+    /**
+     * Checks if the one can perform a drop in a element
+     * 
+     * @param elementOver
+     *            The element to check
+     * @return
+     */
+    private boolean isLocationValid(
+            com.google.gwt.user.client.Element elementOver) {
+        com.google.gwt.user.client.Element monthGridElement = calendarConnector
+                .getWidget().getMonthGrid().getElement();
+
+        // drops are not allowed in:
+        // - weekday header
+        // - week number bart
+        return DOM.isOrHasChild(monthGridElement, elementOver);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler#dragEnter(com
+     * .vaadin.terminal.gwt.client.ui.dd.VDragEvent)
+     */
+    @Override
+    public void dragEnter(VDragEvent drag) {
+        // NOOP, we determine drag acceptance in dragOver
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler#drop(com.vaadin
+     * .terminal.gwt.client.ui.dd.VDragEvent)
+     */
+    @Override
+    public boolean drop(VDragEvent drag) {
+        if (isLocationValid(drag.getElementOver())) {
+            updateDropDetails(drag);
+            deEmphasis();
+            return super.drop(drag);
+
+        } else {
+            deEmphasis();
+            return false;
+        }
+    }
+
+    /**
+     * Updates the drop details sent to the server
+     * 
+     * @param drag
+     *            The drag event
+     */
+    private void updateDropDetails(VDragEvent drag) {
+        int dayIndex = calendarConnector.getWidget().getMonthGrid()
+                .getDayCellIndex(currentTargetDay);
+
+        drag.getDropDetails().put("dropDayIndex", dayIndex);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler#dragLeave(com
+     * .vaadin.terminal.gwt.client.ui.dd.VDragEvent)
+     */
+    @Override
+    public void dragLeave(VDragEvent drag) {
+        deEmphasis();
+        super.dragLeave(drag);
+    }
+}
diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarWeekDropHandler.java b/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarWeekDropHandler.java
new file mode 100644 (file)
index 0000000..fa7aaa4
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.calendar.schedule.dd;
+
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+import com.vaadin.client.Util;
+import com.vaadin.client.ui.calendar.schedule.DateCell;
+import com.vaadin.client.ui.calendar.schedule.DateCellDayEvent;
+import com.vaadin.client.ui.dd.VAcceptCallback;
+import com.vaadin.client.ui.dd.VDragEvent;
+
+/**
+ * Handles DD when the weekly view is showing in the Calendar. In the weekly
+ * view, drops are only allowed in the the time slots for each day. The slot
+ * index and the day index are included in the drop details sent to the server.
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ */
+public class CalendarWeekDropHandler extends CalendarDropHandler {
+
+    private com.google.gwt.user.client.Element currentTargetElement;
+    private DateCell currentTargetDay;
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler#dragAccepted
+     * (com.vaadin.terminal.gwt.client.ui.dd.VDragEvent)
+     */
+    @Override
+    protected void dragAccepted(VDragEvent drag) {
+        deEmphasis();
+        currentTargetElement = drag.getElementOver();
+        currentTargetDay = Util
+                .findWidget(currentTargetElement, DateCell.class);
+        emphasis();
+    }
+
+    /**
+     * Removes the CSS style name from the emphasized element
+     */
+    private void deEmphasis() {
+        if (currentTargetElement != null) {
+            currentTargetDay.removeEmphasisStyle(currentTargetElement);
+            currentTargetElement = null;
+        }
+    }
+
+    /**
+     * Add a CSS stylen name to current target element
+     */
+    private void emphasis() {
+        currentTargetDay.addEmphasisStyle(currentTargetElement);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler#dragOver(com
+     * .vaadin.terminal.gwt.client.ui.dd.VDragEvent)
+     */
+    @Override
+    public void dragOver(final VDragEvent drag) {
+        if (isLocationValid(drag.getElementOver())) {
+            validate(new VAcceptCallback() {
+                public void accepted(VDragEvent event) {
+                    dragAccepted(drag);
+                }
+            }, drag);
+        }
+    }
+
+    /**
+     * Checks if the location is a valid drop location
+     * 
+     * @param elementOver
+     *            The element to check
+     * @return
+     */
+    private boolean isLocationValid(
+            com.google.gwt.user.client.Element elementOver) {
+        com.google.gwt.user.client.Element weekGridElement = calendarConnector
+                .getWidget().getWeekGrid().getElement();
+        com.google.gwt.user.client.Element timeBarElement = calendarConnector
+                .getWidget().getWeekGrid().getTimeBar().getElement();
+
+        com.google.gwt.user.client.Element todayBarElement = null;
+        if (calendarConnector.getWidget().getWeekGrid().hasToday()) {
+            todayBarElement = (Element) calendarConnector.getWidget()
+                    .getWeekGrid().getDateCellOfToday().getTodaybarElement();
+        }
+
+        // drops are not allowed in:
+        // - weekday header
+        // - allday event list
+        // - todaybar
+        // - timebar
+        // - events
+        return DOM.isOrHasChild(weekGridElement, elementOver)
+                && !DOM.isOrHasChild(timeBarElement, elementOver)
+                && todayBarElement != elementOver
+                && (Util.findWidget(elementOver, DateCellDayEvent.class) == null);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler#dragEnter(com
+     * .vaadin.terminal.gwt.client.ui.dd.VDragEvent)
+     */
+    @Override
+    public void dragEnter(VDragEvent drag) {
+        // NOOP, we determine drag acceptance in dragOver
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler#drop(com.vaadin
+     * .terminal.gwt.client.ui.dd.VDragEvent)
+     */
+    @Override
+    public boolean drop(VDragEvent drag) {
+        if (isLocationValid(drag.getElementOver())) {
+            updateDropDetails(drag);
+            deEmphasis();
+            return super.drop(drag);
+
+        } else {
+            deEmphasis();
+            return false;
+        }
+    }
+
+    /**
+     * Update the drop details sent to the server
+     * 
+     * @param drag
+     *            The drag event
+     */
+    private void updateDropDetails(VDragEvent drag) {
+        int slotIndex = currentTargetDay.getSlotIndex(currentTargetElement);
+        int dayIndex = calendarConnector.getWidget().getWeekGrid()
+                .getDateCellIndex(currentTargetDay);
+
+        drag.getDropDetails().put("dropDayIndex", dayIndex);
+        drag.getDropDetails().put("dropSlotIndex", slotIndex);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler#dragLeave(com
+     * .vaadin.terminal.gwt.client.ui.dd.VDragEvent)
+     */
+    @Override
+    public void dragLeave(VDragEvent drag) {
+        deEmphasis();
+        super.dragLeave(drag);
+    }
+}
diff --git a/server/src/com/vaadin/ui/Calendar.java b/server/src/com/vaadin/ui/Calendar.java
new file mode 100644 (file)
index 0000000..4bf2885
--- /dev/null
@@ -0,0 +1,1822 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.ui;
+
+import java.lang.reflect.Method;
+import java.text.DateFormat;
+import java.text.DateFormatSymbols;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.EventListener;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TimeZone;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.vaadin.data.Container;
+import com.vaadin.data.util.BeanItemContainer;
+import com.vaadin.event.Action;
+import com.vaadin.event.Action.Handler;
+import com.vaadin.event.dd.DropHandler;
+import com.vaadin.event.dd.DropTarget;
+import com.vaadin.event.dd.TargetDetails;
+import com.vaadin.server.KeyMapper;
+import com.vaadin.shared.ui.calendar.CalendarEventId;
+import com.vaadin.shared.ui.calendar.CalendarServerRpc;
+import com.vaadin.shared.ui.calendar.CalendarState;
+import com.vaadin.shared.ui.calendar.DateConstants;
+import com.vaadin.ui.components.calendar.CalendarComponentEvent;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.BackwardEvent;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.BackwardHandler;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.DateClickEvent;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.DateClickHandler;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventClick;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventClickHandler;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventMoveHandler;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventResize;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventResizeHandler;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.ForwardEvent;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.ForwardHandler;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.MoveEvent;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.RangeSelectEvent;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.RangeSelectHandler;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.WeekClick;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.WeekClickHandler;
+import com.vaadin.ui.components.calendar.CalendarDateRange;
+import com.vaadin.ui.components.calendar.CalendarTargetDetails;
+import com.vaadin.ui.components.calendar.ContainerEventProvider;
+import com.vaadin.ui.components.calendar.event.BasicEventProvider;
+import com.vaadin.ui.components.calendar.event.CalendarEditableEventProvider;
+import com.vaadin.ui.components.calendar.event.CalendarEvent;
+import com.vaadin.ui.components.calendar.event.CalendarEvent.EventChangeEvent;
+import com.vaadin.ui.components.calendar.event.CalendarEvent.EventChangeListener;
+import com.vaadin.ui.components.calendar.event.CalendarEventProvider;
+import com.vaadin.ui.components.calendar.handler.BasicBackwardHandler;
+import com.vaadin.ui.components.calendar.handler.BasicDateClickHandler;
+import com.vaadin.ui.components.calendar.handler.BasicEventMoveHandler;
+import com.vaadin.ui.components.calendar.handler.BasicEventResizeHandler;
+import com.vaadin.ui.components.calendar.handler.BasicForwardHandler;
+import com.vaadin.ui.components.calendar.handler.BasicWeekClickHandler;
+
+/**
+ * <p>
+ * Vaadin Calendar is for visualizing events in a calendar. Calendar events can
+ * be visualized in the variable length view depending on the start and end
+ * dates.
+ * </p>
+ * 
+ * <li>You can set the viewable date range with the {@link #setStartDate(Date)}
+ * and {@link #setEndDate(Date)} methods. Calendar has a default date range of
+ * one week</li>
+ * 
+ * <li>Calendar has two kind of views: monthly and weekly view</li>
+ * 
+ * <li>If date range is seven days or shorter, the weekly view is used.</li>
+ * 
+ * <li>Calendar queries its events by using a
+ * {@link com.vaadin.addon.calendar.event.CalendarEventProvider
+ * CalendarEventProvider}. By default, a
+ * {@link com.vaadin.addon.calendar.event.BasicEventProvider BasicEventProvider}
+ * is used.</li>
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ */
+@SuppressWarnings("serial")
+public class Calendar extends AbstractComponent implements
+        CalendarComponentEvents.NavigationNotifier,
+        CalendarComponentEvents.EventMoveNotifier,
+        CalendarComponentEvents.RangeSelectNotifier,
+        CalendarComponentEvents.EventResizeNotifier,
+        CalendarEventProvider.EventSetChangeListener, DropTarget,
+        CalendarEditableEventProvider, Action.Container {
+
+    /**
+     * Calendar can use either 12 hours clock or 24 hours clock.
+     */
+    public enum TimeFormat {
+
+        Format12H(), Format24H();
+    }
+
+    /** Defines currently active format for time. 12H/24H. */
+    protected TimeFormat currentTimeFormat;
+
+    /** Internal calendar data source. */
+    protected java.util.Calendar currentCalendar = java.util.Calendar
+            .getInstance();
+
+    /** Defines the component's active time zone. */
+    protected TimeZone timezone;
+
+    /** Defines the calendar's date range starting point. */
+    protected Date startDate = null;
+
+    /** Defines the calendar's date range ending point. */
+    protected Date endDate = null;
+
+    /** Event provider. */
+    private CalendarEventProvider calendarEventProvider;
+
+    /**
+     * Internal buffer for the events that are retrieved from the event
+     * provider.
+     */
+    protected List<CalendarEvent> events;
+
+    /** Date format that will be used in the UIDL for dates. */
+    protected DateFormat df_date = new SimpleDateFormat("yyyy-MM-dd");
+
+    /** Time format that will be used in the UIDL for time. */
+    protected DateFormat df_time = new SimpleDateFormat("HH:mm:ss");
+
+    /** Date format that will be used in the UIDL for both date and time. */
+    protected DateFormat df_date_time = new SimpleDateFormat(
+            DateConstants.CLIENT_DATE_FORMAT + "-"
+                    + DateConstants.CLIENT_TIME_FORMAT);
+
+    /**
+     * Week view's scroll position. Client sends updates to this value so that
+     * scroll position wont reset all the time.
+     */
+    private int scrollTop = 0;
+
+    /** Caption format for the weekly view */
+    private String weeklyCaptionFormat = null;
+
+    /** Map from event ids to event handlers */
+    private final Map<String, EventListener> handlers;
+
+    /**
+     * Drop Handler for Vaadin DD. By default null.
+     */
+    private DropHandler dropHandler;
+
+    /**
+     * First day to show for a week
+     */
+    private int firstDay = 1;
+
+    /**
+     * Last day to show for a week
+     */
+    private int lastDay = 7;
+
+    /**
+     * First hour to show for a day
+     */
+    private int firstHour = 0;
+
+    /**
+     * Last hour to show for a day
+     */
+    private int lastHour = 23;
+
+    /**
+     * List of action handlers.
+     */
+    private LinkedList<Action.Handler> actionHandlers = null;
+
+    /**
+     * Action mapper.
+     */
+    private KeyMapper<Action> actionMapper = null;
+
+    /**
+     * 
+     */
+    private CalendarServerRpcImpl rpc = new CalendarServerRpcImpl();
+
+    /**
+     * Returns the logger for the calendar
+     */
+    protected Logger getLogger() {
+        return Logger.getLogger(Calendar.class.getName());
+    }
+
+    /**
+     * Construct a Vaadin Calendar with a BasicEventProvider and no caption.
+     * Default date range is one week.
+     */
+    public Calendar() {
+        this(null, new BasicEventProvider());
+    }
+
+    /**
+     * Construct a Vaadin Calendar with a BasicEventProvider and the provided
+     * caption. Default date range is one week.
+     * 
+     * @param caption
+     */
+    public Calendar(String caption) {
+        this(caption, new BasicEventProvider());
+    }
+
+    /**
+     * <p>
+     * Construct a Vaadin Calendar with event provider. Event provider is
+     * obligatory, because calendar component will query active events through
+     * it.
+     * </p>
+     * 
+     * <p>
+     * By default, Vaadin Calendar will show dates from the start of the current
+     * week to the end of the current week. Use {@link #setStartDate(Date)} and
+     * {@link #setEndDate(Date)} to change this.
+     * </p>
+     * 
+     * @param eventProvider
+     *            Event provider, cannot be null.
+     */
+    public Calendar(CalendarEventProvider eventProvider) {
+        this(null, eventProvider);
+    }
+
+    /**
+     * <p>
+     * Construct a Vaadin Calendar with event provider and a caption. Event
+     * provider is obligatory, because calendar component will query active
+     * events through it.
+     * </p>
+     * 
+     * <p>
+     * By default, Vaadin Calendar will show dates from the start of the current
+     * week to the end of the current week. Use {@link #setStartDate(Date)} and
+     * {@link #setEndDate(Date)} to change this.
+     * </p>
+     * 
+     * @param eventProvider
+     *            Event provider, cannot be null.
+     */
+    // this is the constructor every other constructor calls
+    public Calendar(String caption, CalendarEventProvider eventProvider) {
+        registerRpc(rpc);
+        setCaption(caption);
+        handlers = new HashMap<String, EventListener>();
+        setDefaultHandlers();
+        currentCalendar.setTime(new Date());
+        setEventProvider(eventProvider);
+        getState().firstDayOfWeek = firstDay;
+        getState().lastVisibleDayOfWeek = lastDay;
+        getState().firstHourOfDay = firstHour;
+        getState().lastHourOfDay = lastHour;
+        setTimeFormat(null);
+
+    }
+
+    @Override
+    public CalendarState getState() {
+        return (CalendarState) super.getState();
+    }
+
+    @Override
+    public void beforeClientResponse(boolean initial) {
+        super.beforeClientResponse(initial);
+
+        initCalendarWithLocale();
+
+        getState().format24H = TimeFormat.Format24H == getTimeFormat();
+        setupDaysAndActions();
+        setupCalendarEvents();
+        rpc.scroll(scrollTop);
+    }
+
+    /**
+     * Set all the wanted default handlers here. This is always called after
+     * constructing this object. All other events have default handlers except
+     * range and event click.
+     */
+    protected void setDefaultHandlers() {
+        setHandler(new BasicBackwardHandler());
+        setHandler(new BasicForwardHandler());
+        setHandler(new BasicWeekClickHandler());
+        setHandler(new BasicDateClickHandler());
+        setHandler(new BasicEventMoveHandler());
+        setHandler(new BasicEventResizeHandler());
+    }
+
+    /**
+     * Gets the calendar's start date.
+     * 
+     * @return First visible date.
+     */
+    public Date getStartDate() {
+        if (startDate == null) {
+            currentCalendar.set(java.util.Calendar.DAY_OF_WEEK,
+                    currentCalendar.getFirstDayOfWeek());
+            return currentCalendar.getTime();
+        }
+        return startDate;
+    }
+
+    /**
+     * Sets start date for the calendar. This and {@link #setEndDate(Date)}
+     * control the range of dates visible on the component. The default range is
+     * one week.
+     * 
+     * @param date
+     *            First visible date to show.
+     */
+    public void setStartDate(Date date) {
+        if (!date.equals(startDate)) {
+            startDate = date;
+            markAsDirty();
+        }
+    }
+
+    /**
+     * Gets the calendar's end date.
+     * 
+     * @return Last visible date.
+     */
+    public Date getEndDate() {
+        if (endDate == null) {
+            currentCalendar.set(java.util.Calendar.DAY_OF_WEEK,
+                    currentCalendar.getFirstDayOfWeek() + 6);
+            return currentCalendar.getTime();
+        }
+        return endDate;
+    }
+
+    /**
+     * Sets end date for the calendar. Starting from startDate, only six weeks
+     * will be shown if duration to endDate is longer than six weeks.
+     * 
+     * This and {@link #setStartDate(Date)} control the range of dates visible
+     * on the component. The default range is one week.
+     * 
+     * @param date
+     *            Last visible date to show.
+     */
+    public void setEndDate(Date date) {
+        if (startDate != null && startDate.after(date)) {
+            startDate = (Date) date.clone();
+            markAsDirty();
+        } else if (!date.equals(endDate)) {
+            endDate = date;
+            markAsDirty();
+        }
+    }
+
+    /**
+     * Sets the locale to be used in the Calendar component.
+     * 
+     * @see com.vaadin.ui.AbstractComponent#setLocale(java.util.Locale)
+     */
+    @Override
+    public void setLocale(Locale newLocale) {
+        super.setLocale(newLocale);
+        initCalendarWithLocale();
+    }
+
+    /**
+     * Initialize the java calendar instance with the current locale and
+     * timezone.
+     */
+    private void initCalendarWithLocale() {
+        if (timezone != null) {
+            currentCalendar = java.util.Calendar.getInstance(timezone,
+                    getLocale());
+
+        } else {
+            currentCalendar = java.util.Calendar.getInstance(getLocale());
+        }
+    }
+
+    private void setupCalendarEvents() {
+        int durationInDays = (int) (((endDate.getTime()) - startDate.getTime()) / DateConstants.DAYINMILLIS);
+        durationInDays++;
+        if (durationInDays > 60) {
+            throw new RuntimeException("Daterange is too big (max 60) = "
+                    + durationInDays);
+        }
+
+        Date firstDateToShow = expandStartDate(startDate, durationInDays > 7);
+        Date lastDateToShow = expandEndDate(endDate, durationInDays > 7);
+
+        currentCalendar.setTime(firstDateToShow);
+        events = getEventProvider().getEvents(firstDateToShow, lastDateToShow);
+
+        List<CalendarState.Event> calendarStateEvents = new ArrayList<CalendarState.Event>();
+        if (events != null) {
+            for (int i = 0; i < events.size(); i++) {
+                CalendarEvent e = events.get(i);
+                CalendarState.Event event = new CalendarState.Event();
+                event.index = i;
+                event.caption = e.getCaption() == null ? "" : e.getCaption();
+                event.dateFrom = df_date.format(e.getStart());
+                event.dateTo = df_date.format(e.getEnd());
+                event.timeFrom = df_time.format(e.getStart());
+                event.timeTo = df_time.format(e.getEnd());
+                event.description = e.getDescription() == null ? "" : e
+                        .getDescription();
+                event.styleName = e.getStyleName() == null ? "" : e
+                        .getStyleName();
+                event.allDay = e.isAllDay();
+                calendarStateEvents.add(event);
+            }
+        }
+        getState().events = calendarStateEvents;
+    }
+
+    private void setupDaysAndActions() {
+        // Make sure we have a up-to-date locale
+        initCalendarWithLocale();
+
+        CalendarState state = getState();
+
+        state.firstDayOfWeek = currentCalendar.getFirstDayOfWeek();
+
+        // If only one is null, throw exception
+        // If both are null, set defaults
+        if (startDate == null ^ endDate == null) {
+            String message = "Schedule cannot be painted without a proper date range.\n";
+            if (startDate == null) {
+                throw new IllegalStateException(message
+                        + "You must set a start date using setStartDate(Date).");
+
+            } else {
+                throw new IllegalStateException(message
+                        + "You must set an end date using setEndDate(Date).");
+            }
+
+        } else if (startDate == null && endDate == null) {
+            // set defaults
+            startDate = getStartDate();
+            endDate = getEndDate();
+        }
+
+        int durationInDays = (int) (((endDate.getTime()) - startDate.getTime()) / DateConstants.DAYINMILLIS);
+        durationInDays++;
+        if (durationInDays > 60) {
+            throw new RuntimeException("Daterange is too big (max 60) = "
+                    + durationInDays);
+        }
+
+        state.dayNames = getDayNamesShort();
+        state.monthNames = getMonthNamesShort();
+
+        // Use same timezone in all dates this component handles.
+        // Show "now"-marker in browser within given timezone.
+        Date now = new Date();
+        currentCalendar.setTime(now);
+        now = currentCalendar.getTime();
+
+        // Reset time zones for custom date formats
+        df_date.setTimeZone(currentCalendar.getTimeZone());
+        df_time.setTimeZone(currentCalendar.getTimeZone());
+
+        state.now = (df_date.format(now) + " " + df_time.format(now));
+
+        Date firstDateToShow = expandStartDate(startDate, durationInDays > 7);
+        Date lastDateToShow = expandEndDate(endDate, durationInDays > 7);
+
+        currentCalendar.setTime(firstDateToShow);
+
+        DateFormat weeklyCaptionFormatter = getWeeklyCaptionFormatter();
+        weeklyCaptionFormatter.setTimeZone(currentCalendar.getTimeZone());
+
+        Map<CalendarDateRange, Set<Action>> actionMap = new HashMap<CalendarDateRange, Set<Action>>();
+
+        List<CalendarState.Day> days = new ArrayList<CalendarState.Day>();
+
+        // Send all dates to client from server. This
+        // approach was taken because gwt doesn't
+        // support date localization properly.
+        while (currentCalendar.getTime().compareTo(lastDateToShow) < 1) {
+            final Date date = currentCalendar.getTime();
+            final CalendarState.Day day = new CalendarState.Day();
+            day.date = df_date.format(date);
+            day.localizedDateFormat = weeklyCaptionFormatter.format(date);
+            day.dayOfWeek = getDowByLocale(currentCalendar);
+            day.week = currentCalendar.get(java.util.Calendar.WEEK_OF_YEAR);
+
+            days.add(day);
+
+            // Get actions for a specific date
+            if (actionHandlers != null) {
+                for (Action.Handler actionHandler : actionHandlers) {
+
+                    // Create calendar which omits time
+                    GregorianCalendar cal = new GregorianCalendar(
+                            getTimeZone(), getLocale());
+                    cal.clear();
+                    cal.set(currentCalendar.get(java.util.Calendar.YEAR),
+                            currentCalendar.get(java.util.Calendar.MONTH),
+                            currentCalendar.get(java.util.Calendar.DATE));
+
+                    // Get day start and end times
+                    Date start = cal.getTime();
+                    cal.add(java.util.Calendar.DATE, 1);
+                    Date end = cal.getTime();
+
+                    boolean monthView = (durationInDays > 7);
+
+                    /**
+                     * If in day or week view add actions for each half-an-hour.
+                     * If in month view add actions for each day
+                     */
+                    if (monthView) {
+                        setActionsForDay(actionMap, start, end, actionHandler);
+                    } else {
+                        setActionsForEachHalfHour(actionMap, start, end,
+                                actionHandler);
+                    }
+
+                }
+            }
+
+            currentCalendar.add(java.util.Calendar.DATE, 1);
+        }
+        state.days = days;
+        state.actions = createActionsList(actionMap);
+    }
+
+    private void setActionsForEachHalfHour(
+            Map<CalendarDateRange, Set<Action>> actionMap, Date start,
+            Date end, Action.Handler actionHandler) {
+        GregorianCalendar cal = new GregorianCalendar(getTimeZone(),
+                getLocale());
+        cal.setTime(start);
+        while (cal.getTime().before(end)) {
+            Date s = cal.getTime();
+            cal.add(java.util.Calendar.MINUTE, 30);
+            Date e = cal.getTime();
+            CalendarDateRange range = new CalendarDateRange(s, e, getTimeZone());
+            Action[] actions = actionHandler.getActions(range, this);
+            if (actions != null) {
+                Set<Action> actionSet = new HashSet<Action>(
+                        Arrays.asList(actions));
+                actionMap.put(range, actionSet);
+            }
+        }
+    }
+
+    private void setActionsForDay(
+            Map<CalendarDateRange, Set<Action>> actionMap, Date start,
+            Date end, Action.Handler actionHandler) {
+        CalendarDateRange range = new CalendarDateRange(start, end,
+                getTimeZone());
+        Action[] actions = actionHandler.getActions(range, this);
+        if (actions != null) {
+            Set<Action> actionSet = new HashSet<Action>(Arrays.asList(actions));
+            actionMap.put(range, actionSet);
+        }
+    }
+
+    private List<CalendarState.Action> createActionsList(
+            Map<CalendarDateRange, Set<Action>> actionMap) {
+        if (actionMap.isEmpty()) {
+            return null;
+        }
+
+        List<CalendarState.Action> calendarActions = new ArrayList<CalendarState.Action>();
+
+        SimpleDateFormat formatter = new SimpleDateFormat(
+                DateConstants.ACTION_DATE_FORMAT_PATTERN);
+        formatter.setTimeZone(getTimeZone());
+
+        for (Entry<CalendarDateRange, Set<Action>> entry : actionMap.entrySet()) {
+            CalendarDateRange range = entry.getKey();
+            Set<Action> actions = entry.getValue();
+            for (Action action : actions) {
+                String key = actionMapper.key(action);
+                CalendarState.Action calendarAction = new CalendarState.Action();
+                calendarAction.actionKey = key;
+                calendarAction.caption = action.getCaption();
+                setResource(key, action.getIcon());
+                calendarAction.iconKey = key;
+                calendarAction.startDate = formatter.format(range.getStart());
+                calendarAction.endDate = formatter.format(range.getEnd());
+                calendarActions.add(calendarAction);
+            }
+        }
+
+        return calendarActions;
+    }
+
+    /**
+     * Gets currently active time format. Value is either TimeFormat.Format12H
+     * or TimeFormat.Format24H.
+     * 
+     * @return TimeFormat Format for the time.
+     */
+    public TimeFormat getTimeFormat() {
+        if (currentTimeFormat == null) {
+            SimpleDateFormat f = (SimpleDateFormat) SimpleDateFormat
+                    .getTimeInstance(SimpleDateFormat.SHORT, getLocale());
+            String p = f.toPattern();
+            if (p.indexOf("HH") != -1 || p.indexOf("H") != -1) {
+                return TimeFormat.Format24H;
+            }
+            return TimeFormat.Format12H;
+        }
+        return currentTimeFormat;
+    }
+
+    /**
+     * Example: <code>setTimeFormat(TimeFormat.Format12H);</code></br> Set to
+     * null, if you want the format being defined by the locale.
+     * 
+     * @param format
+     *            Set 12h or 24h format. Default is defined by the locale.
+     */
+    public void setTimeFormat(TimeFormat format) {
+        currentTimeFormat = format;
+        markAsDirty();
+    }
+
+    /**
+     * Returns a time zone that is currently used by this component.
+     * 
+     * @return Component's Time zone
+     */
+    public TimeZone getTimeZone() {
+        if (timezone == null) {
+            return currentCalendar.getTimeZone();
+        }
+        return timezone;
+    }
+
+    /**
+     * Set time zone that this component will use. Null value sets the default
+     * time zone.
+     * 
+     * @param zone
+     *            Time zone to use
+     */
+    public void setTimeZone(TimeZone zone) {
+        timezone = zone;
+        if (!currentCalendar.getTimeZone().equals(zone)) {
+            if (zone == null) {
+                zone = TimeZone.getDefault();
+            }
+            currentCalendar.setTimeZone(zone);
+            df_date_time.setTimeZone(zone);
+            markAsDirty();
+        }
+    }
+
+    /**
+     * Get the internally used Calendar instance. This is the currently used
+     * instance of {@link java.util.Calendar} but is bound to change during the
+     * lifetime of the component.
+     * 
+     * @return the currently used java calendar
+     */
+    public java.util.Calendar getInternalCalendar() {
+        return currentCalendar;
+    }
+
+    /**
+     * <p>
+     * This method restricts the weekdays that are shown. This affects both the
+     * monthly and the weekly view. The general contract is that <b>firstDay <
+     * lastDay</b>.
+     * </p>
+     * 
+     * <p>
+     * Note that this only affects the rendering process. Events are still
+     * requested by the dates set by {@link #setStartDate(Date)} and
+     * {@link #setEndDate(Date)}.
+     * </p>
+     * 
+     * @param firstDay
+     *            the first day of the week to show, between 1 and 7
+     */
+    public void setFirstVisibleDayOfWeek(int firstDay) {
+        if (this.firstDay != firstDay && firstDay >= 1 && firstDay <= 7
+                && getLastVisibleDayOfWeek() >= firstDay) {
+            this.firstDay = firstDay;
+            getState().firstVisibleDayOfWeek = firstDay;
+        }
+    }
+
+    /**
+     * Get the first visible day of the week. Returns the weekdays as integers
+     * represented by {@link java.util.Calendar#DAY_OF_WEEK}
+     * 
+     * @return An integer representing the week day according to
+     *         {@link java.util.Calendar#DAY_OF_WEEK}
+     */
+    public int getFirstVisibleDayOfWeek() {
+        return firstDay;
+    }
+
+    /**
+     * <p>
+     * This method restricts the weekdays that are shown. This affects both the
+     * monthly and the weekly view. The general contract is that <b>firstDay <
+     * lastDay</b>.
+     * </p>
+     * 
+     * <p>
+     * Note that this only affects the rendering process. Events are still
+     * requested by the dates set by {@link #setStartDate(Date)} and
+     * {@link #setEndDate(Date)}.
+     * </p>
+     * 
+     * @param lastDay
+     *            the first day of the week to show, between 1 and 7
+     */
+    public void setLastVisibleDayOfWeek(int lastDay) {
+        if (this.lastDay != lastDay && lastDay >= 1 && lastDay <= 7
+                && getFirstVisibleDayOfWeek() <= lastDay) {
+            this.lastDay = lastDay;
+            getState().lastVisibleDayOfWeek = lastDay;
+        }
+    }
+
+    /**
+     * Get the last visible day of the week. Returns the weekdays as integers
+     * represented by {@link java.util.Calendar#DAY_OF_WEEK}
+     * 
+     * @return An integer representing the week day according to
+     *         {@link java.util.Calendar#DAY_OF_WEEK}
+     */
+    public int getLastVisibleDayOfWeek() {
+        return lastDay;
+    }
+
+    /**
+     * <p>
+     * This method restricts the hours that are shown per day. This affects the
+     * weekly view. The general contract is that <b>firstHour < lastHour</b>.
+     * </p>
+     * 
+     * <p>
+     * Note that this only affects the rendering process. Events are still
+     * requested by the dates set by {@link #setStartDate(Date)} and
+     * {@link #setEndDate(Date)}.
+     * </p>
+     * 
+     * @param firstHour
+     *            the first hour of the day to show, between 0 and 23
+     */
+    public void setFirstVisibleHourOfDay(int firstHour) {
+        if (this.firstHour != firstHour && firstHour >= 0 && firstHour <= 23
+                && firstHour <= getLastVisibleHourOfDay()) {
+            this.firstHour = firstHour;
+            getState().firstHourOfDay = firstHour;
+        }
+    }
+
+    /**
+     * Returns the first visible hour in the week view. Returns the hour using a
+     * 24h time format
+     * 
+     */
+    public int getFirstVisibleHourOfDay() {
+        return firstHour;
+    }
+
+    /**
+     * <p>
+     * This method restricts the hours that are shown per day. This affects the
+     * weekly view. The general contract is that <b>firstHour < lastHour</b>.
+     * </p>
+     * 
+     * <p>
+     * Note that this only affects the rendering process. Events are still
+     * requested by the dates set by {@link #setStartDate(Date)} and
+     * {@link #setEndDate(Date)}.
+     * </p>
+     * 
+     * @param lastHour
+     *            the first hour of the day to show, between 0 and 23
+     */
+    public void setLastVisibleHourOfDay(int lastHour) {
+        if (this.lastHour != lastHour && lastHour >= 0 && lastHour <= 23
+                && lastHour >= getFirstVisibleHourOfDay()) {
+            this.lastHour = lastHour;
+            getState().lastHourOfDay = lastHour;
+        }
+    }
+
+    /**
+     * Returns the last visible hour in the week view. Returns the hour using a
+     * 24h time format
+     * 
+     */
+    public int getLastVisibleHourOfDay() {
+        return lastHour;
+    }
+
+    /**
+     * Gets the date caption format for the weekly view.
+     * 
+     * @return The pattern used in caption of dates in weekly view.
+     */
+    public String getWeeklyCaptionFormat() {
+        return weeklyCaptionFormat;
+    }
+
+    /**
+     * Sets custom date format for the weekly view. This is the caption of the
+     * date. Format could be like "mmm MM/dd".
+     * 
+     * @param dateFormatPattern
+     *            The date caption pattern.
+     */
+    public void setWeeklyCaptionFormat(String dateFormatPattern) {
+        if ((weeklyCaptionFormat == null && dateFormatPattern != null)
+                || (weeklyCaptionFormat != null && !weeklyCaptionFormat
+                        .equals(dateFormatPattern))) {
+            weeklyCaptionFormat = dateFormatPattern;
+            markAsDirty();
+        }
+    }
+
+    private DateFormat getWeeklyCaptionFormatter() {
+        if (weeklyCaptionFormat != null) {
+            return new SimpleDateFormat(weeklyCaptionFormat, getLocale());
+        } else {
+            return SimpleDateFormat.getDateInstance(SimpleDateFormat.SHORT,
+                    getLocale());
+        }
+    }
+
+    /**
+     * Get the day of week by the given calendar and its locale
+     * 
+     * @param calendar
+     *            The calendar to use
+     * @return
+     */
+    private static int getDowByLocale(java.util.Calendar calendar) {
+        int fow = calendar.get(java.util.Calendar.DAY_OF_WEEK);
+
+        // monday first
+        if (calendar.getFirstDayOfWeek() == java.util.Calendar.MONDAY) {
+            fow = (fow == java.util.Calendar.SUNDAY) ? 7 : fow - 1;
+        }
+
+        return fow;
+    }
+
+    /**
+     * Is the user allowed to trigger events which alters the events
+     * 
+     * @return true if the client is allowed to send changes to server
+     * @see #isEventClickAllowed()
+     */
+    protected boolean isClientChangeAllowed() {
+        return !isReadOnly() && isEnabled();
+    }
+
+    /**
+     * Is the user allowed to trigger click events
+     * 
+     * @return true if the client is allowed to click events
+     * @see #isClientChangeAllowed()
+     */
+    protected boolean isEventClickAllowed() {
+        return isEnabled();
+    }
+
+    /**
+     * Fires an event when the user selecing moving forward/backward in the
+     * calendar.
+     * 
+     * @param forward
+     *            True if the calendar moved forward else backward is assumed.
+     */
+    protected void fireNavigationEvent(boolean forward) {
+        if (forward) {
+            fireEvent(new ForwardEvent(this));
+        } else {
+            fireEvent(new BackwardEvent(this));
+        }
+    }
+
+    /**
+     * Fires an event move event to all server side move listerners
+     * 
+     * @param index
+     *            The index of the event in the events list
+     * @param newFromDatetime
+     *            The changed from date time
+     */
+    protected void fireEventMove(int index, Date newFromDatetime) {
+        MoveEvent event = new MoveEvent(this, events.get(index),
+                newFromDatetime);
+
+        if (calendarEventProvider instanceof EventMoveHandler) {
+            // Notify event provider if it is an event move handler
+            ((EventMoveHandler) calendarEventProvider).eventMove(event);
+        }
+
+        // Notify event move handler attached by using the
+        // setHandler(EventMoveHandler) method
+        fireEvent(event);
+    }
+
+    /**
+     * Fires event when a week was clicked in the calendar.
+     * 
+     * @param week
+     *            The week that was clicked
+     * @param year
+     *            The year of the week
+     */
+    protected void fireWeekClick(int week, int year) {
+        fireEvent(new WeekClick(this, week, year));
+    }
+
+    /**
+     * Fires event when a date was clicked in the calendar. Uses an existing
+     * event from the event cache.
+     * 
+     * @param index
+     *            The index of the event in the event cache.
+     */
+    protected void fireEventClick(Integer index) {
+        fireEvent(new EventClick(this, events.get(index)));
+    }
+
+    /**
+     * Fires event when a date was clicked in the calendar. Creates a new event
+     * for the date and passes it to the listener.
+     * 
+     * @param date
+     *            The date and time that was clicked
+     */
+    protected void fireDateClick(Date date) {
+        fireEvent(new DateClickEvent(this, date));
+    }
+
+    /**
+     * Fires an event range selected event. The event is fired when a user
+     * highlights an area in the calendar. The highlighted areas start and end
+     * dates are returned as arguments.
+     * 
+     * @param from
+     *            The start date and time of the highlighted area
+     * @param to
+     *            The end date and time of the highlighted area
+     * @param monthlyMode
+     *            Is the calendar in monthly mode
+     */
+    protected void fireRangeSelect(Date from, Date to, boolean monthlyMode) {
+        fireEvent(new RangeSelectEvent(this, from, to, monthlyMode));
+    }
+
+    /**
+     * Fires an event resize event. The event is fired when a user resizes the
+     * event in the calendar causing the time range of the event to increase or
+     * decrease. The new start and end times are returned as arguments to this
+     * method.
+     * 
+     * @param index
+     *            The index of the event in the event cache
+     * @param startTime
+     *            The new start date and time of the event
+     * @param endTime
+     *            The new end date and time of the event
+     */
+    protected void fireEventResize(int index, Date startTime, Date endTime) {
+        EventResize event = new EventResize(this, events.get(index), startTime,
+                endTime);
+
+        if (calendarEventProvider instanceof EventResizeHandler) {
+            // Notify event provider if it is an event resize handler
+            ((EventResizeHandler) calendarEventProvider).eventResize(event);
+        }
+
+        // Notify event resize handler attached by using the
+        // setHandler(EventMoveHandler) method
+        fireEvent(event);
+    }
+
+    /**
+     * Localized display names for week days starting from sunday. Returned
+     * array's length is always 7.
+     * 
+     * @return Array of localized weekday names.
+     */
+    protected String[] getDayNamesShort() {
+        DateFormatSymbols s = new DateFormatSymbols(getLocale());
+        return Arrays.copyOfRange(s.getWeekdays(), 1, 8);
+    }
+
+    /**
+     * Localized display names for months starting from January. Returned
+     * array's length is always 12.
+     * 
+     * @return Array of localized month names.
+     */
+    protected String[] getMonthNamesShort() {
+        DateFormatSymbols s = new DateFormatSymbols(getLocale());
+        return Arrays.copyOf(s.getShortMonths(), 12);
+    }
+
+    /**
+     * Gets a date that is first day in the week that target given date belongs
+     * to.
+     * 
+     * @param date
+     *            Target date
+     * @return Date that is first date in same week that given date is.
+     */
+    protected Date getFirstDateForWeek(Date date) {
+        int firstDayOfWeek = currentCalendar.getFirstDayOfWeek();
+        currentCalendar.setTime(date);
+        while (firstDayOfWeek != currentCalendar
+                .get(java.util.Calendar.DAY_OF_WEEK)) {
+            currentCalendar.add(java.util.Calendar.DATE, -1);
+        }
+        return currentCalendar.getTime();
+    }
+
+    /**
+     * Gets a date that is last day in the week that target given date belongs
+     * to.
+     * 
+     * @param date
+     *            Target date
+     * @return Date that is last date in same week that given date is.
+     */
+    protected Date getLastDateForWeek(Date date) {
+        currentCalendar.setTime(date);
+        currentCalendar.add(java.util.Calendar.DATE, 1);
+        int firstDayOfWeek = currentCalendar.getFirstDayOfWeek();
+        // Roll to weeks last day using firstdayofweek. Roll until FDofW is
+        // found and then roll back one day.
+        while (firstDayOfWeek != currentCalendar
+                .get(java.util.Calendar.DAY_OF_WEEK)) {
+            currentCalendar.add(java.util.Calendar.DATE, 1);
+        }
+        currentCalendar.add(java.util.Calendar.DATE, -1);
+        return currentCalendar.getTime();
+    }
+
+    /**
+     * Calculates the end time of the day using the given calendar and date
+     * 
+     * @param date
+     * @param calendar
+     *            the calendar instance to be used in the calculation. The given
+     *            instance is unchanged in this operation.
+     * @return the given date, with time set to the end of the day
+     */
+    private static Date getEndOfDay(java.util.Calendar calendar, Date date) {
+        java.util.Calendar calendarClone = (java.util.Calendar) calendar
+                .clone();
+
+        calendarClone.setTime(date);
+        calendarClone.set(java.util.Calendar.MILLISECOND,
+                calendarClone.getActualMaximum(java.util.Calendar.MILLISECOND));
+        calendarClone.set(java.util.Calendar.SECOND,
+                calendarClone.getActualMaximum(java.util.Calendar.SECOND));
+        calendarClone.set(java.util.Calendar.MINUTE,
+                calendarClone.getActualMaximum(java.util.Calendar.MINUTE));
+        calendarClone.set(java.util.Calendar.HOUR,
+                calendarClone.getActualMaximum(java.util.Calendar.HOUR));
+        calendarClone.set(java.util.Calendar.HOUR_OF_DAY,
+                calendarClone.getActualMaximum(java.util.Calendar.HOUR_OF_DAY));
+
+        return calendarClone.getTime();
+    }
+
+    /**
+     * Calculates the end time of the day using the given calendar and date
+     * 
+     * @param date
+     * @param calendar
+     *            the calendar instance to be used in the calculation. The given
+     *            instance is unchanged in this operation.
+     * @return the given date, with time set to the end of the day
+     */
+    private static Date getStartOfDay(java.util.Calendar calendar, Date date) {
+        java.util.Calendar calendarClone = (java.util.Calendar) calendar
+                .clone();
+
+        calendarClone.setTime(date);
+        calendarClone.set(java.util.Calendar.MILLISECOND, 0);
+        calendarClone.set(java.util.Calendar.SECOND, 0);
+        calendarClone.set(java.util.Calendar.MINUTE, 0);
+        calendarClone.set(java.util.Calendar.HOUR, 0);
+        calendarClone.set(java.util.Calendar.HOUR_OF_DAY, 0);
+
+        return calendarClone.getTime();
+    }
+
+    /**
+     * Finds the first day of the week and returns a day representing the start
+     * of that day
+     * 
+     * @param start
+     *            The actual date
+     * @param expandToFullWeek
+     *            Should the returned date be moved to the start of the week
+     * @return If expandToFullWeek is set then it returns the first day of the
+     *         week, else it returns a clone of the actual date with the time
+     *         set to the start of the day
+     */
+    protected Date expandStartDate(Date start, boolean expandToFullWeek) {
+        // If the duration is more than week, use monthly view and get startweek
+        // and endweek. Example if views daterange is from tuesday to next weeks
+        // wednesday->expand to monday to nextweeks sunday. If firstdayofweek =
+        // monday
+        if (expandToFullWeek) {
+            start = getFirstDateForWeek(start);
+
+        } else {
+            start = (Date) start.clone();
+        }
+
+        // Always expand to the start of the first day to the end of the last
+        // day
+        start = getStartOfDay(currentCalendar, start);
+
+        return start;
+    }
+
+    /**
+     * Finds the last day of the week and returns a day representing the end of
+     * that day
+     * 
+     * @param end
+     *            The actual date
+     * @param expandToFullWeek
+     *            Should the returned date be moved to the end of the week
+     * @return If expandToFullWeek is set then it returns the last day of the
+     *         week, else it returns a clone of the actual date with the time
+     *         set to the end of the day
+     */
+    protected Date expandEndDate(Date end, boolean expandToFullWeek) {
+        // If the duration is more than week, use monthly view and get startweek
+        // and endweek. Example if views daterange is from tuesday to next weeks
+        // wednesday->expand to monday to nextweeks sunday. If firstdayofweek =
+        // monday
+        if (expandToFullWeek) {
+            end = getLastDateForWeek(end);
+
+        } else {
+            end = (Date) end.clone();
+        }
+
+        // Always expand to the start of the first day to the end of the last
+        // day
+        end = getEndOfDay(currentCalendar, end);
+
+        return end;
+    }
+
+    /**
+     * Set the {@link com.vaadin.addon.calendar.event.CalendarEventProvider
+     * CalendarEventProvider} to be used with this calendar. The EventProvider
+     * is used to query for events to show, and must be non-null. By default a
+     * {@link com.vaadin.addon.calendar.event.BasicEventProvider
+     * BasicEventProvider} is used.
+     * 
+     * @param calendarEventProvider
+     *            the calendarEventProvider to set. Cannot be null.
+     */
+    public void setEventProvider(CalendarEventProvider calendarEventProvider) {
+        if (calendarEventProvider == null) {
+            throw new IllegalArgumentException(
+                    "Calendar event provider cannot be null");
+        }
+
+        // remove old listener
+        if (getEventProvider() instanceof EventSetChangeNotifier) {
+            ((EventSetChangeNotifier) getEventProvider()).removeEventSetChangeListener(this);
+        }
+
+        this.calendarEventProvider = calendarEventProvider;
+
+        // add new listener
+        if (calendarEventProvider instanceof EventSetChangeNotifier) {
+            ((EventSetChangeNotifier) calendarEventProvider).addEventSetChangeListener(this);
+        }
+    }
+
+    /**
+     * @return the {@link com.vaadin.addon.calendar.event.CalendarEventProvider
+     *         CalendarEventProvider} currently used
+     */
+    public CalendarEventProvider getEventProvider() {
+        return calendarEventProvider;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.ui.CalendarEvents.EventChangeListener#eventChange
+     * (com.vaadin.addon.calendar.ui.CalendarEvents.EventChange)
+     */
+    public void eventSetChange(EventSetChangeEvent changeEvent) {
+        // sanity check
+        if (calendarEventProvider == changeEvent.getProvider()) {
+            markAsDirty();
+        }
+    }
+
+    /**
+     * Set the handler for the given type information. Mirrors
+     * {@link #addListener(String, Class, Object, Method) addListener} from
+     * AbstractComponent
+     * 
+     * @param eventId
+     *            A unique id for the event. Usually one of
+     *            {@link CalendarEventId}
+     * @param eventType
+     *            The class of the event, most likely a subclass of
+     *            {@link CalendarComponentEvent}
+     * @param listener
+     *            A listener that listens to the given event
+     * @param listenerMethod
+     *            The method on the lister to call when the event is triggered
+     */
+    protected void setHandler(String eventId, Class<?> eventType,
+            EventListener listener, Method listenerMethod) {
+        if (handlers.get(eventId) != null) {
+            removeListener(eventId, eventType, handlers.get(eventId));
+            handlers.remove(eventId);
+        }
+
+        if (listener != null) {
+            addListener(eventId, eventType, listener, listenerMethod);
+            handlers.put(eventId, listener);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier
+     * #addListener
+     * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.ForwardHandler)
+     */
+    public void setHandler(ForwardHandler listener) {
+        setHandler(ForwardEvent.EVENT_ID, ForwardEvent.class, listener,
+                ForwardHandler.forwardMethod);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier
+     * #addListener
+     * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.BackwardHandler)
+     */
+    public void setHandler(BackwardHandler listener) {
+        setHandler(BackwardEvent.EVENT_ID, BackwardEvent.class, listener,
+                BackwardHandler.backwardMethod);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier
+     * #addListener
+     * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.DateClickHandler)
+     */
+    public void setHandler(DateClickHandler listener) {
+        setHandler(DateClickEvent.EVENT_ID, DateClickEvent.class, listener,
+                DateClickHandler.dateClickMethod);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier
+     * #addListener
+     * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventClickHandler)
+     */
+    public void setHandler(EventClickHandler listener) {
+        setHandler(EventClick.EVENT_ID, EventClick.class, listener,
+                EventClickHandler.eventClickMethod);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier
+     * #addListener
+     * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.WeekClickHandler)
+     */
+    public void setHandler(WeekClickHandler listener) {
+        setHandler(WeekClick.EVENT_ID, WeekClick.class, listener,
+                WeekClickHandler.weekClickMethod);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResizeNotifier
+     * #addListener
+     * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResizeHandler
+     * )
+     */
+    public void setHandler(EventResizeHandler listener) {
+        setHandler(EventResize.EVENT_ID, EventResize.class, listener,
+                EventResizeHandler.eventResizeMethod);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.ui.CalendarComponentEvents.RangeSelectNotifier
+     * #addListener
+     * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.RangeSelectHandler
+     * )
+     */
+    public void setHandler(RangeSelectHandler listener) {
+        setHandler(RangeSelectEvent.EVENT_ID, RangeSelectEvent.class, listener,
+                RangeSelectHandler.rangeSelectMethod);
+
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventMoveNotifier
+     * #addListener
+     * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventMoveHandler)
+     */
+    public void setHandler(EventMoveHandler listener) {
+        setHandler(MoveEvent.EVENT_ID, MoveEvent.class, listener,
+                EventMoveHandler.eventMoveMethod);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.ui.CalendarComponentEvents.CalendarEventNotifier
+     * #getHandler(java.lang.String)
+     */
+    public EventListener getHandler(String eventId) {
+        return handlers.get(eventId);
+    }
+
+    /**
+     * Get the currently active drop handler
+     */
+    public DropHandler getDropHandler() {
+        return dropHandler;
+    }
+
+    /**
+     * Set the drop handler for the calendar See {@link DropHandler} for
+     * implementation details.
+     * 
+     * @param dropHandler
+     *            The drop handler to set
+     */
+    public void setDropHandler(DropHandler dropHandler) {
+        this.dropHandler = dropHandler;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.event.dd.DropTarget#translateDropTargetDetails(java.util.Map)
+     */
+    public TargetDetails translateDropTargetDetails(
+            Map<String, Object> clientVariables) {
+        Map<String, Object> serverVariables = new HashMap<String, Object>(1);
+
+        if (clientVariables.containsKey("dropSlotIndex")) {
+            int slotIndex = (Integer) clientVariables.get("dropSlotIndex");
+            int dayIndex = (Integer) clientVariables.get("dropDayIndex");
+
+            currentCalendar.setTime(getStartOfDay(currentCalendar, startDate));
+            currentCalendar.add(java.util.Calendar.DATE, dayIndex);
+
+            // change this if slot length is modified
+            currentCalendar.add(java.util.Calendar.MINUTE, slotIndex * 30);
+
+            serverVariables.put("dropTime", currentCalendar.getTime());
+
+        } else {
+            int dayIndex = (Integer) clientVariables.get("dropDayIndex");
+            currentCalendar.setTime(expandStartDate(startDate, true));
+            currentCalendar.add(java.util.Calendar.DATE, dayIndex);
+            serverVariables.put("dropDay", currentCalendar.getTime());
+        }
+
+        CalendarTargetDetails td = new CalendarTargetDetails(serverVariables,
+                this);
+        td.setHasDropTime(clientVariables.containsKey("dropSlotIndex"));
+
+        return td;
+    }
+
+    /**
+     * Sets a container as a data source for the events in the calendar.
+     * Equivalent for doing
+     * <code>Calendar.setEventProvider(new ContainerEventProvider(container))</code>
+     * 
+     * Use this method if you are adding a container which uses the default
+     * property ids like {@link BeanItemContainer} for instance. If you are
+     * using custom properties instead use
+     * {@link Calendar#setContainerDataSource(com.vaadin.data.Container.Indexed, Object, Object, Object, Object, Object)}
+     * 
+     * Please note that the container must be sorted by date!
+     * 
+     * @param container
+     *            The container to use as a datasource
+     */
+    public void setContainerDataSource(Container.Indexed container) {
+        ContainerEventProvider provider = new ContainerEventProvider(container);
+        provider.addEventSetChangeListener(new CalendarEventProvider.EventSetChangeListener() {
+            public void eventSetChange(EventSetChangeEvent changeEvent) {
+                // Repaint if events change
+                markAsDirty();
+            }
+        });
+        provider.addEventChangeListener(new EventChangeListener() {
+            public void eventChange(EventChangeEvent changeEvent) {
+                // Repaint if event changes
+                markAsDirty();
+            }
+        });
+        setEventProvider(provider);
+    }
+
+    /**
+     * Sets a container as a data source for the events in the calendar.
+     * Equivalent for doing
+     * <code>Calendar.setEventProvider(new ContainerEventProvider(container))</code>
+     * 
+     * Please note that the container must be sorted by date!
+     * 
+     * @param container
+     *            The container to use as a data source
+     * @param captionProperty
+     *            The property that has the caption, null if no caption property
+     *            is present
+     * @param descriptionProperty
+     *            The property that has the description, null if no description
+     *            property is present
+     * @param startDateProperty
+     *            The property that has the starting date
+     * @param endDateProperty
+     *            The property that has the ending date
+     * @param styleNameProperty
+     *            The property that has the stylename, null if no stylname
+     *            property is present
+     */
+    public void setContainerDataSource(Container.Indexed container,
+            Object captionProperty, Object descriptionProperty,
+            Object startDateProperty, Object endDateProperty,
+            Object styleNameProperty) {
+        ContainerEventProvider provider = new ContainerEventProvider(container);
+        provider.setCaptionProperty(captionProperty);
+        provider.setDescriptionProperty(descriptionProperty);
+        provider.setStartDateProperty(startDateProperty);
+        provider.setEndDateProperty(endDateProperty);
+        provider.setStyleNameProperty(styleNameProperty);
+        provider.addEventSetChangeListener(new CalendarEventProvider.EventSetChangeListener() {
+            public void eventSetChange(EventSetChangeEvent changeEvent) {
+                // Repaint if events change
+                markAsDirty();
+            }
+        });
+        provider.addEventChangeListener(new EventChangeListener() {
+            public void eventChange(EventChangeEvent changeEvent) {
+                // Repaint if event changes
+                markAsDirty();
+            }
+        });
+        setEventProvider(provider);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.event.CalendarEventProvider#getEvents(java.
+     * util.Date, java.util.Date)
+     */
+    public List<CalendarEvent> getEvents(Date startDate, Date endDate) {
+        return getEventProvider().getEvents(startDate, endDate);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#addEvent
+     * (com.vaadin.addon.calendar.event.CalendarEvent)
+     */
+    public void addEvent(CalendarEvent event) {
+        if (getEventProvider() instanceof CalendarEditableEventProvider) {
+            CalendarEditableEventProvider provider = (CalendarEditableEventProvider) getEventProvider();
+            provider.addEvent(event);
+            markAsDirty();
+        } else {
+            throw new UnsupportedOperationException(
+                    "Event provider does not support adding events");
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#removeEvent
+     * (com.vaadin.addon.calendar.event.CalendarEvent)
+     */
+    public void removeEvent(CalendarEvent event) {
+        if (getEventProvider() instanceof CalendarEditableEventProvider) {
+            CalendarEditableEventProvider provider = (CalendarEditableEventProvider) getEventProvider();
+            provider.removeEvent(event);
+            markAsDirty();
+        } else {
+            throw new UnsupportedOperationException(
+                    "Event provider does not support removing events");
+        }
+    }
+
+    /**
+     * Adds an action handler to the calender that handles event produced by the
+     * context menu.
+     * 
+     * <p>
+     * The {@link Handler#getActions(Object, Object)} parameters depend on what
+     * view the Calendar is in:
+     * <ul>
+     * <li>If the Calendar is in <i>Day or Week View</i> then the target
+     * parameter will be a {@link CalendarDateRange} with a range of
+     * half-an-hour. The {@link Handler#getActions(Object, Object)} method will
+     * be called once per half-hour slot.</li>
+     * <li>If the Calendar is in <i>Month View</i> then the target parameter
+     * will be a {@link CalendarDateRange} with a range of one day. The
+     * {@link Handler#getActions(Object, Object)} will be called once for each
+     * day.
+     * </ul>
+     * The Dates passed into the {@link CalendarDateRange} are in the same
+     * timezone as the calendar is.
+     * </p>
+     * 
+     * <p>
+     * The {@link Handler#handleAction(Action, Object, Object)} parameters
+     * depend on what the context menu is called upon:
+     * <ul>
+     * <li>If the context menu is called upon an event then the target parameter
+     * is the event, i.e. instanceof {@link CalendarEvent}</li>
+     * <li>If the context menu is called upon an empty slot then the target is a
+     * {@link Date} representing that slot
+     * </ul>
+     * </p>
+     */
+    public void addActionHandler(Handler actionHandler) {
+        if (actionHandler != null) {
+            if (actionHandlers == null) {
+                actionHandlers = new LinkedList<Action.Handler>();
+                actionMapper = new KeyMapper<Action>();
+            }
+            if (!actionHandlers.contains(actionHandler)) {
+                actionHandlers.add(actionHandler);
+                markAsDirty();
+            }
+        }
+    }
+
+    /**
+     * Is the calendar in a mode where all days of the month is shown
+     * 
+     * @return Returns true if calendar is in monthly mode and false if it is in
+     *         weekly mode
+     */
+    public boolean isMonthlyMode() {
+        CalendarState state = (CalendarState) getState(false);
+        if (state.days != null) {
+            return state.days.size() > 7;
+        } else {
+            // Default mode
+            return true;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.event.Action.Container#removeActionHandler(com.vaadin.event
+     * .Action.Handler)
+     */
+    public void removeActionHandler(Handler actionHandler) {
+        if (actionHandlers != null && actionHandlers.contains(actionHandler)) {
+            actionHandlers.remove(actionHandler);
+            if (actionHandlers.isEmpty()) {
+                actionHandlers = null;
+                actionMapper = null;
+            }
+            markAsDirty();
+        }
+    }
+
+    private class CalendarServerRpcImpl implements CalendarServerRpc {
+
+        @Override
+        public void eventMove(int eventIndex, String newDate) {
+            if (!isClientChangeAllowed()) {
+                return;
+            }
+            if (newDate != null) {
+                try {
+                    Date d = df_date_time.parse(newDate);
+                    if (eventIndex >= 0 && eventIndex < events.size()
+                            && events.get(eventIndex) != null) {
+                        fireEventMove(eventIndex, d);
+                    }
+                } catch (ParseException e) {
+                    getLogger().log(Level.WARNING, e.getMessage());
+                }
+            }
+        }
+
+        @Override
+        public void rangeSelect(String range) {
+            if (!isClientChangeAllowed()) {
+                return;
+            }
+
+            if (range != null && range.length() > 14 && range.contains("TO")) {
+                String[] dates = range.split("TO");
+                try {
+                    Date d1 = df_date.parse(dates[0]);
+                    Date d2 = df_date.parse(dates[1]);
+
+                    fireRangeSelect(d1, d2, true);
+
+                } catch (ParseException e) {
+                    // NOP
+                }
+            } else if (range != null && range.length() > 12
+                    && range.contains(":")) {
+                String[] dates = range.split(":");
+                if (dates.length == 3) {
+                    try {
+                        Date d = df_date.parse(dates[0]);
+                        currentCalendar.setTime(d);
+                        int startMinutes = Integer.parseInt(dates[1]);
+                        int endMinutes = Integer.parseInt(dates[2]);
+                        currentCalendar.add(java.util.Calendar.MINUTE,
+                                startMinutes);
+                        Date start = currentCalendar.getTime();
+                        currentCalendar.add(java.util.Calendar.MINUTE,
+                                endMinutes - startMinutes);
+                        Date end = currentCalendar.getTime();
+                        fireRangeSelect(start, end, false);
+                    } catch (ParseException e) {
+                        // NOP
+                    } catch (NumberFormatException e) {
+                        // NOP
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void forward() {
+            fireEvent(new ForwardEvent(Calendar.this));
+        }
+
+        @Override
+        public void backward() {
+            fireEvent(new BackwardEvent(Calendar.this));
+        }
+
+        @Override
+        public void dateClick(String date) {
+            if (!isClientChangeAllowed()) {
+                return;
+            }
+            if (date != null && date.length() > 6) {
+                try {
+                    Date d = df_date.parse(date);
+                    fireDateClick(d);
+                } catch (ParseException e) {
+                }
+            }
+        }
+
+        @Override
+        public void weekClick(String event) {
+            if (!isClientChangeAllowed()) {
+                return;
+            }
+            if (event.length() > 0 && event.contains("w")) {
+                String[] splitted = event.split("w");
+                if (splitted.length == 2) {
+                    try {
+                        int yr = 1900 + Integer.parseInt(splitted[0]);
+                        int week = Integer.parseInt(splitted[1]);
+                        fireWeekClick(week, yr);
+                    } catch (NumberFormatException e) {
+                        // NOP
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void eventClick(int eventIndex) {
+            if (!isEventClickAllowed()) {
+                return;
+            }
+            if (eventIndex >= 0 && eventIndex < events.size()
+                    && events.get(eventIndex) != null) {
+                fireEventClick(eventIndex);
+            }
+        }
+
+        @Override
+        public void eventResize(int eventIndex, String newStartDate,
+                String newEndDate) {
+            if (!isClientChangeAllowed()) {
+                return;
+            }
+            if (newStartDate != null && !"".equals(newStartDate)
+                    && newEndDate != null && !"".equals(newEndDate)) {
+                try {
+                    Date newStartTime = df_date_time.parse(newStartDate);
+                    Date newEndTime = df_date_time.parse(newEndDate);
+
+                    fireEventResize(eventIndex, newStartTime, newEndTime);
+                } catch (ParseException e) {
+                    // NOOP
+                }
+            }
+        }
+
+        @Override
+        public void scroll(int scrollPosition) {
+            scrollTop = scrollPosition;
+            markAsDirty();
+        }
+
+        @Override
+        public void actionOnEmptyCell(String actionKey, String startDate,
+                String endDate) {
+            Action action = actionMapper.get(actionKey);
+            SimpleDateFormat formatter = new SimpleDateFormat(
+                    DateConstants.ACTION_DATE_FORMAT_PATTERN);
+            formatter.setTimeZone(getTimeZone());
+            try {
+                Date start = formatter.parse(startDate);
+                for (Action.Handler ah : actionHandlers) {
+                    ah.handleAction(action, this, start);
+                }
+
+            } catch (ParseException e) {
+                getLogger().log(Level.WARNING,
+                        "Could not parse action date string");
+            }
+
+        }
+
+        @Override
+        public void actionOnEvent(String actionKey, String startDate,
+                String endDate, int eventIndex) {
+            Action action = actionMapper.get(actionKey);
+            SimpleDateFormat formatter = new SimpleDateFormat(
+                    DateConstants.ACTION_DATE_FORMAT_PATTERN);
+            formatter.setTimeZone(getTimeZone());
+            for (Action.Handler ah : actionHandlers) {
+                ah.handleAction(action, this, events.get(eventIndex));
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/server/src/com/vaadin/ui/components/calendar/CalendarComponentEvent.java b/server/src/com/vaadin/ui/components/calendar/CalendarComponentEvent.java
new file mode 100644 (file)
index 0000000..1f01215
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.ui.components.calendar;
+
+import com.vaadin.ui.Calendar;
+import com.vaadin.ui.Component;
+
+/**
+ * All Calendar events extends this class.
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ * 
+ */
+@SuppressWarnings("serial")
+public class CalendarComponentEvent extends Component.Event {
+
+    /**
+     * Set the source of the event
+     * 
+     * @param source
+     *            The source calendar
+     * 
+     */
+    public CalendarComponentEvent(Calendar source) {
+        super(source);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.ui.Component.Event#getComponent()
+     */
+    @Override
+    public Calendar getComponent() {
+        return (Calendar) super.getComponent();
+    }
+}
diff --git a/server/src/com/vaadin/ui/components/calendar/CalendarComponentEvents.java b/server/src/com/vaadin/ui/components/calendar/CalendarComponentEvents.java
new file mode 100644 (file)
index 0000000..1904d69
--- /dev/null
@@ -0,0 +1,603 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.ui.components.calendar;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.Date;
+import java.util.EventListener;
+
+import com.vaadin.shared.ui.calendar.CalendarEventId;
+import com.vaadin.ui.Calendar;
+import com.vaadin.ui.components.calendar.event.CalendarEvent;
+import com.vaadin.util.ReflectTools;
+
+/**
+ * Interface for all Vaadin Calendar events.
+ * 
+ * @since 7.1.0
+ * @author Vaadin Ltd.
+ */
+public interface CalendarComponentEvents extends Serializable {
+
+    /**
+     * Notifier interface for notifying listener of calendar events
+     */
+    public interface CalendarEventNotifier extends Serializable {
+        /**
+         * Get the assigned event handler for the given eventId.
+         * 
+         * @param eventId
+         * @return the assigned eventHandler, or null if no handler is assigned
+         */
+        public EventListener getHandler(String eventId);
+    }
+
+    /**
+     * Notifier interface for event drag & drops.
+     */
+    public interface EventMoveNotifier extends CalendarEventNotifier {
+
+        /**
+         * Set the EventMoveHandler.
+         * 
+         * @param listener
+         *            EventMoveHandler to be added
+         */
+        public void setHandler(EventMoveHandler listener);
+
+    }
+
+    /**
+     * MoveEvent is sent when existing event is dragged to a new position.
+     */
+    @SuppressWarnings("serial")
+    public class MoveEvent extends CalendarComponentEvent {
+
+        public static final String EVENT_ID = CalendarEventId.EVENTMOVE;
+
+        /** Index for the moved Schedule.Event. */
+        private CalendarEvent calendarEvent;
+
+        /** New starting date for the moved Calendar.Event. */
+        private Date newStart;
+
+        /**
+         * MoveEvent needs the target event and new start date.
+         * 
+         * @param source
+         *            Calendar component.
+         * @param calendarEvent
+         *            Target event.
+         * @param newStart
+         *            Target event's new start date.
+         */
+        public MoveEvent(Calendar source, CalendarEvent calendarEvent,
+                Date newStart) {
+            super(source);
+
+            this.calendarEvent = calendarEvent;
+            this.newStart = newStart;
+        }
+
+        /**
+         * Get target event.
+         * 
+         * @return Target event.
+         */
+        public CalendarEvent getCalendarEvent() {
+            return calendarEvent;
+        }
+
+        /**
+         * Get new start date.
+         * 
+         * @return New start date.
+         */
+        public Date getNewStart() {
+            return newStart;
+        }
+    }
+
+    /**
+     * Handler interface for when events are being dragged on the calendar
+     * 
+     */
+    public interface EventMoveHandler extends EventListener, Serializable {
+
+        /** Trigger method for the MoveEvent. */
+        public static final Method eventMoveMethod = ReflectTools.findMethod(
+                EventMoveHandler.class, "eventMove", MoveEvent.class);
+
+        /**
+         * This method will be called when event has been moved to a new
+         * position.
+         * 
+         * @param event
+         *            MoveEvent containing specific information of the new
+         *            position and target event.
+         */
+        public void eventMove(MoveEvent event);
+    }
+
+    /**
+     * Handler interface for day or time cell drag-marking with mouse.
+     */
+    public interface RangeSelectNotifier extends Serializable,
+            CalendarEventNotifier {
+
+        /**
+         * Set the RangeSelectHandler that listens for drag-marking.
+         * 
+         * @param listener
+         *            RangeSelectHandler to be added.
+         */
+        public void setHandler(RangeSelectHandler listener);
+    }
+
+    /**
+     * RangeSelectEvent is sent when day or time cells are drag-marked with
+     * mouse.
+     */
+    @SuppressWarnings("serial")
+    public class RangeSelectEvent extends CalendarComponentEvent {
+
+        public static final String EVENT_ID = CalendarEventId.RANGESELECT;
+
+        /** Calendar event's start date. */
+        private Date start;
+
+        /** Calendar event's end date. */
+        private Date end;
+
+        /**
+         * Defines the event's view mode.
+         */
+        private boolean monthlyMode;
+
+        /**
+         * RangeSelectEvent needs a start and end date.
+         * 
+         * @param source
+         *            Calendar component.
+         * @param start
+         *            Start date.
+         * @param end
+         *            End date.
+         * @param monthlyMode
+         *            Calendar view mode.
+         */
+        public RangeSelectEvent(Calendar source, Date start, Date end,
+                boolean monthlyMode) {
+            super(source);
+            this.start = start;
+            this.end = end;
+            this.monthlyMode = monthlyMode;
+        }
+
+        /**
+         * Get start date.
+         * 
+         * @return Start date.
+         */
+        public Date getStart() {
+            return start;
+        }
+
+        /**
+         * Get end date.
+         * 
+         * @return End date.
+         */
+        public Date getEnd() {
+            return end;
+        }
+
+        /**
+         * Gets the event's view mode. Calendar can be be either in monthly or
+         * weekly mode, depending on the active date range.
+         * 
+         * @deprecated User {@link Calendar#isMonthlyMode()} instead
+         * 
+         * @return Returns true when monthly view is active.
+         */
+        @Deprecated
+        public boolean isMonthlyMode() {
+            return monthlyMode;
+        }
+    }
+
+    /** RangeSelectHandler handles RangeSelectEvent. */
+    public interface RangeSelectHandler extends EventListener, Serializable {
+
+        /** Trigger method for the RangeSelectEvent. */
+        public static final Method rangeSelectMethod = ReflectTools
+                .findMethod(RangeSelectHandler.class, "rangeSelect",
+                        RangeSelectEvent.class);
+
+        /**
+         * This method will be called when day or time cells are drag-marked
+         * with mouse.
+         * 
+         * @param event
+         *            RangeSelectEvent that contains range start and end date.
+         */
+        public void rangeSelect(RangeSelectEvent event);
+    }
+
+    /** Notifier interface for navigation listening. */
+    public interface NavigationNotifier extends Serializable {
+        /**
+         * Add a forward navigation listener.
+         * 
+         * @param handler
+         *            ForwardHandler to be added.
+         */
+        public void setHandler(ForwardHandler handler);
+
+        /**
+         * Add a backward navigation listener.
+         * 
+         * @param handler
+         *            BackwardHandler to be added.
+         */
+        public void setHandler(BackwardHandler handler);
+
+        /**
+         * Add a date click listener.
+         * 
+         * @param handler
+         *            DateClickHandler to be added.
+         */
+        public void setHandler(DateClickHandler handler);
+
+        /**
+         * Add a event click listener.
+         * 
+         * @param handler
+         *            EventClickHandler to be added.
+         */
+        public void setHandler(EventClickHandler handler);
+
+        /**
+         * Add a week click listener.
+         * 
+         * @param handler
+         *            WeekClickHandler to be added.
+         */
+        public void setHandler(WeekClickHandler handler);
+    }
+
+    /**
+     * ForwardEvent is sent when forward navigation button is clicked.
+     */
+    @SuppressWarnings("serial")
+    public class ForwardEvent extends CalendarComponentEvent {
+
+        public static final String EVENT_ID = CalendarEventId.FORWARD;
+
+        /**
+         * ForwardEvent needs only the source component.
+         * 
+         * @param source
+         *            Calendar component.
+         */
+        public ForwardEvent(Calendar source) {
+            super(source);
+        }
+    }
+
+    /** ForwardHandler handles ForwardEvent. */
+    public interface ForwardHandler extends EventListener, Serializable {
+
+        /** Trigger method for the ForwardEvent. */
+        public static final Method forwardMethod = ReflectTools.findMethod(
+                ForwardHandler.class, "forward", ForwardEvent.class);
+
+        /**
+         * This method will be called when date range is moved forward.
+         * 
+         * @param event
+         *            ForwardEvent
+         */
+        public void forward(ForwardEvent event);
+    }
+
+    /**
+     * BackwardEvent is sent when backward navigation button is clicked.
+     */
+    @SuppressWarnings("serial")
+    public class BackwardEvent extends CalendarComponentEvent {
+
+        public static final String EVENT_ID = CalendarEventId.BACKWARD;
+
+        /**
+         * BackwardEvent needs only the source source component.
+         * 
+         * @param source
+         *            Calendar component.
+         */
+        public BackwardEvent(Calendar source) {
+            super(source);
+        }
+    }
+
+    /** BackwardHandler handles BackwardEvent. */
+    public interface BackwardHandler extends EventListener, Serializable {
+
+        /** Trigger method for the BackwardEvent. */
+        public static final Method backwardMethod = ReflectTools.findMethod(
+                BackwardHandler.class, "backward", BackwardEvent.class);
+
+        /**
+         * This method will be called when date range is moved backwards.
+         * 
+         * @param event
+         *            BackwardEvent
+         */
+        public void backward(BackwardEvent event);
+    }
+
+    /**
+     * DateClickEvent is sent when a date is clicked.
+     */
+    @SuppressWarnings("serial")
+    public class DateClickEvent extends CalendarComponentEvent {
+
+        public static final String EVENT_ID = CalendarEventId.DATECLICK;
+
+        /** Date that was clicked. */
+        private Date date;
+
+        /** DateClickEvent needs the target date that was clicked. */
+        public DateClickEvent(Calendar source, Date date) {
+            super(source);
+            this.date = date;
+        }
+
+        /**
+         * Get clicked date.
+         * 
+         * @return Clicked date.
+         */
+        public Date getDate() {
+            return date;
+        }
+    }
+
+    /** DateClickHandler handles DateClickEvent. */
+    public interface DateClickHandler extends EventListener, Serializable {
+
+        /** Trigger method for the DateClickEvent. */
+        public static final Method dateClickMethod = ReflectTools.findMethod(
+                DateClickHandler.class, "dateClick", DateClickEvent.class);
+
+        /**
+         * This method will be called when a date is clicked.
+         * 
+         * @param event
+         *            DateClickEvent containing the target date.
+         */
+        public void dateClick(DateClickEvent event);
+    }
+
+    /**
+     * EventClick is sent when an event is clicked.
+     */
+    @SuppressWarnings("serial")
+    public class EventClick extends CalendarComponentEvent {
+
+        public static final String EVENT_ID = CalendarEventId.EVENTCLICK;
+
+        /** Clicked source event. */
+        private CalendarEvent calendarEvent;
+
+        /** Target source event is needed for the EventClick. */
+        public EventClick(Calendar source, CalendarEvent calendarEvent) {
+            super(source);
+            this.calendarEvent = calendarEvent;
+        }
+
+        /**
+         * Get the clicked event.
+         * 
+         * @return Clicked event.
+         */
+        public CalendarEvent getCalendarEvent() {
+            return calendarEvent;
+        }
+    }
+
+    /** EventClickHandler handles EventClick. */
+    public interface EventClickHandler extends EventListener, Serializable {
+
+        /** Trigger method for the EventClick. */
+        public static final Method eventClickMethod = ReflectTools.findMethod(
+                EventClickHandler.class, "eventClick", EventClick.class);
+
+        /**
+         * This method will be called when an event is clicked.
+         * 
+         * @param event
+         *            EventClick containing the target event.
+         */
+        public void eventClick(EventClick event);
+    }
+
+    /**
+     * WeekClick is sent when week is clicked.
+     */
+    @SuppressWarnings("serial")
+    public class WeekClick extends CalendarComponentEvent {
+
+        public static final String EVENT_ID = CalendarEventId.WEEKCLICK;
+
+        /** Target week. */
+        private int week;
+
+        /** Target year. */
+        private int year;
+
+        /**
+         * WeekClick needs a target year and week.
+         * 
+         * @param source
+         *            Target source.
+         * @param week
+         *            Target week.
+         * @param year
+         *            Target year.
+         */
+        public WeekClick(Calendar source, int week, int year) {
+            super(source);
+            this.week = week;
+            this.year = year;
+        }
+
+        /**
+         * Get week as a integer. See {@link java.util.Calendar} for the allowed
+         * values.
+         * 
+         * @return Week as a integer.
+         */
+        public int getWeek() {
+            return week;
+        }
+
+        /**
+         * Get year as a integer. See {@link java.util.Calendar} for the allowed
+         * values.
+         * 
+         * @return Year as a integer
+         */
+        public int getYear() {
+            return year;
+        }
+    }
+
+    /** WeekClickHandler handles WeekClicks. */
+    public interface WeekClickHandler extends EventListener, Serializable {
+
+        /** Trigger method for the WeekClick. */
+        public static final Method weekClickMethod = ReflectTools.findMethod(
+                WeekClickHandler.class, "weekClick", WeekClick.class);
+
+        /**
+         * This method will be called when a week is clicked.
+         * 
+         * @param event
+         *            WeekClick containing the target week and year.
+         */
+        public void weekClick(WeekClick event);
+    }
+
+    /**
+     * EventResize is sent when an event is resized
+     */
+    @SuppressWarnings("serial")
+    public class EventResize extends CalendarComponentEvent {
+
+        public static final String EVENT_ID = CalendarEventId.EVENTRESIZE;
+
+        private CalendarEvent calendarEvent;
+
+        private Date startTime;
+
+        private Date endTime;
+
+        public EventResize(Calendar source, CalendarEvent calendarEvent,
+                Date startTime, Date endTime) {
+            super(source);
+            this.calendarEvent = calendarEvent;
+            this.startTime = startTime;
+            this.endTime = endTime;
+        }
+
+        /**
+         * Get target event.
+         * 
+         * @return Target event.
+         */
+        public CalendarEvent getCalendarEvent() {
+            return calendarEvent;
+        }
+
+        /**
+         * @deprecated Use {@link #getNewStart()} instead
+         * 
+         * @return the new start time
+         */
+        @Deprecated
+        public Date getNewStartTime() {
+            return startTime;
+        }
+
+        /**
+         * Returns the updated start date/time of the event
+         * 
+         * @return The new date for the event
+         */
+        public Date getNewStart() {
+            return startTime;
+        }
+
+        /**
+         * @deprecated Use {@link #getNewEnd()} instead
+         * 
+         * @return the new end time
+         */
+        @Deprecated
+        public Date getNewEndTime() {
+            return endTime;
+        }
+
+        /**
+         * Returns the updates end date/time of the event
+         * 
+         * @return The new date for the event
+         */
+        public Date getNewEnd() {
+            return endTime;
+        }
+    }
+
+    /**
+     * Notifier interface for event resizing.
+     */
+    public interface EventResizeNotifier extends Serializable {
+
+        /**
+         * Set a EventResizeHandler.
+         * 
+         * @param handler
+         *            EventResizeHandler to be set
+         */
+        public void setHandler(EventResizeHandler handler);
+    }
+
+    /**
+     * Handler for EventResize event.
+     */
+    public interface EventResizeHandler extends EventListener, Serializable {
+
+        /** Trigger method for the EventResize. */
+        public static final Method eventResizeMethod = ReflectTools.findMethod(
+                EventResizeHandler.class, "eventResize", EventResize.class);
+
+        void eventResize(EventResize event);
+    }
+
+}
diff --git a/server/src/com/vaadin/ui/components/calendar/CalendarDateRange.java b/server/src/com/vaadin/ui/components/calendar/CalendarDateRange.java
new file mode 100644 (file)
index 0000000..01b766a
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.ui.components.calendar;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * Class for representing a date range.
+ * 
+ * @since 7.1.0
+ * @author Vaadin Ltd.
+ * 
+ */
+@SuppressWarnings("serial")
+public class CalendarDateRange implements Serializable {
+
+    private Date start;
+
+    private Date end;
+
+    private final transient TimeZone tz;
+
+    /**
+     * Constructor
+     * 
+     * @param start
+     *            The start date and time of the date range
+     * @param end
+     *            The end date and time of the date range
+     */
+    public CalendarDateRange(Date start, Date end, TimeZone tz) {
+        super();
+        this.start = start;
+        this.end = end;
+        this.tz = tz;
+    }
+
+    /**
+     * Get the start date of the date range
+     * 
+     * @return the start Date of the range
+     */
+    public Date getStart() {
+        return start;
+    }
+
+    /**
+     * Get the end date of the date range
+     * 
+     * @return the end Date of the range
+     */
+    public Date getEnd() {
+        return end;
+    }
+
+    /**
+     * Is a date in the date range
+     * 
+     * @param date
+     *            The date to check
+     * @return true if the date range contains a date start and end of range
+     *         inclusive; false otherwise
+     */
+    public boolean inRange(Date date) {
+        if (date == null) {
+            return false;
+        }
+
+        return date.compareTo(start) >= 0 && date.compareTo(end) <= 0;
+    }
+}
diff --git a/server/src/com/vaadin/ui/components/calendar/CalendarTargetDetails.java b/server/src/com/vaadin/ui/components/calendar/CalendarTargetDetails.java
new file mode 100644 (file)
index 0000000..1a3ef67
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.ui.components.calendar;
+
+import java.util.Date;
+import java.util.Map;
+
+import com.vaadin.event.dd.DropTarget;
+import com.vaadin.event.dd.TargetDetailsImpl;
+import com.vaadin.ui.Calendar;
+
+/**
+ * Drop details for {@link com.vaadin.ui.addon.calendar.ui.Calendar Calendar}.
+ * When something is dropped on the Calendar, this class contains the specific
+ * details of the drop point. Specifically, this class gives access to the date
+ * where the drop happened. If the Calendar was in weekly mode, the date also
+ * includes the start time of the slot.
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ */
+@SuppressWarnings("serial")
+public class CalendarTargetDetails extends TargetDetailsImpl {
+
+    private boolean hasDropTime;
+
+    public CalendarTargetDetails(Map<String, Object> rawDropData,
+            DropTarget dropTarget) {
+        super(rawDropData, dropTarget);
+    }
+
+    /**
+     * @return true if {@link #getDropTime()} will return a date object with the
+     *         time set to the start of the time slot where the drop happened
+     */
+    public boolean hasDropTime() {
+        return hasDropTime;
+    }
+
+    /**
+     * Does the dropped item have a time associated with it
+     * 
+     * @param hasDropTime
+     */
+    public void setHasDropTime(boolean hasDropTime) {
+        this.hasDropTime = hasDropTime;
+    }
+
+    /**
+     * @return the date where the drop happened
+     */
+    public Date getDropTime() {
+        if (hasDropTime) {
+            return (Date) getData("dropTime");
+        } else {
+            return (Date) getData("dropDay");
+        }
+    }
+
+    /**
+     * @return the {@link com.vaadin.ui.addon.calendar.ui.Calendar Calendar}
+     *         instance which was the target of the drop
+     */
+    public Calendar getTargetCalendar() {
+        return (Calendar) getTarget();
+    }
+}
diff --git a/server/src/com/vaadin/ui/components/calendar/ContainerEventProvider.java b/server/src/com/vaadin/ui/components/calendar/ContainerEventProvider.java
new file mode 100644 (file)
index 0000000..b01140e
--- /dev/null
@@ -0,0 +1,566 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.ui.components.calendar;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+import com.vaadin.data.Container;
+import com.vaadin.data.Container.Indexed;
+import com.vaadin.data.Container.ItemSetChangeEvent;
+import com.vaadin.data.Container.ItemSetChangeNotifier;
+import com.vaadin.data.Item;
+import com.vaadin.data.Property;
+import com.vaadin.data.Property.ValueChangeEvent;
+import com.vaadin.data.Property.ValueChangeNotifier;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventMoveHandler;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventResize;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventResizeHandler;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.MoveEvent;
+import com.vaadin.ui.components.calendar.event.BasicEvent;
+import com.vaadin.ui.components.calendar.event.CalendarEditableEventProvider;
+import com.vaadin.ui.components.calendar.event.CalendarEvent;
+import com.vaadin.ui.components.calendar.event.CalendarEvent.EventChangeListener;
+import com.vaadin.ui.components.calendar.event.CalendarEvent.EventChangeNotifier;
+import com.vaadin.ui.components.calendar.event.CalendarEventProvider;
+import com.vaadin.ui.components.calendar.event.CalendarEventProvider.EventSetChangeNotifier;
+
+/**
+ * A event provider which uses a {@link Container} as a datasource. Container
+ * used as data source.
+ * 
+ * NOTE: The data source must be sorted by date!
+ * 
+ * @since 7.1.0
+ * @author Vaadin Ltd.
+ */
+@SuppressWarnings("serial")
+public class ContainerEventProvider implements CalendarEditableEventProvider,
+        EventSetChangeNotifier, EventChangeNotifier, EventMoveHandler,
+        EventResizeHandler, Container.ItemSetChangeListener,
+        Property.ValueChangeListener {
+
+    // Default property ids
+    public static final String CAPTION_PROPERTY = "caption";
+    public static final String DESCRIPTION_PROPERTY = "description";
+    public static final String STARTDATE_PROPERTY = "start";
+    public static final String ENDDATE_PROPERTY = "end";
+    public static final String STYLENAME_PROPERTY = "styleName";
+
+    /**
+     * Internal class to keep the container index which item this event
+     * represents
+     * 
+     */
+    private class ContainerCalendarEvent extends BasicEvent {
+        private final int index;
+
+        public ContainerCalendarEvent(int containerIndex) {
+            super();
+            index = containerIndex;
+        }
+
+        public int getContainerIndex() {
+            return index;
+        }
+    }
+
+    /**
+     * Listeners attached to the container
+     */
+    private final List<EventSetChangeListener> eventSetChangeListeners = new LinkedList<CalendarEventProvider.EventSetChangeListener>();
+    private final List<EventChangeListener> eventChangeListeners = new LinkedList<CalendarEvent.EventChangeListener>();
+
+    /**
+     * The event cache contains the events previously created by
+     * {@link #getEvents(Date, Date)}
+     */
+    private final List<CalendarEvent> eventCache = new LinkedList<CalendarEvent>();
+
+    /**
+     * The container used as datasource
+     */
+    private Indexed container;
+
+    /**
+     * Container properties. Defaults based on using the {@link BasicEvent}
+     * helper class.
+     */
+    private Object captionProperty = CAPTION_PROPERTY;
+    private Object descriptionProperty = DESCRIPTION_PROPERTY;
+    private Object startDateProperty = STARTDATE_PROPERTY;
+    private Object endDateProperty = ENDDATE_PROPERTY;
+    private Object styleNameProperty = STYLENAME_PROPERTY;
+
+    /**
+     * Constructor
+     * 
+     * @param container
+     *            Container to use as a data source.
+     */
+    public ContainerEventProvider(Container.Indexed container) {
+        this.container = container;
+        listenToContainerEvents();
+    }
+
+    /**
+     * Set the container data source
+     * 
+     * @param container
+     *            The container to use as datasource
+     * 
+     */
+    public void setContainerDataSource(Container.Indexed container) {
+        // Detach the previous container
+        detachContainerDataSource();
+
+        this.container = container;
+        listenToContainerEvents();
+    }
+
+    /**
+     * Returns the container used as data source
+     * 
+     */
+    public Container.Indexed getContainerDataSource() {
+        return container;
+    }
+
+    /**
+     * Attaches listeners to the container so container events can be processed
+     */
+    private void listenToContainerEvents() {
+        if (container instanceof ItemSetChangeNotifier) {
+            ((ItemSetChangeNotifier) container).addItemSetChangeListener(this);
+        }
+        if (container instanceof ValueChangeNotifier) {
+            ((ValueChangeNotifier) container).addValueChangeListener(this);
+        }
+    }
+
+    /**
+     * Removes listeners from the container so no events are processed
+     */
+    private void ignoreContainerEvents() {
+        if (container instanceof ItemSetChangeNotifier) {
+            ((ItemSetChangeNotifier) container)
+                    .removeItemSetChangeListener(this);
+        }
+        if (container instanceof ValueChangeNotifier) {
+            ((ValueChangeNotifier) container).removeValueChangeListener(this);
+        }
+    }
+
+    /**
+     * Converts an event in the container to an {@link CalendarEvent}
+     * 
+     * @param index
+     *            The index of the item in the container to get the event for
+     * @return
+     */
+    private CalendarEvent getEvent(int index) {
+
+        // Check the event cache first
+        for (CalendarEvent e : eventCache) {
+            if (e instanceof ContainerCalendarEvent
+                    && ((ContainerCalendarEvent) e).getContainerIndex() == index) {
+                return e;
+            } else if (container.getIdByIndex(index) == e) {
+                return e;
+            }
+        }
+
+        final Object id = container.getIdByIndex(index);
+        Item item = container.getItem(id);
+        CalendarEvent event;
+        if (id instanceof CalendarEvent) {
+            /*
+             * If we are using the BeanItemContainer or another container which
+             * stores the objects as ids then just return the instances
+             */
+            event = (CalendarEvent) id;
+
+        } else {
+            /*
+             * Else we use the properties to create the event
+             */
+            BasicEvent basicEvent = new ContainerCalendarEvent(index);
+
+            // Set values from property values
+            if (captionProperty != null
+                    && item.getItemPropertyIds().contains(captionProperty)) {
+                basicEvent.setCaption(String.valueOf(item.getItemProperty(
+                        captionProperty).getValue()));
+            }
+            if (descriptionProperty != null
+                    && item.getItemPropertyIds().contains(descriptionProperty)) {
+                basicEvent.setDescription(String.valueOf(item.getItemProperty(
+                        descriptionProperty).getValue()));
+            }
+            if (startDateProperty != null
+                    && item.getItemPropertyIds().contains(startDateProperty)) {
+                basicEvent.setStart((Date) item.getItemProperty(
+                        startDateProperty).getValue());
+            }
+            if (endDateProperty != null
+                    && item.getItemPropertyIds().contains(endDateProperty)) {
+                basicEvent.setEnd((Date) item.getItemProperty(endDateProperty)
+                        .getValue());
+            }
+            if (styleNameProperty != null
+                    && item.getItemPropertyIds().contains(styleNameProperty)) {
+                basicEvent.setDescription(String.valueOf(item.getItemProperty(
+                        descriptionProperty).getValue()));
+            }
+            event = basicEvent;
+        }
+        return event;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.event.CalendarEventProvider#getEvents(java.
+     * util.Date, java.util.Date)
+     */
+    public List<CalendarEvent> getEvents(Date startDate, Date endDate) {
+        eventCache.clear();
+
+        int[] rangeIndexes = getFirstAndLastEventIndex(startDate, endDate);
+        for (int i = rangeIndexes[0]; i <= rangeIndexes[1]
+                && i < container.size(); i++) {
+            eventCache.add(getEvent(i));
+        }
+        return Collections.unmodifiableList(eventCache);
+    }
+
+    /**
+     * Get the first event for a date
+     * 
+     * @param date
+     *            The date to search for, NUll returns first event in container
+     * @return Returns an array where the first item is the start index and the
+     *         second item is the end item
+     */
+    private int[] getFirstAndLastEventIndex(Date start, Date end) {
+        int startIndex = 0;
+        int size = container.size();
+        int endIndex = size - 1;
+
+        if (start != null) {
+            /*
+             * Iterating from the start of the container, if range is in the end
+             * of the container then this will be slow TODO This could be
+             * improved by using some sort of divide and conquer algorithm
+             */
+            while (startIndex < size) {
+                Object id = container.getIdByIndex(startIndex);
+                Item item = container.getItem(id);
+                Date d = (Date) item.getItemProperty(startDateProperty)
+                        .getValue();
+                if (d.compareTo(start) >= 0) {
+                    break;
+                }
+                startIndex++;
+            }
+        }
+
+        if (end != null) {
+            /*
+             * Iterate from the start index until range ends
+             */
+            endIndex = startIndex;
+            while (endIndex < size - 1) {
+                Object id = container.getIdByIndex(endIndex);
+                Item item = container.getItem(id);
+                Date d = (Date) item.getItemProperty(endDateProperty)
+                        .getValue();
+                if (d == null) {
+                    // No end date present, use start date
+                    d = (Date) item.getItemProperty(startDateProperty)
+                            .getValue();
+                }
+                if (d.compareTo(end) >= 0) {
+                    endIndex--;
+                    break;
+                }
+                endIndex++;
+            }
+        }
+
+        return new int[] { startIndex, endIndex };
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.event.CalendarEventProvider.EventSetChangeNotifier
+     * #addListener(com.vaadin.addon.calendar.event.CalendarEventProvider.
+     * EventSetChangeListener)
+     */
+    public void addEventSetChangeListener(EventSetChangeListener listener) {
+        if (!eventSetChangeListeners.contains(listener)) {
+            eventSetChangeListeners.add(listener);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.event.CalendarEventProvider.EventSetChangeNotifier
+     * #removeListener(com.vaadin.addon.calendar.event.CalendarEventProvider.
+     * EventSetChangeListener)
+     */
+    public void removeEventSetChangeListener(EventSetChangeListener listener) {
+        eventSetChangeListeners.remove(listener);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.event.CalendarEvent.EventChangeNotifier#addListener
+     * (com.vaadin.addon.calendar.event.CalendarEvent.EventChangeListener)
+     */
+    public void addEventChangeListener(EventChangeListener listener) {
+        if (eventChangeListeners.contains(listener)) {
+            eventChangeListeners.add(listener);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.addon.calendar.event.CalendarEvent.EventChangeNotifier#
+     * removeListener
+     * (com.vaadin.addon.calendar.event.CalendarEvent.EventChangeListener)
+     */
+    public void removeEventChangeListener(EventChangeListener listener) {
+        eventChangeListeners.remove(listener);
+    }
+
+    /**
+     * Get the property which provides the caption of the event
+     */
+    public Object getCaptionProperty() {
+        return captionProperty;
+    }
+
+    /**
+     * Set the property which provides the caption of the event
+     */
+    public void setCaptionProperty(Object captionProperty) {
+        this.captionProperty = captionProperty;
+    }
+
+    /**
+     * Get the property which provides the description of the event
+     */
+    public Object getDescriptionProperty() {
+        return descriptionProperty;
+    }
+
+    /**
+     * Set the property which provides the description of the event
+     */
+    public void setDescriptionProperty(Object descriptionProperty) {
+        this.descriptionProperty = descriptionProperty;
+    }
+
+    /**
+     * Get the property which provides the starting date and time of the event
+     */
+    public Object getStartDateProperty() {
+        return startDateProperty;
+    }
+
+    /**
+     * Set the property which provides the starting date and time of the event
+     */
+    public void setStartDateProperty(Object startDateProperty) {
+        this.startDateProperty = startDateProperty;
+    }
+
+    /**
+     * Get the property which provides the ending date and time of the event
+     */
+    public Object getEndDateProperty() {
+        return endDateProperty;
+    }
+
+    /**
+     * Set the property which provides the ending date and time of the event
+     */
+    public void setEndDateProperty(Object endDateProperty) {
+        this.endDateProperty = endDateProperty;
+    }
+
+    /**
+     * Get the property which provides the style name for the event
+     */
+    public Object getStyleNameProperty() {
+        return styleNameProperty;
+    }
+
+    /**
+     * Set the property which provides the style name for the event
+     */
+    public void setStyleNameProperty(Object styleNameProperty) {
+        this.styleNameProperty = styleNameProperty;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.data.Container.ItemSetChangeListener#containerItemSetChange
+     * (com.vaadin.data.Container.ItemSetChangeEvent)
+     */
+    public void containerItemSetChange(ItemSetChangeEvent event) {
+        if (event.getContainer() == container) {
+            // Trigger an eventset change event when the itemset changes
+            for (EventSetChangeListener listener : eventSetChangeListeners) {
+                listener.eventSetChange(new EventSetChangeEvent(this));
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.data.Property.ValueChangeListener#valueChange(com.vaadin.data
+     * .Property.ValueChangeEvent)
+     */
+    public void valueChange(ValueChangeEvent event) {
+        /*
+         * TODO Need to figure out how to get the item which triggered the the
+         * valuechange event and then trigger a EventChange event to the
+         * listeners
+         */
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventMoveHandler
+     * #eventMove
+     * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.MoveEvent)
+     */
+    public void eventMove(MoveEvent event) {
+        CalendarEvent ce = event.getCalendarEvent();
+        if (eventCache.contains(ce)) {
+            int index;
+            if (ce instanceof ContainerCalendarEvent) {
+                index = ((ContainerCalendarEvent) ce).getContainerIndex();
+            } else {
+                index = container.indexOfId(ce);
+            }
+
+            long eventLength = ce.getEnd().getTime() - ce.getStart().getTime();
+            Date newEnd = new Date(event.getNewStart().getTime() + eventLength);
+
+            ignoreContainerEvents();
+            Item item = container.getItem(container.getIdByIndex(index));
+            item.getItemProperty(startDateProperty).setValue(
+                    event.getNewStart());
+            item.getItemProperty(endDateProperty).setValue(newEnd);
+            listenToContainerEvents();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResizeHandler
+     * #eventResize
+     * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResize)
+     */
+    public void eventResize(EventResize event) {
+        CalendarEvent ce = event.getCalendarEvent();
+        if (eventCache.contains(ce)) {
+            int index;
+            if (ce instanceof ContainerCalendarEvent) {
+                index = ((ContainerCalendarEvent) ce).getContainerIndex();
+            } else {
+                index = container.indexOfId(ce);
+            }
+            ignoreContainerEvents();
+            Item item = container.getItem(container.getIdByIndex(index));
+            item.getItemProperty(startDateProperty).setValue(
+                    event.getNewStart());
+            item.getItemProperty(endDateProperty).setValue(event.getNewEnd());
+            listenToContainerEvents();
+        }
+    }
+
+    /**
+     * If you are reusing the container which previously have been attached to
+     * this ContainerEventProvider call this method to remove this event
+     * providers container listeners before attaching it to an other
+     * ContainerEventProvider
+     */
+    public void detachContainerDataSource() {
+        ignoreContainerEvents();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#addEvent
+     * (com.vaadin.addon.calendar.event.CalendarEvent)
+     */
+    public void addEvent(CalendarEvent event) {
+        Item item;
+        try {
+            item = container.addItem(event);
+        } catch (UnsupportedOperationException uop) {
+            // Thrown if container does not support adding items with custom
+            // ids. JPAContainer for example.
+            item = container.getItem(container.addItem());
+        }
+        if (item != null) {
+            item.getItemProperty(getCaptionProperty()).setValue(
+                    event.getCaption());
+            item.getItemProperty(getStartDateProperty()).setValue(
+                    event.getStart());
+            item.getItemProperty(getEndDateProperty()).setValue(event.getEnd());
+            item.getItemProperty(getStyleNameProperty()).setValue(
+                    event.getStyleName());
+            item.getItemProperty(getDescriptionProperty()).setValue(
+                    event.getDescription());
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#removeEvent
+     * (com.vaadin.addon.calendar.event.CalendarEvent)
+     */
+    public void removeEvent(CalendarEvent event) {
+        container.removeItem(event);
+    }
+}
diff --git a/server/src/com/vaadin/ui/components/calendar/event/BasicEvent.java b/server/src/com/vaadin/ui/components/calendar/event/BasicEvent.java
new file mode 100644 (file)
index 0000000..ab342df
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.ui.components.calendar.event;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import com.vaadin.ui.components.calendar.event.CalendarEvent.EventChangeNotifier;
+
+/**
+ * Simple implementation of
+ * {@link com.vaadin.addon.calendar.event.CalendarEvent CalendarEvent}. Has
+ * setters for all required fields and fires events when this event is changed.
+ * 
+ * @since 7.1.0
+ * @author Vaadin Ltd.
+ */
+@SuppressWarnings("serial")
+public class BasicEvent implements EditableCalendarEvent, EventChangeNotifier {
+
+    private String caption;
+    private String description;
+    private Date end;
+    private Date start;
+    private String styleName;
+    private transient List<EventChangeListener> listeners = new ArrayList<EventChangeListener>();
+
+    private boolean isAllDay;
+
+    /**
+     * Default constructor
+     */
+    public BasicEvent() {
+
+    }
+
+    /**
+     * Constructor for creating an event with the same start and end date
+     * 
+     * @param caption
+     *            The caption for the event
+     * @param description
+     *            The description for the event
+     * @param date
+     *            The date the event occurred
+     */
+    public BasicEvent(String caption, String description, Date date) {
+        this.caption = caption;
+        this.description = description;
+        start = date;
+        end = date;
+    }
+
+    /**
+     * Constructor for creating an event with a start date and an end date.
+     * Start date should be before the end date
+     * 
+     * @param caption
+     *            The caption for the event
+     * @param description
+     *            The description for the event
+     * @param startDate
+     *            The start date of the event
+     * @param endDate
+     *            The end date of the event
+     */
+    public BasicEvent(String caption, String description, Date startDate,
+            Date endDate) {
+        this.caption = caption;
+        this.description = description;
+        start = startDate;
+        end = endDate;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.addon.calendar.event.CalendarEvent#getCaption()
+     */
+    public String getCaption() {
+        return caption;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.addon.calendar.event.CalendarEvent#getDescription()
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.addon.calendar.event.CalendarEvent#getEnd()
+     */
+    public Date getEnd() {
+        return end;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.addon.calendar.event.CalendarEvent#getStart()
+     */
+    public Date getStart() {
+        return start;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.addon.calendar.event.CalendarEvent#getStyleName()
+     */
+    public String getStyleName() {
+        return styleName;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.addon.calendar.event.CalendarEvent#isAllDay()
+     */
+    public boolean isAllDay() {
+        return isAllDay;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.event.CalendarEventEditor#setCaption(java.lang
+     * .String)
+     */
+    public void setCaption(String caption) {
+        this.caption = caption;
+        fireEventChange();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.event.CalendarEventEditor#setDescription(java
+     * .lang.String)
+     */
+    public void setDescription(String description) {
+        this.description = description;
+        fireEventChange();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.event.CalendarEventEditor#setEnd(java.util.
+     * Date)
+     */
+    public void setEnd(Date end) {
+        this.end = end;
+        fireEventChange();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.event.CalendarEventEditor#setStart(java.util
+     * .Date)
+     */
+    public void setStart(Date start) {
+        this.start = start;
+        fireEventChange();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.event.CalendarEventEditor#setStyleName(java
+     * .lang.String)
+     */
+    public void setStyleName(String styleName) {
+        this.styleName = styleName;
+        fireEventChange();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.event.CalendarEventEditor#setAllDay(boolean)
+     */
+    public void setAllDay(boolean isAllDay) {
+        this.isAllDay = isAllDay;
+        fireEventChange();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventChangeNotifier
+     * #addListener
+     * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventChangeListener
+     * )
+     */
+    public void addEventChangeListener(EventChangeListener listener) {
+        listeners.add(listener);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventChangeNotifier
+     * #removeListener
+     * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventChangeListener
+     * )
+     */
+    public void removeEventChangeListener(EventChangeListener listener) {
+        listeners.remove(listener);
+    }
+
+    /**
+     * Fires an event change event to the listeners. Should be triggered when
+     * some property of the event changes.
+     */
+    protected void fireEventChange() {
+        EventChangeEvent event = new EventChangeEvent(this);
+
+        for (EventChangeListener listener : listeners) {
+            listener.eventChange(event);
+        }
+    }
+}
diff --git a/server/src/com/vaadin/ui/components/calendar/event/BasicEventProvider.java b/server/src/com/vaadin/ui/components/calendar/event/BasicEventProvider.java
new file mode 100644 (file)
index 0000000..0314652
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.ui.components.calendar.event;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import com.vaadin.ui.components.calendar.event.CalendarEvent.EventChangeEvent;
+import com.vaadin.ui.components.calendar.event.CalendarEventProvider.EventSetChangeNotifier;
+
+/**
+ * <p>
+ * Simple implementation of
+ * {@link com.vaadin.addon.calendar.event.CalendarEventProvider
+ * CalendarEventProvider}. Use {@link #addEvent(CalendarEvent)} and
+ * {@link #removeEvent(CalendarEvent)} to add / remove events.
+ * </p>
+ * 
+ * <p>
+ * {@link com.vaadin.addon.calendar.event.CalendarEventProvider.EventSetChangeNotifier
+ * EventSetChangeNotifier} and
+ * {@link com.vaadin.addon.calendar.event.CalendarEvent.EventChangeListener
+ * EventChangeListener} are also implemented, so the Calendar is notified when
+ * an event is added, changed or removed.
+ * </p>
+ * 
+ * @since 7.1.0
+ * @author Vaadin Ltd.
+ */
+@SuppressWarnings("serial")
+public class BasicEventProvider implements CalendarEditableEventProvider,
+        EventSetChangeNotifier, CalendarEvent.EventChangeListener {
+
+    protected List<CalendarEvent> eventList = new ArrayList<CalendarEvent>();
+
+    private List<EventSetChangeListener> listeners = new ArrayList<EventSetChangeListener>();
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.event.CalendarEventProvider#getEvents(java.
+     * util.Date, java.util.Date)
+     */
+    public List<CalendarEvent> getEvents(Date startDate, Date endDate) {
+        ArrayList<CalendarEvent> activeEvents = new ArrayList<CalendarEvent>();
+
+        for (CalendarEvent ev : eventList) {
+            long from = startDate.getTime();
+            long to = endDate.getTime();
+
+            if (ev.getStart() != null && ev.getEnd() != null) {
+                long f = ev.getStart().getTime();
+                long t = ev.getEnd().getTime();
+                // Select only events that overlaps with startDate and
+                // endDate.
+                if ((f <= to && f >= from) || (t >= from && t <= to)
+                        || (f <= from && t >= to)) {
+                    activeEvents.add(ev);
+                }
+            }
+        }
+
+        return activeEvents;
+    }
+
+    /**
+     * Does this event provider container this event
+     * 
+     * @param event
+     *            The event to check for
+     * @return If this provider has the event then true is returned, else false
+     */
+    public boolean containsEvent(BasicEvent event) {
+        return eventList.contains(event);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventSetChangeNotifier
+     * #addListener
+     * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventSetChangeListener
+     * )
+     */
+    public void addEventSetChangeListener(EventSetChangeListener listener) {
+        listeners.add(listener);
+
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventSetChangeNotifier
+     * #removeListener
+     * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventSetChangeListener
+     * )
+     */
+    public void removeEventSetChangeListener(EventSetChangeListener listener) {
+        listeners.remove(listener);
+    }
+
+    /**
+     * Fires a eventsetchange event. The event is fired when either an event is
+     * added or removed to the event provider
+     */
+    protected void fireEventSetChange() {
+        EventSetChangeEvent event = new EventSetChangeEvent(this);
+
+        for (EventSetChangeListener listener : listeners) {
+            listener.eventSetChange(event);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventChangeListener
+     * #eventChange
+     * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventSetChange)
+     */
+    public void eventChange(EventChangeEvent changeEvent) {
+        // naive implementation
+        fireEventSetChange();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#addEvent
+     * (com.vaadin.addon.calendar.event.CalendarEvent)
+     */
+    public void addEvent(CalendarEvent event) {
+        eventList.add(event);
+        if (event instanceof BasicEvent) {
+            ((BasicEvent) event).addEventChangeListener(this);
+        }
+        fireEventSetChange();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#removeEvent
+     * (com.vaadin.addon.calendar.event.CalendarEvent)
+     */
+    public void removeEvent(CalendarEvent event) {
+        eventList.remove(event);
+        if (event instanceof BasicEvent) {
+            ((BasicEvent) event).removeEventChangeListener(this);
+        }
+        fireEventSetChange();
+    }
+}
diff --git a/server/src/com/vaadin/ui/components/calendar/event/CalendarEditableEventProvider.java b/server/src/com/vaadin/ui/components/calendar/event/CalendarEditableEventProvider.java
new file mode 100644 (file)
index 0000000..145d2b4
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */package com.vaadin.ui.components.calendar.event;
+
+/**
+ * An event provider which allows adding and removing events
+ * 
+ * @since 7.1.0
+ * @author Vaadin Ltd.
+ */
+public interface CalendarEditableEventProvider extends CalendarEventProvider {
+
+    /**
+     * Adds an event to the event provider
+     * 
+     * @param event
+     *            The event to add
+     */
+    void addEvent(CalendarEvent event);
+
+    /**
+     * Removes an event from the event provider
+     * 
+     * @param event
+     *            The event
+     */
+    void removeEvent(CalendarEvent event);
+}
diff --git a/server/src/com/vaadin/ui/components/calendar/event/CalendarEvent.java b/server/src/com/vaadin/ui/components/calendar/event/CalendarEvent.java
new file mode 100644 (file)
index 0000000..531ee72
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.ui.components.calendar.event;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * <p>
+ * Event in the calendar. Customize your own event by implementing this
+ * interface.
+ * </p>
+ * 
+ * <li>Start and end fields are mandatory.</li>
+ * 
+ * <li>In "allDay" events longer than one day, starting and ending clock times
+ * are omitted in UI and only dates are shown.</li>
+ * 
+ * @since 7.1.0
+ * @author Vaadin Ltd.
+ * 
+ */
+public interface CalendarEvent extends Serializable {
+
+    /**
+     * Gets start date of event.
+     * 
+     * @return Start date.
+     */
+    public Date getStart();
+
+    /**
+     * Get end date of event.
+     * 
+     * @return End date;
+     */
+    public Date getEnd();
+
+    /**
+     * Gets caption of event.
+     * 
+     * @return Caption text
+     */
+    public String getCaption();
+
+    /**
+     * Gets description of event. Shown as a tooltip over the event.
+     * 
+     * @return Description text.
+     */
+    public String getDescription();
+
+    /**
+     * <p>
+     * Gets style name of event. In the client, style name will be set to the
+     * event's element class name and can be styled by CSS
+     * </p>
+     * Styling example:</br> <code>Java code: </br>
+     * event.setStyleName("color1");
+     * </br></br>
+     * CSS:</br>
+     * .v-calendar-event-color1 {</br>
+     * &nbsp;&nbsp;&nbsp;background-color: #9effae;</br>}</code>
+     * 
+     * @return Style name.
+     */
+    public String getStyleName();
+
+    /**
+     * An all-day event typically does not occur at a specific time but targets
+     * a whole day or days. The rendering of all-day events differs from normal
+     * events.
+     * 
+     * @return true if this event is an all-day event, false otherwise
+     */
+    public boolean isAllDay();
+
+    /**
+     * Event to signal that an event has changed.
+     */
+    @SuppressWarnings("serial")
+    public class EventChangeEvent implements Serializable {
+
+        private CalendarEvent source;
+
+        public EventChangeEvent(CalendarEvent source) {
+            this.source = source;
+        }
+
+        /**
+         * @return the {@link com.vaadin.addon.calendar.event.CalendarEvent
+         *         CalendarEvent} that has changed
+         */
+        public CalendarEvent getCalendarEvent() {
+            return source;
+        }
+    }
+
+    /**
+     * Listener for EventSetChange events.
+     */
+    public interface EventChangeListener extends Serializable {
+
+        /**
+         * Called when an Event has changed.
+         */
+        public void eventChange(EventChangeEvent eventChangeEvent);
+    }
+
+    /**
+     * Notifier interface for EventChange events.
+     */
+    public interface EventChangeNotifier extends Serializable {
+
+        /**
+         * Add a listener to listen for EventChangeEvents. These events are
+         * fired when a events properties are changed.
+         * 
+         * @param listener
+         *            The listener to add
+         */
+        void addEventChangeListener(EventChangeListener listener);
+
+        /**
+         * Remove a listener from the event provider.
+         * 
+         * @param listener
+         *            The listener to remove
+         */
+        void removeEventChangeListener(EventChangeListener listener);
+    }
+
+}
diff --git a/server/src/com/vaadin/ui/components/calendar/event/CalendarEventProvider.java b/server/src/com/vaadin/ui/components/calendar/event/CalendarEventProvider.java
new file mode 100644 (file)
index 0000000..fefb2ca
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.ui.components.calendar.event;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Interface for querying events. The Vaadin Calendar always has a
+ * CalendarEventProvider set.
+ * 
+ * @since 7.1.0
+ * @author Vaadin Ltd.
+ */
+public interface CalendarEventProvider extends Serializable {
+    /**
+     * <p>
+     * Gets all available events in the target date range between startDate and
+     * endDate. The Vaadin Calendar queries the events from the range that is
+     * shown, which is not guaranteed to be the same as the date range that is
+     * set.
+     * </p>
+     * 
+     * <p>
+     * For example, if you set the date range to be monday 22.2.2010 - wednesday
+     * 24.2.2000, the used Event Provider will be queried for events between
+     * monday 22.2.2010 00:00 and sunday 28.2.2010 23:59. Generally you can
+     * expect the date range to be expanded to whole days and whole weeks.
+     * </p>
+     * 
+     * @param startDate
+     *            Start date
+     * @param endDate
+     *            End date
+     * @return List of events
+     */
+    public List<CalendarEvent> getEvents(Date startDate, Date endDate);
+
+    /**
+     * Event to signal that the set of events has changed and the calendar
+     * should refresh its view from the
+     * {@link com.vaadin.addon.calendar.event.CalendarEventProvider
+     * CalendarEventProvider} .
+     * 
+     */
+    @SuppressWarnings("serial")
+    public class EventSetChangeEvent implements Serializable {
+
+        private CalendarEventProvider source;
+
+        public EventSetChangeEvent(CalendarEventProvider source) {
+            this.source = source;
+        }
+
+        /**
+         * @return the
+         *         {@link com.vaadin.addon.calendar.event.CalendarEventProvider
+         *         CalendarEventProvider} that has changed
+         */
+        public CalendarEventProvider getProvider() {
+            return source;
+        }
+    }
+
+    /**
+     * Listener for EventSetChange events.
+     */
+    public interface EventSetChangeListener extends Serializable {
+
+        /**
+         * Called when the set of Events has changed.
+         */
+        public void eventSetChange(EventSetChangeEvent changeEvent);
+    }
+
+    /**
+     * Notifier interface for EventSetChange events.
+     */
+    public interface EventSetChangeNotifier extends Serializable {
+
+        /**
+         * Add a listener for listening to when new events are adding or removed
+         * from the event provider.
+         * 
+         * @param listener
+         *            The listener to add
+         */
+        void addEventSetChangeListener(EventSetChangeListener listener);
+
+        /**
+         * Remove a listener which listens to {@link EventSetChangeEvent}-events
+         * 
+         * @param listener
+         *            The listener to remove
+         */
+        void removeEventSetChangeListener(EventSetChangeListener listener);
+    }
+}
diff --git a/server/src/com/vaadin/ui/components/calendar/event/EditableCalendarEvent.java b/server/src/com/vaadin/ui/components/calendar/event/EditableCalendarEvent.java
new file mode 100644 (file)
index 0000000..e8a27ad
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.ui.components.calendar.event;
+
+import java.util.Date;
+
+/**
+ * <p>
+ * Extension to the basic {@link com.vaadin.addon.calendar.event.CalendarEvent
+ * CalendarEvent}. This interface provides setters (and thus editing
+ * capabilities) for all {@link com.vaadin.addon.calendar.event.CalendarEvent
+ * CalendarEvent} fields. For descriptions on the fields, refer to the extended
+ * interface.
+ * </p>
+ * 
+ * <p>
+ * This interface is used by some of the basic Calendar event handlers in the
+ * <code>com.vaadin.addon.calendar.ui.handler</code> package to determine
+ * whether an event can be edited.
+ * </p>
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ */
+public interface EditableCalendarEvent extends CalendarEvent {
+
+    /**
+     * Set the visible text in the calendar for the event.
+     * 
+     * @param caption
+     *            The text to show in the calendar
+     */
+    void setCaption(String caption);
+
+    /**
+     * Set the description of the event. This is shown in the calendar when
+     * hoovering over the event.
+     * 
+     * @param description
+     *            The text which describes the event
+     */
+    void setDescription(String description);
+
+    /**
+     * Set the end date of the event. Must be after the start date.
+     * 
+     * @param end
+     *            The end date to set
+     */
+    void setEnd(Date end);
+
+    /**
+     * Set the start date for the event. Must be before the end date
+     * 
+     * @param start
+     *            The start date of the event
+     */
+    void setStart(Date start);
+
+    /**
+     * Set the style name for the event used for styling the event cells
+     * 
+     * @param styleName
+     *            The stylename to use
+     * 
+     */
+    void setStyleName(String styleName);
+
+    /**
+     * Does the event span the whole day. If so then set this to true
+     * 
+     * @param isAllDay
+     *            True if the event spans the whole day. In this case the start
+     *            and end times are ignored.
+     */
+    void setAllDay(boolean isAllDay);
+
+}
diff --git a/server/src/com/vaadin/ui/components/calendar/handler/BasicBackwardHandler.java b/server/src/com/vaadin/ui/components/calendar/handler/BasicBackwardHandler.java
new file mode 100644 (file)
index 0000000..fc2bfd6
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.ui.components.calendar.handler;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import com.vaadin.shared.ui.calendar.DateConstants;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.BackwardEvent;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.BackwardHandler;
+
+/**
+ * Implements basic functionality needed to enable backwards navigation.
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ */
+@SuppressWarnings("serial")
+public class BasicBackwardHandler implements BackwardHandler {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.ui.CalendarComponentEvents.BackwardHandler#
+     * backward
+     * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.BackwardEvent)
+     */
+    public void backward(BackwardEvent event) {
+        Date start = event.getComponent().getStartDate();
+        Date end = event.getComponent().getEndDate();
+
+        // calculate amount to move back
+        int durationInDays = (int) (((end.getTime()) - start.getTime()) / DateConstants.DAYINMILLIS);
+        durationInDays++;
+        durationInDays = -durationInDays;
+
+        // set new start and end times
+        Calendar javaCalendar = event.getComponent().getInternalCalendar();
+        javaCalendar.setTime(start);
+        javaCalendar.add(java.util.Calendar.DATE, durationInDays);
+        Date newStart = javaCalendar.getTime();
+
+        javaCalendar.setTime(end);
+        javaCalendar.add(java.util.Calendar.DATE, durationInDays);
+        Date newEnd = javaCalendar.getTime();
+
+        setDates(event, newStart, newEnd);
+    }
+
+    /**
+     * Set the start and end dates for the event
+     * 
+     * @param event
+     *            The event that the start and end dates should be set
+     * @param start
+     *            The start date
+     * @param end
+     *            The end date
+     */
+    protected void setDates(BackwardEvent event, Date start, Date end) {
+        event.getComponent().setStartDate(start);
+        event.getComponent().setEndDate(end);
+    }
+}
diff --git a/server/src/com/vaadin/ui/components/calendar/handler/BasicDateClickHandler.java b/server/src/com/vaadin/ui/components/calendar/handler/BasicDateClickHandler.java
new file mode 100644 (file)
index 0000000..c91a238
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.ui.components.calendar.handler;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.DateClickEvent;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.DateClickHandler;
+
+/**
+ * Implements basic functionality needed to switch to day view when a single day
+ * is clicked.
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ */
+@SuppressWarnings("serial")
+public class BasicDateClickHandler implements DateClickHandler {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.ui.CalendarComponentEvents.DateClickHandler
+     * #dateClick
+     * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.DateClickEvent)
+     */
+    public void dateClick(DateClickEvent event) {
+        Date clickedDate = event.getDate();
+
+        Calendar javaCalendar = event.getComponent().getInternalCalendar();
+        javaCalendar.setTime(clickedDate);
+
+        // as times are expanded, this is all that is needed to show one day
+        Date start = javaCalendar.getTime();
+        Date end = javaCalendar.getTime();
+
+        setDates(event, start, end);
+    }
+
+    /**
+     * Set the start and end dates for the event
+     * 
+     * @param event
+     *            The event that the start and end dates should be set
+     * @param start
+     *            The start date
+     * @param end
+     *            The end date
+     */
+    protected void setDates(DateClickEvent event, Date start, Date end) {
+        event.getComponent().setStartDate(start);
+        event.getComponent().setEndDate(end);
+    }
+}
diff --git a/server/src/com/vaadin/ui/components/calendar/handler/BasicEventMoveHandler.java b/server/src/com/vaadin/ui/components/calendar/handler/BasicEventMoveHandler.java
new file mode 100644 (file)
index 0000000..139837f
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.ui.components.calendar.handler;
+
+import java.util.Date;
+
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventMoveHandler;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.MoveEvent;
+import com.vaadin.ui.components.calendar.event.CalendarEvent;
+import com.vaadin.ui.components.calendar.event.EditableCalendarEvent;
+
+/**
+ * Implements basic functionality needed to enable moving events.
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ */
+@SuppressWarnings("serial")
+public class BasicEventMoveHandler implements EventMoveHandler {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventMoveHandler
+     * #eventMove
+     * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.MoveEvent)
+     */
+    public void eventMove(MoveEvent event) {
+        CalendarEvent calendarEvent = event.getCalendarEvent();
+
+        if (calendarEvent instanceof EditableCalendarEvent) {
+
+            EditableCalendarEvent editableEvent = (EditableCalendarEvent) calendarEvent;
+
+            Date newFromTime = event.getNewStart();
+
+            // Update event dates
+            long length = editableEvent.getEnd().getTime()
+                    - editableEvent.getStart().getTime();
+            setDates(editableEvent, newFromTime, new Date(newFromTime.getTime()
+                    + length));
+        }
+    }
+
+    /**
+     * Set the start and end dates for the event
+     * 
+     * @param event
+     *            The event that the start and end dates should be set
+     * @param start
+     *            The start date
+     * @param end
+     *            The end date
+     */
+    protected void setDates(EditableCalendarEvent event, Date start, Date end) {
+        event.setStart(start);
+        event.setEnd(end);
+    }
+}
diff --git a/server/src/com/vaadin/ui/components/calendar/handler/BasicEventResizeHandler.java b/server/src/com/vaadin/ui/components/calendar/handler/BasicEventResizeHandler.java
new file mode 100644 (file)
index 0000000..c052d0d
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.ui.components.calendar.handler;
+
+import java.util.Date;
+
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventResize;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventResizeHandler;
+import com.vaadin.ui.components.calendar.event.CalendarEvent;
+import com.vaadin.ui.components.calendar.event.EditableCalendarEvent;
+
+/**
+ * Implements basic functionality needed to enable event resizing.
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ */
+@SuppressWarnings("serial")
+public class BasicEventResizeHandler implements EventResizeHandler {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResizeHandler
+     * #eventResize
+     * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResize)
+     */
+    public void eventResize(EventResize event) {
+        CalendarEvent calendarEvent = event.getCalendarEvent();
+
+        if (calendarEvent instanceof EditableCalendarEvent) {
+            Date newStartTime = event.getNewStart();
+            Date newEndTime = event.getNewEnd();
+
+            EditableCalendarEvent editableEvent = (EditableCalendarEvent) calendarEvent;
+
+            setDates(editableEvent, newStartTime, newEndTime);
+        }
+    }
+
+    /**
+     * Set the start and end dates for the event
+     * 
+     * @param event
+     *            The event that the start and end dates should be set
+     * @param start
+     *            The start date
+     * @param end
+     *            The end date
+     */
+    protected void setDates(EditableCalendarEvent event, Date start, Date end) {
+        event.setStart(start);
+        event.setEnd(end);
+    }
+}
diff --git a/server/src/com/vaadin/ui/components/calendar/handler/BasicForwardHandler.java b/server/src/com/vaadin/ui/components/calendar/handler/BasicForwardHandler.java
new file mode 100644 (file)
index 0000000..a5307ff
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.ui.components.calendar.handler;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import com.vaadin.shared.ui.calendar.DateConstants;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.ForwardEvent;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.ForwardHandler;
+
+/**
+ * Implements basic functionality needed to enable forward navigation.
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ */
+@SuppressWarnings("serial")
+public class BasicForwardHandler implements ForwardHandler {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.ui.CalendarComponentEvents.ForwardHandler#forward
+     * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.ForwardEvent)
+     */
+    public void forward(ForwardEvent event) {
+        Date start = event.getComponent().getStartDate();
+        Date end = event.getComponent().getEndDate();
+
+        // calculate amount to move forward
+        int durationInDays = (int) (((end.getTime()) - start.getTime()) / DateConstants.DAYINMILLIS);
+        durationInDays++;
+
+        // set new start and end times
+        Calendar javaCalendar = Calendar.getInstance();
+        javaCalendar.setTime(start);
+        javaCalendar.add(java.util.Calendar.DATE, durationInDays);
+        Date newStart = javaCalendar.getTime();
+
+        javaCalendar.setTime(end);
+        javaCalendar.add(java.util.Calendar.DATE, durationInDays);
+        Date newEnd = javaCalendar.getTime();
+
+        setDates(event, newStart, newEnd);
+    }
+
+    /**
+     * Set the start and end dates for the event
+     * 
+     * @param event
+     *            The event that the start and end dates should be set
+     * @param start
+     *            The start date
+     * @param end
+     *            The end date
+     */
+    protected void setDates(ForwardEvent event, Date start, Date end) {
+        event.getComponent().setStartDate(start);
+        event.getComponent().setEndDate(end);
+    }
+}
diff --git a/server/src/com/vaadin/ui/components/calendar/handler/BasicWeekClickHandler.java b/server/src/com/vaadin/ui/components/calendar/handler/BasicWeekClickHandler.java
new file mode 100644 (file)
index 0000000..49efe49
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.ui.components.calendar.handler;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.WeekClick;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.WeekClickHandler;
+
+/**
+ * Implements basic functionality needed to change to week view when a week
+ * number is clicked.
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ */
+@SuppressWarnings("serial")
+public class BasicWeekClickHandler implements WeekClickHandler {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.calendar.ui.CalendarComponentEvents.WeekClickHandler
+     * #weekClick
+     * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.WeekClick)
+     */
+    public void weekClick(WeekClick event) {
+        int week = event.getWeek();
+        int year = event.getYear();
+
+        // set correct year and month
+        Calendar javaCalendar = event.getComponent().getInternalCalendar();
+        javaCalendar.set(GregorianCalendar.YEAR, year);
+        javaCalendar.set(GregorianCalendar.WEEK_OF_YEAR, week);
+
+        // starting at the beginning of the week
+        javaCalendar.set(GregorianCalendar.DAY_OF_WEEK,
+                javaCalendar.getFirstDayOfWeek());
+        Date start = javaCalendar.getTime();
+
+        // ending at the end of the week
+        javaCalendar.add(GregorianCalendar.DATE, 6);
+        Date end = javaCalendar.getTime();
+
+        setDates(event, start, end);
+
+        // times are automatically expanded, no need to worry about them
+    }
+
+    /**
+     * Set the start and end dates for the event
+     * 
+     * @param event
+     *            The event that the start and end dates should be set
+     * @param start
+     *            The start date
+     * @param end
+     *            The end date
+     */
+    protected void setDates(WeekClick event, Date start, Date end) {
+        event.getComponent().setStartDate(start);
+        event.getComponent().setEndDate(end);
+    }
+
+}
diff --git a/server/tests/src/com/vaadin/tests/server/component/calendar/CalendarBasics.java b/server/tests/src/com/vaadin/tests/server/component/calendar/CalendarBasics.java
new file mode 100644 (file)
index 0000000..5926cfa
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.server.component.calendar;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import org.junit.Test;
+
+import com.vaadin.ui.Calendar;
+import com.vaadin.ui.Calendar.TimeFormat;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.BackwardEvent;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.DateClickEvent;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventResize;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.ForwardEvent;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.MoveEvent;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.WeekClick;
+import com.vaadin.ui.components.calendar.event.BasicEventProvider;
+import com.vaadin.ui.components.calendar.event.CalendarEventProvider;
+
+/**
+ * Basic API tests for the calendar
+ */
+public class CalendarBasics {
+
+    @Test
+    public void testEmptyConstructorInitialization() {
+
+        Calendar calendar = new Calendar();
+
+        // The calendar should have a basic event provider with no events
+        CalendarEventProvider provider = calendar.getEventProvider();
+        assertNotNull("Event provider should not be null", provider);
+
+        // Basic event handlers should be registered
+        assertNotNull(calendar.getHandler(BackwardEvent.EVENT_ID));
+        assertNotNull(calendar.getHandler(ForwardEvent.EVENT_ID));
+        assertNotNull(calendar.getHandler(WeekClick.EVENT_ID));
+        assertNotNull(calendar.getHandler(DateClickEvent.EVENT_ID));
+        assertNotNull(calendar.getHandler(MoveEvent.EVENT_ID));
+        assertNotNull(calendar.getHandler(EventResize.EVENT_ID));
+
+        // Calendar should have undefined size
+        assertTrue(calendar.getWidth() < 0);
+        assertTrue(calendar.getHeight() < 0);
+    }
+
+    @Test
+    public void testConstructorWithCaption() {
+        final String caption = "My Calendar Caption";
+        Calendar calendar = new Calendar(caption);
+        assertEquals(caption, calendar.getCaption());
+    }
+
+    @Test
+    public void testConstructorWithCustomEventProvider() {
+        BasicEventProvider myProvider = new BasicEventProvider();
+        Calendar calendar = new Calendar(myProvider);
+        assertEquals(myProvider, calendar.getEventProvider());
+    }
+
+    @Test
+    public void testConstructorWithCustomEventProviderAndCaption() {
+        BasicEventProvider myProvider = new BasicEventProvider();
+        final String caption = "My Calendar Caption";
+        Calendar calendar = new Calendar(caption, myProvider);
+        assertEquals(caption, calendar.getCaption());
+        assertEquals(myProvider, calendar.getEventProvider());
+    }
+
+    @Test
+    public void testDefaultStartAndEndDates() {
+        Calendar calendar = new Calendar();
+
+        // If no start and end date is set the calendar will display the current
+        // week
+        java.util.Calendar c = new GregorianCalendar();
+        java.util.Calendar c2 = new GregorianCalendar();
+
+        c2.setTime(calendar.getStartDate());
+        assertEquals(c.getFirstDayOfWeek(),
+                c2.get(java.util.Calendar.DAY_OF_WEEK));
+        c2.setTime(calendar.getEndDate());
+
+        c.set(java.util.Calendar.DAY_OF_WEEK, c.getFirstDayOfWeek() + 6);
+        assertEquals(c.get(java.util.Calendar.DAY_OF_WEEK),
+                c2.get(java.util.Calendar.DAY_OF_WEEK));
+    }
+
+    @Test
+    public void testCustomStartAndEndDates() {
+        Calendar calendar = new Calendar();
+        java.util.Calendar c = new GregorianCalendar();
+
+        Date start = c.getTime();
+        c.add(java.util.Calendar.DATE, 3);
+        Date end = c.getTime();
+
+        calendar.setStartDate(start);
+        calendar.setEndDate(end);
+
+        assertEquals(start.getTime(), calendar.getStartDate().getTime());
+        assertEquals(end.getTime(), calendar.getEndDate().getTime());
+    }
+
+    @Test
+    public void testCustomLocale() {
+        Calendar calendar = new Calendar();
+        calendar.setLocale(Locale.CANADA_FRENCH);
+
+        // Setting the locale should set the internal calendars locale
+        assertEquals(Locale.CANADA_FRENCH, calendar.getLocale());
+        java.util.Calendar c = new GregorianCalendar(Locale.CANADA_FRENCH);
+        assertEquals(c.getTimeZone().getRawOffset(), calendar
+                .getInternalCalendar().getTimeZone().getRawOffset());
+    }
+
+    @Test
+    public void testTimeFormat() {
+        Calendar calendar = new Calendar();
+
+        // The default timeformat depends on the current locale
+        calendar.setLocale(Locale.ENGLISH);
+        assertEquals(TimeFormat.Format12H, calendar.getTimeFormat());
+
+        calendar.setLocale(Locale.ITALIAN);
+        assertEquals(TimeFormat.Format24H, calendar.getTimeFormat());
+
+        // Setting a specific time format overrides the locale
+        calendar.setTimeFormat(TimeFormat.Format12H);
+        assertEquals(TimeFormat.Format12H, calendar.getTimeFormat());
+    }
+
+    @Test
+    public void testTimeZone() {
+        Calendar calendar = new Calendar();
+        calendar.setLocale(Locale.CANADA_FRENCH);
+
+        // By default the calendars timezone is returned
+        assertEquals(calendar.getInternalCalendar().getTimeZone(),
+                calendar.getTimeZone());
+
+        // One can override the default behaviour by specifying a timezone
+        TimeZone customTimeZone = TimeZone.getTimeZone("Europe/Helsinki");
+        calendar.setTimeZone(customTimeZone);
+        assertEquals(customTimeZone, calendar.getTimeZone());
+    }
+
+    @Test
+    public void testVisibleDaysOfWeek() {
+        Calendar calendar = new Calendar();
+
+        // The defaults are the whole week
+        assertEquals(1, calendar.getFirstVisibleDayOfWeek());
+        assertEquals(7, calendar.getLastVisibleDayOfWeek());
+
+        calendar.setFirstVisibleDayOfWeek(0); // Invalid input
+        assertEquals(1, calendar.getFirstVisibleDayOfWeek());
+
+        calendar.setLastVisibleDayOfWeek(0); // Invalid input
+        assertEquals(7, calendar.getLastVisibleDayOfWeek());
+
+        calendar.setFirstVisibleDayOfWeek(8); // Invalid input
+        assertEquals(1, calendar.getFirstVisibleDayOfWeek());
+
+        calendar.setLastVisibleDayOfWeek(8); // Invalid input
+        assertEquals(7, calendar.getLastVisibleDayOfWeek());
+
+        calendar.setFirstVisibleDayOfWeek(4);
+        assertEquals(4, calendar.getFirstVisibleDayOfWeek());
+
+        calendar.setLastVisibleDayOfWeek(6);
+        assertEquals(6, calendar.getLastVisibleDayOfWeek());
+
+        calendar.setFirstVisibleDayOfWeek(7); // Invalid since last day is 6
+        assertEquals(4, calendar.getFirstVisibleDayOfWeek());
+
+        calendar.setLastVisibleDayOfWeek(2); // Invalid since first day is 4
+        assertEquals(6, calendar.getLastVisibleDayOfWeek());
+    }
+
+    @Test
+    public void testVisibleHoursInDay() {
+        Calendar calendar = new Calendar();
+
+        // Defaults are the whole day
+        assertEquals(0, calendar.getFirstVisibleHourOfDay());
+        assertEquals(23, calendar.getLastVisibleHourOfDay());
+    }
+
+}
diff --git a/server/tests/src/com/vaadin/tests/server/component/calendar/ContainerDataSource.java b/server/tests/src/com/vaadin/tests/server/component/calendar/ContainerDataSource.java
new file mode 100644 (file)
index 0000000..2bc95e3
--- /dev/null
@@ -0,0 +1,362 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.server.component.calendar;
+
+import java.util.Date;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+import com.vaadin.data.Container.Indexed;
+import com.vaadin.data.Container.Sortable;
+import com.vaadin.data.Item;
+import com.vaadin.data.util.BeanItemContainer;
+import com.vaadin.data.util.IndexedContainer;
+import com.vaadin.ui.Calendar;
+import com.vaadin.ui.components.calendar.ContainerEventProvider;
+import com.vaadin.ui.components.calendar.event.BasicEvent;
+import com.vaadin.ui.components.calendar.event.CalendarEvent;
+
+public class ContainerDataSource extends TestCase {
+
+    private Calendar calendar;
+
+    @Override
+    public void setUp() {
+        calendar = new Calendar();
+    }
+
+    /**
+     * Tests adding a bean item container to the Calendar
+     */
+    @Test
+    public void testWithBeanItemContainer() {
+
+        // Create a container to use as a datasource
+        Indexed container = createTestBeanItemContainer();
+
+        // Set datasource
+        calendar.setContainerDataSource(container);
+
+        // Start and end dates to query for
+        java.util.Calendar cal = java.util.Calendar.getInstance();
+        cal.setTime(((CalendarEvent) container.getIdByIndex(0)).getStart());
+        Date start = cal.getTime();
+        cal.add(java.util.Calendar.MONTH, 1);
+        Date end = cal.getTime();
+
+        // Test the all events are returned
+        List<CalendarEvent> events = calendar.getEventProvider().getEvents(
+                start, end);
+        assertEquals(container.size(), events.size());
+
+        // Test that a certain range is returned
+        cal.setTime(((CalendarEvent) container.getIdByIndex(6)).getStart());
+        end = cal.getTime();
+        events = calendar.getEventProvider().getEvents(start, end);
+        assertEquals(6, events.size());
+    }
+
+    /**
+     * This tests tests that if you give the Calendar an unsorted (== not sorted
+     * by starting date) container then the calendar should gracefully handle
+     * it. In this case the size of the container will be wrong. The test is
+     * exactly the same as {@link #testWithBeanItemContainer()} except that the
+     * beans has been intentionally sorted by caption instead of date.
+     */
+    @Test
+    public void testWithUnsortedBeanItemContainer() {
+        // Create a container to use as a datasource
+        Indexed container = createTestBeanItemContainer();
+
+        // Make the container sorted by caption
+        ((Sortable) container).sort(new Object[] { "caption" },
+                new boolean[] { true });
+
+        // Set data source
+        calendar.setContainerDataSource(container);
+
+        // Start and end dates to query for
+        java.util.Calendar cal = java.util.Calendar.getInstance();
+        cal.setTime(((CalendarEvent) container.getIdByIndex(0)).getStart());
+        Date start = cal.getTime();
+        cal.add(java.util.Calendar.MONTH, 1);
+        Date end = cal.getTime();
+
+        // Test the all events are returned
+        List<CalendarEvent> events = calendar.getEventProvider().getEvents(
+                start, end);
+        assertEquals(container.size(), events.size());
+
+        // Test that a certain range is returned
+        cal.setTime(((CalendarEvent) container.getIdByIndex(6)).getStart());
+        end = cal.getTime();
+        events = calendar.getEventProvider().getEvents(start, end);
+
+        // The events size is 1 since the getEvents returns the wrong range
+        assertEquals(1, events.size());
+    }
+
+    /**
+     * Tests adding a Indexed container to the Calendar
+     */
+    @Test
+    public void testWithIndexedContainer() {
+
+        // Create a container to use as a datasource
+        Indexed container = createTestIndexedContainer();
+
+        // Set datasource
+        calendar.setContainerDataSource(container, "testCaption",
+                "testDescription", "testStartDate", "testEndDate", null);
+
+        // Start and end dates to query for
+        java.util.Calendar cal = java.util.Calendar.getInstance();
+        cal.setTime((Date) container.getItem(container.getIdByIndex(0))
+                .getItemProperty("testStartDate").getValue());
+        Date start = cal.getTime();
+        cal.add(java.util.Calendar.MONTH, 1);
+        Date end = cal.getTime();
+
+        // Test the all events are returned
+        List<CalendarEvent> events = calendar.getEventProvider().getEvents(
+                start, end);
+        assertEquals(container.size(), events.size());
+
+        // Check that event values are present
+        CalendarEvent e = events.get(0);
+        assertEquals("Test 1", e.getCaption());
+        assertEquals("Description 1", e.getDescription());
+        assertTrue(e.getStart().compareTo(start) == 0);
+
+        // Test that a certain range is returned
+        cal.setTime((Date) container.getItem(container.getIdByIndex(6))
+                .getItemProperty("testStartDate").getValue());
+        end = cal.getTime();
+        events = calendar.getEventProvider().getEvents(start, end);
+        assertEquals(6, events.size());
+    }
+
+    @Test
+    public void testNullLimitsBeanItemContainer() {
+        // Create a container to use as a datasource
+        Indexed container = createTestBeanItemContainer();
+
+        // Start and end dates to query for
+        java.util.Calendar cal = java.util.Calendar.getInstance();
+        cal.setTime(((CalendarEvent) container.getIdByIndex(0)).getStart());
+        Date start = cal.getTime();
+        cal.add(java.util.Calendar.MONTH, 1);
+        Date end = cal.getTime();
+
+        // Set datasource
+        calendar.setContainerDataSource(container);
+
+        // Test null start time
+        List<CalendarEvent> events = calendar.getEventProvider().getEvents(
+                null, end);
+        assertEquals(container.size(), events.size());
+
+        // Test null end time
+        events = calendar.getEventProvider().getEvents(start, null);
+        assertEquals(container.size(), events.size());
+
+        // Test both null times
+        events = calendar.getEventProvider().getEvents(null, null);
+        assertEquals(container.size(), events.size());
+    }
+
+    @Test
+    public void testNullLimitsIndexedContainer() {
+        // Create a container to use as a datasource
+        Indexed container = createTestIndexedContainer();
+
+        // Start and end dates to query for
+        java.util.Calendar cal = java.util.Calendar.getInstance();
+        cal.setTime((Date) container.getItem(container.getIdByIndex(0))
+                .getItemProperty("testStartDate").getValue());
+        Date start = cal.getTime();
+        cal.add(java.util.Calendar.MONTH, 1);
+        Date end = cal.getTime();
+
+        // Set datasource
+        calendar.setContainerDataSource(container, "testCaption",
+                "testDescription", "testStartDate", "testEndDate", null);
+
+        // Test null start time
+        List<CalendarEvent> events = calendar.getEventProvider().getEvents(
+                null, end);
+        assertEquals(container.size(), events.size());
+
+        // Test null end time
+        events = calendar.getEventProvider().getEvents(start, null);
+        assertEquals(container.size(), events.size());
+
+        // Test both null times
+        events = calendar.getEventProvider().getEvents(null, null);
+        assertEquals(container.size(), events.size());
+    }
+
+    /**
+     * Tests the addEvent convenience method with the default event provider
+     */
+    @Test
+    public void testAddEventConvinienceMethod() {
+
+        // Start and end dates to query for
+        java.util.Calendar cal = java.util.Calendar.getInstance();
+        Date start = cal.getTime();
+        cal.add(java.util.Calendar.MONTH, 1);
+        Date end = cal.getTime();
+
+        // Ensure no events
+        assertEquals(0, calendar.getEvents(start, end).size());
+
+        // Add an event
+        BasicEvent event = new BasicEvent("Test", "Test", start);
+        calendar.addEvent(event);
+
+        // Ensure event exists
+        List<CalendarEvent> events = calendar.getEvents(start, end);
+        assertEquals(1, events.size());
+        assertEquals(events.get(0).getCaption(), event.getCaption());
+        assertEquals(events.get(0).getDescription(), event.getDescription());
+        assertEquals(events.get(0).getStart(), event.getStart());
+    }
+
+    /**
+     * Test the removeEvent convenience method with the default event provider
+     */
+    @Test
+    public void testRemoveEventConvinienceMethod() {
+
+        // Start and end dates to query for
+        java.util.Calendar cal = java.util.Calendar.getInstance();
+        Date start = cal.getTime();
+        cal.add(java.util.Calendar.MONTH, 1);
+        Date end = cal.getTime();
+
+        // Ensure no events
+        assertEquals(0, calendar.getEvents(start, end).size());
+
+        // Add an event
+        CalendarEvent event = new BasicEvent("Test", "Test", start);
+        calendar.addEvent(event);
+
+        // Ensure event exists
+        assertEquals(1, calendar.getEvents(start, end).size());
+
+        // Remove event
+        calendar.removeEvent(event);
+
+        // Ensure no events
+        assertEquals(0, calendar.getEvents(start, end).size());
+    }
+
+    @Test
+    public void testAddEventConvinienceMethodWithCustomEventProvider() {
+
+        // Use a container data source
+        calendar.setEventProvider(new ContainerEventProvider(
+                new BeanItemContainer<BasicEvent>(BasicEvent.class)));
+
+        // Start and end dates to query for
+        java.util.Calendar cal = java.util.Calendar.getInstance();
+        Date start = cal.getTime();
+        cal.add(java.util.Calendar.MONTH, 1);
+        Date end = cal.getTime();
+
+        // Ensure no events
+        assertEquals(0, calendar.getEvents(start, end).size());
+
+        // Add an event
+        BasicEvent event = new BasicEvent("Test", "Test", start);
+        calendar.addEvent(event);
+
+        // Ensure event exists
+        List<CalendarEvent> events = calendar.getEvents(start, end);
+        assertEquals(1, events.size());
+        assertEquals(events.get(0).getCaption(), event.getCaption());
+        assertEquals(events.get(0).getDescription(), event.getDescription());
+        assertEquals(events.get(0).getStart(), event.getStart());
+    }
+
+    @Test
+    public void testRemoveEventConvinienceMethodWithCustomEventProvider() {
+
+        // Use a container data source
+        calendar.setEventProvider(new ContainerEventProvider(
+                new BeanItemContainer<BasicEvent>(BasicEvent.class)));
+
+        // Start and end dates to query for
+        java.util.Calendar cal = java.util.Calendar.getInstance();
+        Date start = cal.getTime();
+        cal.add(java.util.Calendar.MONTH, 1);
+        Date end = cal.getTime();
+
+        // Ensure no events
+        assertEquals(0, calendar.getEvents(start, end).size());
+
+        // Add an event
+        BasicEvent event = new BasicEvent("Test", "Test", start);
+        calendar.addEvent(event);
+
+        // Ensure event exists
+        List<CalendarEvent> events = calendar.getEvents(start, end);
+        assertEquals(1, events.size());
+
+        // Remove event
+        calendar.removeEvent(event);
+
+        // Ensure no events
+        assertEquals(0, calendar.getEvents(start, end).size());
+    }
+
+    private static Indexed createTestBeanItemContainer() {
+        BeanItemContainer<CalendarEvent> eventContainer = new BeanItemContainer<CalendarEvent>(
+                CalendarEvent.class);
+        java.util.Calendar cal = java.util.Calendar.getInstance();
+        for (int i = 1; i <= 10; i++) {
+            eventContainer.addBean(new BasicEvent("Test " + i, "Description "
+                    + i, cal.getTime()));
+            cal.add(java.util.Calendar.DAY_OF_MONTH, 2);
+        }
+        return eventContainer;
+    }
+
+    private static Indexed createTestIndexedContainer() {
+        IndexedContainer container = new IndexedContainer();
+        container.addContainerProperty("testCaption", String.class, "");
+        container.addContainerProperty("testDescription", String.class, "");
+        container.addContainerProperty("testStartDate", Date.class, null);
+        container.addContainerProperty("testEndDate", Date.class, null);
+
+        java.util.Calendar cal = java.util.Calendar.getInstance();
+        for (int i = 1; i <= 10; i++) {
+            Item item = container.getItem(container.addItem());
+            item.getItemProperty("testCaption").setValue("Test " + i);
+            item.getItemProperty("testDescription")
+                    .setValue("Description " + i);
+            item.getItemProperty("testStartDate").setValue(cal.getTime());
+            item.getItemProperty("testEndDate").setValue(cal.getTime());
+            cal.add(java.util.Calendar.DAY_OF_MONTH, 2);
+        }
+        return container;
+    }
+
+}
diff --git a/shared/src/com/vaadin/shared/ui/calendar/CalendarClientRpc.java b/shared/src/com/vaadin/shared/ui/calendar/CalendarClientRpc.java
new file mode 100644 (file)
index 0000000..c1ff8bd
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.shared.ui.calendar;
+
+import com.vaadin.shared.communication.ClientRpc;
+
+/**
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ * 
+ */
+public interface CalendarClientRpc extends ClientRpc {
+    void scroll(int scrollPosition);
+}
diff --git a/shared/src/com/vaadin/shared/ui/calendar/CalendarEventId.java b/shared/src/com/vaadin/shared/ui/calendar/CalendarEventId.java
new file mode 100644 (file)
index 0000000..6f52aab
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.shared.ui.calendar;
+
+/**
+ * CalendarEventId contains static String identifiers for all Calendar events.
+ * These are used both in the client and server side code.
+ * 
+ * @since 7.1
+ * @author Vaadin Ltd.
+ */
+public class CalendarEventId {
+
+    public static final String EVENTMOVE = "eventMove";
+    public static final String RANGESELECT = "rangeSelect";
+    public static final String FORWARD = "forward";
+    public static final String BACKWARD = "backward";
+    public static final String DATECLICK = "dateClick";
+    public static final String WEEKCLICK = "weekClick";
+    public static final String EVENTCLICK = "eventClick";
+    public static final String EVENTRESIZE = "eventResize";
+    public static final String ACTION = "action";
+}
diff --git a/shared/src/com/vaadin/shared/ui/calendar/CalendarServerRpc.java b/shared/src/com/vaadin/shared/ui/calendar/CalendarServerRpc.java
new file mode 100644 (file)
index 0000000..5257310
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.shared.ui.calendar;
+
+import com.vaadin.shared.annotations.Delayed;
+import com.vaadin.shared.communication.ServerRpc;
+
+/**
+ * @since 7.1
+ * @author Vaadin Ltd.
+ */
+public interface CalendarServerRpc extends ServerRpc {
+    void eventMove(int eventIndex, String newDate);
+
+    void rangeSelect(String range);
+
+    void forward();
+
+    void backward();
+
+    void dateClick(String date);
+
+    void weekClick(String event);
+
+    void eventClick(int eventIndex);
+
+    void eventResize(int eventIndex, String newStartDate, String newEndDate);
+
+    void actionOnEmptyCell(String actionKey, String startDate, String endDate);
+
+    void actionOnEvent(String actionKey, String startDate, String endDate,
+            int eventIndex);
+
+    @Delayed(lastOnly = true)
+    void scroll(int scrollPosition);
+}
diff --git a/shared/src/com/vaadin/shared/ui/calendar/CalendarState.java b/shared/src/com/vaadin/shared/ui/calendar/CalendarState.java
new file mode 100644 (file)
index 0000000..fab5fd8
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.shared.ui.calendar;
+
+import java.util.List;
+
+import com.vaadin.shared.AbstractComponentState;
+
+/**
+ * @since 7.1.0
+ * @author Vaadin Ltd.
+ */
+public class CalendarState extends AbstractComponentState {
+
+    public boolean format24H;
+    public String[] dayNames;
+    public String[] monthNames;
+    public int firstVisibleDayOfWeek = 1;
+    public int lastVisibleDayOfWeek = 7;
+    public int firstHourOfDay = 0;
+    public int lastHourOfDay = 23;
+    public int firstDayOfWeek;
+    public int scroll;
+    public String now;
+    public List<CalendarState.Day> days;
+    public List<CalendarState.Event> events;
+    public List<CalendarState.Action> actions;
+
+    public static class Day implements java.io.Serializable {
+        public String date;
+        public String localizedDateFormat;
+        public int dayOfWeek;
+        public int week;
+    }
+
+    public static class Action implements java.io.Serializable {
+
+        public String caption;
+        public String iconKey;
+        public String actionKey;
+        public String startDate;
+        public String endDate;
+    }
+
+    public static class Event implements java.io.Serializable {
+        public int index;
+        public String caption;
+        public String dateFrom;
+        public String dateTo;
+        public String timeFrom;
+        public String timeTo;
+        public String styleName;
+        public String description;
+        public boolean allDay;
+    }
+}
diff --git a/shared/src/com/vaadin/shared/ui/calendar/DateConstants.java b/shared/src/com/vaadin/shared/ui/calendar/DateConstants.java
new file mode 100644 (file)
index 0000000..8a84027
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.shared.ui.calendar;
+
+/**
+ * 
+ * @since 7.1
+ * 
+ */
+public class DateConstants {
+
+    public static final String ACTION_DATE_FORMAT_PATTERN = "yyyy-MM-dd HH:mm:ss";
+    public static final String CLIENT_DATE_FORMAT = "yyyy-MM-dd";
+    public static final String CLIENT_TIME_FORMAT = "HH-mm";
+    public static final long MINUTEINMILLIS = 60 * 1000;
+    public static final long HOURINMILLIS = 60 * MINUTEINMILLIS;
+    public static final long DAYINMILLIS = 24 * HOURINMILLIS;
+    public static final long WEEKINMILLIS = 7 * DAYINMILLIS;
+
+}
diff --git a/uitest/src/com/vaadin/tests/components/calendar/BeanItemContainerTestUI.java b/uitest/src/com/vaadin/tests/components/calendar/BeanItemContainerTestUI.java
new file mode 100644 (file)
index 0000000..4e0b963
--- /dev/null
@@ -0,0 +1,181 @@
+/**
+ * Copyright 2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.components.calendar;
+
+import java.util.Arrays;
+import java.util.Date;
+
+import com.vaadin.data.fieldgroup.FieldGroup;
+import com.vaadin.data.fieldgroup.FieldGroup.CommitException;
+import com.vaadin.data.util.BeanItem;
+import com.vaadin.data.util.BeanItemContainer;
+import com.vaadin.event.Action;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.shared.ui.datefield.Resolution;
+import com.vaadin.ui.Calendar;
+import com.vaadin.ui.DateField;
+import com.vaadin.ui.FormLayout;
+import com.vaadin.ui.Table;
+import com.vaadin.ui.TextField;
+import com.vaadin.ui.UI;
+import com.vaadin.ui.VerticalSplitPanel;
+import com.vaadin.ui.Window;
+import com.vaadin.ui.Window.CloseEvent;
+import com.vaadin.ui.components.calendar.ContainerEventProvider;
+import com.vaadin.ui.components.calendar.event.BasicEvent;
+
+public class BeanItemContainerTestUI extends UI {
+
+    private Calendar calendar;
+
+    private Table table;
+
+    private BeanItemContainer<BasicEvent> events = new BeanItemContainer<BasicEvent>(
+            BasicEvent.class);
+
+    @SuppressWarnings("deprecation")
+    @Override
+    protected void init(VaadinRequest request) {
+        VerticalSplitPanel content = new VerticalSplitPanel();
+        content.setSizeFull();
+        setContent(content);
+
+        // Create Calendar
+        calendar = new Calendar();
+        calendar.setImmediate(true);
+        calendar.setSizeFull();
+        calendar.setContainerDataSource(events);
+        calendar.setStartDate(new Date(100, 1, 1));
+        calendar.setEndDate(new Date(100, 2, 1));
+
+        content.addComponent(calendar);
+
+        // Add event table connected to same data source
+        table = createTable();
+        table.setContainerDataSource(events);
+        table.setVisibleColumns(new Object[] { "caption", "description",
+                "start", "end" });
+        content.addComponent(table);
+    }
+
+    /**
+     * Creates a table with some actions
+     * 
+     * @return
+     */
+    private Table createTable() {
+        Table table = new Table();
+        table.setSizeFull();
+        table.addActionHandler(new Action.Handler() {
+
+            private final Action ADD = new Action("Add event");
+            private final Action EDIT = new Action("Edit event");
+            private final Action REMOVE = new Action("Remove event");
+
+            public void handleAction(Action action, Object sender, Object target) {
+                if (action == ADD) {
+                    BasicEvent event = new BasicEvent();
+                    event.setStart(new Date(100, 1, 1));
+                    event.setEnd(new Date(100, 1, 1));
+                    editEvent(event);
+                } else if (action == EDIT) {
+                    editEvent((BasicEvent) target);
+                } else if (action == REMOVE) {
+                    events.removeItem(target);
+                }
+            }
+
+            public Action[] getActions(Object target, Object sender) {
+                if (target == null) {
+                    return new Action[] { ADD };
+                } else {
+                    return new Action[] { EDIT, REMOVE };
+                }
+            }
+        });
+        return table;
+    }
+
+    /**
+     * Opens up a modal dialog window where an event can be modified
+     * 
+     * @param event
+     *            The event to modify
+     */
+    private void editEvent(final BasicEvent event) {
+        Window modal = new Window("Add event");
+        modal.setModal(true);
+        modal.setResizable(false);
+        modal.setDraggable(false);
+        modal.setWidth("300px");
+        final FieldGroup fieldGroup = new FieldGroup();
+
+        FormLayout formLayout = new FormLayout();
+        TextField captionField = new TextField("Caption");
+        captionField.setImmediate(true);
+        TextField descriptionField = new TextField("Description");
+        descriptionField.setImmediate(true);
+        DateField startField = new DateField("Start");
+        startField.setResolution(Resolution.MINUTE);
+        startField.setImmediate(true);
+        DateField endField = new DateField("End");
+        endField.setImmediate(true);
+        endField.setResolution(Resolution.MINUTE);
+
+        formLayout.addComponent(captionField);
+        formLayout.addComponent(descriptionField);
+        formLayout.addComponent(startField);
+        formLayout.addComponent(endField);
+
+        fieldGroup.bind(captionField, ContainerEventProvider.CAPTION_PROPERTY);
+        fieldGroup.bind(descriptionField,
+                ContainerEventProvider.DESCRIPTION_PROPERTY);
+        fieldGroup.bind(startField, ContainerEventProvider.STARTDATE_PROPERTY);
+        fieldGroup.bind(endField, ContainerEventProvider.ENDDATE_PROPERTY);
+
+        fieldGroup.setItemDataSource(new BeanItem<BasicEvent>(event, Arrays
+                .asList(ContainerEventProvider.CAPTION_PROPERTY,
+                        ContainerEventProvider.DESCRIPTION_PROPERTY,
+                        ContainerEventProvider.STARTDATE_PROPERTY,
+                        ContainerEventProvider.ENDDATE_PROPERTY)));
+        modal.setContent(formLayout);
+        modal.addCloseListener(new Window.CloseListener() {
+            public void windowClose(CloseEvent e) {
+                // Commit changes to bean
+                try {
+                    fieldGroup.commit();
+                } catch (CommitException e1) {
+                    e1.printStackTrace();
+                }
+
+                if (events.containsId(event)) {
+                    /*
+                     * BeanItemContainer does not notify container listeners
+                     * when the bean changes so we need to trigger a
+                     * ItemSetChange event
+                     */
+                    BasicEvent dummy = new BasicEvent();
+                    events.addBean(dummy);
+                    events.removeItem(dummy);
+
+                } else {
+                    events.addBean(event);
+                }
+            }
+        });
+        getUI().addWindow(modal);
+    }
+}
diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarActionsUI.java b/uitest/src/com/vaadin/tests/components/calendar/CalendarActionsUI.java
new file mode 100644 (file)
index 0000000..f5c2d9d
--- /dev/null
@@ -0,0 +1,107 @@
+/**
+ * Copyright 2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.components.calendar;
+
+import java.util.Date;
+import java.util.Locale;
+
+import com.vaadin.event.Action;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Calendar;
+import com.vaadin.ui.GridLayout;
+import com.vaadin.ui.UI;
+import com.vaadin.ui.components.calendar.CalendarDateRange;
+import com.vaadin.ui.components.calendar.event.BasicEvent;
+
+public class CalendarActionsUI extends UI {
+
+    @SuppressWarnings("deprecation")
+    @Override
+    protected void init(VaadinRequest request) {
+        GridLayout content = new GridLayout(1, 2);
+        content.setSizeFull();
+        setContent(content);
+
+        final Calendar calendar = new Calendar();
+        calendar.setLocale(new Locale("fi", "FI"));
+
+        calendar.setSizeFull();
+        calendar.setStartDate(new Date(100, 1, 1));
+        calendar.setEndDate(new Date(100, 2, 1));
+
+        calendar.addActionHandler(new Action.Handler() {
+
+            public final Action NEW_EVENT = new Action("Add event");
+            public final Action EDIT_EVENT = new Action("Edit event");
+            public final Action REMOVE_EVENT = new Action("Remove event");
+
+            /*
+             * (non-Javadoc)
+             * 
+             * @see
+             * com.vaadin.event.Action.Handler#handleAction(com.vaadin.event
+             * .Action, java.lang.Object, java.lang.Object)
+             */
+            public void handleAction(Action action, Object sender, Object target) {
+                Date date = (Date) target;
+                if (action == NEW_EVENT) {
+                    BasicEvent event = new BasicEvent("New event",
+                            "Hello world", date, date);
+                    calendar.addEvent(event);
+                }
+            }
+
+            /*
+             * (non-Javadoc)
+             * 
+             * @see com.vaadin.event.Action.Handler#getActions(java.lang.Object,
+             * java.lang.Object)
+             */
+            public Action[] getActions(Object target, Object sender) {
+                CalendarDateRange date = (CalendarDateRange) target;
+
+                java.util.Calendar cal = java.util.Calendar.getInstance();
+                cal.set(2000, 1, 1, 12, 0, 0);
+
+                if (date.inRange(cal.getTime())) {
+                    return new Action[] { NEW_EVENT, };
+                }
+
+                cal.add(java.util.Calendar.DAY_OF_WEEK, 1);
+
+                if (date.inRange(cal.getTime())) {
+                    return new Action[] { REMOVE_EVENT };
+                }
+
+                return null;
+            }
+        });
+
+        content.addComponent(calendar);
+
+        content.addComponent(new Button("Set week view",
+                new Button.ClickListener() {
+                    public void buttonClick(ClickEvent event) {
+                        calendar.setEndDate(new Date(100, 1, 7));
+                    }
+                }));
+
+        content.setRowExpandRatio(0, 1);
+
+    }
+}
diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarTest.java b/uitest/src/com/vaadin/tests/components/calendar/CalendarTest.java
new file mode 100644 (file)
index 0000000..530e47f
--- /dev/null
@@ -0,0 +1,1227 @@
+/**
+ * Copyright 2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.components.calendar;
+
+import java.text.DateFormatSymbols;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+import com.vaadin.annotations.Theme;
+import com.vaadin.data.Item;
+import com.vaadin.data.Property;
+import com.vaadin.data.Property.ValueChangeEvent;
+import com.vaadin.data.Property.ValueChangeListener;
+import com.vaadin.data.fieldgroup.FieldGroup;
+import com.vaadin.data.fieldgroup.FieldGroup.CommitException;
+import com.vaadin.data.util.BeanItem;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.shared.ui.MarginInfo;
+import com.vaadin.shared.ui.combobox.FilteringMode;
+import com.vaadin.shared.ui.datefield.Resolution;
+import com.vaadin.ui.Alignment;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Button.ClickListener;
+import com.vaadin.ui.Calendar;
+import com.vaadin.ui.Calendar.TimeFormat;
+import com.vaadin.ui.CheckBox;
+import com.vaadin.ui.ComboBox;
+import com.vaadin.ui.DateField;
+import com.vaadin.ui.FormLayout;
+import com.vaadin.ui.GridLayout;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.Layout;
+import com.vaadin.ui.TextArea;
+import com.vaadin.ui.TextField;
+import com.vaadin.ui.UI;
+import com.vaadin.ui.VerticalLayout;
+import com.vaadin.ui.Window;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.DateClickEvent;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventClick;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventClickHandler;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.RangeSelectEvent;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.RangeSelectHandler;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.WeekClick;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.WeekClickHandler;
+import com.vaadin.ui.components.calendar.event.BasicEvent;
+import com.vaadin.ui.components.calendar.event.BasicEventProvider;
+import com.vaadin.ui.components.calendar.event.CalendarEvent;
+import com.vaadin.ui.components.calendar.handler.BasicDateClickHandler;
+import com.vaadin.ui.components.calendar.handler.BasicWeekClickHandler;
+
+/** Calendar component test application */
+@Theme("tests-calendar")
+public class CalendarTest extends UI {
+
+    private static final long serialVersionUID = -5436777475398410597L;
+
+    private static final String DEFAULT_ITEMID = "DEFAULT";
+
+    private enum Mode {
+        MONTH, WEEK, DAY;
+    }
+
+    /**
+     * This Gregorian calendar is used to control dates and time inside of this
+     * test application.
+     */
+    private GregorianCalendar calendar;
+
+    /** Target calendar component that this test application is made for. */
+    private Calendar calendarComponent;
+
+    private Date currentMonthsFirstDate;
+
+    private final Label captionLabel = new Label("");
+
+    private Button monthButton;
+
+    private Button weekButton;
+
+    private Button nextButton;
+
+    private Button prevButton;
+
+    private ComboBox timeZoneSelect;
+
+    private ComboBox formatSelect;
+
+    private ComboBox localeSelect;
+
+    private CheckBox hideWeekendsButton;
+
+    private CheckBox readOnlyButton;
+
+    private TextField captionField;
+
+    private Window scheduleEventPopup;
+
+    private final FormLayout scheduleEventFieldLayout = new FormLayout();
+    private FieldGroup scheduleEventFieldGroup = new FieldGroup();
+
+    private Button deleteEventButton;
+
+    private Button applyEventButton;
+
+    private Mode viewMode = Mode.MONTH;
+
+    private BasicEventProvider dataSource;
+
+    private Button addNewEvent;
+
+    /*
+     * When testBench is set to true, CalendarTest will have static content that
+     * is more suitable for Vaadin TestBench testing. Calendar will use a static
+     * date Mon 10 Jan 2000. Enable by starting the application with a
+     * "testBench" parameter in the URL.
+     */
+    private boolean testBench = false;
+
+    private String calendarHeight = null;
+
+    private String calendarWidth = null;
+
+    private CheckBox disabledButton;
+
+    private Integer firstHour;
+
+    private Integer lastHour;
+
+    private Integer firstDay;
+
+    private Integer lastDay;
+
+    private Locale defaultLocale = Locale.US;
+
+    private boolean showWeeklyView;
+
+    private boolean useSecondResolution;
+
+    private DateField startDateField;
+    private DateField endDateField;
+
+    @SuppressWarnings("serial")
+    @Override
+    public void init(VaadinRequest request) {
+        GridLayout layout = new GridLayout();
+        layout.setSizeFull();
+        layout.setMargin(true);
+        setContent(layout);
+
+        handleURLParams(request.getParameterMap());
+
+        initContent();
+    }
+
+    private void handleURLParams(Map<String, String[]> parameters) {
+        testBench = parameters.containsKey("testBench")
+                || parameters.containsKey("?testBench");
+
+        if (parameters.containsKey("width")) {
+            calendarWidth = parameters.get("width")[0];
+        }
+
+        if (parameters.containsKey("height")) {
+            calendarHeight = parameters.get("height")[0];
+        }
+
+        if (parameters.containsKey("firstDay")) {
+            firstDay = Integer.parseInt(parameters.get("firstDay")[0]);
+        }
+
+        if (parameters.containsKey("lastDay")) {
+            lastDay = Integer.parseInt(parameters.get("lastDay")[0]);
+        }
+
+        if (parameters.containsKey("firstHour")) {
+            firstHour = Integer.parseInt(parameters.get("firstHour")[0]);
+        }
+
+        if (parameters.containsKey("lastHour")) {
+            lastHour = Integer.parseInt(parameters.get("lastHour")[0]);
+        }
+
+        if (parameters.containsKey("locale")) {
+            String localeArray[] = parameters.get("locale")[0].split("_");
+            defaultLocale = new Locale(localeArray[0], localeArray[1]);
+            setLocale(defaultLocale);
+        }
+
+        if (parameters.containsKey(("secondsResolution"))) {
+            useSecondResolution = true;
+        }
+
+        showWeeklyView = parameters.containsKey("weekly");
+
+    }
+
+    public void initContent() {
+        // Set default Locale for this application
+        if (testBench) {
+            setLocale(defaultLocale);
+
+        } else {
+            setLocale(Locale.getDefault());
+        }
+
+        // Initialize locale, timezone and timeformat selects.
+        localeSelect = createLocaleSelect();
+        timeZoneSelect = createTimeZoneSelect();
+        formatSelect = createCalendarFormatSelect();
+
+        initCalendar();
+        initLayoutContent();
+        addInitialEvents();
+    }
+
+    private Date resolveFirstDateOfWeek(Date today,
+            java.util.Calendar currentCalendar) {
+        int firstDayOfWeek = currentCalendar.getFirstDayOfWeek();
+        currentCalendar.setTime(today);
+        while (firstDayOfWeek != currentCalendar
+                .get(java.util.Calendar.DAY_OF_WEEK)) {
+            currentCalendar.add(java.util.Calendar.DATE, -1);
+        }
+        return currentCalendar.getTime();
+    }
+
+    private Date resolveLastDateOfWeek(Date today,
+            java.util.Calendar currentCalendar) {
+        currentCalendar.setTime(today);
+        currentCalendar.add(java.util.Calendar.DATE, 1);
+        int firstDayOfWeek = currentCalendar.getFirstDayOfWeek();
+        // Roll to weeks last day using firstdayofweek. Roll until FDofW is
+        // found and then roll back one day.
+        while (firstDayOfWeek != currentCalendar
+                .get(java.util.Calendar.DAY_OF_WEEK)) {
+            currentCalendar.add(java.util.Calendar.DATE, 1);
+        }
+        currentCalendar.add(java.util.Calendar.DATE, -1);
+        return currentCalendar.getTime();
+    }
+
+    private void addInitialEvents() {
+        Date originalDate = calendar.getTime();
+        Date today = getToday();
+        
+        // Add a event that last a whole week
+        
+        Date start = resolveFirstDateOfWeek(today, calendar);
+        Date end = resolveLastDateOfWeek(today, calendar);
+        CalendarTestEvent event = getNewEvent("Whole week event", start, end);
+        event.setAllDay(true);
+        event.setStyleName("color4");
+        event.setDescription("Description for the whole week event.");
+        dataSource.addEvent(event);
+
+        // Add a allday event
+        calendar.setTime(start);
+        calendar.add(GregorianCalendar.DATE, 3);
+        start = calendar.getTime();
+        end = start;
+        event = getNewEvent("Allday event", start, end);
+        event.setAllDay(true);
+        event.setDescription("Some description.");
+        event.setStyleName("color3");
+        dataSource.addEvent(event);
+
+        // Add a second allday event
+        calendar.add(GregorianCalendar.DATE, 1);
+        start = calendar.getTime();
+        end = start;
+        event = getNewEvent("Second allday event", start, end);
+        event.setAllDay(true);
+        event.setDescription("Some description.");
+        event.setStyleName("color2");
+        dataSource.addEvent(event);
+
+        calendar.add(GregorianCalendar.DATE, -3);
+        calendar.set(GregorianCalendar.HOUR_OF_DAY, 9);
+        calendar.set(GregorianCalendar.MINUTE, 30);
+        start = calendar.getTime();
+        calendar.add(GregorianCalendar.HOUR_OF_DAY, 5);
+        calendar.set(GregorianCalendar.MINUTE, 0);
+        end = calendar.getTime();
+        event = getNewEvent("Appointment", start, end);
+        event.setWhere("Office");
+        event.setStyleName("color1");
+        event.setDescription("A longer description, which should display correctly.");
+        dataSource.addEvent(event);
+
+        calendar.add(GregorianCalendar.DATE, 1);
+        calendar.set(GregorianCalendar.HOUR_OF_DAY, 11);
+        calendar.set(GregorianCalendar.MINUTE, 0);
+        start = calendar.getTime();
+        calendar.add(GregorianCalendar.HOUR_OF_DAY, 8);
+        end = calendar.getTime();
+        event = getNewEvent("Training", start, end);
+        event.setStyleName("color2");
+        dataSource.addEvent(event);
+
+        calendar.add(GregorianCalendar.DATE, 4);
+        calendar.set(GregorianCalendar.HOUR_OF_DAY, 9);
+        calendar.set(GregorianCalendar.MINUTE, 0);
+        start = calendar.getTime();
+        calendar.add(GregorianCalendar.HOUR_OF_DAY, 9);
+        end = calendar.getTime();
+        event = getNewEvent("Free time", start, end);
+        dataSource.addEvent(event);
+
+        calendar.setTime(originalDate);
+    }
+
+    private void initLayoutContent() {
+        initNavigationButtons();
+        initHideWeekEndButton();
+        initReadOnlyButton();
+        initDisabledButton();
+        initAddNewEventButton();
+
+        HorizontalLayout hl = new HorizontalLayout();
+        hl.setWidth("100%");
+        hl.setSpacing(true);
+        hl.setMargin(new MarginInfo(false, false, true, false));
+        hl.addComponent(prevButton);
+        hl.addComponent(captionLabel);
+        hl.addComponent(monthButton);
+        hl.addComponent(weekButton);
+        hl.addComponent(nextButton);
+        hl.setComponentAlignment(prevButton, Alignment.MIDDLE_LEFT);
+        hl.setComponentAlignment(captionLabel, Alignment.MIDDLE_CENTER);
+        hl.setComponentAlignment(monthButton, Alignment.MIDDLE_CENTER);
+        hl.setComponentAlignment(weekButton, Alignment.MIDDLE_CENTER);
+        hl.setComponentAlignment(nextButton, Alignment.MIDDLE_RIGHT);
+
+        monthButton.setVisible(viewMode == Mode.WEEK);
+        weekButton.setVisible(viewMode == Mode.DAY);
+
+        HorizontalLayout controlPanel = new HorizontalLayout();
+        controlPanel.setSpacing(true);
+        controlPanel.setMargin(new MarginInfo(false, false, true, false));
+        controlPanel.setWidth("100%");
+        controlPanel.addComponent(localeSelect);
+        controlPanel.addComponent(timeZoneSelect);
+        controlPanel.addComponent(formatSelect);
+        controlPanel.addComponent(hideWeekendsButton);
+        controlPanel.addComponent(readOnlyButton);
+        controlPanel.addComponent(disabledButton);
+        controlPanel.addComponent(addNewEvent);
+
+        controlPanel.setComponentAlignment(timeZoneSelect,
+                Alignment.MIDDLE_LEFT);
+        controlPanel.setComponentAlignment(formatSelect, Alignment.MIDDLE_LEFT);
+        controlPanel.setComponentAlignment(localeSelect, Alignment.MIDDLE_LEFT);
+        controlPanel.setComponentAlignment(hideWeekendsButton,
+                Alignment.MIDDLE_LEFT);
+        controlPanel.setComponentAlignment(readOnlyButton,
+                Alignment.MIDDLE_LEFT);
+        controlPanel.setComponentAlignment(disabledButton,
+                Alignment.MIDDLE_LEFT);
+        controlPanel.setComponentAlignment(addNewEvent, Alignment.MIDDLE_LEFT);
+
+        GridLayout layout = (GridLayout) getContent();
+        layout.addComponent(controlPanel);
+        layout.addComponent(hl);
+        layout.addComponent(calendarComponent);
+        layout.setRowExpandRatio(layout.getRows() - 1, 1.0f);
+    }
+
+    private void initNavigationButtons() {
+        monthButton = new Button("Month view", new ClickListener() {
+
+            private static final long serialVersionUID = 1L;
+
+            public void buttonClick(ClickEvent event) {
+                switchToMonthView();
+            }
+        });
+
+        weekButton = new Button("Week view", new ClickListener() {
+
+            private static final long serialVersionUID = 1L;
+
+            public void buttonClick(ClickEvent event) {
+                // simulate week click
+                WeekClickHandler handler = (WeekClickHandler) calendarComponent
+                        .getHandler(WeekClick.EVENT_ID);
+                handler.weekClick(new WeekClick(calendarComponent, calendar
+                        .get(GregorianCalendar.WEEK_OF_YEAR), calendar
+                        .get(GregorianCalendar.YEAR)));
+            }
+        });
+
+        nextButton = new Button("Next", new Button.ClickListener() {
+            private static final long serialVersionUID = 1L;
+
+            public void buttonClick(ClickEvent event) {
+                handleNextButtonClick();
+            }
+        });
+
+        prevButton = new Button("Prev", new Button.ClickListener() {
+            private static final long serialVersionUID = 1L;
+
+            public void buttonClick(ClickEvent event) {
+                handlePreviousButtonClick();
+            }
+        });
+    }
+
+    private void initHideWeekEndButton() {
+        hideWeekendsButton = new CheckBox("Hide weekends");
+        hideWeekendsButton.setImmediate(true);
+        hideWeekendsButton
+                .addValueChangeListener(new Property.ValueChangeListener() {
+
+                    private static final long serialVersionUID = 1L;
+
+                    @Override
+                    public void valueChange(ValueChangeEvent event) {
+                        setWeekendsHidden(hideWeekendsButton.getValue());
+                    }
+                });
+    }
+
+    private void setWeekendsHidden(boolean weekendsHidden) {
+        if (weekendsHidden) {
+            int firstToShow = (GregorianCalendar.MONDAY - calendar
+                    .getFirstDayOfWeek()) % 7;
+            calendarComponent.setFirstVisibleDayOfWeek(firstToShow + 1);
+            calendarComponent.setLastVisibleDayOfWeek(firstToShow + 5);
+        } else {
+            calendarComponent.setFirstVisibleDayOfWeek(1);
+            calendarComponent.setLastVisibleDayOfWeek(7);
+        }
+
+    }
+
+    private void initReadOnlyButton() {
+        readOnlyButton = new CheckBox("Read-only mode");
+        readOnlyButton.setImmediate(true);
+        readOnlyButton
+                .addValueChangeListener(new Property.ValueChangeListener() {
+
+                    private static final long serialVersionUID = 1L;
+
+                    @Override
+                    public void valueChange(ValueChangeEvent event) {
+                        calendarComponent.setReadOnly(readOnlyButton.getValue());
+                    }
+                });
+    }
+
+    private void initDisabledButton() {
+        disabledButton = new CheckBox("Disabled");
+        disabledButton.setImmediate(true);
+        disabledButton
+                .addValueChangeListener(new Property.ValueChangeListener() {
+
+                    private static final long serialVersionUID = 1L;
+
+                    @Override
+                    public void valueChange(ValueChangeEvent event) {
+                        calendarComponent.setEnabled(!disabledButton.getValue());
+                    }
+                });
+    }
+
+    public void initAddNewEventButton() {
+        addNewEvent = new Button("Add new event");
+        addNewEvent.addClickListener(new Button.ClickListener() {
+
+            private static final long serialVersionUID = -8307244759142541067L;
+
+            public void buttonClick(ClickEvent event) {
+                Date start = getToday();
+                start.setHours(0);
+                start.setMinutes(0);
+                start.setSeconds(0);
+
+                Date end = getEndOfDay(calendar, start);
+
+                showEventPopup(createNewEvent(start, end), true);
+            }
+        });
+    }
+
+    private void initFormFields(Layout formLayout,
+            Class<? extends CalendarEvent> eventClass) {
+
+        startDateField = createDateField("Start date");
+        endDateField = createDateField("End date");
+
+        final CheckBox allDayField = createCheckBox("All-day");
+        allDayField.addValueChangeListener(new Property.ValueChangeListener() {
+
+            private static final long serialVersionUID = -7104996493482558021L;
+
+            public void valueChange(ValueChangeEvent event) {
+                Object value = event.getProperty().getValue();
+                if (value instanceof Boolean && Boolean.TRUE.equals(value)) {
+                    setFormDateResolution(Resolution.DAY);
+
+                } else {
+                    setFormDateResolution(Resolution.MINUTE);
+                }
+            }
+
+        });
+
+        captionField = createTextField("Caption");
+        final TextField whereField = createTextField("Where");
+        final TextArea descriptionField = createTextArea("Description");
+        descriptionField.setRows(3);
+
+        final ComboBox styleNameField = createStyleNameComboBox();
+
+        formLayout.addComponent(startDateField);
+        formLayout.addComponent(endDateField);
+        formLayout.addComponent(allDayField);
+        formLayout.addComponent(captionField);
+        if (eventClass == CalendarTestEvent.class) {
+            formLayout.addComponent(whereField);
+        }
+        formLayout.addComponent(descriptionField);
+        formLayout.addComponent(styleNameField);
+
+        scheduleEventFieldGroup.bind(startDateField, "start");
+        scheduleEventFieldGroup.bind(endDateField, "end");
+        scheduleEventFieldGroup.bind(captionField, "caption");
+        scheduleEventFieldGroup.bind(descriptionField, "description");
+        if (eventClass == CalendarTestEvent.class) {
+            scheduleEventFieldGroup.bind(whereField, "where");
+        }
+        scheduleEventFieldGroup.bind(styleNameField, "styleName");
+        scheduleEventFieldGroup.bind(allDayField, "allDay");
+    }
+
+    private CheckBox createCheckBox(String caption) {
+        CheckBox cb = new CheckBox(caption);
+        cb.setImmediate(true);
+        return cb;
+    }
+
+    private TextField createTextField(String caption) {
+        TextField f = new TextField(caption);
+        f.setNullRepresentation("");
+        return f;
+    }
+
+    private TextArea createTextArea(String caption) {
+        TextArea f = new TextArea(caption);
+        f.setNullRepresentation("");
+        return f;
+    }
+
+    private DateField createDateField(String caption) {
+        DateField f = new DateField(caption);
+        if (useSecondResolution) {
+            f.setResolution(Resolution.SECOND);
+        } else {
+            f.setResolution(Resolution.MINUTE);
+        }
+        return f;
+    }
+
+    private ComboBox createStyleNameComboBox() {
+        ComboBox s = new ComboBox("Color");
+        s.addContainerProperty("c", String.class, "");
+        s.setItemCaptionPropertyId("c");
+        Item i = s.addItem("color1");
+        i.getItemProperty("c").setValue("Green");
+        i = s.addItem("color2");
+        i.getItemProperty("c").setValue("Blue");
+        i = s.addItem("color3");
+        i.getItemProperty("c").setValue("Red");
+        i = s.addItem("color4");
+        i.getItemProperty("c").setValue("Orange");
+        return s;
+    }
+
+    private void initCalendar() {
+        dataSource = new BasicEventProvider();
+
+        calendarComponent = new Calendar(dataSource);
+        calendarComponent.setLocale(getLocale());
+        calendarComponent.setImmediate(true);
+
+        if (calendarWidth != null || calendarHeight != null) {
+            if (calendarHeight != null) {
+                calendarComponent.setHeight(calendarHeight);
+            }
+            if (calendarWidth != null) {
+                calendarComponent.setWidth(calendarWidth);
+            }
+        } else {
+            calendarComponent.setSizeFull();
+        }
+
+        if (firstHour != null && lastHour != null) {
+            calendarComponent.setFirstVisibleHourOfDay(firstHour);
+            calendarComponent.setLastVisibleHourOfDay(lastHour);
+        }
+
+        if (firstDay != null && lastDay != null) {
+            calendarComponent.setFirstVisibleDayOfWeek(firstDay);
+            calendarComponent.setLastVisibleDayOfWeek(lastDay);
+        }
+
+        Date today = getToday();
+        calendar = new GregorianCalendar(getLocale());
+        calendar.setTime(today);
+
+        updateCaptionLabel();
+
+        if (!showWeeklyView) {
+            int rollAmount = calendar.get(GregorianCalendar.DAY_OF_MONTH) - 1;
+            calendar.add(GregorianCalendar.DAY_OF_MONTH, -rollAmount);
+            resetTime(false);
+            currentMonthsFirstDate = calendar.getTime();
+            calendarComponent.setStartDate(currentMonthsFirstDate);
+            calendar.add(GregorianCalendar.MONTH, 1);
+            calendar.add(GregorianCalendar.DATE, -1);
+            calendarComponent.setEndDate(calendar.getTime());
+        }
+
+        addCalendarEventListeners();
+    }
+
+    private Date getToday() {
+        if (testBench) {
+            GregorianCalendar testDate = new GregorianCalendar();
+            testDate.set(GregorianCalendar.YEAR, 2000);
+            testDate.set(GregorianCalendar.MONTH, 0);
+            testDate.set(GregorianCalendar.DATE, 10);
+            testDate.set(GregorianCalendar.HOUR_OF_DAY, 0);
+            testDate.set(GregorianCalendar.MINUTE, 0);
+            testDate.set(GregorianCalendar.SECOND, 0);
+            testDate.set(GregorianCalendar.MILLISECOND, 0);
+            return testDate.getTime();
+        }
+        return new Date();
+    }
+
+    @SuppressWarnings("serial")
+    private void addCalendarEventListeners() {
+        // Register week clicks by changing the schedules start and end dates.
+        calendarComponent.setHandler(new BasicWeekClickHandler() {
+
+            @Override
+            public void weekClick(WeekClick event) {
+                // let BasicWeekClickHandler handle calendar dates, and update
+                // only the other parts of UI here
+                super.weekClick(event);
+                updateCaptionLabel();
+                switchToWeekView();
+            }
+        });
+
+        calendarComponent.setHandler(new EventClickHandler() {
+
+            public void eventClick(EventClick event) {
+                showEventPopup(event.getCalendarEvent(), false);
+            }
+        });
+
+        calendarComponent.setHandler(new BasicDateClickHandler() {
+
+            @Override
+            public void dateClick(DateClickEvent event) {
+                // let BasicDateClickHandler handle calendar dates, and update
+                // only the other parts of UI here
+                super.dateClick(event);
+                switchToDayView();
+            }
+        });
+
+        calendarComponent.setHandler(new RangeSelectHandler() {
+
+            public void rangeSelect(RangeSelectEvent event) {
+                handleRangeSelect(event);
+            }
+        });
+    }
+
+    private ComboBox createTimeZoneSelect() {
+        ComboBox s = new ComboBox("Timezone");
+        s.addContainerProperty("caption", String.class, "");
+        s.setItemCaptionPropertyId("caption");
+        s.setFilteringMode(FilteringMode.CONTAINS);
+
+        Item i = s.addItem(DEFAULT_ITEMID);
+        i.getItemProperty("caption").setValue(
+                "Default (" + TimeZone.getDefault().getID() + ")");
+        for (String id : TimeZone.getAvailableIDs()) {
+            if (!s.containsId(id)) {
+                i = s.addItem(id);
+                i.getItemProperty("caption").setValue(id);
+            }
+        }
+
+        if (testBench) {
+            s.select("America/New_York");
+        } else {
+            s.select(DEFAULT_ITEMID);
+        }
+        s.setImmediate(true);
+        s.addValueChangeListener(new ValueChangeListener() {
+
+            private static final long serialVersionUID = 1L;
+
+            public void valueChange(ValueChangeEvent event) {
+
+                updateCalendarTimeZone(event.getProperty().getValue());
+            }
+        });
+
+        return s;
+    }
+
+    private ComboBox createCalendarFormatSelect() {
+        ComboBox s = new ComboBox("Calendar format");
+        s.addContainerProperty("caption", String.class, "");
+        s.setItemCaptionPropertyId("caption");
+
+        Item i = s.addItem(DEFAULT_ITEMID);
+        i.getItemProperty("caption").setValue("Default by locale");
+        i = s.addItem(TimeFormat.Format12H);
+        i.getItemProperty("caption").setValue("12H");
+        i = s.addItem(TimeFormat.Format24H);
+        i.getItemProperty("caption").setValue("24H");
+
+        s.select(DEFAULT_ITEMID);
+        s.setImmediate(true);
+        s.addValueChangeListener(new ValueChangeListener() {
+
+            private static final long serialVersionUID = 1L;
+
+            public void valueChange(ValueChangeEvent event) {
+                updateCalendarFormat(event.getProperty().getValue());
+            }
+        });
+
+        return s;
+    }
+
+    private ComboBox createLocaleSelect() {
+        ComboBox s = new ComboBox("Locale");
+        s.addContainerProperty("caption", String.class, "");
+        s.setItemCaptionPropertyId("caption");
+        s.setFilteringMode(FilteringMode.CONTAINS);
+
+        for (Locale l : Locale.getAvailableLocales()) {
+            if (!s.containsId(l)) {
+                Item i = s.addItem(l);
+                i.getItemProperty("caption").setValue(getLocaleItemCaption(l));
+            }
+        }
+
+        s.select(getLocale());
+        s.setImmediate(true);
+        s.addValueChangeListener(new ValueChangeListener() {
+
+            private static final long serialVersionUID = 1L;
+
+            public void valueChange(ValueChangeEvent event) {
+                updateCalendarLocale((Locale) event.getProperty().getValue());
+            }
+        });
+
+        return s;
+    }
+
+    private void updateCalendarTimeZone(Object timezoneId) {
+        TimeZone tz = null;
+        if (!DEFAULT_ITEMID.equals(timezoneId)) {
+            tz = TimeZone.getTimeZone((String) timezoneId);
+        }
+
+        // remember the week that was showing, so we can re-set it later
+        Date startDate = calendarComponent.getStartDate();
+        calendar.setTime(startDate);
+        int weekNumber = calendar.get(java.util.Calendar.WEEK_OF_YEAR);
+        calendarComponent.setTimeZone(tz);
+        calendar.setTimeZone(calendarComponent.getTimeZone());
+
+        if (viewMode == Mode.WEEK) {
+            calendar.set(java.util.Calendar.WEEK_OF_YEAR, weekNumber);
+            calendar.set(java.util.Calendar.DAY_OF_WEEK,
+                    calendar.getFirstDayOfWeek());
+
+            calendarComponent.setStartDate(calendar.getTime());
+            calendar.add(java.util.Calendar.DATE, 6);
+            calendarComponent.setEndDate(calendar.getTime());
+        }
+    }
+
+    private void updateCalendarFormat(Object format) {
+        TimeFormat calFormat = null;
+        if (format instanceof TimeFormat) {
+            calFormat = (TimeFormat) format;
+        }
+
+        calendarComponent.setTimeFormat(calFormat);
+    }
+
+    private String getLocaleItemCaption(Locale l) {
+        String country = l.getDisplayCountry(getLocale());
+        String language = l.getDisplayLanguage(getLocale());
+        StringBuilder caption = new StringBuilder(country);
+        if (caption.length() != 0) {
+            caption.append(", ");
+        }
+        caption.append(language);
+        return caption.toString();
+    }
+
+    private void updateCalendarLocale(Locale l) {
+        int oldFirstDayOfWeek = calendar.getFirstDayOfWeek();
+        setLocale(l);
+        calendarComponent.setLocale(l);
+        calendar = new GregorianCalendar(l);
+        int newFirstDayOfWeek = calendar.getFirstDayOfWeek();
+
+        // we are showing 1 week, and the first day of the week has changed
+        // update start and end dates so that the same week is showing
+        if (viewMode == Mode.WEEK && oldFirstDayOfWeek != newFirstDayOfWeek) {
+            calendar.setTime(calendarComponent.getStartDate());
+            calendar.add(java.util.Calendar.DAY_OF_WEEK, 2);
+            // starting at the beginning of the week
+            calendar.set(GregorianCalendar.DAY_OF_WEEK, newFirstDayOfWeek);
+            Date start = calendar.getTime();
+
+            // ending at the end of the week
+            calendar.add(GregorianCalendar.DATE, 6);
+            Date end = calendar.getTime();
+
+            calendarComponent.setStartDate(start);
+            calendarComponent.setEndDate(end);
+
+            // Week days depend on locale so this must be refreshed
+            setWeekendsHidden(hideWeekendsButton.getValue());
+        }
+
+    }
+
+    private void handleNextButtonClick() {
+        switch (viewMode) {
+        case MONTH:
+            nextMonth();
+            break;
+        case WEEK:
+            nextWeek();
+            break;
+        case DAY:
+            nextDay();
+            break;
+        }
+    }
+
+    private void handlePreviousButtonClick() {
+        switch (viewMode) {
+        case MONTH:
+            previousMonth();
+            break;
+        case WEEK:
+            previousWeek();
+            break;
+        case DAY:
+            previousDay();
+            break;
+        }
+    }
+
+    private void handleRangeSelect(RangeSelectEvent event) {
+        Date start = event.getStart();
+        Date end = event.getEnd();
+
+        /*
+         * If a range of dates is selected in monthly mode, we want it to end at
+         * the end of the last day.
+         */
+        if (event.isMonthlyMode()) {
+            end = getEndOfDay(calendar, end);
+        }
+
+        showEventPopup(createNewEvent(start, end), true);
+    }
+
+    private void showEventPopup(CalendarEvent event, boolean newEvent) {
+        if (event == null) {
+            return;
+        }
+
+        updateCalendarEventPopup(newEvent);
+        updateCalendarEventForm(event);
+
+        if (!getWindows().contains(scheduleEventPopup)) {
+            addWindow(scheduleEventPopup);
+        }
+    }
+
+    /* Initializes a modal window to edit schedule event. */
+    private void createCalendarEventPopup() {
+        VerticalLayout layout = new VerticalLayout();
+        layout.setMargin(true);
+        layout.setSpacing(true);
+
+        scheduleEventPopup = new Window(null, layout);
+        scheduleEventPopup.setWidth("400px");
+        scheduleEventPopup.setModal(true);
+        scheduleEventPopup.center();
+
+        layout.addComponent(scheduleEventFieldLayout);
+
+        applyEventButton = new Button("Apply", new ClickListener() {
+
+            private static final long serialVersionUID = 1L;
+
+            public void buttonClick(ClickEvent event) {
+                try {
+                    commitCalendarEvent();
+                } catch (CommitException e) {
+                    e.printStackTrace();
+                }
+            }
+        });
+        Button cancel = new Button("Cancel", new ClickListener() {
+
+            private static final long serialVersionUID = 1L;
+
+            public void buttonClick(ClickEvent event) {
+                discardCalendarEvent();
+            }
+        });
+        deleteEventButton = new Button("Delete", new ClickListener() {
+
+            private static final long serialVersionUID = 1L;
+
+            public void buttonClick(ClickEvent event) {
+                deleteCalendarEvent();
+            }
+        });
+        scheduleEventPopup.addCloseListener(new Window.CloseListener() {
+
+            private static final long serialVersionUID = 1L;
+
+            public void windowClose(Window.CloseEvent e) {
+                discardCalendarEvent();
+            }
+        });
+
+        HorizontalLayout buttons = new HorizontalLayout();
+        buttons.setSpacing(true);
+        buttons.addComponent(deleteEventButton);
+        buttons.addComponent(applyEventButton);
+        buttons.addComponent(cancel);
+        layout.addComponent(buttons);
+        layout.setComponentAlignment(buttons, Alignment.BOTTOM_RIGHT);
+    }
+
+    private void updateCalendarEventPopup(boolean newEvent) {
+        if (scheduleEventPopup == null) {
+            createCalendarEventPopup();
+        }
+
+        if (newEvent) {
+            scheduleEventPopup.setCaption("New event");
+        } else {
+            scheduleEventPopup.setCaption("Edit event");
+        }
+
+        deleteEventButton.setVisible(!newEvent);
+        deleteEventButton.setEnabled(!calendarComponent.isReadOnly());
+        applyEventButton.setEnabled(!calendarComponent.isReadOnly());
+    }
+
+    private void updateCalendarEventForm(CalendarEvent event) {
+        BeanItem<CalendarEvent> item = new BeanItem<CalendarEvent>(event);
+        scheduleEventFieldLayout.removeAllComponents();
+        scheduleEventFieldGroup = new FieldGroup();
+        initFormFields(scheduleEventFieldLayout, event.getClass());
+        scheduleEventFieldGroup.setBuffered(true);
+        scheduleEventFieldGroup.setItemDataSource(item);
+    }
+
+    private void setFormDateResolution(Resolution resolution) {
+        if (startDateField != null && endDateField != null) {
+            startDateField.setResolution(resolution);
+            endDateField.setResolution(resolution);
+        }
+    }
+
+    private CalendarEvent createNewEvent(Date startDate, Date endDate) {
+
+        BasicEvent event = new BasicEvent();
+        event.setCaption("");
+        event.setStart(startDate);
+        event.setEnd(endDate);
+        event.setStyleName("color1");
+        return event;
+    }
+
+    /* Removes the event from the data source and fires change event. */
+    private void deleteCalendarEvent() {
+        BasicEvent event = getFormCalendarEvent();
+        if (dataSource.containsEvent(event)) {
+            dataSource.removeEvent(event);
+        }
+        removeWindow(scheduleEventPopup);
+    }
+
+    /* Adds/updates the event in the data source and fires change event. */
+    private void commitCalendarEvent() throws CommitException {
+        scheduleEventFieldGroup.commit();
+        BasicEvent event = getFormCalendarEvent();
+        if (event.getEnd() == null) {
+            event.setEnd(event.getStart());
+        }
+        if (!dataSource.containsEvent(event)) {
+            dataSource.addEvent(event);
+        }
+
+        removeWindow(scheduleEventPopup);
+    }
+
+    private void discardCalendarEvent() {
+        scheduleEventFieldGroup.discard();
+        removeWindow(scheduleEventPopup);
+    }
+
+    @SuppressWarnings("unchecked")
+    private BasicEvent getFormCalendarEvent() {
+        BeanItem<CalendarEvent> item = (BeanItem<CalendarEvent>) scheduleEventFieldGroup
+                .getItemDataSource();
+        CalendarEvent event = item.getBean();
+        return (BasicEvent) event;
+    }
+
+    private void nextMonth() {
+        rollMonth(1);
+    }
+
+    private void previousMonth() {
+        rollMonth(-1);
+    }
+
+    private void nextWeek() {
+        rollWeek(1);
+    }
+
+    private void previousWeek() {
+        rollWeek(-1);
+    }
+
+    private void nextDay() {
+        rollDate(1);
+    }
+
+    private void previousDay() {
+        rollDate(-1);
+    }
+
+    private void rollMonth(int direction) {
+        calendar.setTime(currentMonthsFirstDate);
+        calendar.add(GregorianCalendar.MONTH, direction);
+        resetTime(false);
+        currentMonthsFirstDate = calendar.getTime();
+        calendarComponent.setStartDate(currentMonthsFirstDate);
+
+        updateCaptionLabel();
+
+        calendar.add(GregorianCalendar.MONTH, 1);
+        calendar.add(GregorianCalendar.DATE, -1);
+        resetCalendarTime(true);
+    }
+
+    private void rollWeek(int direction) {
+        calendar.add(GregorianCalendar.WEEK_OF_YEAR, direction);
+        calendar.set(GregorianCalendar.DAY_OF_WEEK,
+                calendar.getFirstDayOfWeek());
+        resetCalendarTime(false);
+        resetTime(true);
+        calendar.add(GregorianCalendar.DATE, 6);
+        calendarComponent.setEndDate(calendar.getTime());
+    }
+
+    private void rollDate(int direction) {
+        calendar.add(GregorianCalendar.DATE, direction);
+        resetCalendarTime(false);
+        resetCalendarTime(true);
+    }
+
+    private void updateCaptionLabel() {
+        DateFormatSymbols s = new DateFormatSymbols(getLocale());
+        String month = s.getShortMonths()[calendar.get(GregorianCalendar.MONTH)];
+        captionLabel.setValue(month + " "
+                + calendar.get(GregorianCalendar.YEAR));
+    }
+
+    private CalendarTestEvent getNewEvent(String caption, Date start, Date end) {
+        CalendarTestEvent event = new CalendarTestEvent();
+        event.setCaption(caption);
+        event.setStart(start);
+        event.setEnd(end);
+
+        return event;
+    }
+
+    /*
+     * Switch the view to week view.
+     */
+    public void switchToWeekView() {
+        viewMode = Mode.WEEK;
+        weekButton.setVisible(false);
+        monthButton.setVisible(true);
+    }
+
+    /*
+     * Switch the Calendar component's start and end date range to the target
+     * month only. (sample range: 01.01.2010 00:00.000 - 31.01.2010 23:59.999)
+     */
+    public void switchToMonthView() {
+        viewMode = Mode.MONTH;
+        monthButton.setVisible(false);
+        weekButton.setVisible(false);
+
+        calendar.setTime(currentMonthsFirstDate);
+        calendarComponent.setStartDate(currentMonthsFirstDate);
+
+        updateCaptionLabel();
+
+        calendar.add(GregorianCalendar.MONTH, 1);
+        calendar.add(GregorianCalendar.DATE, -1);
+        resetCalendarTime(true);
+    }
+
+    /*
+     * Switch to day view (week view with a single day visible).
+     */
+    public void switchToDayView() {
+        viewMode = Mode.DAY;
+        monthButton.setVisible(true);
+        weekButton.setVisible(true);
+    }
+
+    private void resetCalendarTime(boolean resetEndTime) {
+        resetTime(resetEndTime);
+        if (resetEndTime) {
+            calendarComponent.setEndDate(calendar.getTime());
+        } else {
+            calendarComponent.setStartDate(calendar.getTime());
+            updateCaptionLabel();
+        }
+    }
+
+    /*
+     * Resets the calendar time (hour, minute second and millisecond) either to
+     * zero or maximum value.
+     */
+    private void resetTime(boolean max) {
+        if (max) {
+            calendar.set(GregorianCalendar.HOUR_OF_DAY,
+                    calendar.getMaximum(GregorianCalendar.HOUR_OF_DAY));
+            calendar.set(GregorianCalendar.MINUTE,
+                    calendar.getMaximum(GregorianCalendar.MINUTE));
+            calendar.set(GregorianCalendar.SECOND,
+                    calendar.getMaximum(GregorianCalendar.SECOND));
+            calendar.set(GregorianCalendar.MILLISECOND,
+                    calendar.getMaximum(GregorianCalendar.MILLISECOND));
+        } else {
+            calendar.set(GregorianCalendar.HOUR_OF_DAY, 0);
+            calendar.set(GregorianCalendar.MINUTE, 0);
+            calendar.set(GregorianCalendar.SECOND, 0);
+            calendar.set(GregorianCalendar.MILLISECOND, 0);
+        }
+    }
+
+    private static Date getEndOfDay(java.util.Calendar calendar, Date date) {
+        java.util.Calendar calendarClone = (java.util.Calendar) calendar
+                .clone();
+
+        calendarClone.setTime(date);
+        calendarClone.set(java.util.Calendar.MILLISECOND,
+                calendarClone.getActualMaximum(java.util.Calendar.MILLISECOND));
+        calendarClone.set(java.util.Calendar.SECOND,
+                calendarClone.getActualMaximum(java.util.Calendar.SECOND));
+        calendarClone.set(java.util.Calendar.MINUTE,
+                calendarClone.getActualMaximum(java.util.Calendar.MINUTE));
+        calendarClone.set(java.util.Calendar.HOUR,
+                calendarClone.getActualMaximum(java.util.Calendar.HOUR));
+        calendarClone.set(java.util.Calendar.HOUR_OF_DAY,
+                calendarClone.getActualMaximum(java.util.Calendar.HOUR_OF_DAY));
+
+        return calendarClone.getTime();
+    }
+
+    private static Date getStartOfDay(java.util.Calendar calendar, Date date) {
+        java.util.Calendar calendarClone = (java.util.Calendar) calendar
+                .clone();
+
+        calendarClone.setTime(date);
+        calendarClone.set(java.util.Calendar.MILLISECOND, 0);
+        calendarClone.set(java.util.Calendar.SECOND, 0);
+        calendarClone.set(java.util.Calendar.MINUTE, 0);
+        calendarClone.set(java.util.Calendar.HOUR, 0);
+        calendarClone.set(java.util.Calendar.HOUR_OF_DAY, 0);
+
+        return calendarClone.getTime();
+    }
+}
diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarTestEvent.java b/uitest/src/com/vaadin/tests/components/calendar/CalendarTestEvent.java
new file mode 100644 (file)
index 0000000..29b8f62
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.components.calendar;
+
+import com.vaadin.ui.components.calendar.event.BasicEvent;
+
+/**
+ * Test CalendarEvent implementation.
+ * 
+ * @see com.vaadin.addon.calendar.test.ui.Calendar.Event
+ */
+public class CalendarTestEvent extends BasicEvent {
+
+    private static final long serialVersionUID = 2820133201983036866L;
+    private String where;
+    private Object data;
+
+    public String getWhere() {
+        return where;
+    }
+
+    public void setWhere(String where) {
+        this.where = where;
+        fireEventChange();
+    }
+
+    public Object getData() {
+        return data;
+    }
+
+    public void setData(Object data) {
+        this.data = data;
+        fireEventChange();
+    }
+}
diff --git a/uitest/src/com/vaadin/tests/components/calendar/HiddenFwdBackButtons.java b/uitest/src/com/vaadin/tests/components/calendar/HiddenFwdBackButtons.java
new file mode 100644 (file)
index 0000000..8b78909
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.components.calendar;
+
+import java.util.Date;
+import java.util.Locale;
+
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Button.ClickListener;
+import com.vaadin.ui.Calendar;
+import com.vaadin.ui.GridLayout;
+import com.vaadin.ui.UI;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.BackwardHandler;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.ForwardHandler;
+
+public class HiddenFwdBackButtons extends UI {
+
+    @SuppressWarnings("deprecation")
+    @Override
+    protected void init(VaadinRequest request) {
+        GridLayout content = new GridLayout(1, 2);
+        content.setSizeFull();
+        setContent(content);
+
+        final Calendar calendar = new Calendar();
+        calendar.setLocale(new Locale("fi", "FI"));
+
+        calendar.setSizeFull();
+        calendar.setStartDate(new Date(100, 1, 1));
+        calendar.setEndDate(new Date(100, 1, 7));
+        content.addComponent(calendar);
+        Button button = new Button("Hide forward and back buttons");
+        button.addClickListener(new ClickListener() {
+            @Override
+            public void buttonClick(ClickEvent event) {
+                // This should hide the forward and back navigation buttons
+                calendar.setHandler((BackwardHandler) null);
+                calendar.setHandler((ForwardHandler) null);
+            }
+        });
+        content.addComponent(button);
+
+        content.setRowExpandRatio(0, 1);
+
+    }
+}
diff --git a/uitest/src/com/vaadin/tests/components/calendar/NotificationTestUI.java b/uitest/src/com/vaadin/tests/components/calendar/NotificationTestUI.java
new file mode 100644 (file)
index 0000000..0bd327d
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.components.calendar;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Calendar;
+import com.vaadin.ui.GridLayout;
+import com.vaadin.ui.Notification;
+import com.vaadin.ui.UI;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.DateClickEvent;
+import com.vaadin.ui.components.calendar.CalendarComponentEvents.DateClickHandler;
+import com.vaadin.ui.components.calendar.event.BasicEvent;
+import com.vaadin.ui.components.calendar.event.CalendarEvent;
+import com.vaadin.ui.components.calendar.event.CalendarEventProvider;
+
+public class NotificationTestUI extends UI {
+
+    private DummyEventProvider provider;
+
+    private static class DummyEventProvider implements CalendarEventProvider {
+
+        private int index;
+        private List<CalendarEvent> events = new ArrayList<CalendarEvent>();
+
+        public void addEvent(Date date) {
+            BasicEvent e = new BasicEvent();
+            e.setAllDay(true);
+            e.setStart(date);
+            e.setEnd(date);
+            e.setCaption("Some event " + ++index);
+            events.add(e);
+        }
+
+        public List<CalendarEvent> getEvents(Date startDate, Date endDate) {
+            return events;
+        }
+
+    }
+
+    @Override
+    protected void init(com.vaadin.server.VaadinRequest request) {
+        GridLayout content = new GridLayout(1, 2);
+        content.setSizeFull();
+        content.setRowExpandRatio(1, 1.0f);
+        setContent(content);
+        final Button btn = new Button("Show working notification",
+                new Button.ClickListener() {
+                    public void buttonClick(ClickEvent event) {
+                        Notification
+                                .show("This will disappear when you move your mouse!");
+                    }
+                });
+        content.addComponent(btn);
+
+        provider = new DummyEventProvider();
+        final Calendar cal = new Calendar(provider);
+        cal.setLocale(Locale.US);
+        cal.setSizeFull();
+        cal.setHandler(new DateClickHandler() {
+            public void dateClick(DateClickEvent event) {
+                provider.addEvent(event.getDate());
+                Notification
+                        .show("This should disappear, but if wont unless clicked.");
+
+                // this requestRepaint call interferes with the notification
+                cal.markAsDirty();
+            }
+        });
+        content.addComponent(cal);
+
+        java.util.Calendar javaCal = java.util.Calendar.getInstance();
+        javaCal.set(java.util.Calendar.YEAR, 2000);
+        javaCal.set(java.util.Calendar.MONTH, 0);
+        javaCal.set(java.util.Calendar.DAY_OF_MONTH, 1);
+        Date start = javaCal.getTime();
+        javaCal.set(java.util.Calendar.DAY_OF_MONTH, 31);
+        Date end = javaCal.getTime();
+
+        cal.setStartDate(start);
+        cal.setEndDate(end);
+    }
+}
diff --git a/uitest/src/com/vaadin/tests/components/calendar/actions.html b/uitest/src/com/vaadin/tests/components/calendar/actions.html
new file mode 100644 (file)
index 0000000..bec3f2a
--- /dev/null
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="http://localhost:8080/" />
+<title>New Test</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">New Test</td></tr>
+</thead><tbody>
+<tr>
+       <td>open</td>
+       <td>/run/com.vaadin.tests.components.calendar.CalendarActionsUI?restartApplication</td>
+       <td></td>
+</tr>
+<tr>
+       <td>contextMenuAt</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarActionsUI::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[7]</td>
+       <td>100,20</td>
+</tr>
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td>addEventContextMenu</td>
+</tr>
+<tr>
+       <td>contextMenuAt</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarActionsUI::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[2]/domChild[0]/domChild[8]</td>
+       <td>100,20</td>
+</tr>
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td>removeEventContextMenu</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarActionsUI::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[3]/domChild[0]/domChild[8]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td>NoContextMenu</td>
+</tr>
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/components/calendar/basicNavigation.html b/uitest/src/com/vaadin/tests/components/calendar/basicNavigation.html
new file mode 100644 (file)
index 0000000..cccb88b
--- /dev/null
@@ -0,0 +1,236 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="" />
+<title>basicNavigation</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">basicNavigation</td></tr>
+</thead><tbody>
+<tr>
+       <td>open</td>
+       <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&amp;width=1000px&amp;height=600px&amp;restartApplication</td>
+       <td></td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td>
+       <td>Jan 2000</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td>
+       <td>Feb 2000</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td>
+       <td>Mar 2000</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td>
+       <td>Feb 2000</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td>
+       <td>Jan 2000</td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+       <td>26</td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[5]/domChild[6]/domChild[0]/domChild[0]</td>
+       <td>5</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[1]/domChild[2]/domChild[0]/domChild[0]</td>
+       <td>9,44</td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]</td>
+       <td>Sunday 1/9/00</td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[7]/domChild[0]</td>
+       <td>Saturday 1/15/00</td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[1]</td>
+       <td>1 AM</td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[23]</td>
+       <td>11 PM</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[8]/domChild[0]</td>
+       <td>4,6</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[8]/domChild[0]</td>
+       <td>4,5</td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]</td>
+       <td>Sunday 1/23/00</td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[7]/domChild[0]</td>
+       <td>Saturday 1/29/00</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+       <td>9,7</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+       <td>9,7</td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]</td>
+       <td>Sunday 1/9/00</td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[7]/domChild[0]</td>
+       <td>Saturday 1/15/00</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]</td>
+       <td>77,5</td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]</td>
+       <td>Tuesday 1/11/00</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+       <td>8,8</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+       <td>10,9</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+       <td>10,9</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+       <td>10,9</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+       <td>10,9</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+       <td>10,9</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+       <td>10,9</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+       <td>10,9</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+       <td>10,9</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+       <td>10,9</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+       <td>10,9</td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]</td>
+       <td>Friday 12/31/99</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[3]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]</td>
+       <td>Sunday 1/30/00</td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[7]/domChild[0]</td>
+       <td>Saturday 2/5/00</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+       <td>26</td>
+</tr>
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/components/calendar/eventSizingNoOverlap.html b/uitest/src/com/vaadin/tests/components/calendar/eventSizingNoOverlap.html
new file mode 100644 (file)
index 0000000..dcf6c1e
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="" />
+<title>eventSizingNoOverlap</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">eventSizingNoOverlap</td></tr>
+</thead><tbody>
+<tr>
+       <td>open</td>
+       <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&amp;firstDay=1&amp;lastDay=5&amp;firstHour=8&amp;lastHour=16&amp;restartApplication</td>
+       <td></td>
+</tr>
+<!-- Go to week view -->
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[1]/domChild[2]/domChild[0]/domChild[0]</td>
+       <td>4,57</td>
+</tr>
+<!-- Open add-event popup, enter event between 12:45-13:15 -->
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[0]/Slot[6]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>1/9/00 12:45 PM</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/9/00 13:15 PM</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>12:45-13:15</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<!-- Open add-event popup, enter event between 13:15-13:25 -->
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[0]/Slot[6]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>1/9/00 13:15 PM</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/9/00 13:25 PM</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>13:15-13:25</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<!-- Open add-event popup, enter event between 13:25-13:55 -->
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[0]/Slot[6]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>1/9/00 13:25 PM</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/9/00 13:55 PM</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>13:25-13:55</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td></td>
+</tr>
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/components/calendar/midnightEventsTest.html b/uitest/src/com/vaadin/tests/components/calendar/midnightEventsTest.html
new file mode 100644 (file)
index 0000000..8991e19
--- /dev/null
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="" />
+<title>midnightEventsTest</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">midnightEventsTest</td></tr>
+</thead><tbody>
+<tr>
+       <td>open</td>
+       <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&amp;width=1000px&amp;height=600px&amp;secondsResolution&amp;restartApplication</td>
+       <td></td>
+</tr>
+<!--Add event from 0:00 to 0:00-->
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[1]/domChild[1]/domChild[0]/domChild[2]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>1/3/00 12:00:00 AM</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/4/00 12:00:00 AM</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Midnight to midnight</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<!--Add event from 00:00 to 00:00 on the same day-->
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[1]/domChild[1]/domChild[0]/domChild[2]</td>
+       <td>84,10</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/7/00 12:00:00 AM</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Zero-length midnight event</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td></td>
+</tr>
+<!--Go to weekly view-->
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[0]</td>
+       <td>10,48</td>
+</tr>
+<!--Assert zero-length event exists-->
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[2]/domChild[0]/domChild[48]/domChild[0]</td>
+       <td>50,4</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Zero-length midnight event</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<!--Assert that the the all day event does not overflow to the next day by checking that the first time-cell is empty-->
+<tr>
+       <td>dragAndDrop</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]/domChild[5]</td>
+       <td>+0,+5</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[1]/VHorizontalLayout[0]/Slot[1]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td></td>
+</tr>
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/components/calendar/monthlyViewNewEvent.html b/uitest/src/com/vaadin/tests/components/calendar/monthlyViewNewEvent.html
new file mode 100644 (file)
index 0000000..760beef
--- /dev/null
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="" />
+<title>monthlyViewNewEvent</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">monthlyViewNewEvent</td></tr>
+</thead><tbody>
+<tr>
+       <td>open</td>
+       <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&amp;width=1000px&amp;height=600px&amp;restartApplication</td>
+       <td></td>
+</tr>
+<!--Create new event with the button-->
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[0]/Slot[6]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VCheckBox[0]/domChild[0]</td>
+       <td>10,5</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>52,8</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>12/26/99</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/1/00</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Test event</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td>
+       <td>Test description</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<!--Open previously created event, assert values-->
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[1]</td>
+       <td>91,9</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>12/26/99</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/1/00</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VCheckBox[0]/domChild[0]</td>
+       <td>on</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Test event</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td>
+       <td>Test description</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td>
+       <td>Green</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[1]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<!--Open previously created event, assert values and shorten the event-->
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[1]</td>
+       <td>41,8</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>12/26/99</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/1/00</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VCheckBox[0]/domChild[0]</td>
+       <td>on</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Test event</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td>
+       <td>Test description</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td>
+       <td>Green</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>12/27/99</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>12/31/99</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<!--Click the calendar where the event previously was, assert new event creation-->
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[3]</td>
+       <td>107,8</td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/components/calendar/monthlyViewNewEvents.html b/uitest/src/com/vaadin/tests/components/calendar/monthlyViewNewEvents.html
new file mode 100644 (file)
index 0000000..68690f8
--- /dev/null
@@ -0,0 +1,410 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="" />
+<title>monthlyViewNewEvents</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">monthlyViewNewEvents</td></tr>
+</thead><tbody>
+<tr>
+       <td>open</td>
+       <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&amp;width=1000px&amp;height=600px&amp;restartApplication</td>
+       <td></td>
+</tr>
+<!--Create new event by dragging, make it all-day-->
+<tr>
+       <td>drag</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[2]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>drop</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[5]/domChild[0]/domChild[2]</td>
+       <td>98,83</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VCheckBox[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>12/27/99</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>12/31/99</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VCheckBox[0]/domChild[0]</td>
+       <td>on</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Test event</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td>
+       <td>Description</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[1]/VHorizontalLayout[0]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<!--Create new event by dragging-->
+<tr>
+       <td>drag</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[1]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>drop</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[3]/domChild[0]</td>
+       <td>38,8</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VCheckBox[0]/domChild[0]</td>
+       <td>7,9</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Second test event</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td>
+       <td>Desc</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[1]/VHorizontalLayout[0]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<!--Create new event by dragging, make it blue-->
+<tr>
+       <td>drag</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[4]/domChild[0]/domChild[4]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>drop</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[6]/domChild[0]/domChild[3]</td>
+       <td>125,80</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VCheckBox[0]/domChild[0]</td>
+       <td>10,8</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Third test event</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td>
+       <td>Testing</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]#button</td>
+       <td>6,9</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::Root/VFilterSelect$SuggestionPopup[0]/VFilterSelect$SuggestionMenu[0]#item2</td>
+       <td>49,10</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[1]/VHorizontalLayout[0]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[3]/domChild[0]/domChild[4]</td>
+       <td>45,85</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VCheckBox[0]/domChild[0]</td>
+       <td>5,9</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Fourth test event</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td>
+       <td>Fourth event</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]#button</td>
+       <td>13,18</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::Root/VFilterSelect$SuggestionPopup[0]/VFilterSelect$SuggestionMenu[0]#item3</td>
+       <td>57,9</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[1]/VHorizontalLayout[0]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<!--Start asserting the previously entered events-->
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[1]</td>
+       <td>12,7</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>12/27/99</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>12/31/99</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VCheckBox[0]/domChild[0]</td>
+       <td>on</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Test event</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td>
+       <td>Description</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td>
+       <td>Green</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[1]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[1]</td>
+       <td>72,6</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>12/27/99</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>12/31/99</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[1]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[2]</td>
+       <td>44,10</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>12/26/99</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>12/29/99</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Second test event</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td>
+       <td>Desc</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td>
+       <td>Green</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[1]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[2]</td>
+       <td>79,5</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>12/26/99</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>12/29/99</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Second test event</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td>
+       <td>Desc</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td>
+       <td>Green</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[1]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[4]/domChild[0]/domChild[2]</td>
+       <td>47,9</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>12/30/99</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/1/00</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Third test event</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td>
+       <td>Testing</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td>
+       <td>Blue</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[1]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[4]/domChild[0]/domChild[2]</td>
+       <td>72,9</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>12/30/99</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/1/00</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Third test event</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td>
+       <td>Testing</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td>
+       <td>Blue</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[1]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[3]/domChild[0]/domChild[3]</td>
+       <td>37,9</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>12/29/99</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>12/29/99</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Fourth test event</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td>
+       <td>Fourth event</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td>
+       <td>Red</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td>
+       <td></td>
+</tr>
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/components/calendar/notifications.html b/uitest/src/com/vaadin/tests/components/calendar/notifications.html
new file mode 100644 (file)
index 0000000..15bf29a
--- /dev/null
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="http://localhost:8080/" />
+<title>New Test</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">New Test</td></tr>
+</thead><tbody>
+<tr>
+       <td>open</td>
+       <td>/run/com.vaadin.tests.components.calendar.NotificationTestUI?restartApplication</td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarNotificationTestUI::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[2]/domChild[0]/domChild[0]</td>
+       <td>83,11</td>
+</tr>
+<tr>
+       <td>mouseMove</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarNotificationTestUI::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[1]/domChild[3]/domChild[0]/domChild[3]</td>
+       <td>10,10</td>
+</tr>
+<tr>
+       <td>closeNotification</td>
+       <td>//div[@id='runcomvaadintestscomponentscalendarNotificationTestUI-1842310749-overlays']/div</td>
+       <td>0,0</td>
+</tr>
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td>no-notification</td>
+</tr>
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/components/calendar/sizeTest1000x600px.html b/uitest/src/com/vaadin/tests/components/calendar/sizeTest1000x600px.html
new file mode 100644 (file)
index 0000000..66d2c2f
--- /dev/null
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="" />
+<title>sizeTest1000x600px</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">sizeTest1000x600px</td></tr>
+</thead><tbody>
+<tr>
+       <td>open</td>
+       <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&amp;restartApplication&amp;width=1000px&amp;height=600px</td>
+       <td></td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td>
+       <td>Jan 2000</td>
+</tr>
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[1]/domChild[2]/domChild[0]/domChild[0]</td>
+       <td>7,53</td>
+</tr>
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td></td>
+</tr>
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/components/calendar/sizeTest100percentXundefined.html b/uitest/src/com/vaadin/tests/components/calendar/sizeTest100percentXundefined.html
new file mode 100644 (file)
index 0000000..7c963f5
--- /dev/null
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="" />
+<title>sizeTest100percentXundefined</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">sizeTest100percentXundefined</td></tr>
+</thead><tbody>
+<tr>
+       <td>open</td>
+       <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&amp;restartApplication&amp;width=100%25</td>
+       <td></td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td>
+       <td>Jan 2000</td>
+</tr>
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[1]/domChild[2]/domChild[0]/domChild[0]</td>
+       <td>10,53</td>
+</tr>
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td></td>
+</tr>
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/components/calendar/sizeTest100x100percent.html b/uitest/src/com/vaadin/tests/components/calendar/sizeTest100x100percent.html
new file mode 100644 (file)
index 0000000..19f03db
--- /dev/null
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="" />
+<title>sizeTest100x100percent</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">sizeTest100x100percent</td></tr>
+</thead><tbody>
+<tr>
+       <td>open</td>
+       <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&amp;restartApplication&amp;width=100%25&amp;height=100%25</td>
+       <td></td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td>
+       <td>Jan 2000</td>
+</tr>
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[1]/domChild[2]/domChild[0]/domChild[0]</td>
+       <td>12,54</td>
+</tr>
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td></td>
+</tr>
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/components/calendar/sizeTest300pxXundefined.html b/uitest/src/com/vaadin/tests/components/calendar/sizeTest300pxXundefined.html
new file mode 100644 (file)
index 0000000..70f6dbd
--- /dev/null
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="" />
+<title>sizeTest300pxXundefined</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">sizeTest300pxXundefined</td></tr>
+</thead><tbody>
+<tr>
+       <td>open</td>
+       <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&amp;restartApplication&amp;width=300px</td>
+       <td></td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td>
+       <td>Jan 2000</td>
+</tr>
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[1]/domChild[2]/domChild[0]/domChild[0]</td>
+       <td>11,52</td>
+</tr>
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td></td>
+</tr>
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/components/calendar/sizeTestSizeFull.html b/uitest/src/com/vaadin/tests/components/calendar/sizeTestSizeFull.html
new file mode 100644 (file)
index 0000000..59d3922
--- /dev/null
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="" />
+<title>sizeTestSizeFull</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">sizeTestSizeFull</td></tr>
+</thead><tbody>
+<tr>
+       <td>open</td>
+       <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&amp;restartApplication</td>
+       <td></td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td>
+       <td>Jan 2000</td>
+</tr>
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[1]/domChild[2]/domChild[0]/domChild[0]</td>
+       <td>9,56</td>
+</tr>
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td></td>
+</tr>
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/components/calendar/sizeTestUndefinedX100percent.html b/uitest/src/com/vaadin/tests/components/calendar/sizeTestUndefinedX100percent.html
new file mode 100644 (file)
index 0000000..d489179
--- /dev/null
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="" />
+<title>sizeTestUndefinedX100percent</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">sizeTestUndefinedX100percent</td></tr>
+</thead><tbody>
+<tr>
+       <td>open</td>
+       <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&amp;restartApplication&amp;height=100%25</td>
+       <td></td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td>
+       <td>Jan 2000</td>
+</tr>
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[1]/domChild[2]/domChild[0]/domChild[0]</td>
+       <td>9,53</td>
+</tr>
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td></td>
+</tr>
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/components/calendar/sizeTestUndefinedX300px.html b/uitest/src/com/vaadin/tests/components/calendar/sizeTestUndefinedX300px.html
new file mode 100644 (file)
index 0000000..2dfce00
--- /dev/null
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="" />
+<title>sizeTestUndefinedX300px</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">sizeTestUndefinedX300px</td></tr>
+</thead><tbody>
+<tr>
+       <td>open</td>
+       <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&amp;restartApplication&amp;height=300px</td>
+       <td></td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td>
+       <td>Jan 2000</td>
+</tr>
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[1]/domChild[2]/domChild[0]/domChild[0]</td>
+       <td>9,26</td>
+</tr>
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td></td>
+</tr>
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/components/calendar/sizeTestUndefinedXUndefined.html b/uitest/src/com/vaadin/tests/components/calendar/sizeTestUndefinedXUndefined.html
new file mode 100644 (file)
index 0000000..3bfae6b
--- /dev/null
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="" />
+<title>sizeTestUndefinedXUndefined</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">sizeTestUndefinedXUndefined</td></tr>
+</thead><tbody>
+<tr>
+       <td>open</td>
+       <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&amp;restartApplication&amp;width=&amp;height=</td>
+       <td></td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td>
+       <td>Jan 2000</td>
+</tr>
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[1]/domChild[2]/domChild[0]/domChild[0]</td>
+       <td>8,52</td>
+</tr>
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td></td>
+</tr>
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/components/calendar/visibleHours24H.html b/uitest/src/com/vaadin/tests/components/calendar/visibleHours24H.html
new file mode 100644 (file)
index 0000000..a0c6798
--- /dev/null
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="" />
+<title>visibleHours24H</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">visibleHours24H</td></tr>
+</thead><tbody>
+<tr>
+       <td>open</td>
+       <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&amp;firstDay=1&amp;lastDay=5&amp;firstHour=8&amp;lastHour=16&amp;restartApplication</td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[1]/domChild[1]/domChild[0]/domChild[0]</td>
+       <td>9,55</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>//div[@id='gwt-uid-9']/div</td>
+       <td>7,14</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>//div[@id='VAADIN_COMBOBOX_OPTIONLIST']/div/div[2]/table/tbody/tr[4]/td</td>
+       <td>120,15</td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[1]</td>
+       <td>9:00</td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[8]</td>
+       <td>16:00</td>
+</tr>
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/components/calendar/weeklyViewNewEvents.html b/uitest/src/com/vaadin/tests/components/calendar/weeklyViewNewEvents.html
new file mode 100644 (file)
index 0000000..b587288
--- /dev/null
@@ -0,0 +1,657 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="" />
+<title>weeklyViewNewEvents</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">weeklyViewNewEvents</td></tr>
+</thead><tbody>
+<tr>
+       <td>open</td>
+       <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&amp;width=1000px&amp;height=600px&amp;restartApplication</td>
+       <td></td>
+</tr>
+<!--Go to weekly view-->
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[1]/domChild[2]/domChild[0]/domChild[0]</td>
+       <td>3,49</td>
+</tr>
+<!--Assert the default event contents-->
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[2]/domChild[0]/domChild[48]/domChild[0]/domChild[0]</td>
+       <td>26,5</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>1/10/00 09:30 AM</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/10/00 02:00 PM</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VCheckBox[0]/domChild[0]</td>
+       <td>off</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Appointment</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[1]</td>
+       <td>Office</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td>
+       <td>A longer description, which should display correctly.</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td>
+       <td>Green</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[48]/domChild[0]/domChild[0]</td>
+       <td>21,12</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>1/11/00 11:00 AM</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/11/00 07:00 PM</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Training</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td>
+       <td>Blue</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[7]/domChild[0]/domChild[48]/domChild[0]/domChild[0]</td>
+       <td>19,9</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>1/15/00 09:00 AM</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/15/00 06:00 PM</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Free time</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[6]/domChild[0]/domChild[0]</td>
+       <td>22,6</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>1/9/00</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/15/00</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VCheckBox[0]/domChild[0]</td>
+       <td>on</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Whole week event</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td>
+       <td>Description for the whole week event.</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td>
+       <td>Orange</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<!--Assert the all-day events-->
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[1]</td>
+       <td>78,3</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>1/12/00</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/12/00</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Allday event</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td>
+       <td>Some description.</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td>
+       <td>Red</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[4]/domChild[0]/domChild[1]</td>
+       <td>57,7</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>1/13/00</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/13/00</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Second allday event</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td>
+       <td>Some description.</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td>
+       <td>Blue</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<!--Enter new event-->
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[0]/Slot[6]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>1/13/00 9:00 AM</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/13/00 2:00 PM</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Test event</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td>
+       <td>Test event description</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[1]</td>
+       <td>3,15</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::Root/VFilterSelect$SuggestionPopup[0]/VFilterSelect$SuggestionMenu[0]#item4</td>
+       <td>36,6</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<!--Assert previously created event-->
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[5]/domChild[0]/domChild[48]/domChild[0]</td>
+       <td>47,21</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>1/13/00 09:00 AM</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/13/00 02:00 PM</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Test event</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td>
+       <td>Test event description</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td>
+       <td>Orange</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td>
+       <td>8,9</td>
+</tr>
+<!--Edit previously created events and change properties-->
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[5]/domChild[0]/domChild[48]/domChild[0]</td>
+       <td>33,16</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#popupButton</td>
+       <td>10,11</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::Root/VOverlay[0]/VCalendarPanel[0]#day11</td>
+       <td>16,11</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#popupButton</td>
+       <td>14,14</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::Root/VOverlay[0]/VCalendarPanel[0]#day11</td>
+       <td>14,10</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[1]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<!--Assert the edited values-->
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[49]/domChild[0]</td>
+       <td>34,21</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>1/11/00 09:00 AM</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/11/00 02:00 PM</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<!--Create new event-->
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[0]/Slot[6]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>1/11/00 10:00 AM</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/11/00 8:00 PM</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Test event 2</td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td>
+       <td>Second test event</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<!--Assert previously created event still exists in the right place (as multiple events occupy the same time)-->
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[50]/domChild[0]</td>
+       <td>7,73</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>1/11/00 09:00 AM</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/11/00 02:00 PM</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<!--Assert previously created event still exists in the right place (as multiple events occupy the same time)-->
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[49]/domChild[0]/domChild[0]</td>
+       <td>12,32</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>1/11/00 11:00 AM</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/11/00 07:00 PM</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td>
+       <td>11,8</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[48]/domChild[1]</td>
+       <td>4,9</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>1/11/00 10:00 AM</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/11/00 08:00 PM</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[50]/domChild[0]</td>
+       <td>14,71</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>1/11/00 09:00 AM</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/11/00 02:00 PM</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Test event</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[48]/domChild[0]</td>
+       <td>16,111</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>1/11/00 10:00 AM</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/11/00 08:00 PM</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Test event 2</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[48]/domChild[0]</td>
+       <td>14,209</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>1/11/00 10:00 AM</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/11/00 08:00 PM</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Test event 2</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[49]/domChild[0]/domChild[0]</td>
+       <td>20,113</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>1/11/00 11:00 AM</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/11/00 07:00 PM</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Training</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td>
+       <td>7,8</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[50]/domChild[0]</td>
+       <td>21,87</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td>
+       <td>1/11/00 09:00 AM</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td>
+       <td>1/11/00 02:00 PM</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Test event</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td>
+       <td>12,10</td>
+</tr>
+<!--Go to monthly view and assert inserted events-->
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[2]/domChild[2]/domChild[0]/domChild[4]</td>
+       <td>36,10</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Test event</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[2]/domChild[2]/domChild[0]/domChild[3]</td>
+       <td>53,6</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Training</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[2]/domChild[2]/domChild[0]/domChild[2]/domChild[0]</td>
+       <td>48,7</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Test event 2</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[2]/domChild[4]/domChild[0]/domChild[1]</td>
+       <td>50,6</td>
+</tr>
+<tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+       <td>Whole week event</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+</tbody></table>
+</body>
+</html>