Calendar

Live Demo

The Calendar component allows organizing and displaying calendar events. The main features of the calendar include:

  • Monthly, weekly, and daily views

  • Two types of events: all-day events and events with a time range

  • Add events directly, from a Container, or with an event provider

  • Control the range of the visible dates

  • Selecting and editing date or time range by dragging

  • Drag and drop events to calendar

  • Support for localization and timezones

User interaction with the calendar elements, such as date and week captions as well as events, is handled with event listeners. Also date/time range selections, event dragging, and event resizing can be listened by the server. The weekly view has navigation buttons to navigate forward and backward in time. These actions are also listened by the server. Custom navigation can be implemented using event handlers, as described in Customizing the Calendar.

The data source of a calendar can be practically anything, as its events are queried dynamically by the component. You can bind the calendar to a Vaadin container, or to any other data source by implementing an event provider.

The Calendar has undefined size by default and you usually want to give it a fixed or relative size, for example as follows.

Calendar cal = new Calendar("My Calendar");
cal.setWidth("600px");
cal.setHeight("300px");

After creating the calendar, you need to set a time range for it, which also controls the view mode, and set up the data source for calendar events.

Date Range and View Mode

The Vaadin Calendar has two types of views that are shown depending on the date range of the calendar. The weekly view displays a week by default. It can show anything between one to seven days a week, and is also used as a single-day view. The view mode is determined from the date range of the calendar, defined by a start and an end date. Calendar will be shown in a monthly view when the date range is over than one week (seven days) long. The date range is always calculated in an accuracy of one millisecond.

Monthly view with All-Day and Normal Events

The monthly view, shown in Monthly view with All-Day and Normal Events, can easily be used to control all types of events, but it is best suited for events that last for one or more days. You can drag the events to move them. In the figure, you can see two longer events that are highlighted with a blue and green background color. Other markings are shorter day events that last less than a 24 hours. These events can not be moved by dragging in the monthly view.

Weekly View

In Weekly View, you can see four normal day events and also all-day events at the top of the time line grid.

In the following, we set the calendar to show only one day, which is the current day.

cal.setStartDate(new Date());
cal.setEndDate(new Date());

Notice that although the range we set above is actually zero time long, the calendar still renders the time from 00:00 to 23:59. This is normal, as the Vaadin Calendar is guaranteed to render at least the date range provided, but may expand it. This behaviour is important to notice when we implement our own event providers.

Calendar Events

All occurrences in a calendar are represented as events. You have three ways to manage the calendar events:

  • Add events directly to the Calendar object using the addEvent()

  • Use a Container as a data source

  • Use the event provider mechanism

You can add events with addEvent() and remove them with the removeEvent(). These methods will use the underlying event provider to write the modifications to the data source.

Event Interfaces and Providers

Events are handled though the CalendarEvent interface. The concrete class of the event depends on the specific CalendarEventProvider used in the calendar.

By default, Calendar uses a BasicEventProvider to provide events, which uses BasicEvent instances.

Calendar does not depend on any particular data source implementation. Events are queried by the Calendar from the provider that just has to implement the CalendarEventProvider interface. It is up to the event provider that Calendar gets the correct events.

You can bind any Vaadin Container to a calendar, in which case a ContainerEventProvider is used transparently. The container must be ordered by start date and time of the events. See "Collecting Items in Containers" for basic information about containers.

Event Types

A calendar event requires a start time and an end time. These are the only mandatory properties. In addition, an event can also be set as an all-day event by setting the all-day property of the event. You can also set the description of an event, which is displayed as a tooltip in the user interface.

If the all-day field of the event is true, then the event is always rendered as an all-day event. In the monthly view, this means that no start time is displayed in the user interface and the event has an colored background. In the weekly view, all-day events are displayed in the upper part of the screen, and rendered similarly to the monthly view. In addition, when the time range of an event is 24 hours or longer, it is rendered as an all-day event in the monthly view.

When the time range of an event is equal or less than 24 hours, with the accuracy of one millisecond, the event is considered as a normal day event. Normal event has a start and end times that may be on different days.

Basic Events

The easiest way to add and manage events in a calendar is to use the basic event management API. Calendar uses by default a BasicEventProvider, which keeps the events in memory in an internal reprensetation.

For example, the following adds a two-hour event starting from the current time. The standard Java GregorianCalendar provides various ways to manipulate date and time.

