/*
* Copyright (c) 2010, 2017, 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 javafx.css.PseudoClass;
import javafx.scene.control.skin.TreeTableCellSkin;
import javafx.beans.InvalidationListener;
import javafx.beans.WeakInvalidationListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.event.Event;
import javafx.collections.WeakListChangeListener;
import java.lang.ref.WeakReference;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.scene.AccessibleAction;
import javafx.scene.AccessibleAttribute;
import javafx.scene.AccessibleRole;
import javafx.scene.control.TreeTableColumn.CellEditEvent;
import javafx.scene.control.TreeTableView.TreeTableViewFocusModel;
Represents a single row/column intersection in a TreeTableView
. To represent this intersection, a TreeTableCell contains an index
property, as well as a tableColumn
property. In addition, a TreeTableCell instance knows what TreeTableRow
it exists in. A note about selection: A TreeTableCell visually shows it is
selected when two conditions are met:
- The
TableSelectionModel.isSelected(int, TableColumnBase)
method returns true for the row / column that this cell represents, and
- The
cell selection mode
property is set to true (to represent that it is allowable to select individual cells (and not just rows of cells)).
Type parameters: See Also: Since: JavaFX 8.0
/**
* Represents a single row/column intersection in a {@link TreeTableView}. To
* represent this intersection, a TreeTableCell contains an
* {@link #indexProperty() index} property, as well as a
* {@link #tableColumnProperty() tableColumn} property. In addition, a TreeTableCell
* instance knows what {@link TreeTableRow} it exists in.
*
* <p><strong>A note about selection:</strong> A TreeTableCell visually shows it is
* selected when two conditions are met:
* <ol>
* <li>The {@link TableSelectionModel#isSelected(int, TableColumnBase)} method
* returns true for the row / column that this cell represents, and</li>
* <li>The {@link javafx.scene.control.TableSelectionModel#cellSelectionEnabledProperty() cell selection mode}
* property is set to true (to represent that it is allowable to select
* individual cells (and not just rows of cells)).</li>
* </ol>
*
* @param <S> The type of the TreeTableView generic type
* @see TreeTableView
* @see TreeTableColumn
* @see Cell
* @see IndexedCell
* @see TreeTableRow
* @param <T> The type of the item contained within the Cell.
* @since JavaFX 8.0
*/
public class TreeTableCell<S,T> extends IndexedCell<T> {
/***************************************************************************
* *
* Constructors *
* *
**************************************************************************/
Constructs a default TreeTableCell instance with a style class of
'tree-table-cell'.
/**
* Constructs a default TreeTableCell instance with a style class of
* 'tree-table-cell'.
*/
public TreeTableCell() {
getStyleClass().addAll(DEFAULT_STYLE_CLASS);
setAccessibleRole(AccessibleRole.TREE_TABLE_CELL);
updateColumnIndex();
}
*
Private fields *
*
/***************************************************************************
* *
* Private fields *
* *
**************************************************************************/
// package for testing
boolean lockItemOnEdit = false;
*
Callbacks and Events *
*
/***************************************************************************
* *
* Callbacks and Events *
* *
**************************************************************************/
private boolean itemDirty = false;
/*
* This is the list observer we use to keep an eye on the SelectedCells
* ObservableList in the tree table view. Because it is possible that the table can
* be mutated, we create this observer here, and add/remove it from the
* storeTableView method.
*/
private ListChangeListener<TreeTablePosition<S,?>> selectedListener = c -> {
while (c.next()) {
if (c.wasAdded() || c.wasRemoved()) {
updateSelection();
}
}
};
// same as above, but for focus
private final InvalidationListener focusedListener = value -> {
updateFocus();
};
// same as above, but for for changes to the properties on TableRow
private final InvalidationListener tableRowUpdateObserver = value -> {
itemDirty = true;
requestLayout();
};
private final InvalidationListener editingListener = value -> {
updateEditing();
};
private ListChangeListener<TreeTableColumn<S,?>> visibleLeafColumnsListener = c -> {
updateColumnIndex();
};
private ListChangeListener<String> columnStyleClassListener = c -> {
while (c.next()) {
if (c.wasRemoved()) {
getStyleClass().removeAll(c.getRemoved());
}
if (c.wasAdded()) {
getStyleClass().addAll(c.getAddedSubList());
}
}
};
private final InvalidationListener rootPropertyListener = observable -> {
updateItem(-1);
};
private final InvalidationListener columnStyleListener = value -> {
if (getTableColumn() != null) {
possiblySetStyle(getTableColumn().getStyle());
}
};
private final InvalidationListener columnIdListener = value -> {
if (getTableColumn() != null) {
possiblySetId(getTableColumn().getId());
}
};
private final WeakListChangeListener<TreeTablePosition<S,?>> weakSelectedListener =
new WeakListChangeListener<TreeTablePosition<S,?>>(selectedListener);
private final WeakInvalidationListener weakFocusedListener =
new WeakInvalidationListener(focusedListener);
private final WeakInvalidationListener weaktableRowUpdateObserver =
new WeakInvalidationListener(tableRowUpdateObserver);
private final WeakInvalidationListener weakEditingListener =
new WeakInvalidationListener(editingListener);
private final WeakListChangeListener<TreeTableColumn<S,?>> weakVisibleLeafColumnsListener =
new WeakListChangeListener<TreeTableColumn<S,?>>(visibleLeafColumnsListener);
private final WeakListChangeListener<String> weakColumnStyleClassListener =
new WeakListChangeListener<String>(columnStyleClassListener);
private final WeakInvalidationListener weakColumnStyleListener =
new WeakInvalidationListener(columnStyleListener);
private final WeakInvalidationListener weakColumnIdListener =
new WeakInvalidationListener(columnIdListener);
private final WeakInvalidationListener weakRootPropertyListener =
new WeakInvalidationListener(rootPropertyListener);
/***************************************************************************
* *
* Properties *
* *
**************************************************************************/
// --- TableColumn
The TreeTableColumn instance that backs this TreeTableCell.
/**
* The TreeTableColumn instance that backs this TreeTableCell.
*/
private ReadOnlyObjectWrapper<TreeTableColumn<S,T>> treeTableColumn =
new ReadOnlyObjectWrapper<TreeTableColumn<S,T>>(this, "treeTableColumn") {
@Override protected void invalidated() {
updateColumnIndex();
}
};
public final ReadOnlyObjectProperty<TreeTableColumn<S,T>> tableColumnProperty() { return treeTableColumn.getReadOnlyProperty(); }
private void setTableColumn(TreeTableColumn<S,T> value) { treeTableColumn.set(value); }
public final TreeTableColumn<S,T> getTableColumn() { return treeTableColumn.get(); }
// --- TableView
The TreeTableView associated with this TreeTableCell.
/**
* The TreeTableView associated with this TreeTableCell.
*/
private ReadOnlyObjectWrapper<TreeTableView<S>> treeTableView;
private void setTreeTableView(TreeTableView<S> value) {
treeTableViewPropertyImpl().set(value);
}
public final TreeTableView<S> getTreeTableView() {
return treeTableView == null ? null : treeTableView.get();
}
public final ReadOnlyObjectProperty<TreeTableView<S>> treeTableViewProperty() {
return treeTableViewPropertyImpl().getReadOnlyProperty();
}
private ReadOnlyObjectWrapper<TreeTableView<S>> treeTableViewPropertyImpl() {
if (treeTableView == null) {
treeTableView = new ReadOnlyObjectWrapper<TreeTableView<S>>(this, "treeTableView") {
private WeakReference<TreeTableView<S>> weakTableViewRef;
@Override protected void invalidated() {
TreeTableView.TreeTableViewSelectionModel<S> sm;
TreeTableView.TreeTableViewFocusModel<S> fm;
if (weakTableViewRef != null) {
TreeTableView<S> oldTableView = weakTableViewRef.get();
if (oldTableView != null) {
sm = oldTableView.getSelectionModel();
if (sm != null) {
sm.getSelectedCells().removeListener(weakSelectedListener);
}
fm = oldTableView.getFocusModel();
if (fm != null) {
fm.focusedCellProperty().removeListener(weakFocusedListener);
}
oldTableView.editingCellProperty().removeListener(weakEditingListener);
oldTableView.getVisibleLeafColumns().removeListener(weakVisibleLeafColumnsListener);
oldTableView.rootProperty().removeListener(weakRootPropertyListener);
}
}
TreeTableView<S> newTreeTableView = get();
if (newTreeTableView != null) {
sm = newTreeTableView.getSelectionModel();
if (sm != null) {
sm.getSelectedCells().addListener(weakSelectedListener);
}
fm = newTreeTableView.getFocusModel();
if (fm != null) {
fm.focusedCellProperty().addListener(weakFocusedListener);
}
newTreeTableView.editingCellProperty().addListener(weakEditingListener);
newTreeTableView.getVisibleLeafColumns().addListener(weakVisibleLeafColumnsListener);
newTreeTableView.rootProperty().addListener(weakRootPropertyListener);
weakTableViewRef = new WeakReference<TreeTableView<S>>(newTreeTableView);
}
updateColumnIndex();
}
};
}
return treeTableView;
}
// --- TableRow
The TreeTableRow that this TreeTableCell currently finds itself placed within.
/**
* The TreeTableRow that this TreeTableCell currently finds itself placed within.
*/
private ReadOnlyObjectWrapper<TreeTableRow<S>> treeTableRow =
new ReadOnlyObjectWrapper<TreeTableRow<S>>(this, "treeTableRow");
private void setTreeTableRow(TreeTableRow<S> value) { treeTableRow.set(value); }
public final TreeTableRow<S> getTreeTableRow() { return treeTableRow.get(); }
public final ReadOnlyObjectProperty<TreeTableRow<S>> tableRowProperty() { return treeTableRow; }
/***************************************************************************
* *
* Editing API *
* *
**************************************************************************/
{@inheritDoc} /** {@inheritDoc} */
@Override public void startEdit() {
if (isEditing()) return;
final TreeTableView<S> table = getTreeTableView();
final TreeTableColumn<S,T> column = getTableColumn();
if (! isEditable() ||
(table != null && ! table.isEditable()) ||
(column != null && ! getTableColumn().isEditable())) {
return;
}
// We check the boolean lockItemOnEdit field here, as whilst we want to
// updateItem normally, when it comes to unit tests we can't have the
// item change in all circumstances.
if (! lockItemOnEdit) {
updateItem(-1);
}
// it makes sense to get the cell into its editing state before firing
// the event to listeners below, so that's what we're doing here
// by calling super.startEdit().
super.startEdit();
if (column != null) {
CellEditEvent editEvent = new CellEditEvent(
table,
table.getEditingCell(),
TreeTableColumn.<S,T>editStartEvent(),
null
);
Event.fireEvent(column, editEvent);
}
}
{@inheritDoc} /** {@inheritDoc} */
@Override public void commitEdit(T newValue) {
if (! isEditing()) return;
final TreeTableView<S> table = getTreeTableView();
if (table != null) {
@SuppressWarnings("unchecked")
TreeTablePosition<S,T> editingCell = (TreeTablePosition<S,T>) table.getEditingCell();
// Inform the TableView of the edit being ready to be committed.
CellEditEvent<S,T> editEvent = new CellEditEvent<S,T>(
table,
editingCell,
TreeTableColumn.<S,T>editCommitEvent(),
newValue
);
Event.fireEvent(getTableColumn(), editEvent);
}
// inform parent classes of the commit, so that they can switch us
// out of the editing state.
// This MUST come before the updateItem call below, otherwise it will
// call cancelEdit(), resulting in both commit and cancel events being
// fired (as identified in RT-29650)
super.commitEdit(newValue);
// update the item within this cell, so that it represents the new value
updateItem(newValue, false);
if (table != null) {
// reset the editing cell on the TableView
table.edit(-1, null);
// request focus back onto the table, only if the current focus
// owner has the table as a parent (otherwise the user might have
// clicked out of the table entirely and given focus to something else.
// It would be rude of us to request it back again.
ControlUtils.requestFocusOnControlOnlyIfCurrentFocusOwnerIsChild(table);
}
}
{@inheritDoc} /** {@inheritDoc} */
@Override public void cancelEdit() {
if (! isEditing()) return;
final TreeTableView<S> table = getTreeTableView();
super.cancelEdit();
// reset the editing index on the TableView
if (table != null) {
@SuppressWarnings("unchecked")
TreeTablePosition<S,T> editingCell = (TreeTablePosition<S,T>) table.getEditingCell();
if (updateEditingIndex) table.edit(-1, null);
// request focus back onto the table, only if the current focus
// owner has the table as a parent (otherwise the user might have
// clicked out of the table entirely and given focus to something else.
// It would be rude of us to request it back again.
ControlUtils.requestFocusOnControlOnlyIfCurrentFocusOwnerIsChild(table);
CellEditEvent<S,T> editEvent = new CellEditEvent<S,T>(
table,
editingCell,
TreeTableColumn.<S,T>editCancelEvent(),
null
);
Event.fireEvent(getTableColumn(), editEvent);
}
}
/* *************************************************************************
* *
* Overriding methods *
* *
**************************************************************************/
{@inheritDoc} /** {@inheritDoc} */
@Override public void updateSelected(boolean selected) {
// copied from Cell, with the first conditional clause below commented
// out, as it is valid for an empty TableCell to be selected, as long
// as the parent TableRow is not empty (see RT-15529).
/*if (selected && isEmpty()) return;*/
if (getTreeTableRow() == null || getTreeTableRow().isEmpty()) return;
setSelected(selected);
}
/* *************************************************************************
* *
* Private Implementation *
* *
**************************************************************************/
{@inheritDoc} /** {@inheritDoc} */
@Override void indexChanged(int oldIndex, int newIndex) {
super.indexChanged(oldIndex, newIndex);
if (isEditing() && newIndex == oldIndex) {
// no-op
// Fix for RT-31165 - if we (needlessly) update the index whilst the
// cell is being edited it will no longer be in an editing state.
// This means that in certain (common) circumstances that it will
// appear that a cell is uneditable as, despite being clicked, it
// will not change to the editing state as a layout of VirtualFlow
// is immediately invoked, which forces all cells to be updated.
} else {
// Ideally we would just use the following two lines of code, rather
// than the updateItem() call beneath, but if we do this we end up with
// RT-22428 where all the columns are collapsed.
// itemDirty = true;
// requestLayout();
updateItem(oldIndex);
updateSelection();
updateFocus();
updateEditing();
}
}
private boolean isLastVisibleColumn = false;
private int columnIndex = -1;
private void updateColumnIndex() {
final TreeTableView<S> tv = getTreeTableView();
TreeTableColumn<S,T> tc = getTableColumn();
columnIndex = tv == null || tc == null ? -1 : tv.getVisibleLeafIndex(tc);
// update the pseudo class state regarding whether this is the last
// visible cell (i.e. the right-most).
isLastVisibleColumn = getTableColumn() != null &&
columnIndex != -1 &&
columnIndex == tv.getVisibleLeafColumns().size() - 1;
pseudoClassStateChanged(PSEUDO_CLASS_LAST_VISIBLE, isLastVisibleColumn);
}
private void updateSelection() {
/*
* 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
* is selected.
*
* If the selection mode is not cell-based, then the listener in the
* TableRow class might pick up the need to set an entire row to be
* selected.
*/
if (isEmpty()) return;
final boolean isSelected = isSelected();
if (! isInCellSelectionMode()) {
if (isSelected) {
updateSelected(false);
}
return;
}
final TreeTableView<S> tv = getTreeTableView();
if (getIndex() == -1 || tv == null) return;
TreeTableView.TreeTableViewSelectionModel<S> sm = tv.getSelectionModel();
if (sm == null) {
updateSelected(false);
return;
}
boolean isSelectedNow = sm.isSelected(getIndex(), getTableColumn());
if (isSelected == isSelectedNow) return;
updateSelected(isSelectedNow);
}
private void updateFocus() {
final boolean isFocused = isFocused();
if (! isInCellSelectionMode()) {
if (isFocused) {
setFocused(false);
}
return;
}
final TreeTableView<S> tv = getTreeTableView();
if (getIndex() == -1 || tv == null) return;
TreeTableView.TreeTableViewFocusModel<S> fm = tv.getFocusModel();
if (fm == null) {
setFocused(false);
return;
}
setFocused(fm.isFocused(getIndex(), getTableColumn()));
}
private void updateEditing() {
final TreeTableView<S> tv = getTreeTableView();
if (getIndex() == -1 || tv == null) return;
TreeTablePosition<S,?> editCell = tv.getEditingCell();
boolean match = match(editCell);
if (match && ! isEditing()) {
startEdit();
} else if (! match && isEditing()) {
// If my index is not the one being edited then I need to cancel
// the edit. The tricky thing here is that as part of this call
// I cannot end up calling list.edit(-1) the way that the standard
// cancelEdit method would do. Yet, I need to call cancelEdit
// so that subclasses which override cancelEdit can execute. So,
// I have to use a kind of hacky flag workaround.
updateEditingIndex = false;
cancelEdit();
updateEditingIndex = true;
}
}
private boolean updateEditingIndex = true;
private boolean match(TreeTablePosition pos) {
return pos != null && pos.getRow() == getIndex() && pos.getTableColumn() == getTableColumn();
}
private boolean isInCellSelectionMode() {
TreeTableView<S> tv = getTreeTableView();
if (tv == null) return false;
TreeTableView.TreeTableViewSelectionModel<S> sm = tv.getSelectionModel();
return sm != null && sm.isCellSelectionEnabled();
}
/*
* This was brought in to fix the issue in RT-22077, namely that the
* ObservableValue was being GC'd, meaning that changes to the value were
* no longer being delivered. By extracting this value out of the method,
* it is now referred to from TableCell and will therefore no longer be
* GC'd.
*/
private ObservableValue<T> currentObservableValue = null;
private boolean isFirstRun = true;
private WeakReference<S> oldRowItemRef;
/*
* This is called when we think that the data within this TreeTableCell may have
* changed. You'll note that this is a private function - it is only called
* when one of the triggers above call it.
*/
private void updateItem(int oldIndex) {
if (currentObservableValue != null) {
currentObservableValue.removeListener(weaktableRowUpdateObserver);
}
// get the total number of items in the data model
final TreeTableView<S> tableView = getTreeTableView();
final TreeTableColumn<S,T> tableColumn = getTableColumn();
final int itemCount = tableView == null ? -1 : getTreeTableView().getExpandedItemCount();
final int index = getIndex();
final boolean isEmpty = isEmpty();
final T oldValue = getItem();
final TreeTableRow<S> tableRow = getTreeTableRow();
final S rowItem = tableRow == null ? null : tableRow.getItem();
final boolean indexExceedsItemCount = index >= itemCount;
// there is a whole heap of reasons why we should just punt...
outer: if (indexExceedsItemCount ||
index < 0 ||
columnIndex < 0 ||
!isVisible() ||
tableColumn == null ||
!tableColumn.isVisible() ||
tableView.getRoot() == null) {
// RT-30484 We need to allow a first run to be special-cased to allow
// for the updateItem method to be called at least once to allow for
// the correct visual state to be set up. In particular, in RT-30484
// refer to Ensemble8PopUpTree.png - in this case the arrows are being
// shown as the new cells are instantiated with the arrows in the
// children list, and are only hidden in updateItem.
// RT-32621: There are circumstances where we need to updateItem,
// even when the index is greater than the itemCount. For example,
// RT-32621 identifies issues where a TreeTableView collapses a
// TreeItem but the custom cells remain visible. This is now
// resolved with the check for indexExceedsItemCount.
if ((!isEmpty && oldValue != null) || isFirstRun || indexExceedsItemCount) {
updateItem(null, true);
isFirstRun = false;
}
return;
} else {
currentObservableValue = tableColumn.getCellObservableValue(index);
final T newValue = currentObservableValue == null ? null : currentObservableValue.getValue();
// RT-35864 - if the index didn't change, then avoid calling updateItem
// unless the item has changed.
if (oldIndex == index) {
if (!isItemChanged(oldValue, newValue)) {
// RT-36670: we need to check the row item here to prevent
// the issue where the cell value and index doesn't change,
// but the backing row object does.
S oldRowItem = oldRowItemRef != null ? oldRowItemRef.get() : null;
if (oldRowItem != null && oldRowItem.equals(rowItem)) {
// RT-37054: we break out of the if/else code here and
// proceed with the code following this, so that we may
// still update references, listeners, etc as required.
break outer;
}
}
}
updateItem(newValue, false);
}
oldRowItemRef = new WeakReference<>(rowItem);
if (currentObservableValue == null) {
return;
}
// add property change listeners to this item
currentObservableValue.addListener(weaktableRowUpdateObserver);
}
@Override protected void layoutChildren() {
if (itemDirty) {
updateItem(-1);
itemDirty = false;
}
super.layoutChildren();
}
/***************************************************************************
* *
* Expert API *
* *
**************************************************************************/
Updates the TreeTableView associated with this TreeTableCell. This is typically
only done once when the TreeTableCell is first added to the TreeTableView.
Note: This function is intended to be used by experts, primarily
by those implementing new Skins. It is not common
for developers or designers to access this function directly.
Params: - tv – the TreeTableView associated with this TreeTableCell
/**
* Updates the TreeTableView associated with this TreeTableCell. This is typically
* only done once when the TreeTableCell is first added to the TreeTableView.
*
* Note: This function is intended to be used by experts, primarily
* by those implementing new Skins. It is not common
* for developers or designers to access this function directly.
* @param tv the TreeTableView associated with this TreeTableCell
*/
public final void updateTreeTableView(TreeTableView<S> tv) {
setTreeTableView(tv);
}
Updates the TreeTableRow associated with this TreeTableCell.
Note: This function is intended to be used by experts, primarily
by those implementing new Skins. It is not common
for developers or designers to access this function directly.
Params: - treeTableRow – the TreeTableRow associated with this TreeTableCell
/**
* Updates the TreeTableRow associated with this TreeTableCell.
*
* Note: This function is intended to be used by experts, primarily
* by those implementing new Skins. It is not common
* for developers or designers to access this function directly.
* @param treeTableRow the TreeTableRow associated with this TreeTableCell
*/
public final void updateTreeTableRow(TreeTableRow<S> treeTableRow) {
this.setTreeTableRow(treeTableRow);
}
Updates the TreeTableColumn associated with this TreeTableCell.
Note: This function is intended to be used by experts, primarily
by those implementing new Skins. It is not common
for developers or designers to access this function directly.
Params: - col – the TreeTableColumn associated with this TreeTableCell
/**
* Updates the TreeTableColumn associated with this TreeTableCell.
*
* Note: This function is intended to be used by experts, primarily
* by those implementing new Skins. It is not common
* for developers or designers to access this function directly.
* @param col the TreeTableColumn associated with this TreeTableCell
*/
public final void updateTreeTableColumn(TreeTableColumn<S,T> col) {
// remove style class of existing tree table column, if it is non-null
TreeTableColumn<S,T> oldCol = getTableColumn();
if (oldCol != null) {
oldCol.getStyleClass().removeListener(weakColumnStyleClassListener);
getStyleClass().removeAll(oldCol.getStyleClass());
oldCol.idProperty().removeListener(weakColumnIdListener);
oldCol.styleProperty().removeListener(weakColumnStyleListener);
String id = getId();
String style = getStyle();
if (id != null && id.equals(oldCol.getId())) {
setId(null);
}
if (style != null && style.equals(oldCol.getStyle())) {
setStyle("");
}
}
setTableColumn(col);
if (col != null) {
getStyleClass().addAll(col.getStyleClass());
col.getStyleClass().addListener(weakColumnStyleClassListener);
col.idProperty().addListener(weakColumnIdListener);
col.styleProperty().addListener(weakColumnStyleListener);
possiblySetId(col.getId());
possiblySetStyle(col.getStyle());
}
}
*
Stylesheet Handling *
*
/***************************************************************************
* *
* Stylesheet Handling *
* *
**************************************************************************/
private static final String DEFAULT_STYLE_CLASS = "tree-table-cell";
private static final PseudoClass PSEUDO_CLASS_LAST_VISIBLE =
PseudoClass.getPseudoClass("last-visible");
{@inheritDoc} /** {@inheritDoc} */
@Override protected Skin<?> createDefaultSkin() {
return new TreeTableCellSkin<S,T>(this);
}
private void possiblySetId(String idCandidate) {
if (getId() == null || getId().isEmpty()) {
setId(idCandidate);
}
}
private void possiblySetStyle(String styleCandidate) {
if (getStyle() == null || getStyle().isEmpty()) {
setStyle(styleCandidate);
}
}
/***************************************************************************
* *
* Accessibility handling *
* *
**************************************************************************/
{@inheritDoc} /** {@inheritDoc} */
@Override
public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) {
switch (attribute) {
case ROW_INDEX: return getIndex();
case COLUMN_INDEX: return columnIndex;
case SELECTED: return isInCellSelectionMode() ? isSelected() : getTreeTableRow().isSelected();
default: return super.queryAccessibleAttribute(attribute, parameters);
}
}
{@inheritDoc} /** {@inheritDoc} */
@Override
public void executeAccessibleAction(AccessibleAction action, Object... parameters) {
switch (action) {
case REQUEST_FOCUS: {
TreeTableView<S> treeTableView = getTreeTableView();
if (treeTableView != null) {
TreeTableViewFocusModel<S> fm = treeTableView.getFocusModel();
if (fm != null) {
fm.focus(getIndex(), getTableColumn());
}
}
break;
}
default: super.executeAccessibleAction(action, parameters);
}
}
}