blob: d8c957b62be784c9eed6f64a39719f50bfa031bf [file] [log] [blame]
/*
* Copyright 2010 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.cell.client;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.text.shared.SafeHtmlRenderer;
import com.google.gwt.text.shared.SimpleSafeHtmlRenderer;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
import com.google.gwt.user.datepicker.client.DatePicker;
import java.util.Date;
/**
* A {@link Cell} used to render and edit {@link Date}s. When a cell is selected
* by clicking on it, a {@link DatePicker} is popped up. When a date is selected
* using the {@code DatePicker}, the new date is passed to the
* {@link ValueUpdater#update update} method of the {@link ValueUpdater} that
* was passed to {@link #onBrowserEvent} for the click event. Note that this
* means that the call to {@code ValueUpdater.update} will occur after {@code
* onBrowserEvent} has returned. Pressing the 'escape' key dismisses the {@code
* DatePicker} popup without calling {@code ValueUpdater.update}.
*
* <p>
* Each {@code DatePickerCell} has a unique {@code DatePicker} popup associated
* with it; thus, if a single {@code DatePickerCell} is used as the cell for a
* column in a table, only one entry in that column will be editable at a given
* time.
* </p>
*/
public class DatePickerCell extends AbstractEditableCell<Date, Date> {
private static final int ESCAPE = 27;
private final DatePicker datePicker;
private final DateTimeFormat format;
private int offsetX = 10;
private int offsetY = 10;
private Object lastKey;
private Element lastParent;
private Date lastValue;
private PopupPanel panel;
private final SafeHtmlRenderer<String> renderer;
private ValueUpdater<Date> valueUpdater;
/**
* Constructs a new DatePickerCell that uses the date/time format given by
* {@link DateTimeFormat#getFullDateFormat}.
*/
@SuppressWarnings("deprecation")
public DatePickerCell() {
this(DateTimeFormat.getFullDateFormat(),
SimpleSafeHtmlRenderer.getInstance());
}
/**
* Constructs a new DatePickerCell that uses the given date/time format and a
* {@link SimpleSafeHtmlRenderer}.
*/
public DatePickerCell(DateTimeFormat format) {
this(format, SimpleSafeHtmlRenderer.getInstance());
}
/**
* Constructs a new DatePickerCell that uses the date/time format given by
* {@link DateTimeFormat#getFullDateFormat} and the given
* {@link SafeHtmlRenderer}.
*/
public DatePickerCell(SafeHtmlRenderer<String> renderer) {
this(DateTimeFormat.getFormat(PredefinedFormat.DATE_FULL), renderer);
}
/**
* Constructs a new DatePickerCell that uses the given date/time format and
* {@link SafeHtmlRenderer}.
*/
public DatePickerCell(DateTimeFormat format, SafeHtmlRenderer<String> renderer) {
super("click", "keydown");
if (format == null) {
throw new IllegalArgumentException("format == null");
}
if (renderer == null) {
throw new IllegalArgumentException("renderer == null");
}
this.format = format;
this.renderer = renderer;
this.datePicker = new DatePicker();
this.panel = new PopupPanel(true, true) {
@Override
protected void onPreviewNativeEvent(NativePreviewEvent event) {
if (Event.ONKEYUP == event.getTypeInt()) {
if (event.getNativeEvent().getKeyCode() == ESCAPE) {
// Dismiss when escape is pressed
panel.hide();
}
}
}
};
panel.addCloseHandler(new CloseHandler<PopupPanel>() {
public void onClose(CloseEvent<PopupPanel> event) {
lastKey = null;
lastValue = null;
if (lastParent != null && !event.isAutoClosed()) {
// Refocus on the containing cell after the user selects a value, but
// not if the popup is auto closed.
lastParent.focus();
}
lastParent = null;
}
});
panel.add(datePicker);
// Hide the panel and call valueUpdater.update when a date is selected
datePicker.addValueChangeHandler(new ValueChangeHandler<Date>() {
public void onValueChange(ValueChangeEvent<Date> event) {
panel.hide();
Date date = event.getValue();
setViewData(lastKey, date);
setValue(lastParent, lastValue, lastKey);
if (valueUpdater != null) {
valueUpdater.update(date);
}
}
});
}
@Override
public boolean isEditing(Element parent, Date value, Object key) {
return lastKey != null && lastKey.equals(key);
}
@Override
public void onBrowserEvent(Element parent, Date value, Object key,
NativeEvent event, ValueUpdater<Date> valueUpdater) {
super.onBrowserEvent(parent, value, key, event, valueUpdater);
if ("click".equals(event.getType())) {
onEnterKeyDown(parent, value, key, event, valueUpdater);
}
}
@Override
public void render(Date value, Object key, SafeHtmlBuilder sb) {
// Get the view data.
Date viewData = getViewData(key);
if (viewData != null && viewData.equals(value)) {
clearViewData(key);
viewData = null;
}
String s = null;
if (viewData != null) {
s = format.format(viewData);
} else if (value != null) {
s = format.format(value);
}
if (s != null) {
sb.append(renderer.render(s));
}
}
@Override
protected void onEnterKeyDown(final Element parent, Date value, Object key,
NativeEvent event, ValueUpdater<Date> valueUpdater) {
this.lastKey = key;
this.lastParent = parent;
this.lastValue = value;
this.valueUpdater = valueUpdater;
Date viewData = getViewData(key);
Date date = (viewData == null) ? value : viewData;
datePicker.setCurrentMonth(date);
datePicker.setValue(date);
panel.setPopupPositionAndShow(new PositionCallback() {
public void setPosition(int offsetWidth, int offsetHeight) {
panel.setPopupPosition(parent.getAbsoluteLeft() + offsetX,
parent.getAbsoluteTop() + offsetY);
}
});
}
}