calendar-component-addon/src/main/java/org/vaadin/addon/calendar/client/ui/schedule/DayToolbar.java
public class DayToolbar extends HorizontalPanel implements ClickHandler {

    private int width = 0;
    public static final int MARGINLEFT = 50;
    public static final int MARGINRIGHT = 15;
    protected Button backLabel;
    protected Button nextLabel;
    private boolean verticalSized;
    private boolean horizontalSized;
    private VCalendar calendar;

    public DayToolbar(VCalendar vcalendar) {
        calendar = vcalendar;

        setStylePrimaryName("v-calendar-header-week");

        backLabel = new Button();
        backLabel.setStylePrimaryName("v-calendar-back");
        backLabel.addClickHandler(this);

        nextLabel = new Button();
        nextLabel.addClickHandler(this);
        nextLabel.setStylePrimaryName("v-calendar-next");

        setBorderWidth(0);
        setSpacing(0);
    }

    public void setWidthPX(int width) {
        this.width = width - MARGINLEFT - MARGINRIGHT;
        // super.setWidth(this.width + "px");
        if (getWidgetCount() == 0) {
            return;
        }
        updateCellWidths();
    }

    public void updateCellWidths() {
        int count = getWidgetCount();
        if (count > 0) {
            setCellWidth(backLabel, MARGINLEFT + "px");
            setCellWidth(nextLabel, MARGINRIGHT + "px");
            setCellHorizontalAlignment(backLabel, ALIGN_RIGHT);
            setCellHorizontalAlignment(nextLabel, ALIGN_LEFT);
            int cellw = width / (count - 2);
            if (cellw > 0) {
                int[] cellWidths = VCalendar.distributeSize(width, count - 2,
                        0);
                for (int i = 1; i < count - 1; i++) {
                    Widget widget = getWidget(i);
                    // if (remain > 0) {
                    // setCellWidth(widget, cellw2 + "px");
                    // remain--;
                    // } else {
                    // setCellWidth(widget, cellw + "px");
                    // }
                    setCellWidth(widget, cellWidths[i - 1] + "px");
                    widget.setWidth(cellWidths[i - 1] + "px");
                }
            }
        }
    }

    public void add(String dayName, final Date date, String localized_date_format, String extraClass) {

        HTML l = new HTML(("<span>" + dayName + "</span> " + localized_date_format).trim());
        l.setStylePrimaryName("v-calendar-header-day");

        if (extraClass != null) {
            l.addStyleDependentName(extraClass);
        }

        if (verticalSized) {
            l.addStyleDependentName("Vsized");
        }
        if (horizontalSized) {
            l.addStyleDependentName("Hsized");
        }

        l.addClickHandler(ce -> {
            if (calendar.getDateClickListener() != null) {
                calendar.getDateClickListener().dateClick(DateConstants.toRPCDate(date));
            }
        });

        add(l);
    }

    public void addBackButton() {
        if (!calendar.isBackwardNavigationEnabled()) {
            nextLabel.getElement().getStyle().setHeight(0, Unit.PX);
        }
        add(backLabel);
    }

    public void addNextButton() {
        if (!calendar.isForwardNavigationEnabled()) {
            backLabel.getElement().getStyle().setHeight(0, Unit.PX);
        }
        add(nextLabel);
    }

    @Override
    public void onClick(ClickEvent event) {
        if (!calendar.isDisabled()) {
            if (event.getSource() == nextLabel) {
                if (calendar.getForwardListener() != null) {
                    calendar.getForwardListener().forward();
                }
            } else if (event.getSource() == backLabel) {
                if (calendar.getBackwardListener() != null) {
                    calendar.getBackwardListener().backward();
                }
            }
        }
    }

    public void setVerticalSized(boolean sized) {
        verticalSized = sized;
        updateDayLabelSizedStyleNames();
    }

    public void setHorizontalSized(boolean sized) {
        horizontalSized = sized;
        updateDayLabelSizedStyleNames();
    }

    private void updateDayLabelSizedStyleNames() {
        for (Widget widget : this) {
            updateWidgetSizedStyleName(widget);
        }
    }

    private void updateWidgetSizedStyleName(Widget w) {
        if (verticalSized) {
            w.addStyleDependentName("Vsized");
        } else {
            w.removeStyleDependentName("VSized");
        }
        if (horizontalSized) {
            w.addStyleDependentName("Hsized");
        } else {
            w.removeStyleDependentName("HSized");
        }
    }
}
calendar-component-addon/src/main/java/org/vaadin/addon/calendar/client/ui/schedule/MonthGrid.java
public class MonthGrid extends FocusableGrid implements KeyDownHandler {

    private SimpleDayCell selectionStart;
    private SimpleDayCell selectionEnd;
    private final VCalendar calendar;
    private boolean rangeSelectDisabled;
    private boolean enabled = true;
    private final HandlerRegistration keyDownHandler;

    public MonthGrid(VCalendar parent, int rows, int columns) {
        super(rows, columns);
        calendar = parent;
        setCellSpacing(0);
        setCellPadding(0);
        setStylePrimaryName("v-calendar-month");

        keyDownHandler = addKeyDownHandler(this);
    }

    @Override
    protected void onUnload() {
        keyDownHandler.removeHandler();
        super.onUnload();
    }

    public void setSelectionEnd(SimpleDayCell simpleDayCell) {
        selectionEnd = simpleDayCell;
        updateSelection();
    }

    public void setSelectionStart(SimpleDayCell simpleDayCell) {
        if (!rangeSelectDisabled && isEnabled()) {
            selectionStart = simpleDayCell;
            setFocus(true);
        }

    }

    private void updateSelection() {

        if (selectionStart == null) {
            return;
        }

        if (selectionEnd != null) {
            Date startDate = selectionStart.getDate();
            Date endDate = selectionEnd.getDate();
            for (int row = 0; row < getRowCount(); row++) {
                for (int cell = 0; cell < getCellCount(row); cell++) {
                    SimpleDayCell sdc = (SimpleDayCell) getWidget(row, cell);
                    if (sdc == null) {
                        return;
                    }
                    Date d = sdc.getDate();
                    if (startDate.compareTo(d) <= 0
                            && endDate.compareTo(d) >= 0) {
                        sdc.addStyleDependentName("selected");

                    } else if (startDate.compareTo(d) >= 0
                            && endDate.compareTo(d) <= 0) {
                        sdc.addStyleDependentName("selected");

                    } else {
                        sdc.removeStyleDependentName("selected");

                    }
                }
            }
        }
    }

    @SuppressWarnings("deprecation")
    public void setSelectionReady() {
        if (selectionStart != null && selectionEnd != null) {

            Date startDate = selectionStart.getDate();
            Date endDate = selectionEnd.getDate();
            if (startDate.compareTo(endDate) > 0) {
                Date temp = startDate;
                startDate = endDate;
                endDate = temp;
            }

            if (calendar.getRangeSelectListener() != null) {

                SelectionRange weekSelection = new SelectionRange();
                weekSelection.setStartDay(DateConstants.toRPCDate(
                        startDate.getYear(),
                        startDate.getMonth(),
                        startDate.getDate()));
                weekSelection.setEndDay(DateConstants.toRPCDate(
                        endDate.getYear(),
                        endDate.getMonth(),
                        endDate.getDate()));

                calendar.getRangeSelectListener().rangeSelected(weekSelection);
            }
            selectionStart = null;
            selectionEnd = null;
            setFocus(false);
        }
    }

    public void cancelRangeSelection() {
        if (selectionStart != null && selectionEnd != null) {
            for (int row = 0; row < getRowCount(); row++) {
                for (int cell = 0; cell < getCellCount(row); cell++) {
                    SimpleDayCell sdc = (SimpleDayCell) getWidget(row, cell);
                    if (sdc == null) {
                        return;
                    }
                    sdc.removeStyleDependentName("selected");
                }
            }
        }
        setFocus(false);
        selectionStart = null;
    }

    public void updateCellSizes(int totalWidthPX, int totalHeightPX) {

        boolean setHeight = totalHeightPX > 0;
        boolean setWidth = totalWidthPX > 0;

        int rows = getRowCount();
        int cells = getCellCount(0);

        int cellWidth = (totalWidthPX / cells) - 1;
        int widthRemainder = totalWidthPX % cells;

        // Division for cells might not be even. Distribute it evenly to will whole space.
        int cellHeight = (totalHeightPX / rows) - 1;
        int heightRemainder = totalHeightPX % rows;

        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cells; j++) {

                SimpleDayCell dayCell = (SimpleDayCell) getWidget(i, j);

                if (setWidth) {
                    if (widthRemainder > 0) {
                        dayCell.setWidth(cellWidth + 1 + "px");
                        widthRemainder--;

                    } else {
                        dayCell.setWidth(cellWidth + "px");
                    }
                }

                if (setHeight) {
                    if (heightRemainder > 0) {
                        dayCell.setHeightPX(cellHeight + 1, true);

                    } else {
                        dayCell.setHeightPX(cellHeight, true);
                    }
                } else {
                    dayCell.setHeightPX(-1, true);
                }
            }
            heightRemainder--;
        }
    }

    /**
     * Disable or enable possibility to select ranges
     */
    @SuppressWarnings("unused")
    public void setRangeSelect(boolean b) {
        rangeSelectDisabled = !b;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public boolean isEnabled() {
        return enabled;
    }

    @Override
    public void onKeyDown(KeyDownEvent event) {
        int keycode = event.getNativeKeyCode();
        if (KeyCodes.KEY_ESCAPE == keycode && selectionStart != null) {
            cancelRangeSelection();
        }
    }

    public int getDayCellIndex(SimpleDayCell dayCell) {
        int rows = getRowCount();
        int cells = getCellCount(0);
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cells; j++) {
                SimpleDayCell sdc = (SimpleDayCell) getWidget(i, j);
                if (dayCell == sdc) {
                    return i * cells + j;
                }
            }
        }

        return -1;
    }

    private static Logger getLogger() {
        return Logger.getLogger(MonthGrid.class.getName());
    }
}
calendar-component-addon/src/main/java/org/vaadin/addon/calendar/client/ui/schedule/WeekGrid.java
public class WeekGrid extends SimplePanel {

