package org.geogebra.web.web.gui.view.spreadsheet; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import org.geogebra.common.awt.GColor; import org.geogebra.common.awt.GPoint; import org.geogebra.common.awt.GRectangle; import org.geogebra.common.euclidian.event.PointerEventType; import org.geogebra.common.gui.view.spreadsheet.CellFormat; import org.geogebra.common.gui.view.spreadsheet.CellFormatInterface; import org.geogebra.common.gui.view.spreadsheet.CellRange; import org.geogebra.common.gui.view.spreadsheet.CellRangeProcessor; import org.geogebra.common.gui.view.spreadsheet.CopyPasteCut; import org.geogebra.common.gui.view.spreadsheet.MyTable; import org.geogebra.common.gui.view.spreadsheet.MyTableInterface; import org.geogebra.common.gui.view.spreadsheet.RelativeCopy; import org.geogebra.common.gui.view.spreadsheet.SpreadsheetController; import org.geogebra.common.gui.view.spreadsheet.SpreadsheetModeProcessor; import org.geogebra.common.gui.view.spreadsheet.SpreadsheetViewInterface; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoElementSpreadsheet; import org.geogebra.common.main.App; import org.geogebra.common.main.Feature; import org.geogebra.common.main.GeoGebraColorConstants; import org.geogebra.common.main.OptionType; import org.geogebra.common.main.SpreadsheetTableModel; import org.geogebra.common.main.settings.SpreadsheetSettings; import org.geogebra.common.plugin.EventType; import org.geogebra.common.plugin.GeoClass; import org.geogebra.common.util.debug.Log; import org.geogebra.ggbjdk.java.awt.DefaultBasicStroke; import org.geogebra.ggbjdk.java.awt.geom.Rectangle; import org.geogebra.web.html5.Browser; import org.geogebra.web.html5.awt.GBasicStrokeW; import org.geogebra.web.html5.gui.inputfield.AutoCompleteTextFieldW; import org.geogebra.web.html5.gui.tooltip.ToolTipManagerW; import org.geogebra.web.html5.gui.util.CancelEventTimer; import org.geogebra.web.html5.gui.util.ClickStartHandler; import org.geogebra.web.html5.main.AppW; import org.geogebra.web.html5.main.GlobalKeyDispatcherW; import org.geogebra.web.html5.util.SpreadsheetTableModelW; import com.google.gwt.canvas.client.Canvas; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.Style; import com.google.gwt.dom.client.Style.Display; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.DoubleClickEvent; import com.google.gwt.event.dom.client.KeyDownEvent; import com.google.gwt.event.dom.client.KeyPressEvent; import com.google.gwt.event.dom.client.MouseDownEvent; import com.google.gwt.event.dom.client.MouseMoveEvent; import com.google.gwt.event.dom.client.MouseUpEvent; import com.google.gwt.event.dom.client.TouchEndEvent; import com.google.gwt.event.dom.client.TouchMoveEvent; import com.google.gwt.event.dom.client.TouchStartEvent; import com.google.gwt.user.client.ui.AbsolutePanel; import com.google.gwt.user.client.ui.AbstractNativeScrollbar; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.Widget; @SuppressWarnings("javadoc") public class MyTableW implements /* FocusListener, */MyTable { private int tableMode = MyTable.TABLE_MODE_STANDARD; public static final int DOT_SIZE = 6; public static final int LINE_THICKNESS1 = 3; public static final int LINE_THICKNESS2 = 2; public static final GColor SELECTED_BACKGROUND_COLOR = GeoGebraColorConstants.TABLE_SELECTED_BACKGROUND_COLOR; public static final GColor SELECTED_BACKGROUND_COLOR_HEADER = GeoGebraColorConstants .TABLE_SELECTED_BACKGROUND_COLOR_HEADER; public static final GColor BACKGROUND_COLOR_HEADER = GeoGebraColorConstants.TABLE_BACKGROUND_COLOR_HEADER; public static final GColor TABLE_GRID_COLOR = GeoGebraColorConstants.GRAY2; public static final GColor HEADER_GRID_COLOR = GeoGebraColorConstants.GRAY4; public static final GColor SELECTED_RECTANGLE_COLOR = GColor.BLUE; protected Kernel kernel; protected AppW app; private MyCellEditorW editor; // private MyCellEditorBooleanW editorBoolean; // private MyCellEditorButton editorButton; // private MyCellEditorList editorList; public CopyPasteCut copyPasteCut; // protected SpreadsheetColumnControllerW.ColumnHeaderRenderer // columnHeaderRenderer; // protected SpreadsheetRowHeaderW.RowHeaderRenderer rowHeaderRenderer; protected SpreadsheetViewW view; protected SpreadsheetTableModel tableModel; private CellRangeProcessor crProcessor; // private MyTableColumnModelListener columnModelListener; MyCellRendererW defaultTableCellRenderer; private CellFormatInterface formatHandler; /** * All currently selected cell ranges are held in this list. Cell ranges are * added when selecting with ctrl-down. The first element is the most * recently selected cell range. */ public ArrayList<CellRange> selectedCellRanges; @Override public ArrayList<CellRange> getSelectedCellRanges() { return selectedCellRanges; } // These keep track of internal selection using actual ranges and do not // use -1 flags for row and column. // Note: selectedCellRanges.get(0) gives the same selection but uses -1 // flags // the following are in Grid coordinates (TableModel coordinates+1) protected int minSelectionRow = -1; protected int maxSelectionRow = -1; protected int minSelectionColumn = -1; protected int maxSelectionColumn = -1; // for emulating the JTable's changeSelection method, in TableModel // coordinates protected int anchorSelectionRow = -1; protected int anchorSelectionColumn = -1; protected int leadSelectionRow = -1; protected int leadSelectionColumn = -1; // Used for rendering headers with ctrl-select protected HashSet<Integer> selectedColumnSet = new HashSet<Integer>(); protected HashSet<Integer> selectedRowSet = new HashSet<Integer>(); private int selectionType = MyTableInterface.CELL_SELECT; private GColor selectionRectangleColor = SELECTED_RECTANGLE_COLOR; // Dragging vars protected boolean isDragingDot = false; protected int draggingToRow = -1; protected int draggingToColumn = -1; protected boolean isOverDot = false; protected boolean isDragging = false; protected boolean showRowHeader = true; protected boolean showColumnHeader = true; protected boolean isEditing = false; boolean repaintAll = false;// sometimes only the repainting of // borders/background is needed // Cells to be resized on next repaint are put in these HashSets. // A cell is added to a set when editing is done. The cells are removed // after a repaint in MyTable. public HashSet<GPoint> cellResizeHeightSet; public HashSet<GPoint> cellResizeWidthSet; private ArrayList<GPoint> adjustedRowHeights = new ArrayList<GPoint>(); private boolean doRecordRowHeights = true; public int preferredColumnWidth = SpreadsheetSettings.TABLE_CELL_WIDTH; // there should be place left for the textfield public int minimumRowHeight = SpreadsheetSettings.TABLE_CELL_HEIGHT + 4; // the textfield is this much smaller than the row height and column width public static int minusRowHeight = 2; // 12; public static int minusColumnWidth = 2; // 14; private HashMap<GPoint, GeoElement> oneClickEditMap = new HashMap<GPoint, GeoElement>(); // cursors // protected Cursor defaultCursor = Cursor.getDefaultCursor(); // protected Cursor crossHairCursor = Cursor // .getPredefinedCursor(Cursor.CROSSHAIR_CURSOR); // protected Cursor handCursor = Cursor // .getPredefinedCursor(Cursor.HAND_CURSOR); // protected Cursor grabbingCursor, grabCursor; protected Grid ssGrid; protected TableScroller scroller; private FlowPanel tableWrapper; protected SpreadsheetRowHeaderW rowHeader; protected SpreadsheetColumnHeaderW columnHeader; private FlowPanel rowHeaderContainer; // special panels for editing and selection private SimplePanel selectionFrame; private SimplePanel dragFrame; private SimplePanel blueDot; private SimplePanel editorPanel; private AbsolutePanel gridPanel; private Grid upperLeftCorner; private Grid upperRightCorner; private FlowPanel headerRow; private Grid lowerLeftCorner; private FlowPanel ssGridContainer; private FlowPanel columnHeaderContainer; private FlowPanel cornerContainerUpperLeft; private FlowPanel cornerContainerLowerLeft; private FlowPanel cornerContainerUpperRight; public Grid dummyTable; private boolean autoScrolls = true; /******************************************************************* * Construct table */ public MyTableW(SpreadsheetViewW view, SpreadsheetTableModel tableModel) { app = view.getApplication(); kernel = app.getKernel(); this.tableModel = tableModel; this.view = view; createFloatingElements(); createGUI(); cellResizeHeightSet = new HashSet<GPoint>(); cellResizeWidthSet = new HashSet<GPoint>(); for (int i = 0; i < getColumnCount(); ++i) { // TODO//getColumnModel().getColumn(i).setHeaderRenderer(headerRenderer); ssGrid.getColumnFormatter().getElement(i).getStyle() .setWidth(preferredColumnWidth, Style.Unit.PX); } // add cell renderer & editors defaultTableCellRenderer = new MyCellRendererW(app, view, this, (CellFormat) getCellFormatHandler()); // this needs defaultTableCellRenderer now ((SpreadsheetTableModelW) tableModel).attachMyTable(this); // :NEXT:Grid.setCellFormatter editor = new MyCellEditorW(kernel, editorPanel, getEditorController()); // setDefaultEditor(Object.class, editor); // initialize selection fields selectedCellRanges = new ArrayList<CellRange>(); selectedCellRanges.add(new CellRange(app)); selectionType = MyTableInterface.CELL_SELECT; // add mouse and key listeners // scc = new SpreadsheetColumnControllerW(app, this); // srh = new SpreadsheetRowHeaderW(app, this); // key listener - old solution // KeyListener[] defaultKeyListeners = getKeyListeners(); // for (int i = 0; i < defaultKeyListeners.length; ++i) { // removeKeyListener(defaultKeyListeners[i]); // } // addKeyListener(new SpreadsheetKeyListener(app, this)); // addDomHandler(new SpreadsheetKeyListener(app, this), // KeyDownEvent.getType()); // setup selection listener // TODO // These listeners are no longer needed. // getSelectionModel().addListSelectionListener(new // RowSelectionListener()); // getColumnModel().getSelectionModel().addListSelectionListener(new // ColumnSelectionListener()); // getColumnModel().getSelectionModel().addListSelectionListener(columnHeader); // add table model listener ((SpreadsheetTableModelW) tableModel) .setChangeListener(new MyTableModelListener()); copyPasteCut = new CopyPasteCutW(app); /* * // - see ticket #135 addFocusListener(this); * * // editing putClientProperty("terminateEditOnFocusLost", * Boolean.TRUE); * * columnModelListener = new MyTableColumnModelListener(); * getColumnModel().addColumnModelListener(columnModelListener); * * // set first cell active // needed in case spreadsheet selected with * ctrl-tab rather than mouse // click // changeSelection(0, 0, false, * false); */ // rowHeaderRenderer = srh.new RowHeaderRenderer(); // columnHeaderRenderer = scc.new ColumnHeaderRenderer(); ssGrid.setCellPadding(0); ssGrid.setCellSpacing(0); ssGrid.getElement().addClassName("geogebraweb-table-spreadsheet"); registerListeners(); repaintAll(); } /** * @return grid */ public Grid getGrid() { return ssGrid; } /** * @return wrapping widget */ public Widget getContainer() { return tableWrapper; } /** * @return editor */ public MyCellEditorW getEditor() { return editor; } /** * @return whether editor is active */ public boolean isEditing() { return isEditing; } /** * @return Collection of cells that contain geos that can be edited with one * click, e.g. booleans, buttons, lists */ public HashMap<GPoint, GeoElement> getOneClickEditMap() { return oneClickEditMap; } /** * @param oneClickEditMap * fast editable geos, see {@link #getOneClickEditMap()} */ public void setOneClickEditMap(HashMap<GPoint, GeoElement> oneClickEditMap) { this.oneClickEditMap = oneClickEditMap; } private void registerListeners() { SpreadsheetMouseListenerW ml = new SpreadsheetMouseListenerW(app, this); gridPanel.addDomHandler(ml, MouseDownEvent.getType()); gridPanel.addDomHandler(ml, MouseUpEvent.getType()); gridPanel.addDomHandler(ml, MouseMoveEvent.getType()); gridPanel.addDomHandler(ml, DoubleClickEvent.getType()); gridPanel.addDomHandler(ml, TouchStartEvent.getType()); gridPanel.addDomHandler(ml, TouchMoveEvent.getType()); gridPanel.addDomHandler(ml, TouchEndEvent.getType()); upperLeftCorner.addDomHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { selectAll(); } }, ClickEvent.getType()); ClickStartHandler.init(gridPanel, new ClickStartHandler() { @Override public void onClickStart(int x, int y, PointerEventType type) { CancelEventTimer.keyboardSetVisible(); } }); } /** * End table constructor ******************************************************************/ private void createFloatingElements() { editorPanel = new SimplePanel(); editorPanel.addStyleName("editorPanel"); editorPanel.getElement().getStyle().setZIndex(6); editorPanel.setVisible(false); selectionFrame = new SimplePanel(); selectionFrame.addStyleName("geogebraweb-selectionframe-spreadsheet"); selectionFrame.setVisible(false); dragFrame = new SimplePanel(); dragFrame.getElement().getStyle().setZIndex(5); dragFrame.getElement().getStyle() .setBorderStyle(Style.BorderStyle.SOLID); dragFrame.getElement().getStyle().setBorderWidth(2, Style.Unit.PX); dragFrame.getElement().getStyle() .setBorderColor(GColor.GRAY.toString()); dragFrame.setVisible(false); blueDot = new SimplePanel(); blueDot.getElement().getStyle().setZIndex(7); blueDot.getElement().getStyle() .setWidth(MyTableW.DOT_SIZE, Style.Unit.PX); blueDot.getElement().getStyle() .setHeight(MyTableW.DOT_SIZE, Style.Unit.PX); blueDot.getElement().getStyle() .setProperty("borderTop", "1px solid white"); blueDot.getElement().getStyle() .setProperty("borderLeft", "1px solid white"); blueDot.getElement() .getStyle() .setBackgroundColor( MyTableW.SELECTED_RECTANGLE_COLOR.toString()); blueDot.setVisible(false); blueDot.setStyleName("cursor_default"); dummyTable = new Grid(1, 1); dummyTable.getElement().getStyle() .setVisibility(Style.Visibility.HIDDEN); dummyTable.setText(0, 0, "x"); dummyTable.getElement().getStyle().setPosition(Style.Position.ABSOLUTE); dummyTable.getElement().getStyle().setTop(0, Unit.PX); dummyTable.getElement().getStyle().setLeft(0, Unit.PX); dummyTable.getElement().addClassName("geogebraweb-table-spreadsheet"); } private void createGUI() { int leftCornerWidth = SpreadsheetViewW.ROW_HEADER_WIDTH; int rightCornerWidth = AbstractNativeScrollbar .getNativeScrollbarWidth(); int lowerLeftCornerHeight = AbstractNativeScrollbar .getNativeScrollbarHeight(); // ------ upper left corner upperLeftCorner = new Grid(1, 1); upperLeftCorner.getElement().addClassName( "geogebraweb-table-spreadsheet"); upperLeftCorner.getCellFormatter().getElement(0, 0) .addClassName("SVheader"); upperLeftCorner.setText(0, 0, "9999"); upperLeftCorner.setCellPadding(0); upperLeftCorner.setCellSpacing(0); Style s = upperLeftCorner.getElement().getStyle(); upperLeftCorner.addStyleName("upperCorner"); s.setWidth(leftCornerWidth, Unit.PX); //s.setBackgroundColor(BACKGROUND_COLOR_HEADER.toString()); //s.setColor(BACKGROUND_COLOR_HEADER.toString()); s.setPosition(Style.Position.ABSOLUTE); s.setTop(0, Unit.PX); s.setLeft(0, Unit.PX); // ----- upper right corner upperRightCorner = new Grid(1, 1); upperRightCorner.setText(0, 0, "xxx"); upperRightCorner.getElement().addClassName( "geogebraweb-table-spreadsheet"); upperRightCorner.addStyleName("upperCorner"); upperRightCorner.getCellFormatter().getElement(0, 0) .addClassName("SVheader"); s = upperRightCorner.getElement().getStyle(); s.setWidth(rightCornerWidth, Unit.PX); //s.setBackgroundColor(BACKGROUND_COLOR_HEADER.toString()); //s.setColor(BACKGROUND_COLOR_HEADER.toString()); s.setPosition(Style.Position.ABSOLUTE); s.setTop(0, Unit.PX); s.setRight(0, Unit.PX); // ----- lower left corner lowerLeftCorner = new Grid(1, 1); upperRightCorner.setText(0, 0, "9999"); lowerLeftCorner.getElement().addClassName( "geogebraweb-table-spreadsheet-lowerLeftCorner"); s = lowerLeftCorner.getElement().getStyle(); s.setWidth(leftCornerWidth - 1, Unit.PX); s.setHeight(lowerLeftCornerHeight - 2, Unit.PX); //s.setBackgroundColor(BACKGROUND_COLOR_HEADER.toString()); s.setPosition(Style.Position.ABSOLUTE); s.setLeft(0, Unit.PX); s.setBottom(0, Unit.PX); // ---- corner containers cornerContainerUpperLeft = new FlowPanel(); cornerContainerUpperLeft.getElement().getStyle() .setDisplay(Style.Display.BLOCK); cornerContainerUpperLeft.add(upperLeftCorner); cornerContainerLowerLeft = new FlowPanel(); cornerContainerLowerLeft.getElement().getStyle() .setDisplay(Style.Display.BLOCK); cornerContainerLowerLeft.add(lowerLeftCorner); cornerContainerUpperRight = new FlowPanel(); cornerContainerUpperRight.getElement().getStyle() .setDisplay(Style.Display.BLOCK); cornerContainerUpperRight.add(upperRightCorner); // ---- column header columnHeader = new SpreadsheetColumnHeaderW(app, this); s = columnHeader.getContainer().getElement().getStyle(); s.setPosition(Style.Position.RELATIVE); columnHeaderContainer = new FlowPanel(); s = columnHeaderContainer.getElement().getStyle(); s.setDisplay(Display.BLOCK); s.setOverflow(Style.Overflow.HIDDEN); s.setMarginLeft(leftCornerWidth, Unit.PX); s.setMarginRight(rightCornerWidth, Unit.PX); columnHeaderContainer.add(columnHeader.getContainer()); // ------ row header rowHeader = new SpreadsheetRowHeaderW(app, this); s = rowHeader.getContainer().getElement().getStyle(); s.setPosition(Style.Position.RELATIVE); rowHeaderContainer = new FlowPanel(); s = rowHeaderContainer.getElement().getStyle(); s.setDisplay(Display.BLOCK); s.setMarginBottom(lowerLeftCornerHeight, Unit.PX); s.setOverflow(Style.Overflow.HIDDEN); s.setPosition(Style.Position.ABSOLUTE); // s.setTop(0, Unit.PX); s.setLeft(0, Unit.PX); rowHeaderContainer.add(rowHeader.getContainer()); rowHeaderContainer.add(cornerContainerLowerLeft); // spreadsheet table ssGrid = new Grid(tableModel.getRowCount(), tableModel.getColumnCount()); gridPanel = new AbsolutePanel(); gridPanel.getElement().getStyle().setPosition(Style.Position.ABSOLUTE); gridPanel.add(ssGrid); gridPanel.add(selectionFrame); gridPanel.add(dragFrame); gridPanel.add(blueDot); gridPanel.add(editorPanel); gridPanel.add(dummyTable); scroller = new TableScroller(this, rowHeader, columnHeader); ssGridContainer = new FlowPanel(); s = ssGridContainer.getElement().getStyle(); s.setVerticalAlign(Style.VerticalAlign.TOP); s.setDisplay(Display.INLINE_BLOCK); s.setMarginLeft(leftCornerWidth, Unit.PX); ssGridContainer.add(scroller); // create table header row headerRow = new FlowPanel(); headerRow.getElement().getStyle() .setWhiteSpace(Style.WhiteSpace.NOWRAP); headerRow.add(cornerContainerUpperLeft); headerRow.add(columnHeaderContainer); headerRow.add(cornerContainerUpperRight); // create table row FlowPanel tableRow = new FlowPanel(); tableRow.getElement().getStyle().setWhiteSpace(Style.WhiteSpace.NOWRAP); tableRow.getElement().getStyle() .setVerticalAlign(Style.VerticalAlign.TOP); tableRow.add(rowHeaderContainer); tableRow.add(ssGridContainer); // put rows together to complete the GUI tableWrapper = new FlowPanel(); tableWrapper.add(headerRow); tableWrapper.add(tableRow); } public AbsolutePanel getGridPanel() { return gridPanel; } private void updateTableLayout() { int leftCornerWidth = SpreadsheetViewW.ROW_HEADER_WIDTH; if (showColumnHeader) { headerRow.getElement().getStyle().setDisplay(Style.Display.BLOCK); } else { headerRow.getElement().getStyle().setDisplay(Style.Display.NONE); } if (showRowHeader) { cornerContainerUpperLeft.getElement().getStyle() .setDisplay(Style.Display.BLOCK); cornerContainerLowerLeft.getElement().getStyle() .setDisplay(Style.Display.BLOCK); rowHeaderContainer.getElement().getStyle() .setDisplay(Style.Display.BLOCK); ssGridContainer.getElement().getStyle() .setMarginLeft(leftCornerWidth, Unit.PX); columnHeaderContainer.getElement().getStyle() .setMarginLeft(leftCornerWidth, Unit.PX); } else { cornerContainerUpperLeft.getElement().getStyle() .setDisplay(Style.Display.NONE); cornerContainerLowerLeft.getElement().getStyle() .setDisplay(Style.Display.NONE); rowHeaderContainer.getElement().getStyle() .setDisplay(Style.Display.NONE); ssGridContainer.getElement().getStyle().setMarginLeft(0, Unit.PX); columnHeaderContainer.getElement().getStyle() .setMarginLeft(0, Unit.PX); } } /** * Returns parent SpreadsheetView for this table * * @return SpreadsheetView */ @Override public SpreadsheetViewInterface getView() { return view; } /** * Simple getter method * * @return App */ @Override public App getApplication() { return app; } /** * Simple getter method * * @return Kernel */ @Override public Kernel getKernel() { return kernel; } public SpreadsheetTableModel getModel() { return tableModel; } public Grid getTableImpl() { return ssGrid; } @Override public CopyPasteCut getCopyPasteCut() { return copyPasteCut; } /** * Returns CellRangeProcessor for this table. If none exists, a new one is * created. */ @Override public CellRangeProcessor getCellRangeProcessor() { if (crProcessor == null) { crProcessor = new CellRangeProcessor(this); } return crProcessor; } /** * Returns CellFormat helper class for this table. If none exists, a new one * is created. */ @Override public CellFormatInterface getCellFormatHandler() { if (formatHandler == null) { formatHandler = new CellFormat(this); } return formatHandler; } /** * Returns boolean editor (checkbox) for this table. If none exists, a new * one is created. */ /* * public MyCellEditorBooleanW getEditorBoolean() { if (editorBoolean == * null) editorBoolean = new MyCellEditorBooleanW(kernel); return * editorBoolean; } */ /** * Returns button editor for this table. If none exists, a new one is * created. */ /* * public MyCellEditorButton getEditorButton() { if (editorButton == null) * editorButton = new MyCellEditorButton(); return editorButton; } */ /** * Returns list editor (comboBox) for this table. If none exists, a new one * is created. */ /* * public MyCellEditorList getEditorList() { if (editorList == null) * editorList = new MyCellEditorList(); return editorList; } */ public GeoClass getCellEditorType(int row, int column) { GPoint p = new GPoint(column, row); if (view.allowSpecialEditor() && oneClickEditMap.containsKey(p) && kernel.getAlgebraStyleSpreadsheet() == Kernel.ALGEBRA_STYLE_VALUE) { switch (oneClickEditMap.get(p).getGeoClassType()) { case BOOLEAN: return GeoClass.BOOLEAN; case BUTTON: return GeoClass.BUTTON; case LIST: return GeoClass.LIST; } } return GeoClass.DEFAULT; } public BaseCellEditor getCellEditor() { return editor; } public int getRowHeight(int row) { return ssGrid.getRowFormatter().getElement(row).getOffsetHeight(); } public int getColumnWidth(int column) { // columnFormatter returns 0 (in Chrome at least) // so cellFormatter used instead return ssGrid.getCellFormatter().getElement(0, column).getOffsetWidth(); } public int getLeadSelectionRow() { if (leadSelectionRow < 0) { return getSelectedRow(); } return leadSelectionRow; } public int getLeadSelectionColumn() { if (leadSelectionColumn < 0) { return getSelectedColumn(); } return leadSelectionColumn; } /** * sets requirement that commands entered into cells must start with "=" */ public void setEqualsRequired(boolean isEqualsRequired) { editor.setEqualsRequired(isEqualsRequired); } /** * gets flag for requirement that commands entered into cells must start * with "=" */ public boolean isEqualsRequired() { return view.isEqualsRequired(); } public void setLabels() { editor.setLabels(); } public int preferredColumnWidth() { return preferredColumnWidth; } public void setPreferredColumnWidth(int preferredColumnWidth) { this.preferredColumnWidth = preferredColumnWidth; } public class MyTableModelListener implements SpreadsheetTableModelW.ChangeListener { @Override public void dimensionChange() { // TODO: comment them out to imitate the Desktop behaviour // TODO//getView().updateRowHeader(); updateColumnCount(); updateRowCount(); // Log.debug("ssGrid dim: " + ssGrid.getRowCount() + " x " + // ssGrid.getColumnCount()); repaintAll(); } @Override public void valueChange() { updateCopiableSelection(); } } public void updateCopiableSelection() { // TODO: can this be made more efficient? if (view != null && view.spreadsheetWrapper != null) { String cs = copyString(); view.spreadsheetWrapper.setSelectedContent(cs); if (rowHeader != null && selectionType == MyTableInterface.ROW_SELECT) { rowHeader.focusPanel.setSelectedContent(cs); } } else if (rowHeader != null && selectionType == MyTableInterface.ROW_SELECT) { rowHeader.focusPanel.setSelectedContent(copyString()); } } void updateRowCount() { if (ssGrid.getRowCount() >= tableModel.getRowCount()) { return; } int oldRowCount = ssGrid.getRowCount(); ssGrid.resizeRows(tableModel.getRowCount()); for (int row = oldRowCount; row < tableModel.getRowCount(); ++row) { setRowHeight(row, app.getSettings().getSpreadsheet() .preferredRowHeight()); } rowHeader.updateRowCount(); } /** * Appends columns to the table if table model column count is larger than * current number of table columns. */ protected void updateColumnCount() { if (ssGrid.getColumnCount() >= tableModel.getColumnCount()) { return; } int oldColumnCount = ssGrid.getColumnCount(); ssGrid.resizeColumns(tableModel.getColumnCount()); for (int col = oldColumnCount; col < tableModel.getColumnCount(); ++col) { ssGrid.getColumnFormatter().getElement(col).getStyle() .setWidth(preferredColumnWidth, Style.Unit.PX); } columnHeader.updateColumnCount(); // addColumn destroys custom row heights, so we must reset them // resetRowHeights(); } // =============================================================== // Selection // =============================================================== /** * @param point * y coordinate is row, x coordinate is column */ public void changeSelection(GPoint point, boolean extend) { changeSelection(point.getY(), point.getX(), extend); } @Override public void changeSelection(int rowIndex, int columnIndex, boolean extend) { // force column selection if (view.isColumnSelect()) { setColumnSelectionInterval(columnIndex, columnIndex); } if (extend) { leadSelectionColumn = columnIndex; leadSelectionRow = rowIndex; if (anchorSelectionColumn == -1) { anchorSelectionColumn = leadSelectionColumn; } if (anchorSelectionRow == -1) { anchorSelectionRow = leadSelectionRow; } } else { anchorSelectionColumn = columnIndex; anchorSelectionRow = rowIndex; leadSelectionColumn = columnIndex; leadSelectionRow = rowIndex; } // let selectionChanged know about a change in single cell selection selectionChanged(); if (autoScrolls) { GRectangle cellRect = getCellRect(rowIndex, columnIndex, false); if (cellRect != null) { scroller.scrollRectToVisible(cellRect); } } } public void selectAll() { setSelectionType(MyTableInterface.CELL_SELECT); setAutoscrolls(false); // select the upper left corner cell changeSelection(0, 0, false); // extend the selection to the current lower right corner cell changeSelection(getRowCount() - 1, getColumnCount() - 1, true); setSelectAll(true); setAutoscrolls(true); scrollRectToVisible(getCellRect(0, 0, true)); // setRowSelectionInterval(0, getRowCount()-1); // getColumnModel().getSelectionModel().setSelectionInterval(0, // getColumnCount()-1); // selectionChanged(); // this.getSelectAll(); } private void setAutoscrolls(boolean autoScrolls) { this.autoScrolls = autoScrolls; } protected void scrollRectToVisible(GRectangle contentRect) { scroller.scrollRectToVisible(contentRect); } /** * This handles all selection changes for the table. */ @Override public void selectionChanged() { // create a cell range object to store // the current table selection CellRange newSelection = new CellRange(app); /* * TODO if (view.isTraceDialogVisible()) { * * newSelection = view.getTraceSelectionRange(getColumnModel() * .getSelectionModel().getAnchorSelectionIndex(), * getSelectionModel().getAnchorSelectionIndex()); * * scrollRectToVisible(getCellRect(newSelection.getMinRow(), * newSelection.getMaxColumn(), true)); * * } else { */ switch (selectionType) { case MyTableInterface.CELL_SELECT: newSelection.setCellRange(anchorSelectionColumn, anchorSelectionRow, leadSelectionColumn, leadSelectionRow); break; case MyTableInterface.ROW_SELECT: newSelection.setCellRange(-1, anchorSelectionRow, -1, leadSelectionRow); break; case MyTableInterface.COLUMN_SELECT: newSelection.setCellRange(anchorSelectionColumn, -1, leadSelectionColumn, -1); break; } /* * } */ // newSelection.debug(); /* * // return if it is not really a new cell * if(selectedCellRanges.size()>0 && * newSelection.equals(selectedCellRanges.get(0))) return; */ // update the selection list if (!GlobalKeyDispatcherW.getControlDown()) { selectedCellRanges.clear(); selectedColumnSet.clear(); selectedRowSet.clear(); selectedCellRanges.add(0, newSelection); } else { // ctrl-select /* * // return if we have already ctrl-selected this range for * (CellRange cr : selectedCellRanges) { if * (cr.equals(newSelection)){ Log.debug("reutrned"); return; } } */ // handle dragging if (selectedCellRanges.get(0).hasSameAnchor(newSelection)) { selectedCellRanges.remove(0); } // add the selection to the list selectedCellRanges.add(0, newSelection); } // update sets of selected rows/columns (used for rendering in the // headers) if (selectionType == MyTableInterface.COLUMN_SELECT) { for (int i = newSelection.getMinColumn(); i <= newSelection .getMaxColumn(); i++) { selectedColumnSet.add(i); } } if (selectionType == MyTableInterface.ROW_SELECT) { for (int i = newSelection.getMinRow(); i <= newSelection .getMaxRow(); i++) { selectedRowSet.add(i); } } // update internal selection variables newSelection.setActualRange(); minSelectionRow = newSelection.getMinRow(); minSelectionColumn = newSelection.getMinColumn(); maxSelectionColumn = newSelection.getMaxColumn(); maxSelectionRow = newSelection.getMaxRow(); // newSelection.debug(); // printSelectionParameters(); if (isSelectNone && (minSelectionColumn != -1 || minSelectionRow != -1)) { setSelectNone(false); } // TODO if (changedAnchor && !isEditing()) view.updateFormulaBar(); // update the geo selection list ArrayList<GeoElement> list = new ArrayList<GeoElement>(); for (int i = 0; i < selectedCellRanges.size(); i++) { list.addAll(0, (selectedCellRanges.get(i)).toGeoList()); } // if the geo selection has changed, update selected geos boolean changed = !list.equals(app.getSelectionManager() .getSelectedGeos()); if (changed) { if (getTableMode() == MyTable.TABLE_MODE_AUTOFUNCTION) { getSpreadsheetModeProcessor().updateAutoFunction(); } app.getSelectionManager().setSelectedGeos(list, false); if (list.size() > 0) { app.updateSelection(true); } else { // don't update properties view for objects, but for spreadsheet app.updateSelection(false); app.setPropertiesViewPanel(OptionType.SPREADSHEET); } } if (view.isVisibleStyleBar()) { view.getSpreadsheetStyleBar().updateStyleBar(); } // if the selection has changed or an empty cell has been clicked, // repaint if (changed || list.isEmpty()) { repaint(); columnHeader.renderSelection(); rowHeader.renderSelection(); // ?//if (this.getTableHeader() != null) // ?// getTableHeader().repaint(); } updateCopiableSelection(); // Log.debug("------------------"); // for (CellRange cr: selectedCellRanges)cr.debug(); } public void printSelectionParameters() { Log.debug("----------------------------------"); Log.debug("minSelectionColumn = " + minSelectionColumn); Log.debug("maxSelectionColumn = " + maxSelectionColumn); Log.debug("minSelectionRow = " + minSelectionRow); Log.debug("maxSelectionRow = " + maxSelectionRow); Log.debug("----------------------------------"); } /** * Sets the initial selection parameters to a single cell. Does this without * calling changeSelection, so it should only be used at startup. */ public void setInitialCellSelection(int row0, int column0) { setSelectionType(MyTableInterface.CELL_SELECT); int row = row0; int column = column0; if (column == -1) { column = 0; } if (row == -1) { row = 0; } minSelectionColumn = column; maxSelectionColumn = column; minSelectionRow = row; maxSelectionRow = row; renderSelectionDeferred(); columnHeader.renderSelection(); rowHeader.renderSelection(); // ?//getColumnModel().getSelectionModel().setSelectionInterval(column, // column); // ?//getSelectionModel().setSelectionInterval(row, row); } /* * public void setSelectionRectangle(CellRange cr){ * * if (cr == null){ this.minSelectionColumn = -1; this.minSelectionRow = -1; * this.maxSelectionColumn = -1; this.maxSelectionRow = -1; return; } * * this.minSelectionColumn = cr.getMinColumn(); this.minSelectionRow = * cr.getMinRow(); this.maxSelectionColumn = cr.getMaxColumn(); * this.maxSelectionRow = cr.getMaxRow(); this.repaint(); * * } */ /* * public void setTraceSelectionRectangle() { * * if (view.getSelectedTrace() == null) { cellFrame = null; } else { * * int c1 = view.getSelectedTrace().traceColumn1; int r1 = * view.getSelectedTrace().traceRow1; int c2 = * view.getSelectedTrace().traceColumn2; int r2 = * view.getSelectedTrace().doRowLimit ? view.getSelectedTrace().traceRow2 : * getRowCount(); * * Point point1 = getPixel(c1,r1, true); Point point2 = getPixel(c2,r2, * false); * * cellFrame.setFrameFromDiagonal(point1, point2); * * // scroll to upper left corner of rectangle * scrollRectToVisible(table.getCellRect(r1,c1, true)); * * } repaint(); * * } */ public boolean setSelection(String cellName) { if (cellName == null) { return setSelection(-1, -1, -1, -1); } GPoint newCell = GeoElementSpreadsheet.spreadsheetIndices(cellName); if (newCell.x != -1 && newCell.y != -1) { return setSelection(newCell.x, newCell.y); } return false; } @Override public boolean setSelection(int c, int r) { CellRange cr = new CellRange(app, c, r, c, r); return setSelection(cr); } public boolean setSelection(int c1, int r1, int c2, int r2) { CellRange cr = new CellRange(app, c1, r1, c2, r2); if (!cr.isValid()) { return false; } // ArrayList<CellRange> list = new ArrayList<CellRange>(); // list.add(cr); return setSelection(cr); } @Override public boolean setSelection(CellRange cr) { if (cr != null && !cr.isValid()) { return false; } try { if (cr == null || cr.isEmptyRange()) { minSelectionColumn = -1; minSelectionRow = -1; maxSelectionColumn = -1; maxSelectionRow = -1; anchorSelectionColumn = -1; anchorSelectionRow = -1; leadSelectionColumn = -1; leadSelectionRow = -1; } else { setAutoscrolls(false); // row selection if (cr.isRow()) { setRowSelectionInterval(cr.getMinRow(), cr.getMaxRow()); // column selection } else if (cr.isColumn()) { setColumnSelectionInterval(cr.getMinColumn(), cr.getMaxColumn()); // cell block selection } else { setSelectionType(MyTableInterface.CELL_SELECT); changeSelection(cr.getMinRow(), cr.getMinColumn(), false); changeSelection(cr.getMaxRow(), cr.getMaxColumn(), true); } selectionChanged(); // scroll to upper left corner of rectangle setAutoscrolls(true); scrollRectToVisible(getCellRect(cr.getMinRow(), cr.getMinColumn(), true)); repaint(); } } catch (Exception e) { e.printStackTrace(); return false; } return true; } // TODO Handle selection for a list of cell ranges /* * public void setSelection(ArrayList<CellRange> selection){ * * selectionRectangleColor = (color == null) ? SELECTED_RECTANGLE_COLOR : * color; * * // rectangle not drawn correctly without handle ... needs fix * this.doShowDragHandle = true; // doShowDragHandle; * * if (selection == null) { * * setSelectionType(COLUMN_SELECT); * * // clear the selection visuals and the deselect geos from here //TODO: * this should be handled by the changeSelection() method * selectedColumnSet.clear(); selectedRowSet.clear(); * this.minSelectionColumn = -1; this.minSelectionRow = -1; * this.maxSelectionColumn = -1; this.maxSelectionRow = -1; * app.setSelectedGeos(null); //setSelectionType(COLUMN_SELECT); * view.repaint(); setSelectionType(CELL_SELECT); * * } else { * * for (CellRange cr : selection) { * * this.setAutoscrolls(false); * * if (cr.isRow()) { setRowSelectionInterval(cr.getMinRow(), * cr.getMaxRow()); } else if (cr.isColumn()) { * setColumnSelectionInterval(cr.getMinColumn(), cr .getMaxColumn()); } else * { changeSelection(cr.getMinRow(), cr.getMinColumn(), false, false); * changeSelection(cr.getMaxRow(), cr.getMaxColumn(), false, true); } * * // scroll to upper left corner of rectangle * * this.setAutoscrolls(true); * scrollRectToVisible(getCellRect(cr.getMinRow(), cr.getMinColumn(), * true)); } * * * } * * } */ public void setSelectionType(int selType) { if (view.isColumnSelect()) { this.selectionType = MyTableInterface.COLUMN_SELECT; } else { // in web, selectionType should do what setSelectionMode do too this.selectionType = selType; } } @Override public int getSelectionType() { return selectionType; } // By adding a call to selectionChanged in JTable's setRowSelectionInterval // and setColumnSelectionInterval methods, selectionChanged becomes // the sole handler for selection events. public void setRowSelectionInterval(int row0, int row1) { setSelectionType(MyTableInterface.ROW_SELECT); anchorSelectionRow = row0; leadSelectionRow = row1; selectionChanged(); } public void setColumnSelectionInterval(int col0, int col1) { setSelectionType(MyTableInterface.COLUMN_SELECT); anchorSelectionColumn = col0; leadSelectionColumn = col1; selectionChanged(); } private boolean isSelectAll = false; private boolean isSelectNone = false; @Override public boolean isSelectNone() { return isSelectNone; } public void setSelectNone(boolean isSelectNone) { this.isSelectNone = isSelectNone; if (isSelectNone) { setSelection(-1, -1, -1, -1); // TODO//view.updateFormulaBar(); } } @Override public boolean isSelectAll() { return isSelectAll; } public void setSelectAll(boolean isSelectAll) { this.isSelectAll = isSelectAll; } public ArrayList<Integer> getSelectedColumnsList() { ArrayList<Integer> columns = new ArrayList<Integer>(); for (CellRange cr : this.selectedCellRanges) { for (int c = cr.getMinColumn(); c <= cr.getMaxColumn(); ++c) { if (!columns.contains(c)) { columns.add(c); } } } return columns; } // @Override public int[] getSelectedColumns() { ArrayList<Integer> columns = getSelectedColumnsList(); int[] ret = new int[columns.size()]; for (int c = 0; c < columns.size(); c++) { ret[c] = columns.get(c); } return ret; } // =============================================================== // Selection Utilities // =============================================================== public GColor getSelectionRectangleColor() { return selectionRectangleColor; } public void setSelectionRectangleColor(GColor color) { selectionRectangleColor = color; } protected GPoint getPixel(int column, int row, boolean min) { return getPixel(column, row, min, true); } protected GPoint getPixel(int column, int row, boolean min, boolean scaleOffset) { if (column < 0 || row < 0 || column >= this.getColumnCount() || row >= this.getRowCount()) { return null; } if (min && column == 0 && row == 0) { // ? Why this returns 0, wt.getAbsoluteLeft would return a greater // number! // return new GPoint(0, 0); } Element wt = ssGrid.getCellFormatter().getElement(row, column); int offx = ssGrid.getAbsoluteLeft(); int offy = ssGrid.getAbsoluteTop(); int left, top; if(scaleOffset){ left = (int) ((wt.getAbsoluteLeft() - offx) / app .getArticleElement().getScaleX()) + offx; top = (int) ((wt.getAbsoluteTop() - offy) / app.getArticleElement() .getScaleY()) + offy; }else{ left = (int) ((wt.getAbsoluteLeft()) / app .getArticleElement().getScaleX()); top = (int) ((wt.getAbsoluteTop() ) / app.getArticleElement() .getScaleY()) ; } // Log.debug("-----------------------" + min); if (min) { // Log.debug("col x row: " + column + " x " + row + " pixels: " + // left + " x " + top); // getPixel2(column,row,min); return new GPoint(left, top); } // Log.debug("col x row: " + column + " x " + row + " pixels: " + (left // + wt.getOffsetWidth()) + // " x " + (top+wt.getOffsetHeight())); // getPixel2(column,row,min); return new GPoint(left + wt.getOffsetWidth(), top + wt.getOffsetHeight()); } protected GPoint getPixelRelative(int column0, int row0, boolean min) { if (column0 < 0 || row0 < 0) { return null; } int row = row0; int column = column0; if (column > getColumnCount() - 1) { column = getColumnCount() - 1; } if (row > getRowCount() - 1) { row = getRowCount() - 1; } GPoint p = new GPoint(0, 0); HashMap<Integer, Integer> widthMap = view.settings().getWidthMap(); HashMap<Integer, Integer> heightMap = view.settings().getHeightMap(); // adjust loop condition dependent on min int extraCell = min ? 0 : 1; for (int c = 0; c < column + extraCell; c++) { Integer w = widthMap.get(c); if (w == null) { w = preferredColumnWidth; } p.x += w; } for (int r = 0; r < row + extraCell; r++) { Integer h = heightMap.get(r); if (h == null) { h = view.settings().preferredRowHeight(); } p.y += h; } // p.x += ssGrid.getAbsoluteLeft(); // p.y += ssGrid.getAbsoluteTop(); // Log.debug("#2col x row: " + column + " x " + row + " pixels: " + p.x // + " x " + p.y); return p; } protected GPoint getMinSelectionPixel() { return getPixel(minSelectionColumn, minSelectionRow, true); } protected GPoint getMaxSelectionPixel(boolean scaleOffset) { return getPixel(maxSelectionColumn, maxSelectionRow, false, scaleOffset); } /** * Returns Point(columnIndex, rowIndex), cell indices for the given pixel * location */ public GPoint getIndexFromPixel(int x, int y) { if (x < 0 || y < 0) { return null; } int columnFrom = 0; int rowFrom = 0; int indexX = -1; int indexY = -1; for (int i = columnFrom; i < getColumnCount(); ++i) { GPoint point = getPixel(i, rowFrom, false, false); if (x < point.getX()) { indexX = i; break; } } if (indexX == -1) { return null; } for (int i = rowFrom; i < getRowCount(); ++i) { GPoint point = getPixel(columnFrom, i, false, false); if (y < point.getY()) { indexY = i; break; } } if (indexY == -1) { return null; } return new GPoint(indexX, indexY); } /** * @param spacing * whether to include border -- TODO unused */ public GRectangle getCellRect(int row, int column, boolean spacing) { GPoint min = getPixel(column, row, true); if (min == null) { return null; } GPoint max = getPixel(column, row, false); if (max == null) { return null; } return new Rectangle(min.x, min.y, max.x - min.x, max.y - min.y); } public GRectangle getCellBlockRect(int column1, int row1, int column2, int row2, boolean includeSpacing) { GRectangle r1 = getCellRect(row1, column1, includeSpacing); GRectangle r2 = getCellRect(row2, column2, includeSpacing); r1.setBounds((int) r1.getX(), (int) r1.getY(), (int) ((r2.getX() - r1.getX()) + r2.getWidth()), (int) ((r2.getY() - r1.getY()) + r2.getHeight())); return r1; } public GRectangle getSelectionRect(boolean includeSpacing) { return getCellBlockRect(minSelectionColumn, minSelectionRow, maxSelectionColumn, maxSelectionRow, includeSpacing); } // target selection frame // ============================= private GRectangle targetcellFrame; private SpreadsheetController controller; public GRectangle getTargetcellFrame() { return targetcellFrame; } public void setTargetcellFrame(GRectangle targetcellFrame) { this.targetcellFrame = targetcellFrame; } final static double dash1[] = { 2.0 }; final static GBasicStrokeW dashed = new GBasicStrokeW(3.0, DefaultBasicStroke.CAP_BUTT, DefaultBasicStroke.JOIN_MITER, 10.0, dash1); /** * @param point * x column, y row * @return true on success */ public boolean editCellAt(GPoint point) { return editCellAt(point.getY(), point.getX()); } /** * Starts in-cell editing for cells with short editing strings. For strings * longer than MAX_CELL_EDIT_STRING_LENGTH, the redefine dialog is shown. * Also prevents fixed cells from being edited. */ @Override public boolean editCellAt(final int row, final int col) { if (row < 0 || col < 0) { return false; } Object ob = tableModel.getValueAt(row, col); // prepare editor to handle equals editor.setEqualsRequired(app.getSettings().getSpreadsheet() .equalsRequired()); if (ob instanceof GeoElement) { GeoElement geo = (GeoElement) ob; if (geo.isGeoButton() || geo.isGeoImage()) { ArrayList<GeoElement> sel = new ArrayList<GeoElement>(); sel.add(geo); app.getDialogManager().showPropertiesDialog(OptionType.OBJECTS, sel); return true; } if (!view.getShowFormulaBar()) { if (getEditorController().redefineIfNeeded(geo)) { return true; } } } // STANDARD case: in cell editing if (isCellEditable(row, col) && !isEditing) { switch (getCellEditorType(row, col)) { case DEFAULT: isEditing = true; AutoCompleteTextFieldW w = (AutoCompleteTextFieldW) ((MyCellEditorW) getCellEditor()) .getTableCellEditorWidget(this, ob, false, row, col); // w.getElement().setAttribute("display", "none"); if (app.has(Feature.ONSCREEN_KEYBOARD_AT_EDIT_SV_CELLS)) { if (view.isKeyboardEnabled()) { app.showKeyboard(w, true); final GRectangle rect = getCellRect(row, col, true); Scheduler.get().scheduleDeferred(new ScheduledCommand(){ @Override public void execute() { scrollRectToVisible(rect); } }); if (Browser.isAndroid() || Browser.isIPad()) { w.setEnabled(false); w.addDummyCursor(w.getCaretPosition()); } } else if (app.has(Feature.KEYBOARD_BEHAVIOUR)){ //if keyboard doesn't enabled, inserts openkeyboard button if there is no in the SV yet app.showKeyboard(w,false); } } // set height and position of the editor int editorHeight = ssGrid.getCellFormatter() .getElement(row, col).getClientHeight(); w.getTextField().getElement().getStyle() .setHeight(editorHeight, Unit.PX); positionEditorPanel(true, row, col); // give it the focus w.requestFocus(); renderSelection(); return true; case BOOLEAN: case BUTTON: case LIST: // instead of editing the checkbox, do not go into editing mode // at all, // because we don't know when to stop editing isEditing = false; positionEditorPanel(false, 0, 0); renderSelection(); return true; } } BaseCellEditor mce = getCellEditor(); if (mce != null) { mce.cancelCellEditing(); } return false;// TODO: implementation needed } private SpreadsheetController getEditorController() { if (controller == null) { controller = new SpreadsheetController(app); } return controller; } public int convertColumnIndexToModel(int viewColumnIndex) { return viewColumnIndex; } private boolean allowEditing = false; private SpreadsheetModeProcessor spredsheetModeProcessor; public boolean isAllowEditing() { return allowEditing; } public void setAllowEditing(boolean allowEditing) { this.allowEditing = allowEditing; } /* * we need to return false for this normally, otherwise we can't detect * double-clicks */ public boolean isCellEditable(int row, int column) { if (view.isColumnSelect()) { return false; } // allow use of special editors for e.g. buttons, lists if (view.allowSpecialEditor() && oneClickEditMap.containsKey(new GPoint(column, row))) { return true; } // normal case: return false so we can handle double click in our // // mouseReleased if (!allowEditing) { return false; } // prevent editing fixed geos when allowEditing == true GeoElement geo = (GeoElement) getModel().getValueAt(row, column); if (geo != null && geo.isProtected(EventType.UPDATE)) { return false; } // return true when editing is allowed (mostly for blank cells). This // lets // the JTable mousePressed listener catch double clicks and invoke the // editor return true; } @Override public void updateEditor(String text) { if (this.isEditing()) { editor.setText(text); } } public void finishEditing(boolean editNext) { isEditing = false; // hide the editor positionEditorPanel(false, 0, 0); if (!editNext) { view.requestFocus(); } // setRepaintAll();//TODO: don't call renderCells, just change the // edited cell repaint(); } public void sendEditorKeyPressEvent(KeyPressEvent e) { editor.sendKeyPressEvent(e); } public void sendEditorKeyDownEvent(KeyDownEvent e) { editor.sendKeyDownEvent(e); } /* * public void focusGained(FocusEvent e) { if * (AppD.isVirtualKeyboardActive()) * ((GuiManagerD)app.getGuiManager()).toggleKeyboard(true); * * } * * public void focusLost(FocusEvent e) { // avoid infinite loop! if * (e.getOppositeComponent() instanceof VirtualKeyboard) return; if * (AppD.isVirtualKeyboardActive()) * ((GuiManagerD)app.getGuiManager()).toggleKeyboard(false); * * } */ // Keep column widths of table and column header in sync public void setColumnWidth(int column, int width) { int width2 = width; // TODO : check if this minimum width is valid, // if so create constant field // there is a minimal width in the Desktop version, // Web version should imitate this; this is visually looking // like 5px, but in the code, it seems that it is 15px if (width2 < 15) { width2 = 15; } if (column >= 0) { ssGrid.getColumnFormatter().getElement(column).getStyle() .setWidth(width2, Style.Unit.PX); if (showColumnHeader) { columnHeader.setColumnWidth(column, width2); } } } public void setColumnWidth(final int width) { Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { minimumRowHeight = dummyTable.getCellFormatter() .getElement(0, 0).getOffsetHeight(); int width2 = Math.max(width, minimumRowHeight); for (int col = 0; col < getColumnCount(); col++) { ssGrid.getColumnFormatter().getElement(col).getStyle() .setWidth(width2, Style.Unit.PX); if (showColumnHeader) { columnHeader.setColumnWidth(col, width2); } } if (view != null) { // view.updatePreferredRowHeight(rowHeight2); } } }); } // Keep row heights of table and row header in sync public void setRowHeight(final int row, final int rowHeight) { Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { setRowHeightCallback(row, rowHeight); } }); } protected void setRowHeightCallback(int row, int rowHeight) { minimumRowHeight = dummyTable.getCellFormatter().getElement(0, 0) .getOffsetHeight(); int rowHeight2 = Math.max(rowHeight, minimumRowHeight); if (row >= 0) { ssGrid.getRowFormatter().getElement(row).getStyle() .setHeight(rowHeight2, Style.Unit.PX); if (showRowHeader) { syncRowHeaderHeight(row); } } if (view != null) { if (doRecordRowHeights) { adjustedRowHeights.add(new GPoint(row, rowHeight2)); } } } // Keep table and row header heights in sync public void syncRowHeaderHeight(final int row) { Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { int rowHeight = ssGrid.getRowFormatter().getElement(row) .getOffsetHeight(); rowHeader.setRowHeight(row, rowHeight); } }); } /* * Fits the content of spreadsheet for its header on the left. */ public void syncTableTop(){ scroller.syncTableTop(); } public void setRowHeight(final int rowHeight) { Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { minimumRowHeight = dummyTable.getCellFormatter() .getElement(0, 0).getOffsetHeight(); int rowHeight2 = Math.max(rowHeight, minimumRowHeight); for (int row = 0; row < getRowCount(); row++) { ssGrid.getRowFormatter().getElement(row).getStyle() .setHeight(rowHeight2, Style.Unit.PX); if (showRowHeader) { rowHeader.setRowHeight(row, rowHeight2); } } if (view != null) { // view.updatePreferredRowHeight(rowHeight2); } } }); } // Reset the row heights --- used after addColumn destroys the row heights public void resetRowHeights() { doRecordRowHeights = false; for (GPoint p : adjustedRowHeights) { setRowHeight(p.x, p.y); } doRecordRowHeights = true; } // ================================================== // Table row and column size adjustment methods // ================================================== /** * Enlarge the row and/or column of all marked cells. A cell is marked by * placing it in one of two hashSets: cellResizeHeightSet or * cellResizeWidthSet. Currently, this is only done after a geo is added to * a cell and the row needs to be widened to fit the LaTeX image. * */ public void resizeMarkedCells() { if (!cellResizeHeightSet.isEmpty()) { for (GPoint cellPoint : cellResizeHeightSet) { setPreferredCellSize(cellPoint.getY(), cellPoint.getX(), false, true); } cellResizeHeightSet.clear(); } if (!cellResizeWidthSet.isEmpty()) { for (GPoint cellPoint : cellResizeWidthSet) { setPreferredCellSize(cellPoint.getY(), cellPoint.getX(), true, false); } cellResizeWidthSet.clear(); } } /** * Enlarge the row and/or column of a cell to fit the cell's preferred size. */ public void setPreferredCellSize(int row, int col, boolean adjustWidth, boolean adjustHeight) { // in table model coordinates Element prefElement = ssGrid.getCellFormatter().getElement(row, col); if (adjustWidth) { Element tableColumn = ssGrid.getColumnFormatter().getElement(col); int resultWidth = Math.max(tableColumn.getOffsetWidth(), prefElement.getOffsetWidth()); tableColumn.getStyle().setWidth(resultWidth /* * TODO this. * getIntercellSpacing * ().width */ , Style.Unit.PX); columnHeader.setColumnWidth(col, resultWidth); } if (adjustHeight) { int resultHeight = Math.max(ssGrid.getRowFormatter() .getElement(row).getOffsetHeight(), prefElement.getOffsetHeight()); int rowHeight2 = resultHeight; // if (rowHeight2 < minimumRowHeight) // rowHeight2 = minimumRowHeight; setRowHeight(row, rowHeight2); } } /** * Adjust the width of a column to fit the maximum preferred width of its * cell contents. */ public void fitColumn(int column) { // in grid coordinates Element tableColumn = ssGrid.getColumnFormatter().getElement(column); int prefWidth = 0; int tempWidth = -1; for (int row = 0; row < getRowCount(); row++) { if (column >= 0 && tableModel.getValueAt(row, column) != null) { tempWidth = ssGrid.getCellFormatter().getElement(row, column) .getOffsetWidth(); prefWidth = Math.max(prefWidth, tempWidth); } } // set the new column width if (tempWidth == -1) { // column is empty prefWidth = preferredColumnWidth /* * TODO getIntercellSpacing().width */; } else { // There was "15" here, but in Desktop, it should visually look // like if there was "5"... prefWidth = Math.max(prefWidth, 15 /* * TODO * tableColumn.getMinWidth() */); } // note: the table might have its header set to null, // so we get the actual header from view // TODO//view.getTableHeader().setResizingColumn(tableColumn); tableColumn.getStyle().setWidth(prefWidth /* * TODO getIntercellSpacing() * .width */ , Style.Unit.PX); columnHeader.setColumnWidth(column, prefWidth); } /** * Adjust the height of a row to fit the maximum preferred height of the its * cell contents. */ public void fitRow(int row) { // in grid coordinates int prefHeight = ssGrid.getRowFormatter().getElement(row) .getOffsetHeight(); // int prefHeight = this.getRowHeight(); int tempHeight = 0; for (int column = 0; column < this.getColumnCount(); column++) { tempHeight = ssGrid.getCellFormatter().getElement(row, column) .getOffsetHeight(); prefHeight = Math.max(prefHeight, tempHeight); } // set the new row height this.setRowHeight(row, prefHeight); } /** * Adjust all rows/columns to fit the maximum preferred height/width of * their cell contents. * */ public void fitAll(boolean doRows, boolean doColumns) { if (doRows) { for (int row = 0; row < getRowCount(); row++) { fitRow(row); } } if (doColumns) { for (int column = 0; column < getColumnCount(); column++) { // ?//fitRow(column); fitColumn(column); } } } /** * Column model listener --- used to reset the preferred column width when * all columns have been selected. */ /* * public class MyTableColumnModelListener implements * TableColumnModelListener { * * public void columnMarginChanged(ChangeEvent e) { if (isSelectAll() && * minSelectionColumn >= 0) { preferredColumnWidth = * getColumnModel().getColumn( minSelectionColumn).getPreferredWidth(); // * view.updatePreferredColumnWidth(preferredColumnWidth); } // TODO: find * more efficient way to record column widths * view.updateAllColumnWidthSettings(); } * * public void columnAdded(TableColumnModelEvent arg0) { } * * public void columnMoved(TableColumnModelEvent arg0) { } * * public void columnRemoved(TableColumnModelEvent arg0) { } * * public void columnSelectionChanged(ListSelectionEvent arg0) { } } */ // When the spreadsheet is smaller than the viewport fill the extra space // with // the same background color as the spreadsheet. // This gives a smoother look when the spreadsheet auto-adjusts to fill the // space. /* * @Override protected void configureEnclosingScrollPane() { * super.configureEnclosingScrollPane(); Container p = getParent(); if (p * instanceof JViewport) { ((JViewport) p).setBackground(getBackground()); } * } */ // ================================================== // Table mode change // ================================================== @Override public int getTableMode() { return tableMode; } /** * Sets the table mode * * @param tableMode */ @Override public void setTableMode(int tableMode) { if (tableMode == MyTable.TABLE_MODE_AUTOFUNCTION) { if (!initAutoFunction()) { return; } } else if (tableMode == MyTable.TABLE_MODE_DROP) { // nothing to do (yet) } else { // Clear the targetcellFrame and ensure the selection rectangle // color is standard targetcellFrame = null; this.setSelectionRectangleColor(GColor.BLUE); } this.tableMode = tableMode; repaint(); } // ================================================== // Autofunction handlers // ================================================== /** * Initializes the autoFunction feature. The targetCell is prepared and the * GUI is adjusted to handle selection drag with an autoFunction */ protected boolean initAutoFunction() { // Selection is a single cell. // The selected cell is the target cell. Allow the user to drag a new // selection for the // autoFunction. The autoFunction values are previewed in the targetCell // while dragging. if (selectedCellRanges.size() == 1 && selectedCellRanges.get(0).isSingleCell()) { // Clear the target cell, exit if this is not possible if (RelativeCopy.getValue(app, minSelectionColumn, minSelectionRow) != null) { boolean isOK = copyPasteCut.delete(minSelectionColumn, minSelectionRow, minSelectionColumn, minSelectionRow); if (!isOK) { return false; } } // Set targetCell as a GeoNumeric that can be used to preview the // autofunction result // (later it will be set as a GeoList) getSpreadsheetModeProcessor().initTargetCell(minSelectionColumn, minSelectionRow); // Set the targetcellFrame so the Paint method can use it to draw a // dashed frame - will be implemented differently in the web version // (TODO) // targetcellFrame = this.getCellBlockRect(minSelectionColumn, // minSelectionRow, minSelectionColumn, minSelectionRow, true); // Change the selection frame color to gray // and clear the current selection setSelectionRectangleColor(GColor.GRAY); minSelectionColumn = -1; maxSelectionColumn = -1; minSelectionRow = -1; maxSelectionRow = -1; app.getSelectionManager().clearSelectedGeos(); } // try to create autoFunction cell(s) adjacent to the selection else if (selectedCellRanges.size() == 1) { try { getSpreadsheetModeProcessor() .performAutoFunctionCreation(selectedCellRanges.get(0), GlobalKeyDispatcherW.getShiftDown()); } catch (Exception e) { e.printStackTrace(); } // Don't stay in this mode, we're done return false; } else { return false; } return true; } // =========================================== // copy/paste/cut/delete methods // // this is temporary code while cleaning up // =========================================== public String copyString() { return ((CopyPasteCutW)copyPasteCut).copyString(getSelectedColumn(), getSelectedRow(), getMaxSelectedColumn(), getMaxSelectedRow()); } public void copy(boolean altDown) { copyPasteCut.copy(getSelectedColumn(), getSelectedRow(), getMaxSelectedColumn(), getMaxSelectedRow(), altDown); } public void copy(boolean altDown, boolean nat) { ((CopyPasteCutW)copyPasteCut).copy(getSelectedColumn(), getSelectedRow(), getMaxSelectedColumn(), getMaxSelectedRow(), altDown, nat); } public boolean paste() { return copyPasteCut.paste(getSelectedColumn(), getSelectedRow(), getMaxSelectedColumn(), getMaxSelectedRow()); } public boolean paste(String cont) { return ((CopyPasteCutW)copyPasteCut).paste(getSelectedColumn(), getSelectedRow(), getMaxSelectedColumn(), getMaxSelectedRow(), cont); } public boolean cut() { return copyPasteCut.cut(getSelectedColumn(), getSelectedRow(), getMaxSelectedColumn(), getMaxSelectedRow()); } public boolean cut(boolean nat) { return ((CopyPasteCutW)copyPasteCut).cut(getSelectedColumn(), getSelectedRow(), getMaxSelectedColumn(), getMaxSelectedRow(), nat); } public boolean delete() { return copyPasteCut.delete(getSelectedColumn(), getSelectedRow(), getMaxSelectedColumn(), getMaxSelectedRow()); } /* * private static Cursor createCursor(Image cursorImage, boolean center) { * Toolkit toolkit = Toolkit.getDefaultToolkit(); java.awt.Point * cursorHotSpot; if (center) { cursorHotSpot = new * java.awt.Point(cursorImage.getWidth(null) / 2, * cursorImage.getHeight(null) / 2); } else { cursorHotSpot = new * java.awt.Point(0, 0); } Cursor cursor = * toolkit.createCustomCursor(cursorImage, cursorHotSpot, null); return * cursor; } */ /** * Force repaint calls to update everything. (Useful for debugging.) */ public void setRepaintAll() { repaintAll = true; } /** * Updates all cell formats and the current selection. */ @Override public void repaintAll() { repaintAll = true; repaint(); } /** * Updates only the current selection. For efficiency the cell formats are * not updated. */ @Override public void repaint() { if (repaintAll) { updateAllCellFormats(); repaintAll = false; } renderSelection(); } public void updateCellFormat(int row, int column) { GeoElement geo = (GeoElement) tableModel.getValueAt(row, column); defaultTableCellRenderer.updateCellFormat(geo, row, column); } public void updateAllCellFormats() { for (int row = 0; row < getRowCount(); row++) { for (int column = 0; column < getColumnCount(); column++) { defaultTableCellRenderer.clearBorder(row, column); updateCellFormat(row, column); } } for (int row = 0; row < getRowCount(); row++) { defaultTableCellRenderer.updateCellBorder(row, -1); } for (int column = 0; column < getColumnCount(); column++) { defaultTableCellRenderer.updateColumnBorder(column); } } public void updateCellFormat(ArrayList<CellRange> cellRangeList) { for (int i = 0; i < cellRangeList.size(); i++) { CellRange cr = cellRangeList.get(i); for (int row = cr.getMinRow(); row <= cr.getMaxRow(); row++) { for (int column = cr.getMinColumn(); column <= cr .getMaxColumn(); column++) { updateCellFormat(row, column); } } } } @Override public void updateTableCellValue(Object value, int row, int column) { if (defaultTableCellRenderer != null) { defaultTableCellRenderer.updateTableCellValue(ssGrid, value, row, column); } } public boolean showCanDragBlueDot() { boolean showBlueDot = !isEditing(); if (minSelectionRow != -1 && maxSelectionRow != -1 && minSelectionColumn != -1 && maxSelectionColumn != -1) { if (showBlueDot) { for (int i = minSelectionRow; i <= maxSelectionRow; i++) { for (int j = minSelectionColumn; j <= maxSelectionColumn; j++) { if (tableModel.getValueAt(i, j) instanceof GeoElement) { showBlueDot &= !((GeoElement) tableModel .getValueAt(i, j)) .isProtected(EventType.UPDATE); } } } } return showBlueDot; } return false; } public void renderSelectionDeferred() { Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { renderSelection(); } }); } void renderSelection() { // TODO implement other features from the old paint method // draw dragging frame GPoint point1 = new GPoint(0, 0); GPoint point2 = new GPoint(0, 0); if (draggingToRow != -1 && draggingToColumn != -1) { // -|1|- // 2|-|3 // -|4|- boolean visible = true; if (draggingToColumn < minSelectionColumn) { // 2 point1 = getPixel(draggingToColumn, minSelectionRow, true); point2 = getPixel(minSelectionColumn - 1, maxSelectionRow, false); } else if (draggingToRow > maxSelectionRow) { // 4 point1 = getPixel(minSelectionColumn, maxSelectionRow + 1, true); point2 = getPixel(maxSelectionColumn, draggingToRow, false); } else if (draggingToRow < minSelectionRow) { // 1 point1 = getPixel(minSelectionColumn, draggingToRow, true); point2 = getPixel(maxSelectionColumn, minSelectionRow - 1, false); } else if (draggingToColumn > maxSelectionColumn) { // 3 point1 = getPixel(maxSelectionColumn + 1, minSelectionRow, true); point2 = getPixel(draggingToColumn, maxSelectionRow, false); } else { visible = false; } updateDragFrame(visible, point1, point2); } else { updateDragFrame(false, point1, point2); } GPoint min = this.getMinSelectionPixel(); GPoint max = this.getMaxSelectionPixel(true); if (minSelectionRow != -1 && maxSelectionRow != -1 && minSelectionColumn != -1 && maxSelectionColumn != -1) { updateSelectionFrame(true, showCanDragBlueDot(), min, max); } else { updateSelectionFrame(false, false, min, max); } // cells GeoElement geo = null; int maxColumn = tableModel.getHighestUsedColumn(); int maxRow = tableModel.getHighestUsedRow(); for (int col = maxColumn; col >= 0; col--) { for (int row = maxRow; row >= 0; row--) { geo = (GeoElement) tableModel.getValueAt(row, col); defaultTableCellRenderer.updateCellBackground(geo, row, col); } } // After rendering the LaTeX image for a geo, update the row height // with the preferred size set by the renderer. resizeMarkedCells(); } public int getSelectedRow() { if (minSelectionRow < 0) { return -1; } return minSelectionRow; } public int getSelectedColumn() { if (minSelectionColumn < 0) { return -1; } return minSelectionColumn; } public int getMaxSelectedRow() { if (maxSelectionRow < 0) { return -1; } return maxSelectionRow; } public int getMaxSelectedColumn() { if (maxSelectionColumn < 0) { return -1; } return maxSelectionColumn; } public Widget getEditorWidget() { return editor.getTextfield(); } /** * Make the row header invisible to the user * * @param showRow * true: show it; false: hide it */ public void setShowRowHeader(boolean showRow) { if (showRow) { if (!showRowHeader) { showRowHeader = true; } } else { showRowHeader = false; } updateTableLayout(); } /** * Make the column header invisible to the user * * @param showCol * true: show it; false: hide it */ public void setShowColumnHeader(boolean showCol) { if (showCol) { if (!showColumnHeader) { showColumnHeader = true; } } else { showColumnHeader = false; } updateTableLayout(); } @Override public void updateCellFormat(String cellFormatString) { view.updateCellFormat(cellFormatString); } @Override public boolean allowSpecialEditor() { return view.allowSpecialEditor(); } @Override public int getColumnCount() { return tableModel.getColumnCount(); } @Override public int getRowCount() { return tableModel.getRowCount(); } public int getOffsetWidth() { return tableWrapper.getOffsetWidth(); } public int getOffsetHeight() { return tableWrapper.getOffsetHeight(); } public void setSize(int width, int height) { tableWrapper.setPixelSize(width, height); scroller.setPixelSize(width - rowHeader.getOffsetWidth(), height - columnHeader.getOffsetHeight()); } public void updateSelectionFrame(boolean visible, boolean showDragHandle, GPoint corner1, GPoint corner2) { if (selectionFrame == null || corner1 == null || corner2 == null) { return; } int borderWidth = 2; // forcing a hide/show is needed to make Chrome redraw selectionFrame.setVisible(false); if (visible) { int x1 = Math.min(corner1.x, corner2.x); int x2 = Math.max(corner1.x, corner2.x); int y1 = Math.min(corner1.y, corner2.y); int y2 = Math.max(corner1.y, corner2.y); int h = y2 - y1 - 2 * borderWidth - 1; int w = x2 - x1 - 2 * borderWidth - 1; if (w >= 0 && h >= 0) { int ssTop = gridPanel.getAbsoluteTop(); int ssLeft = gridPanel.getAbsoluteLeft(); selectionFrame.setWidth(w + "px"); selectionFrame.setHeight(h + "px"); gridPanel.setWidgetPosition(selectionFrame, x1 - ssLeft, y1 - ssTop); blueDot.setVisible(showDragHandle); if (showDragHandle) { gridPanel.setWidgetPosition(blueDot, x2 - ssLeft - MyTableW.DOT_SIZE / 2 - 1, y2 - ssTop - MyTableW.DOT_SIZE / 2 - 1); } selectionFrame.setVisible(true); } } } public void updateDragFrame(boolean visible, GPoint corner1, GPoint corner2) { if (dragFrame == null) { return; } int borderWidth = 2; dragFrame.setVisible(visible); if (visible) { int x1 = Math.min(corner1.x, corner2.x); int x2 = Math.max(corner1.x, corner2.x); int y1 = Math.min(corner1.y, corner2.y); int y2 = Math.max(corner1.y, corner2.y); int h = y2 - y1 - 2 * borderWidth - 1; int w = x2 - x1 - 2 * borderWidth - 1; gridPanel.setWidgetPosition(dragFrame, x1 - gridPanel.getAbsoluteLeft(), y1 - gridPanel.getAbsoluteTop()); dragFrame.setWidth(w + "px"); dragFrame.setHeight(h + "px"); } } public void positionEditorPanel(boolean visible, int row, int column) { if (editorPanel == null) { return; } editorPanel.setVisible(visible); if (visible) { GPoint p = getPixel(column, row, true); int ssTop = gridPanel.getAbsoluteTop(); int ssLeft = gridPanel.getAbsoluteLeft(); gridPanel.setWidgetPosition(editorPanel, p.x - ssLeft, p.y - ssTop); editorPanel.setVisible(true); int w = ssGrid.getCellFormatter().getElement(row, column) .getClientWidth(); int h = ssGrid.getCellFormatter().getElement(row, column) .getClientHeight(); editorPanel.getElement().getStyle().setWidth(w, Unit.PX); editorPanel.getElement().getStyle().setHeight(h, Unit.PX); } } public SimplePanel getEditorPanel() { return editorPanel; } public void setVerticalScrollPosition(int scrollPosition) { scroller.setVerticalScrollPosition(scrollPosition); } public void setHorizontalScrollPosition(int scrollPosition) { scroller.setHorizontalScrollPosition(scrollPosition); } public int getHorizontalScrollPosition() { return scroller.getHorizontalScrollPosition(); } public int getVerticalScrollPosition() { return scroller.getVerticalScrollPosition(); } public void updateFonts() { setRowHeight(0); resetRowHeights(); renderSelection(); } public void setShowVScrollBar(boolean showVScrollBar) { scroller.setShowVScrollBar(showVScrollBar); } public void setShowHScrollBar(boolean showHScrollBar) { scroller.setShowHScrollBar(showHScrollBar); } public void setEnableAutoComplete(boolean enableAutoComplete) { editor.setEnableAutoComplete(enableAutoComplete); } /** * * @param toolTipText * the string to display in a tool tip; if the text is null, the * tool tip is turned off */ public void setToolTipText(String toolTipText) { ToolTipManagerW.sharedInstance().showToolTip(toolTipText); } /** * Update spreadsheet when zoom level changes * * @param ratio * CSS pixel ratio */ public void setPixelRatio(double ratio) { if (editor != null) { editor.stopCellEditing(); } view.setRowHeightsFromSettings(); for (int row = 0; row < this.getRowCount(); row++) { for (int column = 0; column < this.getColumnCount(); column++) { if (ssGrid.getWidget(row, column) instanceof Canvas) { this.updateTableCellValue(app.getSpreadsheetTableModel() .getValueAt(row, column), row, column); } } } } public void allowAutoEdit() { if (editor != null) { editor.allowAutoEdit(); } } public SpreadsheetModeProcessor getSpreadsheetModeProcessor() { if (this.spredsheetModeProcessor == null) { this.spredsheetModeProcessor = new SpreadsheetModeProcessor(app, this); } return this.spredsheetModeProcessor; } }