All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javafx.scene.control.skin.TableSkinUtils Maven / Gradle / Ivy

/*
 * Copyright (c) 2016, 2018, 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.skin;

import com.sun.javafx.scene.control.Properties;
import com.sun.javafx.scene.control.TableColumnBaseHelper;
import com.sun.javafx.scene.control.TreeTableViewBackingList;
import com.sun.javafx.scene.control.skin.Utils;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.control.Control;
import javafx.scene.control.IndexedCell;
import javafx.scene.control.ResizeFeaturesBase;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumnBase;
import javafx.scene.control.TableFocusModel;
import javafx.scene.control.TablePositionBase;
import javafx.scene.control.TableSelectionModel;
import javafx.scene.control.TableView;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableRow;
import javafx.scene.control.TreeTableView;
import javafx.scene.layout.Region;
import javafx.util.Callback;

import java.util.List;
import java.util.Optional;

// NOT PUBLIC API
class TableSkinUtils {

    private TableSkinUtils() { }

    public static boolean resizeColumn(TableViewSkinBase tableSkin, TableColumnBase tc, double delta) {
        if (!tc.isResizable()) return false;

        Object control = tableSkin.getSkinnable();
        if (control instanceof TableView) {
            return ((TableView)control).resizeColumn((TableColumn)tc, delta);
        } else if (control instanceof TreeTableView) {
            return ((TreeTableView)control).resizeColumn((TreeTableColumn)tc, delta);
        }
        return false;
    }

    /*
     * FIXME: Naive implementation ahead
     * Attempts to resize column based on the pref width of all items contained
     * in this column. This can be potentially very expensive if the number of
     * rows is large.
     */
    /** {@inheritDoc} */
    public static void resizeColumnToFitContent(TableViewSkinBase tableSkin, TableColumnBase tc, int maxRows) {
        if (!tc.isResizable()) return;

        Object control = tableSkin.getSkinnable();
        if (control instanceof TableView) {
            resizeColumnToFitContent((TableView)control, (TableColumn)tc, tableSkin, maxRows);
        } else if (control instanceof TreeTableView) {
            resizeColumnToFitContent((TreeTableView)control, (TreeTableColumn)tc, tableSkin, maxRows);
        }
    }

    private static  void resizeColumnToFitContent(TableView tv, TableColumn tc, TableViewSkinBase tableSkin, int maxRows) {
        List items = tv.getItems();
        if (items == null || items.isEmpty()) return;

        Callback/*, TableCell>*/ cellFactory = tc.getCellFactory();
        if (cellFactory == null) return;

        TableCell cell = (TableCell) cellFactory.call(tc);
        if (cell == null) return;

        // set this property to tell the TableCell we want to know its actual
        // preferred width, not the width of the associated TableColumnBase
        cell.getProperties().put(Properties.DEFER_TO_PARENT_PREF_WIDTH, Boolean.TRUE);

        // determine cell padding
        double padding = 10;
        Node n = cell.getSkin() == null ? null : cell.getSkin().getNode();
        if (n instanceof Region) {
            Region r = (Region) n;
            padding = r.snappedLeftInset() + r.snappedRightInset();
        }

        int rows = maxRows == -1 ? items.size() : Math.min(items.size(), maxRows);
        double maxWidth = 0;
        for (int row = 0; row < rows; row++) {
            cell.updateTableColumn(tc);
            cell.updateTableView(tv);
            cell.updateIndex(row);

            if ((cell.getText() != null && !cell.getText().isEmpty()) || cell.getGraphic() != null) {
                tableSkin.getChildren().add(cell);
                cell.applyCss();
                maxWidth = Math.max(maxWidth, cell.prefWidth(-1));
                tableSkin.getChildren().remove(cell);
            }
        }

        // dispose of the cell to prevent it retaining listeners (see RT-31015)
        cell.updateIndex(-1);

        // RT-36855 - take into account the column header text / graphic widths.
        // Magic 10 is to allow for sort arrow to appear without text truncation.
        TableColumnHeader header = tableSkin.getTableHeaderRow().getColumnHeaderFor(tc);
        double headerTextWidth = Utils.computeTextWidth(header.label.getFont(), tc.getText(), -1);
        Node graphic = header.label.getGraphic();
        double headerGraphicWidth = graphic == null ? 0 : graphic.prefWidth(-1) + header.label.getGraphicTextGap();
        double headerWidth = headerTextWidth + headerGraphicWidth + 10 + header.snappedLeftInset() + header.snappedRightInset();
        maxWidth = Math.max(maxWidth, headerWidth);

        // RT-23486
        maxWidth += padding;
        if (tv.getColumnResizePolicy() == TableView.CONSTRAINED_RESIZE_POLICY && tv.getWidth() > 0) {

            if (maxWidth > tc.getMaxWidth()) {
                maxWidth = tc.getMaxWidth();
            }

            int size = tc.getColumns().size();
            if (size > 0) {
                resizeColumnToFitContent(tableSkin, tc.getColumns().get(size - 1), maxRows);
                return;
            }

            resizeColumn(tableSkin, tc, Math.round(maxWidth - tc.getWidth()));
        } else {
            TableColumnBaseHelper.setWidth(tc, maxWidth);
        }
    }


    /*
     * FIXME: Naive implementation ahead
     * Attempts to resize column based on the pref width of all items contained
     * in this column. This can be potentially very expensive if the number of
     * rows is large.
     */
    private static  void resizeColumnToFitContent(TreeTableView ttv, TreeTableColumn tc, TableViewSkinBase tableSkin, int maxRows) {
        List items = new TreeTableViewBackingList(ttv);
        if (items == null || items.isEmpty()) return;

        Callback cellFactory = tc.getCellFactory();
        if (cellFactory == null) return;

        TreeTableCell cell = (TreeTableCell) cellFactory.call(tc);
        if (cell == null) return;

        // set this property to tell the TableCell we want to know its actual
        // preferred width, not the width of the associated TableColumnBase
        cell.getProperties().put(Properties.DEFER_TO_PARENT_PREF_WIDTH, Boolean.TRUE);

        // determine cell padding
        double padding = 10;
        Node n = cell.getSkin() == null ? null : cell.getSkin().getNode();
        if (n instanceof Region) {
            Region r = (Region) n;
            padding = r.snappedLeftInset() + r.snappedRightInset();
        }

        TreeTableRow treeTableRow = new TreeTableRow<>();
        treeTableRow.updateTreeTableView(ttv);

        int rows = maxRows == -1 ? items.size() : Math.min(items.size(), maxRows);
        double maxWidth = 0;
        for (int row = 0; row < rows; row++) {
            treeTableRow.updateIndex(row);
            treeTableRow.updateTreeItem(ttv.getTreeItem(row));

            cell.updateTreeTableColumn(tc);
            cell.updateTreeTableView(ttv);
            cell.updateTreeTableRow(treeTableRow);
            cell.updateIndex(row);

            if ((cell.getText() != null && !cell.getText().isEmpty()) || cell.getGraphic() != null) {
                tableSkin.getChildren().add(cell);
                cell.applyCss();

                double w = cell.prefWidth(-1);

                maxWidth = Math.max(maxWidth, w);
                tableSkin.getChildren().remove(cell);
            }
        }

        // dispose of the cell to prevent it retaining listeners (see RT-31015)
        cell.updateIndex(-1);

        // RT-36855 - take into account the column header text / graphic widths.
        // Magic 10 is to allow for sort arrow to appear without text truncation.
        TableColumnHeader header = tableSkin.getTableHeaderRow().getColumnHeaderFor(tc);
        double headerTextWidth = Utils.computeTextWidth(header.label.getFont(), tc.getText(), -1);
        Node graphic = header.label.getGraphic();
        double headerGraphicWidth = graphic == null ? 0 : graphic.prefWidth(-1) + header.label.getGraphicTextGap();
        double headerWidth = headerTextWidth + headerGraphicWidth + 10 + header.snappedLeftInset() + header.snappedRightInset();
        maxWidth = Math.max(maxWidth, headerWidth);

        // RT-23486
        maxWidth += padding;
        if (ttv.getColumnResizePolicy() == TreeTableView.CONSTRAINED_RESIZE_POLICY && ttv.getWidth() > 0) {

            if (maxWidth > tc.getMaxWidth()) {
                maxWidth = tc.getMaxWidth();
            }

            int size = tc.getColumns().size();
            if (size > 0) {
                resizeColumnToFitContent(tableSkin, tc.getColumns().get(size - 1), maxRows);
                return;
            }

            resizeColumn(tableSkin, tc, Math.round(maxWidth - tc.getWidth()));
        } else {
            TableColumnBaseHelper.setWidth(tc, maxWidth);
        }
    }

    public static ObjectProperty> columnResizePolicyProperty(TableViewSkinBase tableSkin) {
        Object control = tableSkin.getSkinnable();
        if (control instanceof TableView) {
            return ((TableView)control).columnResizePolicyProperty();
        } else if (control instanceof TreeTableView) {
            return ((TreeTableView)control).columnResizePolicyProperty();
        }
        return null;
    }

    public static BooleanProperty tableMenuButtonVisibleProperty(TableViewSkinBase tableSkin) {
        Object control = tableSkin.getSkinnable();
        if (control instanceof TableView) {
            return ((TableView)control).tableMenuButtonVisibleProperty();
        } else if (control instanceof TreeTableView) {
            return ((TreeTableView)control).tableMenuButtonVisibleProperty();
        }
        return null;
    }

    public static ObjectProperty placeholderProperty(TableViewSkinBase tableSkin) {
        Object control = tableSkin.getSkinnable();
        if (control instanceof TableView) {
            return ((TableView)control).placeholderProperty();
        } else if (control instanceof TreeTableView) {
            return ((TreeTableView)control).placeholderProperty();
        }
        return null;
    }

    public static > ObjectProperty> rowFactoryProperty(TableViewSkinBase tableSkin) {
        Object control = tableSkin.getSkinnable();
        if (control instanceof TableView) {
            return ((TableView)control).rowFactoryProperty();
        } else if (control instanceof TreeTableView) {
            return ((TreeTableView)control).rowFactoryProperty();
        }
        return null;
    }

    public static ObservableList> getSortOrder(TableViewSkinBase tableSkin) {
        Object control = tableSkin.getSkinnable();
        if (control instanceof TableView) {
            return ((TableView)control).getSortOrder();
        } else if (control instanceof TreeTableView) {
            return ((TreeTableView)control).getSortOrder();
        }
        return FXCollections.emptyObservableList();
    }

    public static ObservableList> getColumns(TableViewSkinBase tableSkin) {
        Object control = tableSkin.getSkinnable();
        if (control instanceof TableView) {
            return ((TableView)control).getColumns();
        } else if (control instanceof TreeTableView) {
            return ((TreeTableView)control).getColumns();
        }
        return FXCollections.emptyObservableList();
    }

    public static  TableSelectionModel getSelectionModel(TableViewSkinBase tableSkin) {
        Object control = tableSkin.getSkinnable();
        if (control instanceof TableView) {
            return ((TableView)control).getSelectionModel();
        } else if (control instanceof TreeTableView) {
            return ((TreeTableView)control).getSelectionModel();
        }
        return null;
    }

    public static  TableFocusModel getFocusModel(TableViewSkinBase tableSkin) {
        Object control = tableSkin.getSkinnable();
        if (control instanceof TableView) {
            return ((TableView)control).getFocusModel();
        } else if (control instanceof TreeTableView) {
            return ((TreeTableView)control).getFocusModel();
        }
        return null;
    }

    public static > TablePositionBase getFocusedCell(TableViewSkinBase tableSkin) {
        Object control = tableSkin.getSkinnable();
        if (control instanceof TableView) {
            return ((TableView)control).getFocusModel().getFocusedCell();
        } else if (control instanceof TreeTableView) {
            return ((TreeTableView)control).getFocusModel().getFocusedCell();
        }
        return null;
    }

    public static > ObservableList getVisibleLeafColumns(TableViewSkinBase tableSkin) {
        Object control = tableSkin.getSkinnable();
        if (control instanceof TableView) {
            return ((TableView)control).getVisibleLeafColumns();
        } else if (control instanceof TreeTableView) {
            return ((TreeTableView)control).getVisibleLeafColumns();
        }
        return FXCollections.emptyObservableList();
    }

    // returns the index of a column in the visible leaf columns
    public static int getVisibleLeafIndex(TableViewSkinBase tableSkin, TableColumnBase tc) {
        Object control = tableSkin.getSkinnable();
        if (control instanceof TableView) {
            return ((TableView)control).getVisibleLeafIndex((TableColumn)tc);
        } else if (control instanceof TreeTableView) {
            return ((TreeTableView)control).getVisibleLeafIndex((TreeTableColumn)tc);
        }
        return -1;
    }

    // returns the leaf column at the given index
    public static > TC getVisibleLeafColumn(TableViewSkinBase tableSkin, int col) {
        Object control = tableSkin.getSkinnable();
        if (control instanceof TableView) {
            return (TC) ((TableView)control).getVisibleLeafColumn(col);
        } else if (control instanceof TreeTableView) {
            return (TC) ((TreeTableView)control).getVisibleLeafColumn(col);
        }
        return null;
    }

    // returns a property representing the list of items in the control
    public static  ObjectProperty> itemsProperty(TableViewSkinBase tableSkin) {
        Object control = tableSkin.getSkinnable();
        if (control instanceof TableView) {
            return ((TableView)control).itemsProperty();
        } else if (control instanceof TreeTableView && tableSkin instanceof TreeTableViewSkin) {
            TreeTableViewSkin treeTableViewSkin = (TreeTableViewSkin)tableSkin;
            if (treeTableViewSkin.tableBackingListProperty == null) {
                treeTableViewSkin.tableBackingList = new TreeTableViewBackingList<>((TreeTableView)control);
                treeTableViewSkin.tableBackingListProperty = new SimpleObjectProperty<>(treeTableViewSkin.tableBackingList);
            }
            return treeTableViewSkin.tableBackingListProperty;
        }
        return null;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy