package org.geogebra.common.main;
import org.geogebra.common.awt.GPoint;
import org.geogebra.common.kernel.ModeSetter;
import org.geogebra.common.kernel.UpdateLocationView;
import org.geogebra.common.kernel.geos.GProperty;
import org.geogebra.common.kernel.geos.GeoElement;
/**
* Abstract class for managing spreadsheet GeoElement cells in a table model
* that supports the spreadsheet.
*
* The View interface is implemented so that the model can adapt when
* GeoElements with spreadsheet labels (e.g. A1) are changed.
*
* @author G. Sturr
*
*/
public abstract class SpreadsheetTableModel implements UpdateLocationView {
protected App app;
private int highestUsedColumn = -1;
private int highestUsedRow = -1;
/** tells that it's initing */
protected boolean isIniting = true;
/**
* maintains a list of all AlgoCellRanges in the construction and handles
* updates to the cell range lists as cells are added or removed
*/
private AlgoCellRangeManager cellRangeManager;
/***************************************************
* Constructor
*
* @param app
* ggb Application
*/
public SpreadsheetTableModel(App app) {
isIniting = true;
this.app = app;
cellRangeManager = new AlgoCellRangeManager();
}
/**************************************************
* Abstract Methods
*/
public abstract int getRowCount();
public abstract int getColumnCount();
public abstract void setRowCount(int rowCount);
public abstract void setColumnCount(int columnCount);
public abstract Object getValueAt(int row, int column);
public abstract void setValueAt(Object value, int row, int column);
/*
* ************************************************
*/
/**
* Attach to the kernel
*/
public void attachView() {
app.getKernel().notifyAddAll(this);
app.getKernel().attach(this);
}
/**
* Detach from the kernel
*/
public void detachView() {
app.getKernel().detach(this);
}
/**
* Returns index of highest used column. Returns -1 if empty spreadsheet.
*/
public int getHighestUsedColumn() {
return highestUsedColumn;
}
/**
* Returns index of highest used row. Returns -1 if empty spreadsheet.
*/
public int getHighestUsedRow() {
return highestUsedRow;
}
/**
* Updates highestUsedColumn and highestUsedRow
*/
private void updateHighestUsedColAndRow(int col, int row) {
if (col == highestUsedColumn) {
boolean updatedHighestUsedColumn = false;
for (int c = highestUsedColumn; c >= 0; c--) {
boolean columnEmpty = true;
for (int r = 0; r <= highestUsedRow; r++) {
if (getValueAt(r, c) != null) {
// column not empty
columnEmpty = false;
break;
}
}
if (!columnEmpty) {
highestUsedColumn = c;
updatedHighestUsedColumn = true;
break;
}
}
if (!updatedHighestUsedColumn) {
highestUsedColumn = -1;
}
}
if (row == highestUsedRow) {
boolean updatedHighestUsedRow = false;
for (int r = highestUsedRow; r >= 0; r--) {
boolean rowEmpty = true;
for (int c = 0; c <= highestUsedColumn; c++) {
if (getValueAt(r, c) != null) {
// row not empty
rowEmpty = false;
break;
}
}
if (!rowEmpty) {
highestUsedRow = r;
updatedHighestUsedRow = true;
break;
}
}
if (!updatedHighestUsedRow) {
highestUsedRow = -1;
}
}
}
/**
* @return cellRangeManager for handling AlgoCellRange updates
*/
public AlgoCellRangeManager getCellRangeManager() {
return cellRangeManager;
}
// ====================================
// VIEW implementation
// ====================================
@Override
public void add(GeoElement geo) {
update(geo);
addToCellRangeAlgos(geo);
}
private void addWithoutTrace(GeoElement geo) {
updateWithoutTrace(geo);
addToCellRangeAlgos(geo);
}
@Override
public void remove(GeoElement geo) {
GPoint location = geo.getSpreadsheetCoords();
if (location != null) {
doRemove(location.y, location.x);
cellRangeManager.updateCellRangeAlgos(geo, location, true);
}
}
@Override
public void rename(GeoElement geo) {
GPoint location = geo.getOldSpreadsheetCoords();
if (location != null) {
doRemove(location.y, location.x);
cellRangeManager.updateCellRangeAlgos(geo, location, true);
}
addWithoutTrace(geo);
}
private void doRemove(int row, int col) {
setValueAt(null, row, col);
updateHighestUsedColAndRow(col, row);
}
@Override
public void update(GeoElement geo) {
if (geo.isEmptySpreadsheetCell() && geo.isDefined()) {
geo.setEmptySpreadsheetCell(false);
}
updateWithoutTrace(geo);
// trace value
if (!isIniting && geo.getSpreadsheetTrace()) {
app.getTraceManager().traceToSpreadsheet(geo);
}
}
private void addToCellRangeAlgos(GeoElement geo) {
GPoint location = geo.getSpreadsheetCoords();
if (location != null) {
cellRangeManager.addToCellRangeAlgos(geo, location);
}
}
private void updateWithoutTrace(GeoElement geo) {
GPoint location = geo.getSpreadsheetCoords();
if (location != null
&& location.x < app.getMaxSpreadsheetColumnsVisible()
&& location.y < app.getMaxSpreadsheetRowsVisible()) {
highestUsedColumn = Math.max(highestUsedColumn, location.x);
highestUsedRow = Math.max(highestUsedRow, location.y);
if (location.y >= getRowCount()) {
setRowCount(location.y + 1);
}
if (location.x >= getColumnCount()) {
// table.setMyColumnCount(location.x + 1);
// JViewport cH = spreadsheet.getColumnHeader();
// bugfix: double-click to load ggb file gives cH = null
// if (cH != null) cH.revalidate();
}
setValueAt(geo, location.y, location.x);
/*
* DONE ELSEWHERE // add tracing geos to the trace collection if
* (!isIniting && geo.getSpreadsheetTrace()) {
* app.getTraceManager().addSpreadsheetTraceGeo(geo); }
*/
}
}
@Override
public void updateLocation(GeoElement geo) {
updateWithoutTrace(geo);
}
@Override
public void clearView() {
for (int c = 0; c < getColumnCount(); ++c) {
for (int r = 0; r < getRowCount(); ++r) {
setValueAt(null, r, c);
}
}
highestUsedColumn = -1;
highestUsedRow = -1;
cellRangeManager.removeAll();
}
@Override
public void updateVisualStyle(GeoElement geo, GProperty prop) {
updateWithoutTrace(geo);
}
@Override
public void updatePreviewFromInputBar(GeoElement[] geos) {
// not used
}
@Override
public void updateAuxiliaryObject(GeoElement geo) {
// ignore
}
@Override
public void repaintView() {
// ignore
}
@Override
public void reset() {
// ignore
}
@Override
public void setMode(int mode, ModeSetter m) {
// ignore
}
@Override
public int getViewID() {
return App.VIEW_TABLE_MODEL;
}
@Override
public void startBatchUpdate() {
// TODO Auto-generated method stub
}
@Override
public void endBatchUpdate() {
// TODO Auto-generated method stub
}
}