    int width = 0;
    private int height = 0;
    final HorizontalPanel content;
    private VCalendar calendar;
    private boolean disabled;
    final Timebar timebar;
    private Panel wrapper;
    private boolean verticalScrollEnabled;
    private boolean horizontalScrollEnabled;
    private int[] cellHeights;
    private final int slotInMinutes = 30;
    private int dateCellBorder;
    private DateCell dateCellOfToday;
    private int[] cellWidths;
    private int firstHour;
    private int lastHour;

    public WeekGrid(VCalendar parent, boolean format24h) {
        setCalendar(parent);
        content = new HorizontalPanel();
        timebar = new Timebar(format24h);
        content.add(timebar);

        wrapper = new SimplePanel();
        wrapper.setStylePrimaryName("v-calendar-week-wrapper");
        wrapper.add(content);

        setWidget(wrapper);
    }

    private void setVerticalScroll(boolean isVerticalScrollEnabled) {
        if (isVerticalScrollEnabled && !(isVerticalScrollable())) {
            verticalScrollEnabled = true;
            horizontalScrollEnabled = false;
            wrapper.remove(content);

            final ScrollPanel scrollPanel = new ScrollPanel();
            scrollPanel.setStylePrimaryName("v-calendar-week-wrapper");
            scrollPanel.setWidget(content);

            scrollPanel.addScrollHandler(event -> {
                if (calendar.getScrollListener() != null) {
                    int vScrollPos = scrollPanel.getVerticalScrollPosition();
                    calendar.getScrollListener().scroll(vScrollPos);

                    if (vScrollPos > 1) {
                        content.addStyleName("scrolled");
                    } else {
                        content.removeStyleName("scrolled");
                    }
                }
            });

            setWidget(scrollPanel);
            wrapper = scrollPanel;

        } else if (!isVerticalScrollEnabled && (isVerticalScrollable())) {
            verticalScrollEnabled = false;
            horizontalScrollEnabled = false;
            wrapper.remove(content);

            SimplePanel simplePanel = new SimplePanel();
            simplePanel.setStylePrimaryName("v-calendar-week-wrapper");
            simplePanel.setWidget(content);

            setWidget(simplePanel);
            wrapper = simplePanel;
        }
    }

    public void setVerticalScrollPosition(int verticalScrollPosition) {
        if (isVerticalScrollable()) {
            ((ScrollPanel) wrapper)
                    .setVerticalScrollPosition(verticalScrollPosition);
        }
    }

    public int getInternalWidth() {
        return width;
    }

    public void addDate(Date d, Map<Long, CalTimeSlot> timeSlotStyles) {
        final DateCell dc = new DateCell(this, d, timeSlotStyles);
        dc.setDisabled(isDisabled());
        dc.setHorizontalSized(isHorizontalScrollable() || width < 0);
        dc.setVerticalSized(isVerticalScrollable());
        content.add(dc);
    }

    /**
     * @param dateCell
     * @return get the index of the given date cell in this week, starting from
     *         0
     */
    public int getDateCellIndex(DateCell dateCell) {
        return content.getWidgetIndex(dateCell) - 1;
    }

    /**
     * @return get the slot border in pixels
     */
    public int getDateSlotBorder() {
        return ((DateCell) content.getWidget(1)).getSlotBorder();
    }

    private boolean isVerticalScrollable() {
        return verticalScrollEnabled;
    }

    private boolean isHorizontalScrollable() {
        return horizontalScrollEnabled;
    }

    public void setWidthPX(int width) {
        if (isHorizontalScrollable()) {
            updateCellWidths();

            // Otherwise the scroll wrapper is somehow too narrow = horizontal
            // scroll
            wrapper.setWidth(content.getOffsetWidth() + WidgetUtil.getNativeScrollbarSize() + "px");

            this.width = content.getOffsetWidth() - timebar.getOffsetWidth();

        } else {
            this.width = (width == -1) ? width
                    : width - timebar.getOffsetWidth();

            if (isVerticalScrollable() && width != -1) {
                this.width = this.width - WidgetUtil.getNativeScrollbarSize();
            }
            updateCellWidths();
        }
    }

    public void setHeightPX(int intHeight) {
        height = intHeight;

        setVerticalScroll(height <= -1);

        // if not scrollable, use any height given
        if (!isVerticalScrollable() && height > 0) {

            content.setHeight(height + "px");
            setHeight(height + "px");
            wrapper.setHeight(height + "px");
            wrapper.removeStyleDependentName("Vsized");
            updateCellHeights();
            timebar.setCellHeights(cellHeights);
            timebar.setHeightPX(height);

        } else if (isVerticalScrollable()) {
            updateCellHeights();
            wrapper.addStyleDependentName("Vsized");
            timebar.setCellHeights(cellHeights);
            timebar.setHeightPX(height);
        }
    }

    public void clearDates() {
        while (content.getWidgetCount() > 1) {
            content.remove(1);
        }

        dateCellOfToday = null;
    }

    /**
     * @return true if this weekgrid contains a date that is today
     */
    public boolean hasToday() {
        return dateCellOfToday != null;
    }

    public void updateCellWidths() {

        if (!isHorizontalScrollable() && width != -1) {

            int count = content.getWidgetCount();
            int scrollOffset = isVerticalScrollable() ? 0 : DayToolbar.MARGINRIGHT;
            int datesWidth = width - scrollOffset;

            if (datesWidth > 0 && count > 1) {
                cellWidths = VCalendar.distributeSize(datesWidth, count - 1,-1);


                for (int i = 1; i < count; i++) {

                    DateCell dc = (DateCell) content.getWidget(i);
                    dc.setHorizontalSized( isHorizontalScrollable() || width < 0);
                    dc.setWidthPX(cellWidths[i - 1]);

                    if (dc.isToday()) {
                        dc.setTimeBarWidth(getOffsetWidth());
                    }
                }
            }

        } else {

            int count = content.getWidgetCount();
            if (count > 1) {
                for (int i = 1; i < count; i++) {
                    DateCell dc = (DateCell) content.getWidget(i);
                    dc.setHorizontalSized( isHorizontalScrollable() || width < 0);
                }
            }
        }
    }

    /**
     * @return an int-array containing the widths of the cells (days)
     */
    public int[] getDateCellWidths() {
        return cellWidths;
    }

    public void updateCellHeights() {
        if (!isVerticalScrollable()) {
            int count = content.getWidgetCount();
            if (count > 1) {
                DateCell first = (DateCell) content.getWidget(1);
                dateCellBorder = first.getSlotBorder();
                cellHeights = VCalendar.distributeSize(height,
                        first.getNumberOfSlots(), -dateCellBorder);
                for (int i = 1; i < count; i++) {
                    DateCell dc = (DateCell) content.getWidget(i);
                    dc.setHeightPX(height, cellHeights);
                }
            }

        } else {
            int count = content.getWidgetCount();
            if (count > 1) {
                DateCell first = (DateCell) content.getWidget(1);
                dateCellBorder = first.getSlotBorder();
                int dateHeight = (first.getOffsetHeight()
                        / first.getNumberOfSlots()) - dateCellBorder;
                cellHeights = new int[48];
                Arrays.fill(cellHeights, dateHeight);

                for (int i = 1; i < count; i++) {
                    DateCell dc = (DateCell) content.getWidget(i);
                    dc.setVerticalSized(isVerticalScrollable());
                }
            }
        }
    }

    public void addItem(CalendarItem e) {
        int dateCount = content.getWidgetCount();
        Date from = e.getStart();
        Date toTime = e.getEndTime();
        for (int i = 1; i < dateCount; i++) {
            DateCell dc = (DateCell) content.getWidget(i);
            Date dcDate = dc.getDate();
            int comp = dcDate.compareTo(from);
            int comp2 = dcDate.compareTo(toTime);
            if (comp >= 0 && comp2 < 0 || (comp == 0 && comp2 == 0
                    && VCalendar.isZeroLengthMidnightEvent(e))) {
                // Same event may be over two DateCells if event's date
                // range floats over one day. It can't float over two days,
                // because event which range is over 24 hours, will be handled
                // as a "fullDay" event.
                dc.addItem(dcDate, e);
            }
        }
    }

    public int getPixelLengthFor(int startFromMinutes, int durationInMinutes) {
        int pixelLength = 0;
        int currentSlot = 0;

        int firstHourInMinutes = firstHour * DateConstants.HOURINMINUTES;
        int endHourInMinutes = lastHour * DateConstants.HOURINMINUTES;

        if (firstHourInMinutes > startFromMinutes) {
            durationInMinutes = durationInMinutes
                    - (firstHourInMinutes - startFromMinutes);
            startFromMinutes = 0;
        } else {
            startFromMinutes -= firstHourInMinutes;
        }

        int shownHeightInMinutes = endHourInMinutes - firstHourInMinutes
                + DateConstants.HOURINMINUTES;

        durationInMinutes = Math.min(durationInMinutes,
                shownHeightInMinutes - startFromMinutes);

        // calculate full slots to event
        int slotsTillEvent = startFromMinutes / slotInMinutes;
        int startOverFlowTime = slotInMinutes
                - (startFromMinutes % slotInMinutes);
        if (startOverFlowTime == slotInMinutes) {
            startOverFlowTime = 0;
            currentSlot = slotsTillEvent;
        } else {
            currentSlot = slotsTillEvent + 1;
        }

        int durationInSlots = 0;
        int endOverFlowTime = 0;

        if (startOverFlowTime > 0) {
            durationInSlots = (durationInMinutes - startOverFlowTime)
                    / slotInMinutes;
            endOverFlowTime = (durationInMinutes - startOverFlowTime)
                    % slotInMinutes;

        } else {
            durationInSlots = durationInMinutes / slotInMinutes;
            endOverFlowTime = durationInMinutes % slotInMinutes;
        }

        // calculate slot overflow at start
        if (startOverFlowTime > 0 && currentSlot < cellHeights.length) {
            int lastSlotHeight = cellHeights[currentSlot] + dateCellBorder;
            pixelLength += (int) (((double) lastSlotHeight
                    / (double) slotInMinutes) * startOverFlowTime);
        }

        // calculate length in full slots
        int lastFullSlot = currentSlot + durationInSlots;
        for (; currentSlot < lastFullSlot
                && currentSlot < cellHeights.length; currentSlot++) {
            pixelLength += cellHeights[currentSlot] + dateCellBorder;
        }

        // calculate overflow at end
        if (endOverFlowTime > 0 && currentSlot < cellHeights.length) {
            int lastSlotHeight = cellHeights[currentSlot] + dateCellBorder;
            pixelLength += (int) (((double) lastSlotHeight
                    / (double) slotInMinutes) * endOverFlowTime);
        }

        // reduce possible underflow at end
        if (endOverFlowTime < 0) {
            int lastSlotHeight = cellHeights[currentSlot] + dateCellBorder;
            pixelLength += (int) (((double) lastSlotHeight
                    / (double) slotInMinutes) * endOverFlowTime);
        }

        return pixelLength;
    }

