/**
* Sencha GXT 3.0.0b - Sencha for GWT
* Copyright(c) 2007-2012, Sencha, Inc.
* licensing@sencha.com
*
* http://www.sencha.com/products/gxt/license/
*/
package com.sencha.gxt.desktopapp.client.spreadsheet;
import java.util.LinkedList;
import java.util.List;
import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.cell.client.Cell;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.client.ui.Widget;
import com.sencha.gxt.core.client.IdentityValueProvider;
import com.sencha.gxt.core.client.Style.SelectionMode;
import com.sencha.gxt.desktop.client.widget.AutoVerticalLayoutContainer;
import com.sencha.gxt.desktopapp.client.filemanager.images.Images;
import com.sencha.gxt.dnd.core.client.DND.Feedback;
import com.sencha.gxt.dnd.core.client.DndDropEvent;
import com.sencha.gxt.dnd.core.client.GridDragSource;
import com.sencha.gxt.dnd.core.client.GridDropTarget;
import com.sencha.gxt.widget.core.client.Window;
import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer;
import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData;
import com.sencha.gxt.widget.core.client.event.ColumnMoveEvent;
import com.sencha.gxt.widget.core.client.event.ColumnMoveEvent.ColumnMoveHandler;
import com.sencha.gxt.widget.core.client.event.HideEvent;
import com.sencha.gxt.widget.core.client.event.HideEvent.HideHandler;
import com.sencha.gxt.widget.core.client.event.ShowEvent;
import com.sencha.gxt.widget.core.client.event.ShowEvent.ShowHandler;
import com.sencha.gxt.widget.core.client.form.TextField;
import com.sencha.gxt.widget.core.client.grid.CellSelectionModel;
import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
import com.sencha.gxt.widget.core.client.grid.ColumnModel;
import com.sencha.gxt.widget.core.client.grid.Grid;
import com.sencha.gxt.widget.core.client.grid.RowNumberer;
import com.sencha.gxt.widget.core.client.grid.editing.ClicksToEdit;
import com.sencha.gxt.widget.core.client.grid.editing.GridInlineEditing;
import com.sencha.gxt.widget.core.client.menu.Menu;
import com.sencha.gxt.widget.core.client.selection.CellSelection;
import com.sencha.gxt.widget.core.client.selection.CellSelectionChangedEvent;
import com.sencha.gxt.widget.core.client.selection.CellSelectionChangedEvent.CellSelectionChangedHandler;
public class SpreadsheetViewImpl implements SpreadsheetView, HideHandler, ShowHandler, ColumnProvider {
public static final String CELL_TOOL_TIP = "Enter a value or expression, for example:<br/>=((C2-B2)/B2)*100 or =SUM(B2:B10)<br/>Use $ for same row or column (e.g. B$ or $2).";
private static final int DEFAULT_COLUMN_WIDTH = 50;
private SpreadsheetPresenter spreadsheetPresenter;
private Window window;
private Grid<Row> grid;
private Worksheet worksheet;
private ColumnModel<Row> columnModel;
private List<ColumnConfig<Row, ?>> columnConfigs;
private RowNumberer<Row> rowNumberer;
private IdentityValueProvider<Row> identityValueProvider;
private GridInlineEditing<Row> gridInlineEditing;
private VerticalLayoutContainer verticalLayoutContainer;
private SpreadsheetToolBar spreadsheetToolBar;
private Cell<String> displayCell;
private CellSelectionModel<Row> cellSelectionModel;
private GridDragSource<Row> gridDragSource;
private GridDropTarget<Row> gridDropTarget;
private Menu spreadsheetMenu;
private NumberFormat defaultNumberFormat;
public SpreadsheetViewImpl(SpreadsheetPresenter spreadsheetPresenter) {
this.spreadsheetPresenter = spreadsheetPresenter;
}
@Override
public Widget asWidget() {
return getWindow();
}
@Override
public void close() {
getWindow().hide();
}
@Override
public ColumnConfig<Row, Object> getColumn(int cellIndex) {
return getColumnModel().getColumn(cellIndex);
}
@Override
public int getSelectedColumn() {
int selectedColumn = -1;
CellSelection<Row> cell = getCellSelectionModel().getSelectCell();
if (cell != null) {
selectedColumn = cell.getCell() - 1;
}
return selectedColumn;
}
@Override
public int getSelectedRow() {
int selectedRow = -1;
CellSelection<Row> cell = getCellSelectionModel().getSelectCell();
if (cell != null) {
selectedRow = cell.getRow();
}
return selectedRow;
}
@Override
public String getValue() {
return getWorksheet().getValue();
}
@Override
public void onHide(HideEvent event) {
getSpreadsheetPresenter().unbind();
}
@Override
public void onShow(ShowEvent event) {
getSpreadsheetPresenter().bind();
getGrid().focus();
}
@Override
public void refresh() {
getGrid().getView().refresh(false);
}
@Override
public void setColumnHeader(int columnIndex, SafeHtml header) {
getColumnModel().setColumnHeader(columnIndex, header);
}
@Override
public void setTitle(String title) {
getWindow().setHeadingText(title);
}
@Override
public void setValue(String value) {
// Value is provided by setValue(Worksheet worksheet)
}
@Override
public void setValue(Worksheet worksheet) {
this.worksheet = worksheet;
updateGrid();
}
@Override
public void updateDetails(String cellName, String cellValue) {
getSpreadsheetToolBar().setDetails(cellName, cellValue);
}
@Override
public void updateGrid() {
if (getVerticalLayoutContainer().getWidgetCount() == 1) {
getVerticalLayoutContainer().add(getGrid(), new VerticalLayoutData(1, 1));
} else {
columnModel = null;
columnConfigs = null;
getGridInlineEditing().clearEditors();
getGrid().reconfigure(getWorksheet().getListStore(), getColumnModel());
}
adjustColumnWidths();
getSpreadsheetToolBar().setDimensions(getWorksheet().getRowCount(), getWorksheet().getColumnCount());
}
@Override
public void updateSelectedCells(String value) {
CellSelection<Row> cellSelection = getCellSelectionModel().getSelectCell();
if (cellSelection != null) {
getWorksheet().setValue(value, cellSelection);
getGrid().getView().refresh(false);
}
}
private void adjustColumnWidths() {
int requiredWidth = 0;
boolean isRefreshRequired = false;
int cellCount = getWorksheet().getCellCount();
for (int cellIndex = 0; cellIndex < cellCount; cellIndex++) {
ColumnConfig<Row, Object> columnConfig = getColumnModel().getColumn(cellIndex);
if (!columnConfig.isHidden()) {
int columnWidth = columnConfig.getWidth();
if (cellIndex > 0 && columnWidth < DEFAULT_COLUMN_WIDTH) {
columnWidth = DEFAULT_COLUMN_WIDTH;
columnConfig.setWidth(columnWidth);
isRefreshRequired = true;
}
requiredWidth += columnWidth;
}
}
int offsetWidth = getGrid().getOffsetWidth();
boolean isAutoFillRequired = requiredWidth < offsetWidth;
if (isAutoFillRequired != getGrid().getView().isAutoFill()) {
isRefreshRequired = true;
getGrid().getView().setAutoFill(isAutoFillRequired);
}
if (isRefreshRequired) {
getGrid().getView().refresh(true);
}
}
private TextField createCellEditorTextField() {
TextField cellEditorTextField = new TextField();
cellEditorTextField.setSelectOnFocus(true);
cellEditorTextField.setToolTip(CELL_TOOL_TIP);
cellEditorTextField.addValueChangeHandler(new ValueChangeHandler<String>() {
@Override
public void onValueChange(ValueChangeEvent<String> event) {
CellSelection<Row> cell = getCellSelectionModel().getSelectCell();
getSpreadsheetPresenter().onCellValueChange(cell.getRow(), cell.getCell() - 1, event.getValue());
}
});
return cellEditorTextField;
}
private void displaySelectedCell(List<CellSelection<Row>> cellSelections) {
String name = null;
String value = null;
if (cellSelections != null) {
if (cellSelections.size() > 0) {
CellSelection<Row> cellSelection = cellSelections.get(0);
if (cellSelection != null) {
name = getWorksheet().getName(cellSelection);
if (name != null) {
value = getWorksheet().getValue(cellSelection);
}
}
}
}
getSpreadsheetToolBar().setDetails(name, value);
}
private CellSelectionModel<Row> getCellSelectionModel() {
if (cellSelectionModel == null) {
cellSelectionModel = new CellSelectionModel<Row>();
cellSelectionModel.setSelectionMode(SelectionMode.MULTI);
cellSelectionModel.addCellSelectionChangedHandler(new CellSelectionChangedHandler<Row>() {
@Override
public void onCellSelectionChanged(CellSelectionChangedEvent<Row> event) {
List<CellSelection<Row>> cellSelections = event.getSelection();
displaySelectedCell(cellSelections);
}
});
}
return cellSelectionModel;
}
private List<ColumnConfig<Row, ?>> getColumnConfigs() {
if (columnConfigs == null) {
columnConfigs = new LinkedList<ColumnConfig<Row, ?>>();
columnConfigs.add(getRowNumberer());
int columnCount = getWorksheet().getColumnCount();
for (int i = 0; i < columnCount; i++) {
ColumnConfig<Row, String> columnConfig = new ColumnConfig<Row, String>(new RowValueProvider(i));
columnConfig.setHeader(SpreadsheetUtilities.getColumnName(i));
columnConfig.setWidth(DEFAULT_COLUMN_WIDTH);
columnConfig.setCell(getDisplayCell());
getGridInlineEditing().addEditor(columnConfig, createCellEditorTextField());
columnConfigs.add(columnConfig);
}
}
return columnConfigs;
}
private ColumnModel<Row> getColumnModel() {
if (columnModel == null) {
columnModel = new ColumnModel<Row>(getColumnConfigs());
columnModel.addColumnMoveHandler(new ColumnMoveHandler() {
@Override
public void onColumnMove(ColumnMoveEvent event) {
getWorksheet().renameColumns(event.getIndex());
getSpreadsheetPresenter().onColumnMove();
}
});
}
return columnModel;
}
private NumberFormat getDefaultNumberFormat() {
if (defaultNumberFormat == null) {
defaultNumberFormat = NumberFormat.getFormat("#.##");
}
return defaultNumberFormat;
}
private Cell<String> getDisplayCell() {
if (displayCell == null) {
displayCell = new AbstractCell<String>() {
@Override
public void render(Context context, String value, SafeHtmlBuilder sb) {
if (value != null) {
if (value.startsWith(Evaluator.EXPRESSION_MARKER)) {
double result = getWorksheet().evaluateCell(value, context.getIndex(), context.getColumn());
String formattedResult = getDefaultNumberFormat().format(result);
sb.appendHtmlConstant("<b>" + formattedResult + "</b>");
} else {
sb.appendEscaped(value);
}
}
}
};
}
return displayCell;
}
private Grid<Row> getGrid() {
if (grid == null) {
grid = new Grid<Row>(getWorksheet().getListStore(), getColumnModel());
grid.setSelectionModel(getCellSelectionModel());
grid.getView().setStripeRows(true);
grid.getView().setColumnLines(true);
grid.setColumnReordering(true);
grid.setContextMenu(getSpreadsheetMenu());
getRowNumberer().initPlugin(grid);
getGridInlineEditing().setEditableGrid(grid);
getGridDragSource();
getGridDropTarget();
grid.addResizeHandler(new ResizeHandler() {
@Override
public void onResize(ResizeEvent event) {
adjustColumnWidths();
}
});
}
return grid;
}
private GridDragSource<Row> getGridDragSource() {
if (gridDragSource == null) {
gridDragSource = new GridDragSource<Row>(getGrid());
}
return gridDragSource;
}
private GridDropTarget<Row> getGridDropTarget() {
if (gridDropTarget == null) {
gridDropTarget = new GridDropTarget<Row>(getGrid()) {
@Override
protected void onDragDrop(DndDropEvent e) {
getGrid().getStore().clearSortInfo();
getGrid().getView().getHeader().refresh();
super.onDragDrop(e);
getSpreadsheetPresenter().onDragDrop();
}
};
gridDropTarget.setAllowSelfAsSource(true);
gridDropTarget.setFeedback(Feedback.BOTH);
}
return gridDropTarget;
}
private GridInlineEditing<Row> getGridInlineEditing() {
if (gridInlineEditing == null) {
gridInlineEditing = new GridInlineEditing<Row>(null);
gridInlineEditing.setClicksToEdit(ClicksToEdit.TWO);
}
return gridInlineEditing;
}
private IdentityValueProvider<Row> getIdentityValueProvider() {
if (identityValueProvider == null) {
identityValueProvider = new IdentityValueProvider<Row>();
}
return identityValueProvider;
}
private RowNumberer<Row> getRowNumberer() {
if (rowNumberer == null) {
rowNumberer = new RowNumberer<Row>(getIdentityValueProvider());
}
return rowNumberer;
}
private Menu getSpreadsheetMenu() {
if (spreadsheetMenu == null) {
spreadsheetMenu = new SpreadsheetMenu(getSpreadsheetPresenter()).getMenu();
}
return spreadsheetMenu;
}
private SpreadsheetPresenter getSpreadsheetPresenter() {
return spreadsheetPresenter;
}
private SpreadsheetToolBar getSpreadsheetToolBar() {
if (spreadsheetToolBar == null) {
spreadsheetToolBar = new SpreadsheetToolBar(getSpreadsheetPresenter());
}
return spreadsheetToolBar;
}
private VerticalLayoutContainer getVerticalLayoutContainer() {
if (verticalLayoutContainer == null) {
verticalLayoutContainer = new AutoVerticalLayoutContainer();
verticalLayoutContainer.setBorders(true);
verticalLayoutContainer.add(getSpreadsheetToolBar(), new VerticalLayoutData(1, -1));
}
return verticalLayoutContainer;
}
private Window getWindow() {
if (window == null) {
window = new Window();
window.getHeader().setIcon(Images.getImageResources().table());
window.setMinimizable(true);
window.setMaximizable(true);
window.setPixelSize(500, 400);
window.setOnEsc(false);
window.setWidget(getVerticalLayoutContainer());
window.addHideHandler(this);
window.addShowHandler(this);
}
return window;
}
private Worksheet getWorksheet() {
return worksheet;
}
}