2 * Jexer - Java Text User Interface
4 * The MIT License (MIT)
6 * Copyright (C) 2019 Kevin Lamonte
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
31 import java
.io
.IOException
;
32 import java
.util
.ArrayList
;
33 import java
.util
.List
;
34 import java
.util
.ResourceBundle
;
36 import jexer
.bits
.CellAttributes
;
37 import jexer
.event
.TKeypressEvent
;
38 import jexer
.event
.TMouseEvent
;
39 import jexer
.event
.TResizeEvent
;
40 import static jexer
.TKeypress
.*;
43 * TTableWidget is used to display and edit regular two-dimensional tables of
46 * This class was inspired by a TTable implementation originally developed by
47 * David "Niki" ROULET [niki@nikiroo.be], made available under MIT at
48 * https://github.com/nikiroo/jexer/tree/ttable_pull.
50 public class TTableWidget
extends TWidget
{
55 private static final ResourceBundle i18n
= ResourceBundle
.getBundle(TTableWidget
.class.getName());
57 // ------------------------------------------------------------------------
58 // Constants --------------------------------------------------------------
59 // ------------------------------------------------------------------------
62 * Available borders for cells.
71 * Single bar: \u2502 (vertical) and \u2500 (horizontal).
76 * Double bar: \u2551 (vertical) and \u2550 (horizontal).
81 * Thick bar: \u2503 (vertical heavy) and \u2501 (horizontal heavy).
87 * If true, put a grid of numbers in the cells.
89 private static final boolean DEBUG
= false;
94 private static final int ROW_LABEL_WIDTH
= 8;
97 * Column label height.
99 private static final int COLUMN_LABEL_HEIGHT
= 1;
102 * Column default width.
104 private static final int COLUMN_DEFAULT_WIDTH
= 8;
109 private static final int EXTRA_ROWS
= (DEBUG ?
10 : 0);
112 * Extra columns to add.
114 private static final int EXTRA_COLUMNS
= (DEBUG ?
10 * (8 + 1) : 0);
116 // ------------------------------------------------------------------------
117 // Variables --------------------------------------------------------------
118 // ------------------------------------------------------------------------
121 * The underlying data, organized as columns.
123 private ArrayList
<Column
> columns
= new ArrayList
<Column
>();
126 * The underlying data, organized as rows.
128 private ArrayList
<Row
> rows
= new ArrayList
<Row
>();
131 * The row in model corresponding to the top-left visible cell.
136 * The column in model corresponding to the top-left visible cell.
138 private int left
= 0;
141 * The row in model corresponding to the currently selected cell.
143 private int selectedRow
= 0;
146 * The column in model corresponding to the currently selected cell.
148 private int selectedColumn
= 0;
151 * If true, highlight the entire row of the currently-selected cell.
153 private boolean highlightRow
= false;
156 * If true, highlight the entire column of the currently-selected cell.
158 private boolean highlightColumn
= false;
161 * If true, show the row labels as the first column.
163 private boolean showRowLabels
= true;
166 * If true, show the column labels as the first row.
168 private boolean showColumnLabels
= true;
171 * The top border for the first row.
173 private Border topBorder
= Border
.NONE
;
176 * The left border for the first column.
178 private Border leftBorder
= Border
.NONE
;
181 * Column represents a column of cells.
183 public class Column
{
186 * X position of this column.
193 private int width
= COLUMN_DEFAULT_WIDTH
;
196 * The cells of this column.
198 private ArrayList
<Cell
> cells
= new ArrayList
<Cell
>();
203 private String label
= "";
206 * The right border for this column.
208 private Border rightBorder
= Border
.NONE
;
211 * Constructor sets label to lettered column.
213 * @param col column number to use for this column. Column 0 will be
214 * "A", column 1 will be "B", column 26 will be "AA", and so on.
217 label
= makeColumnLabel(col
);
221 * Add an entry to this column.
223 * @param cell the cell to add
225 public void add(final Cell cell
) {
230 * Get an entry from this column.
232 * @param row the entry index to get
233 * @return the cell at row
235 public Cell
get(final int row
) {
236 return cells
.get(row
);
240 * Get the X position of the cells in this column.
242 * @return the position
249 * Set the X position of the cells in this column.
251 * @param x the position
253 public void setX(final int x
) {
254 for (Cell cell
: cells
) {
263 * Row represents a row of cells.
268 * Y position of this row.
275 private int height
= 1;
278 * The cells of this row.
280 private ArrayList
<Cell
> cells
= new ArrayList
<Cell
>();
285 private String label
= "";
288 * The bottom border for this row.
290 private Border bottomBorder
= Border
.NONE
;
293 * Constructor sets label to numbered row.
295 * @param row row number to use for this row
298 label
= Integer
.toString(row
);
302 * Add an entry to this column.
304 * @param cell the cell to add
306 public void add(final Cell cell
) {
311 * Get an entry from this row.
313 * @param column the entry index to get
314 * @return the cell at column
316 public Cell
get(final int column
) {
317 return cells
.get(column
);
320 * Get the Y position of the cells in this column.
322 * @return the position
329 * Set the Y position of the cells in this column.
331 * @param y the position
333 public void setY(final int y
) {
334 for (Cell cell
: cells
) {
343 * Cell represents an editable cell in the table. Normally, navigation
344 * to a cell only highlights it; pressing Enter or F2 will switch to
347 public class Cell
extends TWidget
{
349 // --------------------------------------------------------------------
350 // Variables ----------------------------------------------------------
351 // --------------------------------------------------------------------
354 * The field containing the cell's data.
356 private TField field
;
359 * The column of this cell.
364 * The row of this cell.
369 * If true, the cell is being edited.
371 private boolean isEditing
= false;
374 * If true, the cell is read-only (non-editable).
376 private boolean readOnly
= false;
379 * Text of field before editing.
381 private String fieldText
;
383 // --------------------------------------------------------------------
384 // Constructors -------------------------------------------------------
385 // --------------------------------------------------------------------
388 * Public constructor.
390 * @param parent parent widget
391 * @param x column relative to parent
392 * @param y row relative to parent
393 * @param width width of widget
394 * @param height height of widget
395 * @param column column index of this cell
396 * @param row row index of this cell
398 public Cell(final TTableWidget parent
, final int x
, final int y
,
399 final int width
, final int height
, final int column
,
402 super(parent
, x
, y
, width
, height
);
403 this.column
= column
;
406 field
= addField(0, 0, width
, false);
407 field
.setEnabled(false);
408 field
.setBackgroundChar(' ');
411 // --------------------------------------------------------------------
412 // Event handlers -----------------------------------------------------
413 // --------------------------------------------------------------------
416 * Handle mouse double-click events.
418 * @param mouse mouse double-click event
421 public void onMouseDoubleClick(final TMouseEvent mouse
) {
422 // Use TWidget's code to pass the event to the children.
423 super.onMouseDown(mouse
);
425 // Double-click means to start editing.
426 fieldText
= field
.getText();
428 field
.setEnabled(true);
432 // Let the table know that I was activated.
433 ((TTableWidget
) getParent()).selectedRow
= row
;
434 ((TTableWidget
) getParent()).selectedColumn
= column
;
435 ((TTableWidget
) getParent()).alignGrid();
440 * Handle mouse press events.
442 * @param mouse mouse button press event
445 public void onMouseDown(final TMouseEvent mouse
) {
446 // Use TWidget's code to pass the event to the children.
447 super.onMouseDown(mouse
);
450 // Let the table know that I was activated.
451 ((TTableWidget
) getParent()).selectedRow
= row
;
452 ((TTableWidget
) getParent()).selectedColumn
= column
;
453 ((TTableWidget
) getParent()).alignGrid();
458 * Handle mouse release events.
460 * @param mouse mouse button release event
463 public void onMouseUp(final TMouseEvent mouse
) {
464 // Use TWidget's code to pass the event to the children.
465 super.onMouseDown(mouse
);
468 // Let the table know that I was activated.
469 ((TTableWidget
) getParent()).selectedRow
= row
;
470 ((TTableWidget
) getParent()).selectedColumn
= column
;
471 ((TTableWidget
) getParent()).alignGrid();
478 * @param keypress keystroke event
481 public void onKeypress(final TKeypressEvent keypress
) {
482 // System.err.println("Cell onKeypress: " + keypress);
485 // Read only: do nothing.
490 if (keypress
.equals(kbEsc
)) {
491 // ESC cancels the edit.
495 if (keypress
.equals(kbEnter
)) {
496 // Enter ends editing.
498 // Pass down to field first so that it can execute
499 // enterAction if specified.
500 super.onKeypress(keypress
);
502 fieldText
= field
.getText();
504 field
.setEnabled(false);
507 // Pass down to field.
508 super.onKeypress(keypress
);
511 if (keypress
.equals(kbEnter
) || keypress
.equals(kbF2
)) {
512 // Enter or F2 starts editing.
513 fieldText
= field
.getText();
515 field
.setEnabled(true);
521 // --------------------------------------------------------------------
522 // TWidget ------------------------------------------------------------
523 // --------------------------------------------------------------------
530 TTableWidget table
= (TTableWidget
) getParent();
532 if (isAbsoluteActive()) {
534 field
.setActiveColorKey("tfield.active");
535 field
.setInactiveColorKey("tfield.inactive");
537 field
.setActiveColorKey("ttable.selected");
538 field
.setInactiveColorKey("ttable.selected");
540 } else if (((table
.selectedColumn
== column
)
541 && ((table
.selectedRow
== row
)
542 || (table
.highlightColumn
== true)))
543 || ((table
.selectedRow
== row
)
544 && ((table
.selectedColumn
== column
)
545 || (table
.highlightRow
== true)))
547 field
.setActiveColorKey("ttable.active");
548 field
.setInactiveColorKey("ttable.active");
550 field
.setActiveColorKey("ttable.active");
551 field
.setInactiveColorKey("ttable.inactive");
554 assert (isVisible() == true);
559 // --------------------------------------------------------------------
560 // TTable.Cell --------------------------------------------------------
561 // --------------------------------------------------------------------
568 public final String
getText() {
569 return field
.getText();
575 * @param text the new field text
577 public void setText(final String text
) {
582 * Cancel any pending edit.
584 public void cancelEdit() {
585 // Cancel any pending edit.
586 if (fieldText
!= null) {
587 field
.setText(fieldText
);
590 field
.setEnabled(false);
594 * Set an entire column of cells read-only (non-editable) or not.
596 * @param readOnly if true, the cells will be non-editable
598 public void setReadOnly(final boolean readOnly
) {
600 this.readOnly
= readOnly
;
605 // ------------------------------------------------------------------------
606 // Constructors -----------------------------------------------------------
607 // ------------------------------------------------------------------------
610 * Public constructor.
612 * @param parent parent widget
613 * @param x column relative to parent
614 * @param y row relative to parent
615 * @param width width of widget
616 * @param height height of widget
618 public TTableWidget(final TWidget parent
, final int x
, final int y
,
619 final int width
, final int height
) {
621 super(parent
, x
, y
, width
, height
);
623 // Initialize the starting row and column.
624 rows
.add(new Row(0));
625 columns
.add(new Column(0));
627 // Place a grid of cells that fit in this space.
629 for (int i
= 0; i
< height
+ EXTRA_ROWS
; i
+= rows
.get(0).height
) {
631 for (int j
= 0; j
< width
+ EXTRA_COLUMNS
;
632 j
+= columns
.get(0).width
) {
634 Cell cell
= new Cell(this, 0, 0, columns
.get(0).width
,
635 rows
.get(0).height
, column
, row
);
638 // For debugging: set a grid of cell index labels.
639 cell
.setText("" + row
+ " " + column
);
641 rows
.get(row
).add(cell
);
642 columns
.get(column
).add(cell
);
644 (j
+ columns
.get(0).width
< width
+ EXTRA_COLUMNS
)
646 columns
.add(new Column(column
+ 1));
650 if (i
+ rows
.get(0).height
< height
+ EXTRA_ROWS
) {
651 rows
.add(new Row(row
+ 1));
655 for (int i
= 0; i
< rows
.size(); i
++) {
656 rows
.get(i
).setY(i
+ (showColumnLabels ? COLUMN_LABEL_HEIGHT
: 0));
658 for (int j
= 0; j
< columns
.size(); j
++) {
659 columns
.get(j
).setX((j
* COLUMN_DEFAULT_WIDTH
) +
660 (showRowLabels ? ROW_LABEL_WIDTH
: 0));
662 activate(columns
.get(selectedColumn
).get(selectedRow
));
667 // ------------------------------------------------------------------------
668 // Event handlers ---------------------------------------------------------
669 // ------------------------------------------------------------------------
672 * Handle mouse press events.
674 * @param mouse mouse button press event
677 public void onMouseDown(final TMouseEvent mouse
) {
678 if (mouse
.isMouseWheelUp() || mouse
.isMouseWheelDown()) {
679 // Treat wheel up/down as 3 up/down
680 TKeypressEvent keyEvent
;
681 if (mouse
.isMouseWheelUp()) {
682 keyEvent
= new TKeypressEvent(kbUp
);
684 keyEvent
= new TKeypressEvent(kbDown
);
686 for (int i
= 0; i
< 3; i
++) {
687 onKeypress(keyEvent
);
692 // Use TWidget's code to pass the event to the children.
693 super.onMouseDown(mouse
);
699 * @param keypress keystroke event
702 public void onKeypress(final TKeypressEvent keypress
) {
703 if (keypress
.equals(kbTab
)
704 || keypress
.equals(kbShiftTab
)
706 // Squash tab and back-tab. They don't make sense in the TTable
711 // If editing, pass to that cell and do nothing else.
712 if (getSelectedCell().isEditing
) {
713 super.onKeypress(keypress
);
717 if (keypress
.equals(kbLeft
)) {
719 if (selectedColumn
> 0) {
722 activate(columns
.get(selectedColumn
).get(selectedRow
));
723 } else if (keypress
.equals(kbRight
)) {
725 if (selectedColumn
< columns
.size() - 1) {
728 activate(columns
.get(selectedColumn
).get(selectedRow
));
729 } else if (keypress
.equals(kbUp
)) {
731 if (selectedRow
> 0) {
734 activate(columns
.get(selectedColumn
).get(selectedRow
));
735 } else if (keypress
.equals(kbDown
)) {
737 if (selectedRow
< rows
.size() - 1) {
740 activate(columns
.get(selectedColumn
).get(selectedRow
));
741 } else if (keypress
.equals(kbHome
)) {
742 // Home - leftmost column
744 activate(columns
.get(selectedColumn
).get(selectedRow
));
745 } else if (keypress
.equals(kbEnd
)) {
746 // End - rightmost column
747 selectedColumn
= columns
.size() - 1;
748 activate(columns
.get(selectedColumn
).get(selectedRow
));
749 } else if (keypress
.equals(kbPgUp
)) {
750 // PgUp - Treat like multiple up
751 for (int i
= 0; i
< getHeight() - 2; i
++) {
752 if (selectedRow
> 0) {
756 activate(columns
.get(selectedColumn
).get(selectedRow
));
757 } else if (keypress
.equals(kbPgDn
)) {
758 // PgDn - Treat like multiple up
759 for (int i
= 0; i
< getHeight() - 2; i
++) {
760 if (selectedRow
< rows
.size() - 1) {
764 activate(columns
.get(selectedColumn
).get(selectedRow
));
765 } else if (keypress
.equals(kbCtrlHome
)) {
766 // Ctrl-Home - go to top-left
769 activate(columns
.get(selectedColumn
).get(selectedRow
));
770 activate(columns
.get(selectedColumn
).get(selectedRow
));
771 } else if (keypress
.equals(kbCtrlEnd
)) {
772 // Ctrl-End - go to bottom-right
773 selectedRow
= rows
.size() - 1;
774 selectedColumn
= columns
.size() - 1;
775 activate(columns
.get(selectedColumn
).get(selectedRow
));
776 activate(columns
.get(selectedColumn
).get(selectedRow
));
779 super.onKeypress(keypress
);
782 // We may have scrolled off screen. Reset positions as needed to
783 // make the newly selected cell visible.
788 * Handle widget resize events.
790 * @param event resize event
793 public void onResize(final TResizeEvent event
) {
794 super.onResize(event
);
799 // ------------------------------------------------------------------------
800 // TWidget ----------------------------------------------------------------
801 // ------------------------------------------------------------------------
804 * Draw the table row/column labels, and borders.
808 CellAttributes labelColor
= getTheme().getColor("ttable.label");
809 CellAttributes labelColorSelected
= getTheme().getColor("ttable.label.selected");
810 CellAttributes borderColor
= getTheme().getColor("ttable.border");
813 if (showColumnLabels
== true) {
814 for (int i
= left
; i
< columns
.size(); i
++) {
815 if (columns
.get(i
).get(top
).isVisible() == false) {
818 putStringXY(columns
.get(i
).get(top
).getX(), 0,
819 String
.format(" %-" +
820 (columns
.get(i
).width
- 2)
821 + "s ", columns
.get(i
).label
),
822 (i
== selectedColumn ? labelColorSelected
: labelColor
));
827 if (showRowLabels
== true) {
828 for (int i
= top
; i
< rows
.size(); i
++) {
829 if (rows
.get(i
).get(left
).isVisible() == false) {
832 putStringXY(0, rows
.get(i
).get(left
).getY(),
833 String
.format(" %-6s ", rows
.get(i
).label
),
834 (i
== selectedRow ? labelColorSelected
: labelColor
));
838 // Draw vertical borders.
839 if (leftBorder
== Border
.SINGLE
) {
840 vLineXY((showRowLabels ? ROW_LABEL_WIDTH
: 0),
841 (topBorder
== Border
.NONE ?
0 : 1) +
842 (showColumnLabels ? COLUMN_LABEL_HEIGHT
: 0),
843 getHeight(), '\u2502', borderColor
);
845 for (int i
= left
; i
< columns
.size(); i
++) {
846 if (columns
.get(i
).get(top
).isVisible() == false) {
849 if (columns
.get(i
).rightBorder
== Border
.SINGLE
) {
850 vLineXY(columns
.get(i
).getX() + columns
.get(i
).width
,
851 (topBorder
== Border
.NONE ?
0 : 1) +
852 (showColumnLabels ? COLUMN_LABEL_HEIGHT
: 0),
853 getHeight(), '\u2502', borderColor
);
857 // Draw horizontal borders.
858 if (topBorder
== Border
.SINGLE
) {
859 hLineXY((showRowLabels ? ROW_LABEL_WIDTH
: 0),
860 (showColumnLabels ? COLUMN_LABEL_HEIGHT
: 0),
861 getWidth(), '\u2500', borderColor
);
863 for (int i
= top
; i
< rows
.size(); i
++) {
864 if (rows
.get(i
).get(left
).isVisible() == false) {
867 if (rows
.get(i
).bottomBorder
== Border
.SINGLE
) {
868 hLineXY((leftBorder
== Border
.NONE ?
0 : 1) +
869 (showRowLabels ? ROW_LABEL_WIDTH
: 0),
870 rows
.get(i
).getY() + rows
.get(i
).height
- 1,
871 getWidth(), '\u2500', borderColor
);
872 } else if (rows
.get(i
).bottomBorder
== Border
.DOUBLE
) {
873 hLineXY((leftBorder
== Border
.NONE ?
0 : 1) +
874 (showRowLabels ? ROW_LABEL_WIDTH
: 0),
875 rows
.get(i
).getY() + rows
.get(i
).height
- 1,
876 getWidth(), '\u2550', borderColor
);
877 } else if (rows
.get(i
).bottomBorder
== Border
.THICK
) {
878 hLineXY((leftBorder
== Border
.NONE ?
0 : 1) +
879 (showRowLabels ? ROW_LABEL_WIDTH
: 0),
880 rows
.get(i
).getY() + rows
.get(i
).height
- 1,
881 getWidth(), '\u2501', borderColor
);
884 // Top-left corner if needed
885 if ((topBorder
== Border
.SINGLE
) && (leftBorder
== Border
.SINGLE
)) {
886 putCharXY((showRowLabels ? ROW_LABEL_WIDTH
: 0),
887 (showColumnLabels ? COLUMN_LABEL_HEIGHT
: 0),
888 '\u250c', borderColor
);
891 // Now draw the correct corners
892 for (int i
= top
; i
< rows
.size(); i
++) {
893 if (rows
.get(i
).get(left
).isVisible() == false) {
896 for (int j
= left
; j
< columns
.size(); j
++) {
897 if (columns
.get(j
).get(i
).isVisible() == false) {
900 if ((i
== top
) && (topBorder
== Border
.SINGLE
)
901 && (columns
.get(j
).rightBorder
== Border
.SINGLE
)
904 putCharXY(columns
.get(j
).getX() + columns
.get(j
).width
,
905 (showColumnLabels ? COLUMN_LABEL_HEIGHT
: 0),
906 '\u252c', borderColor
);
908 if ((j
== left
) && (leftBorder
== Border
.SINGLE
)
909 && (rows
.get(i
).bottomBorder
== Border
.SINGLE
)
912 putCharXY((showRowLabels ? ROW_LABEL_WIDTH
: 0),
913 rows
.get(i
).getY() + rows
.get(i
).height
- 1,
914 '\u251c', borderColor
);
916 if ((columns
.get(j
).rightBorder
== Border
.SINGLE
)
917 && (rows
.get(i
).bottomBorder
== Border
.SINGLE
)
919 // Intersection of single bars
920 putCharXY(columns
.get(j
).getX() + columns
.get(j
).width
,
921 rows
.get(i
).getY() + rows
.get(i
).height
- 1,
922 '\u253c', borderColor
);
924 if ((j
== left
) && (leftBorder
== Border
.SINGLE
)
925 && (rows
.get(i
).bottomBorder
== Border
.DOUBLE
)
927 // Left tee: single bar vertical, double bar horizontal
928 putCharXY((showRowLabels ? ROW_LABEL_WIDTH
: 0),
929 rows
.get(i
).getY() + rows
.get(i
).height
- 1,
930 '\u255e', borderColor
);
932 if ((j
== left
) && (leftBorder
== Border
.SINGLE
)
933 && (rows
.get(i
).bottomBorder
== Border
.THICK
)
935 // Left tee: single bar vertical, thick bar horizontal
936 putCharXY((showRowLabels ? ROW_LABEL_WIDTH
: 0),
937 rows
.get(i
).getY() + rows
.get(i
).height
- 1,
938 '\u251d', borderColor
);
940 if ((columns
.get(j
).rightBorder
== Border
.SINGLE
)
941 && (rows
.get(i
).bottomBorder
== Border
.DOUBLE
)
943 // Intersection: single bar vertical, double bar
945 putCharXY(columns
.get(j
).getX() + columns
.get(j
).width
,
946 rows
.get(i
).getY() + rows
.get(i
).height
- 1,
947 '\u256a', borderColor
);
949 if ((columns
.get(j
).rightBorder
== Border
.SINGLE
)
950 && (rows
.get(i
).bottomBorder
== Border
.THICK
)
952 // Intersection: single bar vertical, thick bar
954 putCharXY(columns
.get(j
).getX() + columns
.get(j
).width
,
955 rows
.get(i
).getY() + rows
.get(i
).height
- 1,
956 '\u253f', borderColor
);
961 // Now draw the window borders.
965 // ------------------------------------------------------------------------
966 // TTable -----------------------------------------------------------------
967 // ------------------------------------------------------------------------
970 * Generate the default letter name for a column number.
972 * @param col column number to use for this column. Column 0 will be
973 * "A", column 1 will be "B", column 26 will be "AA", and so on.
975 private String
makeColumnLabel(int col
) {
976 StringBuilder sb
= new StringBuilder();
978 sb
.append((char) ('A' + (col
% 26)));
984 return sb
.reverse().toString();
988 * Get the currently-selected cell.
990 * @return the selected cell
992 public Cell
getSelectedCell() {
993 assert (rows
.get(selectedRow
) != null);
994 assert (rows
.get(selectedRow
).get(selectedColumn
) != null);
995 assert (columns
.get(selectedColumn
) != null);
996 assert (columns
.get(selectedColumn
).get(selectedRow
) != null);
997 assert (rows
.get(selectedRow
).get(selectedColumn
) ==
998 columns
.get(selectedColumn
).get(selectedRow
));
1000 return (columns
.get(selectedColumn
).get(selectedRow
));
1004 * Get the currently-selected column.
1006 * @return the selected column
1008 public Column
getSelectedColumn() {
1009 assert (selectedColumn
>= 0);
1010 assert (columns
.size() > selectedColumn
);
1011 assert (columns
.get(selectedColumn
) != null);
1012 return columns
.get(selectedColumn
);
1016 * Get the currently-selected row.
1018 * @return the selected row
1020 public Row
getSelectedRow() {
1021 assert (selectedRow
>= 0);
1022 assert (rows
.size() > selectedRow
);
1023 assert (rows
.get(selectedRow
) != null);
1024 return rows
.get(selectedRow
);
1028 * Get the currently-selected column number. 0 is the left-most column.
1030 * @return the selected column number
1032 public int getSelectedColumnNumber() {
1033 return selectedColumn
;
1037 * Set the currently-selected column number. 0 is the left-most column.
1039 * @param column the column number to select
1041 public void setSelectedColumnNumber(final int column
) {
1042 if ((column
< 0) || (column
> columns
.size() - 1)) {
1043 throw new IndexOutOfBoundsException("Column count is " +
1044 columns
.size() + ", requested index " + column
);
1046 selectedColumn
= column
;
1047 activate(columns
.get(selectedColumn
).get(selectedRow
));
1052 * Get the currently-selected row number. 0 is the top-most row.
1054 * @return the selected row number
1056 public int getSelectedRowNumber() {
1061 * Set the currently-selected row number. 0 is the left-most column.
1063 * @param row the row number to select
1065 public void setSelectedRowNumber(final int row
) {
1066 if ((row
< 0) || (row
> rows
.size() - 1)) {
1067 throw new IndexOutOfBoundsException("Row count is " +
1068 rows
.size() + ", requested index " + row
);
1071 activate(columns
.get(selectedColumn
).get(selectedRow
));
1076 * Get the highlight row flag.
1078 * @return true if the selected row is highlighted
1080 public boolean getHighlightRow() {
1081 return highlightRow
;
1085 * Set the highlight row flag.
1087 * @param highlightRow if true, the selected row will be highlighted
1089 public void setHighlightRow(final boolean highlightRow
) {
1090 this.highlightRow
= highlightRow
;
1094 * Get the highlight column flag.
1096 * @return true if the selected column is highlighted
1098 public boolean getHighlightColumn() {
1099 return highlightColumn
;
1103 * Set the highlight column flag.
1105 * @param highlightColumn if true, the selected column will be highlighted
1107 public void setHighlightColumn(final boolean highlightColumn
) {
1108 this.highlightColumn
= highlightColumn
;
1112 * Get the show row labels flag.
1114 * @return true if row labels are shown
1116 public boolean getShowRowLabels() {
1117 return showRowLabels
;
1121 * Set the show row labels flag.
1123 * @param showRowLabels if true, the row labels will be shown
1125 public void setShowRowLabels(final boolean showRowLabels
) {
1126 this.showRowLabels
= showRowLabels
;
1130 * Get the show column labels flag.
1132 * @return true if column labels are shown
1134 public boolean getShowColumnLabels() {
1135 return showColumnLabels
;
1139 * Set the show column labels flag.
1141 * @param showColumnLabels if true, the column labels will be shown
1143 public void setShowColumnLabels(final boolean showColumnLabels
) {
1144 this.showColumnLabels
= showColumnLabels
;
1148 * Get the number of columns.
1150 * @return the number of columns
1152 public int getColumnCount() {
1153 return columns
.size();
1157 * Get the number of rows.
1159 * @return the number of rows
1161 public int getRowCount() {
1167 * Push top and left to the bottom-most right corner of the available
1170 private void bottomRightCorner() {
1171 int viewColumns
= getWidth();
1172 if (showRowLabels
== true) {
1173 viewColumns
-= ROW_LABEL_WIDTH
;
1176 // Set left and top such that the table stays on screen if possible.
1177 top
= rows
.size() - getHeight();
1178 left
= columns
.size() - (getWidth() / (viewColumns
/ (COLUMN_DEFAULT_WIDTH
+ 1)));
1179 // Now ensure the selection is visible.
1184 * Align the grid so that the selected cell is fully visible.
1186 private void alignGrid() {
1187 int viewColumns
= getWidth();
1188 if (showRowLabels
== true) {
1189 viewColumns
-= ROW_LABEL_WIDTH
;
1191 if (leftBorder
!= Border
.NONE
) {
1194 int viewRows
= getHeight();
1195 if (showColumnLabels
== true) {
1196 viewRows
-= COLUMN_LABEL_HEIGHT
;
1198 if (topBorder
!= Border
.NONE
) {
1202 // If we pushed left or right, adjust the box to include the new
1204 if (selectedColumn
< left
) {
1205 left
= selectedColumn
- 1;
1210 if (selectedRow
< top
) {
1211 top
= selectedRow
- 1;
1218 * viewColumns and viewRows now contain the available columns and
1219 * rows available to view the selected cell. We adjust left and top
1220 * to ensure the selected cell is within view, and then make all
1221 * cells outside the box between (left, top) and (right, bottom)
1224 * We need to calculate right and bottom now.
1228 boolean done
= false;
1230 int rightCellX
= (showRowLabels ? ROW_LABEL_WIDTH
: 0);
1231 if (leftBorder
!= Border
.NONE
) {
1234 int maxCellX
= rightCellX
+ viewColumns
;
1236 boolean selectedIsVisible
= false;
1238 for (int x
= left
; x
< columns
.size(); x
++) {
1239 if (x
== selectedColumn
) {
1240 selectedX
= rightCellX
;
1241 if (selectedX
+ columns
.get(x
).width
+ 1 <= maxCellX
) {
1242 selectedIsVisible
= true;
1245 rightCellX
+= columns
.get(x
).width
+ 1;
1246 if (rightCellX
>= maxCellX
) {
1251 if (right
< selectedColumn
) {
1252 // selectedColumn is outside the view range. Push left over,
1253 // and calculate again.
1255 } else if (left
== selectedColumn
) {
1256 // selectedColumn doesn't fit inside the view range, but we
1257 // can't go over any further either. Bail out.
1259 } else if (selectedIsVisible
== false) {
1260 // selectedColumn doesn't fit inside the view range, continue
1264 // selectedColumn is fully visible, all done.
1265 assert (selectedIsVisible
== true);
1271 // We have the left/right range correct, set cell visibility and
1272 // column X positions.
1273 int leftCellX
= showRowLabels ? ROW_LABEL_WIDTH
: 0;
1274 if (leftBorder
!= Border
.NONE
) {
1277 for (int x
= 0; x
< columns
.size(); x
++) {
1278 if ((x
< left
) || (x
> right
)) {
1279 for (int i
= 0; i
< rows
.size(); i
++) {
1280 columns
.get(x
).get(i
).setVisible(false);
1281 columns
.get(x
).setX(getWidth() + 1);
1285 for (int i
= 0; i
< rows
.size(); i
++) {
1286 columns
.get(x
).get(i
).setVisible(true);
1288 columns
.get(x
).setX(leftCellX
);
1289 leftCellX
+= columns
.get(x
).width
+ 1;
1296 int bottomCellY
= (showColumnLabels ? COLUMN_LABEL_HEIGHT
: 0);
1297 if (topBorder
!= Border
.NONE
) {
1300 int maxCellY
= bottomCellY
+ viewRows
;
1302 for (int y
= top
; y
< rows
.size(); y
++) {
1303 bottomCellY
+= rows
.get(y
).height
;
1304 if (bottomCellY
>= maxCellY
) {
1309 if (bottom
< selectedRow
) {
1310 // selectedRow is outside the view range. Push top down, and
1314 // selectedRow is inside the view range, done.
1319 // We have the top/bottom range correct, set cell visibility and
1321 int topCellY
= showColumnLabels ? COLUMN_LABEL_HEIGHT
: 0;
1322 if (topBorder
!= Border
.NONE
) {
1325 for (int y
= 0; y
< rows
.size(); y
++) {
1326 if ((y
< top
) || (y
> bottom
)) {
1327 for (int i
= 0; i
< columns
.size(); i
++) {
1328 rows
.get(y
).get(i
).setVisible(false);
1330 rows
.get(y
).setY(getHeight() + 1);
1333 for (int i
= 0; i
< columns
.size(); i
++) {
1334 rows
.get(y
).get(i
).setVisible(true);
1336 rows
.get(y
).setY(topCellY
);
1337 topCellY
+= rows
.get(y
).height
;
1340 // Last thing: cancel any edits that are not the selected cell.
1341 for (int y
= 0; y
< rows
.size(); y
++) {
1342 for (int x
= 0; x
< columns
.size(); x
++) {
1343 if ((x
== selectedColumn
) && (y
== selectedRow
)) {
1346 rows
.get(y
).get(x
).cancelEdit();
1352 * Save contents to file in CSV format.
1354 * @param filename file to save to
1355 * @throws IOException if a java.io operation throws
1357 public void saveToCsvFilename(final String filename
) throws IOException
{
1362 * Save contents to file in text format with lines.
1364 * @param filename file to save to
1365 * @throws IOException if a java.io operation throws
1367 public void saveToTextFilename(final String filename
) throws IOException
{
1372 * Set the selected cell location.
1374 * @param column the selected cell location column
1375 * @param row the selected cell location row
1377 public void setSelectedCell(final int column
, final int row
) {
1378 if ((column
< 0) || (column
> columns
.size() - 1)) {
1379 throw new IndexOutOfBoundsException("Column count is " +
1380 columns
.size() + ", requested index " + column
);
1382 if ((row
< 0) || (row
> rows
.size() - 1)) {
1383 throw new IndexOutOfBoundsException("Row count is " +
1384 rows
.size() + ", requested index " + row
);
1386 selectedColumn
= column
;
1392 * Get a particular cell.
1394 * @param column the cell column
1395 * @param row the cell row
1398 public Cell
getCell(final int column
, final int row
) {
1399 if ((column
< 0) || (column
> columns
.size() - 1)) {
1400 throw new IndexOutOfBoundsException("Column count is " +
1401 columns
.size() + ", requested index " + column
);
1403 if ((row
< 0) || (row
> rows
.size() - 1)) {
1404 throw new IndexOutOfBoundsException("Row count is " +
1405 rows
.size() + ", requested index " + row
);
1407 return rows
.get(row
).get(column
);
1411 * Get the text of a particular cell.
1413 * @param column the cell column
1414 * @param row the cell row
1415 * @return the text in the cell
1417 public String
getCellText(final int column
, final int row
) {
1418 if ((column
< 0) || (column
> columns
.size() - 1)) {
1419 throw new IndexOutOfBoundsException("Column count is " +
1420 columns
.size() + ", requested index " + column
);
1422 if ((row
< 0) || (row
> rows
.size() - 1)) {
1423 throw new IndexOutOfBoundsException("Row count is " +
1424 rows
.size() + ", requested index " + row
);
1426 return rows
.get(row
).get(column
).getText();
1430 * Set the text of a particular cell.
1432 * @param column the cell column
1433 * @param row the cell row
1434 * @param text the text to put into the cell
1436 public void setCellText(final int column
, final int row
,
1437 final String text
) {
1439 if ((column
< 0) || (column
> columns
.size() - 1)) {
1440 throw new IndexOutOfBoundsException("Column count is " +
1441 columns
.size() + ", requested index " + column
);
1443 if ((row
< 0) || (row
> rows
.size() - 1)) {
1444 throw new IndexOutOfBoundsException("Row count is " +
1445 rows
.size() + ", requested index " + row
);
1447 rows
.get(row
).get(column
).setText(text
);
1451 * Set the action to perform when the user presses enter on a particular
1454 * @param column the cell column
1455 * @param row the cell row
1456 * @param action the action to perform when the user presses enter on the
1459 public void setCellEnterAction(final int column
, final int row
,
1460 final TAction action
) {
1462 if ((column
< 0) || (column
> columns
.size() - 1)) {
1463 throw new IndexOutOfBoundsException("Column count is " +
1464 columns
.size() + ", requested index " + column
);
1466 if ((row
< 0) || (row
> rows
.size() - 1)) {
1467 throw new IndexOutOfBoundsException("Row count is " +
1468 rows
.size() + ", requested index " + row
);
1470 rows
.get(row
).get(column
).field
.setEnterAction(action
);
1474 * Set the action to perform when the user updates a particular cell.
1476 * @param column the cell column
1477 * @param row the cell row
1478 * @param action the action to perform when the user updates the cell
1480 public void setCellUpdateAction(final int column
, final int row
,
1481 final TAction action
) {
1483 if ((column
< 0) || (column
> columns
.size() - 1)) {
1484 throw new IndexOutOfBoundsException("Column count is " +
1485 columns
.size() + ", requested index " + column
);
1487 if ((row
< 0) || (row
> rows
.size() - 1)) {
1488 throw new IndexOutOfBoundsException("Row count is " +
1489 rows
.size() + ", requested index " + row
);
1491 rows
.get(row
).get(column
).field
.setUpdateAction(action
);
1495 * Get the width of a column.
1497 * @param column the column number
1498 * @return the width of the column
1500 public int getColumnWidth(final int column
) {
1501 if ((column
< 0) || (column
> columns
.size() - 1)) {
1502 throw new IndexOutOfBoundsException("Column count is " +
1503 columns
.size() + ", requested index " + column
);
1505 return columns
.get(column
).width
;
1509 * Set the width of a column.
1511 * @param column the column number
1512 * @param width the new width of the column
1514 public void setColumnWidth(final int column
, final int width
) {
1515 if ((column
< 0) || (column
> columns
.size() - 1)) {
1516 throw new IndexOutOfBoundsException("Column count is " +
1517 columns
.size() + ", requested index " + column
);
1521 // Columns may not be smaller than 4 cells wide.
1525 int delta
= width
- columns
.get(column
).width
;
1526 columns
.get(column
).width
= width
;
1527 for (Cell cell
: columns
.get(column
).cells
) {
1528 cell
.setWidth(columns
.get(column
).width
);
1529 cell
.field
.setWidth(columns
.get(column
).width
);
1531 for (int i
= column
+ 1; i
< columns
.size(); i
++) {
1532 columns
.get(i
).setX(columns
.get(i
).getX() + delta
);
1534 if (column
== columns
.size() - 1) {
1535 bottomRightCorner();
1542 * Get the label of a column.
1544 * @param column the column number
1545 * @return the label of the column
1547 public String
getColumnLabel(final int column
) {
1548 if ((column
< 0) || (column
> columns
.size() - 1)) {
1549 throw new IndexOutOfBoundsException("Column count is " +
1550 columns
.size() + ", requested index " + column
);
1552 return columns
.get(column
).label
;
1556 * Set the label of a column.
1558 * @param column the column number
1559 * @param label the new label of the column
1561 public void setColumnLabel(final int column
, final String label
) {
1562 if ((column
< 0) || (column
> columns
.size() - 1)) {
1563 throw new IndexOutOfBoundsException("Column count is " +
1564 columns
.size() + ", requested index " + column
);
1566 columns
.get(column
).label
= label
;
1570 * Get the label of a row.
1572 * @param row the row number
1573 * @return the label of the row
1575 public String
getRowLabel(final int row
) {
1576 if ((row
< 0) || (row
> rows
.size() - 1)) {
1577 throw new IndexOutOfBoundsException("Row count is " +
1578 rows
.size() + ", requested index " + row
);
1580 return rows
.get(row
).label
;
1584 * Set the label of a row.
1586 * @param row the row number
1587 * @param label the new label of the row
1589 public void setRowLabel(final int row
, final String label
) {
1590 if ((row
< 0) || (row
> rows
.size() - 1)) {
1591 throw new IndexOutOfBoundsException("Row count is " +
1592 rows
.size() + ", requested index " + row
);
1594 rows
.get(row
).label
= label
;
1598 * Insert one row at a particular index.
1600 * @param idx the row number
1602 private void insertRowAt(final int idx
) {
1603 Row newRow
= new Row(idx
);
1604 for (int i
= 0; i
< columns
.size(); i
++) {
1605 Cell cell
= new Cell(this, columns
.get(i
).getX(),
1606 rows
.get(selectedRow
).getY(), COLUMN_DEFAULT_WIDTH
, 1, i
, idx
);
1608 columns
.get(i
).cells
.add(idx
, cell
);
1610 rows
.add(idx
, newRow
);
1612 for (int x
= 0; x
< columns
.size(); x
++) {
1613 for (int y
= idx
; y
< rows
.size(); y
++) {
1614 columns
.get(x
).get(y
).row
= y
;
1615 columns
.get(x
).get(y
).column
= x
;
1618 for (int i
= idx
+ 1; i
< rows
.size(); i
++) {
1619 String oldRowLabel
= Integer
.toString(i
- 1);
1620 if (rows
.get(i
).label
.equals(oldRowLabel
)) {
1621 rows
.get(i
).label
= Integer
.toString(i
);
1628 * Insert one row above a particular row.
1630 * @param row the row number
1632 public void insertRowAbove(final int row
) {
1633 if ((row
< 0) || (row
> rows
.size() - 1)) {
1634 throw new IndexOutOfBoundsException("Row count is " +
1635 rows
.size() + ", requested index " + row
);
1637 insertRowAt(selectedRow
);
1639 activate(columns
.get(selectedColumn
).get(selectedRow
));
1643 * Insert one row below a particular row.
1645 * @param row the row number
1647 public void insertRowBelow(final int row
) {
1648 if ((row
< 0) || (row
> rows
.size() - 1)) {
1649 throw new IndexOutOfBoundsException("Row count is " +
1650 rows
.size() + ", requested index " + row
);
1652 int idx
= selectedRow
+ 1;
1653 if (idx
< rows
.size()) {
1655 activate(columns
.get(selectedColumn
).get(selectedRow
));
1659 // selectedRow is the last row, we need to perform an append.
1660 Row newRow
= new Row(idx
);
1661 for (int i
= 0; i
< columns
.size(); i
++) {
1662 Cell cell
= new Cell(this, columns
.get(i
).getX(),
1663 rows
.get(selectedRow
).getY(), COLUMN_DEFAULT_WIDTH
, 1, i
, idx
);
1665 columns
.get(i
).cells
.add(cell
);
1669 activate(columns
.get(selectedColumn
).get(selectedRow
));
1673 * Delete a particular row.
1675 * @param row the row number
1677 public void deleteRow(final int row
) {
1678 if ((row
< 0) || (row
> rows
.size() - 1)) {
1679 throw new IndexOutOfBoundsException("Row count is " +
1680 rows
.size() + ", requested index " + row
);
1682 if (rows
.size() == 1) {
1683 // Don't delete the last row.
1686 for (int i
= 0; i
< columns
.size(); i
++) {
1687 Cell cell
= columns
.get(i
).cells
.remove(row
);
1688 getChildren().remove(cell
);
1692 for (int x
= 0; x
< columns
.size(); x
++) {
1693 for (int y
= row
; y
< rows
.size(); y
++) {
1694 columns
.get(x
).get(y
).row
= y
;
1695 columns
.get(x
).get(y
).column
= x
;
1698 for (int i
= row
; i
< rows
.size(); i
++) {
1699 String oldRowLabel
= Integer
.toString(i
+ 1);
1700 if (rows
.get(i
).label
.equals(oldRowLabel
)) {
1701 rows
.get(i
).label
= Integer
.toString(i
);
1704 if (selectedRow
== rows
.size()) {
1707 activate(columns
.get(selectedColumn
).get(selectedRow
));
1708 bottomRightCorner();
1712 * Insert one column at a particular index.
1714 * @param idx the column number
1716 private void insertColumnAt(final int idx
) {
1717 Column newColumn
= new Column(idx
);
1718 for (int i
= 0; i
< rows
.size(); i
++) {
1719 Cell cell
= new Cell(this, columns
.get(selectedColumn
).getX(),
1720 rows
.get(i
).getY(), COLUMN_DEFAULT_WIDTH
, 1, idx
, i
);
1721 newColumn
.add(cell
);
1722 rows
.get(i
).cells
.add(idx
, cell
);
1724 columns
.add(idx
, newColumn
);
1726 for (int x
= idx
; x
< columns
.size(); x
++) {
1727 for (int y
= 0; y
< rows
.size(); y
++) {
1728 columns
.get(x
).get(y
).row
= y
;
1729 columns
.get(x
).get(y
).column
= x
;
1732 for (int i
= idx
+ 1; i
< columns
.size(); i
++) {
1733 String oldColumnLabel
= makeColumnLabel(i
- 1);
1734 if (columns
.get(i
).label
.equals(oldColumnLabel
)) {
1735 columns
.get(i
).label
= makeColumnLabel(i
);
1742 * Insert one column to the left of a particular column.
1744 * @param column the column number
1746 public void insertColumnLeft(final int column
) {
1747 if ((column
< 0) || (column
> columns
.size() - 1)) {
1748 throw new IndexOutOfBoundsException("Column count is " +
1749 columns
.size() + ", requested index " + column
);
1751 insertColumnAt(selectedColumn
);
1753 activate(columns
.get(selectedColumn
).get(selectedRow
));
1757 * Insert one column to the right of a particular column.
1759 * @param column the column number
1761 public void insertColumnRight(final int column
) {
1762 if ((column
< 0) || (column
> columns
.size() - 1)) {
1763 throw new IndexOutOfBoundsException("Column count is " +
1764 columns
.size() + ", requested index " + column
);
1766 int idx
= selectedColumn
+ 1;
1767 if (idx
< columns
.size()) {
1768 insertColumnAt(idx
);
1769 activate(columns
.get(selectedColumn
).get(selectedRow
));
1773 // selectedColumn is the last column, we need to perform an append.
1774 Column newColumn
= new Column(idx
);
1775 for (int i
= 0; i
< rows
.size(); i
++) {
1776 Cell cell
= new Cell(this, columns
.get(selectedColumn
).getX(),
1777 rows
.get(i
).getY(), COLUMN_DEFAULT_WIDTH
, 1, idx
, i
);
1778 newColumn
.add(cell
);
1779 rows
.get(i
).cells
.add(cell
);
1781 columns
.add(newColumn
);
1783 activate(columns
.get(selectedColumn
).get(selectedRow
));
1787 * Delete a particular column.
1789 * @param column the column number
1791 public void deleteColumn(final int column
) {
1792 if ((column
< 0) || (column
> columns
.size() - 1)) {
1793 throw new IndexOutOfBoundsException("Column count is " +
1794 columns
.size() + ", requested index " + column
);
1796 if (columns
.size() == 1) {
1797 // Don't delete the last column.
1800 for (int i
= 0; i
< rows
.size(); i
++) {
1801 Cell cell
= rows
.get(i
).cells
.remove(column
);
1802 getChildren().remove(cell
);
1804 columns
.remove(column
);
1806 for (int x
= column
; x
< columns
.size(); x
++) {
1807 for (int y
= 0; y
< rows
.size(); y
++) {
1808 columns
.get(x
).get(y
).row
= y
;
1809 columns
.get(x
).get(y
).column
= x
;
1812 for (int i
= column
; i
< columns
.size(); i
++) {
1813 String oldColumnLabel
= makeColumnLabel(i
+ 1);
1814 if (columns
.get(i
).label
.equals(oldColumnLabel
)) {
1815 columns
.get(i
).label
= makeColumnLabel(i
);
1818 if (selectedColumn
== columns
.size()) {
1821 activate(columns
.get(selectedColumn
).get(selectedRow
));
1822 bottomRightCorner();
1826 * Delete the selected cell, shifting cells over to the left.
1828 public void deleteCellShiftLeft() {
1829 // All we do is copy the text from every cell in this row over.
1830 for (int i
= selectedColumn
+ 1; i
< columns
.size(); i
++) {
1831 setCellText(i
- 1, selectedRow
, getCellText(i
, selectedRow
));
1833 setCellText(columns
.size() - 1, selectedRow
, "");
1837 * Delete the selected cell, shifting cells from below up.
1839 public void deleteCellShiftUp() {
1840 // All we do is copy the text from every cell in this column up.
1841 for (int i
= selectedRow
+ 1; i
< rows
.size(); i
++) {
1842 setCellText(selectedColumn
, i
- 1, getCellText(selectedColumn
, i
));
1844 setCellText(selectedColumn
, rows
.size() - 1, "");
1848 * Set a particular cell read-only (non-editable) or not.
1850 * @param column the cell column
1851 * @param row the cell row
1852 * @param readOnly if true, the cell will be non-editable
1854 public void setCellReadOnly(final int column
, final int row
,
1855 final boolean readOnly
) {
1857 if ((column
< 0) || (column
> columns
.size() - 1)) {
1858 throw new IndexOutOfBoundsException("Column count is " +
1859 columns
.size() + ", requested index " + column
);
1861 if ((row
< 0) || (row
> rows
.size() - 1)) {
1862 throw new IndexOutOfBoundsException("Row count is " +
1863 rows
.size() + ", requested index " + row
);
1865 rows
.get(row
).get(column
).setReadOnly(readOnly
);
1869 * Set an entire row of cells read-only (non-editable) or not.
1871 * @param row the row number
1872 * @param readOnly if true, the cells will be non-editable
1874 public void setRowReadOnly(final int row
, final boolean readOnly
) {
1875 if ((row
< 0) || (row
> rows
.size() - 1)) {
1876 throw new IndexOutOfBoundsException("Row count is " +
1877 rows
.size() + ", requested index " + row
);
1879 for (Cell cell
: rows
.get(row
).cells
) {
1880 cell
.setReadOnly(readOnly
);
1885 * Set an entire column of cells read-only (non-editable) or not.
1887 * @param column the column number
1888 * @param readOnly if true, the cells will be non-editable
1890 public void setColumnReadOnly(final int column
, final boolean readOnly
) {
1891 if ((column
< 0) || (column
> columns
.size() - 1)) {
1892 throw new IndexOutOfBoundsException("Column count is " +
1893 columns
.size() + ", requested index " + column
);
1895 for (Cell cell
: columns
.get(column
).cells
) {
1896 cell
.setReadOnly(readOnly
);
1901 * Set all borders across the entire table to Border.NONE.
1903 public void setBorderAllNone() {
1904 topBorder
= Border
.NONE
;
1905 leftBorder
= Border
.NONE
;
1906 for (int i
= 0; i
< columns
.size(); i
++) {
1907 columns
.get(i
).rightBorder
= Border
.NONE
;
1909 for (int i
= 0; i
< rows
.size(); i
++) {
1910 rows
.get(i
).bottomBorder
= Border
.NONE
;
1911 rows
.get(i
).height
= 1;
1913 bottomRightCorner();
1917 * Set all borders across the entire table to Border.SINGLE.
1919 public void setBorderAllSingle() {
1920 topBorder
= Border
.SINGLE
;
1921 leftBorder
= Border
.SINGLE
;
1922 for (int i
= 0; i
< columns
.size(); i
++) {
1923 columns
.get(i
).rightBorder
= Border
.SINGLE
;
1925 for (int i
= 0; i
< rows
.size(); i
++) {
1926 rows
.get(i
).bottomBorder
= Border
.SINGLE
;
1927 rows
.get(i
).height
= 2;
1933 * Set all borders around the selected cell to Border.NONE.
1935 public void setBorderCellNone() {
1936 if (selectedRow
== 0) {
1937 topBorder
= Border
.NONE
;
1939 if (selectedColumn
== 0) {
1940 leftBorder
= Border
.NONE
;
1942 columns
.get(selectedColumn
).rightBorder
= Border
.NONE
;
1943 rows
.get(selectedRow
).bottomBorder
= Border
.NONE
;
1944 rows
.get(selectedRow
).height
= 1;
1945 bottomRightCorner();
1949 * Set all borders around the selected cell to Border.SINGLE.
1951 public void setBorderCellSingle() {
1952 if (selectedRow
== 0) {
1953 topBorder
= Border
.SINGLE
;
1955 if (selectedColumn
== 0) {
1956 leftBorder
= Border
.SINGLE
;
1958 columns
.get(selectedColumn
).rightBorder
= Border
.SINGLE
;
1959 rows
.get(selectedRow
).bottomBorder
= Border
.SINGLE
;
1960 rows
.get(selectedRow
).height
= 2;
1965 * Set the column border to the right of the selected cell to
1968 public void setBorderColumnRightSingle() {
1969 columns
.get(selectedColumn
).rightBorder
= Border
.SINGLE
;
1974 * Set the column border to the right of the selected cell to
1977 public void setBorderColumnLeftSingle() {
1978 if (selectedColumn
== 0) {
1979 leftBorder
= Border
.SINGLE
;
1981 columns
.get(selectedColumn
- 1).rightBorder
= Border
.SINGLE
;
1987 * Set the row border above the selected cell to Border.SINGLE.
1989 public void setBorderRowAboveSingle() {
1990 if (selectedRow
== 0) {
1991 topBorder
= Border
.SINGLE
;
1993 rows
.get(selectedRow
- 1).bottomBorder
= Border
.SINGLE
;
1994 rows
.get(selectedRow
- 1).height
= 2;
2000 * Set the row border below the selected cell to Border.SINGLE.
2002 public void setBorderRowBelowSingle() {
2003 rows
.get(selectedRow
).bottomBorder
= Border
.SINGLE
;
2004 rows
.get(selectedRow
).height
= 2;
2009 * Set the row border below the selected cell to Border.DOUBLE.
2011 public void setBorderRowBelowDouble() {
2012 rows
.get(selectedRow
).bottomBorder
= Border
.DOUBLE
;
2013 rows
.get(selectedRow
).height
= 2;
2018 * Set the row border below the selected cell to Border.THICK.
2020 public void setBorderRowBelowThick() {
2021 rows
.get(selectedRow
).bottomBorder
= Border
.THICK
;
2022 rows
.get(selectedRow
).height
= 2;