    public int getPixelTopFor(int startFromMinutes) {
        int pixelsToTop = 0;
        int slotIndex = 0;

        int firstHourInMinutes = firstHour * 60;

        if (firstHourInMinutes > startFromMinutes) {
            startFromMinutes = 0;
        } else {
            startFromMinutes -= firstHourInMinutes;
        }

        // calculate full slots to event
        int slotsTillEvent = startFromMinutes / slotInMinutes;
        int overFlowTime = startFromMinutes % slotInMinutes;
        if (slotsTillEvent > 0) {
            for (slotIndex = 0; slotIndex < slotsTillEvent; slotIndex++) {
                pixelsToTop += cellHeights[slotIndex] + dateCellBorder;
            }
        }

        // calculate lengths less than one slot
        if (overFlowTime > 0) {
            int lastSlotHeight = cellHeights[slotIndex] + dateCellBorder;
            pixelsToTop += ((double) lastSlotHeight / (double) slotInMinutes)
                    * overFlowTime;
        }

        return pixelsToTop;
    }

    public void itemMoved(DateCellDayItem dayItem) {

        Style s = dayItem.getElement().getStyle();

        String si = s.getLeft().substring(0, s.getLeft().length() - 2);

        // offset can be empty
        if (si.isEmpty()) return;

        int left =  Integer.parseInt(si);

        DateCell previousParent = (DateCell) dayItem.getParent();
        DateCell newParent = (DateCell) content.getWidget((left / getDateCellWidth()) + 1);

        CalendarItem se = dayItem.getCalendarItem();
        previousParent.removeEvent(dayItem);
        newParent.addItem(dayItem);

        if (!previousParent.equals(newParent)) {
            previousParent.recalculateItemWidths();
        }

        newParent.recalculateItemWidths();

        if (calendar.getItemMovedListener() != null) {
            calendar.getItemMovedListener().itemMoved(se);
        }
    }

    public void setToday(Date todayDate, Date todayTimestamp) {
        int count = content.getWidgetCount();
        if (count > 1) {
            for (int i = 1; i < count; i++) {
                DateCell dc = (DateCell) content.getWidget(i);
                if (dc.getDate().getTime() == todayDate.getTime()) {
                    if (isVerticalScrollable()) {
                        dc.setToday(todayTimestamp, -1);
                    } else {
                        dc.setToday(todayTimestamp, getOffsetWidth());
                    }
                }
                dateCellOfToday = dc;
            }
        }
    }

    public DateCell getDateCellOfToday() {
        return dateCellOfToday;
    }

    public void setDisabled(boolean disabled) {
        this.disabled = disabled;
    }

    public boolean isDisabled() {
        return disabled;
    }

    public Timebar getTimeBar() {
        return timebar;
    }

    public void setDateColor(Date when, Date to, String styleName) {
        int dateCount = content.getWidgetCount();
        for (int i = 1; i < dateCount; i++) {
            DateCell dc = (DateCell) content.getWidget(i);
            Date dcDate = dc.getDate();
            int comp = dcDate.compareTo(when);
            int comp2 = dcDate.compareTo(to);
            if (comp >= 0 && comp2 <= 0) {
                dc.setDateColor(styleName);
            }
        }
    }

    /**
     * @param calendar
     *            the calendar to set
     */
    public void setCalendar(VCalendar calendar) {
        this.calendar = calendar;
    }

    /**
     * @return the calendar
     */
    public VCalendar getCalendar() {
        return calendar;
    }

    /**
     * Get width of the single date cell
     *
     * @return Date cell width
     */
    public int getDateCellWidth() {
        int count = content.getWidgetCount() - 1;
        int cellWidth = -1;
        if (count <= 0) {
            return cellWidth;
        }

        if (width == -1) {
            Widget firstWidget = content.getWidget(1);
            cellWidth = firstWidget.getElement().getOffsetWidth();
        } else {
            cellWidth = getInternalWidth() / count;
        }
        return cellWidth;
    }

    /**
     * @return the number of day cells in this week
     */
    public int getDateCellCount() {
        return content.getWidgetCount() - 1;
    }

    public void setFirstHour(int firstHour) {
        this.firstHour = firstHour;
        timebar.setFirstHour(firstHour);
    }

    public void setLastHour(int lastHour) {
        this.lastHour = lastHour;
        timebar.setLastHour(lastHour);
    }

    public int getFirstHour() {
        return firstHour;
    }

    public int getLastHour() {
        return lastHour;
    }

    public static class Timebar extends HTML {

        private static final int[] timesFor12h = { 12, 1, 2, 3, 4, 5, 6, 7, 8,
                9, 10, 11 };

        private int height;

        private final int verticalPadding = 7; // FIXME measure this from DOM

        private int[] slotCellHeights;

        private int firstHour;

        private int lastHour;

        public Timebar(boolean format24h) {
            createTimeBar(format24h);
        }

        public void setLastHour(int lastHour) {
            this.lastHour = lastHour;
        }

        public void setFirstHour(int firstHour) {
            this.firstHour = firstHour;

        }

        public void setCellHeights(int[] cellHeights) {
            slotCellHeights = cellHeights;
        }

        private void createTimeBar(boolean format24h) {
            setStylePrimaryName("v-calendar-times");

            // Fist "time" is empty
            Element e = DOM.createDiv();
            setStyleName(e, "v-calendar-time");
            e.setInnerText("");
            getElement().appendChild(e);

            DateTimeService dts = new DateTimeService();

            if (format24h) {
                for (int i = firstHour + 1; i <= lastHour; i++) {
                    e = DOM.createDiv();
                    setStyleName(e, "v-calendar-time");
                    String delimiter = dts.getClockDelimeter();
                    e.setInnerHTML("<span>" + i + "</span>" + delimiter + "00");
                    getElement().appendChild(e);
                }
            } else {
                // FIXME Use dts.getAmPmStrings(); and make sure that
                // DateTimeService has a some Locale set.
                String[] ampm = new String[] { "AM", "PM" };

                int amStop = (lastHour < 11) ? lastHour : 11;
                int pmStart = (firstHour > 11) ? firstHour % 11 : 0;

                if (firstHour < 12) {
                    for (int i = firstHour + 1; i <= amStop; i++) {
                        e = DOM.createDiv();
                        setStyleName(e, "v-calendar-time");
                        e.setInnerHTML("<span>" + timesFor12h[i] + "</span>"
                                + " " + ampm[0]);
                        getElement().appendChild(e);
                    }
                }

                if (lastHour > 11) {
                    for (int i = pmStart; i < lastHour - 11; i++) {
                        e = DOM.createDiv();
                        setStyleName(e, "v-calendar-time");
                        e.setInnerHTML("<span>" + timesFor12h[i] + "</span>"
                                + " " + ampm[1]);
                        getElement().appendChild(e);
                    }
                }
            }
        }

        public void updateTimeBar(boolean format24h) {
            clear();
            createTimeBar(format24h);
        }

        private void clear() {
            while (getElement().getChildCount() > 0) {
                getElement().removeChild(getElement().getChild(0));
            }
        }

        public void setHeightPX(int pixelHeight) {
            height = pixelHeight;

            if (pixelHeight > -1) {
                // as the negative margins on children pulls the whole element
                // upwards, we must compensate. otherwise the element would be
                // too short
                super.setHeight((height + verticalPadding) + "px");
                removeStyleDependentName("Vsized");
                updateChildHeights();

            } else {
                addStyleDependentName("Vsized");
                updateChildHeights();
            }
        }

        private void updateChildHeights() {
            int childCount = getElement().getChildCount();

            if (height != -1) {

                // 23 hours + first is empty
                // we try to adjust the height of time labels to the distributed
                // heights of the time slots
                int hoursPerDay = lastHour - firstHour + 1;

                int slotsPerHour = slotCellHeights.length / hoursPerDay;
                int[] cellHeights = new int[slotCellHeights.length
                        / slotsPerHour];

                int slotHeightPosition = 0;
                for (int i = 0; i < cellHeights.length; i++) {
                    for (int j = slotHeightPosition; j < slotHeightPosition
                            + slotsPerHour; j++) {
                        cellHeights[i] += slotCellHeights[j] + 1;
                        // 1px more for borders
                        // FIXME measure from DOM
                    }
                    slotHeightPosition += slotsPerHour;
                }

                for (int i = 0; i < childCount; i++) {
                    Element e = (Element) getElement().getChild(i);
                    e.getStyle().setHeight(cellHeights[i], Unit.PX);
                }

            } else {
                for (int i = 0; i < childCount; i++) {
                    Element e = (Element) getElement().getChild(i);
                    e.getStyle().setProperty("height", "");
                }
            }
        }
    }

    public VCalendar getParentCalendar() {
        return calendar;
    }
}
calendar-component-addon/src/main/java/org/vaadin/addon/calendar/client/ui/schedule/SimpleDayToolbar.java
public class SimpleDayToolbar extends HorizontalPanel implements ClickHandler {

    public static int STYLE_BACK_PIXEL_WIDTH = 20;
    public static int STYLE_NEXT_PIXEL_WIDTH = 15;

    private int width = 0;
    private boolean isWidthUndefined = false;

    protected Button backLabel;
    protected Button nextLabel;

    private VCalendar calendar;

    public SimpleDayToolbar(VCalendar calendar) {

        this.calendar = calendar;

        setStylePrimaryName("v-calendar-header-month");

        backLabel = new Button();
        backLabel.setStylePrimaryName("v-calendar-back");
        backLabel.addClickHandler(this);

        nextLabel = new Button();
        nextLabel.addClickHandler(this);
        nextLabel.setStylePrimaryName("v-calendar-next");

    }

