aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Ahlroos <john@vaadin.com>2013-03-27 16:33:28 +0200
committerVaadin Code Review <review@vaadin.com>2013-04-03 08:03:37 +0000
commit217ba18e53a8607a9e2480574ec1c3da11f4037f (patch)
tree2c38b306985b77144e0797e8bc83d386c8575aa3
parent1d25d6d6427f94e93e3bf7417aa968aaa9673dab (diff)
downloadvaadin-framework-217ba18e53a8607a9e2480574ec1c3da11f4037f.tar.gz
vaadin-framework-217ba18e53a8607a9e2480574ec1c3da11f4037f.zip
Integrate Calendar into core #11079
Everything else integrated, except TB3 tests (ticket #11090, old TB2 tests used instead) Change-Id: If1700d7680a6c0a45f84d6e3c7b80e6536da78c8
-rw-r--r--WebContent/VAADIN/themes/base/base.scss2
-rw-r--r--WebContent/VAADIN/themes/base/calendar/calendar.scss378
-rw-r--r--WebContent/VAADIN/themes/base/calendar/img/arrows.pngbin0 -> 248 bytes
-rw-r--r--WebContent/VAADIN/themes/tests-calendar/styles.css96
-rw-r--r--client/src/com/vaadin/client/ui/VCalendar.java1444
-rw-r--r--client/src/com/vaadin/client/ui/calendar/CalendarConnector.java637
-rw-r--r--client/src/com/vaadin/client/ui/calendar/VCalendarAction.java138
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/CalendarDay.java55
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/CalendarEvent.java313
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/DateCell.java808
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/DateCellContainer.java114
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java659
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/DateCellGroup.java60
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/DateUtil.java70
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/DayToolbar.java179
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/FocusableComplexPanel.java117
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/FocusableGrid.java129
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/FocusableHTML.java119
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/HasTooltipKey.java33
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/MonthEventLabel.java142
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/MonthGrid.java215
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/SimpleDayCell.java696
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/SimpleDayToolbar.java97
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/SimpleWeekToolbar.java108
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/WeekGrid.java677
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/WeekGridMinuteTimeRange.java61
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/WeekLabel.java51
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/WeeklyLongEvents.java184
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/WeeklyLongEventsDateCell.java67
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarDropHandler.java64
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarMonthDropHandler.java166
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarWeekDropHandler.java181
-rw-r--r--server/src/com/vaadin/ui/Calendar.java1822
-rw-r--r--server/src/com/vaadin/ui/components/calendar/CalendarComponentEvent.java51
-rw-r--r--server/src/com/vaadin/ui/components/calendar/CalendarComponentEvents.java603
-rw-r--r--server/src/com/vaadin/ui/components/calendar/CalendarDateRange.java86
-rw-r--r--server/src/com/vaadin/ui/components/calendar/CalendarTargetDetails.java80
-rw-r--r--server/src/com/vaadin/ui/components/calendar/ContainerEventProvider.java566
-rw-r--r--server/src/com/vaadin/ui/components/calendar/event/BasicEvent.java251
-rw-r--r--server/src/com/vaadin/ui/components/calendar/event/BasicEventProvider.java173
-rw-r--r--server/src/com/vaadin/ui/components/calendar/event/CalendarEditableEventProvider.java40
-rw-r--r--server/src/com/vaadin/ui/components/calendar/event/CalendarEvent.java146
-rw-r--r--server/src/com/vaadin/ui/components/calendar/event/CalendarEventProvider.java112
-rw-r--r--server/src/com/vaadin/ui/components/calendar/event/EditableCalendarEvent.java91
-rw-r--r--server/src/com/vaadin/ui/components/calendar/handler/BasicBackwardHandler.java78
-rw-r--r--server/src/com/vaadin/ui/components/calendar/handler/BasicDateClickHandler.java69
-rw-r--r--server/src/com/vaadin/ui/components/calendar/handler/BasicEventMoveHandler.java73
-rw-r--r--server/src/com/vaadin/ui/components/calendar/handler/BasicEventResizeHandler.java69
-rw-r--r--server/src/com/vaadin/ui/components/calendar/handler/BasicForwardHandler.java76
-rw-r--r--server/src/com/vaadin/ui/components/calendar/handler/BasicWeekClickHandler.java81
-rw-r--r--server/tests/src/com/vaadin/tests/server/component/calendar/CalendarBasics.java210
-rw-r--r--server/tests/src/com/vaadin/tests/server/component/calendar/ContainerDataSource.java362
-rw-r--r--shared/src/com/vaadin/shared/ui/calendar/CalendarClientRpc.java28
-rw-r--r--shared/src/com/vaadin/shared/ui/calendar/CalendarEventId.java36
-rw-r--r--shared/src/com/vaadin/shared/ui/calendar/CalendarServerRpc.java49
-rw-r--r--shared/src/com/vaadin/shared/ui/calendar/CalendarState.java69
-rw-r--r--shared/src/com/vaadin/shared/ui/calendar/DateConstants.java33
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/BeanItemContainerTestUI.java181
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/CalendarActionsUI.java107
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/CalendarTest.java1227
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/CalendarTestEvent.java48
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/HiddenFwdBackButtons.java61
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/NotificationTestUI.java101
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/actions.html51
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/basicNavigation.html236
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/eventSizingNoOverlap.html110
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/midnightEventsTest.html116
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/monthlyViewNewEvent.html170
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/monthlyViewNewEvents.html410
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/notifications.html41
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/sizeTest1000x600px.html41
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/sizeTest100percentXundefined.html41
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/sizeTest100x100percent.html41
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/sizeTest300pxXundefined.html41
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/sizeTestSizeFull.html41
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/sizeTestUndefinedX100percent.html41
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/sizeTestUndefinedX300px.html41
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/sizeTestUndefinedXUndefined.html41
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/visibleHours24H.html46
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/weeklyViewNewEvents.html657
80 files changed, 17204 insertions, 0 deletions
diff --git a/WebContent/VAADIN/themes/base/base.scss b/WebContent/VAADIN/themes/base/base.scss
index 87754b6777..83e463fa00 100644
--- a/WebContent/VAADIN/themes/base/base.scss
+++ b/WebContent/VAADIN/themes/base/base.scss
@@ -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
index 0000000000..8ff97df0f9
--- /dev/null
+++ b/WebContent/VAADIN/themes/base/calendar/calendar.scss
@@ -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
index 0000000000..9905c0b065
--- /dev/null
+++ b/WebContent/VAADIN/themes/base/calendar/img/arrows.png
Binary files differ
diff --git a/WebContent/VAADIN/themes/tests-calendar/styles.css b/WebContent/VAADIN/themes/tests-calendar/styles.css
new file mode 100644
index 0000000000..7a37fcfdaf
--- /dev/null
+++ b/WebContent/VAADIN/themes/tests-calendar/styles.css
@@ -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
index 0000000000..e66a2d7552
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/VCalendar.java
@@ -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
index 0000000000..120a65d842
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/CalendarConnector.java
@@ -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
index 0000000000..2a529354e5
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/VCalendarAction.java
@@ -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
index 0000000000..ca176c08c1
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/CalendarDay.java
@@ -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
index 0000000000..e2c06d41ea
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/CalendarEvent.java
@@ -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
index 0000000000..05e2a808fe
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/DateCell.java
@@ -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
index 0000000000..f1b45c83c5
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/DateCellContainer.java
@@ -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
index 0000000000..039a00e25a
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java
@@ -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
index 0000000000..d2add53389
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/DateCellGroup.java
@@ -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
index 0000000000..84726327e2
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/DateUtil.java
@@ -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
index 0000000000..bb0155d892
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/DayToolbar.java
@@ -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
index 0000000000..62332385d2
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/FocusableComplexPanel.java
@@ -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
index 0000000000..d3177362bf
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/FocusableGrid.java
@@ -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
index 0000000000..c3fe1958f0
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/FocusableHTML.java
@@ -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
index 0000000000..5827068840
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/HasTooltipKey.java
@@ -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
index 0000000000..b7f6ee7a3c
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/MonthEventLabel.java
@@ -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
index 0000000000..f5afd12e42
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/MonthGrid.java
@@ -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
index 0000000000..8d1ca0fcda
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/SimpleDayCell.java
@@ -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
index 0000000000..fc75136b93
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/SimpleDayToolbar.java
@@ -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
index 0000000000..f86ba03053
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/SimpleWeekToolbar.java
@@ -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
index 0000000000..c5646f97ae
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/WeekGrid.java
@@ -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
index 0000000000..27ace91c4e
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/WeekGridMinuteTimeRange.java
@@ -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
index 0000000000..bde8675435
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/WeekLabel.java
@@ -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
index 0000000000..e3b7d5d7fe
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/WeeklyLongEvents.java
@@ -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
index 0000000000..a97d352e81
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/WeeklyLongEventsDateCell.java
@@ -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
index 0000000000..03db4d091e
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarDropHandler.java
@@ -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
index 0000000000..6e57fb6fef
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarMonthDropHandler.java
@@ -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
index 0000000000..fa7aaa428b
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarWeekDropHandler.java
@@ -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
index 0000000000..4bf2885a8c
--- /dev/null
+++ b/server/src/com/vaadin/ui/Calendar.java
@@ -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
index 0000000000..1f012157b5
--- /dev/null
+++ b/server/src/com/vaadin/ui/components/calendar/CalendarComponentEvent.java
@@ -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
index 0000000000..1904d69898
--- /dev/null
+++ b/server/src/com/vaadin/ui/components/calendar/CalendarComponentEvents.java
@@ -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
index 0000000000..01b766a6db
--- /dev/null
+++ b/server/src/com/vaadin/ui/components/calendar/CalendarDateRange.java
@@ -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
index 0000000000..1a3ef67377
--- /dev/null
+++ b/server/src/com/vaadin/ui/components/calendar/CalendarTargetDetails.java
@@ -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
index 0000000000..b01140eb88
--- /dev/null
+++ b/server/src/com/vaadin/ui/components/calendar/ContainerEventProvider.java
@@ -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
index 0000000000..ab342dfabf
--- /dev/null
+++ b/server/src/com/vaadin/ui/components/calendar/event/BasicEvent.java
@@ -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
index 0000000000..0314652245
--- /dev/null
+++ b/server/src/com/vaadin/ui/components/calendar/event/BasicEventProvider.java
@@ -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
index 0000000000..145d2b4aa4
--- /dev/null
+++ b/server/src/com/vaadin/ui/components/calendar/event/CalendarEditableEventProvider.java
@@ -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
index 0000000000..531ee72c7f
--- /dev/null
+++ b/server/src/com/vaadin/ui/components/calendar/event/CalendarEvent.java
@@ -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
index 0000000000..fefb2ca9b6
--- /dev/null
+++ b/server/src/com/vaadin/ui/components/calendar/event/CalendarEventProvider.java
@@ -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
index 0000000000..e8a27ad50f
--- /dev/null
+++ b/server/src/com/vaadin/ui/components/calendar/event/EditableCalendarEvent.java
@@ -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
index 0000000000..fc2bfd6df4
--- /dev/null
+++ b/server/src/com/vaadin/ui/components/calendar/handler/BasicBackwardHandler.java
@@ -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
index 0000000000..c91a238b86
--- /dev/null
+++ b/server/src/com/vaadin/ui/components/calendar/handler/BasicDateClickHandler.java
@@ -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
index 0000000000..139837f339
--- /dev/null
+++ b/server/src/com/vaadin/ui/components/calendar/handler/BasicEventMoveHandler.java
@@ -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
index 0000000000..c052d0d77b
--- /dev/null
+++ b/server/src/com/vaadin/ui/components/calendar/handler/BasicEventResizeHandler.java
@@ -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
index 0000000000..a5307ffd5c
--- /dev/null
+++ b/server/src/com/vaadin/ui/components/calendar/handler/BasicForwardHandler.java
@@ -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
index 0000000000..49efe49e48
--- /dev/null
+++ b/server/src/com/vaadin/ui/components/calendar/handler/BasicWeekClickHandler.java
@@ -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
index 0000000000..5926cfa1ca
--- /dev/null
+++ b/server/tests/src/com/vaadin/tests/server/component/calendar/CalendarBasics.java
@@ -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
index 0000000000..2bc95e371c
--- /dev/null
+++ b/server/tests/src/com/vaadin/tests/server/component/calendar/ContainerDataSource.java
@@ -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
index 0000000000..c1ff8bdda5
--- /dev/null
+++ b/shared/src/com/vaadin/shared/ui/calendar/CalendarClientRpc.java
@@ -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
index 0000000000..6f52aabf43
--- /dev/null
+++ b/shared/src/com/vaadin/shared/ui/calendar/CalendarEventId.java
@@ -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
index 0000000000..5257310cbf
--- /dev/null
+++ b/shared/src/com/vaadin/shared/ui/calendar/CalendarServerRpc.java
@@ -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
index 0000000000..fab5fd828e
--- /dev/null
+++ b/shared/src/com/vaadin/shared/ui/calendar/CalendarState.java
@@ -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
index 0000000000..8a840274c2
--- /dev/null
+++ b/shared/src/com/vaadin/shared/ui/calendar/DateConstants.java
@@ -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
index 0000000000..4e0b963534
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/BeanItemContainerTestUI.java
@@ -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
index 0000000000..f5c2d9da7e
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarActionsUI.java
@@ -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
index 0000000000..530e47f1e0
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarTest.java
@@ -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
index 0000000000..29b8f62403
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarTestEvent.java
@@ -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
index 0000000000..8b789098e6
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/HiddenFwdBackButtons.java
@@ -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
index 0000000000..0bd327da18
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/NotificationTestUI.java
@@ -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
index 0000000000..bec3f2aded
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/actions.html
@@ -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
index 0000000000..cccb88bfb2
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/basicNavigation.html
@@ -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
index 0000000000..dcf6c1ec53
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/eventSizingNoOverlap.html
@@ -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
index 0000000000..8991e1983d
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/midnightEventsTest.html
@@ -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
index 0000000000..760beef6c0
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/monthlyViewNewEvent.html
@@ -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
index 0000000000..68690f81b4
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/monthlyViewNewEvents.html
@@ -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
index 0000000000..15bf29a0fd
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/notifications.html
@@ -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
index 0000000000..66d2c2f126
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/sizeTest1000x600px.html
@@ -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
index 0000000000..7c963f547f
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/sizeTest100percentXundefined.html
@@ -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
index 0000000000..19f03db784
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/sizeTest100x100percent.html
@@ -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
index 0000000000..70f6dbd75b
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/sizeTest300pxXundefined.html
@@ -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
index 0000000000..59d39226d9
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/sizeTestSizeFull.html
@@ -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
index 0000000000..d48917904c
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/sizeTestUndefinedX100percent.html
@@ -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
index 0000000000..2dfce00635
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/sizeTestUndefinedX300px.html
@@ -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
index 0000000000..3bfae6be35
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/sizeTestUndefinedXUndefined.html
@@ -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
index 0000000000..a0c6798edb
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/visibleHours24H.html
@@ -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
index 0000000000..b587288a24
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/calendar/weeklyViewNewEvents.html
@@ -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>