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.

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);
        }
    }));

    ...
  }
});