// Add a two-hour event
GregorianCalendar start = new GregorianCalendar();
GregorianCalendar end   = new GregorianCalendar();
end.add(java.util.Calendar.HOUR, 2);
calendar.addEvent(new BasicEvent("Calendar study",
        "Learning how to use Vaadin Calendar",
        start.getTime(), end.getTime()));

This adds a new event that lasts for 3 hours. As the BasicEventProvider and BasicEvent implement some optional event interfaces provided by the calendar package, there is no need to refresh the calendar. Just create events, set their properties and add them to the Event Provider.

Getting Events from a Container

You can use any Vaadin Container that implements the Indexed interface as the data source for calendar events. The Calendar will listen to change events from the container as well as write changes to the container. You can attach a container to a Calendar with setContainerDataSource().

In the following example, we bind a BeanItemContainer that contains built-in BasicEvent events to a calendar.

// Create the calendar
Calendar calendar = new Calendar("Bound Calendar");

// Use a container of built-in BasicEvents
final BeanItemContainer<BasicEvent> container =
    new BeanItemContainer<BasicEvent>(BasicEvent.class);

// Create a meeting in the container
container.addBean(new BasicEvent("The Event", "Single Event",
            new GregorianCalendar(2012,1,14,12,00).getTime(),
            new GregorianCalendar(2012,1,14,14,00).getTime()));

// The container must be ordered by the start time. You
// have to sort the BIC every time after you have added
// or modified events.
container.sort(new Object[]{"start"}, new boolean[]{true});

calendar.setContainerDataSource(container, "caption",
    "description", "start", "end", "styleName");

The container must either use the default property IDs for event data, as defined in the CalendarEvent interface, or provide them as parameters for the setContainerDataSource() method, as we did in the example above.

Keeping the Container Ordered

The events in the container must be kept ordered by their start date/time. Failing to do so may and will result in the events not showing in the calendar properly.

Ordering depends on the container. With some containers, such as BeanItemContainer, you have to sort the container explicitly every time after you have added or modified events, usually with the sort() method, as we did in the example above. Some container, such as JPAContainer, keep the in container automatically order if you provide a sorting rule.

For example, you could order a JPAContainer by the following rule, assuming that the start date/time is held in the startDate property:

// The container must be ordered by start date. For JPAContainer
// we can just set up sorting once and it will stay ordered.
container.sort(new String[]{"startDate"}, new boolean[]{true});

Delegation of Event Management

Setting a container as the calendar data source with setContainerDataSource() automatically switches to ContainerEventProvider. You can manipulate the event data through the API in Calendar and the user can move and resize event through the user interface. The event provider delegates all such calendar operations to the container.

If you add events through the Calendar API, notice that you may be unable to create events of the type held in the container or adding them requires some container-specific operations. In such case, you may need to customize the addEvent() method.

For example, JPAContainer requires adding new items with addEntity(). You could first add the entity to the container or entity manager directly and then pass it to the addEvent(). That does not, however, work if the entity class does not implement CalendarEvent. This is actually the case always if the property names differ from the ones defined in the interface. You could handle creating the underlying entity objects in the addEvent() as follows:

// Create a JPAContainer
final JPAContainer<MyCalendarEvent> container =
    JPAContainerFactory.make(MyCalendarEvent.class,
                             "book-examples");

// Customize the event provider for adding events
// as entities
ContainerEventProvider cep =
        new ContainerEventProvider(container) {
    @Override
    public void addEvent(CalendarEvent event) {
        MyCalendarEvent entity = new MyCalendarEvent(
            event.getCaption(), event.getDescription(),
            event.getStart(), event.getEnd(),
            event.getStyleName());
        container.addEntity(entity);
    }
}

// Set the container as the data source
calendar.setEventProvider(cep);

// Now we can add events to the database through the calendar
BasicEvent event = new BasicEvent("The Event", "Single Event",
    new GregorianCalendar(2012,1,15,12,00).getTime(),
    new GregorianCalendar(2012,1,15,14,00).getTime());
calendar.addEvent(event);

Implementing an Event Provider

If the two simple ways of storing and managing events for a calendar are not enough, you may need to implement a custom event provider. It is the most flexible way of providing events. You need to attach the event provider to the Calendar using the setEventProvider() method.

Event queries are done by asking the event provider for all the events between two given dates. The range of these dates is guaranteed to be at least as long as the start and end dates set for the component. The component can, however, ask for a longer range to ensure correct rendering. In particular, all start dates are expanded to the start of the day, and all end dates are expanded to the end of the day.

