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