From 217ba18e53a8607a9e2480574ec1c3da11f4037f Mon Sep 17 00:00:00 2001
From: John Ahlroos
Date: Wed, 27 Mar 2013 16:33:28 +0200
Subject: Integrate Calendar into core #11079
Everything else integrated, except TB3 tests (ticket #11090, old TB2 tests used instead)
Change-Id: If1700d7680a6c0a45f84d6e3c7b80e6536da78c8
---
WebContent/VAADIN/themes/base/base.scss | 2 +
.../VAADIN/themes/base/calendar/calendar.scss | 378 ++++
.../VAADIN/themes/base/calendar/img/arrows.png | Bin 0 -> 248 bytes
WebContent/VAADIN/themes/tests-calendar/styles.css | 96 ++
client/src/com/vaadin/client/ui/VCalendar.java | 1444 ++++++++++++++++
.../client/ui/calendar/CalendarConnector.java | 637 +++++++
.../vaadin/client/ui/calendar/VCalendarAction.java | 138 ++
.../client/ui/calendar/schedule/CalendarDay.java | 55 +
.../client/ui/calendar/schedule/CalendarEvent.java | 313 ++++
.../client/ui/calendar/schedule/DateCell.java | 808 +++++++++
.../ui/calendar/schedule/DateCellContainer.java | 114 ++
.../ui/calendar/schedule/DateCellDayEvent.java | 659 +++++++
.../client/ui/calendar/schedule/DateCellGroup.java | 60 +
.../client/ui/calendar/schedule/DateUtil.java | 70 +
.../client/ui/calendar/schedule/DayToolbar.java | 179 ++
.../calendar/schedule/FocusableComplexPanel.java | 117 ++
.../client/ui/calendar/schedule/FocusableGrid.java | 129 ++
.../client/ui/calendar/schedule/FocusableHTML.java | 119 ++
.../client/ui/calendar/schedule/HasTooltipKey.java | 33 +
.../ui/calendar/schedule/MonthEventLabel.java | 142 ++
.../client/ui/calendar/schedule/MonthGrid.java | 215 +++
.../client/ui/calendar/schedule/SimpleDayCell.java | 696 ++++++++
.../ui/calendar/schedule/SimpleDayToolbar.java | 97 ++
.../ui/calendar/schedule/SimpleWeekToolbar.java | 108 ++
.../client/ui/calendar/schedule/WeekGrid.java | 677 ++++++++
.../calendar/schedule/WeekGridMinuteTimeRange.java | 61 +
.../client/ui/calendar/schedule/WeekLabel.java | 51 +
.../ui/calendar/schedule/WeeklyLongEvents.java | 184 ++
.../schedule/WeeklyLongEventsDateCell.java | 67 +
.../calendar/schedule/dd/CalendarDropHandler.java | 64 +
.../schedule/dd/CalendarMonthDropHandler.java | 166 ++
.../schedule/dd/CalendarWeekDropHandler.java | 181 ++
server/src/com/vaadin/ui/Calendar.java | 1822 ++++++++++++++++++++
.../calendar/CalendarComponentEvent.java | 51 +
.../calendar/CalendarComponentEvents.java | 603 +++++++
.../ui/components/calendar/CalendarDateRange.java | 86 +
.../components/calendar/CalendarTargetDetails.java | 80 +
.../calendar/ContainerEventProvider.java | 566 ++++++
.../ui/components/calendar/event/BasicEvent.java | 251 +++
.../calendar/event/BasicEventProvider.java | 173 ++
.../event/CalendarEditableEventProvider.java | 40 +
.../components/calendar/event/CalendarEvent.java | 146 ++
.../calendar/event/CalendarEventProvider.java | 112 ++
.../calendar/event/EditableCalendarEvent.java | 91 +
.../calendar/handler/BasicBackwardHandler.java | 78 +
.../calendar/handler/BasicDateClickHandler.java | 69 +
.../calendar/handler/BasicEventMoveHandler.java | 73 +
.../calendar/handler/BasicEventResizeHandler.java | 69 +
.../calendar/handler/BasicForwardHandler.java | 76 +
.../calendar/handler/BasicWeekClickHandler.java | 81 +
.../server/component/calendar/CalendarBasics.java | 210 +++
.../component/calendar/ContainerDataSource.java | 362 ++++
.../shared/ui/calendar/CalendarClientRpc.java | 28 +
.../vaadin/shared/ui/calendar/CalendarEventId.java | 36 +
.../shared/ui/calendar/CalendarServerRpc.java | 49 +
.../vaadin/shared/ui/calendar/CalendarState.java | 69 +
.../vaadin/shared/ui/calendar/DateConstants.java | 33 +
.../calendar/BeanItemContainerTestUI.java | 181 ++
.../components/calendar/CalendarActionsUI.java | 107 ++
.../tests/components/calendar/CalendarTest.java | 1227 +++++++++++++
.../components/calendar/CalendarTestEvent.java | 48 +
.../components/calendar/HiddenFwdBackButtons.java | 61 +
.../components/calendar/NotificationTestUI.java | 101 ++
.../vaadin/tests/components/calendar/actions.html | 51 +
.../tests/components/calendar/basicNavigation.html | 236 +++
.../components/calendar/eventSizingNoOverlap.html | 110 ++
.../components/calendar/midnightEventsTest.html | 116 ++
.../components/calendar/monthlyViewNewEvent.html | 170 ++
.../components/calendar/monthlyViewNewEvents.html | 410 +++++
.../tests/components/calendar/notifications.html | 41 +
.../components/calendar/sizeTest1000x600px.html | 41 +
.../calendar/sizeTest100percentXundefined.html | 41 +
.../calendar/sizeTest100x100percent.html | 41 +
.../calendar/sizeTest300pxXundefined.html | 41 +
.../components/calendar/sizeTestSizeFull.html | 41 +
.../calendar/sizeTestUndefinedX100percent.html | 41 +
.../calendar/sizeTestUndefinedX300px.html | 41 +
.../calendar/sizeTestUndefinedXUndefined.html | 41 +
.../tests/components/calendar/visibleHours24H.html | 46 +
.../components/calendar/weeklyViewNewEvents.html | 657 +++++++
80 files changed, 17204 insertions(+)
create mode 100644 WebContent/VAADIN/themes/base/calendar/calendar.scss
create mode 100644 WebContent/VAADIN/themes/base/calendar/img/arrows.png
create mode 100644 WebContent/VAADIN/themes/tests-calendar/styles.css
create mode 100644 client/src/com/vaadin/client/ui/VCalendar.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/CalendarConnector.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/VCalendarAction.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/CalendarDay.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/CalendarEvent.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/DateCell.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/DateCellContainer.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/DateCellGroup.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/DateUtil.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/DayToolbar.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/FocusableComplexPanel.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/FocusableGrid.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/FocusableHTML.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/HasTooltipKey.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/MonthEventLabel.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/MonthGrid.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/SimpleDayCell.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/SimpleDayToolbar.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/SimpleWeekToolbar.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/WeekGrid.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/WeekGridMinuteTimeRange.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/WeekLabel.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/WeeklyLongEvents.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/WeeklyLongEventsDateCell.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarDropHandler.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarMonthDropHandler.java
create mode 100644 client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarWeekDropHandler.java
create mode 100644 server/src/com/vaadin/ui/Calendar.java
create mode 100644 server/src/com/vaadin/ui/components/calendar/CalendarComponentEvent.java
create mode 100644 server/src/com/vaadin/ui/components/calendar/CalendarComponentEvents.java
create mode 100644 server/src/com/vaadin/ui/components/calendar/CalendarDateRange.java
create mode 100644 server/src/com/vaadin/ui/components/calendar/CalendarTargetDetails.java
create mode 100644 server/src/com/vaadin/ui/components/calendar/ContainerEventProvider.java
create mode 100644 server/src/com/vaadin/ui/components/calendar/event/BasicEvent.java
create mode 100644 server/src/com/vaadin/ui/components/calendar/event/BasicEventProvider.java
create mode 100644 server/src/com/vaadin/ui/components/calendar/event/CalendarEditableEventProvider.java
create mode 100644 server/src/com/vaadin/ui/components/calendar/event/CalendarEvent.java
create mode 100644 server/src/com/vaadin/ui/components/calendar/event/CalendarEventProvider.java
create mode 100644 server/src/com/vaadin/ui/components/calendar/event/EditableCalendarEvent.java
create mode 100644 server/src/com/vaadin/ui/components/calendar/handler/BasicBackwardHandler.java
create mode 100644 server/src/com/vaadin/ui/components/calendar/handler/BasicDateClickHandler.java
create mode 100644 server/src/com/vaadin/ui/components/calendar/handler/BasicEventMoveHandler.java
create mode 100644 server/src/com/vaadin/ui/components/calendar/handler/BasicEventResizeHandler.java
create mode 100644 server/src/com/vaadin/ui/components/calendar/handler/BasicForwardHandler.java
create mode 100644 server/src/com/vaadin/ui/components/calendar/handler/BasicWeekClickHandler.java
create mode 100644 server/tests/src/com/vaadin/tests/server/component/calendar/CalendarBasics.java
create mode 100644 server/tests/src/com/vaadin/tests/server/component/calendar/ContainerDataSource.java
create mode 100644 shared/src/com/vaadin/shared/ui/calendar/CalendarClientRpc.java
create mode 100644 shared/src/com/vaadin/shared/ui/calendar/CalendarEventId.java
create mode 100644 shared/src/com/vaadin/shared/ui/calendar/CalendarServerRpc.java
create mode 100644 shared/src/com/vaadin/shared/ui/calendar/CalendarState.java
create mode 100644 shared/src/com/vaadin/shared/ui/calendar/DateConstants.java
create mode 100644 uitest/src/com/vaadin/tests/components/calendar/BeanItemContainerTestUI.java
create mode 100644 uitest/src/com/vaadin/tests/components/calendar/CalendarActionsUI.java
create mode 100644 uitest/src/com/vaadin/tests/components/calendar/CalendarTest.java
create mode 100644 uitest/src/com/vaadin/tests/components/calendar/CalendarTestEvent.java
create mode 100644 uitest/src/com/vaadin/tests/components/calendar/HiddenFwdBackButtons.java
create mode 100644 uitest/src/com/vaadin/tests/components/calendar/NotificationTestUI.java
create mode 100644 uitest/src/com/vaadin/tests/components/calendar/actions.html
create mode 100644 uitest/src/com/vaadin/tests/components/calendar/basicNavigation.html
create mode 100644 uitest/src/com/vaadin/tests/components/calendar/eventSizingNoOverlap.html
create mode 100644 uitest/src/com/vaadin/tests/components/calendar/midnightEventsTest.html
create mode 100644 uitest/src/com/vaadin/tests/components/calendar/monthlyViewNewEvent.html
create mode 100644 uitest/src/com/vaadin/tests/components/calendar/monthlyViewNewEvents.html
create mode 100644 uitest/src/com/vaadin/tests/components/calendar/notifications.html
create mode 100644 uitest/src/com/vaadin/tests/components/calendar/sizeTest1000x600px.html
create mode 100644 uitest/src/com/vaadin/tests/components/calendar/sizeTest100percentXundefined.html
create mode 100644 uitest/src/com/vaadin/tests/components/calendar/sizeTest100x100percent.html
create mode 100644 uitest/src/com/vaadin/tests/components/calendar/sizeTest300pxXundefined.html
create mode 100644 uitest/src/com/vaadin/tests/components/calendar/sizeTestSizeFull.html
create mode 100644 uitest/src/com/vaadin/tests/components/calendar/sizeTestUndefinedX100percent.html
create mode 100644 uitest/src/com/vaadin/tests/components/calendar/sizeTestUndefinedX300px.html
create mode 100644 uitest/src/com/vaadin/tests/components/calendar/sizeTestUndefinedXUndefined.html
create mode 100644 uitest/src/com/vaadin/tests/components/calendar/visibleHours24H.html
create mode 100644 uitest/src/com/vaadin/tests/components/calendar/weeklyViewNewEvents.html
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
Binary files /dev/null and b/WebContent/VAADIN/themes/base/calendar/img/arrows.png differ
diff --git a/WebContent/VAADIN/themes/tests-calendar/styles.css b/WebContent/VAADIN/themes/tests-calendar/styles.css
new file mode 100644
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 "w"
+ */
+ 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
+ * "::" if called from the
+ * {@link SimpleWeekToolbar} and "TO"
+ * 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 allDayLong = new ArrayList();
+ List belowDayLong = new ArrayList();
+
+ 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 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 dayCells = new ArrayList();
+ List timeCells = new ArrayList();
+ 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 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 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 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 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 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 getEventComparator() {
+ return new Comparator() {
+
+ 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 events,
+ List 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 events,
+ List 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 actionMap = new HashMap();
+ private HashMap
+ *
+ *
+ * The {@link Handler#handleAction(Action, Object, Object)} parameters
+ * depend on what the context menu is called upon:
+ *
+ *
If the context menu is called upon an event then the target parameter
+ * is the event, i.e. instanceof {@link CalendarEvent}
+ *
If the context menu is called upon an empty slot then the target is a
+ * {@link Date} representing that slot
+ *
+ *
+ */
+ public void addActionHandler(Handler actionHandler) {
+ if (actionHandler != null) {
+ if (actionHandlers == null) {
+ actionHandlers = new LinkedList();
+ actionMapper = new KeyMapper();
+ }
+ 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 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 eventSetChangeListeners = new LinkedList();
+ private final List eventChangeListeners = new LinkedList();
+
+ /**
+ * The event cache contains the events previously created by
+ * {@link #getEvents(Date, Date)}
+ */
+ private final List eventCache = new LinkedList();
+
+ /**
+ * 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 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 listeners = new ArrayList();
+
+ 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;
+
+/**
+ *
+ * Simple implementation of
+ * {@link com.vaadin.addon.calendar.event.CalendarEventProvider
+ * CalendarEventProvider}. Use {@link #addEvent(CalendarEvent)} and
+ * {@link #removeEvent(CalendarEvent)} to add / remove events.
+ *
+ *
+ *
+ * {@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.
+ *
+ *
+ * @since 7.1.0
+ * @author Vaadin Ltd.
+ */
+@SuppressWarnings("serial")
+public class BasicEventProvider implements CalendarEditableEventProvider,
+ EventSetChangeNotifier, CalendarEvent.EventChangeListener {
+
+ protected List eventList = new ArrayList();
+
+ private List listeners = new ArrayList();
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.addon.calendar.event.CalendarEventProvider#getEvents(java.
+ * util.Date, java.util.Date)
+ */
+ public List getEvents(Date startDate, Date endDate) {
+ ArrayList activeEvents = new ArrayList();
+
+ 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;
+
+/**
+ *
+ * Event in the calendar. Customize your own event by implementing this
+ * interface.
+ *
+ *
+ *
Start and end fields are mandatory.
+ *
+ *
In "allDay" events longer than one day, starting and ending clock times
+ * are omitted in UI and only dates are shown.
+ *
+ * @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();
+
+ /**
+ *
+ * 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
+ *
+ * Styling example: Java code:
+ * event.setStyleName("color1");
+ *
+ * CSS:
+ * .v-calendar-event-color1 {
+ * background-color: #9effae;}
+ *
+ * @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 {
+ /**
+ *
+ * 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.
+ *
+ *
+ *
+ * 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.
+ *
+ *
+ * @param startDate
+ * Start date
+ * @param endDate
+ * End date
+ * @return List of events
+ */
+ public List 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;
+
+/**
+ *
+ * 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.
+ *
+ *
+ *
+ * This interface is used by some of the basic Calendar event handlers in the
+ * com.vaadin.addon.calendar.ui.handler package to determine
+ * whether an event can be edited.
+ *
+ *
+ * @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 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 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 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 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 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 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.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 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.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 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 eventContainer = new BeanItemContainer(
+ 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 days;
+ public List events;
+ public List 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 events = new BeanItemContainer(
+ 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(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 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 item = new BeanItem(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 item = (BeanItem) 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 events = new ArrayList();
+
+ 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 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 @@
+
+
+
+
+
+
+New Test
+
+
+