    public void setDayNames(String[] dayNames) {
        clear();

        addBackButton();

        for (String dayName : dayNames) {
            Label l = new Label(dayName);
            l.setStylePrimaryName("v-calendar-header-day");
            add(l);
        }

        addNextButton();

        updateCellWidth();
    }

    public void setWidthPX(int width) {
        this.width = width;

        setWidthUndefined(width == -1);

        if (!isWidthUndefined()) {
            super.setWidth(this.width + "px");
            if (getWidgetCount() == 0) {
                return;
            }
        }
        updateCellWidth();
    }

    private boolean isWidthUndefined() {
        return isWidthUndefined;
    }

    private void setWidthUndefined(boolean isWidthUndefined) {
        this.isWidthUndefined = isWidthUndefined;

        if (isWidthUndefined) {
            addStyleDependentName("Hsized");

        } else {
            removeStyleDependentName("Hsized");
        }
    }

    private void updateCellWidth() {

        setCellWidth(backLabel, STYLE_BACK_PIXEL_WIDTH + "px");
        setCellWidth(nextLabel, STYLE_NEXT_PIXEL_WIDTH + "px");
        setCellHorizontalAlignment(backLabel, ALIGN_LEFT);
        setCellHorizontalAlignment(nextLabel, ALIGN_RIGHT);

        int cellw = -1;
        int widgetCount = getWidgetCount();
        if (widgetCount <= 0) {
            return;
        }

        if (isWidthUndefined()) {

            Widget widget = getWidget(1);
            String w = widget.getElement().getStyle().getWidth();

            if (w.length() > 2) {
                cellw = Integer.parseInt(w.substring(0, w.length() - 2));
            }

        } else {
            cellw = width / getWidgetCount();
        }

        if (cellw > 0) {

            int cW;

            for (int i = 1; i < getWidgetCount() -1; i++) {

                Widget widget = getWidget(i);

                cW = cellw - (i == getWidgetCount() -2 ? STYLE_NEXT_PIXEL_WIDTH : 0);

                setCellWidth(widget, cW + "px");
            }
        }
    }

    private void addBackButton() {
        if (!calendar.isBackwardNavigationEnabled()) {
            nextLabel.getElement().getStyle().setHeight(0, Style.Unit.PX);
        }
        add(backLabel);
    }

    private void addNextButton() {
        if (!calendar.isForwardNavigationEnabled()) {
            backLabel.getElement().getStyle().setHeight(0, Style.Unit.PX);
        }
        add(nextLabel);
    }

    @Override
    public void onClick(ClickEvent event) {
        if (!calendar.isDisabled()) {
            if (event.getSource() == nextLabel) {
                if (calendar.getForwardListener() != null) {
                    calendar.getForwardListener().forward();
                }
            } else if (event.getSource() == backLabel) {
                if (calendar.getBackwardListener() != null) {
                    calendar.getBackwardListener().backward();
                }
            }
        }
    }
}
calendar-component-addon/src/main/java/org/vaadin/addon/calendar/client/ui/schedule/CalendarItem.java
public class CalendarItem {

    public static final String SINGLE_TIME = "%s";
    public static final String RANGE_TIME = "%s - %s";

    private int index;
    private String caption;
    private Date start, end;
    private String styleName;
    private Date startTime, endTime;
    private String description;
    private int slotIndex = -1;
    private boolean format24h;

    private String dateCaptionFormat = SINGLE_TIME;

    DateTimeFormat dateformat_date = DateTimeFormat.getFormat("h:mm a"); // TODO make user adjustable
    DateTimeFormat dateformat_date24 = DateTimeFormat.getFormat("H:mm"); // TODO make user adjustable
    private boolean allDay;

    private boolean moveable = true;
    private boolean resizeable = true;
    private boolean clickable = true;

    /**
     * @return The time caption format (eg. ['%s'] )
     */
    public String getDateCaptionFormat() {
        return dateCaptionFormat;
    }

    /**
     * Set the time caption format. Only the '%s' placeholder is supported.
     *
     * @param dateCaptionFormat The time caption format
     */
    public void setDateCaptionFormat(String dateCaptionFormat) {
        this.dateCaptionFormat = dateCaptionFormat;
    }

    /**
     * @see org.vaadin.addon.calendar.item.CalendarItem#getStyleName()
     */
    public String getStyleName() {
        return styleName;
    }

    /**
     * @see org.vaadin.addon.calendar.item.CalendarItem#getStart()
     */
    public Date getStart() {
        return start;
    }

    /**
     * @see org.vaadin.addon.calendar.item.CalendarItem#getStyleName()
     * @param style  The stylename
     */
    public void setStyleName(String style) {
        styleName = style;
    }

    /**
     * @see org.vaadin.addon.calendar.item.CalendarItem#getStart()
     * @param start The start date
     */
    public void setStart(Date start) {
        this.start = start;
    }

    /**
     * @see org.vaadin.addon.calendar.item.CalendarItem#getEnd()
     * @return The end date
     */
    public Date getEnd() {
        return end;
    }

    /**
     * @see org.vaadin.addon.calendar.item.CalendarItem#getEnd()
     * @param end The end date
     */
    public void setEnd(Date end) {
        this.end = end;
    }

    /**
     * Returns the start time of the event
     *
     * @return Time embedded in the {@link Date} object
     */
    public Date getStartTime() {
        return startTime;
    }

    /**
     * Set the start time of the event
     *
     * @param startTime
     *            The time of the event. Use the time fields in the {@link Date}
     *            object
     */
    public void setStartTime(Date startTime) {
        this.startTime = startTime;
    }

    /**
     * Get the end time of the event
     *
     * @return Time embedded in the {@link Date} object
     */
    public Date getEndTime() {
        return endTime;
    }

    /**
     * Set the end time of the event
     *
     * @param endTime
     *            Time embedded in the {@link Date} object
     */
    public void setEndTime(Date endTime) {
        this.endTime = endTime;
    }

    /**
     * Get the (server side) index of the event
     *
     * @return the (server side) index of the event
     */
    public int getIndex() {
        return index;
    }

    /**
     * Get the index of the slot where the event in rendered
     *
     * @return the index of the slot where the event in rendered
     */
    public int getSlotIndex() {
        return slotIndex;
    }

    /**
     * Set the index of the slot where the event in rendered
     *
     * @param index
     *            The index of the slot
     */
    public void setSlotIndex(int index) {
        slotIndex = index;
    }

    /**
     * Set the (server side) index of the event
     *
     * @param index
     *            The index
     */
    public void setIndex(int index) {
        this.index = index;
    }

    /**
     * Get the caption of the event. The caption is the text displayed in the
     * calendar on the event.
     *
     * @return The visible caption of the event
     */
    public String getCaption() {
        return caption;
    }

    /**
     * Set the caption of the event. The caption is the text displayed in the
     * calendar on the event.
     *
     * @param caption
     *            The visible caption of the event
     */
    public void setCaption(String caption) {
        this.caption = caption;
    }

    /**
     * Get the description of the event. The description is the text displayed
     * when hoovering over the event with the mouse
     *
     * @return The description is the text displayed
     *                      when hoovering over the event with the mouse
     */
    public String getDescription() {
        return description;
    }

    /**
     * Set the description of the event. The description is the text displayed
     * when hoovering over the event with the mouse
     *
     * @param description The description is the text displayed
     *                      when hoovering over the event with the mouse
     */
    public void setDescription(String description) {
        this.description = description;
    }

    /**
     * Does the event use the 24h time format
     *
     * @param format24h
     *            True if it uses the 24h format, false if it uses the 12h time
     *            format
     */
    public void setFormat24h(boolean format24h) {
        this.format24h = format24h;
    }

    /**
     * Is the event an all day event.
     *
     * @param allDay
     *            True if the event should be rendered all day
     */
    public void setAllDay(boolean allDay) {
        this.allDay = allDay;
    }

    /**
     * Is the event an all day event.
     *
     * @return The event an all day event.
     */
    public boolean isAllDay() {
        return allDay;
    }

    /**
     * Get the start time as a formatted string
     *
     * @return The start time as a formatted string
     */
    public String getFormattedStartTime() {
        if (format24h) {
            return dateformat_date24.format(startTime);
        } else {
            return dateformat_date.format(startTime);
        }
    }
    /**
     * Get the end time as a formatted string
     *
     * @return The end time as a formatted string
     */
    public String getFormattedEndTime() {
        if (format24h) {
            return dateformat_date24.format(endTime);
        } else {
            return dateformat_date.format(endTime);
        }
    }


    /**
     * Get the amount of milliseconds between the start and end of the event
     *
     * @return the amount of milliseconds between the start and end of the event
     */
    public long getRangeInMilliseconds() {
        return getEndTime().getTime() - getStartTime().getTime();
    }

    /**
     * Get the amount of minutes between the start and end of the event
     *
     * @return the amount of minutes between the start and end of the event
     */
    public long getRangeInMinutes() {
        return (getRangeInMilliseconds() / DateConstants.MINUTEINMILLIS);
    }

    /**
     * Answers whether the start of the event and end of the event is within
     * the same day. 
     *
     * @return true if start and end are in the same day, false otherwise
     */
    @SuppressWarnings("deprecation")
    public boolean isSingleDay() {
        Date start = getStart();
        Date end = getEnd();
        return start.getYear() == end.getYear() && start.getMonth() == end.getMonth() && start.getDate() == end.getDate();
    }

    /**
     * Get the amount of minutes for the event on a specific day. This is useful
     * if the event spans several days.
     *
     * @param targetDay
     *            The date to check
     * @return the amount of minutes for the event on a specific day. This is useful
     * if the event spans several days.
     */
    public long getRangeInMinutesForDay(Date targetDay) {

        long rangeInMinutesForDay;

        // we must take into account that here can be not only 1 and 2 days, but
        // 1, 2, 3, 4... days first and last days - special cases all another
        // days between first and last - have range "ALL DAY"
        if (isTimeOnDifferentDays()) {
            if (targetDay.compareTo(getStart()) == 0) { // for first day
                rangeInMinutesForDay = DateConstants.DAYINMINUTES
                        - (getStartTime().getTime() - getStart().getTime())
                        / DateConstants.MINUTEINMILLIS;

            } else if (targetDay.compareTo(getEnd()) == 0) { // for last day
                rangeInMinutesForDay = (getEndTime().getTime()
                        - getEnd().getTime())
                        / DateConstants.MINUTEINMILLIS;

            } else { // for in-between days
                rangeInMinutesForDay = DateConstants.DAYINMINUTES;
            }
        } else { // simple case - period is in one day
            rangeInMinutesForDay = getRangeInMinutes();
        }
        return rangeInMinutesForDay;
    }

