diff --git a/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TableCellBehaviorBase.java b/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TableCellBehaviorBase.java --- a/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TableCellBehaviorBase.java +++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TableCellBehaviorBase.java @@ -258,11 +258,7 @@ sm.clearSelection(); // and then perform the selection - for (int _row = minRow; _row <= maxRow; _row++) { - for (int _col = minColumn; _col <= maxColumn; _col++) { - sm.select(_row, getVisibleLeafColumn(_col)); - } - } + sm.selectRange(minRow, minColumn, maxRow, maxColumn); } else { // To prevent RT-32119, we make a copy of the selected indices // list first, so that we are not iterating and modifying it diff --git a/modules/controls/src/main/java/javafx/scene/control/TableCell.java b/modules/controls/src/main/java/javafx/scene/control/TableCell.java --- a/modules/controls/src/main/java/javafx/scene/control/TableCell.java +++ b/modules/controls/src/main/java/javafx/scene/control/TableCell.java @@ -107,14 +107,72 @@ */ private ListChangeListener selectedListener = new ListChangeListener() { @Override public void onChanged(Change c) { - updateSelection(); + if (! isInCellSelectionMode()) { + return; + } + + boolean updateSelection = false; + boolean isSelected = false; + + final int index = getIndex(); + if (index < 0) { + return; + } + final TableColumn tc = getTableColumn(); + + while (c.next() && ! updateSelection) { + // check if selection was removed + final List removed = c.getRemoved(); + for (int i = 0, max = c.getRemovedSize(); i < max; i++) { + TablePosition tp = removed.get(i); + + // lets assume that the cells are ordered by row, so if + // tp.getRow() > index, then we break out. This isn't actually + // true today. + if (tp.getRow() > index) { + break; + } + + if (tp != null && tp.getRow() == index && tc.equals(tp.getTableColumn())) { + updateSelection = true; + isSelected = false; + break; + } + } + + // if we haven't already determined we need to update selection, + // we check if selection was added to this cell + if (! updateSelection) { + final List added = c.getAddedSubList(); + for (int i = 0, max = c.getAddedSize(); i < max; i++) { + TablePosition tp = added.get(i); + + // lets assume that the cells are ordered by row, so if + // tp.getRow() > index, then we break out. This isn't actually + // true today. + if (tp.getRow() > index) { + break; + } + + if (tp != null && tp.getRow() == index && tc.equals(tp.getTableColumn())) { + updateSelection = true; + isSelected = true; + break; + } + } + } + } + + if (updateSelection) { + updateSelection(isSelected); + } } }; // same as above, but for focus private final InvalidationListener focusedListener = new InvalidationListener() { @Override public void invalidated(Observable value) { - updateFocus(); +// updateFocus(); } }; @@ -431,7 +489,7 @@ // itemDirty = true; // requestLayout(); updateItem(); - updateSelection(); + updateSelection(null); updateFocus(); } @@ -451,7 +509,10 @@ pseudoClassStateChanged(PSEUDO_CLASS_LAST_VISIBLE, isLastVisibleColumn); } - private void updateSelection() { + // Boolean - true == the cell is selected + // - false == the cell is not selected + // - null == cell selection is unknown (go and find out) + private void updateSelection(final Boolean isSelected) { /* * This cell should be selected if the selection mode of the table * is cell-based, and if the row and column that this cell represents @@ -462,23 +523,31 @@ * selected. */ if (isEmpty()) return; - if (getIndex() == -1 || getTableView() == null) return; - if (getTableView().getSelectionModel() == null) return; - - boolean isSelected = isInCellSelectionMode() && - getTableView().getSelectionModel().isSelected(getIndex(), getTableColumn()); - if (isSelected() == isSelected) return; - updateSelected(isSelected); + final TableView tableView = getTableView(); + if (getIndex() == -1 || tableView == null) return; + + TableSelectionModel sm = tableView.getSelectionModel(); + if (sm == null) return; + + boolean _isSelected = isSelected == null ? + isInCellSelectionMode() && sm.isSelected(getIndex(), getTableColumn()) : + isSelected; + if (isSelected() == _isSelected) return; + + updateSelected(_isSelected); } private void updateFocus() { - if (getIndex() == -1 || getTableView() == null) return; - if (getTableView().getFocusModel() == null) return; + final TableView tableView = getTableView(); + if (getIndex() == -1 || tableView == null) return; + + final TableViewFocusModel fm = tableView.getFocusModel(); + if (fm == null) return; boolean isFocused = isInCellSelectionMode() && - getTableView().getFocusModel() != null && - getTableView().getFocusModel().isFocused(getIndex(), getTableColumn()); + fm != null && + fm.isFocused(getIndex(), getTableColumn()); setFocused(isFocused); } @@ -510,9 +579,9 @@ } private boolean isInCellSelectionMode() { - return getTableView() != null && - getTableView().getSelectionModel() != null && - getTableView().getSelectionModel().isCellSelectionEnabled(); + TableView tableView = getTableView(); + TableSelectionModel sm = tableView.getSelectionModel(); + return tableView != null && sm != null && sm.isCellSelectionEnabled(); } /* diff --git a/modules/controls/src/main/java/javafx/scene/control/TableSelectionModel.java b/modules/controls/src/main/java/javafx/scene/control/TableSelectionModel.java --- a/modules/controls/src/main/java/javafx/scene/control/TableSelectionModel.java +++ b/modules/controls/src/main/java/javafx/scene/control/TableSelectionModel.java @@ -99,4 +99,12 @@ public final boolean isCellSelectionEnabled() { return cellSelectionEnabled == null ? false : cellSelectionEnabled.get(); } + + public void selectRange(int minRow, int minColumn, int maxRow, int maxColumn) { +// for (int _row = minRow; _row <= maxRow; _row++) { +// for (int _col = minColumn; _col <= maxColumn; _col++) { +// sm.select(_row, getVisibleLeafColumn(_col)); +// } +// } + } } diff --git a/modules/controls/src/main/java/javafx/scene/control/TableView.java b/modules/controls/src/main/java/javafx/scene/control/TableView.java --- a/modules/controls/src/main/java/javafx/scene/control/TableView.java +++ b/modules/controls/src/main/java/javafx/scene/control/TableView.java @@ -26,13 +26,7 @@ package javafx.scene.control; import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import com.sun.javafx.scene.control.Logging; import javafx.beans.DefaultProperty; @@ -1929,6 +1923,13 @@ return (ObservableList)(Object)selectedCellsSeq; } + private final Map selectedCellBitSetMap = new TreeMap<>(new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o1.compareTo(o2); + } + }); + /*********************************************************************** * * @@ -2121,9 +2122,9 @@ quietClearSelection(); } - if (! selectedCells.contains(pos)) { +// if (! selectedCells.contains(pos)) { selectedCells.add(pos); - } +// } updateSelectedIndex(row); focus(row, column); @@ -2284,6 +2285,70 @@ } } + public void selectRange(int minRow, int minColumn, int maxRow, int maxColumn) { + makeAtomic = true; + + final int itemCount = getItemCount(); + final boolean isCellSelectionEnabled = isCellSelectionEnabled(); + + for (int _row = minRow; _row <= maxRow; _row++) { + for (int _col = minColumn; _col <= maxColumn; _col++) { +// select(_row, tableView.getVisibleLeafColumn(_col)); + + // begin copy/paste of select(int, column) method + if (_row < 0 || _row >= itemCount) return; + + final TableColumn column = tableView.getVisibleLeafColumn(_col); + + // if I'm in cell selection mode but the column is null, I don't want + // to select the whole row instead... + if (column == null && isCellSelectionEnabled) return; + + TablePosition pos = new TablePosition<>(tableView, _row, column); + + if (getSelectionMode() == SelectionMode.SINGLE) { + quietClearSelection(); + } + +// if (! selectedCells.contains(pos)) { + selectedCells.add(pos); +// } + +// updateSelectedIndex(_row); +// focus(_row, column); + // end copy/paste + } + } + makeAtomic = false; + + // refresh the map + selectedCellBitSetMap.clear(); + final int columnCount = tableView.getVisibleLeafColumns().size(); + for (int i = 0, max = selectedCells.size(); i < max; i++) { + final TablePosition tp = selectedCells.get(i); + final int row = tp.getRow(); + final int columnIndex = tp.getColumn(); + + BitSet bitset; + if (! selectedCellBitSetMap.containsKey(row)) { + bitset = new BitSet(columnCount); + selectedCellBitSetMap.put(row, bitset); + } else { + bitset = selectedCellBitSetMap.get(row); + } + + bitset.set(columnIndex); + } + + // fire off events + updateSelectedIndex(maxRow); + focus(maxRow, tableView.getVisibleLeafColumn(maxColumn)); + + final int startChangeIndex = selectedCells.indexOf(new TablePosition(tableView, minRow, tableView.getVisibleLeafColumn(minColumn))); + final int endChangeIndex = selectedCells.indexOf(new TablePosition(tableView, maxRow, tableView.getVisibleLeafColumn(maxColumn))); + handleSelectedCellsListChangeEvent(new NonIterableChange.SimpleAddChange<>(startChangeIndex, endChangeIndex + 1, selectedCellsSeq)); + } + @Override public void clearSelection(int index) { clearSelection(index, null); } @@ -2328,18 +2393,27 @@ // When in cell selection mode, we currently do NOT support selecting // entire rows, so a isSelected(row, null) // should always return false. - if (isCellSelectionEnabled() && (column == null)) return false; - - for (TablePosition tp : getSelectedCells()) { - boolean columnMatch = ! isCellSelectionEnabled() || - (column == null && tp.getTableColumn() == null) || - (column != null && column.equals(tp.getTableColumn())); - - if (tp.getRow() == row && columnMatch) { - return true; - } - } - return false; + final boolean isCellSelectionEnabled = isCellSelectionEnabled(); + if (isCellSelectionEnabled && column == null) return false; + + int columnIndex = tableView.getVisibleLeafIndex(column); + return selectedCellBitSetMap.containsKey(row) ? selectedCellBitSetMap.get(row).get(columnIndex) : false; + +// // FIXME Need a quicker way to look up selection rather than +// // iterating through the selected cells list. +// for (int i = 0, max = selectedCells.size(); i < max; i++) { +// final TablePosition tp = selectedCells.get(i); +// final TableColumn tc = tp.getTableColumn(); +// +// final boolean columnMatch = ! isCellSelectionEnabled || +// (column == null && tc == null) || +// (column != null && column.equals(tc)); +// +// if (tp.getRow() == row && columnMatch) { +// return true; +// } +// } +// return false; } @Override public boolean isEmpty() {