package com.vaadin.addon.spreadsheet.client;
/*
* #%L
* Vaadin Spreadsheet
* %%
* Copyright (C) 2013 - 2015 Vaadin Ltd
* %%
* This program is available under Commercial Vaadin Add-On License 3.0
* (CVALv3).
*
* See the file license.html distributed with this software for more
* information about licensing.
*
* You should have received a copy of the CVALv3 along with this program.
* If not, see <http://vaadin.com/license/cval-3>.
* #L%
*/
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.TouchEvent;
import com.google.gwt.event.logical.shared.AttachEvent;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.addon.spreadsheet.client.MergedRegionUtil.MergedRegionContainer;
import com.vaadin.addon.spreadsheet.client.SheetTabSheet.SheetTabSheetHandler;
import com.vaadin.addon.spreadsheet.client.SpreadsheetConnector.CommsTrigger;
import com.vaadin.addon.spreadsheet.shared.GroupingData;
import com.vaadin.client.Focusable;
import com.vaadin.client.ServerConnector;
import com.vaadin.client.Util;
import com.vaadin.client.WidgetUtil;
import com.vaadin.client.communication.RpcProxy;
public class SpreadsheetWidget extends Composite implements SheetHandler,
FormulaBarHandler, SheetTabSheetHandler, Focusable {
public interface SheetContextMenuHandler {
/**
* Right click (event) on top of the cell at the indexes.
*
* @param event
* the browser event related (right mouse button click)
* @param column
* 1-based, index of cell
* @param row
* 1-based, index of cell
*/
public void cellContextMenu(NativeEvent event, int column, int row);
/**
* Right click (event) on top of row header at the index
*
* @param nativeEvent
* @param rowIndex
* 1-based
*/
public void rowHeaderContextMenu(NativeEvent nativeEvent, int rowIndex);
/**
* Right click (event) on top of column header at the index
*
* @param nativeEvent
* @param columnIndex
* 1-based
*/
public void columnHeaderContextMenu(NativeEvent nativeEvent,
int columnIndex);
}
private static final int DELAYED_SERVER_REQUEST_DELAY = 200; // ms
private static final String DEFAULT_WIDTH = "500.0px";
private static final String DEFAULT_HEIGHT = "400.0px";
private final SheetWidget sheetWidget;
final FormulaBarWidget formulaBarWidget;
private final SheetTabSheet sheetTabSheet;
private final SelectionHandler selectionHandler;
SpreadsheetHandler spreadsheetHandler;
private SheetContextMenuHandler sheetContextMenuHandler;
SpreadsheetCustomEditorFactory customEditorFactory;
private int rowBufferSize;
private int columnBufferSize;
private int rows;
private int cols;
private float defRowH;
private int defColW;
private float[] rowH;
private int[] colW;
/** 1-based */
private int activeSheetIndex;
private Map<Integer, String> cellStyleToCSSStyle;
public Map<Integer, Integer> rowIndexToStyleIndex;
public Map<Integer, Integer> columnIndexToStyleIndex;
private Set<Integer> lockedColumnIndexes;
private Set<Integer> lockedRowIndexes;
private Map<Integer, String> conditionalFormattingStyles = new HashMap<Integer, String>();
private boolean loaded;
private boolean touchMode;
private boolean formulaBarEditing;
private boolean inlineEditing;
private boolean cancelDeferredCommit;
private boolean selectedCellIsFormulaType;
boolean cellLocked;
boolean customCellEditorDisplayed;
private boolean sheetProtected;
private boolean cancelNextSheetRelayout;
private String cachedCellValue;
private int[] verticalScrollPositions;
private int[] horizontalScrollPositions;
// private int firstVisibleTab; Not working in POI -> disabled
private String[] sheetNames;
List<Integer> hiddenColumnIndexes;
List<Integer> hiddenRowIndexes;
private List<MergedRegion> mergedRegions;
private boolean lockFormatColumns = true;
private boolean lockFormatRows = true;
/**
* Timer flag for sending lazy RPCs to server. Used so that we don't send an
* RPC for each key press. Default timeout is a second.
*/
private boolean okToSendCellProtectRpc = true;
@SuppressWarnings("serial")
MergedRegionContainer mergedRegionContainer = new MergedRegionContainer() {
@Override
public MergedRegion getMergedRegionStartingFrom(int column, int row) {
if (mergedRegions != null) {
for (MergedRegion region : mergedRegions) {
if (region.col1 == column && region.row1 == row) {
return region;
}
}
}
return null;
}
@Override
public MergedRegion getMergedRegion(int column, int row) {
if (mergedRegions != null) {
for (MergedRegion region : mergedRegions) {
if (region.col1 <= column && region.row1 <= row
&& region.col2 >= column && region.row2 >= row) {
return region;
}
}
}
return null;
}
};
private CommsTrigger commsTrigger;
/**
* The last click coords when editing formula
*/
private int tempSelectionStartCol;
/**
* The last click coords when editing formula
*/
private int tempSelectionStartRow;
public SpreadsheetWidget() {
setTouchMode(TouchEvent.isSupported());
sheetWidget = new SheetWidget(this, touchMode);
formulaBarWidget = new FormulaBarWidget(this, sheetWidget);
sheetTabSheet = new SheetTabSheet(this);
selectionHandler = new SelectionHandler(this, sheetWidget);
sheetWidget.getElement().appendChild(formulaBarWidget.getElement());
sheetWidget.getElement().appendChild(sheetTabSheet.getElement());
initWidget(sheetWidget);
//There is a bug in CssLayout/VerticalLayout.
//If a component calls setVisible(false) another component in the layout
//next to it is detached and then attached to the layout and the scroll
//position is reset. We need to store the scroll position on detach and
//then set on attach event.
sheetWidget.addAttachHandler(new AttachEvent.Handler() {
int leftScrollPosition = 0;
int topScrollPosition = 0;
@Override
public void onAttachOrDetach(AttachEvent attachEvent) {
if (attachEvent.isAttached()) {
sheetWidget.setScrollPosition(leftScrollPosition, topScrollPosition);
} else {
leftScrollPosition = sheetWidget.getSheetScrollLeft();
topScrollPosition = sheetWidget.getSheetScrollTop();
}
}
});
}
@Override
public void setHeight(final String height) {
if (height != null && !height.isEmpty()) {
super.setHeight(height);
} else {
super.setHeight(DEFAULT_HEIGHT);
}
}
@Override
public void setWidth(final String width) {
if (width != null && !width.isEmpty()) {
super.setWidth(width);
} else {
super.setWidth(DEFAULT_WIDTH);
}
}
/**
* Enable or disable Formatting columns locking.
*
* @param enabled
* the new content. Can not be HTML.
*/
public void setLockFormatColumns(boolean enabled) {
lockFormatColumns = enabled;
if (lockFormatColumns) {
if (!getStyleName().contains("lock-format-columns")) {
addStyleName("lock-format-columns");
}
} else {
removeStyleName("lock-format-columns");
}
}
/**
* Enable or disable Formatting rows locking.
*
* @param enabled
* the new content. Can not be HTML.
*/
public void setLockFormatRows(boolean enabled) {
lockFormatRows = enabled;
if (lockFormatRows) {
addStyleName("lock-format-rows");
} else {
removeStyleName("lock-format-rows");
}
}
/**
* Sets the content of the info label.
*
* @param value
* the new content. Can not be HTML.
*/
public void setInfoLabelValue(String value) {
sheetTabSheet.setInfoLabelValue(value);
}
/**
* @return current content of the info label.
*/
public String getInfoLabelValue() {
return sheetTabSheet.getInfoLabelValue();
}
public SheetWidget getSheetWidget() {
return sheetWidget;
}
public void load() {
if (loaded) {
clearSpreadsheet(false);
} else {
loaded = true;
}
loadSheet(activeSheetIndex - 1);
}
public void sheetUpdated(String[] sheetNames, int sheetIndex,
boolean clearScrollPosition) {
if (!loaded) { // component first load
sheetTabSheet.addTabs(sheetNames);
sheetTabSheet.setSelectedTab(sheetIndex);
} else {
if (activeSheetIndex != sheetIndex) {
// active sheet or whole spreadsheet has changed
sheetTabSheet.setTabs(sheetNames, clearScrollPosition);
sheetTabSheet.setSelectedTab(sheetIndex);
} else if (this.sheetNames == null
|| !Arrays.equals(this.sheetNames, sheetNames)) {
// sheet renamed
sheetTabSheet.setTabs(sheetNames, false);
}
}
this.sheetNames = sheetNames;
activeSheetIndex = sheetIndex;
}
public void widgetSizeChanged() {
sheetWidget.onWidgetResize();
sheetTabSheet.onWidgetResize();
}
/** Clear all current sheet related data */
public void clearSpreadsheet(boolean removed) {
selectionHandler.clearBeforeMergeCells();
// reset function bar
formulaBarWidget.clear();
clearMergedRegions();
// reset sheet
sheetWidget.clearAll(removed);
}
/**
*
* @param sheetIndex
* 0-based index of the sheet to load
*/
protected void loadSheet(final int sheetIndex) {
// load all sheet stuff from model
final int scrollLeft;
if (horizontalScrollPositions.length > sheetIndex) {
scrollLeft = horizontalScrollPositions[sheetIndex];
} else {
scrollLeft = 0;
}
final int scrollTop;
if (verticalScrollPositions.length > sheetIndex) {
scrollTop = verticalScrollPositions[sheetIndex];
} else {
scrollTop = 0;
}
sheetWidget.resetFromModel(scrollLeft, scrollTop);
if (scrollLeft != 0 || scrollTop != 0) {
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
sheetWidget.setScrollPosition(scrollLeft, scrollTop);
}
});
}
}
public void relayoutSheet() {
if (cancelNextSheetRelayout) {
cancelNextSheetRelayout = false;
} else {
sheetWidget.relayoutSheet(true);
}
}
public void setSpreadsheetHandler(SpreadsheetHandler spreadsheetHandler) {
this.spreadsheetHandler = spreadsheetHandler;
}
public void setSheetContextMenuHandler(
SheetContextMenuHandler sheetContextMenuHandler) {
this.sheetContextMenuHandler = sheetContextMenuHandler;
}
@Override
public boolean hasCustomContextMenu() {
return sheetContextMenuHandler != null;
}
public void showCellCustomComponents(HashMap<String, Widget> customWidgetMap) {
sheetWidget.showCustomWidgets(customWidgetMap);
}
public void addPopupButton(PopupButtonWidget widget) {
sheetWidget.addPopupButton(widget);
}
public void removePopupButton(PopupButtonWidget popupButton) {
sheetWidget.removePopupButton(popupButton);
}
public void showCellValue(String value, int col, int row, boolean formula,
boolean locked) {
// do check in case the user has changed the selected cell before the
// formula was sent
if (sheetWidget.getSelectedCellColumn() == col
&& sheetWidget.getSelectedCellRow() == row) {
updateSelectedCellValues(col, row);
}
}
public void invalidCellAddress() {
formulaBarWidget.revertCellAddressValue();
}
public void focusSheet() {
sheetWidget.focusSheet();
}
/**
* @return the customEditorFactory
*/
public SpreadsheetCustomEditorFactory getCustomEditorFactory() {
return customEditorFactory;
}
/**
* @param customEditorFactory
* the customEditorFactory to set
*/
public void setCustomEditorFactory(
SpreadsheetCustomEditorFactory customEditorFactory) {
this.customEditorFactory = customEditorFactory;
selectionHandler.newSelectedCellSet();
}
/**
* Called when the {@link #customEditorFactory} might have a new editor for
* the currently selected cell.
*/
public void loadSelectedCellEditor() {
if (!sheetWidget.isSelectedCellCustomized()
&& !cellLocked
&& customEditorFactory != null
&& customEditorFactory.hasCustomEditor(sheetWidget
.getSelectedCellKey())) {
Widget customEditor = customEditorFactory
.getCustomEditor(sheetWidget.getSelectedCellKey());
if (customEditor != null) {
customCellEditorDisplayed = true;
formulaBarWidget.setFormulaFieldEnabled(false);
sheetWidget.displayCustomCellEditor(customEditor);
}
}
}
public void addVisibleCellComment(String key) {
sheetWidget.setCellCommentVisible(true, key);
}
public void removeVisibleCellComment(String key) {
sheetWidget.setCellCommentVisible(false, key);
}
/**
* Handles overlays, currently images and charts.
*/
void addOverlay(String key, Widget widget, OverlayInfo overlayInfo) {
SheetOverlay overlay = new SheetOverlay(widget, overlayInfo);
sheetWidget.addSheetOverlay(key, overlay);
}
void updateOverlay(String key, OverlayInfo overlayInfo) {
sheetWidget.updateOverlayInfo(key, overlayInfo);
}
void removeOverlay(String key) {
sheetWidget.removeSheetOverlay(key);
}
public void updateMergedRegions(final ArrayList<MergedRegion> mergedRegions) {
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
// remove old, add new
clearMergedRegions();
if (mergedRegions != null) {
int i = 0;
while (i < mergedRegions.size()) {
MergedRegion newMergedRegion = mergedRegions.get(i);
sheetWidget.addMergedRegion(newMergedRegion);
i++;
}
sheetWidget.checkMergedRegionPositions();
}
// copy list for later
if (mergedRegions == null) {
SpreadsheetWidget.this.mergedRegions = null;
} else {
SpreadsheetWidget.this.mergedRegions = new ArrayList<MergedRegion>(
mergedRegions);
}
}
});
}
private void clearMergedRegions() {
if (mergedRegions != null) {
while (0 < mergedRegions.size()) {
sheetWidget.removeMergedRegion(mergedRegions.remove(0), 0);
}
}
}
@Override
public void onScrollViewChanged(int firstRowIndex, int lastRowIndex,
int firstColumnIndex, int lastColumnIndex) {
spreadsheetHandler.onSheetScroll(firstRowIndex, firstColumnIndex,
lastRowIndex, lastColumnIndex);
startDelayedSendingTimer();
}
@Override
public void onLinkCellClick(int column, int row) {
spreadsheetHandler.linkCellClicked(row, column);
}
@Override
public void onCellRightClick(NativeEvent event, int column, int row) {
// logic handled on server side
if (sheetContextMenuHandler != null) {
if (column != sheetWidget.getSelectedCellColumn()
|| row != sheetWidget.getSelectedCellRow()) {
doCommitIfEditing();
}
sheetContextMenuHandler.cellContextMenu(event, column, row);
}
}
@Override
public void onRowHeaderRightClick(NativeEvent nativeEvent, int rowIndex) {
if (sheetContextMenuHandler != null) {
sheetContextMenuHandler.rowHeaderContextMenu(nativeEvent, rowIndex);
}
}
@Override
public void onColumnHeaderRightClick(NativeEvent nativeEvent,
int columnIndex) {
if (sheetContextMenuHandler != null) {
sheetContextMenuHandler.columnHeaderContextMenu(nativeEvent,
columnIndex);
}
}
@Override
public void onCellClick(int column, int row, String value,
boolean shiftKey, boolean metaOrCtrlKey,
boolean updateToActionHandler) {
doCommitIfEditing();
if (column == 0 || row == 0) {
return;
}
boolean hasSelectedCellChangedOnClick = false;
if (!updateToActionHandler) {
hasSelectedCellChangedOnClick = row != sheetWidget
.getSelectedCellRow()
|| column != sheetWidget.getSelectedCellColumn();
}
if (shiftKey) {
// select everything from previously selected cell to the clicked
// cell, keep the old selected cell as the selected
final int selectedCellCol = sheetWidget.getSelectedCellColumn();
final int selectedCellRow = sheetWidget.getSelectedCellRow();
int c1 = selectedCellCol > column ? column : selectedCellCol;
int c2 = selectedCellCol > column ? selectedCellCol : column;
int r1 = selectedCellRow > row ? row : selectedCellRow;
int r2 = selectedCellRow > row ? selectedCellRow : row;
MergedRegion selectedRegion = MergedRegionUtil
.findIncreasingSelection(mergedRegionContainer, r1, r2, c1,
c2);
if (formulaBarWidget.isEditingFormula()) {
// do nothing here
} else if (sheetWidget.isSelectionRangeOutlineVisible()) {
sheetWidget.updateSelectionOutline(selectedRegion.col1,
selectedRegion.col2, selectedRegion.row1,
selectedRegion.row2);
sheetWidget.updateSelectedCellStyles(selectedRegion.col1,
selectedRegion.col2, selectedRegion.row1,
selectedRegion.row2, true);
} else {
sheetWidget.updateSelectedCellStyles(selectedRegion.col1,
selectedRegion.col2, selectedRegion.row1,
selectedRegion.row2, false);
}
// TODO update the selection coherence, if the areas align properly
if (!sheetWidget.isCoherentSelection()) {
}
if (formulaBarWidget.isEditingFormula()) {
formulaBarWidget.setFormulaCellRange(tempSelectionStartCol,
tempSelectionStartRow, column, row);
} else if (updateToActionHandler) {
if (sheetWidget.isSelectionRangeOutlineVisible()) {
spreadsheetHandler.cellRangeSelected(selectedRegion.row1,
selectedRegion.col1, selectedRegion.row2,
selectedRegion.col2);
} else {
spreadsheetHandler.cellsAddedToRangeSelection(
selectedRegion.row1, selectedRegion.col1,
selectedRegion.row2, selectedRegion.col2);
}
startDelayedSendingTimer();
}
} else if (metaOrCtrlKey) {
// add the selected cell into the selection, set it as the selected
if (column == sheetWidget.getSelectedCellColumn()
&& row == sheetWidget.getSelectedCellRow()) {
// clicked on the selected cell again -> nothing happens
return;
}
if (formulaBarWidget.isEditingFormula()) {
formulaBarWidget.addFormulaCellRange(column, row, column, row);
} else {
// TODO update the selection coherence, if the areas align
// properly
if (sheetWidget.isCoherentSelection()) {
sheetWidget.setCoherentSelection(false);
}
if (sheetWidget.isSelectionRangeOutlineVisible()) {
sheetWidget.setSelectionRangeOutlineVisible(false);
}
sheetWidget.swapCellSelection(column, row);
selectionHandler.newSelectedCellSet();
if (hasSelectedCellChangedOnClick) {
updateSelectedCellValues(column, row);
}
if (updateToActionHandler) {
spreadsheetHandler.cellAddedToSelectionAndSelected(row,
column);
startDelayedSendingTimer();
}
}
} else {
// select cell
MergedRegion cell = mergedRegionContainer
.getMergedRegionStartingFrom(column, row);
if (formulaBarWidget.isEditingFormula()) {
tempSelectionStartCol = column;
tempSelectionStartRow = row;
formulaBarWidget.setFormulaCellRange(column, row, column, row);
} else {
if (!sheetWidget.isCoherentSelection()) {
sheetWidget.setCoherentSelection(true);
}
if (!sheetWidget.isSelectionRangeOutlineVisible()) {
sheetWidget.setSelectionRangeOutlineVisible(true);
sheetWidget.clearSelectedCellStyle();
}
sheetWidget.setSelectedCell(column, row);
if (cell != null) {
sheetWidget.updateSelectionOutline(cell.col1, cell.col2,
cell.row1, cell.row2);
sheetWidget.updateSelectedCellStyles(cell.col1, cell.col2,
cell.row1, cell.row2, true);
selectionHandler.setColBeforeMergedCell(cell.col1);
selectionHandler.setRowBeforeMergedCell(cell.row1);
} else {
sheetWidget
.updateSelectionOutline(column, column, row, row);
sheetWidget.updateSelectedCellStyles(column, column, row,
row, true);
}
if (hasSelectedCellChangedOnClick) {
updateSelectedCellValues(column, row);
}
if (updateToActionHandler) {
selectionHandler.newSelectedCellSet();
spreadsheetHandler.cellSelected(row, column, true);
startDelayedSendingTimer();
}
}
}
}
public void updateSelectedCellValues(int column, int row) {
if (!sheetWidget.isEditingCell()) {
String formulaValue = sheetWidget.getCellFormulaValue(column, row);
if (formulaValue != null && !formulaValue.isEmpty()) {
formulaBarWidget.setCellFormulaValue(formulaValue);
sheetWidget.updateInputValue("=" + formulaValue);
} else {
formulaBarWidget.setCellPlainValue(sheetWidget
.getOriginalCellValue(column, row));
}
}
cellLocked = sheetWidget.isCellLocked(column, row);
if (!customCellEditorDisplayed) {
formulaBarWidget.setFormulaFieldEnabled(!cellLocked);
} else {
sheetWidget.displayCustomCellEditor(customEditorFactory
.getCustomEditor(sheetWidget.getSelectedCellKey()));
}
formulaBarWidget.setSelectedCellAddress(createCellAddress(column, row));
}
@Override
public void onRowHeaderClick(int row, boolean shiftPressed,
boolean metaOrCrtlPressed) {
int firstColumnIndex = sheetWidget.hasFrozenColumns() ? 1 : sheetWidget
.getLeftVisibleColumnIndex();
doCommitIfEditing();
if (!shiftPressed) {
updateSelectedCellValues(firstColumnIndex, row);
}
if (shiftPressed) {
// keep selected, add the whole range from old selected as range
// select all rows from previous to new
int c1 = 1;
int c2 = cols;
final int selectedCellRow = sheetWidget.getSelectedCellRow();
int r1 = selectedCellRow > row ? row : selectedCellRow;
int r2 = selectedCellRow > row ? selectedCellRow : row;
if (sheetWidget.isSelectionRangeOutlineVisible()) {
sheetWidget.updateSelectionOutline(c1, c2, r1, r2);
sheetWidget.updateSelectedCellStyles(c1, c2, r1, r2, true);
} else {
sheetWidget.updateSelectedCellStyles(c1, c2, r1, r2, false);
}
// TODO update the selection coherence, if the areas align properly
if (!sheetWidget.isCoherentSelection()) {
}
if (sheetWidget.isSelectionRangeOutlineVisible()) {
spreadsheetHandler.cellRangeSelected(r1, c1, r2, c2);
} else {
spreadsheetHandler.cellsAddedToRangeSelection(r1, c1, r2, c2);
}
} else if (metaOrCrtlPressed) {
// change selected, add whole row to range
// TODO update the selection coherence, if the areas align properly
if (sheetWidget.isCoherentSelection()) {
sheetWidget.setCoherentSelection(false);
}
if (sheetWidget.isSelectionRangeOutlineVisible()) {
sheetWidget.setSelectionRangeOutlineVisible(false);
}
// set the row first cell as the selected
sheetWidget.swapCellSelection(firstColumnIndex, row);
selectionHandler.newSelectedCellSet();
// add the selection styles
sheetWidget.updateSelectedCellStyles(1, cols, row, row, false);
spreadsheetHandler.rowAddedToRangeSelection(row, firstColumnIndex);
} else {
if (!sheetWidget.isCoherentSelection()) {
sheetWidget.setCoherentSelection(true);
}
if (!sheetWidget.isSelectionRangeOutlineVisible()) {
sheetWidget.setSelectionRangeOutlineVisible(true);
sheetWidget.clearSelectedCellStyle();
}
sheetWidget.setSelectedCell(firstColumnIndex, row);
sheetWidget.updateSelectionOutline(1, cols, row, row);
sheetWidget.updateSelectedCellStyles(1, cols, row, row, true);
selectionHandler.newSelectedCellSet();
spreadsheetHandler.rowSelected(row, firstColumnIndex);
}
startDelayedSendingTimer();
}
@Override
public void onColumnHeaderClick(int column, boolean shiftPressed,
boolean metaOrCrtlPressed) {
doCommitIfEditing();
int firstRowIndex = sheetWidget.hasFrozenRows() ? 1 : sheetWidget
.getTopVisibleRowIndex();
if (!shiftPressed) {
updateSelectedCellValues(column, firstRowIndex);
}
if (shiftPressed) {
// keep selected, add the whole range from old selected as range
// select or columns from previous to new
int selectedCellCol = sheetWidget.getSelectedCellColumn();
int c1 = selectedCellCol > column ? column : selectedCellCol;
int c2 = selectedCellCol > column ? selectedCellCol : column;
int r1 = 1;
int r2 = rows;
if (sheetWidget.isSelectionRangeOutlineVisible()) {
sheetWidget.updateSelectionOutline(c1, c2, r1, r2);
sheetWidget.updateSelectedCellStyles(c1, c2, r1, r2, true);
} else {
sheetWidget.updateSelectedCellStyles(c1, c2, r1, r2, false);
}
// TODO update the selection coherence, if the areas align properly
// if (!sheetWidget.isCoherentSelection()) {
// }
if (sheetWidget.isSelectionRangeOutlineVisible()) {
spreadsheetHandler.cellRangeSelected(r1, c1, r2, c2);
} else {
spreadsheetHandler.cellsAddedToRangeSelection(r1, c1, r2, c2);
}
} else if (metaOrCrtlPressed) {
// change selected, add whole row to range
// TODO update the selection coherence, if the areas align properly
if (sheetWidget.isCoherentSelection()) {
sheetWidget.setCoherentSelection(false);
}
if (sheetWidget.isSelectionRangeOutlineVisible()) {
sheetWidget.setSelectionRangeOutlineVisible(false);
}
// set the row first cell as the selected
sheetWidget.swapCellSelection(column, firstRowIndex);
selectionHandler.newSelectedCellSet();
// add the selection styles
sheetWidget
.updateSelectedCellStyles(column, column, 1, rows, false);
spreadsheetHandler.columnAddedToSelection(firstRowIndex, column);
} else {
if (!sheetWidget.isCoherentSelection()) {
sheetWidget.setCoherentSelection(true);
}
if (!sheetWidget.isSelectionRangeOutlineVisible()) {
sheetWidget.setSelectionRangeOutlineVisible(true);
sheetWidget.clearSelectedCellStyle();
}
sheetWidget.setSelectedCell(column, firstRowIndex);
sheetWidget.updateSelectionOutline(column, column, 1, rows);
sheetWidget.updateSelectedCellStyles(column, column, 1, rows, true);
selectionHandler.newSelectedCellSet();
spreadsheetHandler.columnSelected(column, firstRowIndex);
}
startDelayedSendingTimer();
}
@Override
public void onColumnHeaderResizeDoubleClick(int columnIndex) {
spreadsheetHandler.onColumnAutofit(columnIndex);
}
void doCommitIfEditing() {
if (formulaBarWidget.isEditingFormula()) {
// do nothing
} else if (inlineEditing || formulaBarEditing) {
cancelDeferredCommit = true;
final String editedValue = formulaBarWidget.getFormulaFieldValue();
spreadsheetHandler.cellValueEdited(
sheetWidget.getSelectedCellRow(),
sheetWidget.getSelectedCellColumn(), editedValue);
cellEditingDone(editedValue, true);
} else if (customCellEditorDisplayed) {
customCellEditorDisplayed = false;
sheetWidget.removeCustomCellEditor();
formulaBarWidget.setFormulaFieldEnabled(true);
}
}
@Override
public void onSelectingCellsWithDrag(int col, int row) {
if (col == 0) {
col = 1;
} else if (col < 0) {
col = sheetWidget.getRightVisibleColumnIndex() + 1;
}
if (col > cols) {
col = cols;
}
if (row == 0) {
row = 1;
} else if (row < 0) {
row = sheetWidget.getBottomVisibleRowIndex() + 1;
}
if (row > rows) {
row = rows;
}
int selectedCellColumn = sheetWidget.getSelectedCellColumn();
int selectedCellRow = sheetWidget.getSelectedCellRow();
int col1;
int col2;
int row1;
int row2;
if (col <= selectedCellColumn) {
col1 = col;
col2 = selectedCellColumn;
} else {
col1 = selectedCellColumn;
col2 = col;
}
if (row <= selectedCellRow) {
row1 = row;
row2 = selectedCellRow;
} else {
row1 = selectedCellRow;
row2 = row;
}
if (formulaBarWidget.isEditingFormula()) {
formulaBarWidget.setFormulaCellRange(tempSelectionStartCol,
tempSelectionStartRow, col, row);
} else {
MergedRegion selectedRegion = MergedRegionUtil
.findIncreasingSelection(mergedRegionContainer, row1, row2,
col1, col2);
sheetWidget.updateSelectionOutline(selectedRegion.col1,
selectedRegion.col2, selectedRegion.row1,
selectedRegion.row2);
sheetWidget.updateSelectedCellStyles(selectedRegion.col1,
selectedRegion.col2, selectedRegion.row1,
selectedRegion.row2, true);
formulaBarWidget.setSelectedCellAddress(createRangeSelectionString(
selectedRegion.col1, selectedRegion.col2,
selectedRegion.row1, selectedRegion.row2));
}
}
@Override
public void onFinishedSelectingCellsWithDrag(int col1, int col2, int row1,
int row2) {
if (col1 == 0 || col2 == 0 || row1 == 0 || row2 == 0 || col1 == col2
&& row1 == row2 && col1 == sheetWidget.getSelectedCellColumn()
&& row1 == sheetWidget.getSelectedCellRow()) {
return;
}
int origCol2 = col2;
int origRow2 = row2;
// swap coordinates so that 1 is smaller than 2
int temp;
if (col1 > col2) {
temp = col1;
col1 = col2;
col2 = temp;
}
if (row1 > row2) {
temp = row1;
row1 = row2;
row2 = temp;
}
if (formulaBarWidget.isEditingFormula()) {
formulaBarWidget.setFormulaCellRange(tempSelectionStartCol,
tempSelectionStartRow, origCol2, origRow2);
formulaBarWidget.clearFormulaSelection();
} else {
MergedRegion selectedRegion = MergedRegionUtil
.findIncreasingSelection(mergedRegionContainer, row1, row2,
col1, col2);
spreadsheetHandler.cellRangePainted(
sheetWidget.getSelectedCellRow(),
sheetWidget.getSelectedCellColumn(), selectedRegion.row1,
selectedRegion.col1, selectedRegion.row2,
selectedRegion.col2);
formulaBarWidget.setSelectedCellAddress(createCellAddress(
sheetWidget.getSelectedCellColumn(),
sheetWidget.getSelectedCellRow()));
selectionHandler.newSelectedCellSet();
startDelayedSendingTimer();
}
}
@Override
public void onCellDoubleClick(int column, int row, String value) {
if (sheetWidget.getSelectedCellRow() != row
&& sheetWidget.getSelectedCellColumn() != column) {
onCellClick(column, row, value, false, false, true);
} else {
Cell cell = sheetWidget.getCell(column, row);
value = cell.getValue();
cachedCellValue = value;
formulaBarWidget.cacheFormulaFieldValue();
value = formulaBarWidget.getFormulaFieldValue();
}
formulaBarEditing = false;
checkEditableAndNotify();
if (!cellLocked) {
if (!inlineEditing && !customCellEditorDisplayed) {
inlineEditing = true;
sheetWidget.startEditingCell(true, true, value);
formulaBarWidget.startInlineEdit(true);
}
}
}
@Override
public void onCellInputBlur(final String inputValue) {
// need to do this deferred in case focus moved to the formula field
if (inlineEditing && !formulaBarWidget.isEditingFormula()) {
doDeferredCellValueCommit(inputValue, true);
}
}
/* This is only for when focus is changed from formula field to inline input */
@Override
public void onCellInputFocus() {
if (!inlineEditing && !formulaBarWidget.isEditingFormula()) {
inlineEditing = true;
cancelDeferredCommit = true;
if (formulaBarEditing) { // just swap, everything should work
formulaBarEditing = false;
} else { // need to make sure the input value is correct
sheetWidget.startEditingCell(true, false,
formulaBarWidget.getFormulaFieldValue());
formulaBarWidget.startInlineEdit(true);
}
}
}
@Override
public void onCellInputCancel() {
cellEditingDone(cachedCellValue, true);
formulaBarWidget.revertCellValue();
sheetWidget.focusSheet();
}
@Override
public void onCellInputEnter(String value, boolean shift) {
spreadsheetHandler.cellValueEdited(sheetWidget.getSelectedCellRow(),
sheetWidget.getSelectedCellColumn(), value);
cellEditingDone(value, true);
sheetWidget.focusSheet();
if (shift) {
selectionHandler.moveSelectionUp(false);
} else {
selectionHandler.moveSelectionDown(false);
}
}
@Override
public void onCellInputTab(String value, boolean shift) {
spreadsheetHandler.cellValueEdited(sheetWidget.getSelectedCellRow(),
sheetWidget.getSelectedCellColumn(), value);
cellEditingDone(value, true);
sheetWidget.focusSheet();
if (shift) {
selectionHandler.moveSelectionLeft(false);
} else {
selectionHandler.moveSelectionRight(false);
}
}
@Override
public void onCellInputValueChange(String value) {
formulaBarWidget.setFormulaFieldValue(value);
}
@Override
public void onSheetKeyPress(NativeEvent event, String enteredCharacter) {
// Here we need to also check for char code 13 (which is code for enter)
// since for some reason the enter key is reported having both
// KeyCodes.ENTER and the char code 13, whereas other such non-character
// keys here have no char codes. Enter key must be detected here to
// start editing a cell.
if ((event.getCharCode() == 0 && event.getKeyCode() != KeyCodes.KEY_SPACE)
|| event.getCharCode() == 13) {
switch (event.getKeyCode()) {
case KeyCodes.KEY_BACKSPACE:
case KeyCodes.KEY_DELETE:
checkEditableAndNotify();
if (!cellLocked) {
spreadsheetHandler.deleteSelectedCells();
formulaBarWidget.setCellPlainValue("");
}
break;
case KeyCodes.KEY_DOWN:
if (event.getShiftKey()) {
selectionHandler.increaseVerticalSelection(true);
} else {
selectionHandler.moveSelectionDown(true);
}
break;
case KeyCodes.KEY_LEFT:
if (event.getShiftKey()) {
selectionHandler.increaseHorizontalSelection(false);
} else {
selectionHandler.moveSelectionLeft(true);
}
break;
case KeyCodes.KEY_TAB:
if (event.getShiftKey()) {
selectionHandler.moveSelectionLeft(isSelectedCellHidden());
} else {
selectionHandler.moveSelectionRight(isSelectedCellHidden());
}
break;
case KeyCodes.KEY_RIGHT:
if (event.getShiftKey()) {
selectionHandler.increaseHorizontalSelection(true);
} else {
selectionHandler.moveSelectionRight(true);
}
break;
case KeyCodes.KEY_UP:
if (event.getShiftKey()) {
selectionHandler.increaseVerticalSelection(false);
} else {
selectionHandler.moveSelectionUp(true);
}
break;
case KeyCodes.KEY_ALT:
case KeyCodes.KEY_CTRL:
case KeyCodes.KEY_END:
case KeyCodes.KEY_ESCAPE:
case KeyCodes.KEY_HOME:
case KeyCodes.KEY_PAGEDOWN:
case KeyCodes.KEY_PAGEUP:
case KeyCodes.KEY_SHIFT:
break;
case KeyCodes.KEY_F2:
case KeyCodes.KEY_ENTER:
if (KeyCodes.KEY_ENTER == event.getKeyCode()) {
if (isSelectedCellHidden()) {
selectionHandler.moveSelectionDown(true);
break;
} else {
if (sheetWidget.getSelectionLeftCol() != sheetWidget
.getSelectionRightCol()
|| sheetWidget.getSelectionTopRow() != sheetWidget
.getSelectionBottomRow()) {
if (event.getShiftKey()) {
selectionHandler.moveSelectionUp(false);
} else {
selectionHandler.moveSelectionDown(false);
}
break;
}
}
}
checkEditableAndNotify();
if (!sheetWidget.isSelectedCellCustomized() && !inlineEditing
&& !cellLocked && !customCellEditorDisplayed) {
cachedCellValue = sheetWidget.getSelectedCellLatestValue();
formulaBarWidget.cacheFormulaFieldValue();
formulaBarEditing = false;
inlineEditing = true;
sheetWidget.startEditingCell(true, true,
formulaBarWidget.getFormulaFieldValue());
formulaBarWidget.startInlineEdit(true);
}
break;
}
} else {
if (!isSelectedCellHidden()) {
checkEditableAndNotify();
if (!sheetWidget.isSelectedCellCustomized() && !inlineEditing
&& !cellLocked && !customCellEditorDisplayed) {
// cache value and start editing cell as empty
inlineEditing = true;
cachedCellValue = sheetWidget.getSelectedCellLatestValue();
formulaBarWidget.startInlineEdit(true);
if (cachedCellValue.endsWith("%")
|| sheetWidget.isSelectedCellPergentage()) {
if (isNumericChar(enteredCharacter)) {
enteredCharacter = enteredCharacter + "%";
}
sheetWidget.startEditingCell(true, true,
enteredCharacter);
} else {
sheetWidget.startEditingCell(true, true,
enteredCharacter);
formulaBarWidget.cacheFormulaFieldValue();
}
formulaBarWidget.setCellPlainValue(enteredCharacter);
}
}
}
}
private static boolean isNumericChar(String input) {
Set<String> allowedChars = new HashSet<String>();
allowedChars.add("0");
allowedChars.add("1");
allowedChars.add("2");
allowedChars.add("3");
allowedChars.add("4");
allowedChars.add("5");
allowedChars.add("6");
allowedChars.add("7");
allowedChars.add("8");
allowedChars.add("9");
allowedChars.add("-");
allowedChars.add("+");
return allowedChars.contains(input);
}
private boolean isSelectedCellHidden() {
return hiddenColumnIndexes
.contains(sheetWidget.getSelectedCellColumn())
|| hiddenRowIndexes.contains(sheetWidget.getSelectedCellRow());
}
/**
* Checks if selected cell is locked, and sends an RPC to server if it is.
*/
private void checkEditableAndNotify() {
if (cellLocked) {
if (!okToSendCellProtectRpc) {
// don't send just yet
return;
}
Timer timer = new Timer() {
@Override
public void run() {
okToSendCellProtectRpc = true;
}
};
timer.schedule(1000);
okToSendCellProtectRpc = false;
ServerConnector connector = Util.findConnectorFor(this);
SpreadsheetServerRpc rpc = RpcProxy.create(
SpreadsheetServerRpc.class, connector);
rpc.protectedCellWriteAttempted();
}
}
@Override
public void onAddressEntered(String value) {
spreadsheetHandler.sheetAddressChanged(value);
}
@Override
public void onAddressFieldEsc() {
sheetWidget.focusSheet();
}
@Override
public void onSheetTabSelected(int sheetIndex) {
if (formulaBarWidget.isEditingFormula()) {
// TODO commit or ignore value? this ignores. Excel remembers that
// editor was open. If editing from formula bar, value is stored..
formulaBarWidget.stopInlineEdit();
sheetWidget.stopEditingCell(false);
}
int scrollLeft = sheetWidget.getSheetScrollLeft();
int scrollTop = sheetWidget.getSheetScrollTop();
spreadsheetHandler.sheetSelected(sheetIndex, scrollLeft, scrollTop);
}
@Override
public void onFirstTabIndexChange(int firstVisibleTab) {
// Disabled because not working in Apache POI
// actionHandler.firstVisibleTabChanged(firstVisibleTab);
}
@Override
public void onSheetRename(int sheetIndex, String newName) {
spreadsheetHandler.sheetRenamed(sheetIndex, newName);
}
@Override
public void onNewSheetCreated() {
int scrollLeft = sheetWidget.getSheetScrollLeft();
int scrollTop = sheetWidget.getSheetScrollTop();
spreadsheetHandler.sheetCreated(scrollLeft, scrollTop);
}
@Override
public void onSheetRenameCancel() {
sheetWidget.focusSheet();
}
@Override
public void onSheetTabSheetFocus() {
sheetWidget.focusSheet(false);
}
@Override
public int[] getRowHeightsPX() {
return sheetWidget.getRowHeights();
}
@Override
public MergedRegion getMergedRegion(int column, int row) {
return mergedRegionContainer.getMergedRegion(column, row);
}
@Override
public MergedRegion getMergedRegionStartingFrom(int column, int row) {
return mergedRegionContainer.getMergedRegionStartingFrom(column, row);
}
@Override
public void onSelectionIncreasePainted(int c1, int c2, int r1, int r2) {
MergedRegion evenedRegion = MergedRegionUtil.findIncreasingSelection(
mergedRegionContainer, r1, r2, c1, c2);
// discard painted area if merged cells don't align
if (evenedRegion.col1 == c1 && evenedRegion.col2 == c2
&& evenedRegion.row1 == r1 && evenedRegion.row2 == r2) {
spreadsheetHandler.selectionIncreasePainted(r1, c1, r2, c2);
startDelayedSendingTimer();
}
}
@Override
public void onSelectionDecreasePainted(int colEdgeIndex, int rowEdgeIndex) {
// the selection widget has made sure the decreasing area is not in
// middle of merged cells.
spreadsheetHandler.selectionDecreasePainted(rowEdgeIndex, colEdgeIndex);
startDelayedSendingTimer();
}
@Override
public void onFormulaFieldFocus(String value) {
formulaBarEditing = true;
cancelDeferredCommit = true;
if (inlineEditing) { // just swap and everything should work
inlineEditing = false;
} else {
if (sheetWidget.isSelectedCellCustomized()) {
cachedCellValue = "";
} else {
cachedCellValue = sheetWidget.getSelectedCellLatestValue();
}
}
}
@Override
public void onFormulaFieldBlur(final String value) {
// need to do this as deferred because in case the focus was passed to
// inline input element
if (formulaBarEditing) {
doDeferredCellValueCommit(value, false);
}
}
@Override
public void onFormulaEnter(String value) {
spreadsheetHandler.cellValueEdited(sheetWidget.getSelectedCellRow(),
sheetWidget.getSelectedCellColumn(), value);
cellEditingDone(value, true);
sheetWidget.focusSheet();
selectionHandler.moveSelectionDown(false);
}
@Override
public void onFormulaTab(String value, boolean focusSheet) {
spreadsheetHandler.cellValueEdited(sheetWidget.getSelectedCellRow(),
sheetWidget.getSelectedCellColumn(), value);
cellEditingDone(value, focusSheet);
if (focusSheet) {
sheetWidget.focusSheet();
selectionHandler.moveSelectionRight(false);
}
}
@Override
public void onFormulaEsc() {
cellEditingDone(cachedCellValue, true);
sheetWidget.focusSheet();
}
@Override
public void onFormulaValueChange(String value) {
if (!sheetWidget.isSelectedCellCustomized()) {
sheetWidget.updateInputValue(value);
}
}
@Override
public void onRowsResized(Map<Integer, Float> newSizes) {
for (Entry<Integer, Float> entry : newSizes.entrySet()) {
int index = entry.getKey();
float size = entry.getValue();
if (size == 0.0F) {
if (hiddenRowIndexes == null) {
hiddenRowIndexes = new ArrayList<Integer>();
hiddenRowIndexes.add(index);
} else if (!hiddenRowIndexes.contains(index)) {
hiddenRowIndexes.add(index);
}
}
rowH[index - 1] = size;
}
sheetWidget.relayoutSheet(false);
if (mergedRegions != null) {
for (MergedRegion region : mergedRegions) {
sheetWidget.updateMergedRegionSize(region);
}
}
cancelNextSheetRelayout = true;
int[] x = sheetWidget.getSheetDisplayRange();
spreadsheetHandler.rowsResized(newSizes, x[0], x[1], x[2], x[3]);
}
@Override
public void onColumnsResized(Map<Integer, Integer> newSizes) {
for (Entry<Integer, Integer> entry : newSizes.entrySet()) {
int index = entry.getKey();
int size = entry.getValue();
if (size == 0F) {
if (hiddenColumnIndexes == null) {
hiddenColumnIndexes = new ArrayList<Integer>();
hiddenColumnIndexes.add(index);
} else if (!hiddenColumnIndexes.contains(index)) {
hiddenColumnIndexes.add(index);
}
}
colW[index - 1] = size;
}
sheetWidget.relayoutSheet(false);
if (mergedRegions != null) {
for (MergedRegion region : mergedRegions) {
sheetWidget.updateMergedRegionSize(region);
}
}
cancelNextSheetRelayout = true;
int[] x = sheetWidget.getSheetDisplayRange();
spreadsheetHandler.columnResized(newSizes, x[0], x[1], x[2], x[3]);
}
@Override
public void onRedoPress() {
spreadsheetHandler.onRedo();
}
@Override
public void onUndoPress() {
spreadsheetHandler.onUndo();
}
/**
* update the sheet display after editing has finished
*
* @param focusSheet
*
* @param focusSheet
*/
private void cellEditingDone(String value, boolean focusSheet) {
inlineEditing = false;
formulaBarWidget.stopInlineEdit();
formulaBarEditing = false;
if (!sheetWidget.isSelectedCellCustomized()) {
if (value == null) {
value = "";
}
selectedCellIsFormulaType = value.startsWith("=")
|| value.startsWith("+");
sheetWidget.stopEditingCell(focusSheet);
if (!selectedCellIsFormulaType) {
sheetWidget.updateSelectedCellValue(value);
}
}
}
/**
*
* @param value
* @param focusSheet
*/
private void doDeferredCellValueCommit(final String value,
final boolean focusSheet) {
cancelDeferredCommit = false;
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
if (!cancelDeferredCommit) {
spreadsheetHandler.cellValueEdited(
sheetWidget.getSelectedCellRow(),
sheetWidget.getSelectedCellColumn(), value);
cellEditingDone(value, focusSheet);
}
}
});
}
protected String createRangeSelectionString(int col1, int col2, int row1,
int row2) {
final StringBuffer sb = new StringBuffer();
sb.append(Math.abs(row2 - row1) + 1);
sb.append("R");
sb.append(" x ");
sb.append(Math.abs(col2 - col1) + 1);
sb.append("C");
return sb.toString();
}
@Override
public String createCellAddress(int column, int row) {
final String c = column > 0 ? getColHeader(column) : "";
final String r = row > 0 ? Integer.toString(row) : "";
return c + r;
}
public void setRowBufferSize(int rowBufferSize) {
this.rowBufferSize = rowBufferSize;
}
public void setColumnBufferSize(int columnBufferSize) {
this.columnBufferSize = columnBufferSize;
}
public void setRows(int rows) {
this.rows = rows;
}
public void setCols(int cols) {
this.cols = cols;
}
public void setColGroupingData(List<GroupingData> data) {
sheetWidget.setColGroupingData(data);
}
public void setRowGroupingData(List<GroupingData> data) {
sheetWidget.setRowGroupingData(data);
}
public void setRowH(float[] rowH) {
this.rowH = rowH;
}
public void setColW(int[] colW) {
this.colW = colW;
}
public void setDefRowH(float defRowH) {
this.defRowH = defRowH;
}
public void setDefColW(int defColW) {
this.defColW = defColW;
}
public void setVerticalScrollPositions(int[] verticalScrollPositions) {
this.verticalScrollPositions = verticalScrollPositions;
}
public void setHorizontalScrollPositions(int[] horizontalScrollPositions) {
this.horizontalScrollPositions = horizontalScrollPositions;
}
public void setVerticalSplitPosition(int verticalSplitPosition) {
sheetWidget.setVerticalSplitPosition(verticalSplitPosition);
}
public void setHorizontalSplitPosition(int horizontalSplitPosition) {
sheetWidget.setHorizontalSplitPosition(horizontalSplitPosition);
}
public void setCellStyleToCSSStyle(
HashMap<Integer, String> cellStyleToCSSStyle) {
if (this.cellStyleToCSSStyle == null) {
this.cellStyleToCSSStyle = cellStyleToCSSStyle;
} else {
this.cellStyleToCSSStyle.clear();
if (cellStyleToCSSStyle != null) {
this.cellStyleToCSSStyle.putAll(cellStyleToCSSStyle);
}
}
}
public void setRowIndexToStyleIndex(
HashMap<Integer, Integer> rowIndexToStyleIndex) {
if (this.rowIndexToStyleIndex == null) {
this.rowIndexToStyleIndex = rowIndexToStyleIndex;
} else {
this.rowIndexToStyleIndex.clear();
if (rowIndexToStyleIndex != null) {
this.rowIndexToStyleIndex.putAll(rowIndexToStyleIndex);
}
}
}
public void setColumnIndexToStyleIndex(
HashMap<Integer, Integer> columnIndexToStyleIndex) {
if (this.columnIndexToStyleIndex == null) {
this.columnIndexToStyleIndex = columnIndexToStyleIndex;
} else {
this.columnIndexToStyleIndex.clear();
if (columnIndexToStyleIndex != null) {
this.columnIndexToStyleIndex.putAll(columnIndexToStyleIndex);
}
}
}
public void setLockedColumnIndexes(Set<Integer> lockedColumnIndexes) {
if (this.lockedColumnIndexes == null) {
this.lockedColumnIndexes = lockedColumnIndexes;
} else {
this.lockedColumnIndexes.clear();
if (lockedColumnIndexes != null) {
this.lockedColumnIndexes.addAll(lockedColumnIndexes);
}
}
}
public void setLockedRowIndexes(Set<Integer> lockedRowIndexes) {
if (this.lockedRowIndexes == null) {
this.lockedRowIndexes = lockedRowIndexes;
} else {
this.lockedRowIndexes.clear();
if (lockedRowIndexes != null) {
this.lockedRowIndexes.addAll(lockedRowIndexes);
}
}
}
public void setShiftedCellBorderStyles(
ArrayList<String> shiftedCellBorderStyles) {
sheetWidget.removeShiftedCellBorderStyles();
if (shiftedCellBorderStyles != null) {
sheetWidget.addShiftedCellBorderStyles(shiftedCellBorderStyles);
}
}
public void setHyperlinksTooltips(HashMap<String, String> cellLinksMap) {
sheetWidget.setCellLinks(cellLinksMap);
}
public void setSheetProtected(boolean sheetProtected) {
if (this.sheetProtected != sheetProtected) {
this.sheetProtected = sheetProtected;
if (sheetProtected) {
addStyleName("protected");
} else {
removeStyleName("protected");
}
if (loaded) {
if (sheetProtected) {
if (customCellEditorDisplayed) {
customCellEditorDisplayed = false;
sheetWidget.removeCustomCellEditor();
}
} else { // might need to load the custom editor
cellLocked = false;
selectionHandler.newSelectedCellSet();
if (customCellEditorDisplayed) {
// need to update the editor value on client side
spreadsheetHandler.cellSelected(
sheetWidget.getSelectedCellRow(),
sheetWidget.getSelectedCellColumn(), false);
startDelayedSendingTimer();
}
}
}
}
}
@Override
public boolean isSheetProtected() {
return sheetProtected;
}
@Override
public boolean isColProtected(int col) {
return lockedColumnIndexes.contains(col);
}
@Override
public boolean isRowProtected(int row) {
return lockedRowIndexes.contains(row);
}
public void setWorkbookProtected(boolean workbookProtected) {
sheetTabSheet.setReadOnly(workbookProtected);
}
public void setHiddenColumnIndexes(ArrayList<Integer> hiddenColumnIndexes) {
this.hiddenColumnIndexes = new ArrayList<Integer>(hiddenColumnIndexes);
}
public void setHiddenRowIndexes(ArrayList<Integer> hiddenRowIndexes) {
this.hiddenRowIndexes = new ArrayList<Integer>(hiddenRowIndexes);
}
public void setCellComments(HashMap<String, String> cellComments,
HashMap<String, String> cellCommentAuthors) {
sheetWidget.setCellComments(cellComments, cellCommentAuthors);
}
public void setInvalidFormulaCells(Set<String> invalidFormulaCells) {
sheetWidget.setInvalidFormulaCells(invalidFormulaCells);
}
public void setInvalidFormulaErrorMessage(String invalidFormulaMessage) {
sheetWidget.setInvalidFormulaMessage(invalidFormulaMessage);
}
@Override
public Map<Integer, String> getCellStyleToCSSStyle() {
return cellStyleToCSSStyle;
}
@Override
public Map<Integer, Integer> getRowIndexToStyleIndex() {
return rowIndexToStyleIndex;
}
@Override
public Map<Integer, Integer> getColumnIndexToStyleIndex() {
return columnIndexToStyleIndex;
}
@Override
public float getRowHeight(int row) {
// doesn't take hidden rows into account! (but height is 0 for those)
if (rowH.length >= row) {
return rowH[row - 1];
} else {
return defRowH;
}
}
@Override
public int getColWidth(int col) {
// doesn't take hidden columns into account! (but width is 0 for those)
if (col > 0 && colW.length >= col) {
return colW[col - 1];
} else {
return defColW;
}
}
@Override
public int getColWidthActual(int col) {
if (hiddenColumnIndexes != null && hiddenColumnIndexes.contains(col)) {
return 0;
} else {
return getColWidth(col);
}
}
/** Get column header for column indexed 1.. */
@Override
public final String getColHeader(int col) {
String h = "";
while (col > 0) {
h = (char) ('A' + (col - 1) % 26) + h;
col = (col - 1) / 26;
}
return h;
}
/** Get row header for rows indexed 1.. */
@Override
public String getRowHeader(int row) {
return "" + row;
}
@Override
public int getDefinedRows() {
return rowH.length;
}
@Override
public int[] getColWidths() {
return colW;
}
@Override
public float getDefaultRowHeight() {
return defRowH;
}
@Override
public int getRowBufferSize() {
return rowBufferSize;
}
@Override
public int getColumnBufferSize() {
return columnBufferSize;
}
@Override
public int getMaxColumns() {
return cols;
}
@Override
public int getMaxRows() {
return rows;
}
@Override
public boolean isColumnHidden(int columnIndex) {
return (hiddenColumnIndexes == null ? false : hiddenColumnIndexes
.contains(columnIndex));
}
@Override
public boolean isRowHidden(int rowIndex) {
return (hiddenRowIndexes == null ? false : hiddenRowIndexes
.contains(rowIndex));
}
@Override
public boolean canResizeColumn() {
return (!sheetProtected || !lockFormatColumns) && !touchMode;
}
@Override
public boolean canResizeRow() {
return (!sheetProtected || !lockFormatRows) && !touchMode;
}
public void setDisplayGridlines(boolean displayGridlines) {
sheetWidget.setDisplayGridlines(displayGridlines);
}
public void setDisplayRowColHeadings(boolean displayRowColHeadings) {
sheetWidget.setDisplayRowColHeadings(displayRowColHeadings);
}
public void refreshOverlayPositions() {
sheetWidget.refreshAlwaysVisibleCellCommentOverlays();
sheetWidget.refreshCurrentCellCommentOverlay();
sheetWidget.refreshPopupButtonOverlays();
}
public void updateBottomRightCellValues(ArrayList<CellData> cellData) {
sheetWidget.updateBottomRightCellValues(cellData);
}
public void updateTopLeftCellValues(ArrayList<CellData> cellData) {
sheetWidget.updateTopLeftCellValues(cellData);
}
public void updateTopRightCellValues(ArrayList<CellData> cellData) {
sheetWidget.updateTopRightCellValues(cellData);
}
public void updateBottomLeftCellValues(ArrayList<CellData> cellData) {
sheetWidget.updateBottomLeftCellValues(cellData);
}
/**
* This can contain values for any of the panes or values that are just in
* the client side cache, but the cell is not actually visible.
*
* @param updatedCellData
*/
public void cellValuesUpdated(ArrayList<CellData> updatedCellData) {
sheetWidget.cellValuesUpdated(updatedCellData);
}
@Override
public void setCellStyleWidthRatios(
HashMap<Integer, Float> cellStyleWidthRatioMap) {
spreadsheetHandler.setCellStyleWidthRatios(cellStyleWidthRatioMap);
}
@Override
public void onSheetPaste(String text) {
spreadsheetHandler.onPaste(text);
}
@Override
public void clearSelectedCellsOnCut() {
spreadsheetHandler.clearSelectedCellsOnCut();
}
@Override
public Map<Integer, String> getConditionalFormattingStyles() {
return conditionalFormattingStyles;
}
public void setConditionalFormattingStyles(HashMap<Integer, String> map) {
conditionalFormattingStyles.clear();
if (map != null) {
conditionalFormattingStyles.putAll(map);
}
}
public void selectCell(int col, int row, String value, boolean formula,
boolean locked, boolean initialSelection) {
selectionHandler.selectCell(col, row, value, formula, locked,
initialSelection);
}
public void selectCellRange(int selectedCellColumn, int selectedCellRow,
int firstColumn, int lastColumn, int firstRow, int lastRow,
String value, boolean formula, boolean locked, boolean scroll) {
selectionHandler.selectCellRange(selectedCellColumn, selectedCellRow,
firstColumn, lastColumn, firstRow, lastRow, value, formula,
locked, scroll);
}
public void refreshCellStyles() {
getSheetWidget().refreshCellStyles();
}
@Override
public boolean isTouchMode() {
return touchMode;
}
public void setTouchMode(boolean touchMode) {
this.touchMode = touchMode;
}
@Override
public FormulaBarWidget getFormulaBarWidget() {
return formulaBarWidget;
}
public void editCellComment(int col, int row) {
sheetWidget.editCellComment(col, row);
}
@Override
public void updateCellComment(String text, int col, int row) {
spreadsheetHandler.updateCellComment(text, col, row);
}
@Override
public void selectAll() {
sheetWidget.setSelectedCell(1, 1);
onSelectingCellsWithDrag(cols, rows);
onFinishedSelectingCellsWithDrag(1, cols, 1, rows);
updateSelectedCellValues(1, 1);
}
@Override
public void focus() {
focusSheet();
}
public void setCommsTrigger(CommsTrigger commsTrigger) {
this.commsTrigger = commsTrigger;
}
private Timer delayedSending = new Timer() {
@Override
public void run() {
commsTrigger.sendUpdates();
}
};
void startDelayedSendingTimer() {
delayedSending.schedule(DELAYED_SERVER_REQUEST_DELAY);
}
static int getTouchOrMouseClientX(Event event) {
int scrollLeft = Document.get().getScrollLeft();
if (WidgetUtil.isTouchEvent(event)) {
return event.getChangedTouches().get(0).getClientX() + scrollLeft;
} else {
return event.getClientX() + scrollLeft;
}
}
static int getTouchOrMouseClientY(Event event) {
int scrollTop = Document.get().getScrollTop();
if (WidgetUtil.isTouchEvent(event)) {
return event.getChangedTouches().get(0).getClientY() + scrollTop;
} else {
return event.getClientY() + scrollTop;
}
}
static int getTouchOrMouseClientY(NativeEvent currentGwtEvent) {
return getTouchOrMouseClientY(Event.as(currentGwtEvent));
}
static int getTouchOrMouseClientX(NativeEvent event) {
return getTouchOrMouseClientX(Event.as(event));
}
@Override
public void setSheetFocused(boolean focused) {
sheetWidget.setFocused(focused);
}
public void setId(String connectorId) {
sheetWidget.postInit(connectorId);
}
@Override
public String[] getSheetNames() {
return sheetNames;
}
@Override
public String getActiveSheetName() {
return sheetNames[activeSheetIndex - 1];
}
@Override
public void setGroupingCollapsed(boolean isCols, int colIndex,
boolean collapsed) {
spreadsheetHandler.setGroupingCollapsed(isCols, colIndex, collapsed);
}
@Override
public void levelHeaderClicked(boolean cols, int level) {
spreadsheetHandler.levelHeaderClicked(cols, level);
}
public void setColGroupingMax(int max) {
sheetWidget.setColGroupingMax(max);
}
public void setRowGroupingMax(int max) {
sheetWidget.setRowGroupingMax(max);
}
public void setColGroupingInversed(boolean inversed) {
sheetWidget.setColGroupingInversed(inversed);
}
public void setRowGroupingInversed(boolean inversed) {
sheetWidget.setRowGroupingInversed(inversed);
}
}