/*
* 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.gen2.table.client;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.gen2.table.override.client.HTMLTable;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.AbstractImagePrototype;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.ImageBundle;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
/**
* An abstract representation of an editor used to edit the contents of a cell.
*
* <h3>CSS Style Rules</h3>
* <dl>
* <dt>.gwt-InlineCellEditor</dt>
* <dd>applied to the entire widget</dd>
* <dt>.gwt-InlineCellEditor .accept</dt>
* <dd>applied to the accept image</dd>
* <dt>.gwt-InlineCellEditor .cancel</dt>
* <dd>applied to the cancel image</dd>
* </dl>
*
* @param <ColType> the data type of the column
*/
public abstract class InlineCellEditor<ColType> extends PopupPanel implements
CellEditor<ColType> {
/**
* An {@link ImageBundle} that provides images for {@link InlineCellEditor}.
*/
public static interface InlineCellEditorImages extends ImageBundle {
/**
* An image used to fill the available width.
*
* @return a prototype of this image
*/
AbstractImagePrototype cellEditorAccept();
/**
* An image indicating that a column is sorted in ascending order.
*
* @return a prototype of this image
*/
AbstractImagePrototype cellEditorCancel();
}
/**
* <code>ClickDecoratorPanel</code> decorates any widget with the minimal
* amount of machinery to receive clicks for delegation to the parent.
*/
private static final class ClickDecoratorPanel extends SimplePanel {
public ClickDecoratorPanel(Widget child, ClickHandler delegate) {
setWidget(child);
addDomHandler(delegate, ClickEvent.getType());
}
}
/**
* Default style name.
*/
public static final String DEFAULT_STYLENAME = "gwt-InlineCellEditor";
/**
* The click listener used to accept.
*/
private ClickHandler cancelHandler = new ClickHandler() {
public void onClick(ClickEvent event) {
cancel();
}
};
/**
* The click listener used to accept.
*/
private ClickHandler acceptHandler = new ClickHandler() {
public void onClick(ClickEvent event) {
accept();
}
};
/**
* The current {@link CellEditor.Callback}.
*/
private Callback<ColType> curCallback = null;
/**
* The current {@link CellEditor.CellEditInfo}.
*/
private CellEditInfo curCellEditInfo = null;
/**
* The main grid used for layout.
*/
private FlexTable layoutTable;
/**
* Construct a new {@link InlineCellEditor}.
*
* @param content the {@link Widget} used to edit
*/
protected InlineCellEditor(Widget content) {
this(content,
GWT.<InlineCellEditorImages> create(InlineCellEditorImages.class));
}
/**
* Construct a new {@link InlineCellEditor} with the specified images.
*
* @param content the {@link Widget} used to edit
* @param images the images to use for the accept/cancel buttons
*/
protected InlineCellEditor(Widget content, InlineCellEditorImages images) {
super(true, true);
setStylePrimaryName(DEFAULT_STYLENAME);
// Wrap contents in a table
layoutTable = new FlexTable();
FlexCellFormatter formatter = layoutTable.getFlexCellFormatter();
layoutTable.setCellSpacing(0);
setWidget(layoutTable);
// Add a label
setLabel("");
formatter.setColSpan(0, 0, 3);
// Add content widget
layoutTable.setWidget(1, 0, content);
// Add accept and cancel buttons
setAcceptWidget(images.cellEditorAccept().createImage());
setCancelWidget(images.cellEditorCancel().createImage());
}
public void editCell(CellEditInfo cellEditInfo, ColType cellValue,
Callback<ColType> callback) {
// Save the current values
curCallback = callback;
curCellEditInfo = cellEditInfo;
// Get the info about the cell
HTMLTable table = curCellEditInfo.getTable();
int row = curCellEditInfo.getRowIndex();
int cell = curCellEditInfo.getCellIndex();
// Get the location of the cell
Element cellElem = table.getCellFormatter().getElement(row, cell);
int top = DOM.getAbsoluteTop(cellElem) + getOffsetTop();
int left = DOM.getAbsoluteLeft(cellElem) + getOffsetLeft();
setPopupPosition(left, top);
// Set the current value
setValue(cellValue);
// Show the editor
show();
}
/**
* @return the label text
*/
public String getLabel() {
return layoutTable.getHTML(0, 0);
}
/**
* Set the label for this cell editor.
*
* @param label the new label
*/
public void setLabel(String label) {
layoutTable.setHTML(0, 0, label);
}
/**
* Accept the contents of the cell editor as the new cell value.
*/
protected void accept() {
// Check if we are ready to accept
if (!onAccept()) {
return;
}
// Get the value before hiding the editor
ColType cellValue = getValue();
// Hide the editor
hide();
// Send the new cell value to the callback
curCallback.onComplete(curCellEditInfo, cellValue);
curCallback = null;
curCellEditInfo = null;
}
/**
* Cancel the cell edit.
*/
protected void cancel() {
// Fire the event
if (!onCancel()) {
return;
}
// Hide the popup
hide();
// Call the callback
if (curCallback != null) {
curCallback.onCancel(curCellEditInfo);
curCellEditInfo = null;
curCallback = null;
}
}
/**
* @return the Widget that is used to accept the current value.
*/
protected Widget getAcceptWidget() {
ClickDecoratorPanel clickPanel = (ClickDecoratorPanel) layoutTable.getWidget(
1, 1);
return clickPanel.getWidget();
}
/**
* @return the Widget that is used to cancel editing.
*/
protected Widget getCancelWidget() {
ClickDecoratorPanel clickPanel = (ClickDecoratorPanel) layoutTable.getWidget(
1, 2);
return clickPanel.getWidget();
}
/**
* @return the content widget
*/
protected Widget getContentWidget() {
return layoutTable.getWidget(1, 0);
}
/**
* Get the additional number of pixels to offset this cell editor from the top
* left corner of the cell. Override this method to shift the editor left or
* right.
*
* @return the additional left offset in pixels
*/
protected int getOffsetLeft() {
return 0;
}
/**
* Get the additional number of pixels to offset this cell editor from the top
* left corner of the cell. Override this method to shift the editor up or
* down.
*
* @return the additional top offset in pixels
*/
protected int getOffsetTop() {
return 0;
}
/**
* Get the new cell value from the editor.
*
* @return the new cell value
*/
protected abstract ColType getValue();
/**
* Called before an accept takes place.
*
* @return true to allow the accept, false to prevent it
*/
protected boolean onAccept() {
return true;
}
/**
* Called before a cancel takes place.
*
* @return true to allow the cancel, false to prevent it
*/
protected boolean onCancel() {
return true;
}
/**
* Set the Widget that is used to accept the current value.
*
* @param w the widget
*/
protected void setAcceptWidget(Widget w) {
ClickDecoratorPanel clickPanel = new ClickDecoratorPanel(w, acceptHandler);
clickPanel.setStyleName("accept");
layoutTable.setWidget(1, 1, clickPanel);
}
/**
* Set the Widget that is used to cancel editing.
*
* @param w the widget
*/
protected void setCancelWidget(Widget w) {
ClickDecoratorPanel clickPanel = new ClickDecoratorPanel(w, cancelHandler);
clickPanel.setStyleName("cancel");
layoutTable.setWidget(1, 2, clickPanel);
}
/**
* Set the cell value in the editor.
*
* @param cellValue the value in the cell
*/
protected abstract void setValue(ColType cellValue);
}