/* * 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 org.vaadin.addon.calendar; import static java.time.DayOfWeek.MONDAY; import static java.time.DayOfWeek.SUNDAY; import static java.time.temporal.TemporalAdjusters.firstDayOfMonth; import static java.time.temporal.TemporalAdjusters.lastDayOfMonth; import java.lang.reflect.Method; import java.text.DateFormatSymbols; import java.text.SimpleDateFormat; import java.time.DayOfWeek; import java.time.Duration; import java.time.LocalDate; import java.time.LocalTime; import java.time.Month; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; import java.time.temporal.ChronoField; import java.time.temporal.ChronoUnit; import java.time.temporal.WeekFields; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.EventListener; import java.util.HashMap; import java.util.HashSet; 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.Objects; import java.util.Set; import java.util.logging.Logger; import org.jsoup.nodes.Attributes; import org.jsoup.nodes.Element; import org.vaadin.addon.calendar.client.CalendarEventId; import org.vaadin.addon.calendar.client.CalendarServerRpc; 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.CalTime; import org.vaadin.addon.calendar.client.ui.schedule.SelectionRange; import org.vaadin.addon.calendar.handler.BasicBackwardHandler; import org.vaadin.addon.calendar.handler.BasicDateClickHandler; import org.vaadin.addon.calendar.handler.BasicForwardHandler; import org.vaadin.addon.calendar.handler.BasicItemMoveHandler; import org.vaadin.addon.calendar.handler.BasicItemResizeHandler; import org.vaadin.addon.calendar.handler.BasicWeekClickHandler; import org.vaadin.addon.calendar.item.BasicItemProvider; import org.vaadin.addon.calendar.item.CalendarItem; import org.vaadin.addon.calendar.item.CalendarItemProvider; import org.vaadin.addon.calendar.item.EditableCalendarItem; import org.vaadin.addon.calendar.ui.CalendarComponentEvent; import org.vaadin.addon.calendar.ui.CalendarComponentEvents; import org.vaadin.addon.calendar.ui.CalendarDateRange; import org.vaadin.addon.calendar.ui.CalendarTargetDetails; import org.vaadin.addon.calendar.ui.WeeklyCaptionProvider; 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.shared.Registration; import com.vaadin.shared.ui.ContentMode; import com.vaadin.ui.AbstractComponent; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; /** * <p> * Vaadin Calendar is for visualizing items in a calendar. Calendar items 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(ZonedDateTime)} * and {@link #setEndDate(ZonedDateTime)} 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 items by using a {@link CalendarItemProvider}. By * default, a {@link BasicItemProvider} is used.</li> * * @since 7.1 * @author Vaadin Ltd. * */ @SuppressWarnings({"serial","unused"}) public class Calendar<ITEM extends EditableCalendarItem> extends AbstractComponent implements CalendarComponentEvents.NavigationNotifier, CalendarComponentEvents.ItemMoveNotifier, CalendarComponentEvents.RangeSelectNotifier, CalendarComponentEvents.ItemResizeNotifier, CalendarItemProvider.ItemSetChangedListener, DropTarget, Action.Container //,LegacyComponent //,CalendarItemProvider<ITEM> { /** * Calendar can use either 12 hours clock or 24 hours clock. */ public enum TimeFormat { Format12H(), Format24H() } /** Defines currently active format for time. 12H/24H. */ protected TimeFormat currentTimeFormat; /** Defines the component's active time zone. */ protected ZoneId zoneId = ZoneId.systemDefault(); /** Defines the calendar's date range starting point. */ protected ZonedDateTime startDate = null; /** Defines the calendar's date range ending point. */ protected ZonedDateTime endDate = null; /** Item provider. */ private CalendarItemProvider<ITEM> calendarItemProvider; /** * Internal buffer for the items that are retrieved from the item provider. */ protected List<? extends CalendarItem> items; /** Date format that will be used in the UIDL for dates. */ private final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern(DateConstants.DATE_FORMAT_PATTERN); /** Time format that will be used in the UIDL for time. */ private final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern(DateConstants.TIME_FORMAT_PATTERN); /** Time format that will be used in the UIDL for time. */ protected final DateTimeFormatter ACTION_DATE_TIME_FORMAT = DateTimeFormatter.ofPattern(DateConstants.ACTION_DATE_TIME_FORMAT_PATTERN); /** Caption format provuder for the weekly view */ private WeeklyCaptionProvider weeklyCaptionFormatProvider = date -> DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(getLocale()).format(date); /** Map from event ids to event handlers */ private final Map<String, Registration> 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<Handler> actionHandlers = null; /** * Action mapper. */ private KeyMapper<Action> actionMapper = null; /** * 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 DayOfWeek customFirstDayOfWeek; /** * A map with blocked timeslots.<br> * Contains a set with timestamp of starttimes. */ private final Map<LocalDate, Set<CalendarState.SlotStyle>> styledTimes = new HashMap<>(); /** * Initial date for all blocked times */ private final LocalDate allOverDate = LocalDate.ofEpochDay(0); /** * Returns the logger for the calendar */ protected Logger getLogger() { return Logger.getLogger(Calendar.class.getName()); } /** * Construct a Vaadin Calendar with a BasicItemProvider and no caption. * Default date range is one week. */ public Calendar() { this(null, null); } /** * Construct a Vaadin Calendar with a BasicItemProvider and the provided * caption. Default date range is one week. * * @param caption A caption */ public Calendar(String caption) { this(caption, null); } /** * <p> * Construct a Vaadin Calendar with event provider. Item provider is * obligatory, because calendar component will query active items 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(ZonedDateTime)} and * {@link #setEndDate(ZonedDateTime)} to change this. * </p> * * @param dataProvider * Item provider, cannot be null. */ public Calendar(CalendarItemProvider<ITEM> dataProvider) { this(null, dataProvider); } /** * <p> * Construct a Vaadin Calendar with item provider and a caption. Item * provider is obligatory, because calendar component will query active * items 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(ZonedDateTime)} and * {@link #setEndDate(ZonedDateTime)} to change this. * </p> * * @param caption * A caption * @param dataProvider * Item provider, cannot be null. */ // this is the constructor every other constructor calls public Calendar(String caption, CalendarItemProvider<ITEM> dataProvider) { registerRpc(new CalendarServerRpcImpl()); setCaption(caption); handlers = new HashMap<>(); setDefaultHandlers(); setDataProvider(dataProvider != null ? dataProvider : new BasicItemProvider()); getState().firstDayOfWeek = 1; getState().firstVisibleDayOfWeek = firstDay; getState().lastVisibleDayOfWeek = lastDay; getState().firstHourOfDay = firstHour; getState().lastHourOfDay = lastHour; setTimeFormat(null); withWeek(ZonedDateTime.now(getZoneId())); } @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); getState().format24H = TimeFormat.Format24H == getTimeFormat(); setupDaysAndActions(); setupCalendarItems(); } /** * Set the ContentMode * * @param contentMode The new content mode */ public void setContentMode(ContentMode contentMode) { getState().descriptionContentMode = Objects.isNull(contentMode) ? ContentMode.PREFORMATTED : contentMode; } /** * @return The content mode */ public ContentMode getContentMode() { return getState(false).descriptionContentMode; } /** * Set all the wanted default handlers here. This is always called after * constructing this object. All other items 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 BasicItemMoveHandler()); setHandler(new BasicItemResizeHandler()); } /** * Gets the calendar's start date. * * @return First visible date. */ public ZonedDateTime getStartDate() { if (startDate == null) { // A new datetime with components zone id startDate = ZonedDateTime.now(getZoneId()) // and first day in week .with(ChronoField.DAY_OF_WEEK, 1) // with a time of 00:00:00:000 .with(LocalTime.MIN); } return startDate; } /** * Sets start date for the calendar. This and {@link #setEndDate(ZonedDateTime)} * 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(ZonedDateTime date) { // reset all time information date = date.withZoneSameLocal(getZoneId()).with(LocalTime.MIN); startDate = date; markAsDirty(); } /** * Gets the calendar's end date. * * @return Last visible date. */ public ZonedDateTime getEndDate() { if (endDate == null) { endDate = ZonedDateTime.now(getZoneId()) // and last day of week .with(ChronoField.DAY_OF_WEEK, 7) // with a time of 23:59:59.999999999 .with(LocalTime.MAX); } 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(ZonedDateTime)} 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(ZonedDateTime date) { // reset all time information date = date.withZoneSameLocal(getZoneId()).with(LocalTime.MIN); // check start after end if (startDate != null && startDate.isAfter(date)) { startDate = date; markAsDirty(); } else // and end is not the same as current end if(endDate == null || !endDate.equals(date)) { endDate = date; markAsDirty(); } } /** * Sets the locale to be used in the Calendar component. * * @see AbstractComponent#setLocale(Locale) */ @Override public void setLocale(Locale newLocale) { super.setLocale(newLocale); markAsDirty(); } private void setupCalendarItems() { long durationInDays = Duration.between(startDate, endDate).toDays(); durationInDays++; if (durationInDays > 60) { throw new RuntimeException( "Daterange is too big (max 60) = " + durationInDays); } ZonedDateTime firstDateToShow = expandStartDate(startDate, durationInDays > 7); ZonedDateTime lastDateToShow = expandEndDate(endDate, durationInDays > 7); items = getDataProvider().getItems(firstDateToShow, lastDateToShow); cacheMinMaxTimeOfDay(items); List<CalendarState.Item> calendarStateItems = new ArrayList<>(); if (items != null) { for (int i = 0; i < items.size(); i++) { CalendarItem calItem = items.get(i); CalendarState.Item item = new CalendarState.Item(); item.index = i; item.caption = calItem.getCaption() == null ? "" : calItem.getCaption(); // XXX STRING FORMATTER yyyy-MM-dd item.dateFrom = DATE_FORMAT.format(calItem.getStart()); item.dateTo = DATE_FORMAT.format(calItem.getEnd()); // XXX STRING FORMATTER HH:mm:ss item.timeFrom = TIME_FORMAT.format(calItem.getStart()); item.timeTo = TIME_FORMAT.format(calItem.getEnd()); item.description = calItem.getDescription() == null ? "" : calItem.getDescription(); item.styleName = calItem.getStyleName() == null ? "" : calItem.getStyleName(); item.dateCaptionFormat = calItem.getDateCaptionFormat(); item.allDay = calItem.isAllDay(); item.moveable = calItem.isMoveable(); item.resizeable = calItem.isResizeable(); item.clickable = calItem.isClickable(); calendarStateItems.add(item); } } getState().items = calendarStateItems; } /** * Stores the minimum and maximum time-of-day in minutes for the items. * * @param items * A list of calendar items. Can be <code>null</code>. */ private void cacheMinMaxTimeOfDay(List<? extends CalendarItem> items) { minTimeInMinutes = null; maxTimeInMinutes = null; if (items != null) { for (CalendarItem item : items) { int minuteOfDayStart = getMinuteOfDay(item.getStart()); int minuteOfDayEnd = getMinuteOfDay(item.getEnd()); if (minTimeInMinutes == null) { minTimeInMinutes = minuteOfDayStart; maxTimeInMinutes = minuteOfDayEnd; } else { if (minuteOfDayStart < minTimeInMinutes) { minTimeInMinutes = minuteOfDayStart; } if (minuteOfDayEnd > maxTimeInMinutes) { maxTimeInMinutes = minuteOfDayEnd; } } } } } private static int getMinuteOfDay(ZonedDateTime date) { return date.getHour() * 60 + date.getMinute(); } /** * Sets the displayed start and end time to fit all current items that were * retrieved from the last call to getItems(). * <p> * If no items exist, nothing happens. * <p> * <b>NOTE: triggering this method only does this once for the current * items - items 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 setupFirstDayOfWeek() { // TODO change calculation to Java 8 Style day mapping // Convert to old day mapping from Java 7 getState().firstDayOfWeek = getFirstDayOfWeek() == MONDAY ? 2 : 1; } private DayOfWeek getFirstDayOfWeek() { if(Objects.nonNull(customFirstDayOfWeek)) { return customFirstDayOfWeek; } return WeekFields.of(getLocale()).getFirstDayOfWeek(); } /** * Allow setting first day of week independent of Locale. Set to null if you * want first day of week being defined by the locale * * @param dayOfWeek * any of java.time.DayOfWeek * or null to revert to default first day of week by locale */ public void setFirstDayOfWeek(DayOfWeek dayOfWeek) { customFirstDayOfWeek = dayOfWeek; markAsDirty(); } private void setupDaysAndActions() { CalendarState state = getState(); setupFirstDayOfWeek(); // 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) { // set defaults startDate = getStartDate(); endDate = getEndDate(); } long durationInDays = Duration.between(startDate, endDate).toDays(); durationInDays++; if (durationInDays > 60) { throw new RuntimeException( "Daterange is too big (max 60) = " + durationInDays); } state.dayNames = getDayNamesShort(); state.monthNames = getMonthNamesShort(); // Show "now"-marker in browser within given timezone. final ZonedDateTime now = ZonedDateTime.now(getZoneId()); state.now = new CalDate(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), new CalTime(now.getHour(), now.getMinute(), now.getSecond())); // Send all dates to client from server. This // approach was taken because gwt doesn't // support date localization properly. boolean monthView = durationInDays > 7; ZonedDateTime firstDateToShow = expandStartDate(startDate, monthView); ZonedDateTime lastDateToShow = expandEndDate(endDate, monthView); ZonedDateTime dateToShow = firstDateToShow; Map<CalendarDateRange, Set<Action>> actionMap = new HashMap<>(); List<CalendarState.Day> days = new ArrayList<>(); while (dateToShow.compareTo(lastDateToShow) < 1) { final CalendarState.Day day = new CalendarState.Day(); day.date = new CalDate(dateToShow.getYear(), dateToShow.getMonthValue(), dateToShow.getDayOfMonth()); day.localizedDateFormat = weeklyCaptionFormatProvider.captionFrom(dateToShow); day.dayOfWeek = dateToShow.getDayOfWeek().getValue(); day.week = (int) WeekFields.of(getLocale()).weekOfYear().getFrom(dateToShow); day.yearOfWeek = dateToShow.getYear(); day.slotStyles = new HashSet<>(); if (styledTimes.containsKey(allOverDate)) { day.slotStyles.addAll(styledTimes.get(allOverDate)); } if (styledTimes.containsKey(dateToShow.toLocalDate())) { day.slotStyles.addAll(styledTimes.get(dateToShow.toLocalDate())); } days.add(day); // Get actions for a specific date if (actionHandlers != null) { for (Action.Handler actionHandler : actionHandlers) { // Get day start and end times ZonedDateTime start = dateToShow.with(LocalTime.MIN); ZonedDateTime end = dateToShow.with(LocalTime.MAX); /* * 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); } } } dateToShow = dateToShow.plus(1, ChronoUnit.DAYS); } state.days = days; state.actions = createActionsList(actionMap); } /** * 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 ZonedDateTime expandStartDate(ZonedDateTime start, boolean expandToFullWeek) { if (expandToFullWeek) { start = getFirstDayOfWeek(start); } else { start = getDayByLocale(start); } // Always expand to the start of the first day to the end of the last day return start.with(LocalTime.MIN); } /** * 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 ZonedDateTime expandEndDate(ZonedDateTime end, boolean expandToFullWeek) { if (expandToFullWeek) { end = getLastDayOfWeek(end); } else { end = getDayByLocale(end); } // Always expand to the start of the first day to the end of the last day return end.with(LocalTime.MAX); } /** * 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 ZonedDateTime getFirstDayOfWeek(ZonedDateTime date) { DayOfWeek firstDayOfWeek = getFirstDayOfWeek(); while (firstDayOfWeek != date.getDayOfWeek()) { date = date.minus(1, ChronoUnit.DAYS); } return date; } /** * 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 ZonedDateTime getLastDayOfWeek(ZonedDateTime date) { DayOfWeek firstDayOfWeek = getFirstDayOfWeek(); date = date.plus(1, ChronoUnit.DAYS); // Roll to weeks last day using firstdayofweek. Roll until FDofW is // found and then roll back one day. while (firstDayOfWeek != date.getDayOfWeek()) { date = date.plus(1, ChronoUnit.DAYS); } date = date.minus(1, ChronoUnit.DAYS); return date; } /** * Get the day of week by the given calendar and its locale * * @param date * Target date * @return The date corespond to the locale */ private ZonedDateTime getDayByLocale(ZonedDateTime date) { DayOfWeek firstDayOfWeek = getFirstDayOfWeek(); int fow = date.getDayOfWeek().getValue(); // sonday first ? if (firstDayOfWeek == SUNDAY && Duration.between(startDate, endDate).toDays() > 0) { return date.minus(1, ChronoUnit.DAYS); } return date; } private void setActionsForEachHalfHour(Map<CalendarDateRange, Set<Action>> actionMap, ZonedDateTime start, ZonedDateTime end, Action.Handler actionHandler) { ZonedDateTime actionTime = start; while (actionTime.isBefore(end)) { ZonedDateTime endTime = actionTime.plus(30, ChronoUnit.MINUTES); CalendarDateRange range = new CalendarDateRange(actionTime, endTime); Action[] actions = actionHandler.getActions(range, this); if (actions != null) { Set<Action> actionSet = new LinkedHashSet<>(Arrays.asList(actions)); actionMap.put(range, actionSet); } actionTime = endTime; } } private void setActionsForDay(Map<CalendarDateRange, Set<Action>> actionMap, ZonedDateTime start, ZonedDateTime end, Action.Handler actionHandler) { CalendarDateRange range = new CalendarDateRange(start, end); Action[] actions = actionHandler.getActions(range, this); if (actions != null) { Set<Action> actionSet = new LinkedHashSet<>(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<>(); for (Entry<CalendarDateRange, Set<Action>> entry : actionMap.entrySet()) { CalendarDateRange range = entry.getKey(); for (Action action : entry.getValue()) { 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 = ACTION_DATE_TIME_FORMAT.format(range.getStart()); calendarAction.endDate = ACTION_DATE_TIME_FORMAT.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.contains("H")) { 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 ZoneId getZoneId() { return zoneId; } /** * Set time zone that this component will use. Null value sets the default * time zone. * * @param zone * Time zone to use */ public void setZoneId(ZoneId zone) { if (!zoneId.equals(zone)) { zoneId = zone; refreshDates(); } } /** * Reset the current dates with current settings */ private void refreshDates() { setStartDate(getStartDate()); setEndDate(getEndDate()); markAsDirty(); } /** * <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. Items are still * requested by the dates set by {@link #setStartDate(ZonedDateTime)} and * {@link #setEndDate(ZonedDateTime)}. * </p> * * @param firstDay * the first day of the week to show, between 1 and 7 * @param lastDay * the first day of the week to show, between 1 and 7 */ private void setVisibleDayRange(int firstDay, int lastDay) { assert (firstDay >= 1 && firstDay < lastDay && lastDay <= 7); this.firstDay = firstDay; this.lastDay = lastDay; getState(false).firstVisibleDayOfWeek = firstDay; getState().lastVisibleDayOfWeek = lastDay; } /** * 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; } /** * 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. Items are still * requested by the dates set by {@link #setStartDate(ZonedDateTime)} and * {@link #setEndDate(ZonedDateTime)}. * </p> * You can use {@link #autoScaleVisibleHoursOfDay()} for automatic scaling * of the visible hours based on current items. * * @param firstHour * the first hour of the day to show, between 0 and 23 * @see #autoScaleVisibleHoursOfDay() */ private void setFirstVisibleHourOfDay(int firstHour) { if (this.firstHour != firstHour && firstHour >= 0 && firstHour <= 23 && firstHour <= getLastVisibleHourOfDay()) { this.firstHour = firstHour; getState(false).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. Items are still * requested by the dates set by {@link #setStartDate(ZonedDateTime)} and * {@link #setEndDate(ZonedDateTime)}. * <p> * You can use {@link #autoScaleVisibleHoursOfDay()} for automatic scaling * of the visible hours based on current items. * * @param lastHour * the first hour of the day to show, between 0 and 23 * @see #autoScaleVisibleHoursOfDay() */ private 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 custom date caption provider for the weekly view. * * @return The custom date caption provider for the weekly view. */ public WeeklyCaptionProvider getWeeklyCaptionProvider() { return weeklyCaptionFormatProvider; } /** * Sets custom date caption provider for the weekly view. This is the caption of the * date. Format could be like "mmm MM/dd". * * @param captionProvider * The caption provider. */ public void setWeeklyCaptionProvider(WeeklyCaptionProvider captionProvider) { if (captionProvider != null) { weeklyCaptionFormatProvider = captionProvider; markAsDirty(); } } /** * Sets sort order for items. By default sort order is * {@link CalendarState.ItemSortOrder#DURATION_DESC}. * * @param order * sort strategy for items */ public void setItemSortOrder(CalendarState.ItemSortOrder order) { if (order == null) { getState().itemSortOrder = CalendarState.ItemSortOrder.DURATION_DESC; } else { getState().itemSortOrder = CalendarState.ItemSortOrder.values()[order.ordinal()]; } } /** * Returns sort order for items. * * @return currently active sort strategy */ public CalendarState.ItemSortOrder getItemSortOrder() { CalendarState.ItemSortOrder order = getState(false).itemSortOrder; if (order == null) { return CalendarState.ItemSortOrder.DURATION_DESC; } else { return order; } } /** * Is the user allowed to trigger items which alters the items * * @return true if the client is allowed to send changes to server */ protected boolean isClientChangeAllowed() { return isEnabled(); } /** * 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 CalendarComponentEvents.ForwardEvent(this)); } else { fireEvent(new CalendarComponentEvents.BackwardEvent(this)); } } /** * Fires an item move event to all server side move listerners * * @param index * The index of the item in the items list * @param newFromDatetime * The changed from date time */ protected void fireItemMove(int index, ZonedDateTime newFromDatetime) { CalendarComponentEvents.ItemMoveEvent event = new CalendarComponentEvents.ItemMoveEvent(this, items.get(index), newFromDatetime); if (calendarItemProvider instanceof CalendarComponentEvents.ItemMoveHandler) { // Notify event provider if it is an event move handler ((CalendarComponentEvents.ItemMoveHandler) calendarItemProvider).itemMove(event); } // Notify event move handler attached by using the // setHandler(ItemMoveHandler) 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 CalendarComponentEvents.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 fireItemClick(Integer index) { fireEvent(new CalendarComponentEvents.ItemClickEvent(this, items.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(ZonedDateTime date) { fireEvent(new CalendarComponentEvents.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 */ protected void fireRangeSelect(ZonedDateTime from, ZonedDateTime to) { fireEvent(new CalendarComponentEvents.RangeSelectEvent(this, from, to)); } /** * Fires an item resize event. The event is fired when a user resizes the * item in the calendar causing the time range of the item to increase or * decrease. The new start and end times are returned as arguments to this * method. * * @param index * The index of the item in the item cache * @param startTime * The new start date and time of the item * @param endTime * The new end date and time of the item */ protected void fireItemResize(int index, ZonedDateTime startTime, ZonedDateTime endTime) { CalendarComponentEvents.ItemResizeEvent event = new CalendarComponentEvents.ItemResizeEvent(this, items.get(index), startTime, endTime); if (calendarItemProvider instanceof CalendarComponentEvents.EventResizeHandler) { // Notify event provider if it is an event resize handler ((CalendarComponentEvents.EventResizeHandler) calendarItemProvider).itemResize(event); } // Notify event resize handler attached by using the // setHandler(ItemMoveHandler) 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); } /** * Set the {@link CalendarItemProvider} to be used with this calendar. The * DataProvider is used to query for items to show, and must be non-null. * By default a {@link BasicItemProvider} is used. * * @param calendarItemProvider * the calendarItemProvider to set. Cannot be null. */ public void setDataProvider(CalendarItemProvider<ITEM> calendarItemProvider) { if (calendarItemProvider == null) { throw new IllegalArgumentException( "Calendar event provider cannot be null"); } // remove old listener if (getDataProvider() instanceof CalendarItemProvider.ItemSetChangedNotifier) { ((CalendarItemProvider.ItemSetChangedNotifier) getDataProvider()).removeItemSetChangedListener(this); } this.calendarItemProvider = calendarItemProvider; // add new listener if (calendarItemProvider instanceof CalendarItemProvider.ItemSetChangedNotifier) { ((CalendarItemProvider.ItemSetChangedNotifier) calendarItemProvider).addItemSetChangedListener(this); } } /** * @return the {@link CalendarItemProvider} currently used */ public CalendarItemProvider<ITEM> getDataProvider() { return calendarItemProvider; } @Override public void itemSetChanged(CalendarItemProvider.ItemSetChangedEvent changeEvent) { // sanity check if (calendarItemProvider == 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 * @return handler registration */ protected Registration setHandler(String eventId, Class<?> eventType, EventListener listener, Method listenerMethod) { if (handlers.get(eventId) != null) { handlers.get(eventId).remove(); handlers.remove(eventId); } if (listener != null) { Registration registration = addListener(eventId, eventType, listener, listenerMethod); handlers.put(eventId, registration); return registration; } return null; } @Override public Registration setHandler(CalendarComponentEvents.ForwardHandler listener) { return setHandler(CalendarComponentEvents.ForwardEvent.EVENT_ID, CalendarComponentEvents.ForwardEvent.class, listener, CalendarComponentEvents.ForwardHandler.forwardMethod); } @Override public Registration setHandler(CalendarComponentEvents.BackwardHandler listener) { return setHandler(CalendarComponentEvents.BackwardEvent.EVENT_ID, CalendarComponentEvents.BackwardEvent.class, listener, CalendarComponentEvents.BackwardHandler.backwardMethod); } @Override public Registration setHandler(CalendarComponentEvents.DateClickHandler listener) { return setHandler(CalendarComponentEvents.DateClickEvent.EVENT_ID, CalendarComponentEvents.DateClickEvent.class, listener, CalendarComponentEvents.DateClickHandler.dateClickMethod); } @Override public Registration setHandler(CalendarComponentEvents.ItemClickHandler listener) { return setHandler(CalendarComponentEvents.ItemClickEvent.EVENT_ID, CalendarComponentEvents.ItemClickEvent.class, listener, CalendarComponentEvents.ItemClickHandler.itemClickMethod); } @Override public Registration setHandler(CalendarComponentEvents.WeekClickHandler listener) { return setHandler(CalendarComponentEvents.WeekClick.EVENT_ID, CalendarComponentEvents.WeekClick.class, listener, CalendarComponentEvents.WeekClickHandler.weekClickMethod); } @Override public Registration setHandler(CalendarComponentEvents.EventResizeHandler listener) { return setHandler(CalendarComponentEvents.ItemResizeEvent.EVENT_ID, CalendarComponentEvents.ItemResizeEvent.class, listener, CalendarComponentEvents.EventResizeHandler.itemResizeMethod); } @Override public Registration setHandler(CalendarComponentEvents.RangeSelectHandler listener) { return setHandler(CalendarComponentEvents.RangeSelectEvent.EVENT_ID, CalendarComponentEvents.RangeSelectEvent.class, listener, CalendarComponentEvents.RangeSelectHandler.rangeSelectMethod); } @Override public Registration setHandler(CalendarComponentEvents.ItemMoveHandler listener) { return setHandler(CalendarComponentEvents.ItemMoveEvent.EVENT_ID, CalendarComponentEvents.ItemMoveEvent.class, listener, CalendarComponentEvents.ItemMoveHandler.itemMoveMethod); } /** * 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<>(); if (clientVariables.containsKey("dropSlotIndex")) { int slotIndex = (Integer) clientVariables.get("dropSlotIndex"); int dayIndex = (Integer) clientVariables.get("dropDayIndex"); ZonedDateTime dropTime = startDate .with(LocalTime.MIN) .plus(dayIndex, ChronoUnit.DAYS) .plus(slotIndex * 30, ChronoUnit.MINUTES); serverVariables.put("dropTime", dropTime.toEpochSecond() * 1000); } else { int dayIndex = (Integer) clientVariables.get("dropDayIndex"); ZonedDateTime dropTime = expandStartDate(startDate, true) .plus(dayIndex, ChronoUnit.DAYS); serverVariables.put("dropDay", dropTime.toEpochSecond() * 1000); } serverVariables.put("mouseEvent", clientVariables.get("mouseEvent")); CalendarTargetDetails td = new CalendarTargetDetails(serverVariables, this); td.setHasDropTime(clientVariables.containsKey("dropSlotIndex")); return td; } /** * Return the calendar items from current data provider * * @param startDate The start of the date range * @param endDate The end of the date range * @return A list of calendar items */ public List<ITEM> getItems(ZonedDateTime startDate, ZonedDateTime endDate) { List<ITEM> events = getDataProvider().getItems(startDate, endDate); cacheMinMaxTimeOfDay(events); return events; } /** * Adds an action handler to the calendar 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 item then the target parameter * is the item, i.e. instanceof {@link CalendarItem}</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<>(); actionMapper = new KeyMapper<>(); } 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); return state.days == null || state.days.size() > 7; } /** * Is the calendar in a mode where one day of the month is shown * * @return Returns true if calendar is in day mode and false if it is in * weekly mode */ public boolean isDayMode() { CalendarState state = getState(false); return state.days == null || state.days.size() == 1; } /** * Is the calendar in a mode where two day or max 7 days of the month is shown * * @return Returns true if calendar is in weekly mode and false if not */ public boolean isWeeklyMode() { return !isDayMode() && !isMonthlyMode(); } @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 itemResize(int itemIndex, CalDate newStartDate, CalDate newEndDate) { if (!isClientChangeAllowed()) { return; } fireItemResize(itemIndex, ZonedDateTime.of( newStartDate.y, newStartDate.m, newStartDate.d, newStartDate.t.h, newStartDate.t.m, newStartDate.t.s, 0, getZoneId()), ZonedDateTime.of( newEndDate.y, newEndDate.m, newEndDate.d, newEndDate.t.h, newEndDate.t.m, newEndDate.t.s, 0, getZoneId())); } @Override public void itemMove(int itemIndex, CalDate newDate) { if (!isClientChangeAllowed()) { return; } if (itemIndex >= 0 && itemIndex < items.size() && items.get(itemIndex) != null) { fireItemMove(itemIndex, ZonedDateTime.of( newDate.y, newDate.m, newDate.d, newDate.t.h, newDate.t.m, newDate.t.s, 0, getZoneId())); } } @Override public void rangeSelect(SelectionRange selectionRange) { if (!isClientChangeAllowed()) { return; } // MounthSelection if (selectionRange.s != null && selectionRange.e != null) { fireRangeSelect( ZonedDateTime.of( selectionRange.s.y, selectionRange.s.m, selectionRange.s.d, 0,0,0,0, getZoneId()), ZonedDateTime.of( selectionRange.e.y, selectionRange.e.m, selectionRange.e.d, 23, 59, 59, 999999, getZoneId())); } else if (selectionRange.s != null) { ZonedDateTime dateTime = ZonedDateTime.of( selectionRange.s.y, selectionRange.s.m, selectionRange.s.d, 0,0,0,0, getZoneId()); ZonedDateTime start = ZonedDateTime.from(dateTime.plus(selectionRange.sMin, ChronoUnit.MINUTES)); ZonedDateTime end = ZonedDateTime.from(dateTime.plus(selectionRange.eMin, ChronoUnit.MINUTES)); fireRangeSelect(start, end); } } @Override public void forward() { fireNavigationEvent(true); } @Override public void backward() { fireNavigationEvent(false); } @Override public void dateClick(CalDate date) { fireDateClick(ZonedDateTime.of(date.y, date.m, date.d, 0,0,0,0, getZoneId())); } @Override public void weekClick(String eventValue) { if (eventValue.length() > 0 && eventValue.contains("w")) { String[] splitted = eventValue.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 itemClick(int itemIndex) { if (itemIndex >= 0 && itemIndex < items.size() && items.get(itemIndex) != null) { fireItemClick(itemIndex); } } @Override public void scroll(int scrollPosition) { getState().scroll = scrollPosition; } @Override public void actionOnEmptyCell(String actionKey, CalDate startDate, CalDate endDate) { Action action = actionMapper.get(actionKey); for (Action.Handler ah : actionHandlers) { ah.handleAction(action, Calendar.this, ZonedDateTime.of(startDate.y, startDate.m, startDate.d, startDate.t.h, startDate.t.m, startDate.t.s, 0, getZoneId())); } } @Override public void actionOnItem(String actionKey, CalDate startDate, CalDate endDate, int itemIndex) { Action action = actionMapper.get(actionKey); for (Action.Handler ah : actionHandlers) { ah.handleAction(action, Calendar.this, items.get(itemIndex)); } } } // @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 item 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 itemCaptionAsHtml * true if the captions are rendered as HTML, false if rendered * as plain text */ public void setItemCaptionAsHtml(boolean itemCaptionAsHtml) { getState().itemCaptionAsHtml = itemCaptionAsHtml; } /** * 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 isItemCaptionAsHtml() { return getState(false).itemCaptionAsHtml; } @Override public void readDesign(Element design, DesignContext designContext) { super.readDesign(design, designContext); Attributes attr = design.attributes(); if (design.hasAttr("time-zone")) { setZoneId(ZoneId.of(DesignAttributeHandler.readAttribute("end-date", attr, String.class))); } if (design.hasAttr("time-format")) { setTimeFormat(TimeFormat.valueOf( "Format" + design.attr("time-format").toUpperCase())); } if (design.hasAttr("start-date")) { setStartDate( ZonedDateTime.ofInstant(DesignAttributeHandler.readAttribute("start-date", attr, Date.class) .toInstant(), getZoneId())); } if (design.hasAttr("end-date")) { setEndDate( ZonedDateTime.ofInstant(DesignAttributeHandler.readAttribute("end-date", attr, Date.class) .toInstant(), getZoneId())); } } @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", DATE_FORMAT.format(getStartDate())); } if (endDate != null) { design.attr("end-date", DATE_FORMAT.format(getEndDate())); } if (!getZoneId().equals(ZoneId.systemDefault())) { design.attr("time-zone", getZoneId().getId()); } } @Override protected Collection<String> getCustomAttributes() { Collection<String> customAttributes = super.getCustomAttributes(); customAttributes.add("time-zone"); customAttributes.add("time-format"); customAttributes.add("start-date"); customAttributes.add("end-date"); return customAttributes; } /** * Add a time block start index. Time steps are half hour beginning at 0 * and a minimal time slot length of 1800000 milliseconds is used. * * @param day Day for this time slot * @param fromMillies time millies from where the block starts * @param styleName css class for this block (currently unused) */ protected final void addTimeBlockInternaly(LocalDate day, Long fromMillies, String styleName) { Set<CalendarState.SlotStyle> styledTimes; if (this.styledTimes.containsKey(day)) { styledTimes = this.styledTimes.get(day); } else { styledTimes = new HashSet<>(); } // create a new styled time slot final CalendarState.SlotStyle slotStyle = new CalendarState.SlotStyle(); slotStyle.slotStart = fromMillies; slotStyle.styleName = styleName; styledTimes.add(slotStyle); this.styledTimes.put(day, styledTimes); } /** * Add a time block marker for a range of time. Time steps are half hour, * so a minimal time slot is 1800000 milliseconds long. * * @param fromMillies time millies from where the block starts * @param toMillies time millies from where the block ends * @param styleName css class for this block (currently unused) */ public void addTimeBlock(long fromMillies, long toMillies, String styleName) { addTimeBlock(allOverDate, fromMillies, toMillies, styleName); } /** * Add a time block marker for a range of time. Time steps are half hour, * so a minimal time slot is 1800000 milliseconds long. * * @param day Day for this time slot * @param fromMillies time millies from where the block starts * @param toMillies time millies from where the block ends * @param styleName css class for this block (currently unused) */ public void addTimeBlock(LocalDate day, long fromMillies, long toMillies, String styleName) { assert (toMillies > fromMillies && fromMillies % 1800000 == 0 && toMillies % 1800000 == 0); while (fromMillies < toMillies) { addTimeBlockInternaly(day, fromMillies, styleName); fromMillies += 1800000; } markAsDirty(); } public void clearTimeBlocks() { styledTimes.clear(); markAsDirty(); } public void clearTimeBlocks(LocalDate day) { if (styledTimes.containsKey(day)) { styledTimes.remove(day); } markAsDirty(); } /* * Builder methods */ public Calendar<ITEM> withDataProvider(CalendarItemProvider<ITEM> provider) { setDataProvider(provider); return this; } public Calendar<ITEM> withDay(ZonedDateTime today) { setZoneId(today.getZone()); setStartDate(today); setEndDate(today); return this; } public Calendar<ITEM> withDay(LocalDate today) { setStartDate(ZonedDateTime.now(getZoneId()).with(today)); setEndDate(ZonedDateTime.now(getZoneId()).with(today)); return this; } public Calendar<ITEM> withDayInMonth(ZonedDateTime today) { setZoneId(today.getZone()); withMonth(today.getMonth()); return this; } public Calendar<ITEM> withDayInMonth(LocalDate today) { withMonth(today.getMonth()); return this; } public Calendar<ITEM> withWeek(ZonedDateTime today) { setZoneId(today.getZone()); setStartDate(today.with(ChronoField.DAY_OF_WEEK, getFirstVisibleDayOfWeek())); setEndDate(today.with(ChronoField.DAY_OF_WEEK, getLastVisibleDayOfWeek())); return this; } public Calendar<ITEM> withWeek(LocalDate today) { setStartDate(ZonedDateTime.now(getZoneId()).with(today).with(ChronoField.DAY_OF_WEEK, getFirstVisibleDayOfWeek())); setEndDate(ZonedDateTime.now(getZoneId()).with(today).with(ChronoField.DAY_OF_WEEK, getLastVisibleDayOfWeek())); return this; } public Calendar<ITEM> withWeekInYear(int weekInYear) { ZonedDateTime dateTime = ZonedDateTime.now(getZoneId()).with(ChronoField.ALIGNED_WEEK_OF_YEAR, weekInYear); setStartDate(dateTime.with(ChronoField.DAY_OF_WEEK, getFirstVisibleDayOfWeek())); setEndDate(dateTime.with(ChronoField.DAY_OF_WEEK, getLastVisibleDayOfWeek())); return this; } public Calendar<ITEM> withMonth(Month month) { ZonedDateTime dateTime = ZonedDateTime.now(getZoneId()).with(month); setStartDate(dateTime.with(firstDayOfMonth())); setEndDate(dateTime.with(lastDayOfMonth())); return this; } public Calendar<ITEM> withMonthInYear(Month month, int year) { ZonedDateTime dateTime = ZonedDateTime.now(getZoneId()).with(month).withYear(year); setStartDate(dateTime.with(firstDayOfMonth())); setEndDate(dateTime.with(lastDayOfMonth())); return this; } public Calendar<ITEM> withFullSize() { return withFullWidth().withFullHeight(); } public Calendar<ITEM> withFullWidth() { return withWidth(100, Unit.PERCENTAGE); } public Calendar<ITEM> withFullHeight() { return withHeight(100, Unit.PERCENTAGE); } public Calendar<ITEM> withWidth(float width, Unit unit) { setWidth(width, unit); return this; } public Calendar<ITEM> withHeight(float width, Unit unit) { setHeight(width, unit); return this; } /** * <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. Items are still * requested by the dates set by {@link #setStartDate(ZonedDateTime)} and * {@link #setEndDate(ZonedDateTime)}. * </p> * * @param firstDay * the first day of the week to show, between 1 and 7 * @param lastDay * the first day of the week to show, between 1 and 7 */ public Calendar<ITEM> withVisibleDays(int firstDay, int lastDay) { setVisibleDayRange(firstDay, lastDay); refreshDates(); return this; } /** * <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. Items are still * requested by the dates set by {@link #setStartDate(ZonedDateTime)} and * {@link #setEndDate(ZonedDateTime)}. * </p> * You can use {@link #autoScaleVisibleHoursOfDay()} for automatic scaling * of the visible hours based on current items. * * @param firstHour * the first hour of the day to show, between 0 and 23 * @param lastHour * the last hour of the day to show, between 0 and 23 * * @see #autoScaleVisibleHoursOfDay() */ public Calendar<ITEM> withVisibleHours(int firstHour, int lastHour) { setFirstVisibleHourOfDay(firstHour); setLastVisibleHourOfDay(lastHour); return this; } }