package com.googlecode.lanterna.gui2.table;
import com.googlecode.lanterna.*;
import com.googlecode.lanterna.graphics.Theme;
import com.googlecode.lanterna.graphics.ThemeDefinition;
import com.googlecode.lanterna.gui2.Direction;
import com.googlecode.lanterna.gui2.ScrollBar;
import com.googlecode.lanterna.gui2.TextGUIGraphics;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
public class DefaultTableRenderer<V> implements TableRenderer<V> {
private final ScrollBar verticalScrollBar;
private final ScrollBar horizontalScrollBar;
private TableCellBorderStyle ;
private TableCellBorderStyle ;
private TableCellBorderStyle cellVerticalBorderStyle;
private TableCellBorderStyle cellHorizontalBorderStyle;
private TerminalSize cachedSize;
private final List<Integer> preferredColumnSizes;
private final List<Integer> preferredRowSizes;
private final Set<Integer> expandableColumns;
private int ;
public DefaultTableRenderer() {
verticalScrollBar = new ScrollBar(Direction.VERTICAL);
horizontalScrollBar = new ScrollBar(Direction.HORIZONTAL);
headerVerticalBorderStyle = TableCellBorderStyle.None;
headerHorizontalBorderStyle = TableCellBorderStyle.EmptySpace;
cellVerticalBorderStyle = TableCellBorderStyle.None;
cellHorizontalBorderStyle = TableCellBorderStyle.EmptySpace;
cachedSize = null;
preferredColumnSizes = new ArrayList<Integer>();
preferredRowSizes = new ArrayList<Integer>();
expandableColumns = new TreeSet<Integer>();
headerSizeInRows = 0;
}
public synchronized void (TableCellBorderStyle headerVerticalBorderStyle) {
this.headerVerticalBorderStyle = headerVerticalBorderStyle;
}
public synchronized void (TableCellBorderStyle headerHorizontalBorderStyle) {
this.headerHorizontalBorderStyle = headerHorizontalBorderStyle;
}
public synchronized void setCellVerticalBorderStyle(TableCellBorderStyle cellVerticalBorderStyle) {
this.cellVerticalBorderStyle = cellVerticalBorderStyle;
}
public synchronized void setCellHorizontalBorderStyle(TableCellBorderStyle cellHorizontalBorderStyle) {
this.cellHorizontalBorderStyle = cellHorizontalBorderStyle;
}
public synchronized void setExpandableColumns(Collection<Integer> expandableColumns) {
this.expandableColumns.clear();
this.expandableColumns.addAll(expandableColumns);
}
private boolean isHorizontallySpaced() {
return headerHorizontalBorderStyle != TableCellBorderStyle.None ||
cellHorizontalBorderStyle != TableCellBorderStyle.None;
}
@Override
public synchronized TerminalSize getPreferredSize(Table<V> table) {
if(!table.isInvalid() && cachedSize != null) {
return cachedSize;
}
TableModel<V> tableModel = table.getTableModel();
int viewLeftColumn = table.getViewLeftColumn();
int viewTopRow = table.getViewTopRow();
int visibleColumns = table.getVisibleColumns();
int visibleRows = table.getVisibleRows();
List<List<V>> rows = tableModel.getRows();
List<String> columnHeaders = tableModel.getColumnLabels();
TableHeaderRenderer<V> tableHeaderRenderer = table.getTableHeaderRenderer();
TableCellRenderer<V> tableCellRenderer = table.getTableCellRenderer();
if(visibleColumns == 0) {
visibleColumns = tableModel.getColumnCount();
}
if(visibleRows == 0) {
visibleRows = tableModel.getRowCount();
}
preferredColumnSizes.clear();
preferredRowSizes.clear();
if(tableModel.getColumnCount() == 0) {
return TerminalSize.ZERO;
}
if(rows.size() == 0) {
for(int columnIndex = viewLeftColumn; columnIndex < viewLeftColumn + visibleColumns; columnIndex++) {
int columnSize = tableHeaderRenderer.getPreferredSize(table, columnHeaders.get(columnIndex), columnIndex).getColumns();
int listOffset = columnIndex - viewLeftColumn;
if(preferredColumnSizes.size() == listOffset) {
preferredColumnSizes.add(columnSize);
}
else {
if(preferredColumnSizes.get(listOffset) < columnSize) {
preferredColumnSizes.set(listOffset, columnSize);
}
}
}
}
for(int rowIndex = 0; rowIndex < rows.size(); rowIndex++) {
List<V> row = rows.get(rowIndex);
for(int columnIndex = viewLeftColumn; columnIndex < Math.min(row.size(), viewLeftColumn + visibleColumns); columnIndex++) {
V cell = row.get(columnIndex);
int columnSize = tableCellRenderer.getPreferredSize(table, cell, columnIndex, rowIndex).getColumns();
int listOffset = columnIndex - viewLeftColumn;
if(preferredColumnSizes.size() == listOffset) {
preferredColumnSizes.add(columnSize);
}
else {
if(preferredColumnSizes.get(listOffset) < columnSize) {
preferredColumnSizes.set(listOffset, columnSize);
}
}
}
if(rowIndex == 0) {
for(int columnIndex = viewLeftColumn; columnIndex < Math.min(row.size(), viewLeftColumn + visibleColumns); columnIndex++) {
int columnSize = tableHeaderRenderer.getPreferredSize(table, columnHeaders.get(columnIndex), columnIndex).getColumns();
int listOffset = columnIndex - viewLeftColumn;
if(preferredColumnSizes.size() == listOffset) {
preferredColumnSizes.add(columnSize);
}
else {
if(preferredColumnSizes.get(listOffset) < columnSize) {
preferredColumnSizes.set(listOffset, columnSize);
}
}
}
}
}
for(int columnIndex = 0; columnIndex < columnHeaders.size(); columnIndex++) {
for(int rowIndex = viewTopRow; rowIndex < Math.min(rows.size(), viewTopRow + visibleRows); rowIndex++) {
V cell = rows.get(rowIndex).get(columnIndex);
int rowSize = tableCellRenderer.getPreferredSize(table, cell, columnIndex, rowIndex).getRows();
int listOffset = rowIndex - viewTopRow;
if(preferredRowSizes.size() == listOffset) {
preferredRowSizes.add(rowSize);
}
else {
if(preferredRowSizes.get(listOffset) < rowSize) {
preferredRowSizes.set(listOffset, rowSize);
}
}
}
}
int preferredRowSize = 0;
int preferredColumnSize = 0;
for(int size: preferredColumnSizes) {
preferredColumnSize += size;
}
for(int size: preferredRowSizes) {
preferredRowSize += size;
}
headerSizeInRows = 0;
for(int columnIndex = 0; columnIndex < columnHeaders.size(); columnIndex++) {
int headerRows = tableHeaderRenderer.getPreferredSize(table, columnHeaders.get(columnIndex), columnIndex).getRows();
if(headerSizeInRows < headerRows) {
headerSizeInRows = headerRows;
}
}
preferredRowSize += headerSizeInRows;
if(headerVerticalBorderStyle != TableCellBorderStyle.None) {
preferredRowSize++;
}
if(cellVerticalBorderStyle != TableCellBorderStyle.None) {
if(!rows.isEmpty()) {
preferredRowSize += Math.min(rows.size(), visibleRows) - 1;
}
}
if(isHorizontallySpaced()) {
if(!columnHeaders.isEmpty()) {
preferredColumnSize += Math.min(tableModel.getColumnCount(), visibleColumns) - 1;
}
}
if(visibleRows < rows.size()) {
preferredColumnSize++;
}
if(visibleColumns < tableModel.getColumnCount()) {
preferredRowSize++;
}
cachedSize = new TerminalSize(preferredColumnSize, preferredRowSize);
return cachedSize;
}
@Override
public TerminalPosition getCursorLocation(Table<V> component) {
return null;
}
@Override
public synchronized void drawComponent(TextGUIGraphics graphics, Table<V> table) {
TerminalSize area = graphics.getSize();
if(area.getRows() == 0 || area.getColumns() == 0) {
return;
}
if(table.isInvalid()) {
getPreferredSize(table);
}
List<Integer> columnSizes = fitColumnsInAvailableSpace(table, area);
int topPosition = drawHeader(graphics, table, columnSizes);
drawRows(graphics, table, topPosition, columnSizes);
}
private List<Integer> fitColumnsInAvailableSpace(Table<V> table, TerminalSize area) {
List<Integer> columnSizes = new ArrayList<Integer>(preferredColumnSizes);
int horizontalSpaceRequirement = 0;
int viewLeftColumn = table.getViewLeftColumn();
List<String> headers = table.getTableModel().getColumnLabels();
int visibleColumns = table.getVisibleColumns();
if(visibleColumns == 0) {
visibleColumns = table.getTableModel().getColumnCount();
}
int endColumnIndex = Math.min(headers.size(), viewLeftColumn + visibleColumns);
List<Integer> visibleExpandableColumns = new ArrayList<Integer>();
for(int index = viewLeftColumn; index < endColumnIndex; index++) {
horizontalSpaceRequirement += preferredColumnSizes.get(index - viewLeftColumn);
if(headerHorizontalBorderStyle != TableCellBorderStyle.None && index < (endColumnIndex - 1)) {
horizontalSpaceRequirement++;
}
if(expandableColumns.contains(index)) {
visibleExpandableColumns.add(index);
}
}
int extraHorizontalSpace = area.getColumns() - horizontalSpaceRequirement;
while(extraHorizontalSpace > 0 && !visibleExpandableColumns.isEmpty()) {
for(int expandableColumnIndex: visibleExpandableColumns) {
columnSizes.set(expandableColumnIndex, columnSizes.get(expandableColumnIndex) + 1);
extraHorizontalSpace--;
if(extraHorizontalSpace == 0) {
break;
}
}
}
return columnSizes;
}
private int (TextGUIGraphics graphics, Table<V> table, List<Integer> columnSizes) {
Theme theme = table.getTheme();
TableHeaderRenderer<V> tableHeaderRenderer = table.getTableHeaderRenderer();
List<String> headers = table.getTableModel().getColumnLabels();
int viewLeftColumn = table.getViewLeftColumn();
int visibleColumns = table.getVisibleColumns();
if(visibleColumns == 0) {
visibleColumns = table.getTableModel().getColumnCount();
}
int topPosition = 0;
int leftPosition = 0;
int endColumnIndex = Math.min(headers.size(), viewLeftColumn + visibleColumns);
for(int index = viewLeftColumn; index < endColumnIndex; index++) {
String label = headers.get(index);
TerminalSize size = new TerminalSize(columnSizes.get(index - viewLeftColumn), headerSizeInRows);
tableHeaderRenderer.drawHeader(table, label, index, graphics.newTextGraphics(new TerminalPosition(leftPosition, 0), size));
leftPosition += size.getColumns();
if(headerHorizontalBorderStyle != TableCellBorderStyle.None && index < (endColumnIndex - 1)) {
graphics.applyThemeStyle(theme.getDefinition(Table.class).getNormal());
graphics.setCharacter(leftPosition, 0, getVerticalCharacter(headerHorizontalBorderStyle));
leftPosition++;
}
}
topPosition += headerSizeInRows;
if(headerVerticalBorderStyle != TableCellBorderStyle.None) {
leftPosition = 0;
graphics.applyThemeStyle(theme.getDefinition(Table.class).getNormal());
for(int i = 0; i < columnSizes.size(); i++) {
if(i > 0) {
graphics.setCharacter(
leftPosition,
topPosition,
getJunctionCharacter(
headerVerticalBorderStyle,
headerHorizontalBorderStyle,
cellHorizontalBorderStyle));
leftPosition++;
}
int columnWidth = columnSizes.get(i);
graphics.drawLine(leftPosition, topPosition, leftPosition + columnWidth - 1, topPosition, getHorizontalCharacter(headerVerticalBorderStyle));
leftPosition += columnWidth;
}
if(leftPosition < graphics.getSize().getColumns()) {
graphics.drawLine(leftPosition, topPosition, graphics.getSize().getColumns() - 1, topPosition, getHorizontalCharacter(headerVerticalBorderStyle));
}
topPosition++;
}
return topPosition;
}
private void drawRows(TextGUIGraphics graphics, Table<V> table, int topPosition, List<Integer> columnSizes) {
Theme theme = table.getTheme();
ThemeDefinition themeDefinition = theme.getDefinition(Table.class);
TerminalSize area = graphics.getSize();
TableCellRenderer<V> tableCellRenderer = table.getTableCellRenderer();
TableModel<V> tableModel = table.getTableModel();
List<List<V>> rows = tableModel.getRows();
int viewTopRow = table.getViewTopRow();
int viewLeftColumn = table.getViewLeftColumn();
int visibleRows = table.getVisibleRows();
int visibleColumns = table.getVisibleColumns();
if(visibleColumns == 0) {
visibleColumns = tableModel.getColumnCount();
}
if(visibleRows == 0) {
visibleRows = tableModel.getRowCount();
}
if(visibleRows < rows.size()) {
TerminalSize verticalScrollBarPreferredSize = verticalScrollBar.getPreferredSize();
int scrollBarHeight = graphics.getSize().getRows() - topPosition;
if(visibleColumns < tableModel.getColumnCount()) {
scrollBarHeight--;
}
verticalScrollBar.setPosition(new TerminalPosition(graphics.getSize().getColumns() - verticalScrollBarPreferredSize.getColumns(), topPosition));
verticalScrollBar.setSize(verticalScrollBarPreferredSize.withRows(scrollBarHeight));
verticalScrollBar.setScrollMaximum(rows.size());
verticalScrollBar.setViewSize(visibleRows);
verticalScrollBar.setScrollPosition(viewTopRow);
if(table.getParent() != verticalScrollBar.getParent()) {
if(verticalScrollBar.getParent() != null) {
verticalScrollBar.onRemoved(verticalScrollBar.getParent());
}
if(table.getParent() != null) {
verticalScrollBar.onAdded(table.getParent());
}
}
verticalScrollBar.draw(graphics.newTextGraphics(verticalScrollBar.getPosition(), verticalScrollBar.getSize()));
graphics = graphics.newTextGraphics(TerminalPosition.TOP_LEFT_CORNER, graphics.getSize().withRelativeColumns(-verticalScrollBarPreferredSize.getColumns()));
}
if(visibleColumns < tableModel.getColumnCount()) {
TerminalSize horizontalScrollBarPreferredSize = horizontalScrollBar.getPreferredSize();
int scrollBarWidth = graphics.getSize().getColumns();
horizontalScrollBar.setPosition(new TerminalPosition(0, graphics.getSize().getRows() - horizontalScrollBarPreferredSize.getRows()));
horizontalScrollBar.setSize(horizontalScrollBarPreferredSize.withColumns(scrollBarWidth));
horizontalScrollBar.setScrollMaximum(tableModel.getColumnCount());
horizontalScrollBar.setViewSize(visibleColumns);
horizontalScrollBar.setScrollPosition(viewLeftColumn);
if(table.getParent() != horizontalScrollBar.getParent()) {
if(horizontalScrollBar.getParent() != null) {
horizontalScrollBar.onRemoved(horizontalScrollBar.getParent());
}
if(table.getParent() != null) {
horizontalScrollBar.onAdded(table.getParent());
}
}
horizontalScrollBar.draw(graphics.newTextGraphics(horizontalScrollBar.getPosition(), horizontalScrollBar.getSize()));
graphics = graphics.newTextGraphics(TerminalPosition.TOP_LEFT_CORNER, graphics.getSize().withRelativeRows(-horizontalScrollBarPreferredSize.getRows()));
}
int leftPosition;
for(int rowIndex = viewTopRow; rowIndex < Math.min(viewTopRow + visibleRows, rows.size()); rowIndex++) {
leftPosition = 0;
List<V> row = rows.get(rowIndex);
for(int columnIndex = viewLeftColumn; columnIndex < Math.min(viewLeftColumn + visibleColumns, row.size()); columnIndex++) {
if(columnIndex > viewLeftColumn) {
if(table.getSelectedRow() == rowIndex && !table.isCellSelection()) {
if(table.isFocused()) {
graphics.applyThemeStyle(themeDefinition.getActive());
}
else {
graphics.applyThemeStyle(themeDefinition.getSelected());
}
}
else {
graphics.applyThemeStyle(themeDefinition.getNormal());
}
graphics.setCharacter(leftPosition, topPosition, getVerticalCharacter(cellHorizontalBorderStyle));
leftPosition++;
}
V cell = row.get(columnIndex);
TerminalPosition cellPosition = new TerminalPosition(leftPosition, topPosition);
TerminalSize cellArea = new TerminalSize(columnSizes.get(columnIndex - viewLeftColumn), preferredRowSizes.get(rowIndex - viewTopRow));
tableCellRenderer.drawCell(table, cell, columnIndex, rowIndex, graphics.newTextGraphics(cellPosition, cellArea));
leftPosition += cellArea.getColumns();
if(leftPosition > area.getColumns()) {
break;
}
}
topPosition += preferredRowSizes.get(rowIndex - viewTopRow);
if(cellVerticalBorderStyle != TableCellBorderStyle.None) {
leftPosition = 0;
graphics.applyThemeStyle(themeDefinition.getNormal());
for(int i = 0; i < columnSizes.size(); i++) {
if(i > 0) {
graphics.setCharacter(
leftPosition,
topPosition,
getJunctionCharacter(
cellVerticalBorderStyle,
cellHorizontalBorderStyle,
cellHorizontalBorderStyle));
leftPosition++;
}
int columnWidth = columnSizes.get(i);
graphics.drawLine(leftPosition, topPosition, leftPosition + columnWidth - 1, topPosition, getHorizontalCharacter(cellVerticalBorderStyle));
leftPosition += columnWidth;
}
topPosition += 1;
}
if(topPosition > area.getRows()) {
break;
}
}
}
private char getHorizontalCharacter(TableCellBorderStyle style) {
switch(style) {
case SingleLine:
return Symbols.SINGLE_LINE_HORIZONTAL;
case DoubleLine:
return Symbols.DOUBLE_LINE_HORIZONTAL;
default:
return ' ';
}
}
private char getVerticalCharacter(TableCellBorderStyle style) {
switch(style) {
case SingleLine:
return Symbols.SINGLE_LINE_VERTICAL;
case DoubleLine:
return Symbols.DOUBLE_LINE_VERTICAL;
default:
return ' ';
}
}
private char getJunctionCharacter(TableCellBorderStyle mainStyle, TableCellBorderStyle styleAbove, TableCellBorderStyle styleBelow) {
if(mainStyle == TableCellBorderStyle.SingleLine) {
if(styleAbove == TableCellBorderStyle.SingleLine) {
if(styleBelow == TableCellBorderStyle.SingleLine) {
return Symbols.SINGLE_LINE_CROSS;
}
else if(styleBelow == TableCellBorderStyle.DoubleLine) {
return Symbols.SINGLE_LINE_T_UP;
}
else {
return Symbols.SINGLE_LINE_T_UP;
}
}
else if(styleAbove == TableCellBorderStyle.DoubleLine) {
if(styleBelow == TableCellBorderStyle.SingleLine) {
return Symbols.SINGLE_LINE_T_DOUBLE_UP;
}
else if(styleBelow == TableCellBorderStyle.DoubleLine) {
return Symbols.DOUBLE_LINE_VERTICAL_SINGLE_LINE_CROSS;
}
else {
return Symbols.SINGLE_LINE_T_DOUBLE_UP;
}
}
else {
if(styleBelow == TableCellBorderStyle.SingleLine) {
return Symbols.SINGLE_LINE_T_DOWN;
}
else if(styleBelow == TableCellBorderStyle.DoubleLine) {
return Symbols.SINGLE_LINE_T_DOUBLE_DOWN;
}
else {
return Symbols.SINGLE_LINE_HORIZONTAL;
}
}
}
else if(mainStyle == TableCellBorderStyle.DoubleLine) {
if(styleAbove == TableCellBorderStyle.SingleLine) {
if(styleBelow == TableCellBorderStyle.SingleLine) {
return Symbols.DOUBLE_LINE_HORIZONTAL_SINGLE_LINE_CROSS;
}
else if(styleBelow == TableCellBorderStyle.DoubleLine) {
return Symbols.DOUBLE_LINE_T_SINGLE_UP;
}
else {
return Symbols.DOUBLE_LINE_T_SINGLE_UP;
}
}
else if(styleAbove == TableCellBorderStyle.DoubleLine) {
if(styleBelow == TableCellBorderStyle.SingleLine) {
return Symbols.DOUBLE_LINE_T_UP;
}
else if(styleBelow == TableCellBorderStyle.DoubleLine) {
return Symbols.DOUBLE_LINE_CROSS;
}
else {
return Symbols.DOUBLE_LINE_T_UP;
}
}
else {
if(styleBelow == TableCellBorderStyle.SingleLine) {
return Symbols.DOUBLE_LINE_T_SINGLE_DOWN;
}
else if(styleBelow == TableCellBorderStyle.DoubleLine) {
return Symbols.DOUBLE_LINE_T_DOWN;
}
else {
return Symbols.DOUBLE_LINE_HORIZONTAL;
}
}
}
else {
return ' ';
}
}
}