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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.terminal.gwt.client.ui;
  5. import com.google.gwt.event.dom.client.BlurEvent;
  6. import com.google.gwt.event.dom.client.BlurHandler;
  7. import com.google.gwt.event.dom.client.ChangeEvent;
  8. import com.google.gwt.event.dom.client.ChangeHandler;
  9. import com.google.gwt.event.dom.client.FocusEvent;
  10. import com.google.gwt.event.dom.client.FocusHandler;
  11. import com.google.gwt.i18n.client.DateTimeFormat;
  12. import com.google.gwt.user.client.DOM;
  13. import com.google.gwt.user.client.ui.TextBox;
  14. import com.vaadin.terminal.gwt.client.ApplicationConnection;
  15. import com.vaadin.terminal.gwt.client.BrowserInfo;
  16. import com.vaadin.terminal.gwt.client.ClientExceptionHandler;
  17. import com.vaadin.terminal.gwt.client.ContainerResizedListener;
  18. import com.vaadin.terminal.gwt.client.EventId;
  19. import com.vaadin.terminal.gwt.client.Focusable;
  20. import com.vaadin.terminal.gwt.client.LocaleNotLoadedException;
  21. import com.vaadin.terminal.gwt.client.LocaleService;
  22. import com.vaadin.terminal.gwt.client.Paintable;
  23. import com.vaadin.terminal.gwt.client.UIDL;
  24. public class VTextualDate extends VDateField implements Paintable, Field,
  25. ChangeHandler, ContainerResizedListener, Focusable {
  26. private static final String PARSE_ERROR_CLASSNAME = CLASSNAME
  27. + "-parseerror";
  28. private final TextBox text;
  29. private String formatStr;
  30. private String width;
  31. private boolean needLayout;
  32. protected int fieldExtraWidth = -1;
  33. private boolean lenient;
  34. private static final String CLASSNAME_PROMPT = "prompt";
  35. private static final String ATTR_INPUTPROMPT = "prompt";
  36. private String inputPrompt = "";
  37. private boolean prompting = false;
  38. public VTextualDate() {
  39. super();
  40. text = new TextBox();
  41. // use normal textfield styles as a basis
  42. text.setStyleName(VTextField.CLASSNAME);
  43. // add datefield spesific style name also
  44. text.addStyleName(CLASSNAME + "-textfield");
  45. text.addChangeHandler(this);
  46. text.addFocusHandler(new FocusHandler() {
  47. public void onFocus(FocusEvent event) {
  48. text.addStyleName(VTextField.CLASSNAME + "-"
  49. + VTextField.CLASSNAME_FOCUS);
  50. if (prompting) {
  51. text.setText("");
  52. setPrompting(false);
  53. }
  54. if (getClient() != null
  55. && getClient().hasEventListeners(VTextualDate.this,
  56. EventId.FOCUS)) {
  57. getClient()
  58. .updateVariable(getId(), EventId.FOCUS, "", true);
  59. }
  60. }
  61. });
  62. text.addBlurHandler(new BlurHandler() {
  63. public void onBlur(BlurEvent event) {
  64. text.removeStyleName(VTextField.CLASSNAME + "-"
  65. + VTextField.CLASSNAME_FOCUS);
  66. String value = getText();
  67. setPrompting(inputPrompt != null
  68. && (value == null || "".equals(value)));
  69. if (prompting) {
  70. text.setText(readonly ? "" : inputPrompt);
  71. }
  72. if (getClient() != null
  73. && getClient().hasEventListeners(VTextualDate.this,
  74. EventId.BLUR)) {
  75. getClient().updateVariable(getId(), EventId.BLUR, "", true);
  76. }
  77. }
  78. });
  79. add(text);
  80. }
  81. @Override
  82. public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
  83. int origRes = currentResolution;
  84. String oldLocale = currentLocale;
  85. super.updateFromUIDL(uidl, client);
  86. if (origRes != currentResolution || oldLocale != currentLocale) {
  87. // force recreating format string
  88. formatStr = null;
  89. }
  90. if (uidl.hasAttribute("format")) {
  91. formatStr = uidl.getStringAttribute("format");
  92. }
  93. inputPrompt = uidl.getStringAttribute(ATTR_INPUTPROMPT);
  94. lenient = !uidl.getBooleanAttribute("strict");
  95. buildDate();
  96. // not a FocusWidget -> needs own tabindex handling
  97. if (uidl.hasAttribute("tabindex")) {
  98. text.setTabIndex(uidl.getIntAttribute("tabindex"));
  99. }
  100. if (readonly) {
  101. text.addStyleDependentName("readonly");
  102. } else {
  103. text.removeStyleDependentName("readonly");
  104. }
  105. }
  106. protected String getFormatString() {
  107. if (formatStr == null) {
  108. if (currentResolution == RESOLUTION_YEAR) {
  109. formatStr = "yyyy"; // force full year
  110. } else {
  111. try {
  112. String frmString = LocaleService
  113. .getDateFormat(currentLocale);
  114. frmString = cleanFormat(frmString);
  115. String delim = LocaleService
  116. .getClockDelimiter(currentLocale);
  117. if (currentResolution >= RESOLUTION_HOUR) {
  118. if (dts.isTwelveHourClock()) {
  119. frmString += " hh";
  120. } else {
  121. frmString += " HH";
  122. }
  123. if (currentResolution >= RESOLUTION_MIN) {
  124. frmString += ":mm";
  125. if (currentResolution >= RESOLUTION_SEC) {
  126. frmString += ":ss";
  127. if (currentResolution >= RESOLUTION_MSEC) {
  128. frmString += ".SSS";
  129. }
  130. }
  131. }
  132. if (dts.isTwelveHourClock()) {
  133. frmString += " aaa";
  134. }
  135. }
  136. formatStr = frmString;
  137. } catch (LocaleNotLoadedException e) {
  138. ClientExceptionHandler.displayError(e);
  139. }
  140. }
  141. }
  142. return formatStr;
  143. }
  144. /**
  145. *
  146. */
  147. protected void buildDate() {
  148. removeStyleName(PARSE_ERROR_CLASSNAME);
  149. // Create the initial text for the textfield
  150. String dateText;
  151. if (date != null) {
  152. String formatStr = getFormatString();
  153. /*
  154. * Check if format contains the month name. If it does we need to
  155. * manually convert it to the month name since DateTimeFormat.format
  156. * always uses the current locale and will replace the month name
  157. * wrong if current locale is different from the locale set for the
  158. * DateField. Does not support abbreviated month names.
  159. */
  160. if (formatStr.contains("MMM")) {
  161. String monthName = getDateTimeService().getMonth(
  162. date.getMonth());
  163. if (monthName != null) {
  164. // Capitalize month name
  165. monthName = Character.toUpperCase(monthName.charAt(0))
  166. + monthName.substring(1);
  167. /*
  168. * Replace all month occurrences with the month name. All
  169. * strings with 3 or more M's are converted to the month
  170. * name as defined in
  171. * http://www.docjar.com/docs/api/com/google
  172. * /gwt/i18n/client/DateTimeFormat.html
  173. */
  174. formatStr = formatStr.replaceAll("[M]{3,}", "'" + monthName
  175. + "'");
  176. }
  177. }
  178. DateTimeFormat format = DateTimeFormat.getFormat(formatStr);
  179. dateText = format.format(date);
  180. } else {
  181. dateText = "";
  182. }
  183. setText(dateText);
  184. text.setEnabled(enabled);
  185. text.setReadOnly(readonly);
  186. if (readonly) {
  187. text.addStyleName("v-readonly");
  188. } else {
  189. text.removeStyleName("v-readonly");
  190. }
  191. }
  192. protected void setPrompting(boolean prompting) {
  193. this.prompting = prompting;
  194. if (prompting) {
  195. addStyleDependentName(CLASSNAME_PROMPT);
  196. } else {
  197. removeStyleDependentName(CLASSNAME_PROMPT);
  198. }
  199. }
  200. public void onChange(ChangeEvent event) {
  201. if (!text.getText().equals("")) {
  202. try {
  203. DateTimeFormat format = DateTimeFormat
  204. .getFormat(getFormatString());
  205. if (lenient) {
  206. date = format.parse(text.getText());
  207. if (date != null) {
  208. // if date value was leniently parsed, normalize text
  209. // presentation
  210. text.setValue(
  211. DateTimeFormat.getFormat(getFormatString())
  212. .format(date), false);
  213. }
  214. } else {
  215. date = format.parseStrict(text.getText());
  216. }
  217. long stamp = date.getTime();
  218. if (stamp == 0) {
  219. // If date parsing fails in firefox the stamp will be 0
  220. date = null;
  221. addStyleName(PARSE_ERROR_CLASSNAME);
  222. } else {
  223. // remove possibly added invalid value indication
  224. removeStyleName(PARSE_ERROR_CLASSNAME);
  225. }
  226. } catch (final Exception e) {
  227. ClientExceptionHandler.displayError(e.getMessage());
  228. addStyleName(PARSE_ERROR_CLASSNAME);
  229. // this is a hack that may eventually be removed
  230. getClient().updateVariable(getId(), "lastInvalidDateString",
  231. text.getText(), false);
  232. date = null;
  233. }
  234. } else {
  235. date = null;
  236. // remove possibly added invalid value indication
  237. removeStyleName(PARSE_ERROR_CLASSNAME);
  238. }
  239. // always send the date string
  240. getClient()
  241. .updateVariable(getId(), "dateString", text.getText(), false);
  242. // Update variables
  243. // (only the smallest defining resolution needs to be
  244. // immediate)
  245. getClient().updateVariable(getId(), "year",
  246. date != null ? date.getYear() + 1900 : -1,
  247. currentResolution == VDateField.RESOLUTION_YEAR && immediate);
  248. if (currentResolution >= VDateField.RESOLUTION_MONTH) {
  249. getClient().updateVariable(
  250. getId(),
  251. "month",
  252. date != null ? date.getMonth() + 1 : -1,
  253. currentResolution == VDateField.RESOLUTION_MONTH
  254. && immediate);
  255. }
  256. if (currentResolution >= VDateField.RESOLUTION_DAY) {
  257. getClient()
  258. .updateVariable(
  259. getId(),
  260. "day",
  261. date != null ? date.getDate() : -1,
  262. currentResolution == VDateField.RESOLUTION_DAY
  263. && immediate);
  264. }
  265. if (currentResolution >= VDateField.RESOLUTION_HOUR) {
  266. getClient().updateVariable(
  267. getId(),
  268. "hour",
  269. date != null ? date.getHours() : -1,
  270. currentResolution == VDateField.RESOLUTION_HOUR
  271. && immediate);
  272. }
  273. if (currentResolution >= VDateField.RESOLUTION_MIN) {
  274. getClient()
  275. .updateVariable(
  276. getId(),
  277. "min",
  278. date != null ? date.getMinutes() : -1,
  279. currentResolution == VDateField.RESOLUTION_MIN
  280. && immediate);
  281. }
  282. if (currentResolution >= VDateField.RESOLUTION_SEC) {
  283. getClient()
  284. .updateVariable(
  285. getId(),
  286. "sec",
  287. date != null ? date.getSeconds() : -1,
  288. currentResolution == VDateField.RESOLUTION_SEC
  289. && immediate);
  290. }
  291. if (currentResolution == VDateField.RESOLUTION_MSEC) {
  292. getClient().updateVariable(getId(), "msec",
  293. date != null ? getMilliseconds() : -1, immediate);
  294. }
  295. }
  296. private String cleanFormat(String format) {
  297. // Remove unnecessary d & M if resolution is too low
  298. if (currentResolution < VDateField.RESOLUTION_DAY) {
  299. format = format.replaceAll("d", "");
  300. }
  301. if (currentResolution < VDateField.RESOLUTION_MONTH) {
  302. format = format.replaceAll("M", "");
  303. }
  304. // Remove unsupported patterns
  305. // TODO support for 'G', era designator (used at least in Japan)
  306. format = format.replaceAll("[GzZwWkK]", "");
  307. // Remove extra delimiters ('/' and '.')
  308. while (format.startsWith("/") || format.startsWith(".")
  309. || format.startsWith("-")) {
  310. format = format.substring(1);
  311. }
  312. while (format.endsWith("/") || format.endsWith(".")
  313. || format.endsWith("-")) {
  314. format = format.substring(0, format.length() - 1);
  315. }
  316. // Remove duplicate delimiters
  317. format = format.replaceAll("//", "/");
  318. format = format.replaceAll("\\.\\.", ".");
  319. format = format.replaceAll("--", "-");
  320. return format.trim();
  321. }
  322. @Override
  323. public void setWidth(String newWidth) {
  324. if (!"".equals(newWidth) && (width == null || !newWidth.equals(width))) {
  325. if (BrowserInfo.get().isIE6()) {
  326. // in IE6 cols ~ min-width
  327. DOM.setElementProperty(text.getElement(), "size", "1");
  328. }
  329. needLayout = true;
  330. width = newWidth;
  331. super.setWidth(width);
  332. iLayout();
  333. if (newWidth.indexOf("%") < 0) {
  334. needLayout = false;
  335. }
  336. } else {
  337. if ("".equals(newWidth) && width != null && !"".equals(width)) {
  338. if (BrowserInfo.get().isIE6()) {
  339. // revert IE6 hack
  340. DOM.setElementProperty(text.getElement(), "size", "");
  341. }
  342. super.setWidth("");
  343. needLayout = true;
  344. iLayout();
  345. needLayout = false;
  346. width = null;
  347. }
  348. }
  349. }
  350. /**
  351. * Returns pixels in x-axis reserved for other than textfield content.
  352. *
  353. * @return extra width in pixels
  354. */
  355. protected int getFieldExtraWidth() {
  356. if (fieldExtraWidth < 0) {
  357. text.setWidth("0");
  358. fieldExtraWidth = text.getOffsetWidth();
  359. if (BrowserInfo.get().isFF3()) {
  360. // Firefox somehow always leaves the INPUT element 2px wide
  361. fieldExtraWidth -= 2;
  362. }
  363. }
  364. return fieldExtraWidth;
  365. }
  366. public void updateWidth() {
  367. needLayout = true;
  368. fieldExtraWidth = -1;
  369. iLayout();
  370. }
  371. public void iLayout() {
  372. if (needLayout) {
  373. text.setWidth((getOffsetWidth() - getFieldExtraWidth()) + "px");
  374. }
  375. }
  376. public void focus() {
  377. text.setFocus(true);
  378. }
  379. protected String getText() {
  380. if (prompting) {
  381. return "";
  382. }
  383. return text.getText();
  384. }
  385. protected void setText(String text) {
  386. if (inputPrompt != null && (text == null || "".equals(text))) {
  387. text = readonly ? "" : inputPrompt;
  388. setPrompting(true);
  389. } else {
  390. setPrompting(false);
  391. }
  392. this.text.setText(text);
  393. }
  394. }