    /**
     * Does the item span several days
     *
     * @return  true, if the item span several days
     */
    @SuppressWarnings("deprecation")
    public boolean isTimeOnDifferentDays() {
        // if difference between start and end times is more than day - of
        // course it is not one day, but several days

        return getEndTime().getTime() - getStartTime().getTime() > DateConstants.DAYINMILLIS
                || getStart().compareTo(getEnd()) != 0
                && !((getEndTime().getHours() == 0 && getEndTime().getMinutes() == 0));
    }

    public boolean isMoveable() {
        return moveable;
    }

    public void setMoveable(boolean moveable) {
        this.moveable = moveable;
    }

    public boolean isResizeable() {
        return resizeable;
    }

    public void setResizeable(boolean resizeable) {
        this.resizeable = resizeable;
    }

    public boolean isClickable() {
        return clickable;
    }

    public void setClickable(boolean clickable) {
        this.clickable = clickable;
    }
}
calendar-component-addon/src/main/java/org/vaadin/addon/calendar/client/ui/schedule/SimpleDayCell.java
public class SimpleDayCell extends FocusableFlowPanel implements MouseUpHandler,
        MouseDownHandler, MouseOverHandler, MouseMoveHandler {

    private static final int BORDERPADDINGSIZE = 1;

    private static int eventHeight = -1;
    private static int bottomSpacerHeight = -1;

    private final VCalendar calendar;
    private Date date;
    private int intHeight;
    private final HTML bottomspacer;
    private final Label caption;
    private final CalendarItem[] calendarItems = new CalendarItem[10];
    private final int cell;
    private final int row;
    private boolean monthNameVisible;
    private HandlerRegistration mouseUpRegistration;
    private HandlerRegistration mouseDownRegistration;
    private HandlerRegistration mouseOverRegistration;
    private boolean monthEventMouseDown;
    private boolean labelMouseDown;
    private int itemCount = 0;

    private int startX = -1;
    private int startY = -1;
    private int startYrelative;
    private int startXrelative;
    // "from" date of date which is source of Dnd
    private Date dndSourceDateFrom;
    // "to" date of date which is source of Dnd
    private Date dndSourceDateTo;
    // "from" time of date which is source of Dnd
    private Date dndSourceStartDateTime;
    // "to" time of date which is source of Dnd
    private Date dndSourceEndDateTime;

    private boolean extended = false;

    private int prevDayDiff = 0;
    private int prevWeekDiff = 0;

    private HandlerRegistration keyDownHandler;
    private HandlerRegistration moveRegistration;
    private HandlerRegistration bottomSpacerMouseDownHandler;

    private CalendarItem movingItem;

    private Widget clickedWidget;
    private MonthGrid monthGrid;

    public SimpleDayCell(VCalendar calendar, int row, int cell) {
        this.calendar = calendar;
        this.row = row;
        this.cell = cell;
        setStylePrimaryName("v-calendar-month-day");
        caption = new Label();
        caption.setStyleName("v-calendar-day-number");
        caption.addMouseDownHandler(this);
        caption.addMouseUpHandler(this);
        add(caption);

        bottomspacer = new HTML();
        bottomspacer.setStyleName("v-calendar-bottom-spacer-empty");
        bottomspacer.setWidth(3 + "em");
        add(bottomspacer);
    }

    @Override
    public void onLoad() {
        bottomSpacerHeight = bottomspacer.getOffsetHeight();
        eventHeight = bottomSpacerHeight;
    }

    public void setMonthGrid(MonthGrid monthGrid) {
        this.monthGrid = monthGrid;
    }

    public MonthGrid getMonthGrid() {
        return monthGrid;
    }

    @SuppressWarnings("deprecation")
    public void setDate(Date date) {
        int dayOfMonth = date.getDate();
        if (monthNameVisible) {
            caption.setText(dayOfMonth + " " + calendar.getMonthNames()[date.getMonth()]);
        } else {
            caption.setText("" + dayOfMonth);
        }

        if (dayOfMonth == 1) {
            addStyleName("firstDay");
        } else {
            removeStyleName("firstDay");
        }

        this.date = date;
    }

    public Date getDate() {
        return date;
    }

    public void reDraw(boolean clear) {
        setHeightPX(intHeight + BORDERPADDINGSIZE, clear);
    }

    /*
     * Events and whole cell content are drawn by this method. By the
     * clear-argument, you can choose to clear all old content. Notice that
     * clearing will also remove all element's event handlers.
     */
    public void setHeightPX(int px, boolean clear) {
        // measure from DOM if needed
        if (px < 0) {
            intHeight = getOffsetHeight() - BORDERPADDINGSIZE;
        } else {
            intHeight = px - BORDERPADDINGSIZE;
        }

        // Couldn't measure height or it ended up negative. Don't bother
        // continuing
        if (intHeight == -1) {
            return;
        }

        if (clear) {
            while (getWidgetCount() > 1) {
                remove(1);
            }
        }

        // How many calendarItems can be shown in UI
        int slots = 0;
        if (extended) {

            for (int i = 0; i < calendarItems.length; i++) {
                if (calendarItems[i] != null) {
                    slots = i + 1;
                }
            }

        } else {

           slots = (intHeight - caption.getOffsetHeight() - bottomSpacerHeight) / eventHeight;
           if (slots > 10) {
               slots = 10;
           }
        }

        setHeight(intHeight + "px"); // Fixed height

        updateItems(slots, clear);

    }

    public void updateItems(int slots, boolean clear) {
        int eventsAdded = 0;

        for (int i = 0; i < slots; i++) {

            CalendarItem e = calendarItems[i];

            if (e == null) {

                // Empty slot
                HTML slot = new HTML();
                slot.setStyleName("v-calendar-spacer");

                if (!clear) {
                    remove(i + 1);
                    insert(slot, i + 1);
                } else {
                    add(slot);
                }

            } else {

                // Item slot
                eventsAdded++;
                if (!clear) {

                    Widget w = getWidget(i + 1);

                    if (!(w instanceof MonthItemLabel)) {
                        remove(i + 1);
                        insert(createMonthItemLabel(e), i + 1);
                    }

                } else {
                    add(createMonthItemLabel(e));
                }
            }
        }

        int remainingSpace = intHeight - ((slots * eventHeight) + bottomSpacerHeight + caption.getOffsetHeight());
        int newHeight = remainingSpace + bottomSpacerHeight;

        if (newHeight < 0) {
            newHeight = eventHeight;
        }
        bottomspacer.setHeight(newHeight + "px");

        if (clear) {
            add(bottomspacer);
        }

        int more = itemCount - eventsAdded;
        if (more > 0) {
            if (bottomSpacerMouseDownHandler == null) {
                bottomSpacerMouseDownHandler = bottomspacer
                        .addMouseDownHandler(this);
            }
            bottomspacer.setStyleName("v-calendar-bottom-spacer");
            bottomspacer.setHTML("<span>" + more + "</span>");

        } else {
            if (!extended && bottomSpacerMouseDownHandler != null) {
                bottomSpacerMouseDownHandler.removeHandler();
                bottomSpacerMouseDownHandler = null;
            }

            if (extended) {
                bottomspacer.setStyleName("v-calendar-bottom-spacer-expanded");
                bottomspacer.setHTML("<span></span>");

            } else {
                bottomspacer.setStyleName("v-calendar-bottom-spacer-empty");
                bottomspacer.setText("");
            }
        }
    }

    private MonthItemLabel createMonthItemLabel(CalendarItem item) {

        // Create a new MonthItemLabel
        MonthItemLabel eventDiv = new MonthItemLabel();
        eventDiv.addStyleDependentName("month");
        eventDiv.addMouseDownHandler(this);
        eventDiv.addMouseUpHandler(this);
        eventDiv.setCalendar(calendar);
        eventDiv.setItemIndex(item.getIndex());
        eventDiv.setCalendarItem(item);

        if (item.isSingleDay() && !item.isAllDay()) {

            if (item.getStyleName() != null) {
                eventDiv.addStyleDependentName(item.getStyleName());
            }

            eventDiv.setTimeSpecificEvent(true);
            eventDiv.setCaption(item.getCaption());
            eventDiv.setTime(item.getStartTime());

        } else {

            eventDiv.setTimeSpecificEvent(false);

            if (item.getStyleName().length() > 0) {
                eventDiv.addStyleName("month-event " + item.getStyleName());
            } else {
                eventDiv.addStyleName("month-event");
            }

            int fromCompareToDate = item.getStart().compareTo(date);
            int toCompareToDate = item.getEnd().compareTo(date);

            eventDiv.addStyleDependentName("all-day");

            if (fromCompareToDate == 0) {
                eventDiv.addStyleDependentName("start");
                eventDiv.setCaption(item.getCaption());

            } else if (fromCompareToDate < 0 && cell == 0) {
                eventDiv.addStyleDependentName("continued-from");
                eventDiv.setCaption(item.getCaption());
            }

            if (toCompareToDate == 0) {
                eventDiv.addStyleDependentName("end");
            } else if (toCompareToDate > 0 && (cell + 1) == getMonthGrid().getCellCount(row)) {
                eventDiv.addStyleDependentName("continued-to");
            }

            if (item.getStyleName() != null) {
                eventDiv.addStyleDependentName(item.getStyleName() + "-all-day");
            }
        }

        return eventDiv;
    }

    private void setUnlimitedCellHeight() {
        extended = true;
        addStyleDependentName("extended");
    }

    private void setLimitedCellHeight() {
        extended = false;
        removeStyleDependentName("extended");
    }

    public void addItem(CalendarItem item) {
        itemCount++;
        int slot = item.getSlotIndex();
        if (slot == -1) {
            for (int i = 0; i < calendarItems.length; i++) {
                if (calendarItems[i] == null) {
                    calendarItems[i] = item;
                    item.setSlotIndex(i);
                    break;
                }
            }
        } else {
            calendarItems[slot] = item;
        }
    }

    @SuppressWarnings("deprecation")
    public void setMonthNameVisible(boolean b) {
        monthNameVisible = b;
        caption.setText( date.getDate() + " " + calendar.getMonthNames()[date.getMonth()]);
    }

    public HandlerRegistration addMouseMoveHandler(MouseMoveHandler handler) {
        return addDomHandler(handler, MouseMoveEvent.getType());
    }

    @Override
    protected void onAttach() {
        super.onAttach();
        mouseUpRegistration = addDomHandler(this, MouseUpEvent.getType());
        mouseDownRegistration = addDomHandler(this, MouseDownEvent.getType());
        mouseOverRegistration = addDomHandler(this, MouseOverEvent.getType());
    }

    @Override
    protected void onDetach() {
        mouseUpRegistration.removeHandler();
        mouseDownRegistration.removeHandler();
        mouseOverRegistration.removeHandler();
        super.onDetach();
    }

    @Override
    public void onMouseUp(MouseUpEvent event) {
        if (event.getNativeButton() != NativeEvent.BUTTON_LEFT) {
            return;
        }

        if (moveRegistration != null) {
            Event.releaseCapture(getElement());
            moveRegistration.removeHandler();
            moveRegistration = null;
            keyDownHandler.removeHandler();
            keyDownHandler = null;
        }

        Widget w = (Widget) event.getSource();
        if (w == bottomspacer && monthEventMouseDown) {
            GWT.log("Mouse up over bottomspacer");

        } else if (clickedWidget instanceof MonthItemLabel  && monthEventMouseDown) {

            MonthItemLabel mel = (MonthItemLabel) clickedWidget;

            int endX = event.getClientX();
            int endY = event.getClientY();
            int xDiff = 0, yDiff = 0;
            if (startX != -1 && startY != -1) {
                xDiff = startX - endX;
                yDiff = startY - endY;
            }
            startX = -1;
            startY = -1;
            prevDayDiff = 0;
            prevWeekDiff = 0;

            if (xDiff < -3 || xDiff > 3 || yDiff < -3 || yDiff > 3) {
                itemMoved(movingItem);

            } else if (calendar.getItemClickListener() != null) {
                CalendarItem e = getItemByWidget(mel);

                if(e.isClickable())
                calendar.getItemClickListener().itemClick(e);
            }

            movingItem = null;

        } else if (w == this) {
            getMonthGrid().setSelectionReady();

        } else if (w instanceof Label && labelMouseDown) {
            if (calendar.getDateClickListener() != null) {
                calendar.getDateClickListener().dateClick(DateConstants.toRPCDate(date));
            }
        }

        monthEventMouseDown = false;
        labelMouseDown = false;
        clickedWidget = null;
    }

    @Override
    public void onMouseDown(MouseDownEvent event) {

        if (calendar.isDisabled() || event.getNativeButton() != NativeEvent.BUTTON_LEFT) {
            return;
        }

        Widget w = (Widget) event.getSource();
        clickedWidget = w;

        if (w instanceof MonthItemLabel) {

            // event clicks should be allowed even when read-only
            monthEventMouseDown = true;

            if (calendar.isItemMoveAllowed()
                    && ((MonthItemLabel)w).getCalendarItem().isMoveable()) {
                startCalendarItemDrag(event, (MonthItemLabel) w);
            }

        } else if (w == bottomspacer) {

            if (extended) {
                setLimitedCellHeight();
            } else {
                setUnlimitedCellHeight();
            }

            reDraw(true);

        } else if (w instanceof Label) {
            labelMouseDown = true;

        } else if (w == this && !extended) {

            MonthGrid grid = getMonthGrid();
            if (grid.isEnabled() && calendar.isRangeSelectAllowed()) {
                grid.setSelectionStart(this);
                grid.setSelectionEnd(this);
            }
        }

        event.stopPropagation();
        event.preventDefault();
    }

    @Override
    public void onMouseOver(MouseOverEvent event) {
        event.preventDefault();
        getMonthGrid().setSelectionEnd(this);
    }

    @Override
    public void onMouseMove(MouseMoveEvent event) {

        if (clickedWidget instanceof MonthItemLabel && !monthEventMouseDown
                || (startY < 0 && startX < 0)) {
            return;
        }

        if (calendar.isDisabled()) {
            Event.releaseCapture(getElement());
            monthEventMouseDown = false;
            startY = -1;
            startX = -1;
            return;
        }

        int currentY = event.getClientY();
        int currentX = event.getClientX();
        int moveY = (currentY - startY);
        int moveX = (currentX - startX);
        if ((moveY < 5 && moveY > -6) && (moveX < 5 && moveX > -6)) {
            return;
        }

        int dateCellWidth = getWidth();
        int dateCellHeigth = getHeigth();

        Element parent = getMonthGrid().getElement();
        int relativeX = event.getRelativeX(parent);
        int relativeY = event.getRelativeY(parent);
        int weekDiff;

        if (moveY > 0) {
            weekDiff = (startYrelative + moveY) / dateCellHeigth;
        } else {
            weekDiff = (moveY - (dateCellHeigth - startYrelative))
                    / dateCellHeigth;
        }

        int dayDiff;
        if (moveX >= 0) {
            dayDiff = (startXrelative + moveX) / dateCellWidth;
        } else {
            dayDiff = (moveX - (dateCellWidth - startXrelative))
                    / dateCellWidth;
        }
        // Check boundaries
        if (relativeY < 0
                || relativeY >= (calendar.getMonthGrid().getRowCount()
                        * dateCellHeigth)
                || relativeX < 0
                || relativeX >= (calendar.getMonthGrid().getColumnCount()
                        * dateCellWidth)) {
            return;
        }

        MonthItemLabel widget = (MonthItemLabel) clickedWidget;

        CalendarItem item = movingItem;
        if (item == null) {
            item = getItemByWidget(widget);
        }

        Date from = item.getStart();
        Date to = item.getEnd();

        long daysMs = dayDiff * DateConstants.DAYINMILLIS;
        long weeksMs = weekDiff * DateConstants.WEEKINMILLIS;

        setDates(item, from, to, weeksMs + daysMs, false);

        item.setStart(from);
        item.setEnd(to);

        if (widget.isTimeSpecificEvent()) {
            Date start = new Date();
            Date end = new Date();

            setDates(item, start, end, weeksMs + daysMs, true);

            item.setStartTime(start);
            item.setEndTime(end);

        } else {

            item.setStartTime(new Date(from.getTime()));
            item.setEndTime(new Date(to.getTime()));
        }

        updateDragPosition(widget, dayDiff, weekDiff);
    }

    private void setDates(CalendarItem e, Date start, Date end, long shift, boolean isDateTime) {

        Date currentStart;
        Date currentEnd;

        if (isDateTime) {
            currentStart = e.getStartTime();
            currentEnd = e.getEndTime();
        } else {
            currentStart = e.getStart();
            currentEnd = e.getEnd();
        }

        if (isDateTime) {
            start.setTime(dndSourceStartDateTime.getTime() + shift);
        } else {
            start.setTime(dndSourceDateFrom.getTime() + shift);
        }

        end.setTime((start.getTime() + currentEnd.getTime() - currentStart.getTime()));
    }

    private void itemMoved(CalendarItem calendarItem) {
        calendar.updateItemToMonthGrid(calendarItem);
        if (calendar.getItemMovedListener() != null) {
            calendar.getItemMovedListener().itemMoved(calendarItem);
        }
    }

    public void startCalendarItemDrag(MouseDownEvent event, final MonthItemLabel label) {

        moveRegistration = addMouseMoveHandler(this);
        startX = event.getClientX();
        startY = event.getClientY();
        startYrelative = event.getRelativeY(label.getParent().getElement())
                % getHeigth();
        startXrelative = event.getRelativeX(label.getParent().getElement())
                % getWidth();

        CalendarItem e = getItemByWidget(label);
        dndSourceDateFrom = (Date) e.getStart().clone();
        dndSourceDateTo = (Date) e.getEnd().clone();

        dndSourceStartDateTime = (Date) e.getStartTime().clone();
        dndSourceEndDateTime = (Date) e.getEndTime().clone();

        Event.setCapture(getElement());
        keyDownHandler = addKeyDownHandler(keyDownHandler -> {
            if (keyDownHandler.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) {
                cancelItemDrag(label);
            }
        });

        focus();

        GWT.log("Start drag");
    }

    protected void cancelItemDrag(MonthItemLabel label) {
        if (moveRegistration != null) {
            // reset position
            if (movingItem == null) {
                movingItem = getItemByWidget(label);
            }

            movingItem.setStart(dndSourceDateFrom);
            movingItem.setEnd(dndSourceDateTo);
            movingItem.setStartTime(dndSourceStartDateTime);
            movingItem.setEndTime(dndSourceEndDateTime);
            calendar.updateItemToMonthGrid(movingItem);

            // reset drag-related properties
            Event.releaseCapture(getElement());
            moveRegistration.removeHandler();
            moveRegistration = null;
            keyDownHandler.removeHandler();
            keyDownHandler = null;
            setFocus(false);
            monthEventMouseDown = false;
            startY = -1;
            startX = -1;
            movingItem = null;
            labelMouseDown = false;
            clickedWidget = null;
        }
    }

    public void updateDragPosition(MonthItemLabel label, int dayDiff, int weekDiff) {
        // Draw event to its new position only when position has changed
        if (dayDiff == prevDayDiff && weekDiff == prevWeekDiff) {
            return;
        }

        prevDayDiff = dayDiff;
        prevWeekDiff = weekDiff;

        if (movingItem == null) {
            movingItem = getItemByWidget(label);
        }

        calendar.updateItemToMonthGrid(movingItem);
    }

    public int getRow() {
        return row;
    }

    public int getCell() {
        return cell;
    }

    public int getHeigth() {
        return intHeight + BORDERPADDINGSIZE;
    }

    public int getWidth() {
        return getOffsetWidth() - BORDERPADDINGSIZE;
    }

    public void setToday(boolean today) {
        if (today) {
            addStyleDependentName("today");
        } else {
            removeStyleDependentName("today");
        }
    }

    public boolean removeItem(CalendarItem targetEvent, boolean reDrawImmediately) {
        int slot = targetEvent.getSlotIndex();
        if (slot < 0) {
            return false;
        }

        CalendarItem e = getCalendarItem(slot);
        if (targetEvent.equals(e)) {
            calendarItems[slot] = null;
            itemCount--;
            if (reDrawImmediately) {
                reDraw(movingItem == null);
            }
            return true;
        }
        return false;
    }

    private CalendarItem getItemByWidget(MonthItemLabel eventWidget) {
        int index = getWidgetIndex(eventWidget);
        return getCalendarItem(index - 1);
    }

    public CalendarItem getCalendarItem(int i) {
        return calendarItems[i];
    }

    public CalendarItem[] getCalendarItems() {
        return calendarItems;
    }

    public int getItemCount() {
        return itemCount;
    }

    public CalendarItem getMoveItem() {
        return movingItem;
    }

    public void addEmphasisStyle() {
        addStyleDependentName("dragemphasis");
    }

    public void removeEmphasisStyle() {
        removeStyleDependentName("dragemphasis");
    }

}
calendar-component-addon/src/main/java/org/vaadin/addon/calendar/client/ui/util/ItemDurationComparator.java
public class ItemDurationComparator extends AbstractEventComparator {

