package org.geogebra.desktop.gui.view.spreadsheet;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.ListSelectionModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableColumn;
import org.geogebra.common.awt.GPoint;
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.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.GeoGebraColorConstants;
import org.geogebra.common.main.OptionType;
import org.geogebra.common.main.settings.SpreadsheetSettings;
import org.geogebra.common.plugin.EventType;
import org.geogebra.common.util.debug.Log;
import org.geogebra.desktop.awt.GColorD;
import org.geogebra.desktop.gui.GuiManagerD;
import org.geogebra.desktop.gui.virtualkeyboard.VirtualKeyboardD;
import org.geogebra.desktop.main.AppD;
import org.geogebra.desktop.util.GuiResourcesD;
public class MyTableD extends JTable implements FocusListener, MyTable {
private static final long serialVersionUID = 1L;
private int tableMode = MyTable.TABLE_MODE_STANDARD;
public static final int DOT_SIZE = 7;
public static final int LINE_THICKNESS1 = 3;
public static final int LINE_THICKNESS2 = 2;
public static final Color SELECTED_BACKGROUND_COLOR = GColorD.getAwtColor(
GeoGebraColorConstants.TABLE_SELECTED_BACKGROUND_COLOR);
public static final Color SELECTED_BACKGROUND_COLOR_HEADER = GColorD
.getAwtColor(
GeoGebraColorConstants.TABLE_SELECTED_BACKGROUND_COLOR_HEADER);
public static final Color BACKGROUND_COLOR_HEADER = GColorD
.getAwtColor(GeoGebraColorConstants.TABLE_BACKGROUND_COLOR_HEADER);
public static final Color TABLE_GRID_COLOR = GColorD
.getAwtColor(GeoGebraColorConstants.GRAY2);
public static final Color HEADER_GRID_COLOR = GColorD
.getAwtColor(GeoGebraColorConstants.GRAY4);
public static final Color SELECTED_RECTANGLE_COLOR = Color.BLUE;
protected Kernel kernel;
protected AppD app;
protected MyCellEditorSpreadsheet editor;
private MyCellEditorBoolean editorBoolean;
private MyCellEditorButton editorButton;
private MyCellEditorList editorList;
protected RelativeCopy relativeCopy;
public CopyPasteCutD copyPasteCut;
protected SpreadsheetColumnControllerD.ColumnHeaderRenderer headerRenderer;
protected SpreadsheetViewD view;
protected DefaultTableModel tableModel;
private CellRangeProcessor crProcessor;
private MyTableColumnModelListener columnModelListener;
private CellFormat 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
protected int minSelectionRow = -1;
protected int maxSelectionRow = -1;
protected int minSelectionColumn = -1;
protected int maxSelectionColumn = -1;
public boolean[] selectedColumns;
// 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 boolean doShowDragHandle = true;
private Color selectionRectangleColor = SELECTED_RECTANGLE_COLOR;
// Dragging vars
protected boolean isDragingDot = false;
protected int dragingToRow = -1;
protected int dragingToColumn = -1;
protected boolean isOverDot = false;
protected boolean isDragging2 = false;
protected int minColumn2 = -1;
protected int maxColumn2 = -1;
protected int minRow2 = -1;
protected int maxRow2 = -1;
protected boolean isOverDnDRegion = false;
public boolean isOverDnDRegion() {
return isOverDnDRegion;
}
// Keep track of ctrl-down. This is needed in some
// selection methods that do not receive key events.
protected boolean metaDown = false;
// 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;
// Collection of cells that contain geos that can be edited with one click,
// e.g. booleans, buttons, lists
protected HashMap<GPoint, GeoElement> oneClickEditMap = new HashMap<GPoint, GeoElement>();
public HashMap<GPoint, GeoElement> getOneClickEditMap() {
return oneClickEditMap;
}
public void setOneClickEditMap(
HashMap<GPoint, GeoElement> oneClickEditMap) {
this.oneClickEditMap = oneClickEditMap;
}
// 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;
private SpreadsheetController controller;
/*******************************************************************
* Construct table
*/
public MyTableD(SpreadsheetViewD view, DefaultTableModel tableModel) {
super(tableModel);
cellResizeHeightSet = new HashSet<GPoint>();
cellResizeWidthSet = new HashSet<GPoint>();
app = view.getApplication();
kernel = app.getKernel();
this.tableModel = tableModel;
this.view = view;
grabCursor = createCursor(
app.getImageIcon(GuiResourcesD.CURSOR_GRAB).getImage(), true);
grabbingCursor = createCursor(
app.getImageIcon(GuiResourcesD.CURSOR_GRABBING).getImage(),
true);
// set row height
setRowHeight(SpreadsheetSettings.TABLE_CELL_HEIGHT);
// prepare column headers
SpreadsheetColumnControllerD columnController = new SpreadsheetColumnControllerD(
app, this);
headerRenderer = columnController.new ColumnHeaderRenderer();
getTableHeader().setFocusable(true);
getTableHeader().addMouseListener(columnController);
getTableHeader().addMouseMotionListener(columnController);
getTableHeader().addKeyListener(columnController);
getTableHeader().setReorderingAllowed(false);
setAutoCreateColumnsFromModel(false);
// set columns and column headers
setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
headerRenderer.setPreferredSize(new Dimension(preferredColumnWidth,
SpreadsheetSettings.TABLE_CELL_HEIGHT));
for (int i = 0; i < getColumnCount(); ++i) {
getColumnModel().getColumn(i).setHeaderRenderer(headerRenderer);
getColumnModel().getColumn(i)
.setPreferredWidth(preferredColumnWidth);
}
// set visual appearance
setShowGrid(true);
setGridColor(TABLE_GRID_COLOR);
setSelectionBackground(SELECTED_BACKGROUND_COLOR);
setSelectionForeground(Color.BLACK);
// add cell renderer & editors
setDefaultRenderer(Object.class, new MyCellRendererD(this));
editor = new MyCellEditorSpreadsheet(kernel, getEditorController());
setDefaultEditor(Object.class, editor);
// initialize selection fields
selectedCellRanges = new ArrayList<CellRange>();
selectedCellRanges.add(new CellRange(app));
setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
setCellSelectionEnabled(true);
// add mouse and key listeners
SpreadsheetMouseListenerD ml = new SpreadsheetMouseListenerD(app, this);
MouseListener[] mouseListeners = getMouseListeners();
addMouseListener(ml);
for (int i = 0; i < mouseListeners.length; ++i) {
removeMouseListener(mouseListeners[i]);
addMouseListener(mouseListeners[i]);
}
MouseMotionListener[] mouseMotionListeners = getMouseMotionListeners();
addMouseMotionListener(ml);
for (int i = 0; i < mouseMotionListeners.length; ++i) {
removeMouseMotionListener(mouseMotionListeners[i]);
addMouseMotionListener(mouseMotionListeners[i]);
}
// key listener
KeyListener[] defaultKeyListeners = getKeyListeners();
for (int i = 0; i < defaultKeyListeners.length; ++i) {
removeKeyListener(defaultKeyListeners[i]);
}
addKeyListener(new SpreadsheetKeyListenerD(app, this));
// 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
tableModel.addTableModelListener(new MyTableModelListener());
// relative copy
relativeCopy = new RelativeCopy(kernel);
copyPasteCut = new CopyPasteCutD(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);
}
private SpreadsheetController getEditorController() {
if (controller == null) {
controller = new SpreadsheetController(app);
}
return controller;
}
/**
* End table constructor
******************************************************************/
/**
* Simple getter method
*
* @return CopyPasteCut
*/
@Override
public CopyPasteCut getCopyPasteCut() {
return copyPasteCut;
}
/**
* Simple getter method
*
* @return Kernel
*/
@Override
public Kernel getKernel() {
return kernel;
}
/**
* Simple getter method
*
* @return App
*/
@Override
public App getApplication() {
return app;
}
/**
* Returns parent SpreadsheetView for this table
*
* @return SpreadsheetView
*/
@Override
public SpreadsheetViewD getView() {
return view;
}
/**
* 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 MyCellEditorBoolean getEditorBoolean() {
if (editorBoolean == null) {
editorBoolean = new MyCellEditorBoolean(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;
}
/**
* Appends columns to the table if table model column count is larger than
* current number of table columns.
*/
protected void updateColumnCount() {
if (tableModel.getColumnCount() <= this.getColumnCount()) {
return;
}
// ensure that auto-create is off
if (this.getAutoCreateColumnsFromModel()) {
throw new IllegalStateException();
}
// add new columns to table
for (int i = this.getColumnCount(); i < tableModel
.getColumnCount(); ++i) {
TableColumn col = new TableColumn(i);
col.setHeaderRenderer(headerRenderer);
col.setPreferredWidth(preferredColumnWidth);
addColumn(col);
}
// addColumn destroys custom row heights, so we must reset them
resetRowHeights();
}
@Override
public TableCellEditor getCellEditor(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 getEditorBoolean();
case BUTTON:
return getEditorButton();
case LIST:
return getEditorList();
}
}
return editor;
}
public boolean isEnableAutoComplete() {
return editor.isEnableAutoComplete();
}
public void setEnableAutoComplete(boolean enableAutoComplete) {
editor.setEnableAutoComplete(enableAutoComplete);
}
/**
* 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 TableModelListener {
@Override
public void tableChanged(TableModelEvent e) {
// force rowHeader redraw when a new row is added (after drag
// down or arrow down)
if (e.getType() == TableModelEvent.INSERT) {
getView().updateRowHeader();
}
// update table column model if new columns added
if (e.getType() == TableModelEvent.UPDATE) {
updateColumnCount();
}
}
}
// ===============================================================
// Selection
// ===============================================================
@Override
public void changeSelection(int rowIndex, int columnIndex, boolean extend) {
this.changeSelection(rowIndex, columnIndex, false, extend);
}
/**
* JTable does not support non-contiguous cell selection. It treats
* ctrl-down cell selection as if it was shift-extend. To prevent this
* behavior the JTable changeSelection method is overridden here.
*/
@Override
public void changeSelection(int rowIndex, int columnIndex, boolean toggle,
boolean extend) {
// if(Application.getControlDown())
// super.changeSelection(rowIndex, columnIndex, false, false);
// else
// force column selection
if (view.isColumnSelect()) {
setColumnSelectionInterval(columnIndex, columnIndex);
}
super.changeSelection(rowIndex, columnIndex, toggle, extend);
// let selectionChanged know about a change in single cell selection
selectionChanged();
}
@Override
public void selectAll() {
setSelectionType(MyTableInterface.CELL_SELECT);
this.setAutoscrolls(false);
// select the upper left corner cell
changeSelection(0, 0, false, false);
// extend the selection to the current lower right corner cell
changeSelection(getRowCount() - 1, getColumnCount() - 1, false, true);
setSelectAll(true);
this.setAutoscrolls(true);
// this.scrollRectToVisible(getCellRect(0,0,true));
// setRowSelectionInterval(0, getRowCount()-1);
// getColumnModel().getSelectionModel().setSelectionInterval(0,
// getColumnCount()-1);
// selectionChanged();
// this.getSelectAll();
}
/**
* 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);
if (view.isTraceDialogVisible()) {
newSelection = view.getTraceSelectionRange(
getColumnModel().getSelectionModel()
.getAnchorSelectionIndex(),
getSelectionModel().getAnchorSelectionIndex());
scrollRectToVisible(getCellRect(newSelection.getMinRow(),
newSelection.getMaxColumn(), true));
} else {
switch (selectionType) {
default:
case MyTableInterface.CELL_SELECT:
newSelection.setCellRange(
getColumnModel().getSelectionModel()
.getAnchorSelectionIndex(),
getSelectionModel().getAnchorSelectionIndex(),
getColumnModel().getSelectionModel()
.getLeadSelectionIndex(),
getSelectionModel().getLeadSelectionIndex());
break;
case MyTableInterface.ROW_SELECT:
newSelection.setCellRange(-1,
getSelectionModel().getAnchorSelectionIndex(), -1,
getSelectionModel().getLeadSelectionIndex());
break;
case MyTableInterface.COLUMN_SELECT:
newSelection.setCellRange(
getColumnModel().getSelectionModel()
.getAnchorSelectionIndex(),
-1, getColumnModel().getSelectionModel()
.getLeadSelectionIndex(),
-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 (!app.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)){ System.out.println("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);
}
}
// check for change in anchor cell (for now this is minrow and mincol
// ...)
boolean changedAnchor = minSelectionColumn
- newSelection.getMinColumn() != 0
|| minSelectionRow - newSelection.getMinRow() != 0;
// update internal selection variables
newSelection.setActualRange();
minSelectionColumn = newSelection.getMinColumn();
maxSelectionColumn = newSelection.getMaxColumn();
minSelectionRow = newSelection.getMinRow();
maxSelectionRow = newSelection.getMaxRow();
// newSelection.debug();
// printSelectionParameters();
if (isSelectNone && (minSelectionColumn != -1 || minSelectionRow != -1)) {
setSelectNone(false);
}
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();
}
if (view.isVisibleStyleBar()) {
view.getSpreadsheetStyleBar().updateStyleBar();
}
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 the selection has changed or an empty cell has been clicked,
// repaint
if (changed || list.isEmpty()) {
repaint();
if (this.getTableHeader() != null) {
getTableHeader().repaint();
}
}
// System.out.println("------------------");
// for (CellRange cr: selectedCellRanges)cr.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 row, int column) {
setSelectionType(MyTableInterface.CELL_SELECT);
if (column == -1) {
minSelectionColumn = 0;
} else {
minSelectionColumn = column;
}
if (row == -1) {
minSelectionRow = 0;
} else {
minSelectionRow = row;
}
maxSelectionColumn = minSelectionColumn;
maxSelectionRow = minSelectionRow;
getColumnModel().getSelectionModel().setSelectionInterval(
minSelectionColumn, maxSelectionColumn);
getSelectionModel().setSelectionInterval(minSelectionRow,
maxSelectionRow);
}
/*
* 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()) {
getSelectionModel().clearSelection();
} else {
this.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,
false);
changeSelection(cr.getMaxRow(), cr.getMaxColumn(), false,
true);
}
selectionChanged();
// scroll to upper left corner of rectangle
this.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 selType0) {
int selType = selType0;
if (view.isColumnSelect()) {
selType = MyTableInterface.COLUMN_SELECT;
}
switch (selType) {
default:
case MyTableInterface.CELL_SELECT:
setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
setColumnSelectionAllowed(true);
setRowSelectionAllowed(true);
break;
case MyTableInterface.ROW_SELECT:
setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
setColumnSelectionAllowed(false);
setRowSelectionAllowed(true);
break;
case MyTableInterface.COLUMN_SELECT:
setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
setColumnSelectionAllowed(true);
setRowSelectionAllowed(false);
break;
}
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.
@Override
public void setRowSelectionInterval(int row0, int row1) {
setSelectionType(MyTableInterface.ROW_SELECT);
super.setRowSelectionInterval(row0, row1);
selectionChanged();
}
@Override
public void setColumnSelectionInterval(int col0, int col1) {
setSelectionType(MyTableInterface.COLUMN_SELECT);
super.setColumnSelectionInterval(col0, 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);
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 Color getSelectionRectangleColor() {
return selectionRectangleColor;
}
public void setSelectionRectangleColor(Color color) {
selectionRectangleColor = color;
}
protected GPoint getPixel(int column, int row, boolean min) {
if (column < 0 || row < 0) {
return null;
}
if (min && column == 0 && row == 0) {
return new GPoint(0, 0);
}
Rectangle cellRect = getCellRect(row, column, false);
if (min) {
return new GPoint(cellRect.x, cellRect.y);
}
return new GPoint(cellRect.x + cellRect.width,
cellRect.y + cellRect.height);
}
protected GPoint getMinSelectionPixel() {
return getPixel(minSelectionColumn, minSelectionRow, true);
}
protected GPoint getMaxSelectionPixel() {
return getPixel(maxSelectionColumn, maxSelectionRow, false);
}
/**
* 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 indexX = -1;
int indexY = -1;
for (int i = 0; i < getColumnCount(); ++i) {
GPoint point = getPixel(i, 0, false);
if (x < point.getX()) {
indexX = i;
break;
}
}
if (indexX == -1) {
return null;
}
for (int i = 0; i < getRowCount(); ++i) {
GPoint point = getPixel(0, i, false);
if (y < point.getY()) {
indexY = i;
break;
}
}
if (indexY == -1) {
return null;
}
return new GPoint(indexX, indexY);
}
public Rectangle getCellBlockRect(int column1, int row1, int column2,
int row2, boolean includeSpacing) {
Rectangle r1 = getCellRect(row1, column1, includeSpacing);
Rectangle r2 = getCellRect(row2, column2, includeSpacing);
r1.setBounds(r1.x, r1.y, (r2.x - r1.x) + r2.width,
(r2.y - r1.y) + r2.height);
return r1;
}
public Rectangle getSelectionRect(boolean includeSpacing) {
return getCellBlockRect(minSelectionColumn, minSelectionRow,
maxSelectionColumn, maxSelectionRow, includeSpacing);
}
// target selection frame
// =============================
private Rectangle targetcellFrame;
public Rectangle getTargetcellFrame() {
return targetcellFrame;
}
public void setTargetcellFrame(Rectangle targetcellFrame) {
this.targetcellFrame = targetcellFrame;
}
final static float dash1[] = { 2.0f };
final static BasicStroke dashed = new BasicStroke(3.0f,
BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, dash1, 0.0f);
public boolean showCanDragBlueDot() {
boolean showBlueDot = !editor.isEditing()
&& !view.isTraceDialogVisible();
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;
}
// ===============================================================
// Paint
// ===============================================================
/**
* Overrides the paint() to draw special spreadsheet table graphics, e.g.
* selection rectangle and custom borders
*/
@Override
public void paint(Graphics graphics) {
super.paint(graphics);
Graphics2D g2 = (Graphics2D) graphics;
// draw custom borders
SpreadsheetBorders.drawFormatBorders(g2, this);
// draw special target cell frame
if (targetcellFrame != null) {
g2.setColor(GColorD.getAwtColor(GeoGebraColorConstants.DARKBLUE));
g2.setStroke(dashed);
g2.draw(targetcellFrame);
}
// if the spreadsheet doesn't have focus
// then don't draw the selection graphics ... exit now
if (!view.hasViewFocus()) {
if (!isSelectNone) {
setSelectNone(true);
}
return;
}
// draw special dragging frame for cell editor
if (isDragging2) {
GPoint point1 = getPixel(minColumn2, minRow2, true);
GPoint point2 = getPixel(maxColumn2, maxRow2, false);
int x1 = point1.getX();
int y1 = point1.getY();
int x2 = point2.getX();
int y2 = point2.getY();
graphics.setColor(Color.GRAY);
// Application.debug(x1 + "," + y1 + "," + x2 + "," + y2);
graphics.fillRect(x1, y1, x2 - x1, LINE_THICKNESS1);
graphics.fillRect(x1, y1, LINE_THICKNESS1, y2 - y1);
graphics.fillRect(x1, y2 - LINE_THICKNESS1, x2 - x1,
LINE_THICKNESS1);
graphics.fillRect(x2 - LINE_THICKNESS1, y1, LINE_THICKNESS1,
y2 - y1);
}
// draw dragging frame
if (dragingToRow != -1 && dragingToColumn != -1) {
/*
* Application.debug("minSelectionRow = " + minSelectionRow);
* Application.debug("minSelectionColumn = " + minSelectionColumn);
* Application.debug("maxSelectionRow = " + maxSelectionRow);
* Application.debug("maxSelectionColumn = " + maxSelectionColumn);
* Application.debug("dragingToRow = " + dragingToRow);
* Application.debug("dragingToColumn = " + dragingToColumn); /*
*/
// -|1|-
// 2|-|3
// -|4|-
graphics.setColor(Color.gray);
if (dragingToColumn < minSelectionColumn) { // 2
GPoint point1 = getPixel(dragingToColumn, minSelectionRow,
true);
GPoint point2 = getPixel(minSelectionColumn - 1,
maxSelectionRow, false);
int x1 = point1.getX();
int y1 = point1.getY();
int x2 = point2.getX();
int y2 = point2.getY();
graphics.fillRect(x1, y1, x2 - x1, LINE_THICKNESS1);
graphics.fillRect(x1, y1, LINE_THICKNESS1, y2 - y1);
graphics.fillRect(x1, y2 - LINE_THICKNESS1, x2 - x1,
LINE_THICKNESS1);
} else if (dragingToRow > maxSelectionRow) { // 4
GPoint point1 = getPixel(minSelectionColumn,
maxSelectionRow + 1, true);
GPoint point2 = getPixel(maxSelectionColumn, dragingToRow,
false);
int x1 = point1.getX();
int y1 = point1.getY();
int x2 = point2.getX();
int y2 = point2.getY();
graphics.fillRect(x1, y1, LINE_THICKNESS1, y2 - y1);
graphics.fillRect(x1, y2 - LINE_THICKNESS1, x2 - x1,
LINE_THICKNESS1);
graphics.fillRect(x2 - LINE_THICKNESS1, y1, LINE_THICKNESS1,
y2 - y1);
} else if (dragingToRow < minSelectionRow) { // 1
GPoint point1 = getPixel(minSelectionColumn, dragingToRow,
true);
GPoint point2 = getPixel(maxSelectionColumn,
minSelectionRow - 1, false);
int x1 = point1.getX();
int y1 = point1.getY();
int x2 = point2.getX();
int y2 = point2.getY();
graphics.fillRect(x1, y1, x2 - x1, LINE_THICKNESS1);
graphics.fillRect(x1, y1, LINE_THICKNESS1, y2 - y1);
graphics.fillRect(x2 - LINE_THICKNESS1, y1, LINE_THICKNESS1,
y2 - y1);
} else if (dragingToColumn > maxSelectionColumn) { // 3
GPoint point1 = getPixel(maxSelectionColumn + 1,
minSelectionRow, true);
GPoint point2 = getPixel(dragingToColumn, maxSelectionRow,
false);
int x1 = point1.getX();
int y1 = point1.getY();
int x2 = point2.getX();
int y2 = point2.getY();
graphics.fillRect(x2 - LINE_THICKNESS1, y1, LINE_THICKNESS1,
y2 - y1);
graphics.fillRect(x1, y2 - LINE_THICKNESS1, x2 - x1,
LINE_THICKNESS1);
graphics.fillRect(x1, y1, x2 - x1, LINE_THICKNESS1);
}
}
// draw dragging dot
GPoint pixel1 = getMaxSelectionPixel();
if (doShowDragHandle && pixel1 != null && !editor.isEditing()) {
if (showCanDragBlueDot()) {
// Highlight the dragging dot if mouseover
if (isOverDot) {
graphics.setColor(Color.gray);
} else
// {graphics.setColor(Color.BLUE);}
{
graphics.setColor(selectionRectangleColor);
}
int x = pixel1.getX() - (DOT_SIZE + 1) / 2;
int y = pixel1.getY() - (DOT_SIZE + 1) / 2;
graphics.fillRect(x, y, DOT_SIZE, DOT_SIZE);
}
}
if (minSelectionRow != -1 && maxSelectionRow != -1
&& minSelectionColumn != -1 && maxSelectionColumn != -1) {
GPoint min = this.getMinSelectionPixel();
GPoint max = this.getMaxSelectionPixel();
int x1 = min.getX();
int y1 = min.getY();
int x2 = max.getX();
int y2 = max.getY();
// graphics.setColor(Color.BLUE);
graphics.setColor(selectionRectangleColor);
// draw frame around current selection
// G.Sturr 2009-9-23 adjusted parameters to work with getPixel fix
if (!editor.isEditing()) {
graphics.fillRect(x1, y1, x2 - x1, LINE_THICKNESS2);
graphics.fillRect(x1, y1, LINE_THICKNESS2, y2 - y1);
graphics.fillRect(x2 - LINE_THICKNESS2, y1, LINE_THICKNESS2,
y2 - y1);
graphics.fillRect(x1, y2 - LINE_THICKNESS2, x2 - x1,
LINE_THICKNESS2);
}
// draw small frame around current editing cell
else {
x1 -= LINE_THICKNESS2 - 1;
x2 += LINE_THICKNESS2 - 1;
y1 -= LINE_THICKNESS2 - 1;
y2 += LINE_THICKNESS2 - 1;
graphics.fillRect(x1, y1, x2 - x1, LINE_THICKNESS2);
graphics.fillRect(x1, y1, LINE_THICKNESS2, y2 - y1);
graphics.fillRect(x2 - LINE_THICKNESS2, y1, LINE_THICKNESS2,
y2 - y1);
graphics.fillRect(x1, y2 - LINE_THICKNESS2, x2 - x1,
LINE_THICKNESS2);
}
}
// After rendering the LaTeX image for a geo, update the row height
// with the preferred size set by the renderer.
resizeMarkedCells();
}
/**
* 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(int row, int col) {
Object ob = 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
return super.editCellAt(row, col);
}
// This handles ctrl-select dragging of cell blocks
// because JTable does not do this correctly.
// TODO: JTable is still making selections that are not overridden,
// so sometimes you can still get unwanted extended selection.
//
protected void handleControlDragSelect(MouseEvent e) {
Point p = e.getPoint();
int row = this.rowAtPoint(p);
int column = this.columnAtPoint(p);
ListSelectionModel cm = getColumnModel().getSelectionModel();
ListSelectionModel rm = getSelectionModel();
/*
* //handle startup case of empty selection if ((column == -1) && (row
* == -1)){ cm.setSelectionInterval(0, 0); rm.setSelectionInterval(0,
* 0); }
*/
if ((column == -1) || (row == -1)) {
return;
}
// adjust the selection if mouse has left the old selected cell
if (row != this.getSelectedRow()
|| column != this.getSelectedColumn()) {
// boolean selected = true;
int colAnchor = cm.getAnchorSelectionIndex();
int rowAnchor = rm.getAnchorSelectionIndex();
if (rowAnchor == -1 || rowAnchor >= getRowCount()) {
rowAnchor = 0;
// selected = false;
}
if (colAnchor == -1 || colAnchor >= getColumnCount()) {
colAnchor = 0;
// selected = false;
}
// selected = selected && isCellSelected(rowAnchor, colAnchor);
cm.setSelectionInterval(colAnchor, column);
rm.setSelectionInterval(rowAnchor, row);
selectionChanged();
}
}
@Override
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
*/
@Override
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);
}
}
@Override
public void focusGained(FocusEvent e) {
if (AppD.isVirtualKeyboardActive()) {
((GuiManagerD) app.getGuiManager()).toggleKeyboard(true);
}
}
@Override
public void focusLost(FocusEvent e) {
// avoid infinite loop!
if (e.getOppositeComponent() instanceof VirtualKeyboardD) {
return;
}
if (AppD.isVirtualKeyboardActive()) {
((GuiManagerD) app.getGuiManager()).toggleKeyboard(false);
}
}
// Keep row heights of table and rowHeader in sync
@Override
public void setRowHeight(int row, int rowHeight) {
super.setRowHeight(row, rowHeight);
try {
if (view != null) {
view.updateRowHeader();
if (doRecordRowHeights) {
adjustedRowHeights.add(new GPoint(row, rowHeight));
}
view.updateRowHeightSetting(row, rowHeight);
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void setRowHeight(int rowHeight) {
super.setRowHeight(rowHeight);
try {
if (view != null) {
view.updateRowHeader();
view.updatePreferredRowHeight(rowHeight);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 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) {
Dimension prefSize = this
.getCellRenderer(row, col).getTableCellRendererComponent(this,
this.getValueAt(row, col), false, false, row, col)
.getPreferredSize();
if (adjustWidth) {
TableColumn tableColumn = this.getColumnModel().getColumn(col);
int resultWidth = Math.max(tableColumn.getWidth(),
(int) prefSize.getWidth());
tableColumn
.setWidth(resultWidth + this.getIntercellSpacing().width);
}
if (adjustHeight) {
int resultHeight = Math.max(getRowHeight(row),
(int) prefSize.getHeight());
setRowHeight(row, resultHeight);
}
}
/**
* Adjust the width of a column to fit the maximum preferred width of its
* cell contents.
*/
public void fitColumn(int column) {
TableColumn tableColumn = getColumnModel().getColumn(column);
int prefWidth = 0;
int tempWidth = -1;
for (int row = 0; row < getRowCount(); row++) {
if (getValueAt(row, column) != null) {
tempWidth = (int) getCellRenderer(row, column)
.getTableCellRendererComponent(this,
getValueAt(row, column), false, false, row,
column)
.getPreferredSize().getWidth();
prefWidth = Math.max(prefWidth, tempWidth);
}
}
// set the new column width
if (tempWidth == -1) {
// column is empty
prefWidth = preferredColumnWidth - getIntercellSpacing().width;
} else {
prefWidth = Math.max(prefWidth, tableColumn.getMinWidth());
}
// note: the table might have its header set to null,
// so we get the actual header from view
view.getTableHeader().setResizingColumn(tableColumn);
tableColumn.setWidth(prefWidth + getIntercellSpacing().width);
}
/**
* Adjust the height of a row to fit the maximum preferred height of the its
* cell contents.
*/
public void fitRow(int row) {
int prefHeight = this.getRowHeight();
int tempHeight = 0;
for (int column = 0; column < this.getColumnCount(); column++) {
tempHeight = (int) this.getCellRenderer(row, column)
.getTableCellRendererComponent(this,
getValueAt(row, column), false, false, row, column)
.getPreferredSize().getHeight();
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) {
Log.debug("MyTableD.fitAll is only partly implemented");
// for (int column = 0; column < getColumnCount(); column++) {
// TODO:test//fitColumn(column);
// }
}
}
/**
* Column model listener --- used to reset the preferred column width when
* all columns have been selected.
*/
public class MyTableColumnModelListener
implements TableColumnModelListener {
@Override
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();
}
@Override
public void columnAdded(TableColumnModelEvent arg0) {
// only care about margin
}
@Override
public void columnMoved(TableColumnModelEvent arg0) {
// only care about margin
}
@Override
public void columnRemoved(TableColumnModelEvent arg0) {
// only care about margin
}
@Override
public void columnSelectionChanged(ListSelectionEvent arg0) {
// only care about margin
}
}
// 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(Color.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
targetcellFrame = this.getCellBlockRect(minSelectionColumn,
minSelectionRow, minSelectionColumn, minSelectionRow, true);
// Change the selection frame color to gray
// and clear the current selection
setSelectionRectangleColor(Color.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), app.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 void copy(boolean altDown) {
copyPasteCut.copy(minSelectionColumn, minSelectionRow,
maxSelectionColumn, maxSelectionRow, altDown);
}
public boolean paste() {
return copyPasteCut.paste(minSelectionColumn, minSelectionRow,
maxSelectionColumn, maxSelectionRow);
}
public boolean cut() {
return copyPasteCut.cut(minSelectionColumn, minSelectionRow,
maxSelectionColumn, maxSelectionRow);
}
public boolean delete() {
return copyPasteCut.delete(minSelectionColumn, minSelectionRow,
maxSelectionColumn, maxSelectionRow);
}
private static Cursor createCursor(Image cursorImage, boolean center) {
Toolkit toolkit = Toolkit.getDefaultToolkit();
Point cursorHotSpot;
if (center) {
cursorHotSpot = new Point(cursorImage.getWidth(null) / 2,
cursorImage.getHeight(null) / 2);
} else {
cursorHotSpot = new Point(0, 0);
}
Cursor cursor = toolkit.createCustomCursor(cursorImage, cursorHotSpot,
null);
return cursor;
}
@Override
public void updateCellFormat(String cellFormat) {
view.updateCellFormat(cellFormat);
}
@Override
public boolean allowSpecialEditor() {
return view.allowSpecialEditor();
}
@Override
public void updateTableCellValue(Object value, int i, int j) {
// only used in Web
}
@Override
public void repaintAll() {
repaint();
// method for web, do nothing else here
}
public SpreadsheetModeProcessor getSpreadsheetModeProcessor() {
if (this.spredsheetModeProcessor == null) {
this.spredsheetModeProcessor = new SpreadsheetModeProcessor(app,
this);
}
return this.spredsheetModeProcessor;
}
}