In this section, we give a tutorial for how to make various basic customizations of the Vaadin Calendar. The event provider and styling was described earlier, so now we concentrate on other features of the Calendar API.
We use example code to demonstrate the customizations. You can find the source
code of the example application on-line with the name
CustomizedCalendarDemo
at
http://dev.vaadin.com/svn/addons/Calendar
. Some of the less
important code for this document has been left out to make the code more
readable and shorter.
Most of the handlers related to calendar events have sensible default handlers. These are found in the com.vaadin.ui.handler package. The default handlers and their functionalities are described below.
BasicBackwardHandler
. Handles clicking the
back-button of the weekly view so that the viewed month is changed to
the previous one.
BasicForwardHandler
. Handles clicking the
forward-button of the weekly view so that the viewed month is changed
to the next one.
BasicWeekClickHandler
. Handles clicking the
week numbers int the monthly view so that the viewable date range is
changed to the clicked week.
BasicDateClickHandler
. Handles clicking the
dates on both the monthly view and the weekly view. Changes the
viewable date range so that only the clicked day is visible.
BasicEventMoveHandler
. Handles moving the
events in both monthly view and the weekly view. Events can be moved
and their start and end dates are changed correctly, but only if the
event implements CalendarEventEditor
(implemented by BasicEvent
).
BasicEventResizeHandler
. Handles resizing the
events in the weekly view. Events can be resized and their start and
end dates are changed correctly, but only if the event implements
CalendarEventEditor
(implemented by the
BasicEvent
).
All of these handlers are automatically set when creating a new
Calendar
. If you wish to disable some of the
default functionality, you can simply set the corresponding handler to
null
. This will prevent the functionality from ever
appearing on the user interface. For example, if you set the
EventMoveHandler
to null
, the
user will be unable to move events in the browser.
Let us first create a new Calendar
instance. Here
we use our own event provider, the MyEventProvider
described in Section 16.4.2, “Implementing the Event Provider”.
Calendar cal = new Calendar(new MyEventProvider());
This initializes the Calendar. To customize the viewable date range, we must set a start and end date to it.
There is only one visible event in the timeline, starting from the current time. That is what our event provider passes to the client.
It would be nice to also be able to control the navigation forward and backward. The default navigation is provided by the default handlers, but perhaps we want to restrict the users so they can only navigate dates in the current year. Maybe we also want to pose some other restrictions to the clicking week numbers and dates.
These restrictions and other custom logic can be defined with custom
handlers. You can find the handlers in the
com.vaadin.addon.calendar.ui.handler package
and they can be easily extended. Note that if you don not want to extend
the default handlers, you are free to implement your own. The interfaces
are described in CalendarComponentEvents
.
Vaadin Calendar has only limited built-in navigation support. The weekly view has navigation buttons in the top left and top right corners.
You can handle backward and forward navigation with a
BackwardListener
and
ForwardListener
.
cal.setHandler(new BasicBackwardHandler() { protected void setDates(BackwardEvent event, Date start, Date end) { java.util.Calendar calendar = event.getComponent() .getInternalCalendar(); if (isThisYear(calendar, end) && isThisYear(calendar, start)) { super.setDates(event, start, end); } }});
The forward navigation handler can be implemented in the same way. The example handler restricts the dates to the current year.
By default, clicking a date either in month or week view switches
single-day view. The date click event is handled by a
DateClickHandler
.
The following example handles click events so that when the user clicks the date header in the weekly view, it will switch to single-day view, and in the single-day view switch back to the weekly view.
cal.setHandler(new BasicDateClickHandler() { public void dateClick(DateClickEvent event) { Calendar cal = event.getComponent(); long currentCalDateRange = cal.getEndDate().getTime() - cal.getStartDate().getTime(); if (currentCalDateRange < VCalendar.DAYINMILLIS) { // Change the date range to the current week cal.setStartDate(cal.getFirstDateForWeek(event.getDate())); cal.setEndDate(cal.getLastDateForWeek(event.getDate())); } else { // Default behaviour, change date range to one day super.dateClick(event); } } });
The monthly view displays week numbers for each week row on the left side
of the date grid. The week number are clickable and you can handle the
click events by setting a WeekClickHandler
for the Calendar
object. The default handler
changes the date range to be the clicked week.
In the following example, we add a week click handler that changes the date range of the calendar to one week only if the start and end dates of the week are in the current month.
cal.setHandler(new BasicWeekClickHandler() { protected void setDates(WeekClick event, Date start, Date end) { java.util.Calendar calendar = event.getComponent() .getInternalCalendar(); if (isThisMonth(calendar, start) && isThisMonth(calendar, end)) { super.setDates(event, start, end); } } });
The calendar events in all views are are clickable. There is no default
handler. Just like the date and week click handlers, event click handling
is enabled by setting an EventClickHandler
for the Calendar
object.
You can get hold of the clicked event by the
getCalendarEvent()
method in the
EventClick
object passed to the handler, as shown
in the following example.
cal.addListener(new EventClickListener() { public void eventClick(EventClick event) { BasicEvent e = (BasicEvent) event.getCalendarEvent(); getMainWindow().showNotification( "Event clicked: " + e.getCaption(), e.getDescription()); } });
The user can drag an event to change its position in time. The default handler sets the start and end time of the event accordingly. You can do many things with a custom move handler, such as restrict moving events.
In the following example, we add a
EventMoveHandler
to a
Calendar
. The event handler updates the new
position to the datasource, but only if the new dates are in the current
month. This requires making some changes to the event provider class.
cal.setHandler(new BasicEventMoveHandler() { private java.util.Calendar javaCalendar; public void eventMove(MoveEvent event) { javaCalendar = event.getComponent().getInternalCalendar(); super.eventMove(event); } protected void setDates(CalendarEventEditor event, Date start, Date end) { if (isThisMonth(javaCalendar, start) && isThisMonth(javaCalendar, end)) { super.setDates(event, start, end); } } });
For the above example to work, the example event provider presented
earlier needs to be changed slightly so that it doesn't always create a
new event when getEvents()
is called.
public static class MyEventProvider implements CalendarEventProvider { private List<CalendarEvent> events = new ArrayList<CalendarEvent>(); public MyEventProvider() { events = new ArrayList<CalendarEvent>(); GregorianCalendar cal = new GregorianCalendar(); cal.setTime(new Date()); Date start = cal.getTime(); cal.add(GregorianCalendar.HOUR, 5); Date end = cal.getTime(); BasicEvent event = new BasicEvent(); event.setCaption("My Event"); event.setDescription("My Event Description"); event.setStart(start); event.setEnd(end); events.add(event); } public void addEvent(CalendarEvent BasicEvent) { events.add(BasicEvent); } public List<CalendarEvent> getEvents(Date startDate, Date endDate) { return events; } }
After these changes, the user can move events around as earlier, but dropping an event, the start and end dates are checked by the server. Note that as the server-side must move the event in order for it to render to the place it was dropped. The server can also reject moves by not doing anything when the event is received.
Drag selection works both in the monthly and weekly views. To listen for
drag selection, you can add a
RangeSelectListener
to the
Calendar
. There is no default handler for range
select.
In the code example below, we create an new event when any date range is
selected. Drag selection opens a window where the user is asked for a
caption for the new event. After confirming, the new event is be passed to
the event provider and calendar is updated. Note that as our example event
provider and event classes do not implement the event change interface, we
must refresh the Calendar
manually after changing
the events.
cal.setHandler(new RangeSelectHandler() { public void rangeSelect(RangeSelectEvent event) { BasicEvent calendarEvent = new BasicEvent(); calendarEvent.setStart(event.getStart()); calendarEvent.setEnd(event.getEnd()); // Create popup window and add a form in it. VerticalLayout layout = new VerticalLayout(); layout.setMargin(true); layout.setSpacing(true); final Window w = new Window(null, layout); ... // Wrap the calendar event to a BeanItem // and pass it to the form final BeanItem<CalendarEvent> item = new BeanItem<CalendarEvent>(myEvent); final Form form = new Form(); form.setItemDataSource(item); ... layout.addComponent(form); HorizontalLayout buttons = new HorizontalLayout(); buttons.setSpacing(true); buttons.addComponent(new Button("OK", new ClickListener() { public void buttonClick(ClickEvent event) { form.commit(); // Update event provider's data source provider.addEvent(item.getBean()); // Calendar needs to be repainted cal.requestRepaint(); getMainWindow().removeWindow(w); } })); ... } });
The user can resize an event by dragging from both ends to change its start or end time. This offers a convenient way to change event times without the need to type anything. The default resize handler sets the start and end time of the event according to the resize.
In the example below, we set a custom handler for resize events. The handler prevents any event to be resized over 12 hours in length. Note that this does not prevent the user from resizing an event over 12 hours in the client. The resize will just be corrected by the server.
cal.setHandler(new BasicEventResizeHandler() { private static final long twelveHoursInMs = 12*60*60*1000; protected void setDates(CalendarEventEditor event, Date start, Date end) { long eventLength = end.getTime() - start.getTime(); if (eventLength <= twelveHoursInMs) { super.setDates(event, start, end); } } });