Custom Events

An event provider could use the built-in BasicEvent, but it is usually more proper to define a custom event type that is bound directly to the data source. Custom events may be useful for some other purposes as well, such as when you need to add extra information to an event or customize how it is acquired.

Custom events must implement the CalendarEvent interface or extend an existing event class. The built-in BasicEvent class should serve as a good example of implementing simple events. It keeps the data in member variables.

public class BasicEvent
       implements CalendarEventEditor, EventChangeNotifier {
   ...

   public String getCaption() {
        return caption;
    }

    public String getDescription() {
        return description;
    }

    public Date getEnd() {
        return end;
    }

    public Date getStart() {
        return start;
    }

    public String getStyleName() {
        return styleName;
    }

    public boolean isAllDay() {
        return isAllDay;
    }

    public void setCaption(String caption) {
        this.caption = caption;
        fireEventChange();
    }

    public void setDescription(String description) {
        this.description = description;
        fireEventChange();
    }

    public void setEnd(Date end) {
        this.end = end;
        fireEventChange();
    }

    public void setStart(Date start) {
        this.start = start;
        fireEventChange();
    }

    public void setStyleName(String styleName) {
        this.styleName = styleName;
        fireEventChange();
    }

    public void setAllDay(boolean isAllDay) {
        this.isAllDay = isAllDay;
        fireEventChange();
    }

    public void addEventChangeListener(
            EventChangeListener listener) {
        ...
    }

    public void removeListener(EventChangeListener listener) {
        ...
    }

    protected void fireEventChange() {...}
}

You may have noticed that there was some additional code in the BasicEvent that was not in the CalendarEvent interface. Namely BasicEvent also implements two additional interfaces:

CalendarEditor

This interface defines setters for all the fields, and is required for some of the default handlers to work.

EventChangeNotifier

This interface adds the possibility to listen for changes in the event, and enables the Calendar to render the changes immediately.

The start time and end time are mandatory, but caption, description, and style name are not. The style name is used as a part of the CSS class name for the HTML DOM element of the event.

In addition to the basic event interfaces, you can enhance the functionality of your event and event provider classes by using the EventChange and EventSetChange events. They let the Calendar component to know about changes in events and update itself accordingly. The BasicEvent and BasicEventProvider examples given earlier include a simple implementation of these interfaces.

Implementing the Event Provider

An event provider needs to implement the CalendarEventProvider interface. It has only one method to be implemented. Whenever the calendar is painted, getEvents(Date, Date) method is called and it must return a list of events between the given start and end time.

The following example implementation returns only one example event. The event starts from the current time and is five hours long.

public class MyEventProvider implements CalendarEventProvider{
    public List<Event> getEvents(Date startDate, Date endDate){
        List<Event> events = new ArrayList<Event>();
        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);

        return events;
    }
}

It is important to notice that the Calendar may query for dates beyond the range defined by start date and end date. Particularly, it may expand the date range to make sure the user interface is rendered correctly.

Styling a Calendar

Configuring the appearance of the Vaadin Calendar component is one of the basic tasks. At the least, you need to consider its sizing in your user interface. You also quite probably want to use some color or colors for events.

Sizing

The Calendar supports component sizing as usual for defined (fixed or relative) sizes. When using an undefined size for the calendar, all the sizes come from CSS. In addition, when the height is undefined, a scrollbar is displayed in the weekly view to better fit the cells to the user interface.

Below is a list of style rules that define the size of a Calendar with undefined size (these are the defaults):

.v-calendar-month-sizedheight .v-calendar-month-day {
	height: 100px;
}

.v-calendar-month-sizedwidth .v-calendar-month-day {
	width: 100px;
}

.v-calendar-header-month-Hsized .v-calendar-header-day {
	width: 101px;
}

/* for IE */
.v-ie6 .v-calendar-header-month-Hsized .v-calendar-header-day {
	width: 104px;
}

/* for others */
.v-calendar-header-month-Hsized td:first-child {
	padding-left: 21px;
}

.v-calendar-header-day-Hsized {
	width: 200px;
}

.v-calendar-week-numbers-Vsized .v-calendar-week-number {
	height: 100px;
	line-height: 100px;
}

.v-calendar-week-wrapper-Vsized {
	height: 400px;
	overflow-x: hidden !important;
}

.v-calendar-times-Vsized .v-calendar-time {
	height: 38px;
}

.v-calendar-times-Hsized .v-calendar-time {
	width: 42px;
}

