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.

VPopupCalendar.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.terminal.gwt.client.ui;
  5. import java.util.Date;
  6. import com.google.gwt.core.client.GWT;
  7. import com.google.gwt.event.dom.client.ClickEvent;
  8. import com.google.gwt.event.dom.client.ClickHandler;
  9. import com.google.gwt.event.dom.client.DomEvent;
  10. import com.google.gwt.event.dom.client.KeyCodes;
  11. import com.google.gwt.event.logical.shared.CloseEvent;
  12. import com.google.gwt.event.logical.shared.CloseHandler;
  13. import com.google.gwt.user.client.DOM;
  14. import com.google.gwt.user.client.Event;
  15. import com.google.gwt.user.client.Timer;
  16. import com.google.gwt.user.client.Window;
  17. import com.google.gwt.user.client.ui.Button;
  18. import com.google.gwt.user.client.ui.PopupPanel;
  19. import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
  20. import com.vaadin.terminal.gwt.client.ApplicationConnection;
  21. import com.vaadin.terminal.gwt.client.DateTimeService;
  22. import com.vaadin.terminal.gwt.client.Paintable;
  23. import com.vaadin.terminal.gwt.client.UIDL;
  24. import com.vaadin.terminal.gwt.client.ui.VCalendarPanel.FocusOutListener;
  25. import com.vaadin.terminal.gwt.client.ui.VCalendarPanel.SubmitListener;
  26. import com.vaadin.terminal.gwt.client.ui.VCalendarPanel.TimeChangeListener;
  27. import com.vaadin.terminal.gwt.client.ui.VCalendarPanel.ValueChangeListener;
  28. /**
  29. * Represents a date selection component with a text field and a popup date
  30. * selector.
  31. *
  32. * <b>Note:</b> To change the keyboard assignments used in the popup dialog you
  33. * should extend <code>com.vaadin.terminal.gwt.client.ui.VCalendarPanel</code>
  34. * and then pass set it by calling the
  35. * <code>setCalendarPanel(VCalendarPanel panel)</code> method.
  36. *
  37. */
  38. public class VPopupCalendar extends VTextualDate implements Paintable, Field,
  39. ClickHandler, CloseHandler<PopupPanel> {
  40. private final Button calendarToggle;
  41. private VCalendarPanel calendar;
  42. private final VOverlay popup;
  43. private boolean open = false;
  44. private boolean parsable = true;
  45. public VPopupCalendar() {
  46. super();
  47. calendarToggle = new Button();
  48. calendarToggle.setStyleName(CLASSNAME + "-button");
  49. calendarToggle.setText("");
  50. calendarToggle.addClickHandler(this);
  51. calendarToggle.getElement().setTabIndex(-1);
  52. add(calendarToggle);
  53. calendar = GWT.create(VCalendarPanel.class);
  54. calendar.setFocusOutListener(new FocusOutListener() {
  55. public boolean onFocusOut(DomEvent event) {
  56. ApplicationConnection.getConsole().log(
  57. "Focus out event, due to "
  58. + event.getNativeEvent().getType());
  59. event.preventDefault();
  60. closeCalendarPanel();
  61. return true;
  62. }
  63. });
  64. calendar.setSubmitListener(new SubmitListener() {
  65. public void onSubmit() {
  66. updateValue(calendar.getDate());
  67. closeCalendarPanel();
  68. }
  69. public void onCancel() {
  70. closeCalendarPanel();
  71. }
  72. });
  73. popup = new VOverlay(true, true, true);
  74. popup.setStyleName(VDateField.CLASSNAME + "-popup");
  75. popup.setWidget(calendar);
  76. popup.addCloseHandler(this);
  77. DOM.setElementProperty(calendar.getElement(), "id",
  78. "PID_VAADIN_POPUPCAL");
  79. sinkEvents(Event.ONKEYDOWN);
  80. }
  81. private void updateValue(Date newDate) {
  82. Date currentDate = getCurrentDate();
  83. if (currentDate == null || newDate.getTime() != currentDate.getTime()) {
  84. setCurrentDate(newDate);
  85. getClient().updateVariable(getId(), "year",
  86. newDate.getYear() + 1900, false);
  87. if (getCurrentResolution() > VDateField.RESOLUTION_YEAR) {
  88. getClient().updateVariable(getId(), "month",
  89. newDate.getMonth() + 1, false);
  90. if (getCurrentResolution() > RESOLUTION_MONTH) {
  91. getClient().updateVariable(getId(), "day",
  92. newDate.getDate(), false);
  93. if (getCurrentResolution() > RESOLUTION_DAY) {
  94. getClient().updateVariable(getId(), "hour",
  95. newDate.getHours(), false);
  96. if (getCurrentResolution() > RESOLUTION_HOUR) {
  97. getClient().updateVariable(getId(), "min",
  98. newDate.getMinutes(), false);
  99. if (getCurrentResolution() > RESOLUTION_MIN) {
  100. getClient().updateVariable(getId(), "sec",
  101. newDate.getSeconds(), false);
  102. if (getCurrentResolution() == RESOLUTION_MSEC) {
  103. getClient().updateVariable(
  104. getId(),
  105. "msec",
  106. DateTimeService
  107. .getMilliseconds(newDate),
  108. false);
  109. }
  110. }
  111. }
  112. }
  113. }
  114. }
  115. if (isImmediate()) {
  116. getClient().sendPendingVariableChanges();
  117. }
  118. }
  119. }
  120. /*
  121. * (non-Javadoc)
  122. *
  123. * @see
  124. * com.vaadin.terminal.gwt.client.ui.VTextualDate#updateFromUIDL(com.vaadin
  125. * .terminal.gwt.client.UIDL,
  126. * com.vaadin.terminal.gwt.client.ApplicationConnection)
  127. */
  128. @Override
  129. public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
  130. boolean lastReadOnlyState = readonly;
  131. parsable = uidl.getBooleanAttribute("parsable");
  132. super.updateFromUIDL(uidl, client);
  133. popup.setStyleName(VDateField.CLASSNAME + "-popup "
  134. + VDateField.CLASSNAME + "-"
  135. + resolutionToString(currentResolution));
  136. calendar.setDateTimeService(getDateTimeService());
  137. calendar.setShowISOWeekNumbers(isShowISOWeekNumbers());
  138. calendar.setResolution(currentResolution);
  139. calendarToggle.setEnabled(enabled);
  140. if (currentResolution <= RESOLUTION_MONTH) {
  141. calendar.setValueChangeListener(new ValueChangeListener() {
  142. public void changed(Date date) {
  143. setCurrentDate(date);
  144. buildDate();
  145. }
  146. });
  147. }
  148. if (currentResolution > RESOLUTION_DAY) {
  149. calendar.setTimeChangeListener(new TimeChangeListener() {
  150. public void changed(int hour, int min, int sec, int msec) {
  151. Date d = (Date) date.clone();
  152. d.setHours(hour);
  153. d.setMinutes(min);
  154. d.setSeconds(sec);
  155. DateTimeService.setMilliseconds(d, msec);
  156. // Always update time changes to the server
  157. updateValue(d);
  158. // Update text field
  159. buildDate();
  160. }
  161. });
  162. }
  163. if (readonly) {
  164. calendarToggle.addStyleName(CLASSNAME + "-button-readonly");
  165. } else {
  166. calendarToggle.removeStyleName(CLASSNAME + "-button-readonly");
  167. }
  168. if (lastReadOnlyState != readonly) {
  169. updateWidth();
  170. }
  171. calendarToggle.setEnabled(true);
  172. }
  173. /*
  174. * (non-Javadoc)
  175. *
  176. * @see
  177. * com.google.gwt.user.client.ui.UIObject#setStyleName(java.lang.String)
  178. */
  179. @Override
  180. public void setStyleName(String style) {
  181. // make sure the style is there before size calculation
  182. super.setStyleName(style + " " + CLASSNAME + "-popupcalendar");
  183. }
  184. /**
  185. * Opens the calendar panel popup
  186. */
  187. public void openCalendarPanel() {
  188. if (!open && !readonly) {
  189. open = true;
  190. final Date start = new Date();
  191. if (getCurrentDate() != null) {
  192. calendar.setDate((Date) getCurrentDate().clone());
  193. } else {
  194. calendar.setDate(new Date());
  195. }
  196. // clear previous values
  197. popup.setWidth("");
  198. popup.setHeight("");
  199. popup.setPopupPositionAndShow(new PositionCallback() {
  200. public void setPosition(int offsetWidth, int offsetHeight) {
  201. final int w = offsetWidth;
  202. final int h = offsetHeight;
  203. final int browserWindowWidth = Window.getClientWidth()
  204. + Window.getScrollLeft();
  205. final int browserWindowHeight = Window.getClientHeight()
  206. + Window.getScrollTop();
  207. int t = calendarToggle.getAbsoluteTop();
  208. int l = calendarToggle.getAbsoluteLeft();
  209. // Add a little extra space to the right to avoid
  210. // problems with IE6/IE7 scrollbars and to make it look
  211. // nicer.
  212. int extraSpace = 30;
  213. boolean overflowRight = false;
  214. if (l + +w + extraSpace > browserWindowWidth) {
  215. overflowRight = true;
  216. // Part of the popup is outside the browser window
  217. // (to the right)
  218. l = browserWindowWidth - w - extraSpace;
  219. }
  220. if (t + h + calendarToggle.getOffsetHeight() + 30 > browserWindowHeight) {
  221. // Part of the popup is outside the browser window
  222. // (below)
  223. t = browserWindowHeight - h
  224. - calendarToggle.getOffsetHeight() - 30;
  225. if (!overflowRight) {
  226. // Show to the right of the popup button unless we
  227. // are in the lower right corner of the screen
  228. l += calendarToggle.getOffsetWidth();
  229. }
  230. }
  231. // fix size
  232. popup.setWidth(w + "px");
  233. popup.setHeight(h + "px");
  234. popup.setPopupPosition(l,
  235. t + calendarToggle.getOffsetHeight() + 2);
  236. Date end = new Date();
  237. ApplicationConnection.getConsole().log(
  238. "Rendering VCalendar took "
  239. + (end.getTime() - start.getTime() + "ms"));
  240. /*
  241. * We have to wait a while before focusing since the popup
  242. * needs to be opened before we can focus
  243. */
  244. Timer focusTimer = new Timer() {
  245. @Override
  246. public void run() {
  247. setFocus(true);
  248. }
  249. };
  250. focusTimer.schedule(100);
  251. }
  252. });
  253. } else {
  254. ApplicationConnection.getConsole().error(
  255. "Cannot reopen popup, it is already open!");
  256. }
  257. }
  258. /*
  259. * (non-Javadoc)
  260. *
  261. * @see
  262. * com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event
  263. * .dom.client.ClickEvent)
  264. */
  265. public void onClick(ClickEvent event) {
  266. if (event.getSource() == calendarToggle) {
  267. openCalendarPanel();
  268. }
  269. }
  270. /*
  271. * (non-Javadoc)
  272. *
  273. * @see
  274. * com.google.gwt.event.logical.shared.CloseHandler#onClose(com.google.gwt
  275. * .event.logical.shared.CloseEvent)
  276. */
  277. public void onClose(CloseEvent<PopupPanel> event) {
  278. if (event.getSource() == popup) {
  279. if (getCurrentResolution() <= VDateField.RESOLUTION_MONTH) {
  280. // due to UI limitations always fetch date from popup if
  281. // resolution is month or year
  282. date = calendar.getDate();
  283. }
  284. buildDate();
  285. focus();
  286. // TODO resolve what the "Sigh." is all about and document it here
  287. // Sigh.
  288. Timer t = new Timer() {
  289. @Override
  290. public void run() {
  291. open = false;
  292. }
  293. };
  294. t.schedule(100);
  295. }
  296. }
  297. /**
  298. * Sets focus to Calendar panel.
  299. *
  300. * @param focus
  301. */
  302. public void setFocus(boolean focus) {
  303. calendar.setFocus(focus);
  304. }
  305. /*
  306. * (non-Javadoc)
  307. *
  308. * @see com.vaadin.terminal.gwt.client.ui.VTextualDate#getFieldExtraWidth()
  309. */
  310. @Override
  311. protected int getFieldExtraWidth() {
  312. if (fieldExtraWidth < 0) {
  313. fieldExtraWidth = super.getFieldExtraWidth();
  314. fieldExtraWidth += calendarToggle.getOffsetWidth();
  315. }
  316. return fieldExtraWidth;
  317. }
  318. /*
  319. * (non-Javadoc)
  320. *
  321. * @see com.vaadin.terminal.gwt.client.ui.VTextualDate#buildDate()
  322. */
  323. @Override
  324. protected void buildDate() {
  325. // Save previous value
  326. String previousValue = getText();
  327. super.buildDate();
  328. // Restore previous value if the input could not be parsed
  329. if (!parsable) {
  330. setText(previousValue);
  331. }
  332. }
  333. protected void buildDate(boolean forceValid) {
  334. if (forceValid) {
  335. parsable = true;
  336. }
  337. buildDate();
  338. }
  339. /*
  340. * (non-Javadoc)
  341. *
  342. * @see
  343. * com.vaadin.terminal.gwt.client.ui.VDateField#onBrowserEvent(com.google
  344. * .gwt.user.client.Event)
  345. */
  346. @Override
  347. public void onBrowserEvent(com.google.gwt.user.client.Event event) {
  348. super.onBrowserEvent(event);
  349. if (DOM.eventGetType(event) == Event.ONKEYDOWN
  350. && event.getKeyCode() == getOpenCalenderPanelKey()) {
  351. openCalendarPanel();
  352. event.preventDefault();
  353. }
  354. }
  355. /**
  356. * Get the key code that opens the calendar panel. By default it is the down
  357. * key but you can override this to be whatever you like
  358. *
  359. * @return
  360. */
  361. protected int getOpenCalenderPanelKey() {
  362. return KeyCodes.KEY_DOWN;
  363. }
  364. /**
  365. * Closes the open popup panel
  366. */
  367. public void closeCalendarPanel() {
  368. if (open) {
  369. popup.hide(true);
  370. }
  371. }
  372. }