90 * <ul>
91 * <li>Powerful {@link TableColumn} API:
92 * <ul>
93 * <li>Support for {@link TableColumn#cellFactoryProperty() cell factories} to
94 * easily customize {@link Cell cell} contents in both rendering and editing
95 * states.
96 * <li>Specification of {@link TableColumn#minWidthProperty() minWidth}/
97 * {@link TableColumn#prefWidthProperty() prefWidth}/
98 * {@link TableColumn#maxWidthProperty() maxWidth},
99 * and also {@link TableColumn#resizableProperty() fixed width columns}.
100 * <li>Width resizing by the user at runtime.
101 * <li>Column reordering by the user at runtime.
102 * <li>Built-in support for {@link TableColumn#getColumns() column nesting}
103 * </ul>
104 * <li>Different {@link #columnResizePolicyProperty() resizing policies} to
105 * dictate what happens when the user resizes columns.
106 * <li>Support for {@link #getSortOrder() multiple column sorting} by clicking
107 * the column header (hold down Shift keyboard key whilst clicking on a
108 * header to sort by multiple columns).
109 * </ul>
110 * </p>
111 *
112 * <p>Note that TableView is intended to be used to visualize data - it is not
113 * intended to be used for laying out your user interface. If you want to lay
114 * your user interface out in a grid-like fashion, consider the
115 * {@link javafx.scene.layout.GridPane} layout instead.</p>
116 *
117 * <h2>Creating a TableView</h2>
118 *
119 * <p>Creating a TableView is a multi-step process, and also depends on the
120 * underlying data model needing to be represented. For this example we'll use
121 * an ObservableList<Person>, as it is the simplest way of showing data in a
122 * TableView. The <code>Person</code> class will consist of a first
123 * name and last name properties. That is:
124 *
125 * <pre>
126 * {@code
127 * public class Person {
128 * private StringProperty firstName;
129 * public void setFirstName(String value) { firstNameProperty().set(value); }
130 * public String getFirstName() { return firstNameProperty().get(); }
131 * public StringProperty firstNameProperty() {
132 * if (firstName == null) firstName = new SimpleStringProperty(this, "firstName");
133 * return firstName;
134 * }
135 *
136 * private StringProperty lastName;
137 * public void setLastName(String value) { lastNameProperty().set(value); }
138 * public String getLastName() { return lastNameProperty().get(); }
139 * public StringProperty lastNameProperty() {
140 * if (lastName == null) lastName = new SimpleStringProperty(this, "lastName");
141 * return lastName;
142 * }
143 * }}</pre>
144 *
145 * <p>Firstly, a TableView instance needs to be defined, as such:
146 *
147 * <pre>
148 * {@code
149 * TableView<Person> table = new TableView<Person>();}</pre>
150 *
151 * <p>With the basic table defined, we next focus on the data model. As mentioned,
152 * for this example, we'll be using a ObservableList<Person>. We can immediately
153 * set such a list directly in to the TableView, as such:
154 *
155 * <pre>
156 * {@code
157 * ObservableList<Person> teamMembers = getTeamMembers();
158 * table.setItems(teamMembers);}</pre>
159 *
160 * <p>With the items set as such, TableView will automatically update whenever
161 * the <code>teamMembers</code> list changes. If the items list is available
162 * before the TableView is instantiated, it is possible to pass it directly into
163 * the constructor.
164 *
165 * <p>At this point we now have a TableView hooked up to observe the
166 * <code>teamMembers</code> observableList. The missing ingredient
167 * now is the means of splitting out the data contained within the model and
168 * representing it in one or more {@link TableColumn TableColumn} instances. To
169 * create a two-column TableView to show the firstName and lastName properties,
170 * we extend the last code sample as follows:
171 *
172 * <pre>
173 * {@code
817
818 private final WeakListChangeListener<TableColumn<S,?>> weakColumnsObserver =
819 new WeakListChangeListener<TableColumn<S,?>>(columnsObserver);
820
821 private final WeakInvalidationListener weakCellSelectionModelInvalidationListener =
822 new WeakInvalidationListener(cellSelectionModelInvalidationListener);
823
824
825
826 /***************************************************************************
827 * *
828 * Properties *
829 * *
830 **************************************************************************/
831
832
833 // --- Items
834 /**
835 * The underlying data model for the TableView. Note that it has a generic
836 * type that must match the type of the TableView itself.
837 */
838 public final ObjectProperty<ObservableList<S>> itemsProperty() { return items; }
839 private ObjectProperty<ObservableList<S>> items =
840 new SimpleObjectProperty<ObservableList<S>>(this, "items") {
841 WeakReference<ObservableList<S>> oldItemsRef;
842
843 @Override protected void invalidated() {
844 final ObservableList<S> oldItems = oldItemsRef == null ? null : oldItemsRef.get();
845 final ObservableList<S> newItems = getItems();
846
847 // Fix for RT-36425
848 if (newItems != null && newItems == oldItems) {
849 return;
850 }
851
852 // Fix for RT-35763
853 if (! (newItems instanceof SortedList)) {
854 getSortOrder().clear();
855 }
856
857 oldItemsRef = new WeakReference<>(newItems);
858 }
859 };
860 public final void setItems(ObservableList<S> value) { itemsProperty().set(value); }
861 public final ObservableList<S> getItems() {return items.get(); }
862
863
864 // --- Table menu button visible
865 private BooleanProperty tableMenuButtonVisible;
866 /**
867 * This controls whether a menu button is available when the user clicks
868 * in a designated space within the TableView, within which is a radio menu
869 * item for each TableColumn in this table. This menu allows for the user to
870 * show and hide all TableColumns easily.
871 */
872 public final BooleanProperty tableMenuButtonVisibleProperty() {
873 if (tableMenuButtonVisible == null) {
874 tableMenuButtonVisible = new SimpleBooleanProperty(this, "tableMenuButtonVisible");
875 }
876 return tableMenuButtonVisible;
877 }
878 public final void setTableMenuButtonVisible (boolean value) {
879 tableMenuButtonVisibleProperty().set(value);
880 }
881 public final boolean isTableMenuButtonVisible() {
882 return tableMenuButtonVisible == null ? false : tableMenuButtonVisible.get();
883 }
884
885
886 // --- Column Resize Policy
887 private ObjectProperty<Callback<ResizeFeatures, Boolean>> columnResizePolicy;
888 public final void setColumnResizePolicy(Callback<ResizeFeatures, Boolean> callback) {
889 columnResizePolicyProperty().set(callback);
890 }
891 public final Callback<ResizeFeatures, Boolean> getColumnResizePolicy() {
892 return columnResizePolicy == null ? UNCONSTRAINED_RESIZE_POLICY : columnResizePolicy.get();
893 }
894
895 /**
896 * This is the function called when the user completes a column-resize
897 * operation. The two most common policies are available as static functions
898 * in the TableView class: {@link #UNCONSTRAINED_RESIZE_POLICY} and
899 * {@link #CONSTRAINED_RESIZE_POLICY}.
900 */
901 public final ObjectProperty<Callback<ResizeFeatures, Boolean>> columnResizePolicyProperty() {
902 if (columnResizePolicy == null) {
903 columnResizePolicy = new SimpleObjectProperty<Callback<ResizeFeatures, Boolean>>(this, "columnResizePolicy", UNCONSTRAINED_RESIZE_POLICY) {
904 private Callback<ResizeFeatures, Boolean> oldPolicy;
905
906 @Override protected void invalidated() {
907 if (isInited) {
908 get().call(new ResizeFeatures(TableView.this, null, 0.0));
909
910 if (oldPolicy != null) {
911 PseudoClass state = PseudoClass.getPseudoClass(oldPolicy.toString());
912 pseudoClassStateChanged(state, false);
913 }
914 if (get() != null) {
915 PseudoClass state = PseudoClass.getPseudoClass(get().toString());
916 pseudoClassStateChanged(state, true);
917 }
918 oldPolicy = get();
919 }
924 }
925
926
927 // --- Row Factory
928 private ObjectProperty<Callback<TableView<S>, TableRow<S>>> rowFactory;
929
930 /**
931 * A function which produces a TableRow. The system is responsible for
932 * reusing TableRows. Return from this function a TableRow which
933 * might be usable for representing a single row in a TableView.
934 * <p>
935 * Note that a TableRow is <b>not</b> a TableCell. A TableRow is
936 * simply a container for a TableCell, and in most circumstances it is more
937 * likely that you'll want to create custom TableCells, rather than
938 * TableRows. The primary use case for creating custom TableRow
939 * instances would most probably be to introduce some form of column
940 * spanning support.
941 * <p>
942 * You can create custom TableCell instances per column by assigning the
943 * appropriate function to the cellFactory property in the TableColumn class.
944 */
945 public final ObjectProperty<Callback<TableView<S>, TableRow<S>>> rowFactoryProperty() {
946 if (rowFactory == null) {
947 rowFactory = new SimpleObjectProperty<Callback<TableView<S>, TableRow<S>>>(this, "rowFactory");
948 }
949 return rowFactory;
950 }
951 public final void setRowFactory(Callback<TableView<S>, TableRow<S>> value) {
952 rowFactoryProperty().set(value);
953 }
954 public final Callback<TableView<S>, TableRow<S>> getRowFactory() {
955 return rowFactory == null ? null : rowFactory.get();
956 }
957
958
959 // --- Placeholder Node
960 private ObjectProperty<Node> placeholder;
961 /**
962 * This Node is shown to the user when the table has no content to show.
963 * This may be the case because the table model has no data in the first
964 * place, that a filter has been applied to the table model, resulting
965 * in there being nothing to show the user, or that there are no currently
966 * visible columns.
967 */
968 public final ObjectProperty<Node> placeholderProperty() {
969 if (placeholder == null) {
970 placeholder = new SimpleObjectProperty<Node>(this, "placeholder");
971 }
972 return placeholder;
973 }
974 public final void setPlaceholder(Node value) {
975 placeholderProperty().set(value);
976 }
977 public final Node getPlaceholder() {
978 return placeholder == null ? null : placeholder.get();
979 }
980
981
982 // --- Selection Model
983 private ObjectProperty<TableViewSelectionModel<S>> selectionModel
984 = new SimpleObjectProperty<TableViewSelectionModel<S>>(this, "selectionModel") {
985
986 TableViewSelectionModel<S> oldValue = null;
993 if (oldValue instanceof TableViewArrayListSelectionModel) {
994 ((TableViewArrayListSelectionModel)oldValue).dispose();
995 }
996 }
997
998 oldValue = get();
999
1000 if (oldValue != null) {
1001 oldValue.cellSelectionEnabledProperty().addListener(weakCellSelectionModelInvalidationListener);
1002 // fake an invalidation to ensure updated pseudo-class state
1003 weakCellSelectionModelInvalidationListener.invalidated(oldValue.cellSelectionEnabledProperty());
1004 }
1005 }
1006 };
1007
1008 /**
1009 * The SelectionModel provides the API through which it is possible
1010 * to select single or multiple items within a TableView, as well as inspect
1011 * which items have been selected by the user. Note that it has a generic
1012 * type that must match the type of the TableView itself.
1013 */
1014 public final ObjectProperty<TableViewSelectionModel<S>> selectionModelProperty() {
1015 return selectionModel;
1016 }
1017 public final void setSelectionModel(TableViewSelectionModel<S> value) {
1018 selectionModelProperty().set(value);
1019 }
1020
1021 public final TableViewSelectionModel<S> getSelectionModel() {
1022 return selectionModel.get();
1023 }
1024
1025
1026 // --- Focus Model
1027 private ObjectProperty<TableViewFocusModel<S>> focusModel;
1028 public final void setFocusModel(TableViewFocusModel<S> value) {
1029 focusModelProperty().set(value);
1030 }
1031 public final TableViewFocusModel<S> getFocusModel() {
1032 return focusModel == null ? null : focusModel.get();
1033 }
1034 /**
1035 * Represents the currently-installed {@link TableViewFocusModel} for this
1036 * TableView. Under almost all circumstances leaving this as the default
1037 * focus model will suffice.
1038 */
1039 public final ObjectProperty<TableViewFocusModel<S>> focusModelProperty() {
1040 if (focusModel == null) {
1041 focusModel = new SimpleObjectProperty<TableViewFocusModel<S>>(this, "focusModel");
1042 }
1043 return focusModel;
1044 }
1045
1046
1047 // // --- Span Model
1048 // private ObjectProperty<SpanModel<S>> spanModel
1049 // = new SimpleObjectProperty<SpanModel<S>>(this, "spanModel") {
1050 //
1051 // @Override protected void invalidated() {
1052 // ObservableList<String> styleClass = getStyleClass();
1053 // if (getSpanModel() == null) {
1054 // styleClass.remove(CELL_SPAN_TABLE_VIEW_STYLE_CLASS);
1055 // } else if (! styleClass.contains(CELL_SPAN_TABLE_VIEW_STYLE_CLASS)) {
1056 // styleClass.add(CELL_SPAN_TABLE_VIEW_STYLE_CLASS);
1057 // }
1064 // public final void setSpanModel(SpanModel<S> value) {
1065 // spanModelProperty().set(value);
1066 // }
1067 //
1068 // public final SpanModel<S> getSpanModel() {
1069 // return spanModel.get();
1070 // }
1071
1072 // --- Editable
1073 private BooleanProperty editable;
1074 public final void setEditable(boolean value) {
1075 editableProperty().set(value);
1076 }
1077 public final boolean isEditable() {
1078 return editable == null ? false : editable.get();
1079 }
1080 /**
1081 * Specifies whether this TableView is editable - only if the TableView, the
1082 * TableColumn (if applicable) and the TableCells within it are both
1083 * editable will a TableCell be able to go into their editing state.
1084 */
1085 public final BooleanProperty editableProperty() {
1086 if (editable == null) {
1087 editable = new SimpleBooleanProperty(this, "editable", false);
1088 }
1089 return editable;
1090 }
1091
1092
1093 // --- Fixed cell size
1094 private DoubleProperty fixedCellSize;
1095
1096 /**
1097 * Sets the new fixed cell size for this control. Any value greater than
1098 * zero will enable fixed cell size mode, whereas a zero or negative value
1099 * (or Region.USE_COMPUTED_SIZE) will be used to disabled fixed cell size
1100 * mode.
1101 *
1102 * @param value The new fixed cell size value, or a value less than or equal
1103 * to zero (or Region.USE_COMPUTED_SIZE) to disable.
1121 }
1122 /**
1123 * Specifies whether this control has cells that are a fixed height (of the
1124 * specified value). If this value is less than or equal to zero,
1125 * then all cells are individually sized and positioned. This is a slow
1126 * operation. Therefore, when performance matters and developers are not
1127 * dependent on variable cell sizes it is a good idea to set the fixed cell
1128 * size value. Generally cells are around 24px, so setting a fixed cell size
1129 * of 24 is likely to result in very little difference in visuals, but a
1130 * improvement to performance.
1131 *
1132 * <p>To set this property via CSS, use the -fx-fixed-cell-size property.
1133 * This should not be confused with the -fx-cell-size property. The difference
1134 * between these two CSS properties is that -fx-cell-size will size all
1135 * cells to the specified size, but it will not enforce that this is the
1136 * only size (thus allowing for variable cell sizes, and preventing the
1137 * performance gains from being possible). Therefore, when performance matters
1138 * use -fx-fixed-cell-size, instead of -fx-cell-size. If both properties are
1139 * specified in CSS, -fx-fixed-cell-size takes precedence.</p>
1140 *
1141 * @since JavaFX 8.0
1142 */
1143 public final DoubleProperty fixedCellSizeProperty() {
1144 if (fixedCellSize == null) {
1145 fixedCellSize = new StyleableDoubleProperty(Region.USE_COMPUTED_SIZE) {
1146 @Override public CssMetaData<TableView<?>,Number> getCssMetaData() {
1147 return StyleableProperties.FIXED_CELL_SIZE;
1148 }
1149
1150 @Override public Object getBean() {
1151 return TableView.this;
1152 }
1153
1154 @Override public String getName() {
1155 return "fixedCellSize";
1156 }
1157 };
1158 }
1159 return fixedCellSize;
1160 }
1161
1162
1163 // --- Editing Cell
1164 private ReadOnlyObjectWrapper<TablePosition<S,?>> editingCell;
1165 private void setEditingCell(TablePosition<S,?> value) {
1166 editingCellPropertyImpl().set(value);
1167 }
1168 public final TablePosition<S,?> getEditingCell() {
1169 return editingCell == null ? null : editingCell.get();
1170 }
1171
1172 /**
1173 * Represents the current cell being edited, or null if
1174 * there is no cell being edited.
1175 */
1176 public final ReadOnlyObjectProperty<TablePosition<S,?>> editingCellProperty() {
1177 return editingCellPropertyImpl().getReadOnlyProperty();
1178 }
1179
1180 private ReadOnlyObjectWrapper<TablePosition<S,?>> editingCellPropertyImpl() {
1181 if (editingCell == null) {
1182 editingCell = new ReadOnlyObjectWrapper<TablePosition<S,?>>(this, "editingCell");
1183 }
1184 return editingCell;
1185 }
1186
1187
1188 // --- Comparator (built via sortOrder list, so read-only)
1189 /**
1190 * The comparator property is a read-only property that is representative of the
1191 * current state of the {@link #getSortOrder() sort order} list. The sort
1192 * order list contains the columns that have been added to it either programmatically
1193 * or via a user clicking on the headers themselves.
1194 * @since JavaFX 8.0
1286 return "onSort";
1287 }
1288 };
1289 }
1290 return onSort;
1291 }
1292
1293
1294 /***************************************************************************
1295 * *
1296 * Public API *
1297 * *
1298 **************************************************************************/
1299 /**
1300 * The TableColumns that are part of this TableView. As the user reorders
1301 * the TableView columns, this list will be updated to reflect the current
1302 * visual ordering.
1303 *
1304 * <p>Note: to display any data in a TableView, there must be at least one
1305 * TableColumn in this ObservableList.</p>
1306 */
1307 public final ObservableList<TableColumn<S,?>> getColumns() {
1308 return columns;
1309 }
1310
1311 /**
1312 * The sortOrder list defines the order in which {@link TableColumn} instances
1313 * are sorted. An empty sortOrder list means that no sorting is being applied
1314 * on the TableView. If the sortOrder list has one TableColumn within it,
1315 * the TableView will be sorted using the
1316 * {@link TableColumn#sortTypeProperty() sortType} and
1317 * {@link TableColumn#comparatorProperty() comparator} properties of this
1318 * TableColumn (assuming
1319 * {@link TableColumn#sortableProperty() TableColumn.sortable} is true).
1320 * If the sortOrder list contains multiple TableColumn instances, then
1321 * the TableView is firstly sorted based on the properties of the first
1322 * TableColumn. If two elements are considered equal, then the second
1323 * TableColumn in the list is used to determine ordering. This repeats until
1324 * the results from all TableColumn comparators are considered, if necessary.
1325 *
1434 @Override protected void invalidated() {
1435 EventType<ScrollToEvent<TableColumn<S, ?>>> type = ScrollToEvent.scrollToColumn();
1436 setEventHandler(type, get());
1437 }
1438
1439 @Override public Object getBean() {
1440 return TableView.this;
1441 }
1442
1443 @Override public String getName() {
1444 return "onScrollToColumn";
1445 }
1446 };
1447 }
1448 return onScrollToColumn;
1449 }
1450
1451 /**
1452 * Applies the currently installed resize policy against the given column,
1453 * resizing it based on the delta value provided.
1454 */
1455 public boolean resizeColumn(TableColumn<S,?> column, double delta) {
1456 if (column == null || Double.compare(delta, 0.0) == 0) return false;
1457
1458 boolean allowed = getColumnResizePolicy().call(new ResizeFeatures<S>(TableView.this, column, delta));
1459 if (!allowed) return false;
1460
1461 return true;
1462 }
1463
1464 /**
1465 * Causes the cell at the given row/column view indexes to switch into
1466 * its editing state, if it is not already in it, and assuming that the
1467 * TableView and column are also editable.
1468 *
1469 * <p><strong>Note:</strong> This method will cancel editing if the given row
1470 * value is less than zero and the given column is null.</p>
1471 */
1472 public void edit(int row, TableColumn<S,?> column) {
1473 if (!isEditable() || (column != null && ! column.isEditable())) {
1474 return;
1475 }
1476
1477 if (row < 0 && column == null) {
1478 setEditingCell(null);
1479 } else {
1480 setEditingCell(new TablePosition<>(this, row, column));
1481 }
1482 }
1483
1484 /**
1485 * Returns an unmodifiable list containing the currently visible leaf columns.
1486 */
1487 public ObservableList<TableColumn<S,?>> getVisibleLeafColumns() {
1488 return unmodifiableVisibleLeafColumns;
1489 }
1490
1491 /**
1492 * Returns the position of the given column, relative to all other
1493 * visible leaf columns.
1494 */
1495 public int getVisibleLeafIndex(TableColumn<S,?> column) {
1496 return visibleLeafColumns.indexOf(column);
1497 }
1498
1499 /**
1500 * Returns the TableColumn in the given column index, relative to all other
1501 * visible leaf columns.
1502 */
1503 public TableColumn<S,?> getVisibleLeafColumn(int column) {
1504 if (column < 0 || column >= visibleLeafColumns.size()) return null;
1505 return visibleLeafColumns.get(column);
1506 }
1507
1508 /** {@inheritDoc} */
1509 @Override protected Skin<?> createDefaultSkin() {
1510 return new TableViewSkin<S>(this);
1511 }
1512
1513 /**
1514 * The sort method forces the TableView to re-run its sorting algorithm. More
1515 * often than not it is not necessary to call this method directly, as it is
1516 * automatically called when the {@link #getSortOrder() sort order},
1517 * {@link #sortPolicyProperty() sort policy}, or the state of the
1518 * TableColumn {@link TableColumn#sortTypeProperty() sort type} properties
1519 * change. In other words, this method should only be called directly when
1520 * something external changes and a sort is required.
1521 * @since JavaFX 8.0
1808 * TableView resize operation.
1809 * @param delta The amount of horizontal space added or removed in the
1810 * resize operation.
1811 */
1812 public ResizeFeatures(TableView<S> table, TableColumn<S,?> column, Double delta) {
1813 super(column, delta);
1814 this.table = table;
1815 }
1816
1817 /**
1818 * Returns the column upon which the resize is occurring, or null
1819 * if this ResizeFeatures instance was created as a result of a
1820 * TableView resize operation.
1821 */
1822 @Override public TableColumn<S,?> getColumn() {
1823 return (TableColumn<S,?>) super.getColumn();
1824 }
1825
1826 /**
1827 * Returns the TableView upon which the resize operation is occurring.
1828 */
1829 public TableView<S> getTable() {
1830 return table;
1831 }
1832 }
1833
1834
1835
1836 /***************************************************************************
1837 * *
1838 * Support Classes *
1839 * *
1840 **************************************************************************/
1841
1842
1843 /**
1844 * A simple extension of the {@link SelectionModel} abstract class to
1845 * allow for special support for TableView controls.
1846 * @since JavaFX 2.0
1847 */
1875 public TableViewSelectionModel(final TableView<S> tableView) {
1876 if (tableView == null) {
1877 throw new NullPointerException("TableView can not be null");
1878 }
1879
1880 this.tableView = tableView;
1881 }
1882
1883
1884
1885 /***********************************************************************
1886 * *
1887 * Abstract API *
1888 * *
1889 **********************************************************************/
1890
1891 /**
1892 * A read-only ObservableList representing the currently selected cells
1893 * in this TableView. Rather than directly modify this list, please
1894 * use the other methods provided in the TableViewSelectionModel.
1895 */
1896 public abstract ObservableList<TablePosition> getSelectedCells();
1897
1898
1899 /***********************************************************************
1900 * *
1901 * Generic (type erasure) bridging *
1902 * *
1903 **********************************************************************/
1904
1905 // --- isSelected
1906 /** {@inheritDoc} */
1907 @Override public boolean isSelected(int row, TableColumnBase<S, ?> column) {
1908 return isSelected(row, (TableColumn<S,?>)column);
1909 }
1910
1911 /**
1912 * Convenience function which tests whether the given row and column index
1913 * is currently selected in this table instance.
1914 */
1915 public abstract boolean isSelected(int row, TableColumn<S, ?> column);
1916
1917
1918 // --- select
1919 /** {@inheritDoc} */
1920 @Override public void select(int row, TableColumnBase<S, ?> column) {
1921 select(row, (TableColumn<S,?>)column);
1922 }
1923
1924 /**
1925 * Selects the cell at the given row/column intersection.
1926 */
1927 public abstract void select(int row, TableColumn<S, ?> column);
1928
1929
1930 // --- clearAndSelect
1931 /** {@inheritDoc} */
1932 @Override public void clearAndSelect(int row, TableColumnBase<S,?> column) {
1933 clearAndSelect(row, (TableColumn<S,?>) column);
1934 }
1935
1936 /**
1937 * Clears all selection, and then selects the cell at the given row/column
1938 * intersection.
1939 */
1940 public abstract void clearAndSelect(int row, TableColumn<S,?> column);
1941
1942
1943 // --- clearSelection
1944 /** {@inheritDoc} */
1945 @Override public void clearSelection(int row, TableColumnBase<S,?> column) {
1946 clearSelection(row, (TableColumn<S,?>) column);
1947 }
1948
1949 /**
1950 * Removes selection from the specified row/column position (in view indexes).
1951 * If this particular cell (or row if the column value is -1) is not selected,
1952 * nothing happens.
1953 */
1954 public abstract void clearSelection(int row, TableColumn<S, ?> column);
1955
1956 /** {@inheritDoc} */
1957 @Override public void selectRange(int minRow, TableColumnBase<S,?> minColumn,
1958 int maxRow, TableColumnBase<S,?> maxColumn) {
1959 final int minColumnIndex = tableView.getVisibleLeafIndex((TableColumn<S,?>)minColumn);
1960 final int maxColumnIndex = tableView.getVisibleLeafIndex((TableColumn<S,?>)maxColumn);
1961 for (int _row = minRow; _row <= maxRow; _row++) {
1962 for (int _col = minColumnIndex; _col <= maxColumnIndex; _col++) {
1963 select(_row, tableView.getVisibleLeafColumn(_col));
1964 }
1965 }
1966 }
1967
1968
1969
1970 /***********************************************************************
1971 * *
1972 * Public API *
1973 * *
1974 **********************************************************************/
1975
1976 /**
1977 * Returns the TableView instance that this selection model is installed in.
1978 */
1979 public TableView<S> getTableView() {
1980 return tableView;
1981 }
1982
1983 /**
1984 * Convenience method that returns getTableView().getItems().
1985 * @return The items list of the current TableView.
1986 */
1987 protected List<S> getTableModel() {
1988 return tableView.getItems();
1989 }
1990
1991 /** {@inheritDoc} */
1992 @Override protected S getModelItem(int index) {
1993 if (index < 0 || index >= getItemCount()) return null;
1994 return tableView.getItems().get(index);
1995 }
1996
1997 /** {@inheritDoc} */
3201 * *
3202 **********************************************************************/
3203
3204 /**
3205 * Tests whether the row / cell at the given location currently has the
3206 * focus within the TableView.
3207 */
3208 @Override public boolean isFocused(int row, TableColumn<S,?> column) {
3209 if (row < 0 || row >= getItemCount()) return false;
3210
3211 TablePosition cell = getFocusedCell();
3212 boolean columnMatch = column == null || column.equals(cell.getTableColumn());
3213
3214 return cell.getRow() == row && columnMatch;
3215 }
3216
3217 /**
3218 * Causes the item at the given index to receive the focus. This does not
3219 * cause the current selection to change. Updates the focusedItem and
3220 * focusedIndex properties such that <code>focusedIndex = -1</code> unless
3221 * <pre><code>0 <= index < model size</code></pre>.
3222 *
3223 * @param index The index of the item to get focus.
3224 */
3225 @Override public void focus(int index) {
3226 if (index < 0 || index >= getItemCount()) {
3227 setFocusedCell(EMPTY_CELL);
3228 } else {
3229 setFocusedCell(new TablePosition<>(tableView, index, null));
3230 }
3231 }
3232
3233 /**
3234 * Attempts to move focus to the cell above the currently focused cell.
3235 */
3236 @Override public void focusAboveCell() {
3237 TablePosition cell = getFocusedCell();
3238
3239 if (getFocusedIndex() == -1) {
3240 focus(getItemCount() - 1, cell.getTableColumn());
3241 } else if (getFocusedIndex() > 0) {
|
90 * <ul>
91 * <li>Powerful {@link TableColumn} API:
92 * <ul>
93 * <li>Support for {@link TableColumn#cellFactoryProperty() cell factories} to
94 * easily customize {@link Cell cell} contents in both rendering and editing
95 * states.
96 * <li>Specification of {@link TableColumn#minWidthProperty() minWidth}/
97 * {@link TableColumn#prefWidthProperty() prefWidth}/
98 * {@link TableColumn#maxWidthProperty() maxWidth},
99 * and also {@link TableColumn#resizableProperty() fixed width columns}.
100 * <li>Width resizing by the user at runtime.
101 * <li>Column reordering by the user at runtime.
102 * <li>Built-in support for {@link TableColumn#getColumns() column nesting}
103 * </ul>
104 * <li>Different {@link #columnResizePolicyProperty() resizing policies} to
105 * dictate what happens when the user resizes columns.
106 * <li>Support for {@link #getSortOrder() multiple column sorting} by clicking
107 * the column header (hold down Shift keyboard key whilst clicking on a
108 * header to sort by multiple columns).
109 * </ul>
110 *
111 * <p>Note that TableView is intended to be used to visualize data - it is not
112 * intended to be used for laying out your user interface. If you want to lay
113 * your user interface out in a grid-like fashion, consider the
114 * {@link javafx.scene.layout.GridPane} layout instead.</p>
115 *
116 * <h2>Creating a TableView</h2>
117 *
118 * <p>
119 * {@literal Creating a TableView is a multi-step process, and also depends on the
120 * underlying data model needing to be represented. For this example we'll use
121 * an ObservableList<Person>, as it is the simplest way of showing data in a
122 * TableView. The <code>Person</code> class will consist of a first
123 * name and last name properties. That is:}
124 *
125 * <pre>
126 * {@code
127 * public class Person {
128 * private StringProperty firstName;
129 * public void setFirstName(String value) { firstNameProperty().set(value); }
130 * public String getFirstName() { return firstNameProperty().get(); }
131 * public StringProperty firstNameProperty() {
132 * if (firstName == null) firstName = new SimpleStringProperty(this, "firstName");
133 * return firstName;
134 * }
135 *
136 * private StringProperty lastName;
137 * public void setLastName(String value) { lastNameProperty().set(value); }
138 * public String getLastName() { return lastNameProperty().get(); }
139 * public StringProperty lastNameProperty() {
140 * if (lastName == null) lastName = new SimpleStringProperty(this, "lastName");
141 * return lastName;
142 * }
143 * }}</pre>
144 *
145 * <p>Firstly, a TableView instance needs to be defined, as such:
146 *
147 * <pre>
148 * {@code
149 * TableView<Person> table = new TableView<Person>();}</pre>
150 *
151 * {@literal
152 * <p>With the basic table defined, we next focus on the data model. As mentioned,
153 * for this example, we'll be using a ObservableList<Person>. We can immediately
154 * set such a list directly in to the TableView, as such:}
155 *
156 * <pre>
157 * {@code
158 * ObservableList<Person> teamMembers = getTeamMembers();
159 * table.setItems(teamMembers);}</pre>
160 *
161 * <p>With the items set as such, TableView will automatically update whenever
162 * the <code>teamMembers</code> list changes. If the items list is available
163 * before the TableView is instantiated, it is possible to pass it directly into
164 * the constructor.
165 *
166 * <p>At this point we now have a TableView hooked up to observe the
167 * <code>teamMembers</code> observableList. The missing ingredient
168 * now is the means of splitting out the data contained within the model and
169 * representing it in one or more {@link TableColumn TableColumn} instances. To
170 * create a two-column TableView to show the firstName and lastName properties,
171 * we extend the last code sample as follows:
172 *
173 * <pre>
174 * {@code
818
819 private final WeakListChangeListener<TableColumn<S,?>> weakColumnsObserver =
820 new WeakListChangeListener<TableColumn<S,?>>(columnsObserver);
821
822 private final WeakInvalidationListener weakCellSelectionModelInvalidationListener =
823 new WeakInvalidationListener(cellSelectionModelInvalidationListener);
824
825
826
827 /***************************************************************************
828 * *
829 * Properties *
830 * *
831 **************************************************************************/
832
833
834 // --- Items
835 /**
836 * The underlying data model for the TableView. Note that it has a generic
837 * type that must match the type of the TableView itself.
838 * @return the items property
839 */
840 public final ObjectProperty<ObservableList<S>> itemsProperty() { return items; }
841 private ObjectProperty<ObservableList<S>> items =
842 new SimpleObjectProperty<ObservableList<S>>(this, "items") {
843 WeakReference<ObservableList<S>> oldItemsRef;
844
845 @Override protected void invalidated() {
846 final ObservableList<S> oldItems = oldItemsRef == null ? null : oldItemsRef.get();
847 final ObservableList<S> newItems = getItems();
848
849 // Fix for RT-36425
850 if (newItems != null && newItems == oldItems) {
851 return;
852 }
853
854 // Fix for RT-35763
855 if (! (newItems instanceof SortedList)) {
856 getSortOrder().clear();
857 }
858
859 oldItemsRef = new WeakReference<>(newItems);
860 }
861 };
862 public final void setItems(ObservableList<S> value) { itemsProperty().set(value); }
863 public final ObservableList<S> getItems() {return items.get(); }
864
865
866 // --- Table menu button visible
867 private BooleanProperty tableMenuButtonVisible;
868 /**
869 * This controls whether a menu button is available when the user clicks
870 * in a designated space within the TableView, within which is a radio menu
871 * item for each TableColumn in this table. This menu allows for the user to
872 * show and hide all TableColumns easily.
873 * @return the tableMenuButtonVisible property
874 */
875 public final BooleanProperty tableMenuButtonVisibleProperty() {
876 if (tableMenuButtonVisible == null) {
877 tableMenuButtonVisible = new SimpleBooleanProperty(this, "tableMenuButtonVisible");
878 }
879 return tableMenuButtonVisible;
880 }
881 public final void setTableMenuButtonVisible (boolean value) {
882 tableMenuButtonVisibleProperty().set(value);
883 }
884 public final boolean isTableMenuButtonVisible() {
885 return tableMenuButtonVisible == null ? false : tableMenuButtonVisible.get();
886 }
887
888
889 // --- Column Resize Policy
890 private ObjectProperty<Callback<ResizeFeatures, Boolean>> columnResizePolicy;
891 public final void setColumnResizePolicy(Callback<ResizeFeatures, Boolean> callback) {
892 columnResizePolicyProperty().set(callback);
893 }
894 public final Callback<ResizeFeatures, Boolean> getColumnResizePolicy() {
895 return columnResizePolicy == null ? UNCONSTRAINED_RESIZE_POLICY : columnResizePolicy.get();
896 }
897
898 /**
899 * This is the function called when the user completes a column-resize
900 * operation. The two most common policies are available as static functions
901 * in the TableView class: {@link #UNCONSTRAINED_RESIZE_POLICY} and
902 * {@link #CONSTRAINED_RESIZE_POLICY}.
903 * @return columnResizePolicy property
904 */
905 public final ObjectProperty<Callback<ResizeFeatures, Boolean>> columnResizePolicyProperty() {
906 if (columnResizePolicy == null) {
907 columnResizePolicy = new SimpleObjectProperty<Callback<ResizeFeatures, Boolean>>(this, "columnResizePolicy", UNCONSTRAINED_RESIZE_POLICY) {
908 private Callback<ResizeFeatures, Boolean> oldPolicy;
909
910 @Override protected void invalidated() {
911 if (isInited) {
912 get().call(new ResizeFeatures(TableView.this, null, 0.0));
913
914 if (oldPolicy != null) {
915 PseudoClass state = PseudoClass.getPseudoClass(oldPolicy.toString());
916 pseudoClassStateChanged(state, false);
917 }
918 if (get() != null) {
919 PseudoClass state = PseudoClass.getPseudoClass(get().toString());
920 pseudoClassStateChanged(state, true);
921 }
922 oldPolicy = get();
923 }
928 }
929
930
931 // --- Row Factory
932 private ObjectProperty<Callback<TableView<S>, TableRow<S>>> rowFactory;
933
934 /**
935 * A function which produces a TableRow. The system is responsible for
936 * reusing TableRows. Return from this function a TableRow which
937 * might be usable for representing a single row in a TableView.
938 * <p>
939 * Note that a TableRow is <b>not</b> a TableCell. A TableRow is
940 * simply a container for a TableCell, and in most circumstances it is more
941 * likely that you'll want to create custom TableCells, rather than
942 * TableRows. The primary use case for creating custom TableRow
943 * instances would most probably be to introduce some form of column
944 * spanning support.
945 * <p>
946 * You can create custom TableCell instances per column by assigning the
947 * appropriate function to the cellFactory property in the TableColumn class.
948 * @return rowFactory property
949 */
950 public final ObjectProperty<Callback<TableView<S>, TableRow<S>>> rowFactoryProperty() {
951 if (rowFactory == null) {
952 rowFactory = new SimpleObjectProperty<Callback<TableView<S>, TableRow<S>>>(this, "rowFactory");
953 }
954 return rowFactory;
955 }
956 public final void setRowFactory(Callback<TableView<S>, TableRow<S>> value) {
957 rowFactoryProperty().set(value);
958 }
959 public final Callback<TableView<S>, TableRow<S>> getRowFactory() {
960 return rowFactory == null ? null : rowFactory.get();
961 }
962
963
964 // --- Placeholder Node
965 private ObjectProperty<Node> placeholder;
966 /**
967 * This Node is shown to the user when the table has no content to show.
968 * This may be the case because the table model has no data in the first
969 * place, that a filter has been applied to the table model, resulting
970 * in there being nothing to show the user, or that there are no currently
971 * visible columns.
972 * @return placeholder property
973 */
974 public final ObjectProperty<Node> placeholderProperty() {
975 if (placeholder == null) {
976 placeholder = new SimpleObjectProperty<Node>(this, "placeholder");
977 }
978 return placeholder;
979 }
980 public final void setPlaceholder(Node value) {
981 placeholderProperty().set(value);
982 }
983 public final Node getPlaceholder() {
984 return placeholder == null ? null : placeholder.get();
985 }
986
987
988 // --- Selection Model
989 private ObjectProperty<TableViewSelectionModel<S>> selectionModel
990 = new SimpleObjectProperty<TableViewSelectionModel<S>>(this, "selectionModel") {
991
992 TableViewSelectionModel<S> oldValue = null;
999 if (oldValue instanceof TableViewArrayListSelectionModel) {
1000 ((TableViewArrayListSelectionModel)oldValue).dispose();
1001 }
1002 }
1003
1004 oldValue = get();
1005
1006 if (oldValue != null) {
1007 oldValue.cellSelectionEnabledProperty().addListener(weakCellSelectionModelInvalidationListener);
1008 // fake an invalidation to ensure updated pseudo-class state
1009 weakCellSelectionModelInvalidationListener.invalidated(oldValue.cellSelectionEnabledProperty());
1010 }
1011 }
1012 };
1013
1014 /**
1015 * The SelectionModel provides the API through which it is possible
1016 * to select single or multiple items within a TableView, as well as inspect
1017 * which items have been selected by the user. Note that it has a generic
1018 * type that must match the type of the TableView itself.
1019 * @return selectionModel property
1020 */
1021 public final ObjectProperty<TableViewSelectionModel<S>> selectionModelProperty() {
1022 return selectionModel;
1023 }
1024 public final void setSelectionModel(TableViewSelectionModel<S> value) {
1025 selectionModelProperty().set(value);
1026 }
1027
1028 public final TableViewSelectionModel<S> getSelectionModel() {
1029 return selectionModel.get();
1030 }
1031
1032
1033 // --- Focus Model
1034 private ObjectProperty<TableViewFocusModel<S>> focusModel;
1035 public final void setFocusModel(TableViewFocusModel<S> value) {
1036 focusModelProperty().set(value);
1037 }
1038 public final TableViewFocusModel<S> getFocusModel() {
1039 return focusModel == null ? null : focusModel.get();
1040 }
1041 /**
1042 * Represents the currently-installed {@link TableViewFocusModel} for this
1043 * TableView. Under almost all circumstances leaving this as the default
1044 * focus model will suffice.
1045 * @return focusModel property
1046 */
1047 public final ObjectProperty<TableViewFocusModel<S>> focusModelProperty() {
1048 if (focusModel == null) {
1049 focusModel = new SimpleObjectProperty<TableViewFocusModel<S>>(this, "focusModel");
1050 }
1051 return focusModel;
1052 }
1053
1054
1055 // // --- Span Model
1056 // private ObjectProperty<SpanModel<S>> spanModel
1057 // = new SimpleObjectProperty<SpanModel<S>>(this, "spanModel") {
1058 //
1059 // @Override protected void invalidated() {
1060 // ObservableList<String> styleClass = getStyleClass();
1061 // if (getSpanModel() == null) {
1062 // styleClass.remove(CELL_SPAN_TABLE_VIEW_STYLE_CLASS);
1063 // } else if (! styleClass.contains(CELL_SPAN_TABLE_VIEW_STYLE_CLASS)) {
1064 // styleClass.add(CELL_SPAN_TABLE_VIEW_STYLE_CLASS);
1065 // }
1072 // public final void setSpanModel(SpanModel<S> value) {
1073 // spanModelProperty().set(value);
1074 // }
1075 //
1076 // public final SpanModel<S> getSpanModel() {
1077 // return spanModel.get();
1078 // }
1079
1080 // --- Editable
1081 private BooleanProperty editable;
1082 public final void setEditable(boolean value) {
1083 editableProperty().set(value);
1084 }
1085 public final boolean isEditable() {
1086 return editable == null ? false : editable.get();
1087 }
1088 /**
1089 * Specifies whether this TableView is editable - only if the TableView, the
1090 * TableColumn (if applicable) and the TableCells within it are both
1091 * editable will a TableCell be able to go into their editing state.
1092 * @return the editable property
1093 */
1094 public final BooleanProperty editableProperty() {
1095 if (editable == null) {
1096 editable = new SimpleBooleanProperty(this, "editable", false);
1097 }
1098 return editable;
1099 }
1100
1101
1102 // --- Fixed cell size
1103 private DoubleProperty fixedCellSize;
1104
1105 /**
1106 * Sets the new fixed cell size for this control. Any value greater than
1107 * zero will enable fixed cell size mode, whereas a zero or negative value
1108 * (or Region.USE_COMPUTED_SIZE) will be used to disabled fixed cell size
1109 * mode.
1110 *
1111 * @param value The new fixed cell size value, or a value less than or equal
1112 * to zero (or Region.USE_COMPUTED_SIZE) to disable.
1130 }
1131 /**
1132 * Specifies whether this control has cells that are a fixed height (of the
1133 * specified value). If this value is less than or equal to zero,
1134 * then all cells are individually sized and positioned. This is a slow
1135 * operation. Therefore, when performance matters and developers are not
1136 * dependent on variable cell sizes it is a good idea to set the fixed cell
1137 * size value. Generally cells are around 24px, so setting a fixed cell size
1138 * of 24 is likely to result in very little difference in visuals, but a
1139 * improvement to performance.
1140 *
1141 * <p>To set this property via CSS, use the -fx-fixed-cell-size property.
1142 * This should not be confused with the -fx-cell-size property. The difference
1143 * between these two CSS properties is that -fx-cell-size will size all
1144 * cells to the specified size, but it will not enforce that this is the
1145 * only size (thus allowing for variable cell sizes, and preventing the
1146 * performance gains from being possible). Therefore, when performance matters
1147 * use -fx-fixed-cell-size, instead of -fx-cell-size. If both properties are
1148 * specified in CSS, -fx-fixed-cell-size takes precedence.</p>
1149 *
1150 * @return fixedCellSize property
1151 * @since JavaFX 8.0
1152 */
1153 public final DoubleProperty fixedCellSizeProperty() {
1154 if (fixedCellSize == null) {
1155 fixedCellSize = new StyleableDoubleProperty(Region.USE_COMPUTED_SIZE) {
1156 @Override public CssMetaData<TableView<?>,Number> getCssMetaData() {
1157 return StyleableProperties.FIXED_CELL_SIZE;
1158 }
1159
1160 @Override public Object getBean() {
1161 return TableView.this;
1162 }
1163
1164 @Override public String getName() {
1165 return "fixedCellSize";
1166 }
1167 };
1168 }
1169 return fixedCellSize;
1170 }
1171
1172
1173 // --- Editing Cell
1174 private ReadOnlyObjectWrapper<TablePosition<S,?>> editingCell;
1175 private void setEditingCell(TablePosition<S,?> value) {
1176 editingCellPropertyImpl().set(value);
1177 }
1178 public final TablePosition<S,?> getEditingCell() {
1179 return editingCell == null ? null : editingCell.get();
1180 }
1181
1182 /**
1183 * Represents the current cell being edited, or null if
1184 * there is no cell being edited.
1185 * @return the editingCell property
1186 */
1187 public final ReadOnlyObjectProperty<TablePosition<S,?>> editingCellProperty() {
1188 return editingCellPropertyImpl().getReadOnlyProperty();
1189 }
1190
1191 private ReadOnlyObjectWrapper<TablePosition<S,?>> editingCellPropertyImpl() {
1192 if (editingCell == null) {
1193 editingCell = new ReadOnlyObjectWrapper<TablePosition<S,?>>(this, "editingCell");
1194 }
1195 return editingCell;
1196 }
1197
1198
1199 // --- Comparator (built via sortOrder list, so read-only)
1200 /**
1201 * The comparator property is a read-only property that is representative of the
1202 * current state of the {@link #getSortOrder() sort order} list. The sort
1203 * order list contains the columns that have been added to it either programmatically
1204 * or via a user clicking on the headers themselves.
1205 * @since JavaFX 8.0
1297 return "onSort";
1298 }
1299 };
1300 }
1301 return onSort;
1302 }
1303
1304
1305 /***************************************************************************
1306 * *
1307 * Public API *
1308 * *
1309 **************************************************************************/
1310 /**
1311 * The TableColumns that are part of this TableView. As the user reorders
1312 * the TableView columns, this list will be updated to reflect the current
1313 * visual ordering.
1314 *
1315 * <p>Note: to display any data in a TableView, there must be at least one
1316 * TableColumn in this ObservableList.</p>
1317 * @return the columns
1318 */
1319 public final ObservableList<TableColumn<S,?>> getColumns() {
1320 return columns;
1321 }
1322
1323 /**
1324 * The sortOrder list defines the order in which {@link TableColumn} instances
1325 * are sorted. An empty sortOrder list means that no sorting is being applied
1326 * on the TableView. If the sortOrder list has one TableColumn within it,
1327 * the TableView will be sorted using the
1328 * {@link TableColumn#sortTypeProperty() sortType} and
1329 * {@link TableColumn#comparatorProperty() comparator} properties of this
1330 * TableColumn (assuming
1331 * {@link TableColumn#sortableProperty() TableColumn.sortable} is true).
1332 * If the sortOrder list contains multiple TableColumn instances, then
1333 * the TableView is firstly sorted based on the properties of the first
1334 * TableColumn. If two elements are considered equal, then the second
1335 * TableColumn in the list is used to determine ordering. This repeats until
1336 * the results from all TableColumn comparators are considered, if necessary.
1337 *
1446 @Override protected void invalidated() {
1447 EventType<ScrollToEvent<TableColumn<S, ?>>> type = ScrollToEvent.scrollToColumn();
1448 setEventHandler(type, get());
1449 }
1450
1451 @Override public Object getBean() {
1452 return TableView.this;
1453 }
1454
1455 @Override public String getName() {
1456 return "onScrollToColumn";
1457 }
1458 };
1459 }
1460 return onScrollToColumn;
1461 }
1462
1463 /**
1464 * Applies the currently installed resize policy against the given column,
1465 * resizing it based on the delta value provided.
1466 * @param column the column
1467 * @param delta the delta
1468 * @return true if column resize is allowed
1469 */
1470 public boolean resizeColumn(TableColumn<S,?> column, double delta) {
1471 if (column == null || Double.compare(delta, 0.0) == 0) return false;
1472
1473 boolean allowed = getColumnResizePolicy().call(new ResizeFeatures<S>(TableView.this, column, delta));
1474 if (!allowed) return false;
1475
1476 return true;
1477 }
1478
1479 /**
1480 * Causes the cell at the given row/column view indexes to switch into
1481 * its editing state, if it is not already in it, and assuming that the
1482 * TableView and column are also editable.
1483 *
1484 * <p><strong>Note:</strong> This method will cancel editing if the given row
1485 * value is less than zero and the given column is null.</p>
1486 * @param row the row
1487 * @param column the column
1488 */
1489 public void edit(int row, TableColumn<S,?> column) {
1490 if (!isEditable() || (column != null && ! column.isEditable())) {
1491 return;
1492 }
1493
1494 if (row < 0 && column == null) {
1495 setEditingCell(null);
1496 } else {
1497 setEditingCell(new TablePosition<>(this, row, column));
1498 }
1499 }
1500
1501 /**
1502 * Returns an unmodifiable list containing the currently visible leaf columns.
1503 * @return an unmodifiable list containing the currently visible leaf columns
1504 */
1505 public ObservableList<TableColumn<S,?>> getVisibleLeafColumns() {
1506 return unmodifiableVisibleLeafColumns;
1507 }
1508
1509 /**
1510 * Returns the position of the given column, relative to all other
1511 * visible leaf columns.
1512 * @param column the column
1513 * @return the position of the given column, relative to all other
1514 * visible leaf columns
1515 */
1516 public int getVisibleLeafIndex(TableColumn<S,?> column) {
1517 return visibleLeafColumns.indexOf(column);
1518 }
1519
1520 /**
1521 * Returns the TableColumn in the given column index, relative to all other
1522 * visible leaf columns.
1523 * @param column the column
1524 * @return the TableColumn in the given column index, relative to all other
1525 * visible leaf columns
1526 */
1527 public TableColumn<S,?> getVisibleLeafColumn(int column) {
1528 if (column < 0 || column >= visibleLeafColumns.size()) return null;
1529 return visibleLeafColumns.get(column);
1530 }
1531
1532 /** {@inheritDoc} */
1533 @Override protected Skin<?> createDefaultSkin() {
1534 return new TableViewSkin<S>(this);
1535 }
1536
1537 /**
1538 * The sort method forces the TableView to re-run its sorting algorithm. More
1539 * often than not it is not necessary to call this method directly, as it is
1540 * automatically called when the {@link #getSortOrder() sort order},
1541 * {@link #sortPolicyProperty() sort policy}, or the state of the
1542 * TableColumn {@link TableColumn#sortTypeProperty() sort type} properties
1543 * change. In other words, this method should only be called directly when
1544 * something external changes and a sort is required.
1545 * @since JavaFX 8.0
1832 * TableView resize operation.
1833 * @param delta The amount of horizontal space added or removed in the
1834 * resize operation.
1835 */
1836 public ResizeFeatures(TableView<S> table, TableColumn<S,?> column, Double delta) {
1837 super(column, delta);
1838 this.table = table;
1839 }
1840
1841 /**
1842 * Returns the column upon which the resize is occurring, or null
1843 * if this ResizeFeatures instance was created as a result of a
1844 * TableView resize operation.
1845 */
1846 @Override public TableColumn<S,?> getColumn() {
1847 return (TableColumn<S,?>) super.getColumn();
1848 }
1849
1850 /**
1851 * Returns the TableView upon which the resize operation is occurring.
1852 * @return the TableView
1853 */
1854 public TableView<S> getTable() {
1855 return table;
1856 }
1857 }
1858
1859
1860
1861 /***************************************************************************
1862 * *
1863 * Support Classes *
1864 * *
1865 **************************************************************************/
1866
1867
1868 /**
1869 * A simple extension of the {@link SelectionModel} abstract class to
1870 * allow for special support for TableView controls.
1871 * @since JavaFX 2.0
1872 */
1900 public TableViewSelectionModel(final TableView<S> tableView) {
1901 if (tableView == null) {
1902 throw new NullPointerException("TableView can not be null");
1903 }
1904
1905 this.tableView = tableView;
1906 }
1907
1908
1909
1910 /***********************************************************************
1911 * *
1912 * Abstract API *
1913 * *
1914 **********************************************************************/
1915
1916 /**
1917 * A read-only ObservableList representing the currently selected cells
1918 * in this TableView. Rather than directly modify this list, please
1919 * use the other methods provided in the TableViewSelectionModel.
1920 * @return a read-only ObservableList representing the currently
1921 * selected cells in this TableView
1922 */
1923 public abstract ObservableList<TablePosition> getSelectedCells();
1924
1925
1926 /***********************************************************************
1927 * *
1928 * Generic (type erasure) bridging *
1929 * *
1930 **********************************************************************/
1931
1932 // --- isSelected
1933 /** {@inheritDoc} */
1934 @Override public boolean isSelected(int row, TableColumnBase<S, ?> column) {
1935 return isSelected(row, (TableColumn<S,?>)column);
1936 }
1937
1938 /**
1939 * Convenience function which tests whether the given row and column index
1940 * is currently selected in this table instance.
1941 * @param row the row
1942 * @param column the column
1943 * @return true if row and column index is currently selected
1944 */
1945 public abstract boolean isSelected(int row, TableColumn<S, ?> column);
1946
1947
1948 // --- select
1949 /** {@inheritDoc} */
1950 @Override public void select(int row, TableColumnBase<S, ?> column) {
1951 select(row, (TableColumn<S,?>)column);
1952 }
1953
1954 /**
1955 * Selects the cell at the given row/column intersection.
1956 * @param row the row
1957 * @param column the column
1958 */
1959 public abstract void select(int row, TableColumn<S, ?> column);
1960
1961
1962 // --- clearAndSelect
1963 /** {@inheritDoc} */
1964 @Override public void clearAndSelect(int row, TableColumnBase<S,?> column) {
1965 clearAndSelect(row, (TableColumn<S,?>) column);
1966 }
1967
1968 /**
1969 * Clears all selection, and then selects the cell at the given row/column
1970 * intersection.
1971 * @param row the row
1972 * @param column the column
1973 */
1974 public abstract void clearAndSelect(int row, TableColumn<S,?> column);
1975
1976
1977 // --- clearSelection
1978 /** {@inheritDoc} */
1979 @Override public void clearSelection(int row, TableColumnBase<S,?> column) {
1980 clearSelection(row, (TableColumn<S,?>) column);
1981 }
1982
1983 /**
1984 * Removes selection from the specified row/column position (in view indexes).
1985 * If this particular cell (or row if the column value is -1) is not selected,
1986 * nothing happens.
1987 * @param row the row
1988 * @param column the column
1989 */
1990 public abstract void clearSelection(int row, TableColumn<S, ?> column);
1991
1992 /** {@inheritDoc} */
1993 @Override public void selectRange(int minRow, TableColumnBase<S,?> minColumn,
1994 int maxRow, TableColumnBase<S,?> maxColumn) {
1995 final int minColumnIndex = tableView.getVisibleLeafIndex((TableColumn<S,?>)minColumn);
1996 final int maxColumnIndex = tableView.getVisibleLeafIndex((TableColumn<S,?>)maxColumn);
1997 for (int _row = minRow; _row <= maxRow; _row++) {
1998 for (int _col = minColumnIndex; _col <= maxColumnIndex; _col++) {
1999 select(_row, tableView.getVisibleLeafColumn(_col));
2000 }
2001 }
2002 }
2003
2004
2005
2006 /***********************************************************************
2007 * *
2008 * Public API *
2009 * *
2010 **********************************************************************/
2011
2012 /**
2013 * Returns the TableView instance that this selection model is installed in.
2014 * @return the TableView
2015 */
2016 public TableView<S> getTableView() {
2017 return tableView;
2018 }
2019
2020 /**
2021 * Convenience method that returns getTableView().getItems().
2022 * @return The items list of the current TableView.
2023 */
2024 protected List<S> getTableModel() {
2025 return tableView.getItems();
2026 }
2027
2028 /** {@inheritDoc} */
2029 @Override protected S getModelItem(int index) {
2030 if (index < 0 || index >= getItemCount()) return null;
2031 return tableView.getItems().get(index);
2032 }
2033
2034 /** {@inheritDoc} */
3238 * *
3239 **********************************************************************/
3240
3241 /**
3242 * Tests whether the row / cell at the given location currently has the
3243 * focus within the TableView.
3244 */
3245 @Override public boolean isFocused(int row, TableColumn<S,?> column) {
3246 if (row < 0 || row >= getItemCount()) return false;
3247
3248 TablePosition cell = getFocusedCell();
3249 boolean columnMatch = column == null || column.equals(cell.getTableColumn());
3250
3251 return cell.getRow() == row && columnMatch;
3252 }
3253
3254 /**
3255 * Causes the item at the given index to receive the focus. This does not
3256 * cause the current selection to change. Updates the focusedItem and
3257 * focusedIndex properties such that <code>focusedIndex = -1</code> unless
3258 * <pre><code>0 <= index < model size</code></pre>.
3259 *
3260 * @param index The index of the item to get focus.
3261 */
3262 @Override public void focus(int index) {
3263 if (index < 0 || index >= getItemCount()) {
3264 setFocusedCell(EMPTY_CELL);
3265 } else {
3266 setFocusedCell(new TablePosition<>(tableView, index, null));
3267 }
3268 }
3269
3270 /**
3271 * Attempts to move focus to the cell above the currently focused cell.
3272 */
3273 @Override public void focusAboveCell() {
3274 TablePosition cell = getFocusedCell();
3275
3276 if (getFocusedIndex() == -1) {
3277 focus(getItemCount() - 1, cell.getTableColumn());
3278 } else if (getFocusedIndex() > 0) {
|