Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Jexer - Java Text User Interface
*
* The MIT License (MIT)
*
* Copyright (C) 2019 Kevin Lamonte
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* @author Kevin Lamonte [[email protected]]
* @version 1
*/
package jexer;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import jexer.bits.CellAttributes;
import jexer.bits.StringUtils;
import jexer.event.TKeypressEvent;
import jexer.event.TMouseEvent;
import jexer.event.TResizeEvent;
import static jexer.TKeypress.*;
/**
* TTableWidget is used to display and edit regular two-dimensional tables of
* cells.
*
* This class was inspired by a TTable implementation originally developed by
* David "Niki" ROULET [[email protected]], made available under MIT at
* https://github.com/nikiroo/jexer/tree/ttable_pull.
*/
public class TTableWidget extends TWidget {
// ------------------------------------------------------------------------
// Constants --------------------------------------------------------------
// ------------------------------------------------------------------------
/**
* Available borders for cells.
*/
public enum Border {
/**
* No border.
*/
NONE,
/**
* Single bar: \u2502 (vertical) and \u2500 (horizontal).
*/
SINGLE,
/**
* Double bar: \u2551 (vertical) and \u2550 (horizontal).
*/
DOUBLE,
/**
* Thick bar: \u2503 (vertical heavy) and \u2501 (horizontal heavy).
*/
THICK,
}
/**
* If true, put a grid of numbers in the cells.
*/
private static final boolean DEBUG = false;
/**
* Row label width.
*/
private static final int ROW_LABEL_WIDTH = 8;
/**
* Column label height.
*/
private static final int COLUMN_LABEL_HEIGHT = 1;
/**
* Column default width.
*/
private static final int COLUMN_DEFAULT_WIDTH = 8;
/**
* Extra rows to add.
*/
private static final int EXTRA_ROWS = (DEBUG ? 10 : 0);
/**
* Extra columns to add.
*/
private static final int EXTRA_COLUMNS = (DEBUG ? 3 : 0);
// ------------------------------------------------------------------------
// Variables --------------------------------------------------------------
// ------------------------------------------------------------------------
/**
* The underlying data, organized as columns.
*/
private ArrayList columns = new ArrayList();
/**
* The underlying data, organized as rows.
*/
private ArrayList rows = new ArrayList();
/**
* The row in model corresponding to the top-left visible cell.
*/
private int top = 0;
/**
* The column in model corresponding to the top-left visible cell.
*/
private int left = 0;
/**
* The row in model corresponding to the currently selected cell.
*/
private int selectedRow = 0;
/**
* The column in model corresponding to the currently selected cell.
*/
private int selectedColumn = 0;
/**
* If true, highlight the entire row of the currently-selected cell.
*/
private boolean highlightRow = false;
/**
* If true, highlight the entire column of the currently-selected cell.
*/
private boolean highlightColumn = false;
/**
* If true, show the row labels as the first column.
*/
private boolean showRowLabels = true;
/**
* If true, show the column labels as the first row.
*/
private boolean showColumnLabels = true;
/**
* The top border for the first row.
*/
private Border topBorder = Border.NONE;
/**
* The left border for the first column.
*/
private Border leftBorder = Border.NONE;
/**
* Column represents a column of cells.
*/
public class Column {
/**
* X position of this column.
*/
private int x = 0;
/**
* Width of column.
*/
private int width = COLUMN_DEFAULT_WIDTH;
/**
* The cells of this column.
*/
private ArrayList cells = new ArrayList();
/**
* Column label.
*/
private String label = "";
/**
* The right border for this column.
*/
private Border rightBorder = Border.NONE;
/**
* Constructor sets label to lettered column.
*
* @param col column number to use for this column. Column 0 will be
* "A", column 1 will be "B", column 26 will be "AA", and so on.
*/
Column(int col) {
label = makeColumnLabel(col);
}
/**
* Add an entry to this column.
*
* @param cell the cell to add
*/
public void add(final Cell cell) {
cells.add(cell);
}
/**
* Get an entry from this column.
*
* @param row the entry index to get
* @return the cell at row
*/
public Cell get(final int row) {
return cells.get(row);
}
/**
* Get the X position of the cells in this column.
*
* @return the position
*/
public int getX() {
return x;
}
/**
* Set the X position of the cells in this column.
*
* @param x the position
*/
public void setX(final int x) {
for (Cell cell: cells) {
cell.setX(x);
}
this.x = x;
}
}
/**
* Row represents a row of cells.
*/
public class Row {
/**
* Y position of this row.
*/
private int y = 0;
/**
* Height of row.
*/
private int height = 1;
/**
* The cells of this row.
*/
private ArrayList cells = new ArrayList();
/**
* Row label.
*/
private String label = "";
/**
* The bottom border for this row.
*/
private Border bottomBorder = Border.NONE;
/**
* Constructor sets label to numbered row.
*
* @param row row number to use for this row
*/
Row(final int row) {
label = Integer.toString(row);
}
/**
* Add an entry to this column.
*
* @param cell the cell to add
*/
public void add(final Cell cell) {
cells.add(cell);
}
/**
* Get an entry from this row.
*
* @param column the entry index to get
* @return the cell at column
*/
public Cell get(final int column) {
return cells.get(column);
}
/**
* Get the Y position of the cells in this column.
*
* @return the position
*/
public int getY() {
return y;
}
/**
* Set the Y position of the cells in this column.
*
* @param y the position
*/
public void setY(final int y) {
for (Cell cell: cells) {
cell.setY(y);
}
this.y = y;
}
}
/**
* Cell represents an editable cell in the table. Normally, navigation
* to a cell only highlights it; pressing Enter or F2 will switch to
* editing mode.
*/
public class Cell extends TWidget {
// --------------------------------------------------------------------
// Variables ----------------------------------------------------------
// --------------------------------------------------------------------
/**
* The field containing the cell's data.
*/
private TField field;
/**
* The column of this cell.
*/
private int column;
/**
* The row of this cell.
*/
private int row;
/**
* If true, the cell is being edited.
*/
private boolean isEditing = false;
/**
* If true, the cell is read-only (non-editable).
*/
private boolean readOnly = false;
/**
* Text of field before editing.
*/
private String fieldText;
// --------------------------------------------------------------------
// Constructors -------------------------------------------------------
// --------------------------------------------------------------------
/**
* Public constructor.
*
* @param parent parent widget
* @param x column relative to parent
* @param y row relative to parent
* @param width width of widget
* @param height height of widget
* @param column column index of this cell
* @param row row index of this cell
*/
public Cell(final TTableWidget parent, final int x, final int y,
final int width, final int height, final int column,
final int row) {
super(parent, x, y, width, height);
this.column = column;
this.row = row;
field = addField(0, 0, width, false);
field.setEnabled(false);
field.setBackgroundChar(' ');
}
// --------------------------------------------------------------------
// Event handlers -----------------------------------------------------
// --------------------------------------------------------------------
/**
* Handle mouse double-click events.
*
* @param mouse mouse double-click event
*/
@Override
public void onMouseDoubleClick(final TMouseEvent mouse) {
// Use TWidget's code to pass the event to the children.
super.onMouseDown(mouse);
// Double-click means to start editing.
fieldText = field.getText();
isEditing = true;
field.setEnabled(true);
activate(field);
if (isActive()) {
// Let the table know that I was activated.
((TTableWidget) getParent()).selectedRow = row;
((TTableWidget) getParent()).selectedColumn = column;
((TTableWidget) getParent()).alignGrid();
}
}
/**
* Handle mouse press events.
*
* @param mouse mouse button press event
*/
@Override
public void onMouseDown(final TMouseEvent mouse) {
// Use TWidget's code to pass the event to the children.
super.onMouseDown(mouse);
if (isActive()) {
// Let the table know that I was activated.
((TTableWidget) getParent()).selectedRow = row;
((TTableWidget) getParent()).selectedColumn = column;
((TTableWidget) getParent()).alignGrid();
}
}
/**
* Handle mouse release events.
*
* @param mouse mouse button release event
*/
@Override
public void onMouseUp(final TMouseEvent mouse) {
// Use TWidget's code to pass the event to the children.
super.onMouseDown(mouse);
if (isActive()) {
// Let the table know that I was activated.
((TTableWidget) getParent()).selectedRow = row;
((TTableWidget) getParent()).selectedColumn = column;
((TTableWidget) getParent()).alignGrid();
}
}
/**
* Handle keystrokes.
*
* @param keypress keystroke event
*/
@Override
public void onKeypress(final TKeypressEvent keypress) {
// System.err.println("Cell onKeypress: " + keypress);
if (readOnly) {
// Read only: do nothing.
return;
}
if (isEditing) {
if (keypress.equals(kbEsc)) {
// ESC cancels the edit.
cancelEdit();
return;
}
if (keypress.equals(kbEnter)) {
// Enter ends editing.
// Pass down to field first so that it can execute
// enterAction if specified.
super.onKeypress(keypress);
fieldText = field.getText();
isEditing = false;
field.setEnabled(false);
return;
}
// Pass down to field.
super.onKeypress(keypress);
}
if (keypress.equals(kbEnter) || keypress.equals(kbF2)) {
// Enter or F2 starts editing.
fieldText = field.getText();
isEditing = true;
field.setEnabled(true);
activate(field);
return;
}
}
// --------------------------------------------------------------------
// TWidget ------------------------------------------------------------
// --------------------------------------------------------------------
/**
* Draw this cell.
*/
@Override
public void draw() {
TTableWidget table = (TTableWidget) getParent();
if (isAbsoluteActive()) {
if (isEditing) {
field.setActiveColorKey("tfield.active");
field.setInactiveColorKey("tfield.inactive");
} else {
field.setActiveColorKey("ttable.selected");
field.setInactiveColorKey("ttable.selected");
}
} else if (((table.selectedColumn == column)
&& ((table.selectedRow == row)
|| (table.highlightColumn == true)))
|| ((table.selectedRow == row)
&& ((table.selectedColumn == column)
|| (table.highlightRow == true)))
) {
field.setActiveColorKey("ttable.active");
field.setInactiveColorKey("ttable.active");
} else {
field.setActiveColorKey("ttable.active");
field.setInactiveColorKey("ttable.inactive");
}
assert (isVisible() == true);
super.draw();
}
// --------------------------------------------------------------------
// TTable.Cell --------------------------------------------------------
// --------------------------------------------------------------------
/**
* Get field text.
*
* @return field text
*/
public final String getText() {
return field.getText();
}
/**
* Set field text.
*
* @param text the new field text
*/
public void setText(final String text) {
field.setText(text);
}
/**
* Cancel any pending edit.
*/
public void cancelEdit() {
// Cancel any pending edit.
if (fieldText != null) {
field.setText(fieldText);
}
isEditing = false;
field.setEnabled(false);
}
/**
* Set an entire column of cells read-only (non-editable) or not.
*
* @param readOnly if true, the cells will be non-editable
*/
public void setReadOnly(final boolean readOnly) {
cancelEdit();
this.readOnly = readOnly;
}
}
// ------------------------------------------------------------------------
// Constructors -----------------------------------------------------------
// ------------------------------------------------------------------------
/**
* Public constructor.
*
* @param parent parent widget
* @param x column relative to parent
* @param y row relative to parent
* @param width width of widget
* @param height height of widget
* @param gridColumns number of columns in grid
* @param gridRows number of rows in grid
*/
public TTableWidget(final TWidget parent, final int x, final int y,
final int width, final int height, final int gridColumns,
final int gridRows) {
super(parent, x, y, width, height);
/*
System.err.println("gridColumns " + gridColumns +
" gridRows " + gridRows);
*/
if (gridColumns < 1) {
throw new IllegalArgumentException("Column count cannot be less " +
"than 1");
}
if (gridRows < 1) {
throw new IllegalArgumentException("Row count cannot be less " +
"than 1");
}
// Initialize the starting row and column.
rows.add(new Row(0));
columns.add(new Column(0));
assert (rows.get(0).height == 1);
// Place a grid of cells that fit in this space.
for (int row = 0; row < gridRows; row++) {
for (int column = 0; column < gridColumns; column++) {
Cell cell = new Cell(this, 0, 0, COLUMN_DEFAULT_WIDTH, 1,
column, row);
if (DEBUG) {
// For debugging: set a grid of cell index labels.
cell.setText("" + row + " " + column);
}
rows.get(row).add(cell);
columns.get(column).add(cell);
if (columns.size() < gridColumns) {
columns.add(new Column(column + 1));
}
}
if (row < gridRows - 1) {
rows.add(new Row(row + 1));
}
}
for (int i = 0; i < rows.size(); i++) {
rows.get(i).setY(i + (showColumnLabels ? COLUMN_LABEL_HEIGHT : 0));
}
for (int j = 0; j < columns.size(); j++) {
columns.get(j).setX((j * (COLUMN_DEFAULT_WIDTH + 1)) +
(showRowLabels ? ROW_LABEL_WIDTH : 0));
}
activate(columns.get(selectedColumn).get(selectedRow));
alignGrid();
}
/**
* Public constructor.
*
* @param parent parent widget
* @param x column relative to parent
* @param y row relative to parent
* @param width width of widget
* @param height height of widget
*/
public TTableWidget(final TWidget parent, final int x, final int y,
final int width, final int height) {
this(parent, x, y, width, height,
width / (COLUMN_DEFAULT_WIDTH + 1) + EXTRA_COLUMNS,
height + EXTRA_ROWS);
}
// ------------------------------------------------------------------------
// Event handlers ---------------------------------------------------------
// ------------------------------------------------------------------------
/**
* Handle mouse press events.
*
* @param mouse mouse button press event
*/
@Override
public void onMouseDown(final TMouseEvent mouse) {
if (mouse.isMouseWheelUp() || mouse.isMouseWheelDown()) {
// Treat wheel up/down as 3 up/down
TKeypressEvent keyEvent;
if (mouse.isMouseWheelUp()) {
keyEvent = new TKeypressEvent(kbUp);
} else {
keyEvent = new TKeypressEvent(kbDown);
}
for (int i = 0; i < 3; i++) {
onKeypress(keyEvent);
}
return;
}
// Use TWidget's code to pass the event to the children.
super.onMouseDown(mouse);
}
/**
* Handle keystrokes.
*
* @param keypress keystroke event
*/
@Override
public void onKeypress(final TKeypressEvent keypress) {
if (keypress.equals(kbTab)
|| keypress.equals(kbShiftTab)
) {
// Squash tab and back-tab. They don't make sense in the TTable
// grid context.
return;
}
// If editing, pass to that cell and do nothing else.
if (getSelectedCell().isEditing) {
super.onKeypress(keypress);
return;
}
if (keypress.equals(kbLeft)) {
// Left
if (selectedColumn > 0) {
selectedColumn--;
}
activate(columns.get(selectedColumn).get(selectedRow));
} else if (keypress.equals(kbRight)) {
// Right
if (selectedColumn < columns.size() - 1) {
selectedColumn++;
}
activate(columns.get(selectedColumn).get(selectedRow));
} else if (keypress.equals(kbUp)) {
// Up
if (selectedRow > 0) {
selectedRow--;
}
activate(columns.get(selectedColumn).get(selectedRow));
} else if (keypress.equals(kbDown)) {
// Down
if (selectedRow < rows.size() - 1) {
selectedRow++;
}
activate(columns.get(selectedColumn).get(selectedRow));
} else if (keypress.equals(kbHome)) {
// Home - leftmost column
selectedColumn = 0;
activate(columns.get(selectedColumn).get(selectedRow));
} else if (keypress.equals(kbEnd)) {
// End - rightmost column
selectedColumn = columns.size() - 1;
activate(columns.get(selectedColumn).get(selectedRow));
} else if (keypress.equals(kbPgUp)) {
// PgUp - Treat like multiple up
for (int i = 0; i < getHeight() - 2; i++) {
if (selectedRow > 0) {
selectedRow--;
}
}
activate(columns.get(selectedColumn).get(selectedRow));
} else if (keypress.equals(kbPgDn)) {
// PgDn - Treat like multiple up
for (int i = 0; i < getHeight() - 2; i++) {
if (selectedRow < rows.size() - 1) {
selectedRow++;
}
}
activate(columns.get(selectedColumn).get(selectedRow));
} else if (keypress.equals(kbCtrlHome)) {
// Ctrl-Home - go to top-left
selectedRow = 0;
selectedColumn = 0;
activate(columns.get(selectedColumn).get(selectedRow));
activate(columns.get(selectedColumn).get(selectedRow));
} else if (keypress.equals(kbCtrlEnd)) {
// Ctrl-End - go to bottom-right
selectedRow = rows.size() - 1;
selectedColumn = columns.size() - 1;
activate(columns.get(selectedColumn).get(selectedRow));
activate(columns.get(selectedColumn).get(selectedRow));
} else {
// Pass to the Cell.
super.onKeypress(keypress);
}
// We may have scrolled off screen. Reset positions as needed to
// make the newly selected cell visible.
alignGrid();
}
/**
* Handle widget resize events.
*
* @param event resize event
*/
@Override
public void onResize(final TResizeEvent event) {
super.onResize(event);
bottomRightCorner();
}
// ------------------------------------------------------------------------
// TWidget ----------------------------------------------------------------
// ------------------------------------------------------------------------
/**
* Draw the table row/column labels, and borders.
*/
@Override
public void draw() {
CellAttributes labelColor = getTheme().getColor("ttable.label");
CellAttributes labelColorSelected = getTheme().getColor("ttable.label.selected");
CellAttributes borderColor = getTheme().getColor("ttable.border");
// Column labels.
if (showColumnLabels == true) {
for (int i = left; i < columns.size(); i++) {
if (columns.get(i).get(top).isVisible() == false) {
break;
}
putStringXY(columns.get(i).get(top).getX(), 0,
String.format(" %-" +
(columns.get(i).width - 2)
+ "s ", columns.get(i).label),
(i == selectedColumn ? labelColorSelected : labelColor));
}
}
// Row labels.
if (showRowLabels == true) {
for (int i = top; i < rows.size(); i++) {
if (rows.get(i).get(left).isVisible() == false) {
break;
}
putStringXY(0, rows.get(i).get(left).getY(),
String.format(" %-6s ", rows.get(i).label),
(i == selectedRow ? labelColorSelected : labelColor));
}
}
// Draw vertical borders.
if (leftBorder == Border.SINGLE) {
vLineXY((showRowLabels ? ROW_LABEL_WIDTH : 0),
(topBorder == Border.NONE ? 0 : 1) +
(showColumnLabels ? COLUMN_LABEL_HEIGHT : 0),
getHeight(), '\u2502', borderColor);
}
for (int i = left; i < columns.size(); i++) {
if (columns.get(i).get(top).isVisible() == false) {
break;
}
if (columns.get(i).rightBorder == Border.SINGLE) {
vLineXY(columns.get(i).getX() + columns.get(i).width,
(topBorder == Border.NONE ? 0 : 1) +
(showColumnLabels ? COLUMN_LABEL_HEIGHT : 0),
getHeight(), '\u2502', borderColor);
}
}
// Draw horizontal borders.
if (topBorder == Border.SINGLE) {
hLineXY((showRowLabels ? ROW_LABEL_WIDTH : 0),
(showColumnLabels ? COLUMN_LABEL_HEIGHT : 0),
getWidth(), '\u2500', borderColor);
}
for (int i = top; i < rows.size(); i++) {
if (rows.get(i).get(left).isVisible() == false) {
break;
}
if (rows.get(i).bottomBorder == Border.SINGLE) {
hLineXY((leftBorder == Border.NONE ? 0 : 1) +
(showRowLabels ? ROW_LABEL_WIDTH : 0),
rows.get(i).getY() + rows.get(i).height - 1,
getWidth(), '\u2500', borderColor);
} else if (rows.get(i).bottomBorder == Border.DOUBLE) {
hLineXY((leftBorder == Border.NONE ? 0 : 1) +
(showRowLabels ? ROW_LABEL_WIDTH : 0),
rows.get(i).getY() + rows.get(i).height - 1,
getWidth(), '\u2550', borderColor);
} else if (rows.get(i).bottomBorder == Border.THICK) {
hLineXY((leftBorder == Border.NONE ? 0 : 1) +
(showRowLabels ? ROW_LABEL_WIDTH : 0),
rows.get(i).getY() + rows.get(i).height - 1,
getWidth(), '\u2501', borderColor);
}
}
// Top-left corner if needed
if ((topBorder == Border.SINGLE) && (leftBorder == Border.SINGLE)) {
putCharXY((showRowLabels ? ROW_LABEL_WIDTH : 0),
(showColumnLabels ? COLUMN_LABEL_HEIGHT : 0),
'\u250c', borderColor);
}
// Now draw the correct corners
for (int i = top; i < rows.size(); i++) {
if (rows.get(i).get(left).isVisible() == false) {
break;
}
for (int j = left; j < columns.size(); j++) {
if (columns.get(j).get(i).isVisible() == false) {
break;
}
if ((i == top) && (topBorder == Border.SINGLE)
&& (columns.get(j).rightBorder == Border.SINGLE)
) {
// Top tee
putCharXY(columns.get(j).getX() + columns.get(j).width,
(showColumnLabels ? COLUMN_LABEL_HEIGHT : 0),
'\u252c', borderColor);
}
if ((j == left) && (leftBorder == Border.SINGLE)
&& (rows.get(i).bottomBorder == Border.SINGLE)
) {
// Left tee
putCharXY((showRowLabels ? ROW_LABEL_WIDTH : 0),
rows.get(i).getY() + rows.get(i).height - 1,
'\u251c', borderColor);
}
if ((columns.get(j).rightBorder == Border.SINGLE)
&& (rows.get(i).bottomBorder == Border.SINGLE)
) {
// Intersection of single bars
putCharXY(columns.get(j).getX() + columns.get(j).width,
rows.get(i).getY() + rows.get(i).height - 1,
'\u253c', borderColor);
}
if ((j == left) && (leftBorder == Border.SINGLE)
&& (rows.get(i).bottomBorder == Border.DOUBLE)
) {
// Left tee: single bar vertical, double bar horizontal
putCharXY((showRowLabels ? ROW_LABEL_WIDTH : 0),
rows.get(i).getY() + rows.get(i).height - 1,
'\u255e', borderColor);
}
if ((j == left) && (leftBorder == Border.SINGLE)
&& (rows.get(i).bottomBorder == Border.THICK)
) {
// Left tee: single bar vertical, thick bar horizontal
putCharXY((showRowLabels ? ROW_LABEL_WIDTH : 0),
rows.get(i).getY() + rows.get(i).height - 1,
'\u251d', borderColor);
}
if ((columns.get(j).rightBorder == Border.SINGLE)
&& (rows.get(i).bottomBorder == Border.DOUBLE)
) {
// Intersection: single bar vertical, double bar
// horizontal
putCharXY(columns.get(j).getX() + columns.get(j).width,
rows.get(i).getY() + rows.get(i).height - 1,
'\u256a', borderColor);
}
if ((columns.get(j).rightBorder == Border.SINGLE)
&& (rows.get(i).bottomBorder == Border.THICK)
) {
// Intersection: single bar vertical, thick bar
// horizontal
putCharXY(columns.get(j).getX() + columns.get(j).width,
rows.get(i).getY() + rows.get(i).height - 1,
'\u253f', borderColor);
}
}
}
// Now draw the window borders.
super.draw();
}
// ------------------------------------------------------------------------
// TTable -----------------------------------------------------------------
// ------------------------------------------------------------------------
/**
* Generate the default letter name for a column number.
*
* @param col column number to use for this column. Column 0 will be
* "A", column 1 will be "B", column 26 will be "AA", and so on.
*/
private String makeColumnLabel(int col) {
StringBuilder sb = new StringBuilder();
for (;;) {
sb.append((char) ('A' + (col % 26)));
if (col < 26) {
break;
}
col /= 26;
}
return sb.reverse().toString();
}
/**
* Get the currently-selected cell.
*
* @return the selected cell
*/
public Cell getSelectedCell() {
assert (rows.get(selectedRow) != null);
assert (rows.get(selectedRow).get(selectedColumn) != null);
assert (columns.get(selectedColumn) != null);
assert (columns.get(selectedColumn).get(selectedRow) != null);
assert (rows.get(selectedRow).get(selectedColumn) ==
columns.get(selectedColumn).get(selectedRow));
return (columns.get(selectedColumn).get(selectedRow));
}
/**
* Get the currently-selected column.
*
* @return the selected column
*/
public Column getSelectedColumn() {
assert (selectedColumn >= 0);
assert (columns.size() > selectedColumn);
assert (columns.get(selectedColumn) != null);
return columns.get(selectedColumn);
}
/**
* Get the currently-selected row.
*
* @return the selected row
*/
public Row getSelectedRow() {
assert (selectedRow >= 0);
assert (rows.size() > selectedRow);
assert (rows.get(selectedRow) != null);
return rows.get(selectedRow);
}
/**
* Get the currently-selected column number. 0 is the left-most column.
*
* @return the selected column number
*/
public int getSelectedColumnNumber() {
return selectedColumn;
}
/**
* Set the currently-selected column number. 0 is the left-most column.
*
* @param column the column number to select
*/
public void setSelectedColumnNumber(final int column) {
if ((column < 0) || (column > columns.size() - 1)) {
throw new IndexOutOfBoundsException("Column count is " +
columns.size() + ", requested index " + column);
}
selectedColumn = column;
activate(columns.get(selectedColumn).get(selectedRow));
alignGrid();
}
/**
* Get the currently-selected row number. 0 is the top-most row.
*
* @return the selected row number
*/
public int getSelectedRowNumber() {
return selectedRow;
}
/**
* Set the currently-selected row number. 0 is the left-most column.
*
* @param row the row number to select
*/
public void setSelectedRowNumber(final int row) {
if ((row < 0) || (row > rows.size() - 1)) {
throw new IndexOutOfBoundsException("Row count is " +
rows.size() + ", requested index " + row);
}
selectedRow = row;
activate(columns.get(selectedColumn).get(selectedRow));
alignGrid();
}
/**
* Get the highlight row flag.
*
* @return true if the selected row is highlighted
*/
public boolean getHighlightRow() {
return highlightRow;
}
/**
* Set the highlight row flag.
*
* @param highlightRow if true, the selected row will be highlighted
*/
public void setHighlightRow(final boolean highlightRow) {
this.highlightRow = highlightRow;
}
/**
* Get the highlight column flag.
*
* @return true if the selected column is highlighted
*/
public boolean getHighlightColumn() {
return highlightColumn;
}
/**
* Set the highlight column flag.
*
* @param highlightColumn if true, the selected column will be highlighted
*/
public void setHighlightColumn(final boolean highlightColumn) {
this.highlightColumn = highlightColumn;
}
/**
* Get the show row labels flag.
*
* @return true if row labels are shown
*/
public boolean getShowRowLabels() {
return showRowLabels;
}
/**
* Set the show row labels flag.
*
* @param showRowLabels if true, the row labels will be shown
*/
public void setShowRowLabels(final boolean showRowLabels) {
this.showRowLabels = showRowLabels;
}
/**
* Get the show column labels flag.
*
* @return true if column labels are shown
*/
public boolean getShowColumnLabels() {
return showColumnLabels;
}
/**
* Set the show column labels flag.
*
* @param showColumnLabels if true, the column labels will be shown
*/
public void setShowColumnLabels(final boolean showColumnLabels) {
this.showColumnLabels = showColumnLabels;
}
/**
* Get the number of columns.
*
* @return the number of columns
*/
public int getColumnCount() {
return columns.size();
}
/**
* Get the number of rows.
*
* @return the number of rows
*/
public int getRowCount() {
return rows.size();
}
/**
* Push top and left to the bottom-most right corner of the available
* grid.
*/
private void bottomRightCorner() {
int viewColumns = getWidth();
if (showRowLabels == true) {
viewColumns -= ROW_LABEL_WIDTH;
}
// Set left and top such that the table stays on screen if possible.
top = rows.size() - getHeight();
left = columns.size() - (getWidth() / (viewColumns / (COLUMN_DEFAULT_WIDTH + 1)));
// Now ensure the selection is visible.
alignGrid();
}
/**
* Align the grid so that the selected cell is fully visible.
*/
private void alignGrid() {
/*
System.err.println("alignGrid() # columns " + columns.size() +
" # rows " + rows.size());
*/
int viewColumns = getWidth();
if (showRowLabels == true) {
viewColumns -= ROW_LABEL_WIDTH;
}
if (leftBorder != Border.NONE) {
viewColumns--;
}
int viewRows = getHeight();
if (showColumnLabels == true) {
viewRows -= COLUMN_LABEL_HEIGHT;
}
if (topBorder != Border.NONE) {
viewRows--;
}
// If we pushed left or right, adjust the box to include the new
// selected cell.
if (selectedColumn < left) {
left = selectedColumn - 1;
}
if (left < 0) {
left = 0;
}
if (selectedRow < top) {
top = selectedRow - 1;
}
if (top < 0) {
top = 0;
}
/*
* viewColumns and viewRows now contain the available columns and
* rows available to view the selected cell. We adjust left and top
* to ensure the selected cell is within view, and then make all
* cells outside the box between (left, top) and (right, bottom)
* invisible.
*
* We need to calculate right and bottom now.
*/
int right = left;
boolean done = false;
while (!done) {
int rightCellX = (showRowLabels ? ROW_LABEL_WIDTH : 0);
if (leftBorder != Border.NONE) {
rightCellX++;
}
int maxCellX = rightCellX + viewColumns;
right = left;
boolean selectedIsVisible = false;
int selectedX = 0;
for (int x = left; x < columns.size(); x++) {
if (x == selectedColumn) {
selectedX = rightCellX;
if (selectedX + columns.get(x).width + 1 <= maxCellX) {
selectedIsVisible = true;
}
}
rightCellX += columns.get(x).width + 1;
if (rightCellX >= maxCellX) {
break;
}
right++;
}
if (right < selectedColumn) {
// selectedColumn is outside the view range. Push left over,
// and calculate again.
left++;
} else if (left == selectedColumn) {
// selectedColumn doesn't fit inside the view range, but we
// can't go over any further either. Bail out.
done = true;
} else if (selectedIsVisible == false) {
// selectedColumn doesn't fit inside the view range, continue
// on.
left++;
} else {
// selectedColumn is fully visible, all done.
assert (selectedIsVisible == true);
done = true;
}
} // while (!done)
// We have the left/right range correct, set cell visibility and
// column X positions.
int leftCellX = showRowLabels ? ROW_LABEL_WIDTH : 0;
if (leftBorder != Border.NONE) {
leftCellX++;
}
for (int x = 0; x < columns.size(); x++) {
if ((x < left) || (x > right)) {
for (int i = 0; i < rows.size(); i++) {
columns.get(x).get(i).setVisible(false);
columns.get(x).setX(getWidth() + 1);
}
continue;
}
for (int i = 0; i < rows.size(); i++) {
columns.get(x).get(i).setVisible(true);
}
columns.get(x).setX(leftCellX);
leftCellX += columns.get(x).width + 1;
}
int bottom = top;
done = false;
while (!done) {
int bottomCellY = (showColumnLabels ? COLUMN_LABEL_HEIGHT : 0);
if (topBorder != Border.NONE) {
bottomCellY++;
}
int maxCellY = bottomCellY + viewRows;
bottom = top;
for (int y = top; y < rows.size(); y++) {
bottomCellY += rows.get(y).height;
if (bottomCellY >= maxCellY) {
break;
}
bottom++;
}
if (bottom < selectedRow) {
// selectedRow is outside the view range. Push top down, and
// calculate again.
top++;
} else {
// selectedRow is inside the view range, done.
done = true;
}
} // while (!done)
// We have the top/bottom range correct, set cell visibility and
// row Y positions.
int topCellY = showColumnLabels ? COLUMN_LABEL_HEIGHT : 0;
if (topBorder != Border.NONE) {
topCellY++;
}
for (int y = 0; y < rows.size(); y++) {
if ((y < top) || (y > bottom)) {
for (int i = 0; i < columns.size(); i++) {
rows.get(y).get(i).setVisible(false);
}
rows.get(y).setY(getHeight() + 1);
continue;
}
for (int i = 0; i < columns.size(); i++) {
rows.get(y).get(i).setVisible(true);
}
rows.get(y).setY(topCellY);
topCellY += rows.get(y).height;
}
// Last thing: cancel any edits that are not the selected cell.
for (int y = 0; y < rows.size(); y++) {
for (int x = 0; x < columns.size(); x++) {
if ((x == selectedColumn) && (y == selectedRow)) {
continue;
}
rows.get(y).get(x).cancelEdit();
}
}
}
/**
* Load contents from file in CSV format.
*
* @param csvFile a File referencing the CSV data
* @throws IOException if a java.io operation throws
*/
public void loadCsvFile(final File csvFile) throws IOException {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(csvFile));
String line = null;
boolean first = true;
for (line = reader.readLine(); line != null;
line = reader.readLine()) {
List list = StringUtils.fromCsv(line);
if (list.size() == 0) {
continue;
}
if (list.size() > columns.size()) {
int n = list.size() - columns.size();
for (int i = 0; i < n; i++) {
selectedColumn = columns.size() - 1;
insertColumnRight(selectedColumn);
}
}
assert (list.size() == columns.size());
if (first) {
// First row: just replace what is here.
selectedRow = 0;
first = false;
} else {
// All other rows: append to the end.
selectedRow = rows.size() - 1;
insertRowBelow(selectedRow);
selectedRow = rows.size() - 1;
}
for (int i = 0; i < list.size(); i++) {
rows.get(selectedRow).get(i).setText(list.get(i));
}
// TODO: detect header line
}
} finally {
if (reader != null) {
reader.close();
}
}
left = 0;
top = 0;
selectedRow = 0;
selectedColumn = 0;
alignGrid();
activate(columns.get(selectedColumn).get(selectedRow));
}
/**
* Save contents to file in CSV format.
*
* @param filename file to save to
* @throws IOException if a java.io operation throws
*/
public void saveToCsvFilename(final String filename) throws IOException {
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter(filename));
for (Row row: rows) {
List list = new ArrayList(row.cells.size());
for (Cell cell: row.cells) {
list.add(cell.getText());
}
writer.write(StringUtils.toCsv(list));
writer.write("\n");
}
} finally {
if (writer != null) {
writer.close();
}
}
}
/**
* Save contents to file in text format with lines.
*
* @param filename file to save to
* @throws IOException if a java.io operation throws
*/
public void saveToTextFilename(final String filename) throws IOException {
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter(filename));
if ((topBorder == Border.SINGLE) && (leftBorder == Border.SINGLE)) {
// Emit top-left corner.
writer.write("\u250c");
}
if (topBorder == Border.SINGLE) {
int cellI = 0;
for (Cell cell: rows.get(0).cells) {
for (int i = 0; i < columns.get(cellI).width; i++) {
writer.write("\u2500");
}
if (columns.get(cellI).rightBorder == Border.SINGLE) {
if (cellI < columns.size() - 1) {
// Emit top tee.
writer.write("\u252c");
} else {
// Emit top-right corner.
writer.write("\u2510");
}
}
cellI++;
}
}
writer.write("\n");
int rowI = 0;
for (Row row: rows) {
if (leftBorder == Border.SINGLE) {
// Emit left border.
writer.write("\u2502");
}
int cellI = 0;
for (Cell cell: row.cells) {
writer.write(String.format("%" +
columns.get(cellI).width + "s", cell.getText()));
if (columns.get(cellI).rightBorder == Border.SINGLE) {
// Emit right border.
writer.write("\u2502");
}
cellI++;
}
writer.write("\n");
if (row.bottomBorder == Border.NONE) {
// All done, move on to the next row.
continue;
}
// Emit the bottom borders and intersections.
if ((leftBorder == Border.SINGLE)
&& (row.bottomBorder != Border.NONE)
) {
if (rowI < rows.size() - 1) {
if (row.bottomBorder == Border.SINGLE) {
// Emit left tee.
writer.write("\u251c");
} else if (row.bottomBorder == Border.DOUBLE) {
// Emit left tee (double).
writer.write("\u255e");
} else if (row.bottomBorder == Border.THICK) {
// Emit left tee (thick).
writer.write("\u251d");
}
}
if (rowI == rows.size() - 1) {
if (row.bottomBorder == Border.SINGLE) {
// Emit left bottom corner.
writer.write("\u2514");
} else if (row.bottomBorder == Border.DOUBLE) {
// Emit left bottom corner (double).
writer.write("\u2558");
} else if (row.bottomBorder == Border.THICK) {
// Emit left bottom corner (thick).
writer.write("\u2515");
}
}
}
cellI = 0;
for (Cell cell: row.cells) {
for (int i = 0; i < columns.get(cellI).width; i++) {
if (row.bottomBorder == Border.SINGLE) {
writer.write("\u2500");
}
if (row.bottomBorder == Border.DOUBLE) {
writer.write("\u2550");
}
if (row.bottomBorder == Border.THICK) {
writer.write("\u2501");
}
}
if ((rowI < rows.size() - 1)
&& (cellI == columns.size() - 1)
&& (row.bottomBorder == Border.SINGLE)
&& (columns.get(cellI).rightBorder == Border.SINGLE)
) {
// Emit right tee.
writer.write("\u2524");
}
if ((rowI < rows.size() - 1)
&& (cellI == columns.size() - 1)
&& (row.bottomBorder == Border.DOUBLE)
&& (columns.get(cellI).rightBorder == Border.SINGLE)
) {
// Emit right tee (double).
writer.write("\u2561");
}
if ((rowI < rows.size() - 1)
&& (cellI == columns.size() - 1)
&& (row.bottomBorder == Border.THICK)
&& (columns.get(cellI).rightBorder == Border.SINGLE)
) {
// Emit right tee (thick).
writer.write("\u2525");
}
if ((rowI == rows.size() - 1)
&& (cellI == columns.size() - 1)
&& (row.bottomBorder == Border.SINGLE)
&& (columns.get(cellI).rightBorder == Border.SINGLE)
) {
// Emit right bottom corner.
writer.write("\u2518");
}
if ((rowI == rows.size() - 1)
&& (cellI == columns.size() - 1)
&& (row.bottomBorder == Border.DOUBLE)
&& (columns.get(cellI).rightBorder == Border.SINGLE)
) {
// Emit right bottom corner (double).
writer.write("\u255b");
}
if ((rowI == rows.size() - 1)
&& (cellI == columns.size() - 1)
&& (row.bottomBorder == Border.THICK)
&& (columns.get(cellI).rightBorder == Border.SINGLE)
) {
// Emit right bottom corner (thick).
writer.write("\u2519");
}
if ((rowI < rows.size() - 1)
&& (cellI < columns.size() - 1)
&& (row.bottomBorder == Border.SINGLE)
&& (columns.get(cellI).rightBorder == Border.SINGLE)
) {
// Emit intersection.
writer.write("\u253c");
}
if ((rowI < rows.size() - 1)
&& (cellI < columns.size() - 1)
&& (row.bottomBorder == Border.DOUBLE)
&& (columns.get(cellI).rightBorder == Border.SINGLE)
) {
// Emit intersection (double).
writer.write("\u256a");
}
if ((rowI < rows.size() - 1)
&& (cellI < columns.size() - 1)
&& (row.bottomBorder == Border.THICK)
&& (columns.get(cellI).rightBorder == Border.SINGLE)
) {
// Emit intersection (thick).
writer.write("\u253f");
}
if ((rowI == rows.size() - 1)
&& (cellI < columns.size() - 1)
&& (row.bottomBorder == Border.SINGLE)
&& (columns.get(cellI).rightBorder == Border.SINGLE)
) {
// Emit bottom tee.
writer.write("\u2534");
}
if ((rowI == rows.size() - 1)
&& (cellI < columns.size() - 1)
&& (row.bottomBorder == Border.DOUBLE)
&& (columns.get(cellI).rightBorder == Border.SINGLE)
) {
// Emit bottom tee (double).
writer.write("\u2567");
}
if ((rowI == rows.size() - 1)
&& (cellI < columns.size() - 1)
&& (row.bottomBorder == Border.THICK)
&& (columns.get(cellI).rightBorder == Border.SINGLE)
) {
// Emit bottom tee (thick).
writer.write("\u2537");
}
cellI++;
}
writer.write("\n");
rowI++;
}
} finally {
if (writer != null) {
writer.close();
}
}
}
/**
* Set the selected cell location.
*
* @param column the selected cell location column
* @param row the selected cell location row
*/
public void setSelectedCell(final int column, final int row) {
if ((column < 0) || (column > columns.size() - 1)) {
throw new IndexOutOfBoundsException("Column count is " +
columns.size() + ", requested index " + column);
}
if ((row < 0) || (row > rows.size() - 1)) {
throw new IndexOutOfBoundsException("Row count is " +
rows.size() + ", requested index " + row);
}
selectedColumn = column;
selectedRow = row;
alignGrid();
}
/**
* Get a particular cell.
*
* @param column the cell column
* @param row the cell row
* @return the cell
*/
public Cell getCell(final int column, final int row) {
if ((column < 0) || (column > columns.size() - 1)) {
throw new IndexOutOfBoundsException("Column count is " +
columns.size() + ", requested index " + column);
}
if ((row < 0) || (row > rows.size() - 1)) {
throw new IndexOutOfBoundsException("Row count is " +
rows.size() + ", requested index " + row);
}
return rows.get(row).get(column);
}
/**
* Get the text of a particular cell.
*
* @param column the cell column
* @param row the cell row
* @return the text in the cell
*/
public String getCellText(final int column, final int row) {
if ((column < 0) || (column > columns.size() - 1)) {
throw new IndexOutOfBoundsException("Column count is " +
columns.size() + ", requested index " + column);
}
if ((row < 0) || (row > rows.size() - 1)) {
throw new IndexOutOfBoundsException("Row count is " +
rows.size() + ", requested index " + row);
}
return rows.get(row).get(column).getText();
}
/**
* Set the text of a particular cell.
*
* @param column the cell column
* @param row the cell row
* @param text the text to put into the cell
*/
public void setCellText(final int column, final int row,
final String text) {
if ((column < 0) || (column > columns.size() - 1)) {
throw new IndexOutOfBoundsException("Column count is " +
columns.size() + ", requested index " + column);
}
if ((row < 0) || (row > rows.size() - 1)) {
throw new IndexOutOfBoundsException("Row count is " +
rows.size() + ", requested index " + row);
}
rows.get(row).get(column).setText(text);
}
/**
* Set the action to perform when the user presses enter on a particular
* cell.
*
* @param column the cell column
* @param row the cell row
* @param action the action to perform when the user presses enter on the
* cell
*/
public void setCellEnterAction(final int column, final int row,
final TAction action) {
if ((column < 0) || (column > columns.size() - 1)) {
throw new IndexOutOfBoundsException("Column count is " +
columns.size() + ", requested index " + column);
}
if ((row < 0) || (row > rows.size() - 1)) {
throw new IndexOutOfBoundsException("Row count is " +
rows.size() + ", requested index " + row);
}
rows.get(row).get(column).field.setEnterAction(action);
}
/**
* Set the action to perform when the user updates a particular cell.
*
* @param column the cell column
* @param row the cell row
* @param action the action to perform when the user updates the cell
*/
public void setCellUpdateAction(final int column, final int row,
final TAction action) {
if ((column < 0) || (column > columns.size() - 1)) {
throw new IndexOutOfBoundsException("Column count is " +
columns.size() + ", requested index " + column);
}
if ((row < 0) || (row > rows.size() - 1)) {
throw new IndexOutOfBoundsException("Row count is " +
rows.size() + ", requested index " + row);
}
rows.get(row).get(column).field.setUpdateAction(action);
}
/**
* Get the width of a column.
*
* @param column the column number
* @return the width of the column
*/
public int getColumnWidth(final int column) {
if ((column < 0) || (column > columns.size() - 1)) {
throw new IndexOutOfBoundsException("Column count is " +
columns.size() + ", requested index " + column);
}
return columns.get(column).width;
}
/**
* Set the width of a column.
*
* @param column the column number
* @param width the new width of the column
*/
public void setColumnWidth(final int column, final int width) {
if ((column < 0) || (column > columns.size() - 1)) {
throw new IndexOutOfBoundsException("Column count is " +
columns.size() + ", requested index " + column);
}
if (width < 4) {
// Columns may not be smaller than 4 cells wide.
return;
}
int delta = width - columns.get(column).width;
columns.get(column).width = width;
for (Cell cell: columns.get(column).cells) {
cell.setWidth(columns.get(column).width);
cell.field.setWidth(columns.get(column).width);
}
for (int i = column + 1; i < columns.size(); i++) {
columns.get(i).setX(columns.get(i).getX() + delta);
}
if (column == columns.size() - 1) {
bottomRightCorner();
} else {
alignGrid();
}
}
/**
* Get the label of a column.
*
* @param column the column number
* @return the label of the column
*/
public String getColumnLabel(final int column) {
if ((column < 0) || (column > columns.size() - 1)) {
throw new IndexOutOfBoundsException("Column count is " +
columns.size() + ", requested index " + column);
}
return columns.get(column).label;
}
/**
* Set the label of a column.
*
* @param column the column number
* @param label the new label of the column
*/
public void setColumnLabel(final int column, final String label) {
if ((column < 0) || (column > columns.size() - 1)) {
throw new IndexOutOfBoundsException("Column count is " +
columns.size() + ", requested index " + column);
}
columns.get(column).label = label;
}
/**
* Get the label of a row.
*
* @param row the row number
* @return the label of the row
*/
public String getRowLabel(final int row) {
if ((row < 0) || (row > rows.size() - 1)) {
throw new IndexOutOfBoundsException("Row count is " +
rows.size() + ", requested index " + row);
}
return rows.get(row).label;
}
/**
* Set the label of a row.
*
* @param row the row number
* @param label the new label of the row
*/
public void setRowLabel(final int row, final String label) {
if ((row < 0) || (row > rows.size() - 1)) {
throw new IndexOutOfBoundsException("Row count is " +
rows.size() + ", requested index " + row);
}
rows.get(row).label = label;
}
/**
* Insert one row at a particular index.
*
* @param idx the row number
*/
private void insertRowAt(final int idx) {
Row newRow = new Row(idx);
for (int i = 0; i < columns.size(); i++) {
Cell cell = new Cell(this, columns.get(i).getX(),
rows.get(idx).getY(), COLUMN_DEFAULT_WIDTH, 1, i, idx);
newRow.add(cell);
columns.get(i).cells.add(idx, cell);
}
rows.add(idx, newRow);
for (int x = 0; x < columns.size(); x++) {
for (int y = idx; y < rows.size(); y++) {
columns.get(x).get(y).row = y;
columns.get(x).get(y).column = x;
}
}
for (int i = idx + 1; i < rows.size(); i++) {
String oldRowLabel = Integer.toString(i - 1);
if (rows.get(i).label.equals(oldRowLabel)) {
rows.get(i).label = Integer.toString(i);
}
}
alignGrid();
}
/**
* Insert one row above a particular row.
*
* @param row the row number
*/
public void insertRowAbove(final int row) {
if ((row < 0) || (row > rows.size() - 1)) {
throw new IndexOutOfBoundsException("Row count is " +
rows.size() + ", requested index " + row);
}
insertRowAt(row);
selectedRow++;
activate(columns.get(selectedColumn).get(selectedRow));
}
/**
* Insert one row below a particular row.
*
* @param row the row number
*/
public void insertRowBelow(final int row) {
if ((row < 0) || (row > rows.size() - 1)) {
throw new IndexOutOfBoundsException("Row count is " +
rows.size() + ", requested index " + row);
}
int idx = row + 1;
if (idx < rows.size()) {
insertRowAt(idx);
activate(columns.get(selectedColumn).get(selectedRow));
return;
}
// row is the last row, we need to perform an append.
Row newRow = new Row(idx);
for (int i = 0; i < columns.size(); i++) {
Cell cell = new Cell(this, columns.get(i).getX(),
rows.get(row).getY(), COLUMN_DEFAULT_WIDTH, 1, i, idx);
newRow.add(cell);
columns.get(i).cells.add(cell);
}
rows.add(newRow);
alignGrid();
activate(columns.get(selectedColumn).get(selectedRow));
}
/**
* Delete a particular row.
*
* @param row the row number
*/
public void deleteRow(final int row) {
if ((row < 0) || (row > rows.size() - 1)) {
throw new IndexOutOfBoundsException("Row count is " +
rows.size() + ", requested index " + row);
}
if (rows.size() == 1) {
// Don't delete the last row.
return;
}
for (int i = 0; i < columns.size(); i++) {
Cell cell = columns.get(i).cells.remove(row);
getChildren().remove(cell);
}
rows.remove(row);
for (int x = 0; x < columns.size(); x++) {
for (int y = row; y < rows.size(); y++) {
columns.get(x).get(y).row = y;
columns.get(x).get(y).column = x;
}
}
for (int i = row; i < rows.size(); i++) {
String oldRowLabel = Integer.toString(i + 1);
if (rows.get(i).label.equals(oldRowLabel)) {
rows.get(i).label = Integer.toString(i);
}
}
if (selectedRow == rows.size()) {
selectedRow--;
}
activate(columns.get(selectedColumn).get(selectedRow));
bottomRightCorner();
}
/**
* Insert one column at a particular index.
*
* @param idx the column number
*/
private void insertColumnAt(final int idx) {
Column newColumn = new Column(idx);
for (int i = 0; i < rows.size(); i++) {
Cell cell = new Cell(this, columns.get(idx).getX(),
rows.get(i).getY(), COLUMN_DEFAULT_WIDTH, 1, idx, i);
newColumn.add(cell);
rows.get(i).cells.add(idx, cell);
}
columns.add(idx, newColumn);
for (int x = idx; x < columns.size(); x++) {
for (int y = 0; y < rows.size(); y++) {
columns.get(x).get(y).row = y;
columns.get(x).get(y).column = x;
}
}
for (int i = idx + 1; i < columns.size(); i++) {
String oldColumnLabel = makeColumnLabel(i - 1);
if (columns.get(i).label.equals(oldColumnLabel)) {
columns.get(i).label = makeColumnLabel(i);
}
}
alignGrid();
}
/**
* Insert one column to the left of a particular column.
*
* @param column the column number
*/
public void insertColumnLeft(final int column) {
if ((column < 0) || (column > columns.size() - 1)) {
throw new IndexOutOfBoundsException("Column count is " +
columns.size() + ", requested index " + column);
}
insertColumnAt(column);
selectedColumn++;
activate(columns.get(selectedColumn).get(selectedRow));
}
/**
* Insert one column to the right of a particular column.
*
* @param column the column number
*/
public void insertColumnRight(final int column) {
if ((column < 0) || (column > columns.size() - 1)) {
throw new IndexOutOfBoundsException("Column count is " +
columns.size() + ", requested index " + column);
}
int idx = column + 1;
if (idx < columns.size()) {
insertColumnAt(idx);
activate(columns.get(selectedColumn).get(selectedRow));
return;
}
// column is the last column, we need to perform an append.
Column newColumn = new Column(idx);
for (int i = 0; i < rows.size(); i++) {
Cell cell = new Cell(this, columns.get(column).getX(),
rows.get(i).getY(), COLUMN_DEFAULT_WIDTH, 1, idx, i);
newColumn.add(cell);
rows.get(i).cells.add(cell);
}
columns.add(newColumn);
alignGrid();
activate(columns.get(selectedColumn).get(selectedRow));
}
/**
* Delete a particular column.
*
* @param column the column number
*/
public void deleteColumn(final int column) {
if ((column < 0) || (column > columns.size() - 1)) {
throw new IndexOutOfBoundsException("Column count is " +
columns.size() + ", requested index " + column);
}
if (columns.size() == 1) {
// Don't delete the last column.
return;
}
for (int i = 0; i < rows.size(); i++) {
Cell cell = rows.get(i).cells.remove(column);
getChildren().remove(cell);
}
columns.remove(column);
for (int x = column; x < columns.size(); x++) {
for (int y = 0; y < rows.size(); y++) {
columns.get(x).get(y).row = y;
columns.get(x).get(y).column = x;
}
}
for (int i = column; i < columns.size(); i++) {
String oldColumnLabel = makeColumnLabel(i + 1);
if (columns.get(i).label.equals(oldColumnLabel)) {
columns.get(i).label = makeColumnLabel(i);
}
}
if (selectedColumn == columns.size()) {
selectedColumn--;
}
activate(columns.get(selectedColumn).get(selectedRow));
bottomRightCorner();
}
/**
* Delete the selected cell, shifting cells over to the left.
*/
public void deleteCellShiftLeft() {
// All we do is copy the text from every cell in this row over.
for (int i = selectedColumn + 1; i < columns.size(); i++) {
setCellText(i - 1, selectedRow, getCellText(i, selectedRow));
}
setCellText(columns.size() - 1, selectedRow, "");
}
/**
* Delete the selected cell, shifting cells from below up.
*/
public void deleteCellShiftUp() {
// All we do is copy the text from every cell in this column up.
for (int i = selectedRow + 1; i < rows.size(); i++) {
setCellText(selectedColumn, i - 1, getCellText(selectedColumn, i));
}
setCellText(selectedColumn, rows.size() - 1, "");
}
/**
* Set a particular cell read-only (non-editable) or not.
*
* @param column the cell column
* @param row the cell row
* @param readOnly if true, the cell will be non-editable
*/
public void setCellReadOnly(final int column, final int row,
final boolean readOnly) {
if ((column < 0) || (column > columns.size() - 1)) {
throw new IndexOutOfBoundsException("Column count is " +
columns.size() + ", requested index " + column);
}
if ((row < 0) || (row > rows.size() - 1)) {
throw new IndexOutOfBoundsException("Row count is " +
rows.size() + ", requested index " + row);
}
rows.get(row).get(column).setReadOnly(readOnly);
}
/**
* Set an entire row of cells read-only (non-editable) or not.
*
* @param row the row number
* @param readOnly if true, the cells will be non-editable
*/
public void setRowReadOnly(final int row, final boolean readOnly) {
if ((row < 0) || (row > rows.size() - 1)) {
throw new IndexOutOfBoundsException("Row count is " +
rows.size() + ", requested index " + row);
}
for (Cell cell: rows.get(row).cells) {
cell.setReadOnly(readOnly);
}
}
/**
* Set an entire column of cells read-only (non-editable) or not.
*
* @param column the column number
* @param readOnly if true, the cells will be non-editable
*/
public void setColumnReadOnly(final int column, final boolean readOnly) {
if ((column < 0) || (column > columns.size() - 1)) {
throw new IndexOutOfBoundsException("Column count is " +
columns.size() + ", requested index " + column);
}
for (Cell cell: columns.get(column).cells) {
cell.setReadOnly(readOnly);
}
}
/**
* Set all borders across the entire table to Border.NONE.
*/
public void setBorderAllNone() {
topBorder = Border.NONE;
leftBorder = Border.NONE;
for (int i = 0; i < columns.size(); i++) {
columns.get(i).rightBorder = Border.NONE;
}
for (int i = 0; i < rows.size(); i++) {
rows.get(i).bottomBorder = Border.NONE;
rows.get(i).height = 1;
}
bottomRightCorner();
}
/**
* Set all borders across the entire table to Border.SINGLE.
*/
public void setBorderAllSingle() {
topBorder = Border.SINGLE;
leftBorder = Border.SINGLE;
for (int i = 0; i < columns.size(); i++) {
columns.get(i).rightBorder = Border.SINGLE;
}
for (int i = 0; i < rows.size(); i++) {
rows.get(i).bottomBorder = Border.SINGLE;
rows.get(i).height = 2;
}
alignGrid();
}
/**
* Set all borders around the selected cell to Border.NONE.
*/
public void setBorderCellNone() {
if (selectedRow == 0) {
topBorder = Border.NONE;
}
if (selectedColumn == 0) {
leftBorder = Border.NONE;
}
if (selectedColumn > 0) {
columns.get(selectedColumn - 1).rightBorder = Border.NONE;
}
columns.get(selectedColumn).rightBorder = Border.NONE;
if (selectedRow > 0) {
rows.get(selectedRow - 1).bottomBorder = Border.NONE;
rows.get(selectedRow - 1).height = 1;
}
rows.get(selectedRow).bottomBorder = Border.NONE;
rows.get(selectedRow).height = 1;
bottomRightCorner();
}
/**
* Set all borders around the selected cell to Border.SINGLE.
*/
public void setBorderCellSingle() {
if (selectedRow == 0) {
topBorder = Border.SINGLE;
}
if (selectedColumn == 0) {
leftBorder = Border.SINGLE;
}
if (selectedColumn > 0) {
columns.get(selectedColumn - 1).rightBorder = Border.SINGLE;
}
columns.get(selectedColumn).rightBorder = Border.SINGLE;
if (selectedRow > 0) {
rows.get(selectedRow - 1).bottomBorder = Border.SINGLE;
rows.get(selectedRow - 1).height = 2;
}
rows.get(selectedRow).bottomBorder = Border.SINGLE;
rows.get(selectedRow).height = 2;
alignGrid();
}
/**
* Set the column border to the right of the selected cell to
* Border.SINGLE.
*/
public void setBorderColumnRightSingle() {
columns.get(selectedColumn).rightBorder = Border.SINGLE;
alignGrid();
}
/**
* Set the column border to the right of the selected cell to
* Border.SINGLE.
*/
public void setBorderColumnLeftSingle() {
if (selectedColumn == 0) {
leftBorder = Border.SINGLE;
} else {
columns.get(selectedColumn - 1).rightBorder = Border.SINGLE;
}
alignGrid();
}
/**
* Set the row border above the selected cell to Border.SINGLE.
*/
public void setBorderRowAboveSingle() {
if (selectedRow == 0) {
topBorder = Border.SINGLE;
} else {
rows.get(selectedRow - 1).bottomBorder = Border.SINGLE;
rows.get(selectedRow - 1).height = 2;
}
alignGrid();
}
/**
* Set the row border below the selected cell to Border.SINGLE.
*/
public void setBorderRowBelowSingle() {
rows.get(selectedRow).bottomBorder = Border.SINGLE;
rows.get(selectedRow).height = 2;
alignGrid();
}
/**
* Set the row border below the selected cell to Border.DOUBLE.
*/
public void setBorderRowBelowDouble() {
rows.get(selectedRow).bottomBorder = Border.DOUBLE;
rows.get(selectedRow).height = 2;
alignGrid();
}
/**
* Set the row border below the selected cell to Border.THICK.
*/
public void setBorderRowBelowThick() {
rows.get(selectedRow).bottomBorder = Border.THICK;
rows.get(selectedRow).height = 2;
alignGrid();
}
}