Edit C:\Program Files\Java\jdk1.8.0_121\com\sun\javafx\scene\control\skin\TableColumnHeader.java
/* * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * * * * * * * * * * * * * * * * * * * */ package com.sun.javafx.scene.control.skin; import javafx.beans.property.DoubleProperty; import javafx.beans.value.WritableValue; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.collections.WeakListChangeListener; 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.geometry.HPos; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.geometry.VPos; import javafx.scene.AccessibleAttribute; import javafx.scene.AccessibleRole; import javafx.scene.Node; import javafx.scene.control.ContextMenu; import javafx.scene.control.Label; import javafx.scene.control.TableColumn; import javafx.scene.control.TableColumnBase; import javafx.scene.input.ContextMenuEvent; import javafx.scene.input.MouseEvent; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.Region; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; import com.sun.javafx.css.converters.SizeConverter; import com.sun.javafx.scene.control.MultiplePropertyChangeListenerHandler; import static com.sun.javafx.scene.control.TableColumnSortTypeWrapper.getSortTypeName; import static com.sun.javafx.scene.control.TableColumnSortTypeWrapper.getSortTypeProperty; import static com.sun.javafx.scene.control.TableColumnSortTypeWrapper.isAscending; import static com.sun.javafx.scene.control.TableColumnSortTypeWrapper.isDescending; import static com.sun.javafx.scene.control.TableColumnSortTypeWrapper.setSortType; /** * Region responsible for painting a single column header. */ public class TableColumnHeader extends Region { /*************************************************************************** * * * Static Fields * * * **************************************************************************/ // Copied from TableColumn. The value here should always be in-sync with // the value in TableColumn static final double DEFAULT_COLUMN_WIDTH = 80.0F; /*************************************************************************** * * * Private Fields * * * **************************************************************************/ private boolean autoSizeComplete = false; private double dragOffset; private final TableViewSkinBase<?,?,?,?,?,TableColumnBase<?,?>> skin; private NestedTableColumnHeader nestedColumnHeader; private final TableColumnBase<?,?> column; private TableHeaderRow tableHeaderRow; private NestedTableColumnHeader parentHeader; // work out where this column currently is within its parent Label label; // sort order int sortPos = -1; private Region arrow; private Label sortOrderLabel; private HBox sortOrderDots; private Node sortArrow; private boolean isSortColumn; private boolean isSizeDirty = false; boolean isLastVisibleColumn = false; // package for testing int columnIndex = -1; private int newColumnPos; // the line drawn in the table when a user presses and moves a column header // to indicate where the column will be dropped. This is provided by the // table skin, but manipulated by the header protected final Region columnReorderLine; /*************************************************************************** * * * Constructor * * * **************************************************************************/ public TableColumnHeader(final TableViewSkinBase skin, final TableColumnBase tc) { this.skin = skin; this.column = tc; this.columnReorderLine = skin.getColumnReorderLine(); setFocusTraversable(false); updateColumnIndex(); initUI(); // change listener for multiple properties changeListenerHandler = new MultiplePropertyChangeListenerHandler(p -> { handlePropertyChanged(p); return null; }); changeListenerHandler.registerChangeListener(sceneProperty(), "SCENE"); if (column != null && skin != null) { updateSortPosition(); skin.getSortOrder().addListener(weakSortOrderListener); skin.getVisibleLeafColumns().addListener(weakVisibleLeafColumnsListener); } if (column != null) { changeListenerHandler.registerChangeListener(column.idProperty(), "TABLE_COLUMN_ID"); changeListenerHandler.registerChangeListener(column.styleProperty(), "TABLE_COLUMN_STYLE"); changeListenerHandler.registerChangeListener(column.widthProperty(), "TABLE_COLUMN_WIDTH"); changeListenerHandler.registerChangeListener(column.visibleProperty(), "TABLE_COLUMN_VISIBLE"); changeListenerHandler.registerChangeListener(column.sortNodeProperty(), "TABLE_COLUMN_SORT_NODE"); changeListenerHandler.registerChangeListener(column.sortableProperty(), "TABLE_COLUMN_SORTABLE"); changeListenerHandler.registerChangeListener(column.textProperty(), "TABLE_COLUMN_TEXT"); changeListenerHandler.registerChangeListener(column.graphicProperty(), "TABLE_COLUMN_GRAPHIC"); column.getStyleClass().addListener(weakStyleClassListener); setId(column.getId()); setStyle(column.getStyle()); updateStyleClass(); /* Having TableColumn role parented by TableColumn causes VoiceOver to be unhappy */ setAccessibleRole(AccessibleRole.TABLE_COLUMN); } } /*************************************************************************** * * * Listeners * * * **************************************************************************/ protected final MultiplePropertyChangeListenerHandler changeListenerHandler; private ListChangeListener<TableColumnBase<?,?>> sortOrderListener = c -> { updateSortPosition(); }; private ListChangeListener<TableColumnBase<?,?>> visibleLeafColumnsListener = c -> { updateColumnIndex(); updateSortPosition(); }; private ListChangeListener<String> styleClassListener = c -> { updateStyleClass(); }; private WeakListChangeListener<TableColumnBase<?,?>> weakSortOrderListener = new WeakListChangeListener<TableColumnBase<?,?>>(sortOrderListener); private final WeakListChangeListener<TableColumnBase<?,?>> weakVisibleLeafColumnsListener = new WeakListChangeListener<TableColumnBase<?,?>>(visibleLeafColumnsListener); private final WeakListChangeListener<String> weakStyleClassListener = new WeakListChangeListener<String>(styleClassListener); private static final EventHandler<MouseEvent> mousePressedHandler = me -> { TableColumnHeader header = (TableColumnHeader) me.getSource(); // pass focus to the table, so that the user immediately sees // the focus rectangle around the table control. header.getTableViewSkin().getSkinnable().requestFocus(); if (me.isPrimaryButtonDown() && header.isColumnReorderingEnabled()) { header.columnReorderingStarted(me.getX()); } me.consume(); }; private static final EventHandler<MouseEvent> mouseDraggedHandler = me -> { TableColumnHeader header = (TableColumnHeader) me.getSource(); if (me.isPrimaryButtonDown() && header.isColumnReorderingEnabled()) { header.columnReordering(me.getSceneX(), me.getSceneY()); } me.consume(); }; private static final EventHandler<MouseEvent> mouseReleasedHandler = me -> { if (me.isPopupTrigger()) return; TableColumnHeader header = (TableColumnHeader) me.getSource(); TableColumnBase tableColumn = header.getTableColumn(); ContextMenu menu = tableColumn.getContextMenu(); if (menu != null && menu.isShowing()) return; if (header.getTableHeaderRow().isReordering() && header.isColumnReorderingEnabled()) { header.columnReorderingComplete(); } else if (me.isStillSincePress()) { header.sortColumn(me.isShiftDown()); } me.consume(); }; private static final EventHandler<ContextMenuEvent> contextMenuRequestedHandler = me -> { TableColumnHeader header = (TableColumnHeader) me.getSource(); TableColumnBase tableColumn = header.getTableColumn(); ContextMenu menu = tableColumn.getContextMenu(); if (menu != null) { menu.show(header, me.getScreenX(), me.getScreenY()); me.consume(); } }; /*************************************************************************** * * * Properties * * * **************************************************************************/ private DoubleProperty size; private double getSize() { return size == null ? 20.0 : size.doubleValue(); } private DoubleProperty sizeProperty() { if (size == null) { size = new StyleableDoubleProperty(20) { @Override protected void invalidated() { double value = get(); if (value <= 0) { if (isBound()) { unbind(); } set(20); throw new IllegalArgumentException("Size cannot be 0 or negative"); } } @Override public Object getBean() { return TableColumnHeader.this; } @Override public String getName() { return "size"; } @Override public CssMetaData<TableColumnHeader,Number> getCssMetaData() { return StyleableProperties.SIZE; } }; } return size; } /*************************************************************************** * * * Public Methods * * * **************************************************************************/ protected void handlePropertyChanged(String p) { if ("SCENE".equals(p)) { updateScene(); } else if ("TABLE_COLUMN_VISIBLE".equals(p)) { setVisible(getTableColumn().isVisible()); } else if ("TABLE_COLUMN_WIDTH".equals(p)) { // It is this that ensures that when a column is resized that the header // visually adjusts its width as necessary. isSizeDirty = true; requestLayout(); } else if ("TABLE_COLUMN_ID".equals(p)) { setId(column.getId()); } else if ("TABLE_COLUMN_STYLE".equals(p)) { setStyle(column.getStyle()); } else if ("TABLE_COLUMN_SORT_TYPE".equals(p)) { updateSortGrid(); if (arrow != null) { arrow.setRotate(isAscending(column) ? 180 : 0.0); } } else if ("TABLE_COLUMN_SORT_NODE".equals(p)) { updateSortGrid(); } else if ("TABLE_COLUMN_SORTABLE".equals(p)) { // we need to notify all headers that a sortable state has changed, // in case the sort grid in other columns needs to be updated. if (skin.getSortOrder().contains(getTableColumn())) { NestedTableColumnHeader root = getTableHeaderRow().getRootHeader(); updateAllHeaders(root); } } else if ("TABLE_COLUMN_TEXT".equals(p)) { label.setText(column.getText()); } else if ("TABLE_COLUMN_GRAPHIC".equals(p)) { label.setGraphic(column.getGraphic()); } } protected TableViewSkinBase<?,?,?,?,?,TableColumnBase<?,?>> getTableViewSkin() { return skin; } NestedTableColumnHeader getNestedColumnHeader() { return nestedColumnHeader; } void setNestedColumnHeader(NestedTableColumnHeader nch) { nestedColumnHeader = nch; } public TableColumnBase getTableColumn() { return column; } TableHeaderRow getTableHeaderRow() { return tableHeaderRow; } void setTableHeaderRow(TableHeaderRow thr) { tableHeaderRow = thr; } NestedTableColumnHeader getParentHeader() { return parentHeader; } void setParentHeader(NestedTableColumnHeader ph) { parentHeader = ph; } /*************************************************************************** * * * Layout * * * **************************************************************************/ /** {@inheritDoc} */ @Override protected void layoutChildren() { if (isSizeDirty) { resize(getTableColumn().getWidth(), getHeight()); isSizeDirty = false; } double sortWidth = 0; double w = snapSize(getWidth()) - (snappedLeftInset() + snappedRightInset()); double h = getHeight() - (snappedTopInset() + snappedBottomInset()); double x = w; // a bit hacky, but we REALLY don't want the arrow shape to fluctuate // in size if (arrow != null) { arrow.setMaxSize(arrow.prefWidth(-1), arrow.prefHeight(-1)); } if (sortArrow != null && sortArrow.isVisible()) { sortWidth = sortArrow.prefWidth(-1); x -= sortWidth; sortArrow.resize(sortWidth, sortArrow.prefHeight(-1)); positionInArea(sortArrow, x, snappedTopInset(), sortWidth, h, 0, HPos.CENTER, VPos.CENTER); } if (label != null) { double labelWidth = w - sortWidth; label.resizeRelocate(snappedLeftInset(), 0, labelWidth, getHeight()); } } /** {@inheritDoc} */ @Override protected double computePrefWidth(double height) { if (getNestedColumnHeader() != null) { double width = getNestedColumnHeader().prefWidth(height); if (column != null) { column.impl_setWidth(width); } return width; } else if (column != null && column.isVisible()) { return column.getWidth(); } return 0; } /** {@inheritDoc} */ @Override protected double computeMinHeight(double width) { return label == null ? 0 : label.minHeight(width); } /** {@inheritDoc} */ @Override protected double computePrefHeight(double width) { if (getTableColumn() == null) return 0; return Math.max(getSize(), label.prefHeight(-1)); } /*************************************************************************** * * * Private Implementation * * * **************************************************************************/ // RT-29682: When the sortable property of a TableColumnBase changes this // may impact other TableColumnHeaders, as they may need to change their // sort order representation. Rather than install listeners across all // TableColumn in the sortOrder list for their sortable property, we simply // update the sortPosition of all headers whenever the sortOrder property // changes, assuming the column is within the sortOrder list. private void updateAllHeaders(TableColumnHeader header) { if (header instanceof NestedTableColumnHeader) { List<TableColumnHeader> children = ((NestedTableColumnHeader)header).getColumnHeaders(); for (int i = 0; i < children.size(); i++) { updateAllHeaders(children.get(i)); } } else { header.updateSortPosition(); } } private void updateStyleClass() { // For now we leave the 'column-header' style class intact so that the // appropriate border styles are shown, etc. getStyleClass().setAll("column-header"); getStyleClass().addAll(column.getStyleClass()); } private void updateScene() { // RT-17684: If the TableColumn widths are all currently the default, // we attempt to 'auto-size' based on the preferred width of the first // n rows (we can't do all rows, as that could conceivably be an unlimited // number of rows retrieved from a very slow (e.g. remote) data source. // Obviously, the bigger the value of n, the more likely the default // width will be suitable for most values in the column final int n = 30; if (! autoSizeComplete) { if (getTableColumn() == null || getTableColumn().getWidth() != DEFAULT_COLUMN_WIDTH || getScene() == null) { return; } doColumnAutoSize(getTableColumn(), n); autoSizeComplete = true; } } void dispose() { TableViewSkinBase skin = getTableViewSkin(); if (skin != null) { skin.getVisibleLeafColumns().removeListener(weakVisibleLeafColumnsListener); skin.getSortOrder().removeListener(weakSortOrderListener); } changeListenerHandler.dispose(); } private boolean isSortingEnabled() { // this used to check if ! PlatformUtil.isEmbedded(), but has been changed // to always return true (for now), as we want to support column sorting // everywhere return true; } private boolean isColumnReorderingEnabled() { // we only allow for column reordering if there are more than one column, return !BehaviorSkinBase.IS_TOUCH_SUPPORTED && getTableViewSkin().getVisibleLeafColumns().size() > 1; } private void initUI() { // TableColumn will be null if we are dealing with the root NestedTableColumnHeader if (column == null) return; // set up mouse events setOnMousePressed(mousePressedHandler); setOnMouseDragged(mouseDraggedHandler); setOnDragDetected(event -> event.consume()); setOnContextMenuRequested(contextMenuRequestedHandler); setOnMouseReleased(mouseReleasedHandler); // --- label label = new Label(); label.setText(column.getText()); label.setGraphic(column.getGraphic()); label.setVisible(column.isVisible()); // ---- container for the sort arrow (which is not supported on embedded // platforms) if (isSortingEnabled()) { // put together the grid updateSortGrid(); } } private void doColumnAutoSize(TableColumnBase<?,?> column, int cellsToMeasure) { double prefWidth = column.getPrefWidth(); // if the prefWidth has been set, we do _not_ autosize columns if (prefWidth == DEFAULT_COLUMN_WIDTH) { getTableViewSkin().resizeColumnToFitContent(column, cellsToMeasure); } } private void updateSortPosition() { this.sortPos = ! column.isSortable() ? -1 : getSortPosition(); updateSortGrid(); } private void updateSortGrid() { // Fix for RT-14488 if (this instanceof NestedTableColumnHeader) return; getChildren().clear(); getChildren().add(label); // we do not support sorting in embedded devices if (! isSortingEnabled()) return; isSortColumn = sortPos != -1; if (! isSortColumn) { if (sortArrow != null) { sortArrow.setVisible(false); } return; } // RT-28016: if the tablecolumn is not a visible leaf column, we should ignore this int visibleLeafIndex = skin.getVisibleLeafIndex(getTableColumn()); if (visibleLeafIndex == -1) return; final int sortColumnCount = getVisibleSortOrderColumnCount(); boolean showSortOrderDots = sortPos <= 3 && sortColumnCount > 1; Node _sortArrow = null; if (getTableColumn().getSortNode() != null) { _sortArrow = getTableColumn().getSortNode(); getChildren().add(_sortArrow); } else { GridPane sortArrowGrid = new GridPane(); _sortArrow = sortArrowGrid; sortArrowGrid.setPadding(new Insets(0, 3, 0, 0)); getChildren().add(sortArrowGrid); // if we are here, and the sort arrow is null, we better create it if (arrow == null) { arrow = new Region(); arrow.getStyleClass().setAll("arrow"); arrow.setVisible(true); arrow.setRotate(isAscending(column) ? 180.0F : 0.0F); changeListenerHandler.registerChangeListener(getSortTypeProperty(column), "TABLE_COLUMN_SORT_TYPE"); } arrow.setVisible(isSortColumn); if (sortPos > 2) { if (sortOrderLabel == null) { // ---- sort order label (for sort positions greater than 3) sortOrderLabel = new Label(); sortOrderLabel.getStyleClass().add("sort-order"); } // only show the label if the sortPos is greater than 3 (for sortPos // values less than three, we show the sortOrderDots instead) sortOrderLabel.setText("" + (sortPos + 1)); sortOrderLabel.setVisible(sortColumnCount > 1); // update the grid layout sortArrowGrid.add(arrow, 1, 1); GridPane.setHgrow(arrow, Priority.NEVER); GridPane.setVgrow(arrow, Priority.NEVER); sortArrowGrid.add(sortOrderLabel, 2, 1); } else if (showSortOrderDots) { if (sortOrderDots == null) { sortOrderDots = new HBox(0); sortOrderDots.getStyleClass().add("sort-order-dots-container"); } // show the sort order dots boolean isAscending = isAscending(column); int arrowRow = isAscending ? 1 : 2; int dotsRow = isAscending ? 2 : 1; sortArrowGrid.add(arrow, 1, arrowRow); GridPane.setHalignment(arrow, HPos.CENTER); sortArrowGrid.add(sortOrderDots, 1, dotsRow); updateSortOrderDots(sortPos); } else { // only show the arrow sortArrowGrid.add(arrow, 1, 1); GridPane.setHgrow(arrow, Priority.NEVER); GridPane.setVgrow(arrow, Priority.ALWAYS); } } sortArrow = _sortArrow; if (sortArrow != null) { sortArrow.setVisible(isSortColumn); } requestLayout(); } private void updateSortOrderDots(int sortPos) { double arrowWidth = arrow.prefWidth(-1); sortOrderDots.getChildren().clear(); for (int i = 0; i <= sortPos; i++) { Region r = new Region(); r.getStyleClass().add("sort-order-dot"); String sortTypeName = getSortTypeName(column); if (sortTypeName != null && ! sortTypeName.isEmpty()) { r.getStyleClass().add(sortTypeName.toLowerCase(Locale.ROOT)); } sortOrderDots.getChildren().add(r); // RT-34914: fine tuning the placement of the sort dots. We could have gone to a custom layout, but for now // this works fine. if (i < sortPos) { Region spacer = new Region(); double rp = sortPos == 1 ? 1 : 1; double lp = sortPos == 1 ? 1 : 0; spacer.setPadding(new Insets(0, rp, 0, lp)); sortOrderDots.getChildren().add(spacer); } } sortOrderDots.setAlignment(Pos.TOP_CENTER); sortOrderDots.setMaxWidth(arrowWidth); } // Package for testing purposes only. void moveColumn(TableColumnBase column, final int newColumnPos) { if (column == null || newColumnPos < 0) return; ObservableList<TableColumnBase<?,?>> columns = getColumns(column); final int columnsCount = columns.size(); final int currentPos = columns.indexOf(column); int actualNewColumnPos = newColumnPos; // Fix for RT-35141: We need to account for hidden columns final int max = actualNewColumnPos; for (int i = 0; i <= max && i < columnsCount; i++) { actualNewColumnPos += columns.get(i).isVisible() ? 0 : 1; } // --- end of RT-35141 fix if (actualNewColumnPos >= columnsCount) { actualNewColumnPos = columnsCount - 1; } else if (actualNewColumnPos < 0) { actualNewColumnPos = 0; } if (actualNewColumnPos == currentPos) return; List<TableColumnBase<?,?>> tempList = new ArrayList<>(columns); tempList.remove(column); tempList.add(actualNewColumnPos, column); columns.setAll(tempList); } private ObservableList<TableColumnBase<?,?>> getColumns(TableColumnBase column) { return column.getParentColumn() == null ? getTableViewSkin().getColumns() : column.getParentColumn().getColumns(); } private int getIndex(TableColumnBase<?,?> column) { if (column == null) return -1; ObservableList<? extends TableColumnBase<?,?>> columns = getColumns(column); int index = -1; for (int i = 0; i < columns.size(); i++) { TableColumnBase<?,?> _column = columns.get(i); if (! _column.isVisible()) continue; index++; if (column.equals(_column)) break; } return index; } private void updateColumnIndex() { // TableView tv = getTableView(); TableViewSkinBase skin = getTableViewSkin(); TableColumnBase tc = getTableColumn(); columnIndex = skin == null || tc == null ? -1 : skin.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 == getTableViewSkin().getVisibleLeafColumns().size() - 1; pseudoClassStateChanged(PSEUDO_CLASS_LAST_VISIBLE, isLastVisibleColumn); } private void sortColumn(final boolean addColumn) { if (! isSortingEnabled()) return; // we only allow sorting on the leaf columns and columns // that actually have comparators defined, and are sortable if (column == null || column.getColumns().size() != 0 || column.getComparator() == null || !column.isSortable()) return; // final int sortPos = getTable().getSortOrder().indexOf(column); // final boolean isSortColumn = sortPos != -1; final ObservableList<TableColumnBase<?,?>> sortOrder = getTableViewSkin().getSortOrder(); // addColumn is true e.g. when the user is holding down Shift if (addColumn) { if (!isSortColumn) { setSortType(column, TableColumn.SortType.ASCENDING); sortOrder.add(column); } else if (isAscending(column)) { setSortType(column, TableColumn.SortType.DESCENDING); } else { int i = sortOrder.indexOf(column); if (i != -1) { sortOrder.remove(i); } } } else { // the user has clicked on a column header - we should add this to // the TableView sortOrder list if it isn't already there. if (isSortColumn && sortOrder.size() == 1) { // the column is already being sorted, and it's the only column. // We therefore move through the 2nd or 3rd states: // 1st click: sort ascending // 2nd click: sort descending // 3rd click: natural sorting (sorting is switched off) if (isAscending(column)) { setSortType(column, TableColumn.SortType.DESCENDING); } else { // remove from sort sortOrder.remove(column); } } else if (isSortColumn) { // the column is already being used to sort, so we toggle its // sortAscending property, and also make the column become the // primary sort column if (isAscending(column)) { setSortType(column, TableColumn.SortType.DESCENDING); } else if (isDescending(column)) { setSortType(column, TableColumn.SortType.ASCENDING); } // to prevent multiple sorts, we make a copy of the sort order // list, moving the column value from the current position to // its new position at the front of the list List<TableColumnBase<?,?>> sortOrderCopy = new ArrayList<TableColumnBase<?,?>>(sortOrder); sortOrderCopy.remove(column); sortOrderCopy.add(0, column); sortOrder.setAll(column); } else { // add to the sort order, in ascending form setSortType(column, TableColumn.SortType.ASCENDING); sortOrder.setAll(column); } } } // Because it is possible that some columns are in the sortOrder list but are // not themselves sortable, we cannot just do sortOrderList.indexOf(column). // Therefore, this method does the proper work required of iterating through // and ignoring non-sortable (and null) columns in the sortOrder list. private int getSortPosition() { if (column == null) { return -1; } final List<TableColumnBase> sortOrder = getVisibleSortOrderColumns(); int pos = 0; for (int i = 0; i < sortOrder.size(); i++) { TableColumnBase _tc = sortOrder.get(i); if (column.equals(_tc)) { return pos; } pos++; } return -1; } private List<TableColumnBase> getVisibleSortOrderColumns() { final ObservableList<TableColumnBase<?,?>> sortOrder = getTableViewSkin().getSortOrder(); List<TableColumnBase> visibleSortOrderColumns = new ArrayList<>(); for (int i = 0; i < sortOrder.size(); i++) { TableColumnBase _tc = sortOrder.get(i); if (_tc == null || ! _tc.isSortable() || ! _tc.isVisible()) { continue; } visibleSortOrderColumns.add(_tc); } return visibleSortOrderColumns; } // as with getSortPosition above, this method iterates through the sortOrder // list ignoring the null and non-sortable columns, so that we get the correct // number of columns in the sortOrder list. private int getVisibleSortOrderColumnCount() { return getVisibleSortOrderColumns().size(); } /*************************************************************************** * * * Private Implementation: Column Reordering * * * **************************************************************************/ // package for testing void columnReorderingStarted(double dragOffset) { if (! column.impl_isReorderable()) return; // Used to ensure the column ghost is positioned relative to where the // user clicked on the column header this.dragOffset = dragOffset; // Note here that we only allow for reordering of 'root' columns getTableHeaderRow().setReorderingColumn(column); getTableHeaderRow().setReorderingRegion(this); } // package for testing void columnReordering(double sceneX, double sceneY) { if (! column.impl_isReorderable()) return; // this is for handling the column drag to reorder columns. // It shows a line to indicate where the 'drop' will be. // indicate that we've started dragging so that the dragging // line overlay is shown getTableHeaderRow().setReordering(true); // Firstly we need to determine where to draw the line. // Find which column we're over TableColumnHeader hoverHeader = null; // x represents where the mouse is relative to the parent // NestedTableColumnHeader final double x = getParentHeader().sceneToLocal(sceneX, sceneY).getX(); // calculate where the ghost column header should be double dragX = getTableViewSkin().getSkinnable().sceneToLocal(sceneX, sceneY).getX() - dragOffset; getTableHeaderRow().setDragHeaderX(dragX); double startX = 0; double endX = 0; double headersWidth = 0; newColumnPos = 0; for (TableColumnHeader header : getParentHeader().getColumnHeaders()) { if (! header.isVisible()) continue; double headerWidth = header.prefWidth(-1); headersWidth += headerWidth; startX = header.getBoundsInParent().getMinX(); endX = startX + headerWidth; if (x >= startX && x < endX) { hoverHeader = header; break; } newColumnPos++; } // hoverHeader will be null if the drag occurs outside of the // tableview. In this case we handle the newColumnPos specially // and then short-circuit. This results in the drop action // resulting in the correct result (the column will drop at // the start or end of the table). if (hoverHeader == null) { newColumnPos = x > headersWidth ? (getParentHeader().getColumns().size() - 1) : 0; return; } // This is the x-axis value midway through hoverHeader. It's // used to determine whether the drop should be to the left // or the right of hoverHeader. double midPoint = startX + (endX - startX) / 2; boolean beforeMidPoint = x <= midPoint; // Based on where the mouse actually is, we have to shuffle // where we want the column to end up. This code handles that. int currentPos = getIndex(column); newColumnPos += newColumnPos > currentPos && beforeMidPoint ? -1 : (newColumnPos < currentPos && !beforeMidPoint ? 1 : 0); double lineX = getTableHeaderRow().sceneToLocal(hoverHeader.localToScene(hoverHeader.getBoundsInLocal())).getMinX(); lineX = lineX + ((beforeMidPoint) ? (0) : (hoverHeader.getWidth())); if (lineX >= -0.5 && lineX <= getTableViewSkin().getSkinnable().getWidth()) { columnReorderLine.setTranslateX(lineX); // then if this is the first event, we set the property to true // so that the line becomes visible until the drop is completed. // We also set reordering to true so that the various reordering // effects become visible (ghost, transparent overlay, etc). columnReorderLine.setVisible(true); } getTableHeaderRow().setReordering(true); } // package for testing void columnReorderingComplete() { if (! column.impl_isReorderable()) return; // Move col from where it is now to the new position. moveColumn(getTableColumn(), newColumnPos); // cleanup columnReorderLine.setTranslateX(0.0F); columnReorderLine.setLayoutX(0.0F); newColumnPos = 0; getTableHeaderRow().setReordering(false); columnReorderLine.setVisible(false); getTableHeaderRow().setReorderingColumn(null); getTableHeaderRow().setReorderingRegion(null); dragOffset = 0.0F; } /*************************************************************************** * * * Stylesheet Handling * * * **************************************************************************/ private static final PseudoClass PSEUDO_CLASS_LAST_VISIBLE = PseudoClass.getPseudoClass("last-visible"); double getDragRectHeight() { return getHeight(); } /** * Super-lazy instantiation pattern from Bill Pugh. * @treatAsPrivate implementation detail */ private static class StyleableProperties { private static final CssMetaData<TableColumnHeader,Number> SIZE = new CssMetaData<TableColumnHeader,Number>("-fx-size", SizeConverter.getInstance(), 20.0) { @Override public boolean isSettable(TableColumnHeader n) { return n.size == null || !n.size.isBound(); } @Override public StyleableProperty<Number> getStyleableProperty(TableColumnHeader n) { return (StyleableProperty<Number>)(WritableValue<Number>)n.sizeProperty(); } }; private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES; static { final List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<CssMetaData<? extends Styleable, ?>>(Region.getClassCssMetaData()); styleables.add(SIZE); STYLEABLES = Collections.unmodifiableList(styleables); } } /** * @return The CssMetaData associated with this class, which may include the * CssMetaData of its super classes. */ public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() { return StyleableProperties.STYLEABLES; } /** * {@inheritDoc} */ @Override public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() { return getClassCssMetaData(); } @Override public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) { switch (attribute) { case INDEX: return getIndex(column); case TEXT: return column != null ? column.getText() : null; default: return super.queryAccessibleAttribute(attribute, parameters); } } }
Ms-Dos/Windows
Unix
Write backup
jsp File Browser version 1.2 by
www.vonloesch.de