    public ItemDurationComparator(boolean ascending) {
        isAscending = ascending;
    }

    @Override
    public int doCompare(CalendarItem e1, CalendarItem e2) {
        int result = durationCompare(e1, e2, isAscending);
        if (result == 0) {
            return StartDateComparator.startDateCompare(e1, e2,
                    isAscending);
        }
        return result;
    }

    static int durationCompare(CalendarItem e1, CalendarItem e2,
                               boolean ascending) {
        int result = doDurationCompare(e1, e2);
        return ascending ? -result : result;
    }

    private static int doDurationCompare(CalendarItem e1,
                                         CalendarItem e2) {
        Long d1 = e1.getRangeInMilliseconds();
        Long d2 = e2.getRangeInMilliseconds();
        if (!d1.equals(0L) && !d2.equals(0L)) {
            return d2.compareTo(d1);
        }

        if (d2.equals(0L) && d1.equals(0L)) {
            return 0;
        } 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;
        }
        return d2.compareTo(d1);
    }

    private boolean isAscending;

}
calendar-component-addon/src/main/java/org/vaadin/addon/calendar/client/ui/schedule/WeeklyLongItems.java
public class WeeklyLongItems extends HorizontalPanel implements HasTooltipKey {

    private VCalendar calendar;

    private boolean undefinedWidth;

