blob: b0667f1d510b5dbd09fbfb6610beefc51d260f77 [file] [log] [blame]
/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.user.datepicker.client;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.TextBox;
import java.util.Date;
/**
* A text box that shows a {@link DatePicker} when the user focuses on it.
*
* <h3>CSS Style Rules</h3>
*
* <ul class="css">
*
* <li>.gwt-DateBox { }</li>
*
* <li>.dateBoxPopup { Applied to the popup around the DatePicker }</li>
*
* </ul>
*
* <p>
* <h3>Example</h3>
* {@example com.google.gwt.examples.DateBoxExample}
* </p>
*/
public class DateBox extends Composite implements HasValue<Date> {
/**
* Implemented by a delegate to report errors parsing date values from the
* user's input.
*
* @deprecated, is going to be replaced with a format interface shortly.
*/
@Deprecated
public interface InvalidDateReporter {
/**
* Called when a valid date has been parsed, or the datebox has been
* cleared.
*/
void clearError();
/**
* Given an unparseable string, explain the situation to the user.
*
* @param input what the user typed
*/
void reportError(String input);
}
private class DateBoxHandler implements ValueChangeHandler<Date>,
FocusHandler, BlurHandler, ClickHandler, KeyDownHandler {
public void onBlur(BlurEvent event) {
if (!popup.isVisible()) {
updateDateFromTextBox();
}
}
public void onClick(ClickEvent event) {
showDatePicker();
}
public void onFocus(FocusEvent event) {
if (allowDPShow) {
showDatePicker();
}
}
public void onKeyDown(KeyDownEvent event) {
switch (event.getNativeKeyCode()) {
case KeyCodes.KEY_ENTER:
case KeyCodes.KEY_TAB:
case KeyCodes.KEY_ESCAPE:
case KeyCodes.KEY_UP:
updateDateFromTextBox();
hideDatePicker();
break;
case KeyCodes.KEY_DOWN:
showDatePicker();
break;
}
}
public void onValueChange(ValueChangeEvent<Date> event) {
setValue(event.getValue());
hideDatePicker();
preventDatePickerPopup();
box.setFocus(true);
}
}
/**
* Default style name.
*/
public static final String DEFAULT_STYLENAME = "gwt-DateBox";
public static final InvalidDateReporter DEFAULT_INVALID_DATE_REPORTER = new InvalidDateReporter() {
public void clearError() {
}
public void reportError(String input) {
}
};
private static final DateTimeFormat DEFAULT_FORMATTER = DateTimeFormat.getMediumDateFormat();
private final PopupPanel popup;
private final TextBox box = new TextBox();
private final DatePicker picker;
private final InvalidDateReporter invalidDateReporter;
private DateTimeFormat dateFormatter = DEFAULT_FORMATTER;
private boolean allowDPShow = true;
/**
* Create a new date box with a new {@link DatePicker} and the
* {@link #DEFAULT_INVALID_DATE_REPORTER}, which does nothing.
*/
public DateBox() {
this(new DatePicker(), DEFAULT_INVALID_DATE_REPORTER);
}
/**
* Create a new date box.
*
* @param picker the picker to drop down from the date box
*/
public DateBox(DatePicker picker, InvalidDateReporter invalidDateReporter) {
this.picker = picker;
this.invalidDateReporter = invalidDateReporter;
this.popup = new PopupPanel();
popup.setAutoHideEnabled(true);
popup.setAutoHidePartner(box.getElement());
popup.setWidget(picker);
popup.setStyleName("dateBoxPopup");
initWidget(box);
setStyleName(DEFAULT_STYLENAME);
DateBoxHandler handler = new DateBoxHandler();
picker.addValueChangeHandler(handler);
box.addFocusHandler(handler);
box.addBlurHandler(handler);
box.addClickHandler(handler);
box.addKeyDownHandler(handler);
}
public HandlerRegistration addValueChangeHandler(
ValueChangeHandler<Date> handler) {
return addHandler(handler, ValueChangeEvent.getType());
}
/**
* Gets the current cursor position in the date box.
*
* @return the cursor position
*
*/
public int getCursorPos() {
return box.getCursorPos();
}
/**
* Gets the date picker.
*
* @return the date picker
*/
public DatePicker getDatePicker() {
return picker;
}
/**
* Gets the date box's position in the tab index.
*
* @return the date box's tab index
*/
public int getTabIndex() {
return box.getTabIndex();
}
/**
* Get text box.
*
* @return the text box used to enter the formatted date
*/
public TextBox getTextBox() {
return box;
}
/**
* Get the date displayed, or null if the text box is empty, or cannot be
* interpretted. The {@link InvalidDateReporter} may fire as a side effect of
* this call.
*
* @return the Date
*/
public Date getValue() {
return parseDate(true);
}
/**
* Hide the date picker.
*/
public void hideDatePicker() {
popup.hide();
}
/**
* @return true if date picker is currently visible, false if not
*/
public boolean isDatePickerVisible() {
return popup.isVisible();
}
/**
* Sets the date box's 'access key'. This key is used (in conjunction with a
* browser-specific modifier key) to automatically focus the widget.
*
* @param key the date box's access key
*/
public void setAccessKey(char key) {
box.setAccessKey(key);
}
/**
* Sets the date format to the given format. If date box is not empty,
* contents of date box will be replaced with current date in new format. If
* the date cannot be parsed, the current value will be preserved and the
* InvalidDateReporter notified as usual.
*
* @param format format.
*/
public void setDateFormat(DateTimeFormat format) {
if (format != dateFormatter) {
Date date = getValue();
dateFormatter = format;
setValue(date);
}
}
/**
* Sets whether the date box is enabled.
*
* @param enabled is the box enabled
*/
public void setEnabled(boolean enabled) {
box.setEnabled(enabled);
}
/**
* Explicitly focus/unfocus this widget. Only one widget can have focus at a
* time, and the widget that does will receive all keyboard events.
*
* @param focused whether this widget should take focus or release it
*/
public void setFocus(boolean focused) {
box.setFocus(focused);
}
/**
* Sets the date box's position in the tab index. If more than one widget has
* the same tab index, each such widget will receive focus in an arbitrary
* order. Setting the tab index to <code>-1</code> will cause this widget to
* be removed from the tab order.
*
* @param index the date box's tab index
*/
public void setTabIndex(int index) {
box.setTabIndex(index);
}
/**
* Set the date.
*/
public void setValue(Date date) {
setValue(date, false);
}
public void setValue(Date date, boolean fireEvents) {
Date oldDate = getValue();
if (date == null) {
picker.setValue(null);
box.setText("");
} else {
picker.setValue(date, false);
picker.setCurrentMonth(date);
setDate(date);
}
invalidDateReporter.clearError();
if (fireEvents) {
DateChangeEvent.fireIfNotEqualDates(this, oldDate, date);
}
}
/**
* Parses the current date box's value and shows that date.
*/
public void showDatePicker() {
Date current = parseDate(false);
if (current == null) {
current = new Date();
}
picker.setCurrentMonth(current);
popup.showRelativeTo(this);
}
private Date parseDate(boolean reportError) {
Date d = null;
String text = box.getText().trim();
if (!text.equals("")) {
try {
d = dateFormatter.parse(text);
} catch (IllegalArgumentException exception) {
if (reportError) {
invalidDateReporter.reportError(text);
}
}
}
return d;
}
private void preventDatePickerPopup() {
allowDPShow = false;
DeferredCommand.addCommand(new Command() {
public void execute() {
allowDPShow = true;
}
});
}
/**
* Does the actual work of setting the date. Performs no validation, fires no
* events.
*/
private void setDate(Date value) {
box.setText(dateFormatter.format(value));
}
private void updateDateFromTextBox() {
Date parsedDate = parseDate(true);
if (parsedDate != null) {
setValue(parsedDate);
}
}
}