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.

ICalendarPanel.java 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.itmill.toolkit.terminal.gwt.client.ui;
  5. import java.util.Date;
  6. import java.util.Iterator;
  7. import java.util.List;
  8. import com.google.gwt.user.client.DOM;
  9. import com.google.gwt.user.client.Event;
  10. import com.google.gwt.user.client.Timer;
  11. import com.google.gwt.user.client.ui.ClickListener;
  12. import com.google.gwt.user.client.ui.FlexTable;
  13. import com.google.gwt.user.client.ui.MouseListener;
  14. import com.google.gwt.user.client.ui.MouseListenerCollection;
  15. import com.google.gwt.user.client.ui.SourcesMouseEvents;
  16. import com.google.gwt.user.client.ui.SourcesTableEvents;
  17. import com.google.gwt.user.client.ui.TableListener;
  18. import com.google.gwt.user.client.ui.Widget;
  19. import com.itmill.toolkit.terminal.gwt.client.DateTimeService;
  20. import com.itmill.toolkit.terminal.gwt.client.LocaleService;
  21. public class ICalendarPanel extends FlexTable implements MouseListener,
  22. ClickListener {
  23. private final IDateField datefield;
  24. private IEventButton prevYear;
  25. private IEventButton nextYear;
  26. private IEventButton prevMonth;
  27. private IEventButton nextMonth;
  28. private ITime time;
  29. private Date minDate = null;
  30. private Date maxDate = null;
  31. private CalendarEntrySource entrySource;
  32. /* Needed to identify resolution changes */
  33. private int resolution = IDateField.RESOLUTION_YEAR;
  34. /* Needed to identify locale changes */
  35. private String locale = LocaleService.getDefaultLocale();
  36. public ICalendarPanel(IDateField parent) {
  37. datefield = parent;
  38. setStyleName(IDateField.CLASSNAME + "-calendarpanel");
  39. // buildCalendar(true);
  40. addTableListener(new DateClickListener(this));
  41. }
  42. public ICalendarPanel(IDateField parent, Date min, Date max) {
  43. datefield = parent;
  44. setStyleName(IDateField.CLASSNAME + "-calendarpanel");
  45. // buildCalendar(true);
  46. addTableListener(new DateClickListener(this));
  47. }
  48. private void buildCalendar(boolean forceRedraw) {
  49. final boolean needsMonth = datefield.getCurrentResolution() > IDateField.RESOLUTION_YEAR;
  50. boolean needsBody = datefield.getCurrentResolution() >= IDateField.RESOLUTION_DAY;
  51. final boolean needsTime = datefield.getCurrentResolution() >= IDateField.RESOLUTION_HOUR;
  52. buildCalendarHeader(forceRedraw, needsMonth);
  53. clearCalendarBody(!needsBody);
  54. if (needsBody) {
  55. buildCalendarBody();
  56. }
  57. if (needsTime) {
  58. buildTime(forceRedraw);
  59. } else if (time != null) {
  60. remove(time);
  61. time = null;
  62. }
  63. }
  64. private void clearCalendarBody(boolean remove) {
  65. if (!remove) {
  66. for (int row = 2; row < 8; row++) {
  67. for (int col = 0; col < 7; col++) {
  68. setHTML(row, col, "&nbsp;");
  69. }
  70. }
  71. } else if (getRowCount() > 2) {
  72. while (getRowCount() > 2) {
  73. removeRow(2);
  74. }
  75. }
  76. }
  77. private void buildCalendarHeader(boolean forceRedraw, boolean needsMonth) {
  78. if (forceRedraw) {
  79. if (prevMonth == null) { // Only do once
  80. prevYear = new IEventButton();
  81. prevYear.setHTML("&laquo;");
  82. prevYear.setStyleName("i-button-prevyear");
  83. nextYear = new IEventButton();
  84. nextYear.setHTML("&raquo;");
  85. nextYear.setStyleName("i-button-nextyear");
  86. prevYear.addMouseListener(this);
  87. nextYear.addMouseListener(this);
  88. prevYear.addClickListener(this);
  89. nextYear.addClickListener(this);
  90. setWidget(0, 0, prevYear);
  91. setWidget(0, 4, nextYear);
  92. if (needsMonth) {
  93. prevMonth = new IEventButton();
  94. prevMonth.setHTML("&lsaquo;");
  95. prevMonth.setStyleName("i-button-prevmonth");
  96. nextMonth = new IEventButton();
  97. nextMonth.setHTML("&rsaquo;");
  98. nextMonth.setStyleName("i-button-nextmonth");
  99. prevMonth.addMouseListener(this);
  100. nextMonth.addMouseListener(this);
  101. prevMonth.addClickListener(this);
  102. nextMonth.addClickListener(this);
  103. setWidget(0, 3, nextMonth);
  104. setWidget(0, 1, prevMonth);
  105. }
  106. getFlexCellFormatter().setColSpan(0, 2, 3);
  107. getRowFormatter().addStyleName(0,
  108. IDateField.CLASSNAME + "-calendarpanel-header");
  109. } else if (!needsMonth) {
  110. // Remove month traverse buttons
  111. prevMonth.removeClickListener(this);
  112. prevMonth.removeMouseListener(this);
  113. nextMonth.removeClickListener(this);
  114. nextMonth.removeMouseListener(this);
  115. remove(prevMonth);
  116. remove(nextMonth);
  117. prevMonth = null;
  118. nextMonth = null;
  119. }
  120. // Print weekday names
  121. final int firstDay = datefield.getDateTimeService()
  122. .getFirstDayOfWeek();
  123. for (int i = 0; i < 7; i++) {
  124. int day = i + firstDay;
  125. if (day > 6) {
  126. day = 0;
  127. }
  128. if (datefield.getCurrentResolution() > IDateField.RESOLUTION_MONTH) {
  129. setHTML(1, i, "<strong>"
  130. + datefield.getDateTimeService().getShortDay(day)
  131. + "</strong>");
  132. } else {
  133. setHTML(1, i, "");
  134. }
  135. }
  136. }
  137. final String monthName = needsMonth ? datefield.getDateTimeService()
  138. .getMonth(datefield.getShowingDate().getMonth()) : "";
  139. final int year = datefield.getShowingDate().getYear() + 1900;
  140. setHTML(0, 2, "<span class=\"" + IDateField.CLASSNAME
  141. + "-calendarpanel-month\">" + monthName + " " + year
  142. + "</span>");
  143. }
  144. private void buildCalendarBody() {
  145. // date actually selected?
  146. Date currentDate = datefield.getCurrentDate();
  147. Date showing = datefield.getShowingDate();
  148. boolean selected = (currentDate != null
  149. && currentDate.getMonth() == showing.getMonth() && currentDate
  150. .getYear() == showing.getYear());
  151. final int startWeekDay = datefield.getDateTimeService()
  152. .getStartWeekDay(datefield.getShowingDate());
  153. final int numDays = DateTimeService.getNumberOfDaysInMonth(datefield
  154. .getShowingDate());
  155. int dayCount = 0;
  156. final Date today = new Date();
  157. final Date curr = new Date(datefield.getShowingDate().getTime());
  158. for (int row = 2; row < 8; row++) {
  159. for (int col = 0; col < 7; col++) {
  160. if (!(row == 2 && col < startWeekDay)) {
  161. if (dayCount < numDays) {
  162. final int selectedDate = ++dayCount;
  163. String title = "";
  164. if (entrySource != null) {
  165. curr.setDate(dayCount);
  166. final List entries = entrySource.getEntries(curr,
  167. IDateField.RESOLUTION_DAY);
  168. if (entries != null) {
  169. for (final Iterator it = entries.iterator(); it
  170. .hasNext();) {
  171. final CalendarEntry entry = (CalendarEntry) it
  172. .next();
  173. title += (title.length() > 0 ? ", " : "")
  174. + entry.getStringForDate(curr);
  175. }
  176. }
  177. }
  178. final String baseclass = IDateField.CLASSNAME
  179. + "-calendarpanel-day";
  180. String cssClass = baseclass;
  181. if (!isEnabledDate(curr)) {
  182. cssClass += " " + baseclass + "-disabled";
  183. }
  184. if (selected
  185. && datefield.getShowingDate().getDate() == dayCount) {
  186. cssClass += " " + baseclass + "-selected";
  187. }
  188. if (today.getDate() == dayCount
  189. && today.getMonth() == datefield
  190. .getShowingDate().getMonth()
  191. && today.getYear() == datefield
  192. .getShowingDate().getYear()) {
  193. cssClass += " " + baseclass + "-today";
  194. }
  195. if (title.length() > 0) {
  196. cssClass += " " + baseclass + "-entry";
  197. }
  198. setHTML(row, col, "<span title=\"" + title
  199. + "\" class=\"" + cssClass + "\">"
  200. + selectedDate + "</span>");
  201. } else {
  202. break;
  203. }
  204. }
  205. }
  206. }
  207. }
  208. private void buildTime(boolean forceRedraw) {
  209. if (time == null) {
  210. time = new ITime(datefield);
  211. setText(8, 0, ""); // Add new row
  212. getFlexCellFormatter().setColSpan(8, 0, 7);
  213. setWidget(8, 0, time);
  214. }
  215. time.updateTime(forceRedraw);
  216. }
  217. /**
  218. *
  219. * @param forceRedraw
  220. * Build all from scratch, in case of e.g. locale changes
  221. */
  222. public void updateCalendar() {
  223. // Locale and resolution changes force a complete redraw
  224. buildCalendar(locale != datefield.getCurrentLocale()
  225. || resolution != datefield.getCurrentResolution());
  226. if (datefield instanceof ITextualDate) {
  227. ((ITextualDate) datefield).buildDate();
  228. }
  229. locale = datefield.getCurrentLocale();
  230. resolution = datefield.getCurrentResolution();
  231. }
  232. public void onClick(Widget sender) {
  233. // processClickEvent(sender, true);
  234. }
  235. private boolean isEnabledDate(Date date) {
  236. if ((minDate != null && date.before(minDate))
  237. || (maxDate != null && date.after(maxDate))) {
  238. return false;
  239. }
  240. return true;
  241. }
  242. private void processClickEvent(Widget sender, boolean updateVariable) {
  243. if (!datefield.isEnabled() || datefield.isReadonly()) {
  244. return;
  245. }
  246. Date showingDate = datefield.getShowingDate();
  247. if (!updateVariable) {
  248. if (sender == prevYear) {
  249. showingDate.setYear(showingDate.getYear() - 1);
  250. updateCalendar();
  251. } else if (sender == nextYear) {
  252. showingDate.setYear(showingDate.getYear() + 1);
  253. updateCalendar();
  254. } else if (sender == prevMonth) {
  255. showingDate.setMonth(showingDate.getMonth() - 1);
  256. updateCalendar();
  257. } else if (sender == nextMonth) {
  258. showingDate.setMonth(showingDate.getMonth() + 1);
  259. updateCalendar();
  260. }
  261. } else {
  262. if (datefield.getCurrentResolution() == IDateField.RESOLUTION_YEAR
  263. || datefield.getCurrentResolution() == IDateField.RESOLUTION_MONTH) {
  264. // Due to current UI, update variable if res=year/month
  265. datefield.setCurrentDate(new Date(showingDate.getTime()));
  266. if (datefield.getCurrentResolution() == IDateField.RESOLUTION_MONTH) {
  267. datefield.getClient().updateVariable(datefield.getId(),
  268. "month", datefield.getCurrentDate().getMonth() + 1,
  269. false);
  270. }
  271. datefield.getClient().updateVariable(datefield.getId(), "year",
  272. datefield.getCurrentDate().getYear() + 1900,
  273. datefield.isImmediate());
  274. }
  275. }
  276. }
  277. private Timer timer;
  278. public void onMouseDown(final Widget sender, int x, int y) {
  279. if (sender instanceof IEventButton) {
  280. processClickEvent(sender, false);
  281. timer = new Timer() {
  282. public void run() {
  283. processClickEvent(sender, false);
  284. }
  285. };
  286. timer.scheduleRepeating(100);
  287. }
  288. }
  289. public void onMouseEnter(Widget sender) {
  290. }
  291. public void onMouseLeave(Widget sender) {
  292. if (timer != null) {
  293. timer.cancel();
  294. }
  295. }
  296. public void onMouseMove(Widget sender, int x, int y) {
  297. }
  298. public void onMouseUp(Widget sender, int x, int y) {
  299. if (timer != null) {
  300. timer.cancel();
  301. }
  302. processClickEvent(sender, true);
  303. }
  304. private class IEventButton extends IButton implements SourcesMouseEvents {
  305. private MouseListenerCollection mouseListeners;
  306. public IEventButton() {
  307. super();
  308. sinkEvents(Event.FOCUSEVENTS | Event.KEYEVENTS | Event.ONCLICK
  309. | Event.MOUSEEVENTS);
  310. }
  311. public void addMouseListener(MouseListener listener) {
  312. if (mouseListeners == null) {
  313. mouseListeners = new MouseListenerCollection();
  314. }
  315. mouseListeners.add(listener);
  316. }
  317. public void removeMouseListener(MouseListener listener) {
  318. if (mouseListeners != null) {
  319. mouseListeners.remove(listener);
  320. }
  321. }
  322. public void onBrowserEvent(Event event) {
  323. super.onBrowserEvent(event);
  324. switch (DOM.eventGetType(event)) {
  325. case Event.ONMOUSEDOWN:
  326. case Event.ONMOUSEUP:
  327. case Event.ONMOUSEMOVE:
  328. case Event.ONMOUSEOVER:
  329. case Event.ONMOUSEOUT:
  330. if (mouseListeners != null) {
  331. mouseListeners.fireMouseEvent(this, event);
  332. }
  333. break;
  334. }
  335. }
  336. }
  337. private class DateClickListener implements TableListener {
  338. private final ICalendarPanel cal;
  339. public DateClickListener(ICalendarPanel panel) {
  340. cal = panel;
  341. }
  342. public void onCellClicked(SourcesTableEvents sender, int row, int col) {
  343. if (sender != cal || row < 2 || row > 7
  344. || !cal.datefield.isEnabled() || cal.datefield.isReadonly()) {
  345. return;
  346. }
  347. final String text = cal.getText(row, col);
  348. if (text.equals(" ")) {
  349. return;
  350. }
  351. try {
  352. final Integer day = new Integer(text);
  353. final Date newDate = cal.datefield.getShowingDate();
  354. newDate.setDate(day.intValue());
  355. if (!isEnabledDate(newDate)) {
  356. return;
  357. }
  358. if (cal.datefield.getCurrentDate() == null) {
  359. cal.datefield.setCurrentDate(new Date(newDate.getTime()));
  360. // Init variables with current time
  361. datefield.getClient().updateVariable(cal.datefield.getId(),
  362. "hour", newDate.getHours(), false);
  363. datefield.getClient().updateVariable(cal.datefield.getId(),
  364. "min", newDate.getMinutes(), false);
  365. datefield.getClient().updateVariable(cal.datefield.getId(),
  366. "sec", newDate.getSeconds(), false);
  367. datefield.getClient().updateVariable(cal.datefield.getId(),
  368. "msec", datefield.getMilliseconds(), false);
  369. }
  370. cal.datefield.getCurrentDate().setTime(newDate.getTime());
  371. cal.datefield.getClient().updateVariable(cal.datefield.getId(),
  372. "day", cal.datefield.getCurrentDate().getDate(), false);
  373. cal.datefield.getClient().updateVariable(cal.datefield.getId(),
  374. "month", cal.datefield.getCurrentDate().getMonth() + 1,
  375. false);
  376. cal.datefield.getClient().updateVariable(cal.datefield.getId(),
  377. "year",
  378. cal.datefield.getCurrentDate().getYear() + 1900,
  379. cal.datefield.isImmediate());
  380. if (datefield instanceof ITextualDate
  381. && resolution < IDateField.RESOLUTION_HOUR) {
  382. ((IToolkitOverlay) getParent()).hide();
  383. } else {
  384. updateCalendar();
  385. }
  386. } catch (final NumberFormatException e) {
  387. // Not a number, ignore and stop here
  388. return;
  389. }
  390. }
  391. }
  392. public void setLimits(Date min, Date max) {
  393. if (min != null) {
  394. final Date d = new Date(min.getTime());
  395. d.setHours(0);
  396. d.setMinutes(0);
  397. d.setSeconds(1);
  398. minDate = d;
  399. } else {
  400. minDate = null;
  401. }
  402. if (max != null) {
  403. final Date d = new Date(max.getTime());
  404. d.setHours(24);
  405. d.setMinutes(59);
  406. d.setSeconds(59);
  407. maxDate = d;
  408. } else {
  409. maxDate = null;
  410. }
  411. }
  412. public void setCalendarEntrySource(CalendarEntrySource entrySource) {
  413. this.entrySource = entrySource;
  414. }
  415. public CalendarEntrySource getCalendarEntrySource() {
  416. return entrySource;
  417. }
  418. public interface CalendarEntrySource {
  419. public List getEntries(Date date, int resolution);
  420. }
  421. /**
  422. * Sets focus to Calendar panel.
  423. *
  424. * @param focus
  425. */
  426. public void setFocus(boolean focus) {
  427. nextYear.setFocus(focus);
  428. }
  429. }