    public WeeklyLongItems(VCalendar calendar) {
        setStylePrimaryName("v-calendar-weekly-longevents");
        this.calendar = calendar;
    }

    public void addDate(Date d) {
        DateCellContainer dcc = new DateCellContainer();
        dcc.setDate(d);
        dcc.setCalendar(calendar);
        add(dcc);
    }

    public void setWidthPX(int width) {
        if (getWidgetCount() == 0) {
            return;
        }
        undefinedWidth = (width < 0);

        updateCellWidths();
    }

    public void addItems(List<CalendarItem> items) {
        for (CalendarItem item : items) {
            addItem(item);
        }
    }

    public void addItem(CalendarItem calendarItem) {
        updateItemSlot(calendarItem);

        int dateCount = getWidgetCount();
        Date from = calendarItem.getStart();
        Date to = calendarItem.getEnd();
        boolean started = false;
        for (int i = 0; i < dateCount; i++) {
            DateCellContainer dc = (DateCellContainer) getWidget(i);
            Date dcDate = dc.getDate();
            int comp = dcDate.compareTo(from);
            int comp2 = dcDate.compareTo(to);
            WeeklyLongItemsDateCell eventLabel = dc
                    .getDateCell(calendarItem.getSlotIndex());
            eventLabel.setStylePrimaryName("v-calendar-event");
            if (comp >= 0 && comp2 <= 0) {
                eventLabel.setItem(calendarItem);
                eventLabel.setCalendar(calendar);

                eventLabel.addStyleDependentName("all-day");
                if (comp == 0) {
                    eventLabel.addStyleDependentName("start");
                }
                if (comp2 == 0) {
                    eventLabel.addStyleDependentName("end");
                }
                if (!started && comp > 0) {
                    eventLabel.addStyleDependentName("continued-from");
                } else if (i == (dateCount - 1)) {
                    eventLabel.addStyleDependentName("continued-to");
                }
                final String extraStyle = calendarItem.getStyleName();
                if (extraStyle != null && extraStyle.length() > 0) {
                    eventLabel.addStyleDependentName(extraStyle + "-all-day");
                }
                if (!started) {
                    if (calendar.isItemCaptionAsHtml()) {
                        eventLabel.setHTML(calendarItem.getCaption());
                    } else {
                        eventLabel.setText(calendarItem.getCaption());
                    }
                    started = true;
                }
            }
        }
    }

    private void updateItemSlot(CalendarItem e) {
        boolean foundFreeSlot = false;
        int slot = 0;
        while (!foundFreeSlot) {
            if (isSlotFree(slot, e.getStart(), e.getEnd())) {
                e.setSlotIndex(slot);
                foundFreeSlot = true;

            } else {
                slot++;
            }
        }
    }

    private boolean isSlotFree(int slot, Date start, Date end) {
        int dateCount = getWidgetCount();

        // Go over all dates this week
        for (int i = 0; i < dateCount; i++) {
            DateCellContainer dc = (DateCellContainer) getWidget(i);
            Date dcDate = dc.getDate();
            int comp = dcDate.compareTo(start);
            int comp2 = dcDate.compareTo(end);

            // check if the date is in the range we need
            if (comp >= 0 && comp2 <= 0) {

                // check if the slot is taken
                if (dc.hasEvent(slot)) {
                    return false;
                }
            }
        }

        return true;
    }

    public void updateCellWidths() {
        int cells = getWidgetCount();
        if (cells <= 0) {
            return;
        }

        for (int i = 0; i < cells; i++) {
            DateCellContainer dc = (DateCellContainer) getWidget(i);

            if (undefinedWidth) {

                // if width is undefined, use the width of the first cell
                // otherwise use distributed sizes

                dc.setWidth(calendar.getWeekGrid().getDateCellWidth()
                        - calendar.getWeekGrid().getDateSlotBorder() + "px");

            } else {
                dc.setWidth(calendar.getWeekGrid().getDateCellWidths()[i]
                        + calendar.getWeekGrid().getDateSlotBorder() + "px");
            }
        }
    }

    @Override
    public String getTooltipKey() {
        return null;
    }
}
calendar-component-addon/src/main/java/org/vaadin/addon/calendar/client/ui/schedule/SimpleWeekToolbar.java
public class SimpleWeekToolbar extends FlexTable implements ClickHandler {
    private int height;
    private VCalendar calendar;
    private boolean isHeightUndefined;

    public SimpleWeekToolbar(VCalendar parent) {
        calendar = parent;
        setCellSpacing(0);
        setCellPadding(0);
        setStyleName("v-calendar-week-numbers");
    }

    public void addWeek(int week, int year) {
        WeekLabel l = new WeekLabel(week + "", week, year);
        l.addClickHandler(this);
        int rowCount = getRowCount();
        insertRow(rowCount);
        setWidget(rowCount, 0, l);
        updateCellHeights();
    }

    public void updateCellHeights() {
        if (!isHeightUndefined()) {
            int rowCount = getRowCount();
            if (rowCount == 0) {
                return;
            }
            int cellheight = (height / rowCount) - 1;
            int remainder = height % rowCount;
            if (cellheight < 0) {
                cellheight = 0;
            }
            for (int i = 0; i < rowCount; i++) {
                if (remainder > 0) {
                    getWidget(i, 0).setHeight(cellheight + 1 + "px");
                } else {
                    getWidget(i, 0).setHeight(cellheight + "px");
                }
                getWidget(i, 0).getElement().getStyle()
                        .setProperty("lineHeight", cellheight + "px");
                remainder--;
            }
        } else {
            for (int i = 0; i < getRowCount(); i++) {
                getWidget(i, 0).setHeight("");
                getWidget(i, 0).getElement().getStyle()
                        .setProperty("lineHeight", "");
            }
        }
    }

    public void setHeightPX(int intHeight) {
        setHeightUndefined(intHeight == -1);
        height = intHeight;
        updateCellHeights();
    }

    public boolean isHeightUndefined() {
        return isHeightUndefined;
    }

    public void setHeightUndefined(boolean isHeightUndefined) {
        this.isHeightUndefined = isHeightUndefined;

        if (isHeightUndefined) {
            addStyleDependentName("Vsized");

        } else {
            removeStyleDependentName("Vsized");
        }
    }

    @Override
    public void onClick(ClickEvent event) {
        WeekLabel wl = (WeekLabel) event.getSource();
        if (calendar.getWeekClickListener() != null) {
            calendar.getWeekClickListener()
                    .weekClick(wl.getYear() + "w" + wl.getWeek());
        }
    }
}
calendar-component-addon/src/main/java/org/vaadin/addon/calendar/client/ui/schedule/dd/CalendarDropHandler.java
public abstract class CalendarDropHandler extends VAbstractDropHandler {

    protected final CalendarConnector calendarConnector;

    /**
     * Constructor
     *
     * @param connector
     *            The connector of the calendar
     */
    public CalendarDropHandler(CalendarConnector connector) {
        calendarConnector = connector;
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler#getConnector()
     */
    @Override
    public CalendarConnector getConnector() {
        return calendarConnector;
    }

    /*
     * (non-Javadoc)
     *
     * @see com.vaadin.terminal.gwt.client.ui.dd.VDropHandler#
     * getApplicationConnection ()
     */
    @Override
    public ApplicationConnection getApplicationConnection() {
        return calendarConnector.getClient();
    }

}
calendar-component-addon/src/main/java/org/vaadin/addon/calendar/client/ui/schedule/SelectionRange.java
public class SelectionRange implements Serializable {

    /**
     * start minutes
     */
    public int sMin = 0;

