/* * Copyright 2000-2016 Vaadin Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.vaadin.v7.ui; import java.lang.reflect.Method; import java.text.DateFormat; import java.text.DateFormatSymbols; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.EventListener; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TimeZone; import java.util.logging.Level; import java.util.logging.Logger; import org.jsoup.nodes.Attributes; import org.jsoup.nodes.Element; import com.vaadin.event.Action; import com.vaadin.event.Action.Handler; import com.vaadin.event.dd.DropHandler; import com.vaadin.event.dd.DropTarget; import com.vaadin.event.dd.TargetDetails; import com.vaadin.server.KeyMapper; import com.vaadin.server.PaintException; import com.vaadin.server.PaintTarget; import com.vaadin.ui.AbstractComponent; import com.vaadin.ui.LegacyComponent; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; import com.vaadin.v7.data.Container; import com.vaadin.v7.data.util.BeanItemContainer; import com.vaadin.v7.shared.ui.calendar.CalendarEventId; import com.vaadin.v7.shared.ui.calendar.CalendarServerRpc; import com.vaadin.v7.shared.ui.calendar.CalendarState; import com.vaadin.v7.shared.ui.calendar.CalendarState.EventSortOrder; import com.vaadin.v7.shared.ui.calendar.DateConstants; import com.vaadin.v7.ui.components.calendar.CalendarComponentEvent; import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents; import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.BackwardEvent; import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.BackwardHandler; import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.DateClickEvent; import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.DateClickHandler; import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.EventClick; import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.EventClickHandler; import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.EventMoveHandler; import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.EventResize; import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.EventResizeHandler; import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.ForwardEvent; import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.ForwardHandler; import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.MoveEvent; import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.RangeSelectEvent; import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.RangeSelectHandler; import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.WeekClick; import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.WeekClickHandler; import com.vaadin.v7.ui.components.calendar.CalendarDateRange; import com.vaadin.v7.ui.components.calendar.CalendarTargetDetails; import com.vaadin.v7.ui.components.calendar.ContainerEventProvider; import com.vaadin.v7.ui.components.calendar.event.BasicEventProvider; import com.vaadin.v7.ui.components.calendar.event.CalendarEditableEventProvider; import com.vaadin.v7.ui.components.calendar.event.CalendarEvent; import com.vaadin.v7.ui.components.calendar.event.CalendarEvent.EventChangeEvent; import com.vaadin.v7.ui.components.calendar.event.CalendarEvent.EventChangeListener; import com.vaadin.v7.ui.components.calendar.event.CalendarEventProvider; import com.vaadin.v7.ui.components.calendar.handler.BasicBackwardHandler; import com.vaadin.v7.ui.components.calendar.handler.BasicDateClickHandler; import com.vaadin.v7.ui.components.calendar.handler.BasicEventMoveHandler; import com.vaadin.v7.ui.components.calendar.handler.BasicEventResizeHandler; import com.vaadin.v7.ui.components.calendar.handler.BasicForwardHandler; import com.vaadin.v7.ui.components.calendar.handler.BasicWeekClickHandler; /** * <p> * Vaadin Calendar is for visualizing events in a calendar. Calendar events can * be visualized in the variable length view depending on the start and end * dates. * </p> * * <li>You can set the viewable date range with the {@link #setStartDate(Date)} * and {@link #setEndDate(Date)} methods. Calendar has a default date range of * one week</li> * * <li>Calendar has two kind of views: monthly and weekly view</li> * * <li>If date range is seven days or shorter, the weekly view is used.</li> * * <li>Calendar queries its events by using a {@link CalendarEventProvider}. By * default, a {@link BasicEventProvider} is used.</li> * * @since 7.1 * @author Vaadin Ltd. * * @deprecated As of 8.0, no replacement available. */ @SuppressWarnings("serial") @Deprecated public class Calendar extends AbstractLegacyComponent implements CalendarComponentEvents.NavigationNotifier, CalendarComponentEvents.EventMoveNotifier, CalendarComponentEvents.RangeSelectNotifier, CalendarComponentEvents.EventResizeNotifier, CalendarEventProvider.EventSetChangeListener, DropTarget, CalendarEditableEventProvider, Action.Container, LegacyComponent { /** * Calendar can use either 12 hours clock or 24 hours clock. */ @Deprecated public enum TimeFormat { Format12H(), Format24H(); } /** Defines currently active format for time. 12H/24H. */ protected TimeFormat currentTimeFormat; /** Internal calendar data source. */ protected java.util.Calendar currentCalendar = java.util.Calendar .getInstance(); /** Defines the component's active time zone. */ protected TimeZone timezone; /** Defines the calendar's date range starting point. */ protected Date startDate = null; /** Defines the calendar's date range ending point. */ protected Date endDate = null; /** Event provider. */ private CalendarEventProvider calendarEventProvider; /** * Internal buffer for the events that are retrieved from the event * provider. */ protected List<CalendarEvent> events; /** Date format that will be used in the UIDL for dates. */ protected DateFormat df_date = new SimpleDateFormat("yyyy-MM-dd"); /** Time format that will be used in the UIDL for time. */ protected DateFormat df_time = new SimpleDateFormat("HH:mm:ss"); /** Date format that will be used in the UIDL for both date and time. */ protected DateFormat df_date_time = new SimpleDateFormat( DateConstants.CLIENT_DATE_FORMAT + "-" + DateConstants.CLIENT_TIME_FORMAT); /** * Week view's scroll position. Client sends updates to this value so that * scroll position wont reset all the time. */ private int scrollTop = 0; /** Caption format for the weekly view */ private String weeklyCaptionFormat = null; /** Map from event ids to event handlers */ private final Map<String, EventListener> handlers; /** * Drop Handler for Vaadin DD. By default null. */ private DropHandler dropHandler; /** * First day to show for a week */ private int firstDay = 1; /** * Last day to show for a week */ private int lastDay = 7; /** * First hour to show for a day */ private int firstHour = 0; /** * Last hour to show for a day */ private int lastHour = 23; /** * List of action handlers. */ private LinkedList<Action.Handler> actionHandlers = null; /** * Action mapper. */ private KeyMapper<Action> actionMapper = null; /** * */ private CalendarServerRpcImpl rpc = new CalendarServerRpcImpl(); /** * The cached minimum minute shown when using * {@link #autoScaleVisibleHoursOfDay()}. */ private Integer minTimeInMinutes; /** * The cached maximum minute shown when using * {@link #autoScaleVisibleHoursOfDay()}. */ private Integer maxTimeInMinutes; private Integer customFirstDayOfWeek; /** * Returns the logger for the calendar */ protected Logger getLogger() { return Logger.getLogger(Calendar.class.getName()); } /** * Construct a Vaadin Calendar with a BasicEventProvider and no caption. * Default date range is one week. */ public Calendar() { this(null, new BasicEventProvider()); } /** * Construct a Vaadin Calendar with a BasicEventProvider and the provided * caption. Default date range is one week. * * @param caption */ public Calendar(String caption) { this(caption, new BasicEventProvider()); } /** * <p> * Construct a Vaadin Calendar with event provider. Event provider is * obligatory, because calendar component will query active events through * it. * </p> * * <p> * By default, Vaadin Calendar will show dates from the start of the current * week to the end of the current week. Use {@link #setStartDate(Date)} and * {@link #setEndDate(Date)} to change this. * </p> * * @param eventProvider * Event provider, cannot be null. */ public Calendar(CalendarEventProvider eventProvider) { this(null, eventProvider); } /** * <p> * Construct a Vaadin Calendar with event provider and a caption. Event * provider is obligatory, because calendar component will query active * events through it. * </p> * * <p> * By default, Vaadin Calendar will show dates from the start of the current * week to the end of the current week. Use {@link #setStartDate(Date)} and * {@link #setEndDate(Date)} to change this. * </p> * * @param eventProvider * Event provider, cannot be null. */ // this is the constructor every other constructor calls public Calendar(String caption, CalendarEventProvider eventProvider) { registerRpc(rpc); setCaption(caption); handlers = new HashMap<String, EventListener>(); setDefaultHandlers(); currentCalendar.setTime(new Date()); setEventProvider(eventProvider); getState().firstDayOfWeek = firstDay; getState().lastVisibleDayOfWeek = lastDay; getState().firstHourOfDay = firstHour; getState().lastHourOfDay = lastHour; setTimeFormat(null); } @Override public CalendarState getState() { return (CalendarState) super.getState(); } @Override protected CalendarState getState(boolean markAsDirty) { return (CalendarState) super.getState(markAsDirty); } @Override public void beforeClientResponse(boolean initial) { super.beforeClientResponse(initial); initCalendarWithLocale(); getState().format24H = TimeFormat.Format24H == getTimeFormat(); setupDaysAndActions(); setupCalendarEvents(); rpc.scroll(scrollTop); } /** * Set all the wanted default handlers here. This is always called after * constructing this object. All other events have default handlers except * range and event click. */ protected void setDefaultHandlers() { setHandler(new BasicBackwardHandler()); setHandler(new BasicForwardHandler()); setHandler(new BasicWeekClickHandler()); setHandler(new BasicDateClickHandler()); setHandler(new BasicEventMoveHandler()); setHandler(new BasicEventResizeHandler()); } /** * Gets the calendar's start date. * * @return First visible date. */ public Date getStartDate() { if (startDate == null) { currentCalendar.set(java.util.Calendar.MILLISECOND, 0); currentCalendar.set(java.util.Calendar.SECOND, 0); currentCalendar.set(java.util.Calendar.MINUTE, 0); currentCalendar.set(java.util.Calendar.HOUR_OF_DAY, 0); currentCalendar.set(java.util.Calendar.DAY_OF_WEEK, currentCalendar.getFirstDayOfWeek()); return currentCalendar.getTime(); } return startDate; } /** * Sets start date for the calendar. This and {@link #setEndDate(Date)} * control the range of dates visible on the component. The default range is * one week. * * @param date * First visible date to show. */ public void setStartDate(Date date) { if (!date.equals(startDate)) { startDate = date; markAsDirty(); } } /** * Gets the calendar's end date. * * @return Last visible date. */ public Date getEndDate() { if (endDate == null) { currentCalendar.set(java.util.Calendar.MILLISECOND, 0); currentCalendar.set(java.util.Calendar.SECOND, 59); currentCalendar.set(java.util.Calendar.MINUTE, 59); currentCalendar.set(java.util.Calendar.HOUR_OF_DAY, 23); currentCalendar.set(java.util.Calendar.DAY_OF_WEEK, currentCalendar.getFirstDayOfWeek() + 6); return currentCalendar.getTime(); } return endDate; } /** * Sets end date for the calendar. Starting from startDate, only six weeks * will be shown if duration to endDate is longer than six weeks. * * This and {@link #setStartDate(Date)} control the range of dates visible * on the component. The default range is one week. * * @param date * Last visible date to show. */ public void setEndDate(Date date) { if (startDate != null && startDate.after(date)) { startDate = (Date) date.clone(); markAsDirty(); } else if (!date.equals(endDate)) { endDate = date; markAsDirty(); } } /** * Sets the locale to be used in the Calendar component. * * @see AbstractComponent#setLocale(java.util.Locale) */ @Override public void setLocale(Locale newLocale) { super.setLocale(newLocale); initCalendarWithLocale(); } /** * Initialize the java calendar instance with the current locale and * timezone. */ private void initCalendarWithLocale() { if (timezone != null) { currentCalendar = java.util.Calendar.getInstance(timezone, getLocale()); } else { currentCalendar = java.util.Calendar.getInstance(getLocale()); } if (customFirstDayOfWeek != null) { currentCalendar.setFirstDayOfWeek(customFirstDayOfWeek); } } private void setupCalendarEvents() { int durationInDays = (int) ((endDate.getTime() - startDate.getTime()) / DateConstants.DAYINMILLIS); durationInDays++; if (durationInDays > 60) { throw new RuntimeException( "Daterange is too big (max 60) = " + durationInDays); } Date firstDateToShow = expandStartDate(startDate, durationInDays > 7); Date lastDateToShow = expandEndDate(endDate, durationInDays > 7); currentCalendar.setTime(firstDateToShow); events = getEventProvider().getEvents(firstDateToShow, lastDateToShow); cacheMinMaxTimeOfDay(events); List<CalendarState.Event> calendarStateEvents = new ArrayList<CalendarState.Event>(); if (events != null) { for (int i = 0; i < events.size(); i++) { CalendarEvent e = events.get(i); CalendarState.Event event = new CalendarState.Event(); event.index = i; event.caption = e.getCaption() == null ? "" : e.getCaption(); event.dateFrom = df_date.format(e.getStart()); event.dateTo = df_date.format(e.getEnd()); event.timeFrom = df_time.format(e.getStart()); event.timeTo = df_time.format(e.getEnd()); event.description = e.getDescription() == null ? "" : e.getDescription(); event.styleName = e.getStyleName() == null ? "" : e.getStyleName(); event.allDay = e.isAllDay(); calendarStateEvents.add(event); } } getState().events = calendarStateEvents; } /** * Stores the minimum and maximum time-of-day in minutes for the events. * * @param events * A list of calendar events. Can be <code>null</code>. */ private void cacheMinMaxTimeOfDay(List<CalendarEvent> events) { minTimeInMinutes = null; maxTimeInMinutes = null; if (events != null) { for (CalendarEvent event : events) { int minuteOfDayStart = getMinuteOfDay(event.getStart()); int minuteOfDayEnd = getMinuteOfDay(event.getEnd()); if (minTimeInMinutes == null) { minTimeInMinutes = minuteOfDayStart; maxTimeInMinutes = minuteOfDayEnd; } else { if (minuteOfDayStart < minTimeInMinutes) { minTimeInMinutes = minuteOfDayStart; } if (minuteOfDayEnd > maxTimeInMinutes) { maxTimeInMinutes = minuteOfDayEnd; } } } } } private static int getMinuteOfDay(Date date) { java.util.Calendar calendar = java.util.Calendar.getInstance(); calendar.setTime(date); return calendar.get(java.util.Calendar.HOUR_OF_DAY) * 60 + calendar.get(java.util.Calendar.MINUTE); } /** * Sets the displayed start and end time to fit all current events that were * retrieved from the last call to getEvents(). * <p> * If no events exist, nothing happens. * <p> * <b>NOTE: triggering this method only does this once for the current * events - events that are not in the current visible range, are * ignored!</b> * * @see #setFirstVisibleHourOfDay(int) * @see #setLastVisibleHourOfDay(int) */ public void autoScaleVisibleHoursOfDay() { if (minTimeInMinutes != null) { setFirstVisibleHourOfDay(minTimeInMinutes / 60); // Do not show the final hour if last minute ends on it setLastVisibleHourOfDay((maxTimeInMinutes - 1) / 60); } } /** * Resets the {@link #setFirstVisibleHourOfDay(int)} and * {@link #setLastVisibleHourOfDay(int)} to the default values, 0 and 23 * respectively. * * @see #autoScaleVisibleHoursOfDay() * @see #setFirstVisibleHourOfDay(int) * @see #setLastVisibleHourOfDay(int) */ public void resetVisibleHoursOfDay() { setFirstVisibleHourOfDay(0); setLastVisibleHourOfDay(23); } private void setupDaysAndActions() { // Make sure we have a up-to-date locale initCalendarWithLocale(); CalendarState state = getState(); state.firstDayOfWeek = currentCalendar.getFirstDayOfWeek(); // If only one is null, throw exception // If both are null, set defaults if (startDate == null ^ endDate == null) { String message = "Schedule cannot be painted without a proper date range.\n"; if (startDate == null) { throw new IllegalStateException(message + "You must set a start date using setStartDate(Date)."); } else { throw new IllegalStateException(message + "You must set an end date using setEndDate(Date)."); } } else if (startDate == null && endDate == null) { // set defaults startDate = getStartDate(); endDate = getEndDate(); } int durationInDays = (int) ((endDate.getTime() - startDate.getTime()) / DateConstants.DAYINMILLIS); durationInDays++; if (durationInDays > 60) { throw new RuntimeException( "Daterange is too big (max 60) = " + durationInDays); } state.dayNames = getDayNamesShort(); state.monthNames = getMonthNamesShort(); // Use same timezone in all dates this component handles. // Show "now"-marker in browser within given timezone. Date now = new Date(); currentCalendar.setTime(now); now = currentCalendar.getTime(); // Reset time zones for custom date formats df_date.setTimeZone(currentCalendar.getTimeZone()); df_time.setTimeZone(currentCalendar.getTimeZone()); state.now = df_date.format(now) + " " + df_time.format(now); Date firstDateToShow = expandStartDate(startDate, durationInDays > 7); Date lastDateToShow = expandEndDate(endDate, durationInDays > 7); currentCalendar.setTime(firstDateToShow); DateFormat weeklyCaptionFormatter = getWeeklyCaptionFormatter(); weeklyCaptionFormatter.setTimeZone(currentCalendar.getTimeZone()); Map<CalendarDateRange, Set<Action>> actionMap = new HashMap<CalendarDateRange, Set<Action>>(); List<CalendarState.Day> days = new ArrayList<CalendarState.Day>(); // Send all dates to client from server. This // approach was taken because gwt doesn't // support date localization properly. while (currentCalendar.getTime().compareTo(lastDateToShow) < 1) { final Date date = currentCalendar.getTime(); final CalendarState.Day day = new CalendarState.Day(); day.date = df_date.format(date); day.localizedDateFormat = weeklyCaptionFormatter.format(date); day.dayOfWeek = getDowByLocale(currentCalendar); day.week = getWeek(currentCalendar); day.yearOfWeek = getYearOfWeek(currentCalendar); days.add(day); // Get actions for a specific date if (actionHandlers != null) { for (Action.Handler actionHandler : actionHandlers) { // Create calendar which omits time GregorianCalendar cal = new GregorianCalendar(getTimeZone(), getLocale()); cal.clear(); cal.set(currentCalendar.get(java.util.Calendar.YEAR), currentCalendar.get(java.util.Calendar.MONTH), currentCalendar.get(java.util.Calendar.DATE)); // Get day start and end times Date start = cal.getTime(); cal.add(java.util.Calendar.DATE, 1); cal.add(java.util.Calendar.SECOND, -1); Date end = cal.getTime(); boolean monthView = durationInDays > 7; /** * If in day or week view add actions for each half-an-hour. * If in month view add actions for each day */ if (monthView) { setActionsForDay(actionMap, start, end, actionHandler); } else { setActionsForEachHalfHour(actionMap, start, end, actionHandler); } } } currentCalendar.add(java.util.Calendar.DATE, 1); } state.days = days; state.actions = createActionsList(actionMap); } private int getWeek(java.util.Calendar calendar) { return calendar.get(java.util.Calendar.WEEK_OF_YEAR); } private int getYearOfWeek(java.util.Calendar calendar) { // Would use calendar.getWeekYear() but it's only available since 1.7. int week = getWeek(calendar); int month = calendar.get(java.util.Calendar.MONTH); int year = calendar.get(java.util.Calendar.YEAR); if (week == 1 && month == java.util.Calendar.DECEMBER) { return year + 1; } return year; } private void setActionsForEachHalfHour( Map<CalendarDateRange, Set<Action>> actionMap, Date start, Date end, Action.Handler actionHandler) { GregorianCalendar cal = new GregorianCalendar(getTimeZone(), getLocale()); cal.setTime(start); while (cal.getTime().before(end)) { Date s = cal.getTime(); cal.add(java.util.Calendar.MINUTE, 30); Date e = cal.getTime(); CalendarDateRange range = new CalendarDateRange(s, e, getTimeZone()); Action[] actions = actionHandler.getActions(range, this); if (actions != null) { Set<Action> actionSet = new LinkedHashSet<Action>( Arrays.asList(actions)); actionMap.put(range, actionSet); } } } private void setActionsForDay(Map<CalendarDateRange, Set<Action>> actionMap, Date start, Date end, Action.Handler actionHandler) { CalendarDateRange range = new CalendarDateRange(start, end, getTimeZone()); Action[] actions = actionHandler.getActions(range, this); if (actions != null) { Set<Action> actionSet = new LinkedHashSet<Action>( Arrays.asList(actions)); actionMap.put(range, actionSet); } } private List<CalendarState.Action> createActionsList( Map<CalendarDateRange, Set<Action>> actionMap) { if (actionMap.isEmpty()) { return null; } List<CalendarState.Action> calendarActions = new ArrayList<CalendarState.Action>(); SimpleDateFormat formatter = new SimpleDateFormat( DateConstants.ACTION_DATE_FORMAT_PATTERN); formatter.setTimeZone(getTimeZone()); for (Entry<CalendarDateRange, Set<Action>> entry : actionMap .entrySet()) { CalendarDateRange range = entry.getKey(); Set<Action> actions = entry.getValue(); for (Action action : actions) { String key = actionMapper.key(action); CalendarState.Action calendarAction = new CalendarState.Action(); calendarAction.actionKey = key; calendarAction.caption = action.getCaption(); setResource(key, action.getIcon()); calendarAction.iconKey = key; calendarAction.startDate = formatter.format(range.getStart()); calendarAction.endDate = formatter.format(range.getEnd()); calendarActions.add(calendarAction); } } return calendarActions; } /** * Gets currently active time format. Value is either TimeFormat.Format12H * or TimeFormat.Format24H. * * @return TimeFormat Format for the time. */ public TimeFormat getTimeFormat() { if (currentTimeFormat == null) { SimpleDateFormat f; if (getLocale() == null) { f = (SimpleDateFormat) SimpleDateFormat .getTimeInstance(SimpleDateFormat.SHORT); } else { f = (SimpleDateFormat) SimpleDateFormat .getTimeInstance(SimpleDateFormat.SHORT, getLocale()); } String p = f.toPattern(); if (p.indexOf("HH") != -1 || p.indexOf("H") != -1) { return TimeFormat.Format24H; } return TimeFormat.Format12H; } return currentTimeFormat; } /** * Example: <code>setTimeFormat(TimeFormat.Format12H);</code></br> * Set to null, if you want the format being defined by the locale. * * @param format * Set 12h or 24h format. Default is defined by the locale. */ public void setTimeFormat(TimeFormat format) { currentTimeFormat = format; markAsDirty(); } /** * Returns a time zone that is currently used by this component. * * @return Component's Time zone */ public TimeZone getTimeZone() { if (timezone == null) { return currentCalendar.getTimeZone(); } return timezone; } /** * Set time zone that this component will use. Null value sets the default * time zone. * * @param zone * Time zone to use */ public void setTimeZone(TimeZone zone) { timezone = zone; if (!currentCalendar.getTimeZone().equals(zone)) { if (zone == null) { zone = TimeZone.getDefault(); } currentCalendar.setTimeZone(zone); df_date_time.setTimeZone(zone); markAsDirty(); } } /** * Get the internally used Calendar instance. This is the currently used * instance of {@link java.util.Calendar} but is bound to change during the * lifetime of the component. * * @return the currently used java calendar */ public java.util.Calendar getInternalCalendar() { return currentCalendar; } /** * <p> * This method restricts the weekdays that are shown. This affects both the * monthly and the weekly view. The general contract is that <b>firstDay < * lastDay</b>. * </p> * * <p> * Note that this only affects the rendering process. Events are still * requested by the dates set by {@link #setStartDate(Date)} and * {@link #setEndDate(Date)}. * </p> * * @param firstDay * the first day of the week to show, between 1 and 7 */ public void setFirstVisibleDayOfWeek(int firstDay) { if (this.firstDay != firstDay && firstDay >= 1 && firstDay <= 7 && getLastVisibleDayOfWeek() >= firstDay) { this.firstDay = firstDay; getState().firstVisibleDayOfWeek = firstDay; } } /** * Get the first visible day of the week. Returns the weekdays as integers * represented by {@link java.util.Calendar#DAY_OF_WEEK} * * @return An integer representing the week day according to * {@link java.util.Calendar#DAY_OF_WEEK} */ public int getFirstVisibleDayOfWeek() { return firstDay; } /** * <p> * This method restricts the weekdays that are shown. This affects both the * monthly and the weekly view. The general contract is that <b>firstDay < * lastDay</b>. * </p> * * <p> * Note that this only affects the rendering process. Events are still * requested by the dates set by {@link #setStartDate(Date)} and * {@link #setEndDate(Date)}. * </p> * * @param lastDay * the first day of the week to show, between 1 and 7 */ public void setLastVisibleDayOfWeek(int lastDay) { if (this.lastDay != lastDay && lastDay >= 1 && lastDay <= 7 && getFirstVisibleDayOfWeek() <= lastDay) { this.lastDay = lastDay; getState().lastVisibleDayOfWeek = lastDay; } } /** * Get the last visible day of the week. Returns the weekdays as integers * represented by {@link java.util.Calendar#DAY_OF_WEEK} * * @return An integer representing the week day according to * {@link java.util.Calendar#DAY_OF_WEEK} */ public int getLastVisibleDayOfWeek() { return lastDay; } /** * <p> * This method restricts the hours that are shown per day. This affects the * weekly view. The general contract is that <b>firstHour < lastHour</b>. * </p> * * <p> * Note that this only affects the rendering process. Events are still * requested by the dates set by {@link #setStartDate(Date)} and * {@link #setEndDate(Date)}. * </p> * You can use {@link #autoScaleVisibleHoursOfDay()} for automatic scaling * of the visible hours based on current events. * * @param firstHour * the first hour of the day to show, between 0 and 23 * @see #autoScaleVisibleHoursOfDay() */ public void setFirstVisibleHourOfDay(int firstHour) { if (this.firstHour != firstHour && firstHour >= 0 && firstHour <= 23 && firstHour <= getLastVisibleHourOfDay()) { this.firstHour = firstHour; getState().firstHourOfDay = firstHour; } } /** * Returns the first visible hour in the week view. Returns the hour using a * 24h time format * */ public int getFirstVisibleHourOfDay() { return firstHour; } /** * This method restricts the hours that are shown per day. This affects the * weekly view. The general contract is that <b>firstHour < lastHour</b>. * <p> * Note that this only affects the rendering process. Events are still * requested by the dates set by {@link #setStartDate(Date)} and * {@link #setEndDate(Date)}. * <p> * You can use {@link #autoScaleVisibleHoursOfDay()} for automatic scaling * of the visible hours based on current events. * * @param lastHour * the first hour of the day to show, between 0 and 23 * @see #autoScaleVisibleHoursOfDay() */ public void setLastVisibleHourOfDay(int lastHour) { if (this.lastHour != lastHour && lastHour >= 0 && lastHour <= 23 && lastHour >= getFirstVisibleHourOfDay()) { this.lastHour = lastHour; getState().lastHourOfDay = lastHour; } } /** * Returns the last visible hour in the week view. Returns the hour using a * 24h time format * */ public int getLastVisibleHourOfDay() { return lastHour; } /** * Gets the date caption format for the weekly view. * * @return The pattern used in caption of dates in weekly view. */ public String getWeeklyCaptionFormat() { return weeklyCaptionFormat; } /** * Sets custom date format for the weekly view. This is the caption of the * date. Format could be like "mmm MM/dd". * * @param dateFormatPattern * The date caption pattern. */ public void setWeeklyCaptionFormat(String dateFormatPattern) { if (weeklyCaptionFormat == null && dateFormatPattern != null || weeklyCaptionFormat != null && !weeklyCaptionFormat.equals(dateFormatPattern)) { weeklyCaptionFormat = dateFormatPattern; markAsDirty(); } } /** * Sets sort order for events. By default sort order is * {@link EventSortOrder#DURATION_DESC}. * * @param order * sort strategy for events */ public void setEventSortOrder(EventSortOrder order) { if (order == null) { getState().eventSortOrder = EventSortOrder.DURATION_DESC; } else { getState().eventSortOrder = EventSortOrder.values()[order .ordinal()]; } } /** * Returns sort order for events. * * @return currently active sort strategy */ public EventSortOrder getEventSortOrder() { EventSortOrder order = getState(false).eventSortOrder; if (order == null) { return EventSortOrder.DURATION_DESC; } else { return order; } } private DateFormat getWeeklyCaptionFormatter() { if (weeklyCaptionFormat != null) { return new SimpleDateFormat(weeklyCaptionFormat, getLocale()); } else { return SimpleDateFormat.getDateInstance(SimpleDateFormat.SHORT, getLocale()); } } /** * Get the day of week by the given calendar and its locale * * @param calendar * The calendar to use * @return */ private static int getDowByLocale(java.util.Calendar calendar) { int fow = calendar.get(java.util.Calendar.DAY_OF_WEEK); // monday first if (calendar.getFirstDayOfWeek() == java.util.Calendar.MONDAY) { fow = fow == java.util.Calendar.SUNDAY ? 7 : fow - 1; } return fow; } /** * Is the user allowed to trigger events which alters the events * * @return true if the client is allowed to send changes to server * @see #isEventClickAllowed() */ protected boolean isClientChangeAllowed() { return !isReadOnly(); } /** * Is the user allowed to trigger click events. Returns {@code true} by * default. Subclass can override this method to disallow firing event * clicks got from the client side. * * @return true if the client is allowed to click events * @see #isClientChangeAllowed() * @deprecated As of 7.4, override {@link #fireEventClick(Integer)} instead. */ @Deprecated protected boolean isEventClickAllowed() { return true; } /** * Fires an event when the user selecing moving forward/backward in the * calendar. * * @param forward * True if the calendar moved forward else backward is assumed. */ protected void fireNavigationEvent(boolean forward) { if (forward) { fireEvent(new ForwardEvent(this)); } else { fireEvent(new BackwardEvent(this)); } } /** * Fires an event move event to all server side move listerners * * @param index * The index of the event in the events list * @param newFromDatetime * The changed from date time */ protected void fireEventMove(int index, Date newFromDatetime) { MoveEvent event = new MoveEvent(this, events.get(index), newFromDatetime); if (calendarEventProvider instanceof EventMoveHandler) { // Notify event provider if it is an event move handler ((EventMoveHandler) calendarEventProvider).eventMove(event); } // Notify event move handler attached by using the // setHandler(EventMoveHandler) method fireEvent(event); } /** * Fires event when a week was clicked in the calendar. * * @param week * The week that was clicked * @param year * The year of the week */ protected void fireWeekClick(int week, int year) { fireEvent(new WeekClick(this, week, year)); } /** * Fires event when a date was clicked in the calendar. Uses an existing * event from the event cache. * * @param index * The index of the event in the event cache. */ protected void fireEventClick(Integer index) { fireEvent(new EventClick(this, events.get(index))); } /** * Fires event when a date was clicked in the calendar. Creates a new event * for the date and passes it to the listener. * * @param date * The date and time that was clicked */ protected void fireDateClick(Date date) { fireEvent(new DateClickEvent(this, date)); } /** * Fires an event range selected event. The event is fired when a user * highlights an area in the calendar. The highlighted areas start and end * dates are returned as arguments. * * @param from * The start date and time of the highlighted area * @param to * The end date and time of the highlighted area * @param monthlyMode * Is the calendar in monthly mode */ protected void fireRangeSelect(Date from, Date to, boolean monthlyMode) { fireEvent(new RangeSelectEvent(this, from, to, monthlyMode)); } /** * Fires an event resize event. The event is fired when a user resizes the * event in the calendar causing the time range of the event to increase or * decrease. The new start and end times are returned as arguments to this * method. * * @param index * The index of the event in the event cache * @param startTime * The new start date and time of the event * @param endTime * The new end date and time of the event */ protected void fireEventResize(int index, Date startTime, Date endTime) { EventResize event = new EventResize(this, events.get(index), startTime, endTime); if (calendarEventProvider instanceof EventResizeHandler) { // Notify event provider if it is an event resize handler ((EventResizeHandler) calendarEventProvider).eventResize(event); } // Notify event resize handler attached by using the // setHandler(EventMoveHandler) method fireEvent(event); } /** * Localized display names for week days starting from sunday. Returned * array's length is always 7. * * @return Array of localized weekday names. */ protected String[] getDayNamesShort() { DateFormatSymbols s = new DateFormatSymbols(getLocale()); return Arrays.copyOfRange(s.getWeekdays(), 1, 8); } /** * Localized display names for months starting from January. Returned * array's length is always 12. * * @return Array of localized month names. */ protected String[] getMonthNamesShort() { DateFormatSymbols s = new DateFormatSymbols(getLocale()); return Arrays.copyOf(s.getShortMonths(), 12); } /** * Gets a date that is first day in the week that target given date belongs * to. * * @param date * Target date * @return Date that is first date in same week that given date is. */ protected Date getFirstDateForWeek(Date date) { int firstDayOfWeek = currentCalendar.getFirstDayOfWeek(); currentCalendar.setTime(date); while (firstDayOfWeek != currentCalendar .get(java.util.Calendar.DAY_OF_WEEK)) { currentCalendar.add(java.util.Calendar.DATE, -1); } return currentCalendar.getTime(); } /** * Gets a date that is last day in the week that target given date belongs * to. * * @param date * Target date * @return Date that is last date in same week that given date is. */ protected Date getLastDateForWeek(Date date) { currentCalendar.setTime(date); currentCalendar.add(java.util.Calendar.DATE, 1); int firstDayOfWeek = currentCalendar.getFirstDayOfWeek(); // Roll to weeks last day using firstdayofweek. Roll until FDofW is // found and then roll back one day. while (firstDayOfWeek != currentCalendar .get(java.util.Calendar.DAY_OF_WEEK)) { currentCalendar.add(java.util.Calendar.DATE, 1); } currentCalendar.add(java.util.Calendar.DATE, -1); return currentCalendar.getTime(); } /** * Calculates the end time of the day using the given calendar and date * * @param date * @param calendar * the calendar instance to be used in the calculation. The given * instance is unchanged in this operation. * @return the given date, with time set to the end of the day */ private static Date getEndOfDay(java.util.Calendar calendar, Date date) { java.util.Calendar calendarClone = (java.util.Calendar) calendar .clone(); calendarClone.setTime(date); calendarClone.set(java.util.Calendar.MILLISECOND, calendarClone.getActualMaximum(java.util.Calendar.MILLISECOND)); calendarClone.set(java.util.Calendar.SECOND, calendarClone.getActualMaximum(java.util.Calendar.SECOND)); calendarClone.set(java.util.Calendar.MINUTE, calendarClone.getActualMaximum(java.util.Calendar.MINUTE)); calendarClone.set(java.util.Calendar.HOUR, calendarClone.getActualMaximum(java.util.Calendar.HOUR)); calendarClone.set(java.util.Calendar.HOUR_OF_DAY, calendarClone.getActualMaximum(java.util.Calendar.HOUR_OF_DAY)); return calendarClone.getTime(); } /** * Calculates the end time of the day using the given calendar and date * * @param date * @param calendar * the calendar instance to be used in the calculation. The given * instance is unchanged in this operation. * @return the given date, with time set to the end of the day */ private static Date getStartOfDay(java.util.Calendar calendar, Date date) { java.util.Calendar calendarClone = (java.util.Calendar) calendar .clone(); calendarClone.setTime(date); calendarClone.set(java.util.Calendar.MILLISECOND, 0); calendarClone.set(java.util.Calendar.SECOND, 0); calendarClone.set(java.util.Calendar.MINUTE, 0); calendarClone.set(java.util.Calendar.HOUR, 0); calendarClone.set(java.util.Calendar.HOUR_OF_DAY, 0); return calendarClone.getTime(); } /** * Finds the first day of the week and returns a day representing the start * of that day * * @param start * The actual date * @param expandToFullWeek * Should the returned date be moved to the start of the week * @return If expandToFullWeek is set then it returns the first day of the * week, else it returns a clone of the actual date with the time * set to the start of the day */ protected Date expandStartDate(Date start, boolean expandToFullWeek) { // If the duration is more than week, use monthly view and get startweek // and endweek. Example if views daterange is from tuesday to next weeks // wednesday->expand to monday to nextweeks sunday. If firstdayofweek = // monday if (expandToFullWeek) { start = getFirstDateForWeek(start); } else { start = (Date) start.clone(); } // Always expand to the start of the first day to the end of the last // day start = getStartOfDay(currentCalendar, start); return start; } /** * Finds the last day of the week and returns a day representing the end of * that day * * @param end * The actual date * @param expandToFullWeek * Should the returned date be moved to the end of the week * @return If expandToFullWeek is set then it returns the last day of the * week, else it returns a clone of the actual date with the time * set to the end of the day */ protected Date expandEndDate(Date end, boolean expandToFullWeek) { // If the duration is more than week, use monthly view and get startweek // and endweek. Example if views daterange is from tuesday to next weeks // wednesday->expand to monday to nextweeks sunday. If firstdayofweek = // monday if (expandToFullWeek) { end = getLastDateForWeek(end); } else { end = (Date) end.clone(); } // Always expand to the start of the first day to the end of the last // day end = getEndOfDay(currentCalendar, end); return end; } /** * Set the {@link CalendarEventProvider} to be used with this calendar. The * EventProvider is used to query for events to show, and must be non-null. * By default a {@link BasicEventProvider} is used. * * @param calendarEventProvider * the calendarEventProvider to set. Cannot be null. */ public void setEventProvider(CalendarEventProvider calendarEventProvider) { if (calendarEventProvider == null) { throw new IllegalArgumentException( "Calendar event provider cannot be null"); } // remove old listener if (getEventProvider() instanceof EventSetChangeNotifier) { ((EventSetChangeNotifier) getEventProvider()) .removeEventSetChangeListener(this); } this.calendarEventProvider = calendarEventProvider; // add new listener if (calendarEventProvider instanceof EventSetChangeNotifier) { ((EventSetChangeNotifier) calendarEventProvider) .addEventSetChangeListener(this); } } /** * @return the {@link CalendarEventProvider} currently used */ public CalendarEventProvider getEventProvider() { return calendarEventProvider; } @Override public void eventSetChange(EventSetChangeEvent changeEvent) { // sanity check if (calendarEventProvider == changeEvent.getProvider()) { markAsDirty(); } } /** * Set the handler for the given type information. Mirrors * {@link #addListener(String, Class, Object, Method) addListener} from * AbstractComponent * * @param eventId * A unique id for the event. Usually one of * {@link CalendarEventId} * @param eventType * The class of the event, most likely a subclass of * {@link CalendarComponentEvent} * @param listener * A listener that listens to the given event * @param listenerMethod * The method on the lister to call when the event is triggered */ protected void setHandler(String eventId, Class<?> eventType, EventListener listener, Method listenerMethod) { if (handlers.get(eventId) != null) { removeListener(eventId, eventType, handlers.get(eventId)); handlers.remove(eventId); } if (listener != null) { addListener(eventId, eventType, listener, listenerMethod); handlers.put(eventId, listener); } } @Override public void setHandler(ForwardHandler listener) { setHandler(ForwardEvent.EVENT_ID, ForwardEvent.class, listener, ForwardHandler.forwardMethod); } @Override public void setHandler(BackwardHandler listener) { setHandler(BackwardEvent.EVENT_ID, BackwardEvent.class, listener, BackwardHandler.backwardMethod); } @Override public void setHandler(DateClickHandler listener) { setHandler(DateClickEvent.EVENT_ID, DateClickEvent.class, listener, DateClickHandler.dateClickMethod); } @Override public void setHandler(EventClickHandler listener) { setHandler(EventClick.EVENT_ID, EventClick.class, listener, EventClickHandler.eventClickMethod); } @Override public void setHandler(WeekClickHandler listener) { setHandler(WeekClick.EVENT_ID, WeekClick.class, listener, WeekClickHandler.weekClickMethod); } @Override public void setHandler(EventResizeHandler listener) { setHandler(EventResize.EVENT_ID, EventResize.class, listener, EventResizeHandler.eventResizeMethod); } @Override public void setHandler(RangeSelectHandler listener) { setHandler(RangeSelectEvent.EVENT_ID, RangeSelectEvent.class, listener, RangeSelectHandler.rangeSelectMethod); } @Override public void setHandler(EventMoveHandler listener) { setHandler(MoveEvent.EVENT_ID, MoveEvent.class, listener, EventMoveHandler.eventMoveMethod); } @Override public EventListener getHandler(String eventId) { return handlers.get(eventId); } /** * Get the currently active drop handler */ @Override public DropHandler getDropHandler() { return dropHandler; } /** * Set the drop handler for the calendar See {@link DropHandler} for * implementation details. * * @param dropHandler * The drop handler to set */ public void setDropHandler(DropHandler dropHandler) { this.dropHandler = dropHandler; } @Override public TargetDetails translateDropTargetDetails( Map<String, Object> clientVariables) { Map<String, Object> serverVariables = new HashMap<String, Object>(); if (clientVariables.containsKey("dropSlotIndex")) { int slotIndex = (Integer) clientVariables.get("dropSlotIndex"); int dayIndex = (Integer) clientVariables.get("dropDayIndex"); currentCalendar.setTime(getStartOfDay(currentCalendar, startDate)); currentCalendar.add(java.util.Calendar.DATE, dayIndex); // change this if slot length is modified currentCalendar.add(java.util.Calendar.MINUTE, slotIndex * 30); serverVariables.put("dropTime", currentCalendar.getTime()); } else { int dayIndex = (Integer) clientVariables.get("dropDayIndex"); currentCalendar.setTime(expandStartDate(startDate, true)); currentCalendar.add(java.util.Calendar.DATE, dayIndex); serverVariables.put("dropDay", currentCalendar.getTime()); } serverVariables.put("mouseEvent", clientVariables.get("mouseEvent")); CalendarTargetDetails td = new CalendarTargetDetails(serverVariables, this); td.setHasDropTime(clientVariables.containsKey("dropSlotIndex")); return td; } /** * Sets a container as a data source for the events in the calendar. * Equivalent for doing * <code>Calendar.setEventProvider(new ContainerEventProvider(container))</code> * * Use this method if you are adding a container which uses the default * property ids like {@link BeanItemContainer} for instance. If you are * using custom properties instead use * {@link Calendar#setContainerDataSource(Container.Indexed, Object, Object, Object, Object, Object)} * * Please note that the container must be sorted by date! * * @param container * The container to use as a datasource */ public void setContainerDataSource(Container.Indexed container) { ContainerEventProvider provider = new ContainerEventProvider(container); provider.addEventSetChangeListener( new CalendarEventProvider.EventSetChangeListener() { @Override public void eventSetChange( EventSetChangeEvent changeEvent) { // Repaint if events change markAsDirty(); } }); provider.addEventChangeListener(new EventChangeListener() { @Override public void eventChange(EventChangeEvent changeEvent) { // Repaint if event changes markAsDirty(); } }); setEventProvider(provider); } /** * Sets a container as a data source for the events in the calendar. * Equivalent for doing * <code>Calendar.setEventProvider(new ContainerEventProvider(container))</code> * * Please note that the container must be sorted by date! * * @param container * The container to use as a data source * @param captionProperty * The property that has the caption, null if no caption property * is present * @param descriptionProperty * The property that has the description, null if no description * property is present * @param startDateProperty * The property that has the starting date * @param endDateProperty * The property that has the ending date * @param styleNameProperty * The property that has the stylename, null if no stylname * property is present */ public void setContainerDataSource(Container.Indexed container, Object captionProperty, Object descriptionProperty, Object startDateProperty, Object endDateProperty, Object styleNameProperty) { ContainerEventProvider provider = new ContainerEventProvider(container); provider.setCaptionProperty(captionProperty); provider.setDescriptionProperty(descriptionProperty); provider.setStartDateProperty(startDateProperty); provider.setEndDateProperty(endDateProperty); provider.setStyleNameProperty(styleNameProperty); provider.addEventSetChangeListener( new CalendarEventProvider.EventSetChangeListener() { @Override public void eventSetChange( EventSetChangeEvent changeEvent) { // Repaint if events change markAsDirty(); } }); provider.addEventChangeListener(new EventChangeListener() { @Override public void eventChange(EventChangeEvent changeEvent) { // Repaint if event changes markAsDirty(); } }); setEventProvider(provider); } @Override public List<CalendarEvent> getEvents(Date startDate, Date endDate) { List<CalendarEvent> events = getEventProvider().getEvents(startDate, endDate); cacheMinMaxTimeOfDay(events); return events; } @Override public void addEvent(CalendarEvent event) { if (getEventProvider() instanceof CalendarEditableEventProvider) { CalendarEditableEventProvider provider = (CalendarEditableEventProvider) getEventProvider(); provider.addEvent(event); markAsDirty(); } else { throw new UnsupportedOperationException( "Event provider does not support adding events"); } } @Override public void removeEvent(CalendarEvent event) { if (getEventProvider() instanceof CalendarEditableEventProvider) { CalendarEditableEventProvider provider = (CalendarEditableEventProvider) getEventProvider(); provider.removeEvent(event); markAsDirty(); } else { throw new UnsupportedOperationException( "Event provider does not support removing events"); } } /** * Adds an action handler to the calender that handles event produced by the * context menu. * * <p> * The {@link Handler#getActions(Object, Object)} parameters depend on what * view the Calendar is in: * <ul> * <li>If the Calendar is in <i>Day or Week View</i> then the target * parameter will be a {@link CalendarDateRange} with a range of * half-an-hour. The {@link Handler#getActions(Object, Object)} method will * be called once per half-hour slot.</li> * <li>If the Calendar is in <i>Month View</i> then the target parameter * will be a {@link CalendarDateRange} with a range of one day. The * {@link Handler#getActions(Object, Object)} will be called once for each * day. * </ul> * The Dates passed into the {@link CalendarDateRange} are in the same * timezone as the calendar is. * </p> * * <p> * The {@link Handler#handleAction(Action, Object, Object)} parameters * depend on what the context menu is called upon: * <ul> * <li>If the context menu is called upon an event then the target parameter * is the event, i.e. instanceof {@link CalendarEvent}</li> * <li>If the context menu is called upon an empty slot then the target is a * {@link Date} representing that slot * </ul> * </p> */ @Override public void addActionHandler(Handler actionHandler) { if (actionHandler != null) { if (actionHandlers == null) { actionHandlers = new LinkedList<Handler>(); actionMapper = new KeyMapper<Action>(); } if (!actionHandlers.contains(actionHandler)) { actionHandlers.add(actionHandler); markAsDirty(); } } } /** * Is the calendar in a mode where all days of the month is shown * * @return Returns true if calendar is in monthly mode and false if it is in * weekly mode */ public boolean isMonthlyMode() { CalendarState state = getState(false); if (state.days != null) { return state.days.size() > 7; } else { // Default mode return true; } } @Override public void removeActionHandler(Handler actionHandler) { if (actionHandlers != null && actionHandlers.contains(actionHandler)) { actionHandlers.remove(actionHandler); if (actionHandlers.isEmpty()) { actionHandlers = null; actionMapper = null; } markAsDirty(); } } private class CalendarServerRpcImpl implements CalendarServerRpc { @Override public void eventMove(int eventIndex, String newDate) { if (!isClientChangeAllowed()) { return; } if (newDate != null) { try { Date d = df_date_time.parse(newDate); if (eventIndex >= 0 && eventIndex < events.size() && events.get(eventIndex) != null) { fireEventMove(eventIndex, d); } } catch (ParseException e) { getLogger().log(Level.WARNING, e.getMessage()); } } } @Override public void rangeSelect(String range) { if (!isClientChangeAllowed()) { return; } if (range != null && range.length() > 14 && range.contains("TO")) { String[] dates = range.split("TO"); try { Date d1 = df_date.parse(dates[0]); Date d2 = df_date.parse(dates[1]); fireRangeSelect(d1, d2, true); } catch (ParseException e) { // NOP } } else if (range != null && range.length() > 12 && range.contains(":")) { String[] dates = range.split(":"); if (dates.length == 3) { try { Date d = df_date.parse(dates[0]); currentCalendar.setTime(d); int startMinutes = Integer.parseInt(dates[1]); int endMinutes = Integer.parseInt(dates[2]); currentCalendar.add(java.util.Calendar.MINUTE, startMinutes); Date start = currentCalendar.getTime(); currentCalendar.add(java.util.Calendar.MINUTE, endMinutes - startMinutes); Date end = currentCalendar.getTime(); fireRangeSelect(start, end, false); } catch (ParseException e) { // NOP } catch (NumberFormatException e) { // NOP } } } } @Override public void forward() { fireEvent(new ForwardEvent(Calendar.this)); } @Override public void backward() { fireEvent(new BackwardEvent(Calendar.this)); } @Override public void dateClick(String date) { if (date != null && date.length() > 6) { try { Date d = df_date.parse(date); fireDateClick(d); } catch (ParseException e) { } } } @Override public void weekClick(String event) { if (event.length() > 0 && event.contains("w")) { String[] splitted = event.split("w"); if (splitted.length == 2) { try { int yr = Integer.parseInt(splitted[0]); int week = Integer.parseInt(splitted[1]); fireWeekClick(week, yr); } catch (NumberFormatException e) { // NOP } } } } @Override public void eventClick(int eventIndex) { if (!isEventClickAllowed()) { return; } if (eventIndex >= 0 && eventIndex < events.size() && events.get(eventIndex) != null) { fireEventClick(eventIndex); } } @Override public void eventResize(int eventIndex, String newStartDate, String newEndDate) { if (!isClientChangeAllowed()) { return; } if (newStartDate != null && !"".equals(newStartDate) && newEndDate != null && !"".equals(newEndDate)) { try { Date newStartTime = df_date_time.parse(newStartDate); Date newEndTime = df_date_time.parse(newEndDate); fireEventResize(eventIndex, newStartTime, newEndTime); } catch (ParseException e) { // NOOP } } } @Override public void scroll(int scrollPosition) { scrollTop = scrollPosition; markAsDirty(); } @Override public void actionOnEmptyCell(String actionKey, String startDate, String endDate) { Action action = actionMapper.get(actionKey); SimpleDateFormat formatter = new SimpleDateFormat( DateConstants.ACTION_DATE_FORMAT_PATTERN); formatter.setTimeZone(getTimeZone()); try { Date start = formatter.parse(startDate); for (Action.Handler ah : actionHandlers) { ah.handleAction(action, Calendar.this, start); } } catch (ParseException e) { getLogger().log(Level.WARNING, "Could not parse action date string"); } } @Override public void actionOnEvent(String actionKey, String startDate, String endDate, int eventIndex) { Action action = actionMapper.get(actionKey); SimpleDateFormat formatter = new SimpleDateFormat( DateConstants.ACTION_DATE_FORMAT_PATTERN); formatter.setTimeZone(getTimeZone()); for (Action.Handler ah : actionHandlers) { ah.handleAction(action, Calendar.this, events.get(eventIndex)); } } } @Override public void changeVariables(Object source, Map<String, Object> variables) { /* * Only defined to fulfill the LegacyComponent interface used for * calendar drag & drop. No implementation required. */ } @Override public void paintContent(PaintTarget target) throws PaintException { if (dropHandler != null) { dropHandler.getAcceptCriterion().paint(target); } } /** * Sets whether the event captions are rendered as HTML. * <p> * If set to true, the captions are rendered in the browser as HTML and the * developer is responsible for ensuring no harmful HTML is used. If set to * false, the caption is rendered in the browser as plain text. * <p> * The default is false, i.e. to render that caption as plain text. * * @param captionAsHtml * true if the captions are rendered as HTML, false if rendered * as plain text */ public void setEventCaptionAsHtml(boolean eventCaptionAsHtml) { getState().eventCaptionAsHtml = eventCaptionAsHtml; } /** * Checks whether event captions are rendered as HTML * <p> * The default is false, i.e. to render that caption as plain text. * * @return true if the captions are rendered as HTML, false if rendered as * plain text */ public boolean isEventCaptionAsHtml() { return getState(false).eventCaptionAsHtml; } @Override public void readDesign(Element design, DesignContext designContext) { super.readDesign(design, designContext); Attributes attr = design.attributes(); if (design.hasAttr("time-format")) { setTimeFormat(TimeFormat.valueOf( "Format" + design.attr("time-format").toUpperCase())); } if (design.hasAttr("start-date")) { setStartDate(DesignAttributeHandler.readAttribute("start-date", attr, Date.class)); } if (design.hasAttr("end-date")) { setEndDate(DesignAttributeHandler.readAttribute("end-date", attr, Date.class)); } }; @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); if (currentTimeFormat != null) { design.attr("time-format", currentTimeFormat == TimeFormat.Format12H ? "12h" : "24h"); } if (startDate != null) { design.attr("start-date", df_date.format(getStartDate())); } if (endDate != null) { design.attr("end-date", df_date.format(getEndDate())); } if (!getTimeZone().equals(TimeZone.getDefault())) { design.attr("time-zone", getTimeZone().getID()); } } @Override protected Collection<String> getCustomAttributes() { Collection<String> customAttributes = super.getCustomAttributes(); customAttributes.add("time-format"); customAttributes.add("start-date"); customAttributes.add("end-date"); return customAttributes; } /** * Allow setting first day of week independent of Locale. Set to null if you * want first day of week being defined by the locale * * @since 7.6 * @param dayOfWeek * any of java.util.Calendar.SUNDAY..java.util.Calendar.SATURDAY * or null to revert to default first day of week by locale */ public void setFirstDayOfWeek(Integer dayOfWeek) { int minimalSupported = java.util.Calendar.SUNDAY; int maximalSupported = java.util.Calendar.SATURDAY; if (dayOfWeek != null && (dayOfWeek < minimalSupported || dayOfWeek > maximalSupported)) { throw new IllegalArgumentException(String.format( "Day of week must be between %s and %s. Actually received: %s", minimalSupported, maximalSupported, dayOfWeek)); } customFirstDayOfWeek = dayOfWeek; markAsDirty(); } }