X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;ds=sidebyside;f=src%2Fjexer%2FTTableWidget.java;h=13a37aa5a5d2dc6a9e6cc2de20810e958e38f3b1;hb=382bc294dd88b71639fdd6c73216d2519635a080;hp=2e10a3faedc702295335cd740d60a85ca4a331e6;hpb=759cb83ebad2f861e50f39dab34f70eaafe6d6ed;p=fanfix.git diff --git a/src/jexer/TTableWidget.java b/src/jexer/TTableWidget.java index 2e10a3f..13a37aa 100644 --- a/src/jexer/TTableWidget.java +++ b/src/jexer/TTableWidget.java @@ -74,12 +74,36 @@ public class TTableWidget extends TWidget { DOUBLE, /** - * Thick bar: \u258C (vertical, left half block) and \u2580 - * (horizontal, upper block). + * Thick bar: \u2503 (vertical heavy) and \u2501 (horizontal heavy). */ THICK, } + /** + * 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 = 10; + + /** + * Extra columns to add. + */ + private static final int EXTRA_COLUMNS = 10 * (8 + 1); + // ------------------------------------------------------------------------ // Variables -------------------------------------------------------------- // ------------------------------------------------------------------------ @@ -117,12 +141,12 @@ public class TTableWidget extends TWidget { /** * If true, highlight the entire row of the currently-selected cell. */ - private boolean highlightRow = true; + private boolean highlightRow = false; /** * If true, highlight the entire column of the currently-selected cell. */ - private boolean highlightColumn = true; + private boolean highlightColumn = false; /** * If true, show the row labels as the first column. @@ -134,15 +158,30 @@ public class TTableWidget extends TWidget { */ 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 = 8; + private int width = COLUMN_DEFAULT_WIDTH; /** * The cells of this column. @@ -155,9 +194,9 @@ public class TTableWidget extends TWidget { private String label = ""; /** - * The border for this column. + * The right border for this column. */ - private Border border = Border.NONE; + private Border rightBorder = Border.NONE; /** * Constructor sets label to lettered column. @@ -195,6 +234,28 @@ public class TTableWidget extends TWidget { 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; + } + } /** @@ -202,6 +263,11 @@ public class TTableWidget extends TWidget { */ public class Row { + /** + * Y position of this row. + */ + private int y = 0; + /** * Height of row. */ @@ -218,9 +284,9 @@ public class TTableWidget extends TWidget { private String label = ""; /** - * The border for this row. + * The bottom border for this row. */ - private Border border = Border.NONE; + private Border bottomBorder = Border.NONE; /** * Constructor sets label to numbered row. @@ -249,6 +315,26 @@ public class TTableWidget extends TWidget { 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; + } } @@ -504,25 +590,38 @@ public class TTableWidget extends TWidget { // Place a grid of cells that fit in this space. int row = 0; - for (int i = 0; i < height; i += rows.get(0).height) { + for (int i = 0; i < height + EXTRA_ROWS; i += rows.get(0).height) { int column = 0; - for (int j = 0; j < width; j += columns.get(0).width) { - Cell cell = new Cell(this, j, i, columns.get(0).width, + for (int j = 0; j < width + EXTRA_COLUMNS; + j += columns.get(0).width) { + + Cell cell = new Cell(this, 0, 0, /* j, i, */ columns.get(0).width, rows.get(0).height, column, row); + // DEBUG: set a grid of cell index labels + // TODO: remove this cell.setText("" + row + " " + column); rows.get(row).add(cell); columns.get(column).add(cell); - if ((i == 0) && (j + columns.get(0).width < width)) { + if ((i == 0) && + (j + columns.get(0).width < width + EXTRA_COLUMNS) + ) { columns.add(new Column(column + 1)); } column++; } - if (i + rows.get(0).height < height) { + if (i + rows.get(0).height < height + EXTRA_ROWS) { rows.add(new Row(row + 1)); } row++; } + 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) + + (showRowLabels ? ROW_LABEL_WIDTH : 0)); + } activate(columns.get(selectedColumn).get(selectedRow)); alignGrid(); @@ -693,13 +792,79 @@ public class TTableWidget extends TWidget { highlightColumn = getApplication().getMenuItem(menu.getId()).getChecked(); break; case TMenu.MID_TABLE_BORDER_NONE: + 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; + } + break; case TMenu.MID_TABLE_BORDER_ALL: + 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; + } + break; + case TMenu.MID_TABLE_BORDER_CELL_NONE: + if (selectedRow == 0) { + topBorder = Border.NONE; + } + if (selectedColumn == 0) { + leftBorder = Border.NONE; + } + columns.get(selectedColumn).rightBorder = Border.NONE; + rows.get(selectedRow).bottomBorder = Border.NONE; + rows.get(selectedRow).height = 1; + break; + case TMenu.MID_TABLE_BORDER_CELL_ALL: + if (selectedRow == 0) { + topBorder = Border.SINGLE; + } + if (selectedColumn == 0) { + leftBorder = Border.SINGLE; + } + columns.get(selectedColumn).rightBorder = Border.SINGLE; + rows.get(selectedRow).bottomBorder = Border.SINGLE; + rows.get(selectedRow).height = 2; + break; case TMenu.MID_TABLE_BORDER_RIGHT: + columns.get(selectedColumn).rightBorder = Border.SINGLE; + break; case TMenu.MID_TABLE_BORDER_LEFT: + if (selectedColumn == 0) { + leftBorder = Border.SINGLE; + } else { + columns.get(selectedColumn - 1).rightBorder = Border.SINGLE; + } + break; case TMenu.MID_TABLE_BORDER_TOP: + if (selectedRow == 0) { + topBorder = Border.SINGLE; + } else { + rows.get(selectedRow - 1).bottomBorder = Border.SINGLE; + rows.get(selectedRow - 1).height = 2; + } + break; case TMenu.MID_TABLE_BORDER_BOTTOM: + rows.get(selectedRow).bottomBorder = Border.SINGLE; + rows.get(selectedRow).height = 2; + break; case TMenu.MID_TABLE_BORDER_DOUBLE_BOTTOM: + rows.get(selectedRow).bottomBorder = Border.DOUBLE; + rows.get(selectedRow).height = 2; + break; case TMenu.MID_TABLE_BORDER_THICK_BOTTOM: + rows.get(selectedRow).bottomBorder = Border.THICK; + rows.get(selectedRow).height = 2; + break; case TMenu.MID_TABLE_DELETE_LEFT: case TMenu.MID_TABLE_DELETE_UP: case TMenu.MID_TABLE_DELETE_ROW: @@ -716,11 +881,8 @@ public class TTableWidget extends TWidget { cell.field.setWidth(columns.get(selectedColumn).width); } for (int i = selectedColumn + 1; i < columns.size(); i++) { - for (Cell cell: columns.get(i).cells) { - cell.setX(cell.getX() - 1); - } + columns.get(i).setX(columns.get(i).getX() - 1); } - alignGrid(); break; case TMenu.MID_TABLE_COLUMN_WIDEN: columns.get(selectedColumn).width++; @@ -729,11 +891,8 @@ public class TTableWidget extends TWidget { cell.field.setWidth(columns.get(selectedColumn).width); } for (int i = selectedColumn + 1; i < columns.size(); i++) { - for (Cell cell: columns.get(i).cells) { - cell.setX(cell.getX() + 1); - } + columns.get(i).setX(columns.get(i).getX() + 1); } - alignGrid(); break; case TMenu.MID_TABLE_FILE_SAVE_CSV: // TODO @@ -745,6 +904,7 @@ public class TTableWidget extends TWidget { super.onMenu(menu); } + // Fix/redraw the display. alignGrid(); } @@ -769,7 +929,7 @@ public class TTableWidget extends TWidget { } putStringXY(columns.get(i).get(top).getX(), 0, String.format(" %-" + - (columns.get(i).get(top).getWidth() - 2) + (columns.get(i).width - 2) + "s ", columns.get(i).label), (i == selectedColumn ? labelColorSelected : labelColor)); } @@ -787,6 +947,129 @@ public class TTableWidget extends TWidget { } } + // 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(); } @@ -902,228 +1185,160 @@ public class TTableWidget extends TWidget { } /** - * Get the full horizontal width of this table. - * - * @return the width required to render the entire table + * Align the grid so that the selected cell is fully visible. */ - private int getMaximumWidth() { - int totalWidth = 0; + private void alignGrid() { + int viewColumns = getWidth(); if (showRowLabels == true) { - // For now, all row labels are 8 cells wide. TODO: make this - // adjustable. - totalWidth += 8; + viewColumns -= ROW_LABEL_WIDTH; } - for (Cell cell: getSelectedRow().cells) { - totalWidth += cell.getWidth() + 1; + if (leftBorder != Border.NONE) { + viewColumns--; } - return totalWidth; - } - - /** - * Get the full vertical height of this table. - * - * @return the height required to render the entire table - */ - private int getMaximumHeight() { - int totalHeight = 0; + int viewRows = getHeight(); if (showColumnLabels == true) { - // For now, all column labels are 1 cell tall. TODO: make this - // adjustable. - totalHeight += 1; + viewRows -= COLUMN_LABEL_HEIGHT; } - for (Cell cell: getSelectedColumn().cells) { - totalHeight += cell.getHeight(); - // TODO: handle top/bottom borders. + if (topBorder != Border.NONE) { + viewRows--; } - return totalHeight; - } - /** - * Align the grid so that the selected cell is fully visible. - */ - private void alignGrid() { - - /* - * We start by assuming that all cells are visible, and then mark as - * invisible those that are outside the viewable area. - */ - for (int x = 0; x < columns.size(); x++) { - for (int y = 0; y < rows.size(); y++) { - Cell cell = rows.get(y).cells.get(x); - cell.setVisible(true); - - // Special case: mouse double-clicks can lead to multiple - // cells in editing mode. Only allow a cell to remain - // editing if it is fact the active widget. - if (cell.isEditing && !cell.isActive()) { - cell.fieldText = cell.field.getText(); - cell.isEditing = false; - cell.field.setEnabled(false); - } - } + // If we pushed left or right, adjust the box to include the new + // selected cell. + if (selectedColumn < left) { + left = selectedColumn - 1; } - - // Adjust X locations to be visible ----------------------------------- - - // Determine if we need to shift left or right. - int leftCellX = 0; - if (showRowLabels == true) { - // For now, all row labels are 8 cells wide. TODO: make this - // adjustable. - leftCellX += 8; + if (left < 0) { + left = 0; } - Row row = getSelectedRow(); - Cell selectedColumnCell = null; - for (int i = 0; i < row.cells.size(); i++) { - if (i == selectedColumn) { - selectedColumnCell = row.cells.get(i); - break; - } - leftCellX += row.cells.get(i).getWidth() + 1; + if (selectedRow < top) { + top = selectedRow - 1; } - // There should always be a selected column. - assert (selectedColumnCell != null); - - int excessWidth = leftCellX + selectedColumnCell.getWidth() + 1 - getWidth(); - if (excessWidth > 0) { - leftCellX -= excessWidth; - } - if (leftCellX < 0) { - if (showRowLabels == true) { - leftCellX = 8; - } else { - leftCellX = 0; - } + if (top < 0) { + top = 0; } /* - * leftCellX now contains the basic left offset necessary to draw the - * cells such that the selected cell (column) is fully visible within - * this widget's given width. Or, if the widget is too narrow to - * display the full cell, leftCellX is 0 or 8. + * 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. * - * Now reset all of the X positions of the other cells so that the - * selected cell X is leftCellX. + * We need to calculate right and bottom now. */ - for (int y = 0; y < rows.size(); y++) { - // All cells to the left of selected cell. - int newCellX = leftCellX; - left = selectedColumn; - for (int x = selectedColumn - 1; x >= 0; x--) { - newCellX -= rows.get(y).cells.get(x).getWidth() + 1; - if (newCellX >= (showRowLabels ? 8 : 0)) { - rows.get(y).cells.get(x).setVisible(true); - rows.get(y).cells.get(x).setX(newCellX); - left--; - } else { - // This cell won't be visible. - rows.get(y).cells.get(x).setVisible(false); - } - } - - // Selected cell. - rows.get(y).cells.get(selectedColumn).setX(leftCellX); - assert (rows.get(y).cells.get(selectedColumn).isVisible()); + int right = left; - // All cells to the right of selected cell. - newCellX = leftCellX + selectedColumnCell.getWidth() + 1; - for (int x = selectedColumn + 1; x < columns.size(); x++) { - if (newCellX <= getWidth()) { - rows.get(y).cells.get(x).setVisible(true); - rows.get(y).cells.get(x).setX(newCellX); - } else { - // This cell won't be visible. - rows.get(y).cells.get(x).setVisible(false); + 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; + } } - newCellX += rows.get(y).cells.get(x).getWidth() + 1; + 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; } - } - // Adjust Y locations to be visible ----------------------------------- - // The same logic as above, but applied to the column Y. + } // while (!done) - // Determine if we need to shift up or down. - int topCellY = 0; - if (showColumnLabels == true) { - // For now, all column labels are 1 cell high. TODO: make this - // adjustable. - topCellY += 1; + // 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++; } - Column column = getSelectedColumn(); - Cell selectedRowCell = null; - for (int i = 0; i < column.cells.size(); i++) { - if (i == selectedRow) { - selectedRowCell = column.cells.get(i); - break; + 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; } - topCellY += column.cells.get(i).getHeight(); - // TODO: if a border is selected, add 1 to topCellY. - } - // There should always be a selected row. - assert (selectedRowCell != null); - - int excessHeight = topCellY + selectedRowCell.getHeight() - getHeight() - 1; - if (showColumnLabels == true) { - excessHeight += 1; - } - if (excessHeight > 0) { - topCellY -= excessHeight; - } - if (topCellY < 0) { - if (showColumnLabels == true) { - topCellY = 1; - } else { - topCellY = 0; + 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; } - /* - * topCellY now contains the basic top offset necessary to draw the - * cells such that the selected cell (row) is fully visible within - * this widget's given height. Or, if the widget is too short to - * display the full cell, topCellY is 0 or 1. - * - * Now reset all of the Y positions of the other cells so that the - * selected cell Y is topCellY. - */ - for (int x = 0; x < columns.size(); x++) { + int bottom = top; - if (columns.get(x).get(0).isVisible() == false) { - // This column won't be visible as determined by the checks - // above, just continue to the next. - continue; + done = false; + while (!done) { + int bottomCellY = (showColumnLabels ? COLUMN_LABEL_HEIGHT : 0); + if (topBorder != Border.NONE) { + bottomCellY++; } - - // All cells above the selected cell. - int newCellY = topCellY; - top = selectedRow; - for (int y = selectedRow - 1; y >= 0; y--) { - newCellY -= rows.get(y).cells.get(x).getHeight(); - if (newCellY >= (showColumnLabels == true ? 1 : 0)) { - rows.get(y).cells.get(x).setVisible(true); - rows.get(y).cells.get(x).setY(newCellY); - top--; - } else { - // This cell won't be visible. - rows.get(y).cells.get(x).setVisible(false); + 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) - // Selected cell. - columns.get(x).cells.get(selectedRow).setY(topCellY); - - // All cells below the selected cell. - newCellY = topCellY + selectedRowCell.getHeight(); - for (int y = selectedRow + 1; y < rows.size(); y++) { - if (newCellY <= getHeight()) { - rows.get(y).cells.get(x).setVisible(true); - rows.get(y).cells.get(x).setY(newCellY); - } else { - // This cell won't be visible. - rows.get(y).cells.get(x).setVisible(false); + // 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); } - newCellY += rows.get(y).cells.get(x).getHeight(); + 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; } }