    /**
     * end minutes
     */
    public int eMin = 0;

    /**
     * start date
     */
    public CalDate s;

    /**
     * end date
     */
    public CalDate e;

    public SelectionRange() { super(); }

    public void setStartDay(CalDate startDay) {
        s = startDay;
    }

    public void setEndDay(CalDate endDay) {
        e = endDay;
    }

    public CalDate getStartDay() {
        return s;
    }

    public CalDate getEndDay() {
        return e;
    }
}
calendar-component-addon/src/main/java/org/vaadin/addon/calendar/client/DateConstants.java
@SuppressWarnings({"deprecation"})
public class DateConstants implements Serializable {

    public static final String TIME_FORMAT_PATTERN = "HH:mm:ss";
    public static final String DATE_FORMAT_PATTERN = "yyyy-MM-dd";
    public static final String ACTION_DATE_TIME_FORMAT_PATTERN = DATE_FORMAT_PATTERN + " " + TIME_FORMAT_PATTERN;

    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;

    public static final int DAYINMINUTES = 24 * 60;
    public static final int HOURINMINUTES = 60;

    public static Date toClientDate(CalDate date) {
        return new Date(date.y -1900, date.m -1, date.d, 0, 0, 0);
    }

    public static Date toClientDateTime(CalDate date) {
        return new Date(date.y -1900, date.m -1, date.d, date.t.h, date.t.m, date.t.s);
    }

    public static CalDate toRPCDateTime(Date date) {
        return new CalDate(date.getYear() + 1900, date.getMonth() + 1, date.getDate(),
                new CalTime(date.getHours(), date.getMinutes(), date.getSeconds()));
    }

    public static CalDate toRPCDate(Date date) {
        return new CalDate(date.getYear() + 1900, date.getMonth() + 1, date.getDate());
    }

    public static CalDate toRPCDate(int year, int month, int day) {
        return new CalDate(year + 1900, month + 1, day);
    }

}
calendar-component-addon/src/main/java/org/vaadin/addon/calendar/client/CalendarState.java
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 = 1;
    public int scroll = 0;
    public CalDate now;
    public List<Day> days;
    public List<Item> items;
    public List<Action> actions;
    public boolean itemCaptionAsHtml;

    public ItemSortOrder itemSortOrder = ItemSortOrder.DURATION_DESC;

    /**
     * Defines sort strategy for items in calendar month view and week view. In
     * month view items will be sorted from top to bottom using the order in
     * day cell. In week view items inside same day will be sorted from left to
     * right using the order if their intervals are overlapping.
     * <p>
     * <ul>
     * <li>{@code UNSORTED} means no sort. Events will be in the order provided
     * by com.vaadin.ui.components.calendar.event.CalendarItemProvider.
     * <li>{@code START_DATE_DESC} means descending sort by items start date
     * (earlier event are shown first).
     * <li>{@code DURATION_DESC} means descending sort by duration (longer event
     * are shown first).
     * <li>{@code START_DATE_ASC} means ascending sort by items start date
     * (later event are shown first).
     * <li>{@code DURATION_ASC} means ascending sort by duration (shorter event
     * are shown first).
     * 
     * </ul>
     */
    public enum ItemSortOrder {
        UNSORTED, START_DATE_DESC, START_DATE_ASC, DURATION_DESC, DURATION_ASC;
    }

    public static class Day implements java.io.Serializable {
        public CalDate date;
        public String localizedDateFormat;
        public int dayOfWeek;
        public int week;
        public int yearOfWeek;
        public Set<SlotStyle> slotStyles;
    }

    public static class SlotStyle implements Serializable {
        public long slotStart;
        public String styleName;
    }

    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 Item 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;
        public boolean moveable;
        public boolean resizeable;
        public boolean clickable;
        public String dateCaptionFormat;
    }
}
calendar-component-addon/src/main/java/org/vaadin/addon/calendar/client/ui/util/StartDateComparator.java
public class StartDateComparator extends AbstractEventComparator {

    public StartDateComparator(boolean ascending) {
        isAscending = ascending;
    }

    @Override
    public int doCompare(CalendarItem e1, CalendarItem e2) {
        int result = startDateCompare(e1, e2, isAscending);
        if (result == 0) {
            // show a longer event after a shorter event
            return ItemDurationComparator.durationCompare(e1, e2,
                    isAscending);
        }
        return result;
    }

    static int startDateCompare(CalendarItem e1, CalendarItem e2,
                                       boolean ascending) {
        int result = e1.getStartTime().compareTo(e2.getStartTime());
        return ascending ? -result : result;
    }

    private boolean isAscending;
}
calendar-component-addon/src/main/java/org/vaadin/addon/calendar/client/ui/schedule/CalDate.java
public class CalDate implements Serializable {

    public int y;
    public int m;
    public int d;

    public CalTime t;

    public CalDate() { super(); }

    public CalDate(int year, int month, int day) {
        this.y = year;
        this.m = month;
        this.d = day;
    }

    public CalDate(int year, int month, int day, CalTime time) {
        this.y = year;
        this.m = month;
        this.d = day;
        this.t = time;
    }
}
calendar-component-addon/src/main/java/org/vaadin/addon/calendar/client/ui/schedule/CalendarDay.java
public class CalendarDay {

    private Date date;
    private String localizedDateFormat;
    private int dayOfWeek;
    private int week;
    private int yearOfWeek;
    private Map<Long, CalTimeSlot> styledSlots;

    public CalendarDay(Date date, String localizedDateFormat, int dayOfWeek, int week, int yearOfWeek, Set<CalendarState.SlotStyle> slotStyles) {
        super();
        this.date = date;
        this.localizedDateFormat = localizedDateFormat;
        this.dayOfWeek = dayOfWeek;
        this.week = week;
        this.yearOfWeek = yearOfWeek;

        this.styledSlots = new HashMap<>();
        for (CalendarState.SlotStyle slot : slotStyles) {
            styledSlots.put(slot.slotStart, new CalTimeSlot(slot.slotStart, slot.styleName));
        }
    }

    public Date getDate() {
        return date;
    }

    public String getLocalizedDateFormat() {
        return localizedDateFormat;
    }

    public int getDayOfWeek() {
        return dayOfWeek;
    }

    public int getWeek() {
        return week;
    }

    public int getYearOfWeek() {
        return yearOfWeek;
    }

    public Map<Long, CalTimeSlot> getStyledSlots() {
        return styledSlots;
    }
}


Question: import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;
import org.vaadin.addon.calendar.client.CalendarState;
import org.vaadin.addon.calendar.client.DateConstants;
import org.vaadin.addon.calendar.client.ui.schedule.CalDate;
import org.vaadin.addon.calendar.client.ui.schedule.CalendarDay;
import org.vaadin.addon.calendar.client.ui.schedule.CalendarItem;
import org.vaadin.addon.calendar.client.ui.schedule.DayToolbar;
import org.vaadin.addon.calendar.client.ui.schedule.MonthGrid;
import org.vaadin.addon.calendar.client.ui.schedule.SelectionRange;
import org.vaadin.addon.calendar.client.ui.schedule.SimpleDayCell;
import org.vaadin.addon.calendar.client.ui.schedule.SimpleDayToolbar;
import org.vaadin.addon.calendar.client.ui.schedule.SimpleWeekToolbar;
import org.vaadin.addon.calendar.client.ui.schedule.WeekGrid;
import org.vaadin.addon.calendar.client.ui.schedule.WeeklyLongItems;
import org.vaadin.addon.calendar.client.ui.schedule.dd.CalendarDropHandler;
import org.vaadin.addon.calendar.client.ui.util.ItemDurationComparator;
import org.vaadin.addon.calendar.client.ui.util.StartDateComparator;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ContextMenuEvent;
import com.google.gwt.i18n.client.DateTimeFormat;
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.dd.VHasDropHandler;
        for (SimpleDayCell sdc : dayCells) {
            sdc.addItem(e);
        }
        for (SimpleDayCell sdc : timeCells) {
            sdc.addItem(e);
        }

        if (renderImmediately) {
            reDrawAllMonthItems(!itemMoving);
        }
    }

    /*
     * 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 isItemInDayWithTime(Date from, Date to, Date date, Date endTime, boolean isAllDay) {
        return (isAllDay || !(to.getDay() == date.getDay()
                && from.getDay() != to.getDay() && isMidnight(endTime)));
    }

    private void updateItemSlotIndex(CalendarItem item, List<SimpleDayCell> cells) {
        if (cells.isEmpty()) {
            return;
        }

        if (item.getSlotIndex() == -1) {
            // Update slot index
            int newSlot = -1;
            for (SimpleDayCell sdc : cells) {
                int slot = sdc.getItemCount();
                if (slot > newSlot) {
                    newSlot = slot;
                }
            }
            newSlot++;

            for (int i = 0; i < newSlot; i++) {
                // check for empty slot
                if (isSlotEmpty(item, i, cells)) {
                    newSlot = i;
                    break;
                }
            }
            item.setSlotIndex(newSlot);
        }
    }

    private void reDrawAllMonthItems(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(CalendarItem addedEvent, int slotIndex, List<SimpleDayCell> cells) {
        for (SimpleDayCell sdc : cells) {
            CalendarItem item = sdc.getCalendarItem(slotIndex);
            if (item != null && !item.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 removeMonthItem(CalendarItem 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.removeItem(target, repaintImmediately);
                }
            }
        }
    }

    /**
     * Updates an event in the month grid
     *
     * @param changedItem
     *            The event that has changed
     */
    public void updateItemToMonthGrid(CalendarItem changedItem) {
        removeMonthItem(changedItem, true);
        changedItem.setSlotIndex(-1);
        addItemToMonthGrid(changedItem, true);
    }

    /**
     * Sort the items by current sort order
     *
     * @param items
     *            The items to sort
     * @return An array where the items has been sorted
     */
    public CalendarItem[] sortItems(Collection<CalendarItem> items) {
        if (!CalendarState.ItemSortOrder.UNSORTED.equals(itemSortOrder)) {

            CalendarItem[] sorted = items.toArray(new CalendarItem[items.size()]);
            switch (itemSortOrder) {
                case START_DATE_ASC:
Answer:                     Arrays.sort(sorted, new StartDateComparator(true));