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.

VTextualDate.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. /*
  2. @VaadinApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.terminal.gwt.client.ui;
  5. import java.util.Date;
  6. import com.google.gwt.event.dom.client.BlurEvent;
  7. import com.google.gwt.event.dom.client.BlurHandler;
  8. import com.google.gwt.event.dom.client.ChangeEvent;
  9. import com.google.gwt.event.dom.client.ChangeHandler;
  10. import com.google.gwt.event.dom.client.FocusEvent;
  11. import com.google.gwt.event.dom.client.FocusHandler;
  12. import com.google.gwt.user.client.Element;
  13. import com.google.gwt.user.client.ui.TextBox;
  14. import com.vaadin.terminal.gwt.client.ApplicationConnection;
  15. import com.vaadin.terminal.gwt.client.ContainerResizedListener;
  16. import com.vaadin.terminal.gwt.client.EventId;
  17. import com.vaadin.terminal.gwt.client.Focusable;
  18. import com.vaadin.terminal.gwt.client.LocaleNotLoadedException;
  19. import com.vaadin.terminal.gwt.client.LocaleService;
  20. import com.vaadin.terminal.gwt.client.VPaintableWidget;
  21. import com.vaadin.terminal.gwt.client.UIDL;
  22. import com.vaadin.terminal.gwt.client.VConsole;
  23. public class VTextualDate extends VDateField implements VPaintableWidget, Field,
  24. ChangeHandler, ContainerResizedListener, Focusable, SubPartAware {
  25. private static final String PARSE_ERROR_CLASSNAME = CLASSNAME
  26. + "-parseerror";
  27. private final TextBox text;
  28. private String formatStr;
  29. private String width;
  30. private boolean needLayout;
  31. protected int fieldExtraWidth = -1;
  32. private boolean lenient;
  33. private static final String CLASSNAME_PROMPT = "prompt";
  34. private static final String ATTR_INPUTPROMPT = "prompt";
  35. private String inputPrompt = "";
  36. private boolean prompting = false;
  37. public VTextualDate() {
  38. super();
  39. text = new TextBox();
  40. // use normal textfield styles as a basis
  41. text.setStyleName(VTextField.CLASSNAME);
  42. // add datefield spesific style name also
  43. text.addStyleName(CLASSNAME + "-textfield");
  44. text.addChangeHandler(this);
  45. text.addFocusHandler(new FocusHandler() {
  46. public void onFocus(FocusEvent event) {
  47. text.addStyleName(VTextField.CLASSNAME + "-"
  48. + VTextField.CLASSNAME_FOCUS);
  49. if (prompting) {
  50. text.setText("");
  51. setPrompting(false);
  52. }
  53. if (getClient() != null
  54. && getClient().hasEventListeners(VTextualDate.this,
  55. EventId.FOCUS)) {
  56. getClient()
  57. .updateVariable(getId(), EventId.FOCUS, "", true);
  58. }
  59. }
  60. });
  61. text.addBlurHandler(new BlurHandler() {
  62. public void onBlur(BlurEvent event) {
  63. text.removeStyleName(VTextField.CLASSNAME + "-"
  64. + VTextField.CLASSNAME_FOCUS);
  65. String value = getText();
  66. setPrompting(inputPrompt != null
  67. && (value == null || "".equals(value)));
  68. if (prompting) {
  69. text.setText(readonly ? "" : inputPrompt);
  70. }
  71. if (getClient() != null
  72. && getClient().hasEventListeners(VTextualDate.this,
  73. EventId.BLUR)) {
  74. getClient().updateVariable(getId(), EventId.BLUR, "", true);
  75. }
  76. }
  77. });
  78. add(text);
  79. }
  80. @Override
  81. public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
  82. int origRes = currentResolution;
  83. String oldLocale = currentLocale;
  84. super.updateFromUIDL(uidl, client);
  85. if (origRes != currentResolution || oldLocale != currentLocale) {
  86. // force recreating format string
  87. formatStr = null;
  88. }
  89. if (uidl.hasAttribute("format")) {
  90. formatStr = uidl.getStringAttribute("format");
  91. }
  92. inputPrompt = uidl.getStringAttribute(ATTR_INPUTPROMPT);
  93. lenient = !uidl.getBooleanAttribute("strict");
  94. buildDate();
  95. // not a FocusWidget -> needs own tabindex handling
  96. if (uidl.hasAttribute("tabindex")) {
  97. text.setTabIndex(uidl.getIntAttribute("tabindex"));
  98. }
  99. if (readonly) {
  100. text.addStyleDependentName("readonly");
  101. } else {
  102. text.removeStyleDependentName("readonly");
  103. }
  104. }
  105. protected String getFormatString() {
  106. if (formatStr == null) {
  107. if (currentResolution == RESOLUTION_YEAR) {
  108. formatStr = "yyyy"; // force full year
  109. } else {
  110. try {
  111. String frmString = LocaleService
  112. .getDateFormat(currentLocale);
  113. frmString = cleanFormat(frmString);
  114. // String delim = LocaleService
  115. // .getClockDelimiter(currentLocale);
  116. if (currentResolution >= RESOLUTION_HOUR) {
  117. if (dts.isTwelveHourClock()) {
  118. frmString += " hh";
  119. } else {
  120. frmString += " HH";
  121. }
  122. if (currentResolution >= RESOLUTION_MIN) {
  123. frmString += ":mm";
  124. if (currentResolution >= RESOLUTION_SEC) {
  125. frmString += ":ss";
  126. }
  127. }
  128. if (dts.isTwelveHourClock()) {
  129. frmString += " aaa";
  130. }
  131. }
  132. formatStr = frmString;
  133. } catch (LocaleNotLoadedException e) {
  134. // TODO should die instead? Can the component survive
  135. // without format string?
  136. VConsole.error(e);
  137. }
  138. }
  139. }
  140. return formatStr;
  141. }
  142. /**
  143. * Updates the text field according to the current date (provided by
  144. * {@link #getDate()}). Takes care of updating text, enabling and disabling
  145. * the field, setting/removing readonly status and updating readonly styles.
  146. *
  147. * TODO: Split part of this into a method that only updates the text as this
  148. * is what usually is needed except for updateFromUIDL.
  149. */
  150. protected void buildDate() {
  151. removeStyleName(PARSE_ERROR_CLASSNAME);
  152. // Create the initial text for the textfield
  153. String dateText;
  154. Date currentDate = getDate();
  155. if (currentDate != null) {
  156. dateText = getDateTimeService().formatDate(currentDate,
  157. getFormatString());
  158. } else {
  159. dateText = "";
  160. }
  161. setText(dateText);
  162. text.setEnabled(enabled);
  163. text.setReadOnly(readonly);
  164. if (readonly) {
  165. text.addStyleName("v-readonly");
  166. } else {
  167. text.removeStyleName("v-readonly");
  168. }
  169. }
  170. protected void setPrompting(boolean prompting) {
  171. this.prompting = prompting;
  172. if (prompting) {
  173. addStyleDependentName(CLASSNAME_PROMPT);
  174. } else {
  175. removeStyleDependentName(CLASSNAME_PROMPT);
  176. }
  177. }
  178. @SuppressWarnings("deprecation")
  179. public void onChange(ChangeEvent event) {
  180. if (!text.getText().equals("")) {
  181. try {
  182. String enteredDate = text.getText();
  183. setDate(getDateTimeService().parseDate(enteredDate,
  184. getFormatString(), lenient));
  185. if (lenient) {
  186. // If date value was leniently parsed, normalize text
  187. // presentation.
  188. // FIXME: Add a description/example here of when this is
  189. // needed
  190. text.setValue(
  191. getDateTimeService().formatDate(getDate(),
  192. getFormatString()), false);
  193. }
  194. // remove possibly added invalid value indication
  195. removeStyleName(PARSE_ERROR_CLASSNAME);
  196. } catch (final Exception e) {
  197. VConsole.log(e);
  198. addStyleName(PARSE_ERROR_CLASSNAME);
  199. // this is a hack that may eventually be removed
  200. getClient().updateVariable(getId(), "lastInvalidDateString",
  201. text.getText(), false);
  202. setDate(null);
  203. }
  204. } else {
  205. setDate(null);
  206. // remove possibly added invalid value indication
  207. removeStyleName(PARSE_ERROR_CLASSNAME);
  208. }
  209. // always send the date string
  210. getClient()
  211. .updateVariable(getId(), "dateString", text.getText(), false);
  212. // Update variables
  213. // (only the smallest defining resolution needs to be
  214. // immediate)
  215. Date currentDate = getDate();
  216. getClient().updateVariable(getId(), "year",
  217. currentDate != null ? currentDate.getYear() + 1900 : -1,
  218. currentResolution == VDateField.RESOLUTION_YEAR && immediate);
  219. if (currentResolution >= VDateField.RESOLUTION_MONTH) {
  220. getClient().updateVariable(
  221. getId(),
  222. "month",
  223. currentDate != null ? currentDate.getMonth() + 1 : -1,
  224. currentResolution == VDateField.RESOLUTION_MONTH
  225. && immediate);
  226. }
  227. if (currentResolution >= VDateField.RESOLUTION_DAY) {
  228. getClient()
  229. .updateVariable(
  230. getId(),
  231. "day",
  232. currentDate != null ? currentDate.getDate() : -1,
  233. currentResolution == VDateField.RESOLUTION_DAY
  234. && immediate);
  235. }
  236. if (currentResolution >= VDateField.RESOLUTION_HOUR) {
  237. getClient().updateVariable(
  238. getId(),
  239. "hour",
  240. currentDate != null ? currentDate.getHours() : -1,
  241. currentResolution == VDateField.RESOLUTION_HOUR
  242. && immediate);
  243. }
  244. if (currentResolution >= VDateField.RESOLUTION_MIN) {
  245. getClient()
  246. .updateVariable(
  247. getId(),
  248. "min",
  249. currentDate != null ? currentDate.getMinutes() : -1,
  250. currentResolution == VDateField.RESOLUTION_MIN
  251. && immediate);
  252. }
  253. if (currentResolution >= VDateField.RESOLUTION_SEC) {
  254. getClient()
  255. .updateVariable(
  256. getId(),
  257. "sec",
  258. currentDate != null ? currentDate.getSeconds() : -1,
  259. currentResolution == VDateField.RESOLUTION_SEC
  260. && immediate);
  261. }
  262. }
  263. private String cleanFormat(String format) {
  264. // Remove unnecessary d & M if resolution is too low
  265. if (currentResolution < VDateField.RESOLUTION_DAY) {
  266. format = format.replaceAll("d", "");
  267. }
  268. if (currentResolution < VDateField.RESOLUTION_MONTH) {
  269. format = format.replaceAll("M", "");
  270. }
  271. // Remove unsupported patterns
  272. // TODO support for 'G', era designator (used at least in Japan)
  273. format = format.replaceAll("[GzZwWkK]", "");
  274. // Remove extra delimiters ('/' and '.')
  275. while (format.startsWith("/") || format.startsWith(".")
  276. || format.startsWith("-")) {
  277. format = format.substring(1);
  278. }
  279. while (format.endsWith("/") || format.endsWith(".")
  280. || format.endsWith("-")) {
  281. format = format.substring(0, format.length() - 1);
  282. }
  283. // Remove duplicate delimiters
  284. format = format.replaceAll("//", "/");
  285. format = format.replaceAll("\\.\\.", ".");
  286. format = format.replaceAll("--", "-");
  287. return format.trim();
  288. }
  289. @Override
  290. public void setWidth(String newWidth) {
  291. if (!"".equals(newWidth) && (width == null || !newWidth.equals(width))) {
  292. needLayout = true;
  293. width = newWidth;
  294. super.setWidth(width);
  295. iLayout();
  296. if (newWidth.indexOf("%") < 0) {
  297. needLayout = false;
  298. }
  299. } else {
  300. if ("".equals(newWidth) && width != null && !"".equals(width)) {
  301. super.setWidth("");
  302. needLayout = true;
  303. iLayout();
  304. needLayout = false;
  305. width = null;
  306. }
  307. }
  308. }
  309. /**
  310. * Returns pixels in x-axis reserved for other than textfield content.
  311. *
  312. * @return extra width in pixels
  313. */
  314. protected int getFieldExtraWidth() {
  315. if (fieldExtraWidth < 0) {
  316. text.setWidth("0");
  317. fieldExtraWidth = text.getOffsetWidth();
  318. }
  319. return fieldExtraWidth;
  320. }
  321. public void updateWidth() {
  322. needLayout = true;
  323. fieldExtraWidth = -1;
  324. iLayout();
  325. }
  326. public void iLayout() {
  327. if (needLayout) {
  328. int textFieldWidth = getOffsetWidth() - getFieldExtraWidth();
  329. if (textFieldWidth < 0) {
  330. // Field can never be smaller than 0 (causes exception in IE)
  331. textFieldWidth = 0;
  332. }
  333. text.setWidth(textFieldWidth + "px");
  334. }
  335. }
  336. public void focus() {
  337. text.setFocus(true);
  338. }
  339. protected String getText() {
  340. if (prompting) {
  341. return "";
  342. }
  343. return text.getText();
  344. }
  345. protected void setText(String text) {
  346. if (inputPrompt != null && (text == null || "".equals(text))) {
  347. text = readonly ? "" : inputPrompt;
  348. setPrompting(true);
  349. } else {
  350. setPrompting(false);
  351. }
  352. this.text.setText(text);
  353. }
  354. private final String TEXTFIELD_ID = "field";
  355. public Element getSubPartElement(String subPart) {
  356. if (subPart.equals(TEXTFIELD_ID)) {
  357. return text.getElement();
  358. }
  359. return null;
  360. }
  361. public String getSubPartName(Element subElement) {
  362. if (text.getElement().isOrHasChild(subElement)) {
  363. return TEXTFIELD_ID;
  364. }
  365. return null;
  366. }
  367. }