.v-calendar-day-times-Vsized .v-slot,.v-calendar-day-times-Vsized .v-slot-even {
	height: 18px;
}

.v-calendar-day-times-Hsized, .v-calendar-day-times-Hsized .v-slot,.v-calendar-day-times-Hsized .v-slot-even {
	width: 200px;
}

Event Style

Events can be styled with CSS by setting them a style name suffix. The suffix is retrieved with the getStyleName() method in CalendarEvent. If you use BasicEvent events, you can set the suffix with setStyleName().

BasicEvent event = new BasicEvent("Wednesday Wonder", ... );
event.setStyleName("mycolor");
calendar.addEvent(event);

Suffix mycolor would create v-calendar-event-mycolor class for regular events and v-calendar-event-mycolor-add-day for all-day events. You could style the events with the following rules:

.v-calendar .v-calendar-event-mycolor {}
.v-calendar .v-calendar-event-mycolor-all-day {}
.v-calendar .v-calendar-event-mycolor .v-calendar-event-caption {}
.v-calendar .v-calendar-event-mycolor .v-calendar-event-content {}

Visible Hours and Days

As we saw in Date Range and View Mode, you can set the range of dates that are shown by the Calendar. But what if you wanted to show the entire month but hide the weekends? Or show only hours from 8 to 16 in the weekly view? The setVisibleDays() and setVisibleHours() methods allow you to do that.

calendar.setVisibleDays(1,5);   // Monday to Friday
calendar.setVisibleHours(0,15); // Midnight until 4 pm

After the above settings, only weekdays from Monday to Friday would be shown. And when the calendar is in the weekly view, only the time range from 00:00 to 16:00 would be shown.

Note that the excluded times are never shown so you should take care when setting the date range. If the date range contains only dates / times that are excluded, nothing will be displayed. Also note that even if a date is not rendered because these settings, the event provider may still be queried for events for that date.

Drag and Drop

Vaadin Calendar can act as a drop target for drag and drop, described in "Drag and Drop". With the functionality, the user could drag events, for example, from a table to a calendar.

To support dropping, a Calendar must have a drop handler. When the drop handler is set, the days in the monthly view and the time slots in the weekly view can receive drops. Other locations, such as day names in the weekly view, can not currently receive drops.

Calendar uses its own implementation of TargetDetails: CalendarTargetdetails. It holds information about the the drop location, which in the context of Calendar means the date and time. The drop target location can be retrieved via the getDropTime() method. If the drop is done in the monthly view, the returned date does not have exact time information. If the drop happened in the weekly view, the returned date also contains the start time of the slot.

Below is a short example of creating a drop handler and using the drop information to create a new event:

private Calendar createDDCalendar() {
  Calendar calendar = new Calendar();
  calendar.setDropHandler(new DropHandler() {
    public void drop(DragAndDropEvent event) {
      CalendarTargetDetails details =
              (CalendarTargetDetails) event.getTargetDetails();

      TableTransferable transferable =
              (TableTransferable) event.getTransferable();

      createEvent(details, transferable);
      removeTableRow(transferable);
    }

    public AcceptCriterion getAcceptCriterion() {
      return AcceptAll.get();
    }

  });

  return calendar;
}


protected void createEvent(CalendarTargetDetails details,
  TableTransferable transferable) {
  Date dropTime = details.getDropTime();
  java.util.Calendar timeCalendar = details.getTargetCalendar()
                                    .getInternalCalendar();
  timeCalendar.setTime(dropTime);
  timeCalendar.add(java.util.Calendar.MINUTE, 120);
  Date endTime = timeCalendar.getTime();

  Item draggedItem = transferable.getSourceComponent().
                            getItem(transferable.getItemId());

  String eventType = (String)draggedItem.
                            getItemProperty("type").getValue();

  String eventDescription = "Attending: "
             + getParticipantString(
                 (String[]) draggedItem.
                   getItemProperty("participants").getValue());

  BasicEvent newEvent = new BasicEvent();
  newEvent.setAllDay(!details.hasDropTime());
  newEvent.setCaption(eventType);
  newEvent.setDescription(eventDescription);
  newEvent.setStart(dropTime);
  newEvent.setEnd(endTime);

  BasicEventProvider ep = (BasicEventProvider) details
                       .getTargetCalendar().getEventProvider();
  ep.addEvent(newEvent);
}

Using the Context Menu

