001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.gwt.client.ui.input.datebox; 029 030import org.opencms.gwt.client.I_CmsHasInit; 031import org.opencms.gwt.client.Messages; 032import org.opencms.gwt.client.ui.CmsPopup; 033import org.opencms.gwt.client.ui.I_CmsAutoHider; 034import org.opencms.gwt.client.ui.css.I_CmsLayoutBundle; 035import org.opencms.gwt.client.ui.input.CmsRadioButton; 036import org.opencms.gwt.client.ui.input.CmsRadioButtonGroup; 037import org.opencms.gwt.client.ui.input.CmsTextBox; 038import org.opencms.gwt.client.ui.input.I_CmsFormWidget; 039import org.opencms.gwt.client.ui.input.form.CmsWidgetFactoryRegistry; 040import org.opencms.gwt.client.ui.input.form.I_CmsFormWidgetFactory; 041import org.opencms.util.CmsStringUtil; 042 043import java.util.Date; 044import java.util.Map; 045 046import com.google.common.base.Optional; 047import com.google.gwt.core.client.GWT; 048import com.google.gwt.core.client.Scheduler; 049import com.google.gwt.core.client.Scheduler.ScheduledCommand; 050import com.google.gwt.dom.client.Element; 051import com.google.gwt.dom.client.EventTarget; 052import com.google.gwt.dom.client.Style.Unit; 053import com.google.gwt.event.dom.client.BlurEvent; 054import com.google.gwt.event.dom.client.BlurHandler; 055import com.google.gwt.event.dom.client.ClickEvent; 056import com.google.gwt.event.dom.client.ClickHandler; 057import com.google.gwt.event.dom.client.FocusEvent; 058import com.google.gwt.event.dom.client.FocusHandler; 059import com.google.gwt.event.dom.client.HasKeyPressHandlers; 060import com.google.gwt.event.dom.client.KeyCodes; 061import com.google.gwt.event.dom.client.KeyPressEvent; 062import com.google.gwt.event.dom.client.KeyPressHandler; 063import com.google.gwt.event.dom.client.KeyUpEvent; 064import com.google.gwt.event.dom.client.KeyUpHandler; 065import com.google.gwt.event.dom.client.MouseDownEvent; 066import com.google.gwt.event.logical.shared.CloseEvent; 067import com.google.gwt.event.logical.shared.CloseHandler; 068import com.google.gwt.event.logical.shared.ValueChangeEvent; 069import com.google.gwt.event.logical.shared.ValueChangeHandler; 070import com.google.gwt.event.shared.HandlerRegistration; 071import com.google.gwt.i18n.client.DateTimeFormat; 072import com.google.gwt.uibinder.client.UiBinder; 073import com.google.gwt.uibinder.client.UiField; 074import com.google.gwt.user.client.Command; 075import com.google.gwt.user.client.DOM; 076import com.google.gwt.user.client.Event; 077import com.google.gwt.user.client.Event.NativePreviewEvent; 078import com.google.gwt.user.client.Event.NativePreviewHandler; 079import com.google.gwt.user.client.ui.Composite; 080import com.google.gwt.user.client.ui.FlowPanel; 081import com.google.gwt.user.client.ui.HasValue; 082import com.google.gwt.user.client.ui.PopupPanel; 083import com.google.gwt.user.client.ui.RootPanel; 084import com.google.gwt.user.client.ui.UIObject; 085import com.google.gwt.user.datepicker.client.CalendarUtil; 086import com.google.gwt.user.datepicker.client.DatePicker; 087 088/** 089 * A text box that shows a date time picker widget when the user clicks on it. 090 */ 091public class CmsDateBox extends Composite 092implements HasValue<Date>, I_CmsFormWidget, I_CmsHasInit, HasKeyPressHandlers, I_CmsHasDateBoxEventHandlers { 093 094 /** 095 * Drag and drop event preview handler.<p> 096 * 097 * To be used while dragging.<p> 098 */ 099 protected class CloseEventPreviewHandler implements NativePreviewHandler { 100 101 /** 102 * @see com.google.gwt.user.client.Event.NativePreviewHandler#onPreviewNativeEvent(com.google.gwt.user.client.Event.NativePreviewEvent) 103 */ 104 public void onPreviewNativeEvent(NativePreviewEvent event) { 105 106 Event nativeEvent = Event.as(event.getNativeEvent()); 107 switch (DOM.eventGetType(nativeEvent)) { 108 case Event.ONMOUSEMOVE: 109 break; 110 case Event.ONMOUSEUP: 111 break; 112 case Event.ONKEYDOWN: 113 break; 114 case Event.ONMOUSEWHEEL: 115 hidePopup(); 116 break; 117 default: 118 // do nothing 119 } 120 } 121 122 } 123 124 /** 125 * This inner class implements the handler for the date box widget.<p> 126 */ 127 protected class CmsDateBoxHandler 128 implements ClickHandler, FocusHandler, BlurHandler, KeyUpHandler, ValueChangeHandler<Date>, 129 CloseHandler<PopupPanel> { 130 131 /** 132 * @see com.google.gwt.event.dom.client.BlurHandler#onBlur(com.google.gwt.event.dom.client.BlurEvent) 133 */ 134 public void onBlur(BlurEvent event) { 135 136 UIObject source = (UIObject)event.getSource(); 137 if (m_box.getElement().isOrHasChild(source.getElement())) { 138 onDateBoxBlur(); 139 } else if (m_time.getElement().isOrHasChild(source.getElement())) { 140 onTimeBlur(); 141 } 142 } 143 144 /** 145 * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent) 146 */ 147 public void onClick(ClickEvent event) { 148 149 if (event.getSource() == m_box) { 150 onDateBoxClick(); 151 } else if (event.getSource() == m_time) { 152 onTimeClick(); 153 } else if ((event.getSource() == m_am) || (event.getSource() == m_pm)) { 154 onAmPmClick(); 155 } 156 } 157 158 /** 159 * @see com.google.gwt.event.logical.shared.CloseHandler#onClose(com.google.gwt.event.logical.shared.CloseEvent) 160 */ 161 public void onClose(CloseEvent<PopupPanel> event) { 162 163 m_box.setPreventShowError(false); 164 165 } 166 167 /** 168 * @see com.google.gwt.event.dom.client.FocusHandler#onFocus(com.google.gwt.event.dom.client.FocusEvent) 169 */ 170 public void onFocus(FocusEvent event) { 171 172 UIObject source = (UIObject)event.getSource(); 173 if (m_time.getElement().isOrHasChild(source.getElement())) { 174 onFocusTimeBox(); 175 } 176 } 177 178 /** 179 * @see com.google.gwt.event.dom.client.KeyPressHandler#onKeyPress(com.google.gwt.event.dom.client.KeyPressEvent) 180 */ 181 public void onKeyUp(KeyUpEvent event) { 182 183 if (event.getSource() == m_box) { 184 onDateBoxKeyPress(event); 185 } else if (event.getSource() == m_time) { 186 onTimeKeyPressed(event); 187 } 188 } 189 190 /** 191 * @see com.google.gwt.event.logical.shared.ValueChangeHandler#onValueChange(com.google.gwt.event.logical.shared.ValueChangeEvent) 192 */ 193 public void onValueChange(ValueChangeEvent<Date> event) { 194 195 onPickerValueChanged(); 196 } 197 } 198 199 /** The ui-binder interface for this composite. */ 200 interface I_CmsDateBoxUiBinder extends UiBinder<FlowPanel, CmsDateBox> { 201 // GWT interface, nothing to do here 202 } 203 204 /** Dummy value used for invalid dates. */ 205 public static final Date INVALID_DATE = new Date(Integer.MIN_VALUE + 37); // can't use Long.MIN_VALUE since this leads to an invalid Date object in GWT 206 207 /** Format used to parse the configured time in fixed-time mode. */ 208 public static final DateTimeFormat TIME_FORMAT_24H = DateTimeFormat.getFormat("HH:mm"); 209 210 /** The widget type identifier for this widget. */ 211 public static final String WIDGET_TYPE = "datebox"; 212 213 /** The ui-binder instance. */ 214 private static I_CmsDateBoxUiBinder uiBinder = GWT.create(I_CmsDateBoxUiBinder.class); 215 216 /** The am radio button. */ 217 @UiField 218 protected CmsRadioButton m_am; 219 220 /** The radio button group for am/pm selection. */ 221 protected CmsRadioButtonGroup m_ampmGroup; 222 223 /** The auto hide parent. */ 224 protected I_CmsAutoHider m_autoHideParent; 225 226 /** The input field to show the result of picking a date. */ 227 @UiField 228 protected CmsTextBox m_box; 229 230 /** The panel for the date time picker. */ 231 @UiField 232 protected FlowPanel m_dateTimePanel; 233 234 /** The gwt date picker. */ 235 @UiField 236 protected DatePicker m_picker; 237 238 /** The pm radio button. */ 239 @UiField 240 protected CmsRadioButton m_pm; 241 242 /** Event preview handler registration. */ 243 protected HandlerRegistration m_previewHandlerRegistration; 244 245 /** The text box to input the time. */ 246 @UiField 247 protected CmsTextBox m_time; 248 249 /** The panel for the time selection. */ 250 @UiField 251 protected FlowPanel m_timeField; 252 253 /** True if invalid values should be allowed. */ 254 private boolean m_allowInvalidValue; 255 256 /** The value for show date only. */ 257 private boolean m_dateOnly; 258 259 /** The time to use for the widget value when used in date-only mode. */ 260 private String m_fixedTime; 261 262 /** The initial date shown, when the date picker is opened and no date was set before. */ 263 private Date m_initialDate; 264 265 /** Signals whether the date box is valid or not. */ 266 private boolean m_isValidDateBox; 267 268 /** Signals whether the time field is valid or not. */ 269 private boolean m_isValidTime; 270 271 /** The old value for fire event decision. */ 272 private Date m_oldValue; 273 274 /** The popup panel to show the the date time picker widget in. */ 275 private CmsPopup m_popup; 276 277 /** 278 * The event preview handler.<p> 279 * 280 * Blurs the time box if the user clicks outside of it.<p> 281 */ 282 private NativePreviewHandler m_previewHandler = new NativePreviewHandler() { 283 284 /** 285 * @see com.google.gwt.user.client.Event.NativePreviewHandler#onPreviewNativeEvent(com.google.gwt.user.client.Event.NativePreviewEvent) 286 */ 287 public void onPreviewNativeEvent(NativePreviewEvent event) { 288 289 previewNativeEvent(event); 290 } 291 }; 292 293 /** Stores the preview handler. */ 294 private HandlerRegistration m_previewRegistration; 295 296 /** The date used for enable and disable the text box. */ 297 private Date m_tmpValue; 298 299 /** 300 * Create a new date box widget with the date time picker. 301 */ 302 public CmsDateBox() { 303 304 initWidget(uiBinder.createAndBindUi(this)); 305 m_box.colorWhite(); 306 307 m_popup = new CmsPopup(Messages.get().key(Messages.GUI_DATEBOX_TITLE_0)); 308 m_ampmGroup = new CmsRadioButtonGroup(); 309 310 m_am.setText(Messages.get().key(Messages.GUI_DATE_AM_0)); 311 m_am.setGroup(m_ampmGroup); 312 m_pm.setText(Messages.get().key(Messages.GUI_DATE_PM_0)); 313 m_pm.setGroup(m_ampmGroup); 314 315 if (!CmsDateConverter.is12HourPresentation()) { 316 m_pm.setVisible(false); 317 m_am.setVisible(false); 318 } 319 320 CmsDateBoxHandler dateBoxHandler = new CmsDateBoxHandler(); 321 m_picker.addValueChangeHandler(dateBoxHandler); 322 323 m_box.addBlurHandler(dateBoxHandler); 324 m_box.addDomHandler(event -> { 325 onDateBoxClick(); 326 }, MouseDownEvent.getType()); 327 328 m_box.addKeyUpHandler(dateBoxHandler); 329 m_am.addClickHandler(dateBoxHandler); 330 m_pm.addClickHandler(dateBoxHandler); 331 m_time.addClickHandler(dateBoxHandler); 332 m_time.addBlurHandler(dateBoxHandler); 333 m_time.addKeyUpHandler(dateBoxHandler); 334 m_time.addFocusHandler(dateBoxHandler); 335 336 m_popup.add(m_dateTimePanel); 337 m_popup.setWidth(0); 338 m_popup.setModal(true); 339 m_popup.removePadding(); 340 m_popup.setBackgroundColor(I_CmsLayoutBundle.INSTANCE.constants().css().backgroundColorDialog()); 341 m_popup.addDialogClose(new Command() { 342 343 public void execute() { 344 345 m_box.setPreventShowError(false); 346 } 347 }); 348 m_popup.addCloseHandler(dateBoxHandler); 349 m_popup.addAutoHidePartner(m_box.getElement()); 350 m_popup.setAutoHideEnabled(true); 351 } 352 353 /** 354 * Initializes this class.<p> 355 */ 356 public static void initClass() { 357 358 // registers a factory for creating new instances of this widget 359 CmsWidgetFactoryRegistry.instance().registerFactory(WIDGET_TYPE, new I_CmsFormWidgetFactory() { 360 361 /** 362 * @see org.opencms.gwt.client.ui.input.form.I_CmsFormWidgetFactory#createWidget(java.util.Map, com.google.common.base.Optional) 363 */ 364 public I_CmsFormWidget createWidget(Map<String, String> widgetParams, Optional<String> defaultValue) { 365 366 return new CmsDateBox(); 367 } 368 }); 369 } 370 371 /** 372 * @see org.opencms.gwt.client.ui.input.datebox.I_CmsHasDateBoxEventHandlers#addCmsDateBoxEventHandler(org.opencms.gwt.client.ui.input.datebox.I_CmsDateBoxEventHandler) 373 */ 374 public HandlerRegistration addCmsDateBoxEventHandler(I_CmsDateBoxEventHandler handler) { 375 376 return addHandler(handler, CmsDateBoxEvent.TYPE); 377 } 378 379 /** 380 * @see com.google.gwt.event.dom.client.HasKeyPressHandlers#addKeyPressHandler(com.google.gwt.event.dom.client.KeyPressHandler) 381 */ 382 public HandlerRegistration addKeyPressHandler(KeyPressHandler handler) { 383 384 return m_box.addHandler(handler, KeyPressEvent.getType()); 385 } 386 387 /** 388 * @see com.google.gwt.event.logical.shared.HasValueChangeHandlers#addValueChangeHandler(com.google.gwt.event.logical.shared.ValueChangeHandler) 389 */ 390 public HandlerRegistration addValueChangeHandler(ValueChangeHandler<Date> handler) { 391 392 return addHandler(handler, ValueChangeEvent.getType()); 393 } 394 395 /** 396 * Returns true if invalid values should be allowed.<p> 397 * 398 * If invalid values are allowed, methods returning a Date will return a special dummy date in case the date is invalid. 399 * 400 * @return true if invalid values should be allowed 401 */ 402 public boolean allowInvalidValue() { 403 404 return m_allowInvalidValue; 405 } 406 407 /** 408 * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#getApparentValue() 409 */ 410 public String getApparentValue() { 411 412 return getFormValueAsString(); 413 } 414 415 /** 416 * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#getFieldType() 417 */ 418 public FieldType getFieldType() { 419 420 return I_CmsFormWidget.FieldType.DATE; 421 } 422 423 /** 424 * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#getFormValue() 425 */ 426 public Object getFormValue() { 427 428 return getValue(); 429 } 430 431 /** 432 * Returns the value of the date box as String in form of a long.<p> 433 * 434 * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#getFormValueAsString() 435 */ 436 public String getFormValueAsString() { 437 438 Date value = getValue(); 439 if (value == null) { 440 return ""; 441 } else if (allowInvalidValue() && value.equals(INVALID_DATE)) { 442 return "INVALID_DATE"; 443 } 444 String result = String.valueOf(getValue().getTime()); 445 return result; 446 } 447 448 /** 449 * Returns the text box of this widget.<p> 450 * 451 * @return the CmsText Box 452 */ 453 public CmsTextBox getTextField() { 454 455 return m_box; 456 } 457 458 /** 459 * @see com.google.gwt.user.client.ui.HasValue#getValue() 460 */ 461 public Date getValue() { 462 463 Date date = null; 464 if (isEnabled()) { 465 try { 466 if (m_dateOnly) { 467 date = CmsDateConverter.toDayDate(m_box.getText()); 468 if (m_fixedTime != null) { 469 date = CmsDateConverter.getDateWithTime(date, m_fixedTime, TIME_FORMAT_24H); 470 } 471 } else { 472 date = CmsDateConverter.toDate(m_box.getText()); 473 } 474 setErrorMessage(null); 475 } catch (Exception e) { 476 setErrorMessage(Messages.get().key(Messages.ERR_DATEBOX_INVALID_DATE_FORMAT_0)); 477 if (allowInvalidValue()) { 478 // empty is actually a valid value for date fields, so we have to return a different value to distinguish between the cases 479 return INVALID_DATE; 480 } 481 } 482 } 483 return date; 484 } 485 486 /** 487 * Returns the date value as formated String or an empty String if the date value is null.<p> 488 * 489 * @return the date value as formated String 490 */ 491 public String getValueAsFormatedString() { 492 493 return CmsDateConverter.toString(getValue()); 494 } 495 496 /** 497 * Returns <code>true</code> if the box and the time input fields don't have any errors.<p> 498 * 499 * @return <code>true</code> if the box and the time input fields don't have any errors 500 */ 501 public boolean hasErrors() { 502 503 if (m_box.hasError() || m_time.hasError()) { 504 return true; 505 } 506 return false; 507 } 508 509 /** 510 * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#isEnabled() 511 */ 512 public boolean isEnabled() { 513 514 return m_box.isEnabled(); 515 } 516 517 /** 518 * Checks if the String in the date box input field is a valid date format.<p> 519 * 520 * @return <code>true</code> if the String in the date box input field is a valid date format 521 */ 522 public boolean isValideDateBox() { 523 524 try { 525 CmsDateConverter.toDate(m_box.getText()); 526 return true; 527 } catch (Exception e) { 528 return false; 529 } 530 } 531 532 /** 533 * Updates the date box when the user has clicked on the time field.<p> 534 */ 535 public void onTimeClick() { 536 537 updateFromPicker(); 538 } 539 540 /** 541 * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#reset() 542 */ 543 public void reset() { 544 545 setValue(null); 546 } 547 548 /** 549 * Enables or disables whether invalid values are allowed.<p> 550 * 551 * If invalid values are allowed, they will be returned as a special dummy date (INVALID_DATE). 552 * 553 * @param allowInvalidValue true if invalid values should be allowed 554 */ 555 public void setAllowInvalidValue(boolean allowInvalidValue) { 556 557 m_allowInvalidValue = allowInvalidValue; 558 } 559 560 /** 561 * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#setAutoHideParent(org.opencms.gwt.client.ui.I_CmsAutoHider) 562 */ 563 public void setAutoHideParent(I_CmsAutoHider autoHideParent) { 564 565 m_autoHideParent = autoHideParent; 566 } 567 568 /** 569 * Sets the value if the date only should be shown. 570 * @param dateOnly if the date only should be shown 571 */ 572 public void setDateOnly(boolean dateOnly) { 573 574 if (m_dateOnly != dateOnly) { 575 m_dateOnly = dateOnly; 576 if (m_dateOnly) { 577 m_time.removeFromParent(); 578 m_am.removeFromParent(); 579 m_pm.removeFromParent(); 580 } else { 581 m_timeField.add(m_time); 582 m_timeField.add(m_am); 583 m_timeField.add(m_pm); 584 } 585 } 586 } 587 588 /** 589 * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#setEnabled(boolean) 590 */ 591 public void setEnabled(boolean enabled) { 592 593 if (!enabled) { 594 m_tmpValue = getValue(); 595 m_box.setFormValueAsString(Messages.get().key(Messages.GUI_INPUT_NOT_USED_0)); 596 } else { 597 setValue(m_tmpValue); 598 } 599 m_box.setEnabled(enabled); 600 } 601 602 /** 603 * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#setErrorMessage(java.lang.String) 604 */ 605 public void setErrorMessage(String errorMessage) { 606 607 m_box.setErrorMessage(errorMessage); 608 } 609 610 /** 611 * Sets the time to be used when the widget is in date-only mode. 612 * 613 * @param time the time to use when the widget is in date-only mode 614 */ 615 public void setFixedTime(String time) { 616 617 m_fixedTime = time; 618 } 619 620 /** 621 * Expects the value as String in form of a long.<p> 622 * 623 * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#setFormValueAsString(java.lang.String) 624 */ 625 public void setFormValueAsString(String value) { 626 627 if (!CmsStringUtil.isEmpty(value)) { 628 try { 629 long time = Long.parseLong(value); 630 setValue(new Date(time)); 631 } catch (NumberFormatException e) { 632 // if the String value is none long number make the field empty 633 setValue(null); 634 } 635 } else { 636 // if the value is <code>null</code> make the field empty 637 setValue(null); 638 } 639 } 640 641 /** 642 * Sets the initial date shown, when the date picker is opened and no date was set before.<p> 643 * 644 * @param initialDate the initial date 645 */ 646 public void setInitialDate(Date initialDate) { 647 648 m_initialDate = initialDate; 649 } 650 651 /** 652 * Sets the name of the input field.<p> 653 * 654 * @param name of the input field 655 */ 656 public void setName(String name) { 657 658 m_time.setName(name); 659 660 } 661 662 /** 663 * @see com.google.gwt.user.client.ui.HasValue#setValue(java.lang.Object) 664 */ 665 public void setValue(Date value) { 666 667 setValue(value, false); 668 } 669 670 /** 671 * @see com.google.gwt.user.client.ui.HasValue#setValue(java.lang.Object, boolean) 672 */ 673 public void setValue(Date value, boolean fireEvents) { 674 675 m_tmpValue = value; 676 if (fireEvents) { 677 fireChange(value, false); 678 } 679 if (value == null) { 680 m_box.setFormValueAsString(""); 681 } else if (m_dateOnly) { 682 m_box.setFormValueAsString(CmsDateConverter.toDateString(value)); 683 } else { 684 m_box.setFormValueAsString(CmsDateConverter.toString(value)); 685 } 686 } 687 688 /** 689 * Updates the updates the close behavior and sets the value of the date box to the value from the picker.<p> 690 */ 691 protected void executeTimeAction() { 692 693 if (isValidTime()) { 694 updateFromPicker(); 695 } 696 updateCloseBehavior(); 697 } 698 699 /** 700 * Fires the value change event if needed.<p> 701 * 702 * @param newValue the new value 703 * @param isTyping true if the user is currently typing 704 */ 705 protected void fireChange(Date newValue, boolean isTyping) { 706 707 ValueChangeEvent.<Date> fireIfNotEqual(this, m_oldValue, CalendarUtil.copyDate(newValue)); 708 CmsDateBoxEvent.fire(this, newValue, isTyping); 709 m_oldValue = newValue; 710 } 711 712 /** 713 * Hides the date time popup.<p> 714 */ 715 protected void hidePopup() { 716 717 if (CmsDateConverter.validateTime(getTimeText())) { 718 // before hiding the date picker remove the date box popup from the auto hide partners of the parent popup 719 if (m_autoHideParent != null) { 720 m_autoHideParent.removeAutoHidePartner(getElement()); 721 } 722 m_popup.hide(); 723 if (m_previewHandlerRegistration != null) { 724 m_previewHandlerRegistration.removeHandler(); 725 } 726 m_previewHandlerRegistration = null; 727 } 728 } 729 730 /** 731 * If the am or pm radio button is clicked update the date box from the date time picker.<p> 732 */ 733 protected void onAmPmClick() { 734 735 updateFromPicker(); 736 } 737 738 /** 739 * The date box on blur action.<p> 740 * 741 * If the date box loses the focus the date time picker should be updated from the date box value.<p> 742 */ 743 protected void onDateBoxBlur() { 744 745 if (!m_popup.isShowing()) { 746 updateFromTextBox(false); 747 } 748 updateCloseBehavior(); 749 } 750 751 /** 752 * The date box on click action.<p> 753 * 754 * If the date box is clicked the time date picker should be shown.<p> 755 */ 756 protected void onDateBoxClick() { 757 758 if (!m_popup.isShowing()) { 759 showPopup(); 760 } 761 } 762 763 /** 764 * The date box on key down action.<p> 765 * <ul> 766 * <li>If enter or tab is pressed in the date box the date time 767 * picker should be updated with the value from the date box.</li> 768 * <li>If the escape key is pressed the picker should be hided.</li> 769 * <li>If the up key is pressed the value should be taken from the date box.</li> 770 * <li>If the down key is pressed the picker should be hided.</li> 771 * </ul> 772 * 773 * @param event the key down event 774 */ 775 protected void onDateBoxKeyPress(KeyUpEvent event) { 776 777 switch (event.getNativeEvent().getKeyCode()) { 778 case KeyCodes.KEY_CTRL: 779 case KeyCodes.KEY_SHIFT: 780 case KeyCodes.KEY_ALT: 781 case KeyCodes.KEY_LEFT: 782 case KeyCodes.KEY_RIGHT: 783 break; 784 case KeyCodes.KEY_ENTER: 785 case KeyCodes.KEY_TAB: 786 case KeyCodes.KEY_ESCAPE: 787 case KeyCodes.KEY_UP: 788 updateFromTextBox(false); 789 hidePopup(); 790 break; 791 case KeyCodes.KEY_DOWN: 792 showPopup(); 793 break; 794 default: 795 hidePopup(); 796 Scheduler.get().scheduleDeferred(new ScheduledCommand() { 797 798 public void execute() { 799 800 updateCloseBehavior(); 801 if (isValideDateBox() || allowInvalidValue()) { 802 setErrorMessage(null); 803 fireChange(getValue(), true); 804 } 805 } 806 }); 807 break; 808 } 809 } 810 811 /** 812 * Adds the preview handler.<p> 813 */ 814 protected void onFocusTimeBox() { 815 816 m_previewRegistration = Event.addNativePreviewHandler(m_previewHandler); 817 } 818 819 /** 820 * If the value of the picker changes, the value of the date time picker should be updated.<p> 821 */ 822 protected void onPickerValueChanged() { 823 824 setErrorMessage(null); 825 updateFromPicker(); 826 } 827 828 /** 829 * If the time field loses the focus the entered time should be checked.<p> 830 */ 831 protected void onTimeBlur() { 832 833 if (m_previewRegistration != null) { 834 m_previewRegistration.removeHandler(); 835 } 836 checkTime(); 837 } 838 839 /** 840 * If the user presses enter in the time field the value of the 841 * picker should be updated and the the popup should be closed.<p> 842 * 843 * In any other case the popup should be prevented to being closed.<p> 844 * 845 * @param event the key pressed event 846 */ 847 protected void onTimeKeyPressed(KeyUpEvent event) { 848 849 switch (event.getNativeEvent().getKeyCode()) { 850 case KeyCodes.KEY_CTRL: 851 case KeyCodes.KEY_SHIFT: 852 case KeyCodes.KEY_ALT: 853 case KeyCodes.KEY_LEFT: 854 case KeyCodes.KEY_RIGHT: 855 break; 856 case KeyCodes.KEY_ENTER: 857 updateFromPicker(); 858 hidePopup(); 859 break; 860 default: 861 Scheduler.get().scheduleDeferred(new ScheduledCommand() { 862 863 public void execute() { 864 865 executeTimeAction(); 866 fireChange(getValue(), false); 867 } 868 }); 869 break; 870 } 871 } 872 873 /** 874 * Blurs the time box if the user clicks outside of it.<p> 875 * 876 * @param event the native preview event 877 */ 878 protected void previewNativeEvent(NativePreviewEvent event) { 879 880 Event nativeEvent = Event.as(event.getNativeEvent()); 881 if ((nativeEvent.getTypeInt() == Event.ONCLICK)) { 882 EventTarget target = nativeEvent.getEventTarget(); 883 if (!Element.is(target)) { 884 return; 885 } 886 Element element = Element.as(target); 887 if (!m_time.getElement().isOrHasChild(element)) { 888 m_time.blur(); 889 } 890 } 891 } 892 893 /** 894 * Updates the auto hide partner from the parent widget.<p> 895 * 896 * If there is any invalid user input the parent widget should not be closed automatically.<p> 897 */ 898 protected void updateCloseBehavior() { 899 900 if (isEnabled()) { 901 if (!m_isValidTime && isValidTime()) { 902 m_isValidTime = true; 903 m_popup.setAutoHideEnabled(true); 904 } else if (m_isValidTime && !isValidTime()) { 905 m_isValidTime = false; 906 m_popup.setAutoHideEnabled(false); 907 } 908 909 if (!m_isValidDateBox && isValideDateBox()) { 910 m_isValidDateBox = true; 911 if (m_autoHideParent != null) { 912 m_autoHideParent.removeAutoHidePartner(RootPanel.getBodyElement().getParentElement()); 913 } 914 } else if (m_isValidDateBox && !isValideDateBox()) { 915 m_isValidDateBox = false; 916 if (m_autoHideParent != null) { 917 m_autoHideParent.addAutoHidePartner(RootPanel.getBodyElement().getParentElement()); 918 } 919 } 920 } 921 } 922 923 /** 924 * Validates the time and prints out an error message if the time format is incorrect.<p> 925 */ 926 private void checkTime() { 927 928 if (!isValidTime()) { 929 m_time.setErrorMessageWidth((m_popup.getOffsetWidth() - 32) + Unit.PX.toString()); 930 m_time.setErrorMessage(Messages.get().key(Messages.ERR_DATEBOX_INVALID_TIME_FORMAT_0)); 931 } else { 932 m_time.setErrorMessage(null); 933 } 934 updateCloseBehavior(); 935 } 936 937 /** 938 * Returns the time text field value as string.<p> 939 * 940 * @return the time text field value as string 941 */ 942 private String getTimeText() { 943 944 String timeAsString = m_time.getText().trim(); 945 if (CmsDateConverter.is12HourPresentation()) { 946 if (!(timeAsString.contains(CmsDateConverter.AM) || timeAsString.contains(CmsDateConverter.PM))) { 947 if (m_am.isChecked()) { 948 timeAsString = timeAsString + " " + CmsDateConverter.AM; 949 } else { 950 timeAsString = timeAsString + " " + CmsDateConverter.PM; 951 } 952 } 953 } 954 return timeAsString; 955 } 956 957 /** 958 * Checks if the String in the time input field is a valid time format.<p> 959 * 960 * @return <code>true</code> if the String in the time input field is a valid time format 961 */ 962 private boolean isValidTime() { 963 964 return m_dateOnly || CmsDateConverter.validateTime(getTimeText()); 965 } 966 967 /** 968 * Sets the value of the date picker.<p> 969 * 970 * @param date the value to set 971 * @param fireEvents signals whether the value changed event should be fired or not 972 */ 973 private void setPickerValue(Date date, boolean fireEvents) { 974 975 if (date == null) { 976 date = new Date(); 977 } 978 m_picker.setValue(date, fireEvents); 979 m_picker.setCurrentMonth(date); 980 m_time.setFormValueAsString(CmsDateConverter.cutSuffix(CmsDateConverter.getTime(date)).trim()); 981 if (CmsDateConverter.isAm(date)) { 982 m_ampmGroup.selectButton(m_am); 983 } else { 984 m_ampmGroup.selectButton(m_pm); 985 } 986 } 987 988 /** 989 * Shows the date picker popup.<p> 990 */ 991 private void showPopup() { 992 993 updateFromTextBox(true); 994 m_box.setPreventShowError(true); 995 m_popup.showRelativeTo(m_box); 996 if (m_previewHandlerRegistration != null) { 997 m_previewHandlerRegistration.removeHandler(); 998 } 999 m_previewHandlerRegistration = Event.addNativePreviewHandler(new CloseEventPreviewHandler()); 1000 } 1001 1002 /** 1003 * Sets the value of the date box.<p> 1004 */ 1005 private void updateFromPicker() { 1006 1007 checkTime(); 1008 Date date = m_picker.getValue(); 1009 if (!m_dateOnly) { 1010 String timeAsString = getTimeText(); 1011 date = CmsDateConverter.getDateWithTime(date, timeAsString); 1012 } 1013 setValue(date); 1014 setErrorMessage(null); 1015 fireChange(date, false); 1016 } 1017 1018 /** 1019 * Updates the picker if the user manually modified the date of the text box.<p> 1020 * 1021 * @param initial flag indicating if the date box is being initialized 1022 */ 1023 private void updateFromTextBox(boolean initial) { 1024 1025 Date date = getValue(); 1026 if (initial && (date == null)) { 1027 date = m_initialDate; 1028 } 1029 setPickerValue(date, false); 1030 m_time.setErrorMessage(null); 1031 fireChange(getValue(), false); 1032 } 1033}