--- old/modules/controls/src/main/java/javafx/scene/control/TableView.java 2015-09-03 14:59:13.384328300 -0700
+++ new/modules/controls/src/main/java/javafx/scene/control/TableView.java 2015-09-03 14:59:12.829296600 -0700
@@ -1,3435 +1,3441 @@
-/*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package javafx.scene.control;
-
-import java.lang.ref.WeakReference;
-import java.util.*;
-
-import com.sun.javafx.scene.control.Logging;
-import com.sun.javafx.scene.control.SelectedCellsMap;
-import com.sun.javafx.scene.control.behavior.TableCellBehavior;
-import com.sun.javafx.scene.control.behavior.TableCellBehaviorBase;
-import javafx.beans.*;
-import javafx.beans.Observable;
-import javafx.beans.property.BooleanProperty;
-import javafx.beans.property.DoubleProperty;
-import javafx.beans.property.ObjectProperty;
-import javafx.beans.property.ObjectPropertyBase;
-import javafx.beans.property.Property;
-import javafx.beans.property.ReadOnlyObjectProperty;
-import javafx.beans.property.ReadOnlyObjectWrapper;
-import javafx.beans.property.SimpleBooleanProperty;
-import javafx.beans.property.SimpleObjectProperty;
-import javafx.beans.value.ChangeListener;
-import javafx.beans.value.ObservableValue;
-import javafx.collections.FXCollections;
-import javafx.collections.ListChangeListener;
-import javafx.collections.MapChangeListener;
-import javafx.collections.ObservableList;
-import javafx.collections.WeakListChangeListener;
-import javafx.collections.transformation.SortedList;
-import javafx.css.CssMetaData;
-import javafx.css.PseudoClass;
-import javafx.css.Styleable;
-import javafx.css.StyleableDoubleProperty;
-import javafx.css.StyleableProperty;
-import javafx.event.EventHandler;
-import javafx.event.EventType;
-import javafx.scene.AccessibleAttribute;
-import javafx.scene.AccessibleRole;
-import javafx.scene.Node;
-import javafx.scene.layout.Region;
-import javafx.util.Callback;
-
-import com.sun.javafx.collections.MappingChange;
-import com.sun.javafx.collections.NonIterableChange;
-import com.sun.javafx.collections.annotations.ReturnsUnmodifiableCollection;
-import com.sun.javafx.css.converters.SizeConverter;
-import com.sun.javafx.scene.control.ReadOnlyUnbackedObservableList;
-import com.sun.javafx.scene.control.TableColumnComparatorBase.TableColumnComparator;
-import com.sun.javafx.scene.control.skin.TableViewSkin;
-import com.sun.javafx.scene.control.skin.TableViewSkinBase;
-
-/**
- * The TableView control is designed to visualize an unlimited number of rows
- * of data, broken out into columns. A TableView is therefore very similar to the
- * {@link ListView} control, with the addition of support for columns. For an
- * example on how to create a TableView, refer to the 'Creating a TableView'
- * control section below.
- *
- *
The TableView control has a number of features, including:
- *
- *
Powerful {@link TableColumn} API:
- *
- *
Support for {@link TableColumn#cellFactoryProperty() cell factories} to
- * easily customize {@link Cell cell} contents in both rendering and editing
- * states.
- *
Specification of {@link TableColumn#minWidthProperty() minWidth}/
- * {@link TableColumn#prefWidthProperty() prefWidth}/
- * {@link TableColumn#maxWidthProperty() maxWidth},
- * and also {@link TableColumn#resizableProperty() fixed width columns}.
- *
Width resizing by the user at runtime.
- *
Column reordering by the user at runtime.
- *
Built-in support for {@link TableColumn#getColumns() column nesting}
- *
- *
Different {@link #columnResizePolicyProperty() resizing policies} to
- * dictate what happens when the user resizes columns.
- *
Support for {@link #getSortOrder() multiple column sorting} by clicking
- * the column header (hold down Shift keyboard key whilst clicking on a
- * header to sort by multiple columns).
- *
- *
- *
- *
Note that TableView is intended to be used to visualize data - it is not
- * intended to be used for laying out your user interface. If you want to lay
- * your user interface out in a grid-like fashion, consider the
- * {@link javafx.scene.layout.GridPane} layout instead.
- *
- *
Creating a TableView
- *
- *
Creating a TableView is a multi-step process, and also depends on the
- * underlying data model needing to be represented. For this example we'll use
- * an ObservableList, as it is the simplest way of showing data in a
- * TableView. The Person class will consist of a first
- * name and last name properties. That is:
- *
- *
- * {@code
- * public class Person {
- * private StringProperty firstName;
- * public void setFirstName(String value) { firstNameProperty().set(value); }
- * public String getFirstName() { return firstNameProperty().get(); }
- * public StringProperty firstNameProperty() {
- * if (firstName == null) firstName = new SimpleStringProperty(this, "firstName");
- * return firstName;
- * }
- *
- * private StringProperty lastName;
- * public void setLastName(String value) { lastNameProperty().set(value); }
- * public String getLastName() { return lastNameProperty().get(); }
- * public StringProperty lastNameProperty() {
- * if (lastName == null) lastName = new SimpleStringProperty(this, "lastName");
- * return lastName;
- * }
- * }}
- *
- *
Firstly, a TableView instance needs to be defined, as such:
- *
- *
- * {@code
- * TableView table = new TableView();}
- *
- *
With the basic table defined, we next focus on the data model. As mentioned,
- * for this example, we'll be using a ObservableList. We can immediately
- * set such a list directly in to the TableView, as such:
- *
- *
With the items set as such, TableView will automatically update whenever
- * the teamMembers list changes. If the items list is available
- * before the TableView is instantiated, it is possible to pass it directly into
- * the constructor.
- *
- *
At this point we now have a TableView hooked up to observe the
- * teamMembers observableList. The missing ingredient
- * now is the means of splitting out the data contained within the model and
- * representing it in one or more {@link TableColumn TableColumn} instances. To
- * create a two-column TableView to show the firstName and lastName properties,
- * we extend the last code sample as follows:
- *
- *
With the code shown above we have fully defined the minimum properties
- * required to create a TableView instance. Running this code (assuming the
- * people ObservableList is appropriately created) will result in a TableView being
- * shown with two columns for firstName and lastName. Any other properties of the
- * Person class will not be shown, as no TableColumns are defined.
- *
- *
TableView support for classes that don't contain properties
- *
- *
The code shown above is the shortest possible code for creating a TableView
- * when the domain objects are designed with JavaFX properties in mind
- * (additionally, {@link javafx.scene.control.cell.PropertyValueFactory} supports
- * normal JavaBean properties too, although there is a caveat to this, so refer
- * to the class documentation for more information). When this is not the case,
- * it is necessary to provide a custom cell value factory. More information
- * about cell value factories can be found in the {@link TableColumn} API
- * documentation, but briefly, here is how a TableColumn could be specified:
- *
- *
- * {@code
- * firstNameCol.setCellValueFactory(new Callback, ObservableValue>() {
- * public ObservableValue call(CellDataFeatures p) {
- * // p.getValue() returns the Person instance for a particular TableView row
- * return p.getValue().firstNameProperty();
- * }
- * });
- * }}
- *
- *
TableView Selection / Focus APIs
- *
To track selection and focus, it is necessary to become familiar with the
- * {@link SelectionModel} and {@link FocusModel} classes. A TableView has at most
- * one instance of each of these classes, available from
- * {@link #selectionModelProperty() selectionModel} and
- * {@link #focusModelProperty() focusModel} properties respectively.
- * Whilst it is possible to use this API to set a new selection model, in
- * most circumstances this is not necessary - the default selection and focus
- * models should work in most circumstances.
- *
- *
The default {@link SelectionModel} used when instantiating a TableView is
- * an implementation of the {@link MultipleSelectionModel} abstract class.
- * However, as noted in the API documentation for
- * the {@link MultipleSelectionModel#selectionModeProperty() selectionMode}
- * property, the default value is {@link SelectionMode#SINGLE}. To enable
- * multiple selection in a default TableView instance, it is therefore necessary
- * to do the following:
- *
- *
The visuals of the TableView can be entirely customized by replacing the
- * default {@link #rowFactoryProperty() row factory}. A row factory is used to
- * generate {@link TableRow} instances, which are used to represent an entire
- * row in the TableView.
- *
- *
In many cases, this is not what is desired however, as it is more commonly
- * the case that cells be customized on a per-column basis, not a per-row basis.
- * It is therefore important to note that a {@link TableRow} is not a
- * {@link TableCell}. A {@link TableRow} is simply a container for zero or more
- * {@link TableCell}, and in most circumstances it is more likely that you'll
- * want to create custom TableCells, rather than TableRows. The primary use case
- * for creating custom TableRow instances would most probably be to introduce
- * some form of column spanning support.
- *
- *
You can create custom {@link TableCell} instances per column by assigning
- * the appropriate function to the TableColumn
- * {@link TableColumn#cellFactoryProperty() cell factory} property.
- *
- *
See the {@link Cell} class documentation for a more complete
- * description of how to write custom Cells.
- *
- *
Sorting
- *
Prior to JavaFX 8.0, the TableView control would treat the
- * {@link #getItems() items} list as the view model, meaning that any changes to
- * the list would be immediately reflected visually. TableView would also modify
- * the order of this list directly when a user initiated a sort. This meant that
- * (again, prior to JavaFX 8.0) it was not possible to have the TableView return
- * to an unsorted state (after iterating through ascending and descending
- * orders).
- *
- *
Starting with JavaFX 8.0 (and the introduction of {@link SortedList}), it
- * is now possible to have the collection return to the unsorted state when
- * there are no columns as part of the TableView
- * {@link #getSortOrder() sort order}. To do this, you must create a SortedList
- * instance, and bind its
- * {@link javafx.collections.transformation.SortedList#comparatorProperty() comparator}
- * property to the TableView {@link #comparatorProperty() comparator} property,
- * list so:
- *
- *
- * {@code
- * // create a SortedList based on the provided ObservableList
- * SortedList sortedList = new SortedList(FXCollections.observableArrayList(2, 1, 3));
- *
- * // create a TableView with the sorted list set as the items it will show
- * final TableView tableView = new TableView<>(sortedList);
- *
- * // bind the sortedList comparator to the TableView comparator
- * sortedList.comparatorProperty().bind(tableView.comparatorProperty());
- *
- * // Don't forget to define columns!
- * }
- *
- *
Editing
- *
This control supports inline editing of values, and this section attempts to
- * give an overview of the available APIs and how you should use them.
- *
- *
Firstly, cell editing most commonly requires a different user interface
- * than when a cell is not being edited. This is the responsibility of the
- * {@link Cell} implementation being used. For TableView, it is highly
- * recommended that editing be
- * {@link javafx.scene.control.TableColumn#cellFactoryProperty() per-TableColumn},
- * rather than {@link #rowFactoryProperty() per row}, as more often than not
- * you want users to edit each column value differently, and this approach allows
- * for editors specific to each column. It is your choice whether the cell is
- * permanently in an editing state (e.g. this is common for {@link CheckBox} cells),
- * or to switch to a different UI when editing begins (e.g. when a double-click
- * is received on a cell).
- *
- *
To know when editing has been requested on a cell,
- * simply override the {@link javafx.scene.control.Cell#startEdit()} method, and
- * update the cell {@link javafx.scene.control.Cell#textProperty() text} and
- * {@link javafx.scene.control.Cell#graphicProperty() graphic} properties as
- * appropriate (e.g. set the text to null and set the graphic to be a
- * {@link TextField}). Additionally, you should also override
- * {@link Cell#cancelEdit()} to reset the UI back to its original visual state
- * when the editing concludes. In both cases it is important that you also
- * ensure that you call the super method to have the cell perform all duties it
- * must do to enter or exit its editing mode.
- *
- *
Once your cell is in an editing state, the next thing you are most probably
- * interested in is how to commit or cancel the editing that is taking place. This is your
- * responsibility as the cell factory provider. Your cell implementation will know
- * when the editing is over, based on the user input (e.g. when the user presses
- * the Enter or ESC keys on their keyboard). When this happens, it is your
- * responsibility to call {@link Cell#commitEdit(Object)} or
- * {@link Cell#cancelEdit()}, as appropriate.
- *
- *
When you call {@link Cell#commitEdit(Object)} an event is fired to the
- * TableView, which you can observe by adding an {@link EventHandler} via
- * {@link TableColumn#setOnEditCommit(javafx.event.EventHandler)}. Similarly,
- * you can also observe edit events for
- * {@link TableColumn#setOnEditStart(javafx.event.EventHandler) edit start}
- * and {@link TableColumn#setOnEditCancel(javafx.event.EventHandler) edit cancel}.
- *
- *
By default the TableColumn edit commit handler is non-null, with a default
- * handler that attempts to overwrite the property value for the
- * item in the currently-being-edited row. It is able to do this as the
- * {@link Cell#commitEdit(Object)} method is passed in the new value, and this
- * is passed along to the edit commit handler via the
- * {@link javafx.scene.control.TableColumn.CellEditEvent CellEditEvent} that is
- * fired. It is simply a matter of calling
- * {@link javafx.scene.control.TableColumn.CellEditEvent#getNewValue()} to
- * retrieve this value.
- *
- *
It is very important to note that if you call
- * {@link TableColumn#setOnEditCommit(javafx.event.EventHandler)} with your own
- * {@link EventHandler}, then you will be removing the default handler. Unless
- * you then handle the writeback to the property (or the relevant data source),
- * nothing will happen. You can work around this by using the
- * {@link TableColumn#addEventHandler(javafx.event.EventType, javafx.event.EventHandler)}
- * method to add a {@link TableColumn#EDIT_COMMIT_EVENT} {@link EventType} with
- * your desired {@link EventHandler} as the second argument. Using this method,
- * you will not replace the default implementation, but you will be notified when
- * an edit commit has occurred.
- *
- *
Hopefully this summary answers some of the commonly asked questions.
- * Fortunately, JavaFX ships with a number of pre-built cell factories that
- * handle all the editing requirements on your behalf. You can find these
- * pre-built cell factories in the javafx.scene.control.cell package.
- *
- * @see TableColumn
- * @see TablePosition
- * @param The type of the objects contained within the TableView items list.
- * @since JavaFX 2.0
- */
-@DefaultProperty("items")
-public class TableView extends Control {
-
- /***************************************************************************
- * *
- * Static properties and methods *
- * *
- **************************************************************************/
-
- // strings used to communicate via the TableView properties map between
- // the control and the skin. Because they are private here, the strings
- // are also duplicated in the TableViewSkin class - so any changes to these
- // strings must also be duplicated there
- static final String SET_CONTENT_WIDTH = "TableView.contentWidth";
-
- /**
- *
Very simple resize policy that just resizes the specified column by the
- * provided delta and shifts all other columns (to the right of the given column)
- * further to the right (when the delta is positive) or to the left (when the
- * delta is negative).
- *
- *
It also handles the case where we have nested columns by sharing the new space,
- * or subtracting the removed space, evenly between all immediate children columns.
- * Of course, the immediate children may themselves be nested, and they would
- * then use this policy on their children.
- */
- public static final Callback UNCONSTRAINED_RESIZE_POLICY = new Callback() {
- @Override public String toString() {
- return "unconstrained-resize";
- }
-
- @Override public Boolean call(ResizeFeatures prop) {
- double result = TableUtil.resize(prop.getColumn(), prop.getDelta());
- return Double.compare(result, 0.0) == 0;
- }
- };
-
- /**
- *
Simple policy that ensures the width of all visible leaf columns in
- * this table sum up to equal the width of the table itself.
- *
- *
When the user resizes a column width with this policy, the table automatically
- * adjusts the width of the right hand side columns. When the user increases a
- * column width, the table decreases the width of the rightmost column until it
- * reaches its minimum width. Then it decreases the width of the second
- * rightmost column until it reaches minimum width and so on. When all right
- * hand side columns reach minimum size, the user cannot increase the size of
- * resized column any more.
- */
- public static final Callback CONSTRAINED_RESIZE_POLICY = new Callback() {
-
- private boolean isFirstRun = true;
-
- @Override public String toString() {
- return "constrained-resize";
- }
-
- @Override public Boolean call(ResizeFeatures prop) {
- TableView> table = prop.getTable();
- List extends TableColumnBase,?>> visibleLeafColumns = table.getVisibleLeafColumns();
- Boolean result = TableUtil.constrainedResize(prop,
- isFirstRun,
- table.contentWidth,
- visibleLeafColumns);
- isFirstRun = ! isFirstRun ? false : ! result;
- return result;
- }
- };
-
- /**
- * The default {@link #sortPolicyProperty() sort policy} that this TableView
- * will use if no other policy is specified. The sort policy is a simple
- * {@link Callback} that accepts a TableView as the sole argument and expects
- * a Boolean response representing whether the sort succeeded or not. A Boolean
- * response of true represents success, and a response of false (or null) will
- * be considered to represent failure.
- * @since JavaFX 8.0
- */
- public static final Callback DEFAULT_SORT_POLICY = new Callback() {
- @Override public Boolean call(TableView table) {
- try {
- ObservableList> itemsList = table.getItems();
- if (itemsList instanceof SortedList) {
- // it is the responsibility of the SortedList to bind to the
- // comparator provided by the TableView. However, we don't
- // want to fail the sort (which would put the UI in an
- // inconsistent state), so we return true here, but only if
- // the SortedList has its comparator bound to the TableView
- // comparator property.
- SortedList sortedList = (SortedList) itemsList;
- boolean comparatorsBound = sortedList.comparatorProperty().
- isEqualTo(table.comparatorProperty()).get();
-
- if (! comparatorsBound) {
- // this isn't a good situation to be in, so lets log it
- // out in case the developer is unaware
- if (Logging.getControlsLogger().isEnabled()) {
- String s = "TableView items list is a SortedList, but the SortedList " +
- "comparator should be bound to the TableView comparator for " +
- "sorting to be enabled (e.g. " +
- "sortedList.comparatorProperty().bind(tableView.comparatorProperty());).";
- Logging.getControlsLogger().info(s);
- }
- }
- return comparatorsBound;
- } else {
- if (itemsList == null || itemsList.isEmpty()) {
- // sorting is not supported on null or empty lists
- return true;
- }
-
- Comparator comparator = table.getComparator();
- if (comparator == null) {
- return true;
- }
-
- // otherwise we attempt to do a manual sort, and if successful
- // we return true
- FXCollections.sort(itemsList, comparator);
- return true;
- }
- } catch (UnsupportedOperationException e) {
- // TODO might need to support other exception types including:
- // ClassCastException - if the class of the specified element prevents it from being added to this list
- // NullPointerException - if the specified element is null and this list does not permit null elements
- // IllegalArgumentException - if some property of this element prevents it from being added to this list
-
- // If we are here the list does not support sorting, so we gracefully
- // fail the sort request and ensure the UI is put back to its previous
- // state. This is handled in the code that calls the sort policy.
-
- return false;
- }
- }
- };
-
-
-
- /***************************************************************************
- * *
- * Constructors *
- * *
- **************************************************************************/
-
- /**
- * Creates a default TableView control with no content.
- *
- *
Refer to the {@link TableView} class documentation for details on the
- * default state of other properties.
- */
- public TableView() {
- this(FXCollections.observableArrayList());
- }
-
- /**
- * Creates a TableView with the content provided in the items ObservableList.
- * This also sets up an observer such that any changes to the items list
- * will be immediately reflected in the TableView itself.
- *
- *
Refer to the {@link TableView} class documentation for details on the
- * default state of other properties.
- *
- * @param items The items to insert into the TableView, and the list to watch
- * for changes (to automatically show in the TableView).
- */
- public TableView(ObservableList items) {
- getStyleClass().setAll(DEFAULT_STYLE_CLASS);
- setAccessibleRole(AccessibleRole.TABLE_VIEW);
-
- // we quite happily accept items to be null here
- setItems(items);
-
- // install default selection and focus models
- // it's unlikely this will be changed by many users.
- setSelectionModel(new TableViewArrayListSelectionModel(this));
- setFocusModel(new TableViewFocusModel(this));
-
- // we watch the columns list, such that when it changes we can update
- // the leaf columns and visible leaf columns lists (which are read-only).
- getColumns().addListener(weakColumnsObserver);
-
- // watch for changes to the sort order list - and when it changes run
- // the sort method.
- getSortOrder().addListener((ListChangeListener>) c -> {
- doSort(TableUtil.SortEventType.SORT_ORDER_CHANGE, c);
- });
-
- // We're watching for changes to the content width such
- // that the resize policy can be run if necessary. This comes from
- // TreeViewSkin.
- getProperties().addListener(new MapChangeListener