Vaadin Calendar allows the use of context menu (mouse right-click) to manage events. As in other context menus in Vaadin, the menu items are handled in Vaadin as actions by an action handler. To enable a context menu, you have to implement a Vaadin Action.Handler and add it to the calendar with addActionHandler().

An action handler must implement two methods: getActions() and handleAction(). The getActions() is called for each day displayed in the calendar view. It should return a list of allowed actions for that day, that is, the items of the context menu. The target parameter is the context of the click - a CalendarDateRange that spans over the day. The sender is the Calendar object.

The handleActions() receives the target context in the target. If the context menu was opened on an event, the target is the Event object, otherwise it is a CalendarDateRange.

Localization and Formatting

Setting the Locale and Time Zone

Month and weekday names are shown in the language of the locale setting of the Calendar. The translations are acquired from the standard Java locale data. By default, Calendar uses the system default locale for its internal calendar, but you can change it with setLocale(Locale locale). Setting the locale will update also other location specific date and time settings, such as the first day of the week, time zone, and time format. However, time zone and time format can be overridden by settings in the Calendar.

For example, the following would set the language to US English:

cal.setLocale(Locale.US);

The locale defines the default time zone. You can change it with the setTimeZone() method, which takes a java.util.TimeZone object as its parameter. Setting timezone to null will reset timezone to the locale default.

For example, the following would set the Finnish time zone, which is EET

cal.setTimeZone(TimeZone.getTimeZone("Europe/Helsinki"));

Time and Date Caption Format

The time may be shown either in 24 or 12 hour format. The default format is defined by the locale, but you can change it with the setTimeFormat() method. Giving a null setting will reset the time format to the locale default.

cal.setTimeFormat(TimeFormat.Format12H);

You can change the format of the date captions in the week view with the setWeeklyCaptionFormat(String dateFormatPattern) method. The date format pattern should follow the format of the standard Java java.text.SimpleDateFormat class.

For example:

cal.setWeeklyCaptionFormat("dd-MM-yyyy");

Customizing the Calendar

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.

Overview of Handlers

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.

Creating a Calendar

Let us first create a new Calendar instance. Here we use our own event provider, the MyEventProvider described in 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.//TODO See the figure 3.

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.

Backward and Forward Navigation

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.

Date Click Handling

By default, clicking a date either in month or week view switches to single-day view, while clicking on the date header in the day view has no effect. The date click event is handled by a DateClickHandler.

The following example handles click events on the date header in the day view to zoom out to the week view. For other clicks it applies the default behavior; in the week view clicking on a day switches to the day view.

calendar.setHandler(new BasicDateClickHandler() {
    public void dateClick(DateClickEvent event) {
      Calendar cal = event.getComponent();

      // Check if the current range is already one day long
      long currentCalDateRange = cal.getEndDate().getTime() -
                                 cal.getStartDate().getTime();

      // From one-day view, zoom out to week view
      if (currentCalDateRange <= DateConstants.DAYINMILLIS) {
          // Change the date range to the current week
          GregorianCalendar weekstart = new GregorianCalendar();
          GregorianCalendar weekend   = new GregorianCalendar();
          weekstart.setTime(event.getDate());
          weekend.setTime(event.getDate());
          weekstart.setFirstDayOfWeek(java.util.Calendar.SUNDAY);
          weekstart.set(java.util.Calendar.HOUR_OF_DAY, 0);
          weekstart.set(java.util.Calendar.DAY_OF_WEEK,
                       java.util.Calendar.SUNDAY);
          weekend.set(java.util.Calendar.HOUR_OF_DAY, 23);
          weekend.set(java.util.Calendar.DAY_OF_WEEK,
                       java.util.Calendar.SATURDAY);
          cal.setStartDate(weekstart.getTime());
          cal.setEndDate(weekend.getTime());

          Notification.show("Custom zoom to week");
      } else {
        // Default behavior, change date range to one day
        super.dateClick(event);
      }
    }
  });

Handling Week Clicks

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

Handling Event Clicks

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.setHandler(new EventClickHandler() {
    public void eventClick(EventClick event) {
        BasicEvent e = (BasicEvent) event.getCalendarEvent();

        // Do something with it
        new Notification("Event clicked: " + e.getCaption(),
            e.getDescription()).show(Page.getCurrent());
    }
});

Event Dragging

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.

Handling Drag Selection

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

            UI.getCurrent().removeWindow(w);
        }
    }));

    ...
  }
});

Resizing Events

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