Change build scripts
[jvcard.git] / src / com / googlecode / lanterna / gui2 / table / DefaultTableRenderer.java
CommitLineData
a3b510ab
NR
1package com.googlecode.lanterna.gui2.table;
2
3import com.googlecode.lanterna.*;
4import com.googlecode.lanterna.gui2.Direction;
5import com.googlecode.lanterna.gui2.ScrollBar;
6import com.googlecode.lanterna.gui2.TextGUIGraphics;
7
8import java.util.ArrayList;
9import java.util.List;
10
11/**
12 * Default implementation of {@code TableRenderer}
13 * @param <V> Type of data stored in each table cell
14 * @author Martin
15 */
16public class DefaultTableRenderer<V> implements TableRenderer<V> {
17
18 private final ScrollBar verticalScrollBar;
19 private final ScrollBar horizontalScrollBar;
20
21 private TableCellBorderStyle headerVerticalBorderStyle;
22 private TableCellBorderStyle headerHorizontalBorderStyle;
23 private TableCellBorderStyle cellVerticalBorderStyle;
24 private TableCellBorderStyle cellHorizontalBorderStyle;
25
26 //So that we don't have to recalculate the size every time. This still isn't optimal but shouganai.
27 private TerminalSize cachedSize;
28 private List<Integer> columnSizes;
29 private List<Integer> rowSizes;
30 private int headerSizeInRows;
31
32 /**
33 * Default constructor
34 */
35 public DefaultTableRenderer() {
36 verticalScrollBar = new ScrollBar(Direction.VERTICAL);
37 horizontalScrollBar = new ScrollBar(Direction.HORIZONTAL);
38
39 headerVerticalBorderStyle = TableCellBorderStyle.None;
40 headerHorizontalBorderStyle = TableCellBorderStyle.EmptySpace;
41 cellVerticalBorderStyle = TableCellBorderStyle.None;
42 cellHorizontalBorderStyle = TableCellBorderStyle.EmptySpace;
43
44 cachedSize = null;
45
46 columnSizes = new ArrayList<Integer>();
47 rowSizes = new ArrayList<Integer>();
48 headerSizeInRows = 0;
49 }
50
51 /**
52 * Sets the style to be used when separating the table header row from the actual "data" cells below. This will
53 * cause a new line to be added under the header labels, unless set to {@code TableCellBorderStyle.None}.
54 *
55 * @param headerVerticalBorderStyle Style to use to separate Table header from body
56 */
57 public void setHeaderVerticalBorderStyle(TableCellBorderStyle headerVerticalBorderStyle) {
58 this.headerVerticalBorderStyle = headerVerticalBorderStyle;
59 }
60
61 /**
62 * Sets the style to be used when separating the table header labels from each other. This will cause a new
63 * column to be added in between each label, unless set to {@code TableCellBorderStyle.None}.
64 *
65 * @param headerHorizontalBorderStyle Style to use when separating header columns horizontally
66 */
67 public void setHeaderHorizontalBorderStyle(TableCellBorderStyle headerHorizontalBorderStyle) {
68 this.headerHorizontalBorderStyle = headerHorizontalBorderStyle;
69 }
70
71 /**
72 * Sets the style to be used when vertically separating table cells from each other. This will cause a new line
73 * to be added between every row, unless set to {@code TableCellBorderStyle.None}.
74 *
75 * @param cellVerticalBorderStyle Style to use to separate table cells vertically
76 */
77 public void setCellVerticalBorderStyle(TableCellBorderStyle cellVerticalBorderStyle) {
78 this.cellVerticalBorderStyle = cellVerticalBorderStyle;
79 }
80
81 /**
82 * Sets the style to be used when horizontally separating table cells from each other. This will cause a new
83 * column to be added between every row, unless set to {@code TableCellBorderStyle.None}.
84 *
85 * @param cellHorizontalBorderStyle Style to use to separate table cells horizontally
86 */
87 public void setCellHorizontalBorderStyle(TableCellBorderStyle cellHorizontalBorderStyle) {
88 this.cellHorizontalBorderStyle = cellHorizontalBorderStyle;
89 }
90
91 private boolean isHorizontallySpaced() {
92 return headerHorizontalBorderStyle != TableCellBorderStyle.None ||
93 cellHorizontalBorderStyle != TableCellBorderStyle.None;
94 }
95
96 @Override
97 public TerminalSize getPreferredSize(Table<V> table) {
98 //Quick bypass if the table hasn't changed
99 if(!table.isInvalid() && cachedSize != null) {
100 return cachedSize;
101 }
102
103 TableModel<V> tableModel = table.getTableModel();
104 int viewLeftColumn = table.getViewLeftColumn();
105 int viewTopRow = table.getViewTopRow();
106 int visibleColumns = table.getVisibleColumns();
107 int visibleRows = table.getVisibleRows();
108 List<List<V>> rows = tableModel.getRows();
109 List<String> columnHeaders = tableModel.getColumnLabels();
110 TableHeaderRenderer<V> tableHeaderRenderer = table.getTableHeaderRenderer();
111 TableCellRenderer<V> tableCellRenderer = table.getTableCellRenderer();
112
113 if(visibleColumns == 0) {
114 visibleColumns = tableModel.getColumnCount();
115 }
116 if(visibleRows == 0) {
117 visibleRows = tableModel.getRowCount();
118 }
119
120 columnSizes.clear();
121 rowSizes.clear();
122
123 if(tableModel.getColumnCount() == 0) {
124 return TerminalSize.ZERO;
125 }
126
127 for(int rowIndex = 0; rowIndex < rows.size(); rowIndex++) {
128 List<V> row = rows.get(rowIndex);
129 for(int columnIndex = viewLeftColumn; columnIndex < Math.min(row.size(), viewLeftColumn + visibleColumns); columnIndex++) {
130 V cell = row.get(columnIndex);
131 int columnSize = tableCellRenderer.getPreferredSize(table, cell, columnIndex, rowIndex).getColumns();
132 int listOffset = columnIndex - viewLeftColumn;
133 if(columnSizes.size() == listOffset) {
134 columnSizes.add(columnSize);
135 }
136 else {
137 if(columnSizes.get(listOffset) < columnSize) {
138 columnSizes.set(listOffset, columnSize);
139 }
140 }
141 }
142
143 //Do the headers too, on the first iteration
144 if(rowIndex == 0) {
145 for(int columnIndex = viewLeftColumn; columnIndex < Math.min(row.size(), viewLeftColumn + visibleColumns); columnIndex++) {
146 int columnSize = tableHeaderRenderer.getPreferredSize(table, columnHeaders.get(columnIndex), columnIndex).getColumns();
147 int listOffset = columnIndex - viewLeftColumn;
148 if(columnSizes.size() == listOffset) {
149 columnSizes.add(columnSize);
150 }
151 else {
152 if(columnSizes.get(listOffset) < columnSize) {
153 columnSizes.set(listOffset, columnSize);
154 }
155 }
156 }
157 }
158 }
159
160 for(int columnIndex = 0; columnIndex < columnHeaders.size(); columnIndex++) {
161 for(int rowIndex = viewTopRow; rowIndex < Math.min(rows.size(), viewTopRow + visibleRows); rowIndex++) {
162 V cell = rows.get(rowIndex).get(columnIndex);
163 int rowSize = tableCellRenderer.getPreferredSize(table, cell, columnIndex, rowIndex).getRows();
164 int listOffset = rowIndex - viewTopRow;
165 if(rowSizes.size() == listOffset) {
166 rowSizes.add(rowSize);
167 }
168 else {
169 if(rowSizes.get(listOffset) < rowSize) {
170 rowSizes.set(listOffset, rowSize);
171 }
172 }
173 }
174 }
175
176 int preferredRowSize = 0;
177 int preferredColumnSize = 0;
178 for(int size: columnSizes) {
179 preferredColumnSize += size;
180 }
181 for(int size: rowSizes) {
182 preferredRowSize += size;
183 }
184
185 headerSizeInRows = 0;
186 for(int columnIndex = 0; columnIndex < columnHeaders.size(); columnIndex++) {
187 int headerRows = tableHeaderRenderer.getPreferredSize(table, columnHeaders.get(columnIndex), columnIndex).getRows();
188 if(headerSizeInRows < headerRows) {
189 headerSizeInRows = headerRows;
190 }
191 }
192 preferredRowSize += headerSizeInRows;
193
194 if(headerVerticalBorderStyle != TableCellBorderStyle.None) {
195 preferredRowSize++; //Spacing between header and body
196 }
197 if(cellVerticalBorderStyle != TableCellBorderStyle.None) {
198 if(!rows.isEmpty()) {
199 preferredRowSize += Math.min(rows.size(), visibleRows) - 1; //Vertical space between cells
200 }
201 }
202 if(isHorizontallySpaced()) {
203 if(!columnHeaders.isEmpty()) {
204 preferredColumnSize += Math.min(tableModel.getColumnCount(), visibleColumns) - 1; //Spacing between the columns
205 }
206 }
207
208 //Add on space taken by scrollbars (if needed)
209 if(visibleRows < rows.size()) {
210 preferredColumnSize++;
211 }
212 if(visibleColumns < tableModel.getColumnCount()) {
213 preferredRowSize++;
214 }
215
216 cachedSize = new TerminalSize(preferredColumnSize, preferredRowSize);
217 return cachedSize;
218 }
219
220 @Override
221 public TerminalPosition getCursorLocation(Table<V> component) {
222 return null;
223 }
224
225 @Override
226 public void drawComponent(TextGUIGraphics graphics, Table<V> table) {
227 //Get the size
228 TerminalSize area = graphics.getSize();
229
230 //Don't even bother
231 if(area.getRows() == 0 || area.getColumns() == 0) {
232 return;
233 }
234
235 int topPosition = drawHeader(graphics, table);
236 drawRows(graphics, table, topPosition);
237 }
238
239 private int drawHeader(TextGUIGraphics graphics, Table<V> table) {
240 TableHeaderRenderer<V> tableHeaderRenderer = table.getTableHeaderRenderer();
241 List<String> headers = table.getTableModel().getColumnLabels();
242 int viewLeftColumn = table.getViewLeftColumn();
243 int visibleColumns = table.getVisibleColumns();
244 if(visibleColumns == 0) {
245 visibleColumns = table.getTableModel().getColumnCount();
246 }
247 int topPosition = 0;
248 int leftPosition = 0;
249 int endColumnIndex = Math.min(headers.size(), viewLeftColumn + visibleColumns);
250 for(int index = viewLeftColumn; index < endColumnIndex; index++) {
251 String label = headers.get(index);
252 TerminalSize size = new TerminalSize(columnSizes.get(index - viewLeftColumn), headerSizeInRows);
253 tableHeaderRenderer.drawHeader(table, label, index, graphics.newTextGraphics(new TerminalPosition(leftPosition, 0), size));
254 leftPosition += size.getColumns();
255 if(headerHorizontalBorderStyle != TableCellBorderStyle.None && index < (endColumnIndex - 1)) {
256 graphics.applyThemeStyle(graphics.getThemeDefinition(Table.class).getNormal());
257 graphics.setCharacter(leftPosition, 0, getVerticalCharacter(headerHorizontalBorderStyle));
258 leftPosition++;
259 }
260 }
261 topPosition += headerSizeInRows;
262
263 if(headerVerticalBorderStyle != TableCellBorderStyle.None) {
264 leftPosition = 0;
265 graphics.applyThemeStyle(graphics.getThemeDefinition(Table.class).getNormal());
266 for(int i = 0; i < columnSizes.size(); i++) {
267 if(i > 0) {
268 graphics.setCharacter(
269 leftPosition,
270 topPosition,
271 getJunctionCharacter(
272 headerVerticalBorderStyle,
273 headerHorizontalBorderStyle,
274 cellHorizontalBorderStyle));
275 leftPosition++;
276 }
277 int columnWidth = columnSizes.get(i);
278 graphics.drawLine(leftPosition, topPosition, leftPosition + columnWidth - 1, topPosition, getHorizontalCharacter(headerVerticalBorderStyle));
279 leftPosition += columnWidth;
280 }
281 //Expand out the line in case the area is bigger
282 if(leftPosition < graphics.getSize().getColumns()) {
283 graphics.drawLine(leftPosition, topPosition, graphics.getSize().getColumns() - 1, topPosition, getHorizontalCharacter(headerVerticalBorderStyle));
284 }
285 topPosition++;
286 }
287 return topPosition;
288 }
289
290 private void drawRows(TextGUIGraphics graphics, Table<V> table, int topPosition) {
291 TerminalSize area = graphics.getSize();
292 TableCellRenderer<V> tableCellRenderer = table.getTableCellRenderer();
293 TableModel<V> tableModel = table.getTableModel();
294 List<List<V>> rows = tableModel.getRows();
295 int viewTopRow = table.getViewTopRow();
296 int viewLeftColumn = table.getViewLeftColumn();
297 int visibleRows = table.getVisibleRows();
298 int visibleColumns = table.getVisibleColumns();
299 if(visibleColumns == 0) {
300 visibleColumns = tableModel.getColumnCount();
301 }
302 if(visibleRows == 0) {
303 visibleRows = tableModel.getRowCount();
304 }
305
306 //Exit if there are no rows
307 if(rows.isEmpty()) {
308 return;
309 }
310
311 //Draw scrollbars (if needed)
312 if(visibleRows < rows.size()) {
313 TerminalSize verticalScrollBarPreferredSize = verticalScrollBar.getPreferredSize();
314 int scrollBarHeight = graphics.getSize().getRows() - topPosition;
315 if(visibleColumns < tableModel.getColumnCount()) {
316 scrollBarHeight--;
317 }
318 verticalScrollBar.setPosition(new TerminalPosition(graphics.getSize().getColumns() - verticalScrollBarPreferredSize.getColumns(), topPosition));
319 verticalScrollBar.setSize(verticalScrollBarPreferredSize.withRows(scrollBarHeight));
320 verticalScrollBar.setScrollMaximum(rows.size());
321 verticalScrollBar.setViewSize(visibleRows);
322 verticalScrollBar.setScrollPosition(viewTopRow);
323 verticalScrollBar.draw(graphics.newTextGraphics(verticalScrollBar.getPosition(), verticalScrollBar.getSize()));
324 graphics = graphics.newTextGraphics(TerminalPosition.TOP_LEFT_CORNER, graphics.getSize().withRelativeColumns(-verticalScrollBarPreferredSize.getColumns()));
325 }
326 if(visibleColumns < tableModel.getColumnCount()) {
327 TerminalSize horizontalScrollBarPreferredSize = horizontalScrollBar.getPreferredSize();
328 int scrollBarWidth = graphics.getSize().getColumns();
329 horizontalScrollBar.setPosition(new TerminalPosition(0, graphics.getSize().getRows() - horizontalScrollBarPreferredSize.getRows()));
330 horizontalScrollBar.setSize(horizontalScrollBarPreferredSize.withColumns(scrollBarWidth));
331 horizontalScrollBar.setScrollMaximum(tableModel.getColumnCount());
332 horizontalScrollBar.setViewSize(visibleColumns);
333 horizontalScrollBar.setScrollPosition(viewLeftColumn);
334 horizontalScrollBar.draw(graphics.newTextGraphics(horizontalScrollBar.getPosition(), horizontalScrollBar.getSize()));
335 graphics = graphics.newTextGraphics(TerminalPosition.TOP_LEFT_CORNER, graphics.getSize().withRelativeRows(-horizontalScrollBarPreferredSize.getRows()));
336 }
337
338 int leftPosition;
339 for(int rowIndex = viewTopRow; rowIndex < Math.min(viewTopRow + visibleRows, rows.size()); rowIndex++) {
340 leftPosition = 0;
341 List<V> row = rows.get(rowIndex);
342 for(int columnIndex = viewLeftColumn; columnIndex < Math.min(viewLeftColumn + visibleColumns, row.size()); columnIndex++) {
343 if(columnIndex > viewLeftColumn) {
344 if(table.getSelectedRow() == rowIndex && !table.isCellSelection()) {
345 if(table.isFocused()) {
346 graphics.applyThemeStyle(graphics.getThemeDefinition(Table.class).getActive());
347 }
348 else {
349 graphics.applyThemeStyle(graphics.getThemeDefinition(Table.class).getSelected());
350 }
351 }
352 else {
353 graphics.applyThemeStyle(graphics.getThemeDefinition(Table.class).getNormal());
354 }
355 graphics.setCharacter(leftPosition, topPosition, getVerticalCharacter(cellHorizontalBorderStyle));
356 leftPosition++;
357 }
358 V cell = row.get(columnIndex);
359 TerminalPosition cellPosition = new TerminalPosition(leftPosition, topPosition);
360 TerminalSize cellArea = new TerminalSize(columnSizes.get(columnIndex - viewLeftColumn), rowSizes.get(rowIndex - viewTopRow));
361 tableCellRenderer.drawCell(table, cell, columnIndex, rowIndex, graphics.newTextGraphics(cellPosition, cellArea));
362 leftPosition += cellArea.getColumns();
363 if(leftPosition > area.getColumns()) {
364 break;
365 }
366 }
367 topPosition += rowSizes.get(rowIndex - viewTopRow);
368 if(cellVerticalBorderStyle != TableCellBorderStyle.None) {
369 leftPosition = 0;
370 graphics.applyThemeStyle(graphics.getThemeDefinition(Table.class).getNormal());
371 for(int i = 0; i < columnSizes.size(); i++) {
372 if(i > 0) {
373 graphics.setCharacter(
374 leftPosition,
375 topPosition,
376 getJunctionCharacter(
377 cellVerticalBorderStyle,
378 cellHorizontalBorderStyle,
379 cellHorizontalBorderStyle));
380 leftPosition++;
381 }
382 int columnWidth = columnSizes.get(i);
383 graphics.drawLine(leftPosition, topPosition, leftPosition + columnWidth - 1, topPosition, getHorizontalCharacter(cellVerticalBorderStyle));
384 leftPosition += columnWidth;
385 }
386 topPosition += 1;
387 }
388 if(topPosition > area.getRows()) {
389 break;
390 }
391 }
392 }
393
394 private char getHorizontalCharacter(TableCellBorderStyle style) {
395 switch(style) {
396 case SingleLine:
397 return Symbols.SINGLE_LINE_HORIZONTAL;
398 case DoubleLine:
399 return Symbols.DOUBLE_LINE_HORIZONTAL;
400 default:
401 return ' ';
402 }
403 }
404
405 private char getVerticalCharacter(TableCellBorderStyle style) {
406 switch(style) {
407 case SingleLine:
408 return Symbols.SINGLE_LINE_VERTICAL;
409 case DoubleLine:
410 return Symbols.DOUBLE_LINE_VERTICAL;
411 default:
412 return ' ';
413 }
414 }
415
416 private char getJunctionCharacter(TableCellBorderStyle mainStyle, TableCellBorderStyle styleAbove, TableCellBorderStyle styleBelow) {
417 if(mainStyle == TableCellBorderStyle.SingleLine) {
418 if(styleAbove == TableCellBorderStyle.SingleLine) {
419 if(styleBelow == TableCellBorderStyle.SingleLine) {
420 return Symbols.SINGLE_LINE_CROSS;
421 }
422 else if(styleBelow == TableCellBorderStyle.DoubleLine) {
423 //There isn't any character for this, give upper side priority
424 return Symbols.SINGLE_LINE_T_UP;
425 }
426 else {
427 return Symbols.SINGLE_LINE_T_UP;
428 }
429 }
430 else if(styleAbove == TableCellBorderStyle.DoubleLine) {
431 if(styleBelow == TableCellBorderStyle.SingleLine) {
432 //There isn't any character for this, give upper side priority
433 return Symbols.SINGLE_LINE_T_DOUBLE_UP;
434 }
435 else if(styleBelow == TableCellBorderStyle.DoubleLine) {
436 return Symbols.DOUBLE_LINE_VERTICAL_SINGLE_LINE_CROSS;
437 }
438 else {
439 return Symbols.SINGLE_LINE_T_DOUBLE_UP;
440 }
441 }
442 else {
443 if(styleBelow == TableCellBorderStyle.SingleLine) {
444 return Symbols.SINGLE_LINE_T_DOWN;
445 }
446 else if(styleBelow == TableCellBorderStyle.DoubleLine) {
447 return Symbols.SINGLE_LINE_T_DOUBLE_DOWN;
448 }
449 else {
450 return Symbols.SINGLE_LINE_HORIZONTAL;
451 }
452 }
453 }
454 else if(mainStyle == TableCellBorderStyle.DoubleLine) {
455 if(styleAbove == TableCellBorderStyle.SingleLine) {
456 if(styleBelow == TableCellBorderStyle.SingleLine) {
457 return Symbols.DOUBLE_LINE_HORIZONTAL_SINGLE_LINE_CROSS;
458 }
459 else if(styleBelow == TableCellBorderStyle.DoubleLine) {
460 //There isn't any character for this, give upper side priority
461 return Symbols.DOUBLE_LINE_T_SINGLE_UP;
462 }
463 else {
464 return Symbols.DOUBLE_LINE_T_SINGLE_UP;
465 }
466 }
467 else if(styleAbove == TableCellBorderStyle.DoubleLine) {
468 if(styleBelow == TableCellBorderStyle.SingleLine) {
469 //There isn't any character for this, give upper side priority
470 return Symbols.DOUBLE_LINE_T_UP;
471 }
472 else if(styleBelow == TableCellBorderStyle.DoubleLine) {
473 return Symbols.DOUBLE_LINE_CROSS;
474 }
475 else {
476 return Symbols.DOUBLE_LINE_T_UP;
477 }
478 }
479 else {
480 if(styleBelow == TableCellBorderStyle.SingleLine) {
481 return Symbols.DOUBLE_LINE_T_SINGLE_DOWN;
482 }
483 else if(styleBelow == TableCellBorderStyle.DoubleLine) {
484 return Symbols.DOUBLE_LINE_T_DOWN;
485 }
486 else {
487 return Symbols.DOUBLE_LINE_HORIZONTAL;
488 }
489 }
490 }
491 else {
492 return ' ';
493 }
494 }
495}