You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Calendar.java 70KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020
  1. /*
  2. * Copyright 2000-2014 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.ui;
  17. import java.lang.reflect.Method;
  18. import java.text.DateFormat;
  19. import java.text.DateFormatSymbols;
  20. import java.text.ParseException;
  21. import java.text.SimpleDateFormat;
  22. import java.util.ArrayList;
  23. import java.util.Arrays;
  24. import java.util.Collection;
  25. import java.util.Date;
  26. import java.util.EventListener;
  27. import java.util.GregorianCalendar;
  28. import java.util.HashMap;
  29. import java.util.LinkedHashSet;
  30. import java.util.LinkedList;
  31. import java.util.List;
  32. import java.util.Locale;
  33. import java.util.Map;
  34. import java.util.Map.Entry;
  35. import java.util.Set;
  36. import java.util.TimeZone;
  37. import java.util.logging.Level;
  38. import java.util.logging.Logger;
  39. import org.jsoup.nodes.Attributes;
  40. import org.jsoup.nodes.Element;
  41. import com.vaadin.data.Container;
  42. import com.vaadin.data.util.BeanItemContainer;
  43. import com.vaadin.event.Action;
  44. import com.vaadin.event.Action.Handler;
  45. import com.vaadin.event.dd.DropHandler;
  46. import com.vaadin.event.dd.DropTarget;
  47. import com.vaadin.event.dd.TargetDetails;
  48. import com.vaadin.server.KeyMapper;
  49. import com.vaadin.server.PaintException;
  50. import com.vaadin.server.PaintTarget;
  51. import com.vaadin.shared.ui.calendar.CalendarEventId;
  52. import com.vaadin.shared.ui.calendar.CalendarServerRpc;
  53. import com.vaadin.shared.ui.calendar.CalendarState;
  54. import com.vaadin.shared.ui.calendar.DateConstants;
  55. import com.vaadin.ui.components.calendar.CalendarComponentEvent;
  56. import com.vaadin.ui.components.calendar.CalendarComponentEvents;
  57. import com.vaadin.ui.components.calendar.CalendarComponentEvents.BackwardEvent;
  58. import com.vaadin.ui.components.calendar.CalendarComponentEvents.BackwardHandler;
  59. import com.vaadin.ui.components.calendar.CalendarComponentEvents.DateClickEvent;
  60. import com.vaadin.ui.components.calendar.CalendarComponentEvents.DateClickHandler;
  61. import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventClick;
  62. import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventClickHandler;
  63. import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventMoveHandler;
  64. import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventResize;
  65. import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventResizeHandler;
  66. import com.vaadin.ui.components.calendar.CalendarComponentEvents.ForwardEvent;
  67. import com.vaadin.ui.components.calendar.CalendarComponentEvents.ForwardHandler;
  68. import com.vaadin.ui.components.calendar.CalendarComponentEvents.MoveEvent;
  69. import com.vaadin.ui.components.calendar.CalendarComponentEvents.RangeSelectEvent;
  70. import com.vaadin.ui.components.calendar.CalendarComponentEvents.RangeSelectHandler;
  71. import com.vaadin.ui.components.calendar.CalendarComponentEvents.WeekClick;
  72. import com.vaadin.ui.components.calendar.CalendarComponentEvents.WeekClickHandler;
  73. import com.vaadin.ui.components.calendar.CalendarDateRange;
  74. import com.vaadin.ui.components.calendar.CalendarTargetDetails;
  75. import com.vaadin.ui.components.calendar.ContainerEventProvider;
  76. import com.vaadin.ui.components.calendar.event.BasicEventProvider;
  77. import com.vaadin.ui.components.calendar.event.CalendarEditableEventProvider;
  78. import com.vaadin.ui.components.calendar.event.CalendarEvent;
  79. import com.vaadin.ui.components.calendar.event.CalendarEvent.EventChangeEvent;
  80. import com.vaadin.ui.components.calendar.event.CalendarEvent.EventChangeListener;
  81. import com.vaadin.ui.components.calendar.event.CalendarEventProvider;
  82. import com.vaadin.ui.components.calendar.handler.BasicBackwardHandler;
  83. import com.vaadin.ui.components.calendar.handler.BasicDateClickHandler;
  84. import com.vaadin.ui.components.calendar.handler.BasicEventMoveHandler;
  85. import com.vaadin.ui.components.calendar.handler.BasicEventResizeHandler;
  86. import com.vaadin.ui.components.calendar.handler.BasicForwardHandler;
  87. import com.vaadin.ui.components.calendar.handler.BasicWeekClickHandler;
  88. import com.vaadin.ui.declarative.DesignAttributeHandler;
  89. import com.vaadin.ui.declarative.DesignContext;
  90. /**
  91. * <p>
  92. * Vaadin Calendar is for visualizing events in a calendar. Calendar events can
  93. * be visualized in the variable length view depending on the start and end
  94. * dates.
  95. * </p>
  96. *
  97. * <li>You can set the viewable date range with the {@link #setStartDate(Date)}
  98. * and {@link #setEndDate(Date)} methods. Calendar has a default date range of
  99. * one week</li>
  100. *
  101. * <li>Calendar has two kind of views: monthly and weekly view</li>
  102. *
  103. * <li>If date range is seven days or shorter, the weekly view is used.</li>
  104. *
  105. * <li>Calendar queries its events by using a
  106. * {@link com.vaadin.addon.calendar.event.CalendarEventProvider
  107. * CalendarEventProvider}. By default, a
  108. * {@link com.vaadin.addon.calendar.event.BasicEventProvider BasicEventProvider}
  109. * is used.</li>
  110. *
  111. * @since 7.1
  112. * @author Vaadin Ltd.
  113. */
  114. @SuppressWarnings("serial")
  115. public class Calendar extends AbstractComponent implements
  116. CalendarComponentEvents.NavigationNotifier,
  117. CalendarComponentEvents.EventMoveNotifier,
  118. CalendarComponentEvents.RangeSelectNotifier,
  119. CalendarComponentEvents.EventResizeNotifier,
  120. CalendarEventProvider.EventSetChangeListener, DropTarget,
  121. CalendarEditableEventProvider, Action.Container, LegacyComponent {
  122. /**
  123. * Calendar can use either 12 hours clock or 24 hours clock.
  124. */
  125. public enum TimeFormat {
  126. Format12H(), Format24H();
  127. }
  128. /** Defines currently active format for time. 12H/24H. */
  129. protected TimeFormat currentTimeFormat;
  130. /** Internal calendar data source. */
  131. protected java.util.Calendar currentCalendar = java.util.Calendar
  132. .getInstance();
  133. /** Defines the component's active time zone. */
  134. protected TimeZone timezone;
  135. /** Defines the calendar's date range starting point. */
  136. protected Date startDate = null;
  137. /** Defines the calendar's date range ending point. */
  138. protected Date endDate = null;
  139. /** Event provider. */
  140. private CalendarEventProvider calendarEventProvider;
  141. /**
  142. * Internal buffer for the events that are retrieved from the event
  143. * provider.
  144. */
  145. protected List<CalendarEvent> events;
  146. /** Date format that will be used in the UIDL for dates. */
  147. protected DateFormat df_date = new SimpleDateFormat("yyyy-MM-dd");
  148. /** Time format that will be used in the UIDL for time. */
  149. protected DateFormat df_time = new SimpleDateFormat("HH:mm:ss");
  150. /** Date format that will be used in the UIDL for both date and time. */
  151. protected DateFormat df_date_time = new SimpleDateFormat(
  152. DateConstants.CLIENT_DATE_FORMAT + "-"
  153. + DateConstants.CLIENT_TIME_FORMAT);
  154. /**
  155. * Week view's scroll position. Client sends updates to this value so that
  156. * scroll position wont reset all the time.
  157. */
  158. private int scrollTop = 0;
  159. /** Caption format for the weekly view */
  160. private String weeklyCaptionFormat = null;
  161. /** Map from event ids to event handlers */
  162. private final Map<String, EventListener> handlers;
  163. /**
  164. * Drop Handler for Vaadin DD. By default null.
  165. */
  166. private DropHandler dropHandler;
  167. /**
  168. * First day to show for a week
  169. */
  170. private int firstDay = 1;
  171. /**
  172. * Last day to show for a week
  173. */
  174. private int lastDay = 7;
  175. /**
  176. * First hour to show for a day
  177. */
  178. private int firstHour = 0;
  179. /**
  180. * Last hour to show for a day
  181. */
  182. private int lastHour = 23;
  183. /**
  184. * List of action handlers.
  185. */
  186. private LinkedList<Action.Handler> actionHandlers = null;
  187. /**
  188. * Action mapper.
  189. */
  190. private KeyMapper<Action> actionMapper = null;
  191. /**
  192. *
  193. */
  194. private CalendarServerRpcImpl rpc = new CalendarServerRpcImpl();
  195. private Integer customFirstDayOfWeek;
  196. /**
  197. * Returns the logger for the calendar
  198. */
  199. protected Logger getLogger() {
  200. return Logger.getLogger(Calendar.class.getName());
  201. }
  202. /**
  203. * Construct a Vaadin Calendar with a BasicEventProvider and no caption.
  204. * Default date range is one week.
  205. */
  206. public Calendar() {
  207. this(null, new BasicEventProvider());
  208. }
  209. /**
  210. * Construct a Vaadin Calendar with a BasicEventProvider and the provided
  211. * caption. Default date range is one week.
  212. *
  213. * @param caption
  214. */
  215. public Calendar(String caption) {
  216. this(caption, new BasicEventProvider());
  217. }
  218. /**
  219. * <p>
  220. * Construct a Vaadin Calendar with event provider. Event provider is
  221. * obligatory, because calendar component will query active events through
  222. * it.
  223. * </p>
  224. *
  225. * <p>
  226. * By default, Vaadin Calendar will show dates from the start of the current
  227. * week to the end of the current week. Use {@link #setStartDate(Date)} and
  228. * {@link #setEndDate(Date)} to change this.
  229. * </p>
  230. *
  231. * @param eventProvider
  232. * Event provider, cannot be null.
  233. */
  234. public Calendar(CalendarEventProvider eventProvider) {
  235. this(null, eventProvider);
  236. }
  237. /**
  238. * <p>
  239. * Construct a Vaadin Calendar with event provider and a caption. Event
  240. * provider is obligatory, because calendar component will query active
  241. * events through it.
  242. * </p>
  243. *
  244. * <p>
  245. * By default, Vaadin Calendar will show dates from the start of the current
  246. * week to the end of the current week. Use {@link #setStartDate(Date)} and
  247. * {@link #setEndDate(Date)} to change this.
  248. * </p>
  249. *
  250. * @param eventProvider
  251. * Event provider, cannot be null.
  252. */
  253. // this is the constructor every other constructor calls
  254. public Calendar(String caption, CalendarEventProvider eventProvider) {
  255. registerRpc(rpc);
  256. setCaption(caption);
  257. handlers = new HashMap<String, EventListener>();
  258. setDefaultHandlers();
  259. currentCalendar.setTime(new Date());
  260. setEventProvider(eventProvider);
  261. getState().firstDayOfWeek = firstDay;
  262. getState().lastVisibleDayOfWeek = lastDay;
  263. getState().firstHourOfDay = firstHour;
  264. getState().lastHourOfDay = lastHour;
  265. setTimeFormat(null);
  266. }
  267. @Override
  268. public CalendarState getState() {
  269. return (CalendarState) super.getState();
  270. }
  271. @Override
  272. protected CalendarState getState(boolean markAsDirty) {
  273. return (CalendarState) super.getState(markAsDirty);
  274. }
  275. @Override
  276. public void beforeClientResponse(boolean initial) {
  277. super.beforeClientResponse(initial);
  278. initCalendarWithLocale();
  279. getState().format24H = TimeFormat.Format24H == getTimeFormat();
  280. setupDaysAndActions();
  281. setupCalendarEvents();
  282. rpc.scroll(scrollTop);
  283. }
  284. /**
  285. * Set all the wanted default handlers here. This is always called after
  286. * constructing this object. All other events have default handlers except
  287. * range and event click.
  288. */
  289. protected void setDefaultHandlers() {
  290. setHandler(new BasicBackwardHandler());
  291. setHandler(new BasicForwardHandler());
  292. setHandler(new BasicWeekClickHandler());
  293. setHandler(new BasicDateClickHandler());
  294. setHandler(new BasicEventMoveHandler());
  295. setHandler(new BasicEventResizeHandler());
  296. }
  297. /**
  298. * Gets the calendar's start date.
  299. *
  300. * @return First visible date.
  301. */
  302. public Date getStartDate() {
  303. if (startDate == null) {
  304. currentCalendar.set(java.util.Calendar.MILLISECOND, 0);
  305. currentCalendar.set(java.util.Calendar.SECOND, 0);
  306. currentCalendar.set(java.util.Calendar.MINUTE, 0);
  307. currentCalendar.set(java.util.Calendar.HOUR_OF_DAY, 0);
  308. currentCalendar.set(java.util.Calendar.DAY_OF_WEEK,
  309. currentCalendar.getFirstDayOfWeek());
  310. return currentCalendar.getTime();
  311. }
  312. return startDate;
  313. }
  314. /**
  315. * Sets start date for the calendar. This and {@link #setEndDate(Date)}
  316. * control the range of dates visible on the component. The default range is
  317. * one week.
  318. *
  319. * @param date
  320. * First visible date to show.
  321. */
  322. public void setStartDate(Date date) {
  323. if (!date.equals(startDate)) {
  324. startDate = date;
  325. markAsDirty();
  326. }
  327. }
  328. /**
  329. * Gets the calendar's end date.
  330. *
  331. * @return Last visible date.
  332. */
  333. public Date getEndDate() {
  334. if (endDate == null) {
  335. currentCalendar.set(java.util.Calendar.MILLISECOND, 0);
  336. currentCalendar.set(java.util.Calendar.SECOND, 59);
  337. currentCalendar.set(java.util.Calendar.MINUTE, 59);
  338. currentCalendar.set(java.util.Calendar.HOUR_OF_DAY, 23);
  339. currentCalendar.set(java.util.Calendar.DAY_OF_WEEK,
  340. currentCalendar.getFirstDayOfWeek() + 6);
  341. return currentCalendar.getTime();
  342. }
  343. return endDate;
  344. }
  345. /**
  346. * Sets end date for the calendar. Starting from startDate, only six weeks
  347. * will be shown if duration to endDate is longer than six weeks.
  348. *
  349. * This and {@link #setStartDate(Date)} control the range of dates visible
  350. * on the component. The default range is one week.
  351. *
  352. * @param date
  353. * Last visible date to show.
  354. */
  355. public void setEndDate(Date date) {
  356. if (startDate != null && startDate.after(date)) {
  357. startDate = (Date) date.clone();
  358. markAsDirty();
  359. } else if (!date.equals(endDate)) {
  360. endDate = date;
  361. markAsDirty();
  362. }
  363. }
  364. /**
  365. * Sets the locale to be used in the Calendar component.
  366. *
  367. * @see com.vaadin.ui.AbstractComponent#setLocale(java.util.Locale)
  368. */
  369. @Override
  370. public void setLocale(Locale newLocale) {
  371. super.setLocale(newLocale);
  372. initCalendarWithLocale();
  373. }
  374. /**
  375. * Initialize the java calendar instance with the current locale and
  376. * timezone.
  377. */
  378. private void initCalendarWithLocale() {
  379. if (timezone != null) {
  380. currentCalendar = java.util.Calendar.getInstance(timezone,
  381. getLocale());
  382. } else {
  383. currentCalendar = java.util.Calendar.getInstance(getLocale());
  384. }
  385. if (customFirstDayOfWeek != null) {
  386. currentCalendar.setFirstDayOfWeek(customFirstDayOfWeek);
  387. }
  388. }
  389. private void setupCalendarEvents() {
  390. int durationInDays = (int) (((endDate.getTime()) - startDate.getTime()) / DateConstants.DAYINMILLIS);
  391. durationInDays++;
  392. if (durationInDays > 60) {
  393. throw new RuntimeException("Daterange is too big (max 60) = "
  394. + durationInDays);
  395. }
  396. Date firstDateToShow = expandStartDate(startDate, durationInDays > 7);
  397. Date lastDateToShow = expandEndDate(endDate, durationInDays > 7);
  398. currentCalendar.setTime(firstDateToShow);
  399. events = getEventProvider().getEvents(firstDateToShow, lastDateToShow);
  400. List<CalendarState.Event> calendarStateEvents = new ArrayList<CalendarState.Event>();
  401. if (events != null) {
  402. for (int i = 0; i < events.size(); i++) {
  403. CalendarEvent e = events.get(i);
  404. CalendarState.Event event = new CalendarState.Event();
  405. event.index = i;
  406. event.caption = e.getCaption() == null ? "" : e.getCaption();
  407. event.dateFrom = df_date.format(e.getStart());
  408. event.dateTo = df_date.format(e.getEnd());
  409. event.timeFrom = df_time.format(e.getStart());
  410. event.timeTo = df_time.format(e.getEnd());
  411. event.description = e.getDescription() == null ? "" : e
  412. .getDescription();
  413. event.styleName = e.getStyleName() == null ? "" : e
  414. .getStyleName();
  415. event.allDay = e.isAllDay();
  416. calendarStateEvents.add(event);
  417. }
  418. }
  419. getState().events = calendarStateEvents;
  420. }
  421. private void setupDaysAndActions() {
  422. // Make sure we have a up-to-date locale
  423. initCalendarWithLocale();
  424. CalendarState state = getState();
  425. state.firstDayOfWeek = currentCalendar.getFirstDayOfWeek();
  426. // If only one is null, throw exception
  427. // If both are null, set defaults
  428. if (startDate == null ^ endDate == null) {
  429. String message = "Schedule cannot be painted without a proper date range.\n";
  430. if (startDate == null) {
  431. throw new IllegalStateException(message
  432. + "You must set a start date using setStartDate(Date).");
  433. } else {
  434. throw new IllegalStateException(message
  435. + "You must set an end date using setEndDate(Date).");
  436. }
  437. } else if (startDate == null && endDate == null) {
  438. // set defaults
  439. startDate = getStartDate();
  440. endDate = getEndDate();
  441. }
  442. int durationInDays = (int) (((endDate.getTime()) - startDate.getTime()) / DateConstants.DAYINMILLIS);
  443. durationInDays++;
  444. if (durationInDays > 60) {
  445. throw new RuntimeException("Daterange is too big (max 60) = "
  446. + durationInDays);
  447. }
  448. state.dayNames = getDayNamesShort();
  449. state.monthNames = getMonthNamesShort();
  450. // Use same timezone in all dates this component handles.
  451. // Show "now"-marker in browser within given timezone.
  452. Date now = new Date();
  453. currentCalendar.setTime(now);
  454. now = currentCalendar.getTime();
  455. // Reset time zones for custom date formats
  456. df_date.setTimeZone(currentCalendar.getTimeZone());
  457. df_time.setTimeZone(currentCalendar.getTimeZone());
  458. state.now = (df_date.format(now) + " " + df_time.format(now));
  459. Date firstDateToShow = expandStartDate(startDate, durationInDays > 7);
  460. Date lastDateToShow = expandEndDate(endDate, durationInDays > 7);
  461. currentCalendar.setTime(firstDateToShow);
  462. DateFormat weeklyCaptionFormatter = getWeeklyCaptionFormatter();
  463. weeklyCaptionFormatter.setTimeZone(currentCalendar.getTimeZone());
  464. Map<CalendarDateRange, Set<Action>> actionMap = new HashMap<CalendarDateRange, Set<Action>>();
  465. List<CalendarState.Day> days = new ArrayList<CalendarState.Day>();
  466. // Send all dates to client from server. This
  467. // approach was taken because gwt doesn't
  468. // support date localization properly.
  469. while (currentCalendar.getTime().compareTo(lastDateToShow) < 1) {
  470. final Date date = currentCalendar.getTime();
  471. final CalendarState.Day day = new CalendarState.Day();
  472. day.date = df_date.format(date);
  473. day.localizedDateFormat = weeklyCaptionFormatter.format(date);
  474. day.dayOfWeek = getDowByLocale(currentCalendar);
  475. day.week = getWeek(currentCalendar);
  476. day.yearOfWeek = getYearOfWeek(currentCalendar);
  477. days.add(day);
  478. // Get actions for a specific date
  479. if (actionHandlers != null) {
  480. for (Action.Handler actionHandler : actionHandlers) {
  481. // Create calendar which omits time
  482. GregorianCalendar cal = new GregorianCalendar(
  483. getTimeZone(), getLocale());
  484. cal.clear();
  485. cal.set(currentCalendar.get(java.util.Calendar.YEAR),
  486. currentCalendar.get(java.util.Calendar.MONTH),
  487. currentCalendar.get(java.util.Calendar.DATE));
  488. // Get day start and end times
  489. Date start = cal.getTime();
  490. cal.add(java.util.Calendar.DATE, 1);
  491. cal.add(java.util.Calendar.SECOND, -1);
  492. Date end = cal.getTime();
  493. boolean monthView = (durationInDays > 7);
  494. /**
  495. * If in day or week view add actions for each half-an-hour.
  496. * If in month view add actions for each day
  497. */
  498. if (monthView) {
  499. setActionsForDay(actionMap, start, end, actionHandler);
  500. } else {
  501. setActionsForEachHalfHour(actionMap, start, end,
  502. actionHandler);
  503. }
  504. }
  505. }
  506. currentCalendar.add(java.util.Calendar.DATE, 1);
  507. }
  508. state.days = days;
  509. state.actions = createActionsList(actionMap);
  510. }
  511. private int getWeek(java.util.Calendar calendar) {
  512. return calendar.get(java.util.Calendar.WEEK_OF_YEAR);
  513. }
  514. private int getYearOfWeek(java.util.Calendar calendar) {
  515. // Would use calendar.getWeekYear() but it's only available since 1.7.
  516. int week = getWeek(calendar);
  517. int month = calendar.get(java.util.Calendar.MONTH);
  518. int year = calendar.get(java.util.Calendar.YEAR);
  519. if (week == 1 && month == java.util.Calendar.DECEMBER) {
  520. return year + 1;
  521. }
  522. return year;
  523. }
  524. private void setActionsForEachHalfHour(
  525. Map<CalendarDateRange, Set<Action>> actionMap, Date start,
  526. Date end, Action.Handler actionHandler) {
  527. GregorianCalendar cal = new GregorianCalendar(getTimeZone(),
  528. getLocale());
  529. cal.setTime(start);
  530. while (cal.getTime().before(end)) {
  531. Date s = cal.getTime();
  532. cal.add(java.util.Calendar.MINUTE, 30);
  533. Date e = cal.getTime();
  534. CalendarDateRange range = new CalendarDateRange(s, e, getTimeZone());
  535. Action[] actions = actionHandler.getActions(range, this);
  536. if (actions != null) {
  537. Set<Action> actionSet = new LinkedHashSet<Action>(
  538. Arrays.asList(actions));
  539. actionMap.put(range, actionSet);
  540. }
  541. }
  542. }
  543. private void setActionsForDay(
  544. Map<CalendarDateRange, Set<Action>> actionMap, Date start,
  545. Date end, Action.Handler actionHandler) {
  546. CalendarDateRange range = new CalendarDateRange(start, end,
  547. getTimeZone());
  548. Action[] actions = actionHandler.getActions(range, this);
  549. if (actions != null) {
  550. Set<Action> actionSet = new LinkedHashSet<Action>(
  551. Arrays.asList(actions));
  552. actionMap.put(range, actionSet);
  553. }
  554. }
  555. private List<CalendarState.Action> createActionsList(
  556. Map<CalendarDateRange, Set<Action>> actionMap) {
  557. if (actionMap.isEmpty()) {
  558. return null;
  559. }
  560. List<CalendarState.Action> calendarActions = new ArrayList<CalendarState.Action>();
  561. SimpleDateFormat formatter = new SimpleDateFormat(
  562. DateConstants.ACTION_DATE_FORMAT_PATTERN);
  563. formatter.setTimeZone(getTimeZone());
  564. for (Entry<CalendarDateRange, Set<Action>> entry : actionMap.entrySet()) {
  565. CalendarDateRange range = entry.getKey();
  566. Set<Action> actions = entry.getValue();
  567. for (Action action : actions) {
  568. String key = actionMapper.key(action);
  569. CalendarState.Action calendarAction = new CalendarState.Action();
  570. calendarAction.actionKey = key;
  571. calendarAction.caption = action.getCaption();
  572. setResource(key, action.getIcon());
  573. calendarAction.iconKey = key;
  574. calendarAction.startDate = formatter.format(range.getStart());
  575. calendarAction.endDate = formatter.format(range.getEnd());
  576. calendarActions.add(calendarAction);
  577. }
  578. }
  579. return calendarActions;
  580. }
  581. /**
  582. * Gets currently active time format. Value is either TimeFormat.Format12H
  583. * or TimeFormat.Format24H.
  584. *
  585. * @return TimeFormat Format for the time.
  586. */
  587. public TimeFormat getTimeFormat() {
  588. if (currentTimeFormat == null) {
  589. SimpleDateFormat f;
  590. if (getLocale() == null) {
  591. f = (SimpleDateFormat) SimpleDateFormat
  592. .getTimeInstance(SimpleDateFormat.SHORT);
  593. } else {
  594. f = (SimpleDateFormat) SimpleDateFormat.getTimeInstance(
  595. SimpleDateFormat.SHORT, getLocale());
  596. }
  597. String p = f.toPattern();
  598. if (p.indexOf("HH") != -1 || p.indexOf("H") != -1) {
  599. return TimeFormat.Format24H;
  600. }
  601. return TimeFormat.Format12H;
  602. }
  603. return currentTimeFormat;
  604. }
  605. /**
  606. * Example: <code>setTimeFormat(TimeFormat.Format12H);</code></br> Set to
  607. * null, if you want the format being defined by the locale.
  608. *
  609. * @param format
  610. * Set 12h or 24h format. Default is defined by the locale.
  611. */
  612. public void setTimeFormat(TimeFormat format) {
  613. currentTimeFormat = format;
  614. markAsDirty();
  615. }
  616. /**
  617. * Returns a time zone that is currently used by this component.
  618. *
  619. * @return Component's Time zone
  620. */
  621. public TimeZone getTimeZone() {
  622. if (timezone == null) {
  623. return currentCalendar.getTimeZone();
  624. }
  625. return timezone;
  626. }
  627. /**
  628. * Set time zone that this component will use. Null value sets the default
  629. * time zone.
  630. *
  631. * @param zone
  632. * Time zone to use
  633. */
  634. public void setTimeZone(TimeZone zone) {
  635. timezone = zone;
  636. if (!currentCalendar.getTimeZone().equals(zone)) {
  637. if (zone == null) {
  638. zone = TimeZone.getDefault();
  639. }
  640. currentCalendar.setTimeZone(zone);
  641. df_date_time.setTimeZone(zone);
  642. markAsDirty();
  643. }
  644. }
  645. /**
  646. * Get the internally used Calendar instance. This is the currently used
  647. * instance of {@link java.util.Calendar} but is bound to change during the
  648. * lifetime of the component.
  649. *
  650. * @return the currently used java calendar
  651. */
  652. public java.util.Calendar getInternalCalendar() {
  653. return currentCalendar;
  654. }
  655. /**
  656. * <p>
  657. * This method restricts the weekdays that are shown. This affects both the
  658. * monthly and the weekly view. The general contract is that <b>firstDay <
  659. * lastDay</b>.
  660. * </p>
  661. *
  662. * <p>
  663. * Note that this only affects the rendering process. Events are still
  664. * requested by the dates set by {@link #setStartDate(Date)} and
  665. * {@link #setEndDate(Date)}.
  666. * </p>
  667. *
  668. * @param firstDay
  669. * the first day of the week to show, between 1 and 7
  670. */
  671. public void setFirstVisibleDayOfWeek(int firstDay) {
  672. if (this.firstDay != firstDay && firstDay >= 1 && firstDay <= 7
  673. && getLastVisibleDayOfWeek() >= firstDay) {
  674. this.firstDay = firstDay;
  675. getState().firstVisibleDayOfWeek = firstDay;
  676. }
  677. }
  678. /**
  679. * Get the first visible day of the week. Returns the weekdays as integers
  680. * represented by {@link java.util.Calendar#DAY_OF_WEEK}
  681. *
  682. * @return An integer representing the week day according to
  683. * {@link java.util.Calendar#DAY_OF_WEEK}
  684. */
  685. public int getFirstVisibleDayOfWeek() {
  686. return firstDay;
  687. }
  688. /**
  689. * <p>
  690. * This method restricts the weekdays that are shown. This affects both the
  691. * monthly and the weekly view. The general contract is that <b>firstDay <
  692. * lastDay</b>.
  693. * </p>
  694. *
  695. * <p>
  696. * Note that this only affects the rendering process. Events are still
  697. * requested by the dates set by {@link #setStartDate(Date)} and
  698. * {@link #setEndDate(Date)}.
  699. * </p>
  700. *
  701. * @param lastDay
  702. * the first day of the week to show, between 1 and 7
  703. */
  704. public void setLastVisibleDayOfWeek(int lastDay) {
  705. if (this.lastDay != lastDay && lastDay >= 1 && lastDay <= 7
  706. && getFirstVisibleDayOfWeek() <= lastDay) {
  707. this.lastDay = lastDay;
  708. getState().lastVisibleDayOfWeek = lastDay;
  709. }
  710. }
  711. /**
  712. * Get the last visible day of the week. Returns the weekdays as integers
  713. * represented by {@link java.util.Calendar#DAY_OF_WEEK}
  714. *
  715. * @return An integer representing the week day according to
  716. * {@link java.util.Calendar#DAY_OF_WEEK}
  717. */
  718. public int getLastVisibleDayOfWeek() {
  719. return lastDay;
  720. }
  721. /**
  722. * <p>
  723. * This method restricts the hours that are shown per day. This affects the
  724. * weekly view. The general contract is that <b>firstHour < lastHour</b>.
  725. * </p>
  726. *
  727. * <p>
  728. * Note that this only affects the rendering process. Events are still
  729. * requested by the dates set by {@link #setStartDate(Date)} and
  730. * {@link #setEndDate(Date)}.
  731. * </p>
  732. *
  733. * @param firstHour
  734. * the first hour of the day to show, between 0 and 23
  735. */
  736. public void setFirstVisibleHourOfDay(int firstHour) {
  737. if (this.firstHour != firstHour && firstHour >= 0 && firstHour <= 23
  738. && firstHour <= getLastVisibleHourOfDay()) {
  739. this.firstHour = firstHour;
  740. getState().firstHourOfDay = firstHour;
  741. }
  742. }
  743. /**
  744. * Returns the first visible hour in the week view. Returns the hour using a
  745. * 24h time format
  746. *
  747. */
  748. public int getFirstVisibleHourOfDay() {
  749. return firstHour;
  750. }
  751. /**
  752. * <p>
  753. * This method restricts the hours that are shown per day. This affects the
  754. * weekly view. The general contract is that <b>firstHour < lastHour</b>.
  755. * </p>
  756. *
  757. * <p>
  758. * Note that this only affects the rendering process. Events are still
  759. * requested by the dates set by {@link #setStartDate(Date)} and
  760. * {@link #setEndDate(Date)}.
  761. * </p>
  762. *
  763. * @param lastHour
  764. * the first hour of the day to show, between 0 and 23
  765. */
  766. public void setLastVisibleHourOfDay(int lastHour) {
  767. if (this.lastHour != lastHour && lastHour >= 0 && lastHour <= 23
  768. && lastHour >= getFirstVisibleHourOfDay()) {
  769. this.lastHour = lastHour;
  770. getState().lastHourOfDay = lastHour;
  771. }
  772. }
  773. /**
  774. * Returns the last visible hour in the week view. Returns the hour using a
  775. * 24h time format
  776. *
  777. */
  778. public int getLastVisibleHourOfDay() {
  779. return lastHour;
  780. }
  781. /**
  782. * Gets the date caption format for the weekly view.
  783. *
  784. * @return The pattern used in caption of dates in weekly view.
  785. */
  786. public String getWeeklyCaptionFormat() {
  787. return weeklyCaptionFormat;
  788. }
  789. /**
  790. * Sets custom date format for the weekly view. This is the caption of the
  791. * date. Format could be like "mmm MM/dd".
  792. *
  793. * @param dateFormatPattern
  794. * The date caption pattern.
  795. */
  796. public void setWeeklyCaptionFormat(String dateFormatPattern) {
  797. if ((weeklyCaptionFormat == null && dateFormatPattern != null)
  798. || (weeklyCaptionFormat != null && !weeklyCaptionFormat
  799. .equals(dateFormatPattern))) {
  800. weeklyCaptionFormat = dateFormatPattern;
  801. markAsDirty();
  802. }
  803. }
  804. private DateFormat getWeeklyCaptionFormatter() {
  805. if (weeklyCaptionFormat != null) {
  806. return new SimpleDateFormat(weeklyCaptionFormat, getLocale());
  807. } else {
  808. return SimpleDateFormat.getDateInstance(SimpleDateFormat.SHORT,
  809. getLocale());
  810. }
  811. }
  812. /**
  813. * Get the day of week by the given calendar and its locale
  814. *
  815. * @param calendar
  816. * The calendar to use
  817. * @return
  818. */
  819. private static int getDowByLocale(java.util.Calendar calendar) {
  820. int fow = calendar.get(java.util.Calendar.DAY_OF_WEEK);
  821. // monday first
  822. if (calendar.getFirstDayOfWeek() == java.util.Calendar.MONDAY) {
  823. fow = (fow == java.util.Calendar.SUNDAY) ? 7 : fow - 1;
  824. }
  825. return fow;
  826. }
  827. /**
  828. * Is the user allowed to trigger events which alters the events
  829. *
  830. * @return true if the client is allowed to send changes to server
  831. * @see #isEventClickAllowed()
  832. */
  833. protected boolean isClientChangeAllowed() {
  834. return !isReadOnly();
  835. }
  836. /**
  837. * Is the user allowed to trigger click events. Returns {@code true} by
  838. * default. Subclass can override this method to disallow firing event
  839. * clicks got from the client side.
  840. *
  841. * @return true if the client is allowed to click events
  842. * @see #isClientChangeAllowed()
  843. * @deprecated As of 7.4, override {@link #fireEventClick(Integer)} instead.
  844. */
  845. @Deprecated
  846. protected boolean isEventClickAllowed() {
  847. return true;
  848. }
  849. /**
  850. * Fires an event when the user selecing moving forward/backward in the
  851. * calendar.
  852. *
  853. * @param forward
  854. * True if the calendar moved forward else backward is assumed.
  855. */
  856. protected void fireNavigationEvent(boolean forward) {
  857. if (forward) {
  858. fireEvent(new ForwardEvent(this));
  859. } else {
  860. fireEvent(new BackwardEvent(this));
  861. }
  862. }
  863. /**
  864. * Fires an event move event to all server side move listerners
  865. *
  866. * @param index
  867. * The index of the event in the events list
  868. * @param newFromDatetime
  869. * The changed from date time
  870. */
  871. protected void fireEventMove(int index, Date newFromDatetime) {
  872. MoveEvent event = new MoveEvent(this, events.get(index),
  873. newFromDatetime);
  874. if (calendarEventProvider instanceof EventMoveHandler) {
  875. // Notify event provider if it is an event move handler
  876. ((EventMoveHandler) calendarEventProvider).eventMove(event);
  877. }
  878. // Notify event move handler attached by using the
  879. // setHandler(EventMoveHandler) method
  880. fireEvent(event);
  881. }
  882. /**
  883. * Fires event when a week was clicked in the calendar.
  884. *
  885. * @param week
  886. * The week that was clicked
  887. * @param year
  888. * The year of the week
  889. */
  890. protected void fireWeekClick(int week, int year) {
  891. fireEvent(new WeekClick(this, week, year));
  892. }
  893. /**
  894. * Fires event when a date was clicked in the calendar. Uses an existing
  895. * event from the event cache.
  896. *
  897. * @param index
  898. * The index of the event in the event cache.
  899. */
  900. protected void fireEventClick(Integer index) {
  901. fireEvent(new EventClick(this, events.get(index)));
  902. }
  903. /**
  904. * Fires event when a date was clicked in the calendar. Creates a new event
  905. * for the date and passes it to the listener.
  906. *
  907. * @param date
  908. * The date and time that was clicked
  909. */
  910. protected void fireDateClick(Date date) {
  911. fireEvent(new DateClickEvent(this, date));
  912. }
  913. /**
  914. * Fires an event range selected event. The event is fired when a user
  915. * highlights an area in the calendar. The highlighted areas start and end
  916. * dates are returned as arguments.
  917. *
  918. * @param from
  919. * The start date and time of the highlighted area
  920. * @param to
  921. * The end date and time of the highlighted area
  922. * @param monthlyMode
  923. * Is the calendar in monthly mode
  924. */
  925. protected void fireRangeSelect(Date from, Date to, boolean monthlyMode) {
  926. fireEvent(new RangeSelectEvent(this, from, to, monthlyMode));
  927. }
  928. /**
  929. * Fires an event resize event. The event is fired when a user resizes the
  930. * event in the calendar causing the time range of the event to increase or
  931. * decrease. The new start and end times are returned as arguments to this
  932. * method.
  933. *
  934. * @param index
  935. * The index of the event in the event cache
  936. * @param startTime
  937. * The new start date and time of the event
  938. * @param endTime
  939. * The new end date and time of the event
  940. */
  941. protected void fireEventResize(int index, Date startTime, Date endTime) {
  942. EventResize event = new EventResize(this, events.get(index), startTime,
  943. endTime);
  944. if (calendarEventProvider instanceof EventResizeHandler) {
  945. // Notify event provider if it is an event resize handler
  946. ((EventResizeHandler) calendarEventProvider).eventResize(event);
  947. }
  948. // Notify event resize handler attached by using the
  949. // setHandler(EventMoveHandler) method
  950. fireEvent(event);
  951. }
  952. /**
  953. * Localized display names for week days starting from sunday. Returned
  954. * array's length is always 7.
  955. *
  956. * @return Array of localized weekday names.
  957. */
  958. protected String[] getDayNamesShort() {
  959. DateFormatSymbols s = new DateFormatSymbols(getLocale());
  960. return Arrays.copyOfRange(s.getWeekdays(), 1, 8);
  961. }
  962. /**
  963. * Localized display names for months starting from January. Returned
  964. * array's length is always 12.
  965. *
  966. * @return Array of localized month names.
  967. */
  968. protected String[] getMonthNamesShort() {
  969. DateFormatSymbols s = new DateFormatSymbols(getLocale());
  970. return Arrays.copyOf(s.getShortMonths(), 12);
  971. }
  972. /**
  973. * Gets a date that is first day in the week that target given date belongs
  974. * to.
  975. *
  976. * @param date
  977. * Target date
  978. * @return Date that is first date in same week that given date is.
  979. */
  980. protected Date getFirstDateForWeek(Date date) {
  981. int firstDayOfWeek = currentCalendar.getFirstDayOfWeek();
  982. currentCalendar.setTime(date);
  983. while (firstDayOfWeek != currentCalendar
  984. .get(java.util.Calendar.DAY_OF_WEEK)) {
  985. currentCalendar.add(java.util.Calendar.DATE, -1);
  986. }
  987. return currentCalendar.getTime();
  988. }
  989. /**
  990. * Gets a date that is last day in the week that target given date belongs
  991. * to.
  992. *
  993. * @param date
  994. * Target date
  995. * @return Date that is last date in same week that given date is.
  996. */
  997. protected Date getLastDateForWeek(Date date) {
  998. currentCalendar.setTime(date);
  999. currentCalendar.add(java.util.Calendar.DATE, 1);
  1000. int firstDayOfWeek = currentCalendar.getFirstDayOfWeek();
  1001. // Roll to weeks last day using firstdayofweek. Roll until FDofW is
  1002. // found and then roll back one day.
  1003. while (firstDayOfWeek != currentCalendar
  1004. .get(java.util.Calendar.DAY_OF_WEEK)) {
  1005. currentCalendar.add(java.util.Calendar.DATE, 1);
  1006. }
  1007. currentCalendar.add(java.util.Calendar.DATE, -1);
  1008. return currentCalendar.getTime();
  1009. }
  1010. /**
  1011. * Calculates the end time of the day using the given calendar and date
  1012. *
  1013. * @param date
  1014. * @param calendar
  1015. * the calendar instance to be used in the calculation. The given
  1016. * instance is unchanged in this operation.
  1017. * @return the given date, with time set to the end of the day
  1018. */
  1019. private static Date getEndOfDay(java.util.Calendar calendar, Date date) {
  1020. java.util.Calendar calendarClone = (java.util.Calendar) calendar
  1021. .clone();
  1022. calendarClone.setTime(date);
  1023. calendarClone.set(java.util.Calendar.MILLISECOND,
  1024. calendarClone.getActualMaximum(java.util.Calendar.MILLISECOND));
  1025. calendarClone.set(java.util.Calendar.SECOND,
  1026. calendarClone.getActualMaximum(java.util.Calendar.SECOND));
  1027. calendarClone.set(java.util.Calendar.MINUTE,
  1028. calendarClone.getActualMaximum(java.util.Calendar.MINUTE));
  1029. calendarClone.set(java.util.Calendar.HOUR,
  1030. calendarClone.getActualMaximum(java.util.Calendar.HOUR));
  1031. calendarClone.set(java.util.Calendar.HOUR_OF_DAY,
  1032. calendarClone.getActualMaximum(java.util.Calendar.HOUR_OF_DAY));
  1033. return calendarClone.getTime();
  1034. }
  1035. /**
  1036. * Calculates the end time of the day using the given calendar and date
  1037. *
  1038. * @param date
  1039. * @param calendar
  1040. * the calendar instance to be used in the calculation. The given
  1041. * instance is unchanged in this operation.
  1042. * @return the given date, with time set to the end of the day
  1043. */
  1044. private static Date getStartOfDay(java.util.Calendar calendar, Date date) {
  1045. java.util.Calendar calendarClone = (java.util.Calendar) calendar
  1046. .clone();
  1047. calendarClone.setTime(date);
  1048. calendarClone.set(java.util.Calendar.MILLISECOND, 0);
  1049. calendarClone.set(java.util.Calendar.SECOND, 0);
  1050. calendarClone.set(java.util.Calendar.MINUTE, 0);
  1051. calendarClone.set(java.util.Calendar.HOUR, 0);
  1052. calendarClone.set(java.util.Calendar.HOUR_OF_DAY, 0);
  1053. return calendarClone.getTime();
  1054. }
  1055. /**
  1056. * Finds the first day of the week and returns a day representing the start
  1057. * of that day
  1058. *
  1059. * @param start
  1060. * The actual date
  1061. * @param expandToFullWeek
  1062. * Should the returned date be moved to the start of the week
  1063. * @return If expandToFullWeek is set then it returns the first day of the
  1064. * week, else it returns a clone of the actual date with the time
  1065. * set to the start of the day
  1066. */
  1067. protected Date expandStartDate(Date start, boolean expandToFullWeek) {
  1068. // If the duration is more than week, use monthly view and get startweek
  1069. // and endweek. Example if views daterange is from tuesday to next weeks
  1070. // wednesday->expand to monday to nextweeks sunday. If firstdayofweek =
  1071. // monday
  1072. if (expandToFullWeek) {
  1073. start = getFirstDateForWeek(start);
  1074. } else {
  1075. start = (Date) start.clone();
  1076. }
  1077. // Always expand to the start of the first day to the end of the last
  1078. // day
  1079. start = getStartOfDay(currentCalendar, start);
  1080. return start;
  1081. }
  1082. /**
  1083. * Finds the last day of the week and returns a day representing the end of
  1084. * that day
  1085. *
  1086. * @param end
  1087. * The actual date
  1088. * @param expandToFullWeek
  1089. * Should the returned date be moved to the end of the week
  1090. * @return If expandToFullWeek is set then it returns the last day of the
  1091. * week, else it returns a clone of the actual date with the time
  1092. * set to the end of the day
  1093. */
  1094. protected Date expandEndDate(Date end, boolean expandToFullWeek) {
  1095. // If the duration is more than week, use monthly view and get startweek
  1096. // and endweek. Example if views daterange is from tuesday to next weeks
  1097. // wednesday->expand to monday to nextweeks sunday. If firstdayofweek =
  1098. // monday
  1099. if (expandToFullWeek) {
  1100. end = getLastDateForWeek(end);
  1101. } else {
  1102. end = (Date) end.clone();
  1103. }
  1104. // Always expand to the start of the first day to the end of the last
  1105. // day
  1106. end = getEndOfDay(currentCalendar, end);
  1107. return end;
  1108. }
  1109. /**
  1110. * Set the {@link com.vaadin.addon.calendar.event.CalendarEventProvider
  1111. * CalendarEventProvider} to be used with this calendar. The EventProvider
  1112. * is used to query for events to show, and must be non-null. By default a
  1113. * {@link com.vaadin.addon.calendar.event.BasicEventProvider
  1114. * BasicEventProvider} is used.
  1115. *
  1116. * @param calendarEventProvider
  1117. * the calendarEventProvider to set. Cannot be null.
  1118. */
  1119. public void setEventProvider(CalendarEventProvider calendarEventProvider) {
  1120. if (calendarEventProvider == null) {
  1121. throw new IllegalArgumentException(
  1122. "Calendar event provider cannot be null");
  1123. }
  1124. // remove old listener
  1125. if (getEventProvider() instanceof EventSetChangeNotifier) {
  1126. ((EventSetChangeNotifier) getEventProvider())
  1127. .removeEventSetChangeListener(this);
  1128. }
  1129. this.calendarEventProvider = calendarEventProvider;
  1130. // add new listener
  1131. if (calendarEventProvider instanceof EventSetChangeNotifier) {
  1132. ((EventSetChangeNotifier) calendarEventProvider)
  1133. .addEventSetChangeListener(this);
  1134. }
  1135. }
  1136. /**
  1137. * @return the {@link com.vaadin.addon.calendar.event.CalendarEventProvider
  1138. * CalendarEventProvider} currently used
  1139. */
  1140. public CalendarEventProvider getEventProvider() {
  1141. return calendarEventProvider;
  1142. }
  1143. /*
  1144. * (non-Javadoc)
  1145. *
  1146. * @see com.vaadin.addon.calendar.ui.CalendarEvents.EventChangeListener#
  1147. * eventChange (com.vaadin.addon.calendar.ui.CalendarEvents.EventChange)
  1148. */
  1149. @Override
  1150. public void eventSetChange(EventSetChangeEvent changeEvent) {
  1151. // sanity check
  1152. if (calendarEventProvider == changeEvent.getProvider()) {
  1153. markAsDirty();
  1154. }
  1155. }
  1156. /**
  1157. * Set the handler for the given type information. Mirrors
  1158. * {@link #addListener(String, Class, Object, Method) addListener} from
  1159. * AbstractComponent
  1160. *
  1161. * @param eventId
  1162. * A unique id for the event. Usually one of
  1163. * {@link CalendarEventId}
  1164. * @param eventType
  1165. * The class of the event, most likely a subclass of
  1166. * {@link CalendarComponentEvent}
  1167. * @param listener
  1168. * A listener that listens to the given event
  1169. * @param listenerMethod
  1170. * The method on the lister to call when the event is triggered
  1171. */
  1172. protected void setHandler(String eventId, Class<?> eventType,
  1173. EventListener listener, Method listenerMethod) {
  1174. if (handlers.get(eventId) != null) {
  1175. removeListener(eventId, eventType, handlers.get(eventId));
  1176. handlers.remove(eventId);
  1177. }
  1178. if (listener != null) {
  1179. addListener(eventId, eventType, listener, listenerMethod);
  1180. handlers.put(eventId, listener);
  1181. }
  1182. }
  1183. /*
  1184. * (non-Javadoc)
  1185. *
  1186. * @see
  1187. * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier
  1188. * #addListener
  1189. * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.ForwardHandler)
  1190. */
  1191. @Override
  1192. public void setHandler(ForwardHandler listener) {
  1193. setHandler(ForwardEvent.EVENT_ID, ForwardEvent.class, listener,
  1194. ForwardHandler.forwardMethod);
  1195. }
  1196. /*
  1197. * (non-Javadoc)
  1198. *
  1199. * @see
  1200. * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier
  1201. * #addListener
  1202. * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.BackwardHandler)
  1203. */
  1204. @Override
  1205. public void setHandler(BackwardHandler listener) {
  1206. setHandler(BackwardEvent.EVENT_ID, BackwardEvent.class, listener,
  1207. BackwardHandler.backwardMethod);
  1208. }
  1209. /*
  1210. * (non-Javadoc)
  1211. *
  1212. * @see
  1213. * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier
  1214. * #addListener
  1215. * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.DateClickHandler)
  1216. */
  1217. @Override
  1218. public void setHandler(DateClickHandler listener) {
  1219. setHandler(DateClickEvent.EVENT_ID, DateClickEvent.class, listener,
  1220. DateClickHandler.dateClickMethod);
  1221. }
  1222. /*
  1223. * (non-Javadoc)
  1224. *
  1225. * @see
  1226. * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier
  1227. * #addListener
  1228. * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventClickHandler)
  1229. */
  1230. @Override
  1231. public void setHandler(EventClickHandler listener) {
  1232. setHandler(EventClick.EVENT_ID, EventClick.class, listener,
  1233. EventClickHandler.eventClickMethod);
  1234. }
  1235. /*
  1236. * (non-Javadoc)
  1237. *
  1238. * @see
  1239. * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier
  1240. * #addListener
  1241. * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.WeekClickHandler)
  1242. */
  1243. @Override
  1244. public void setHandler(WeekClickHandler listener) {
  1245. setHandler(WeekClick.EVENT_ID, WeekClick.class, listener,
  1246. WeekClickHandler.weekClickMethod);
  1247. }
  1248. /*
  1249. * (non-Javadoc)
  1250. *
  1251. * @see
  1252. * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResizeNotifier
  1253. * #addListener
  1254. * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResizeHandler
  1255. * )
  1256. */
  1257. @Override
  1258. public void setHandler(EventResizeHandler listener) {
  1259. setHandler(EventResize.EVENT_ID, EventResize.class, listener,
  1260. EventResizeHandler.eventResizeMethod);
  1261. }
  1262. /*
  1263. * (non-Javadoc)
  1264. *
  1265. * @see
  1266. * com.vaadin.addon.calendar.ui.CalendarComponentEvents.RangeSelectNotifier
  1267. * #addListener
  1268. * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.RangeSelectHandler
  1269. * )
  1270. */
  1271. @Override
  1272. public void setHandler(RangeSelectHandler listener) {
  1273. setHandler(RangeSelectEvent.EVENT_ID, RangeSelectEvent.class, listener,
  1274. RangeSelectHandler.rangeSelectMethod);
  1275. }
  1276. /*
  1277. * (non-Javadoc)
  1278. *
  1279. * @see
  1280. * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventMoveNotifier
  1281. * #addListener
  1282. * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventMoveHandler)
  1283. */
  1284. @Override
  1285. public void setHandler(EventMoveHandler listener) {
  1286. setHandler(MoveEvent.EVENT_ID, MoveEvent.class, listener,
  1287. EventMoveHandler.eventMoveMethod);
  1288. }
  1289. /*
  1290. * (non-Javadoc)
  1291. *
  1292. * @see com.vaadin.addon.calendar.ui.CalendarComponentEvents.
  1293. * CalendarEventNotifier #getHandler(java.lang.String)
  1294. */
  1295. @Override
  1296. public EventListener getHandler(String eventId) {
  1297. return handlers.get(eventId);
  1298. }
  1299. /**
  1300. * Get the currently active drop handler
  1301. */
  1302. @Override
  1303. public DropHandler getDropHandler() {
  1304. return dropHandler;
  1305. }
  1306. /**
  1307. * Set the drop handler for the calendar See {@link DropHandler} for
  1308. * implementation details.
  1309. *
  1310. * @param dropHandler
  1311. * The drop handler to set
  1312. */
  1313. public void setDropHandler(DropHandler dropHandler) {
  1314. this.dropHandler = dropHandler;
  1315. }
  1316. /*
  1317. * (non-Javadoc)
  1318. *
  1319. * @see
  1320. * com.vaadin.event.dd.DropTarget#translateDropTargetDetails(java.util.Map)
  1321. */
  1322. @Override
  1323. public TargetDetails translateDropTargetDetails(
  1324. Map<String, Object> clientVariables) {
  1325. Map<String, Object> serverVariables = new HashMap<String, Object>();
  1326. if (clientVariables.containsKey("dropSlotIndex")) {
  1327. int slotIndex = (Integer) clientVariables.get("dropSlotIndex");
  1328. int dayIndex = (Integer) clientVariables.get("dropDayIndex");
  1329. currentCalendar.setTime(getStartOfDay(currentCalendar, startDate));
  1330. currentCalendar.add(java.util.Calendar.DATE, dayIndex);
  1331. // change this if slot length is modified
  1332. currentCalendar.add(java.util.Calendar.MINUTE, slotIndex * 30);
  1333. serverVariables.put("dropTime", currentCalendar.getTime());
  1334. } else {
  1335. int dayIndex = (Integer) clientVariables.get("dropDayIndex");
  1336. currentCalendar.setTime(expandStartDate(startDate, true));
  1337. currentCalendar.add(java.util.Calendar.DATE, dayIndex);
  1338. serverVariables.put("dropDay", currentCalendar.getTime());
  1339. }
  1340. serverVariables.put("mouseEvent", clientVariables.get("mouseEvent"));
  1341. CalendarTargetDetails td = new CalendarTargetDetails(serverVariables,
  1342. this);
  1343. td.setHasDropTime(clientVariables.containsKey("dropSlotIndex"));
  1344. return td;
  1345. }
  1346. /**
  1347. * Sets a container as a data source for the events in the calendar.
  1348. * Equivalent for doing
  1349. * <code>Calendar.setEventProvider(new ContainerEventProvider(container))</code>
  1350. *
  1351. * Use this method if you are adding a container which uses the default
  1352. * property ids like {@link BeanItemContainer} for instance. If you are
  1353. * using custom properties instead use
  1354. * {@link Calendar#setContainerDataSource(com.vaadin.data.Container.Indexed, Object, Object, Object, Object, Object)}
  1355. *
  1356. * Please note that the container must be sorted by date!
  1357. *
  1358. * @param container
  1359. * The container to use as a datasource
  1360. */
  1361. public void setContainerDataSource(Container.Indexed container) {
  1362. ContainerEventProvider provider = new ContainerEventProvider(container);
  1363. provider.addEventSetChangeListener(new CalendarEventProvider.EventSetChangeListener() {
  1364. @Override
  1365. public void eventSetChange(EventSetChangeEvent changeEvent) {
  1366. // Repaint if events change
  1367. markAsDirty();
  1368. }
  1369. });
  1370. provider.addEventChangeListener(new EventChangeListener() {
  1371. @Override
  1372. public void eventChange(EventChangeEvent changeEvent) {
  1373. // Repaint if event changes
  1374. markAsDirty();
  1375. }
  1376. });
  1377. setEventProvider(provider);
  1378. }
  1379. /**
  1380. * Sets a container as a data source for the events in the calendar.
  1381. * Equivalent for doing
  1382. * <code>Calendar.setEventProvider(new ContainerEventProvider(container))</code>
  1383. *
  1384. * Please note that the container must be sorted by date!
  1385. *
  1386. * @param container
  1387. * The container to use as a data source
  1388. * @param captionProperty
  1389. * The property that has the caption, null if no caption property
  1390. * is present
  1391. * @param descriptionProperty
  1392. * The property that has the description, null if no description
  1393. * property is present
  1394. * @param startDateProperty
  1395. * The property that has the starting date
  1396. * @param endDateProperty
  1397. * The property that has the ending date
  1398. * @param styleNameProperty
  1399. * The property that has the stylename, null if no stylname
  1400. * property is present
  1401. */
  1402. public void setContainerDataSource(Container.Indexed container,
  1403. Object captionProperty, Object descriptionProperty,
  1404. Object startDateProperty, Object endDateProperty,
  1405. Object styleNameProperty) {
  1406. ContainerEventProvider provider = new ContainerEventProvider(container);
  1407. provider.setCaptionProperty(captionProperty);
  1408. provider.setDescriptionProperty(descriptionProperty);
  1409. provider.setStartDateProperty(startDateProperty);
  1410. provider.setEndDateProperty(endDateProperty);
  1411. provider.setStyleNameProperty(styleNameProperty);
  1412. provider.addEventSetChangeListener(new CalendarEventProvider.EventSetChangeListener() {
  1413. @Override
  1414. public void eventSetChange(EventSetChangeEvent changeEvent) {
  1415. // Repaint if events change
  1416. markAsDirty();
  1417. }
  1418. });
  1419. provider.addEventChangeListener(new EventChangeListener() {
  1420. @Override
  1421. public void eventChange(EventChangeEvent changeEvent) {
  1422. // Repaint if event changes
  1423. markAsDirty();
  1424. }
  1425. });
  1426. setEventProvider(provider);
  1427. }
  1428. /*
  1429. * (non-Javadoc)
  1430. *
  1431. * @see
  1432. * com.vaadin.addon.calendar.event.CalendarEventProvider#getEvents(java.
  1433. * util.Date, java.util.Date)
  1434. */
  1435. @Override
  1436. public List<CalendarEvent> getEvents(Date startDate, Date endDate) {
  1437. return getEventProvider().getEvents(startDate, endDate);
  1438. }
  1439. /*
  1440. * (non-Javadoc)
  1441. *
  1442. * @see
  1443. * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#addEvent
  1444. * (com.vaadin.addon.calendar.event.CalendarEvent)
  1445. */
  1446. @Override
  1447. public void addEvent(CalendarEvent event) {
  1448. if (getEventProvider() instanceof CalendarEditableEventProvider) {
  1449. CalendarEditableEventProvider provider = (CalendarEditableEventProvider) getEventProvider();
  1450. provider.addEvent(event);
  1451. markAsDirty();
  1452. } else {
  1453. throw new UnsupportedOperationException(
  1454. "Event provider does not support adding events");
  1455. }
  1456. }
  1457. /*
  1458. * (non-Javadoc)
  1459. *
  1460. * @see
  1461. * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#removeEvent
  1462. * (com.vaadin.addon.calendar.event.CalendarEvent)
  1463. */
  1464. @Override
  1465. public void removeEvent(CalendarEvent event) {
  1466. if (getEventProvider() instanceof CalendarEditableEventProvider) {
  1467. CalendarEditableEventProvider provider = (CalendarEditableEventProvider) getEventProvider();
  1468. provider.removeEvent(event);
  1469. markAsDirty();
  1470. } else {
  1471. throw new UnsupportedOperationException(
  1472. "Event provider does not support removing events");
  1473. }
  1474. }
  1475. /**
  1476. * Adds an action handler to the calender that handles event produced by the
  1477. * context menu.
  1478. *
  1479. * <p>
  1480. * The {@link Handler#getActions(Object, Object)} parameters depend on what
  1481. * view the Calendar is in:
  1482. * <ul>
  1483. * <li>If the Calendar is in <i>Day or Week View</i> then the target
  1484. * parameter will be a {@link CalendarDateRange} with a range of
  1485. * half-an-hour. The {@link Handler#getActions(Object, Object)} method will
  1486. * be called once per half-hour slot.</li>
  1487. * <li>If the Calendar is in <i>Month View</i> then the target parameter
  1488. * will be a {@link CalendarDateRange} with a range of one day. The
  1489. * {@link Handler#getActions(Object, Object)} will be called once for each
  1490. * day.
  1491. * </ul>
  1492. * The Dates passed into the {@link CalendarDateRange} are in the same
  1493. * timezone as the calendar is.
  1494. * </p>
  1495. *
  1496. * <p>
  1497. * The {@link Handler#handleAction(Action, Object, Object)} parameters
  1498. * depend on what the context menu is called upon:
  1499. * <ul>
  1500. * <li>If the context menu is called upon an event then the target parameter
  1501. * is the event, i.e. instanceof {@link CalendarEvent}</li>
  1502. * <li>If the context menu is called upon an empty slot then the target is a
  1503. * {@link Date} representing that slot
  1504. * </ul>
  1505. * </p>
  1506. */
  1507. @Override
  1508. public void addActionHandler(Handler actionHandler) {
  1509. if (actionHandler != null) {
  1510. if (actionHandlers == null) {
  1511. actionHandlers = new LinkedList<Action.Handler>();
  1512. actionMapper = new KeyMapper<Action>();
  1513. }
  1514. if (!actionHandlers.contains(actionHandler)) {
  1515. actionHandlers.add(actionHandler);
  1516. markAsDirty();
  1517. }
  1518. }
  1519. }
  1520. /**
  1521. * Is the calendar in a mode where all days of the month is shown
  1522. *
  1523. * @return Returns true if calendar is in monthly mode and false if it is in
  1524. * weekly mode
  1525. */
  1526. public boolean isMonthlyMode() {
  1527. CalendarState state = getState(false);
  1528. if (state.days != null) {
  1529. return state.days.size() > 7;
  1530. } else {
  1531. // Default mode
  1532. return true;
  1533. }
  1534. }
  1535. /*
  1536. * (non-Javadoc)
  1537. *
  1538. * @see
  1539. * com.vaadin.event.Action.Container#removeActionHandler(com.vaadin.event
  1540. * .Action.Handler)
  1541. */
  1542. @Override
  1543. public void removeActionHandler(Handler actionHandler) {
  1544. if (actionHandlers != null && actionHandlers.contains(actionHandler)) {
  1545. actionHandlers.remove(actionHandler);
  1546. if (actionHandlers.isEmpty()) {
  1547. actionHandlers = null;
  1548. actionMapper = null;
  1549. }
  1550. markAsDirty();
  1551. }
  1552. }
  1553. private class CalendarServerRpcImpl implements CalendarServerRpc {
  1554. @Override
  1555. public void eventMove(int eventIndex, String newDate) {
  1556. if (!isClientChangeAllowed()) {
  1557. return;
  1558. }
  1559. if (newDate != null) {
  1560. try {
  1561. Date d = df_date_time.parse(newDate);
  1562. if (eventIndex >= 0 && eventIndex < events.size()
  1563. && events.get(eventIndex) != null) {
  1564. fireEventMove(eventIndex, d);
  1565. }
  1566. } catch (ParseException e) {
  1567. getLogger().log(Level.WARNING, e.getMessage());
  1568. }
  1569. }
  1570. }
  1571. @Override
  1572. public void rangeSelect(String range) {
  1573. if (!isClientChangeAllowed()) {
  1574. return;
  1575. }
  1576. if (range != null && range.length() > 14 && range.contains("TO")) {
  1577. String[] dates = range.split("TO");
  1578. try {
  1579. Date d1 = df_date.parse(dates[0]);
  1580. Date d2 = df_date.parse(dates[1]);
  1581. fireRangeSelect(d1, d2, true);
  1582. } catch (ParseException e) {
  1583. // NOP
  1584. }
  1585. } else if (range != null && range.length() > 12
  1586. && range.contains(":")) {
  1587. String[] dates = range.split(":");
  1588. if (dates.length == 3) {
  1589. try {
  1590. Date d = df_date.parse(dates[0]);
  1591. currentCalendar.setTime(d);
  1592. int startMinutes = Integer.parseInt(dates[1]);
  1593. int endMinutes = Integer.parseInt(dates[2]);
  1594. currentCalendar.add(java.util.Calendar.MINUTE,
  1595. startMinutes);
  1596. Date start = currentCalendar.getTime();
  1597. currentCalendar.add(java.util.Calendar.MINUTE,
  1598. endMinutes - startMinutes);
  1599. Date end = currentCalendar.getTime();
  1600. fireRangeSelect(start, end, false);
  1601. } catch (ParseException e) {
  1602. // NOP
  1603. } catch (NumberFormatException e) {
  1604. // NOP
  1605. }
  1606. }
  1607. }
  1608. }
  1609. @Override
  1610. public void forward() {
  1611. fireEvent(new ForwardEvent(Calendar.this));
  1612. }
  1613. @Override
  1614. public void backward() {
  1615. fireEvent(new BackwardEvent(Calendar.this));
  1616. }
  1617. @Override
  1618. public void dateClick(String date) {
  1619. if (date != null && date.length() > 6) {
  1620. try {
  1621. Date d = df_date.parse(date);
  1622. fireDateClick(d);
  1623. } catch (ParseException e) {
  1624. }
  1625. }
  1626. }
  1627. @Override
  1628. public void weekClick(String event) {
  1629. if (event.length() > 0 && event.contains("w")) {
  1630. String[] splitted = event.split("w");
  1631. if (splitted.length == 2) {
  1632. try {
  1633. int yr = Integer.parseInt(splitted[0]);
  1634. int week = Integer.parseInt(splitted[1]);
  1635. fireWeekClick(week, yr);
  1636. } catch (NumberFormatException e) {
  1637. // NOP
  1638. }
  1639. }
  1640. }
  1641. }
  1642. @Override
  1643. public void eventClick(int eventIndex) {
  1644. if (!isEventClickAllowed()) {
  1645. return;
  1646. }
  1647. if (eventIndex >= 0 && eventIndex < events.size()
  1648. && events.get(eventIndex) != null) {
  1649. fireEventClick(eventIndex);
  1650. }
  1651. }
  1652. @Override
  1653. public void eventResize(int eventIndex, String newStartDate,
  1654. String newEndDate) {
  1655. if (!isClientChangeAllowed()) {
  1656. return;
  1657. }
  1658. if (newStartDate != null && !"".equals(newStartDate)
  1659. && newEndDate != null && !"".equals(newEndDate)) {
  1660. try {
  1661. Date newStartTime = df_date_time.parse(newStartDate);
  1662. Date newEndTime = df_date_time.parse(newEndDate);
  1663. fireEventResize(eventIndex, newStartTime, newEndTime);
  1664. } catch (ParseException e) {
  1665. // NOOP
  1666. }
  1667. }
  1668. }
  1669. @Override
  1670. public void scroll(int scrollPosition) {
  1671. scrollTop = scrollPosition;
  1672. markAsDirty();
  1673. }
  1674. @Override
  1675. public void actionOnEmptyCell(String actionKey, String startDate,
  1676. String endDate) {
  1677. Action action = actionMapper.get(actionKey);
  1678. SimpleDateFormat formatter = new SimpleDateFormat(
  1679. DateConstants.ACTION_DATE_FORMAT_PATTERN);
  1680. formatter.setTimeZone(getTimeZone());
  1681. try {
  1682. Date start = formatter.parse(startDate);
  1683. for (Action.Handler ah : actionHandlers) {
  1684. ah.handleAction(action, Calendar.this, start);
  1685. }
  1686. } catch (ParseException e) {
  1687. getLogger().log(Level.WARNING,
  1688. "Could not parse action date string");
  1689. }
  1690. }
  1691. @Override
  1692. public void actionOnEvent(String actionKey, String startDate,
  1693. String endDate, int eventIndex) {
  1694. Action action = actionMapper.get(actionKey);
  1695. SimpleDateFormat formatter = new SimpleDateFormat(
  1696. DateConstants.ACTION_DATE_FORMAT_PATTERN);
  1697. formatter.setTimeZone(getTimeZone());
  1698. for (Action.Handler ah : actionHandlers) {
  1699. ah.handleAction(action, Calendar.this, events.get(eventIndex));
  1700. }
  1701. }
  1702. }
  1703. /*
  1704. * (non-Javadoc)
  1705. *
  1706. * @see com.vaadin.server.VariableOwner#changeVariables(java.lang.Object,
  1707. * java.util.Map)
  1708. */
  1709. @Override
  1710. public void changeVariables(Object source, Map<String, Object> variables) {
  1711. /*
  1712. * Only defined to fulfill the LegacyComponent interface used for
  1713. * calendar drag & drop. No implementation required.
  1714. */
  1715. }
  1716. /*
  1717. * (non-Javadoc)
  1718. *
  1719. * @see
  1720. * com.vaadin.ui.LegacyComponent#paintContent(com.vaadin.server.PaintTarget)
  1721. */
  1722. @Override
  1723. public void paintContent(PaintTarget target) throws PaintException {
  1724. if (dropHandler != null) {
  1725. dropHandler.getAcceptCriterion().paint(target);
  1726. }
  1727. }
  1728. /**
  1729. * Sets whether the event captions are rendered as HTML.
  1730. * <p>
  1731. * If set to true, the captions are rendered in the browser as HTML and the
  1732. * developer is responsible for ensuring no harmful HTML is used. If set to
  1733. * false, the caption is rendered in the browser as plain text.
  1734. * <p>
  1735. * The default is false, i.e. to render that caption as plain text.
  1736. *
  1737. * @param captionAsHtml
  1738. * true if the captions are rendered as HTML, false if rendered
  1739. * as plain text
  1740. */
  1741. public void setEventCaptionAsHtml(boolean eventCaptionAsHtml) {
  1742. getState().eventCaptionAsHtml = eventCaptionAsHtml;
  1743. }
  1744. /**
  1745. * Checks whether event captions are rendered as HTML
  1746. * <p>
  1747. * The default is false, i.e. to render that caption as plain text.
  1748. *
  1749. * @return true if the captions are rendered as HTML, false if rendered as
  1750. * plain text
  1751. */
  1752. public boolean isEventCaptionAsHtml() {
  1753. return getState(false).eventCaptionAsHtml;
  1754. }
  1755. @Override
  1756. public void readDesign(Element design, DesignContext designContext) {
  1757. super.readDesign(design, designContext);
  1758. Attributes attr = design.attributes();
  1759. if (design.hasAttr("time-format")) {
  1760. setTimeFormat(TimeFormat.valueOf("Format"
  1761. + design.attr("time-format").toUpperCase()));
  1762. }
  1763. if (design.hasAttr("start-date")) {
  1764. setStartDate(DesignAttributeHandler.readAttribute("start-date",
  1765. attr, Date.class));
  1766. }
  1767. if (design.hasAttr("end-date")) {
  1768. setEndDate(DesignAttributeHandler.readAttribute("end-date", attr,
  1769. Date.class));
  1770. }
  1771. };
  1772. @Override
  1773. public void writeDesign(Element design, DesignContext designContext) {
  1774. super.writeDesign(design, designContext);
  1775. if (currentTimeFormat != null) {
  1776. design.attr("time-format",
  1777. (currentTimeFormat == TimeFormat.Format12H ? "12h" : "24h"));
  1778. }
  1779. if (startDate != null) {
  1780. design.attr("start-date", df_date.format(getStartDate()));
  1781. }
  1782. if (endDate != null) {
  1783. design.attr("end-date", df_date.format(getEndDate()));
  1784. }
  1785. if (!getTimeZone().equals(TimeZone.getDefault())) {
  1786. design.attr("time-zone", getTimeZone().getID());
  1787. }
  1788. }
  1789. @Override
  1790. protected Collection<String> getCustomAttributes() {
  1791. Collection<String> customAttributes = super.getCustomAttributes();
  1792. customAttributes.add("time-format");
  1793. customAttributes.add("start-date");
  1794. customAttributes.add("end-date");
  1795. return customAttributes;
  1796. }
  1797. /**
  1798. * Allow setting first day of week depending on Locale. Set to null if you
  1799. * want first day of week depend on locale
  1800. *
  1801. * @since 7.6
  1802. * @param dayOfWeek
  1803. */
  1804. public void setFirstDayOfWeek(Integer dayOfWeek) {
  1805. int minimalSupported = java.util.Calendar.SUNDAY;
  1806. int maximalSupported = java.util.Calendar.SATURDAY;
  1807. if (dayOfWeek != null
  1808. && (dayOfWeek < minimalSupported || dayOfWeek > maximalSupported)) {
  1809. throw new IllegalArgumentException(
  1810. String.format(
  1811. "Day of week must be between %s and %s. Actually received: %s",
  1812. minimalSupported, maximalSupported, dayOfWeek));
  1813. }
  1814. customFirstDayOfWeek = dayOfWeek;
  1815. markAsDirty();
  1816. }
  1817. }