public CNestedTableColumnHeader(final TableViewSkinBase skin, final TableColumnBase tc) { super(skin, tc); this.skin = skin; // ((CTableView) skin.getSkinnable()).fixingColumnsAllowedProperty().addListener(new ChangeListener<Boolean>() { // // @Override // public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { // CellView.getValue(() -> { // if (tc != null) { // tc.setContextMenu(getColumnContextMenu()); // } // // }); // } // }); }
protected Collection<TableColumnBase> getTopLevelColumns() { return new GetAction<Collection<TableColumnBase>>() { @Override public void run(Object... parameters) throws Exception { Collection<TableColumnBase> cols; Control control = testedControl.getControl(); if (isTableTests) { cols = ((TableView) control).getColumns(); } else { cols = ((TreeTableView) control).getColumns(); } setResult(cols); } }.dispatch(testedControl.getEnvironment()); }
protected void sortColumnsByName(final boolean isAscending) { new GetAction<Object>() { private Comparator<TableColumnBase> comparator = new Comparator<TableColumnBase>() { public int compare(TableColumnBase o1, TableColumnBase o2) { int result = o1.getText().compareTo(o2.getText()); return isAscending ? result : -result; } }; @Override public void run(Object... os) throws Exception { if (isTableTests) { FXCollections.sort(((TableView) testedControl.getControl()).getColumns(), comparator); } else { FXCollections.sort(((TreeTableView) testedControl.getControl()).getColumns(), comparator); } } }.dispatch(testedControl.getEnvironment()); try { TimeUnit.MILLISECONDS.sleep(1000); } catch (Exception e) { } }
@Override protected TableCell<T, ?> getCell(TableColumnBase tcb) { TableColumn tableColumn = (TableColumn<T, ?>) tcb; TableCell cell = (TableCell) tableColumn.getCellFactory().call(tableColumn); // we set it's TableColumn, TableView and TableRow cell.updateTableColumn(tableColumn); cell.updateTableView(tableColumn.getTableView()); cell.updateTableRow(getSkinnable()); return cell; }
private void recreateCells() { if (cellsMap != null) { Collection<Reference<TableCell<T, ?>>> cells = cellsMap.values(); Iterator<Reference<TableCell<T, ?>>> cellsIter = cells.iterator(); while (cellsIter.hasNext()) { Reference<TableCell<T, ?>> cellRef = cellsIter.next(); TableCell<T, ?> cell = cellRef.get(); if (cell != null) { cell.updateIndex(-1); cell.getSkin().dispose(); cell.setSkin(null); } } cellsMap.clear(); } ObservableList<? extends TableColumnBase/* <T,?> */> columns = getVisibleLeafColumns(); cellsMap = new WeakHashMap<TableColumnBase, Reference<TableCell<T, ?>>>(columns.size()); fullRefreshCounter = DEFAULT_FULL_REFRESH_COUNTER; getChildren().clear(); for (TableColumnBase col : columns) { if (cellsMap.containsKey(col)) { continue; } // create a TableCell for this column and store it in the cellsMap // for future use createCell(col); } }
private TableCell<T, ?> createCell(TableColumnBase col) { // we must create a TableCell for this table column TableCell<T, ?> cell = getCell(col); // and store this in our HashMap until needed cellsMap.put(col, new WeakReference<>(cell)); return cell; }
void setSortOrder(final List<ColumnState> newColumnStates) { new GetAction<Object>() { @Override public void run(Object... parameters) throws Exception { ObservableList<TableColumnBase> columns = observableArrayList(getTopLevelColumns()); ObservableList<TableColumnBase> columnSortOrder = isTableTests ? ((TableView) testedControl.getControl()).getSortOrder() : ((TreeTableView) testedControl.getControl()).getSortOrder(); columnSortOrder.clear(); for (ColumnState columnState : newColumnStates) { TableColumnBase col = columns.get(columnState.columnIndex); setSortType(col, columnState.sortType); checkSortType(col, columnState.sortType); col.setSortable(columnState.sortable); columnSortOrder.add(col); } } }.dispatch(testedControl.getEnvironment()); //Animation delay try { Thread.sleep(100); } catch (Exception e) { } }
static void checkSortType(TableColumnBase col, ColumnState.SortTypeProvider type) { if (isTableTests) { assertTrue(((TableColumn) col).getSortType().equals(type.forTableColumn())); } else { assertTrue(((TreeTableColumn) col).getSortType().equals(type.forTreeTableColumn())); } }
static void setSortType(TableColumnBase col, ColumnState.SortTypeProvider type) { if (isTableTests) { ((TableColumn) col).setSortType(type.forTableColumn()); } else { ((TreeTableColumn) col).setSortType(type.forTreeTableColumn()); } }
protected void applyStyleOnColumn(final TableColumnBase column, final String style) { new GetAction() { @Override public void run(Object... os) throws Exception { column.setStyle(style); } }.dispatch(testedControl.getEnvironment()); }
protected void doRefresh() { List<? extends TableColumnBase> columns = tableViewOp.getDataColumns(); for (int r = 0; r < tableViewOp.getItems().size(); r++) { for (int c = 0; c < columns.size(); c++) { getFound().add(new Point(c, r)); getAux().add(getType().cast(columns.get(c).getCellData(tableViewOp.getRow(r)))); } } }
/** * @return List of columns, which are on the last level (leaf columns) of * tree table header. In the case of nested columns, some columns are * parents, and some are children. This method returns the list of columns, * which have no children. Those columns correspond to the realy shown * columns of data in TreeTableView. */ @Property(DATA_COLUMNS_PROP_NAME) public List<TreeTableColumn> getDataColumns() { return new GetAction<java.util.List<TreeTableColumn>>() { @Override public void run(Object... parameters) throws Exception { ArrayList fillList = new ArrayList<TableColumnBase>(); TableViewWrap.getLastLevelColumns((java.util.List<? extends TableColumnBase>) getControl().getColumns(), fillList); setResult((java.util.List) fillList); } }.dispatch(getEnvironment()); }
protected void doRefresh() { List<? extends TableColumnBase> columns = getColumns(); for (int r = 0; r < getItemsSize(); r++) { for (int c = 0; c < columns.size(); c++) { getFound().add(new Point(c, r)); getAux().add(getType().cast(columns.get(c).getCellData(getRow(r)))); } } }
/** * Build a styled column header * * @param column Target column * @param style Target header style * @return Built header */ public static Node buildColumnHeader(final TableColumnBase<?, ?> column, final String style) { final Label columnNameLabel = new Label(column.getText()); columnNameLabel.getStyleClass().add(style); final StackPane columnHeaderNode = new StackPane(); columnHeaderNode.getChildren().add(columnNameLabel); columnHeaderNode.prefWidthProperty().bind(column.widthProperty().subtract(20)); columnNameLabel.prefWidthProperty().bind(columnHeaderNode.prefWidthProperty()); return columnHeaderNode; }
/** * Store any changes to column order, visibility, and size * * @param <T> Type of the view object stored in the table * @param columns Target columns * @param columnVisibilityProperty * @param defaultColumnVisibilities * @param columnSizeProperty * @param defaultColumnSizes * @param columnOrderProperty * @param defaultColumnOrder */ public static <T> void storeColumnStates(final List<? extends TableColumnBase<T, ?>> columns, final String columnVisibilityProperty, final String defaultColumnVisibilities, final String columnSizeProperty, final String defaultColumnSizes, final String columnOrderProperty, final String defaultColumnOrder) { final String visibility = columns.stream().map(c -> String.valueOf(c.isVisible())).collect( Collectors.joining(GuiProperties.COMPOSITE_PROPERTY_VALUE_SEPARATOR)); final String sizes = columns.stream().map(c -> String.valueOf(c.getWidth())).collect( Collectors.joining(GuiProperties.COMPOSITE_PROPERTY_VALUE_SEPARATOR)); final String order = columns.stream().map(c -> c.getId()).collect( Collectors.joining(GuiProperties.COMPOSITE_PROPERTY_VALUE_SEPARATOR)); final String oldVisibleColumns = ApplicationPreferences.getProperty( columnVisibilityProperty, defaultColumnVisibilities); final String oldColumnSizes = ApplicationPreferences.getProperty( columnSizeProperty, defaultColumnSizes); final String oldColumnOrder = ApplicationPreferences.getProperty( columnOrderProperty, defaultColumnOrder); if(!visibility.equals(oldVisibleColumns)) { ApplicationPreferences.setProperty(columnVisibilityProperty, visibility); } if(!sizes.equals(oldColumnSizes)) { ApplicationPreferences.setProperty(columnSizeProperty, sizes); } if(!order.equals(oldColumnOrder)) { ApplicationPreferences.setProperty(columnOrderProperty, order); } }
private static <T> void resetTableHeader(final ObservableList<? extends TableColumnBase<T, ?>> columns, final List<CheckMenuItem> columnCheckItems, final TableState<T> columnState, final BiConsumer<String, Double> columnResizer) { final Map<String, Double> defaultColumnSizeMapping = columnState.getDefaultColumnSizeMapping(); columns.clear(); columnCheckItems.forEach(ci -> { final String columnId = ci.getId(); ci.setDisable(false); columnResizer.accept(columnId, defaultColumnSizeMapping.get(columnId)); }); columnCheckItems.forEach(ci -> ci.setSelected(columnState.getDefaultVisibleColumnNames().contains(ci.getId()))); }
public TableState(final LinkedHashMap<String, ? extends TableColumnBase<T, ?>> columnMappings, final Map<String, Boolean> columnVisibilityMapping, final Set<String> defaultVisibleColumnNames, final Map<String, Double> defaultColumnSizeMapping, final String defaultColumnOrder) { this.columnMappings = columnMappings; this.columnVisibilityMapping = columnVisibilityMapping; this.defaultVisibleColumnNames = defaultVisibleColumnNames; this.defaultColumnSizeMapping = defaultColumnSizeMapping; this.defaultColumnOrder = defaultColumnOrder; }
/** * Simple Constructor specifying the column along with the header text and or graphic. * * @param column the column managed by the item * @param headerGraphic the header graphic */ public ColumnMenuItem(TableColumnBase<?, ?> column, Node headerGraphic) { super(); this.setSelected(column.isVisible()); this.setGraphic(headerGraphic); // Columns may have a fixed header text defined in the FXML instead of a generated graphic. if (headerGraphic == null) { this.setGraphic(new Text(column.getText())); } // Originally a binding was used : column.visibleProperty().bind(selectedProperty()); // However, this apparently conflicts with the internal JavaFX ColumnMenu (or TableMenu) rebuilding, which binds // bidirectionally to the property. // So both listeners below mimic a bidirectional binding. // See : https://bugs.openjdk.java.net/browse/JDK-8136468 // Toggle column visibility. Double-check visibility to avoid infinite loops with the other listener below. selectedProperty().addListener((property, oldValue, newValue) -> { if (column.isVisible() != newValue) { column.setVisible(newValue); } }); // Toggle menu selection based on column visibility. This is added to keep the selection status in sync with // column visibility when other mechanisms toggle the column visibility. column.visibleProperty().addListener((property, oldValue, newValue) -> { setSelected(newValue); }); }
/** * Simple Constructor specifying the menu item text, the visibility and the columns. * * @param title the menu item text * @param visible the visibility of the columns after the menu item has been selected * @param columns the columns managed by the item */ public ColumnGroupMenuItem(String title, boolean visible, TableColumnBase<?, ?>... columns) { super(); this.setGraphic(new Text(title)); List<TableColumnBase<?, ?>> columnList = asList(columns); setOnAction(event -> columnList.forEach(column -> column.setVisible(visible))); }
/** * Simple Constructor specifying the menu item text, the visibility and collection of columns. * * @param title the menu item text * @param visible the visibility of the columns after the menu item has been selected * @param columns the columns managed by the item */ public ColumnGroupMenuItem(String title, boolean visible, Collection<TableColumnBase<?, ?>> columns) { super(); this.setGraphic(new Text(title)); setOnAction(event -> columns.forEach(column -> column.setVisible(visible))); }
@Override protected HBox getColumnHeader(TableColumnBase<?, ?> column, String title, ProfileContext profileContext) { HBox header = createColoredLabelContainer(CENTER); if (profileContext != null) { addProfileNr(header, profileContext); } header.getChildren().add(new Text(title)); return header; }
/** * Override doing nothing. The {@link AbstractProfileViewController} implementations have fixed column headings * defined in the FXML. */ @Override protected HBox getColumnHeader(TableColumnBase<?, ?> column, String title, ProfileContext context) { return null; }
/** * Sets the appropriate header for the column, using * * @param column * @param title * @param context */ private void configureHeader(TableColumnBase<?, ?> column, String title, ProfileContext context) { HBox header = getColumnHeader(column, title, context); if (header != null) { reconfigureColumn(column, header); } }
/** * Set various {@link TableColumnBase} properties. Somehow it's hard to get a TableColumn to resize properly. * Therefore, we calculate a fair width ourselves. * <p> * * @param column the {@link TreeTableColumn} to be reconfigured * @param graphic the graphic to be displayed in the column header */ public static final void reconfigureColumn(TableColumnBase<?, ?> column, HBox graphic) { if (graphic == null) { return; } double width = calculateHeaderWidth(graphic); column.setGraphic(graphic); column.setMinWidth(width); column.setPrefWidth(width + 5); }
@Override protected boolean isColumnPartiallyOrFullyVisible(TableColumnBase tc) { return tableViewSkin == null ? false : isColumnPartiallyOrFullyVisible((TableColumn) tc); }
protected void updateCells(boolean resetChildren) { // To avoid a potential memory leak (when the TableColumns in the // TableView are created/inserted/removed/deleted, we have a 'refresh // counter' that when we reach 0 will delete all cells in this row // and recreate all of them. if (resetChildren) { if (fullRefreshCounter == 0) { recreateCells(); } fullRefreshCounter--; } // if clear isn't called first, we can run into situations where the // cells aren't updated properly. final boolean cellsEmpty = cells.isEmpty(); cells.clear(); TableRow<T> skinnable = getSkinnable(); final int skinnableIndex = skinnable.getIndex(); final List<? extends TableColumnBase/* <T,?> */> visibleLeafColumns = getVisibleLeafColumns(); for (int i = 0, max = visibleLeafColumns.size(); i < max; i++) { TableColumnBase<T, ?> col = visibleLeafColumns.get(i); System.out.println(col.getText()); TableCell<T, ?> cell = null; if (cellsMap.containsKey(col)) { cell = cellsMap.get(col).get(); // the reference has been gc'd, remove key entry from map if (cell == null) { cellsMap.remove(col); } } if (cell == null) { // if the cell is null it means we don't have it in cache and // need to create it cell = createCell(col); } updateCell(cell, skinnable); cell.updateIndex(skinnableIndex); cells.add(cell); } // update children of each row if (!fixedCellSizeEnabled && (resetChildren || cellsEmpty)) { getChildren().setAll(cells); } }
protected List<? extends TableColumnBase> getColumns() { return treeTableViewWrap.getDataColumns(); }
public JFXNestedTableColumnHeader(TableViewSkinBase skin, TableColumnBase tc) { super(skin, tc); }
protected TableColumnHeader createTableColumnHeader(TableColumnBase col) { return col.getColumns().isEmpty() ? new JFXTableColumnHeader(getTableViewSkin(), col) : new NestedTableColumnHeader(getTableViewSkin(), col); }
public JFXTableColumnHeader(TableViewSkinBase skin, TableColumnBase tc) { super(skin, tc); }
/** * Add context menu to table columns * * @param <T> Type of the view object stored in the table * @param columns Table columns * @param tableState Loaded column states * @param columnResizer Resetter of columns' widths and order */ public static <T> void addTableHeaderContextMenus(final ObservableList<? extends TableColumnBase<T, ?>> columns, final TableState<T> tableState, final BiConsumer<String, Double> columnResizer) { final ContextMenu headerContextMenu = new ContextMenu(); final Map<String, CheckMenuItem> menuItemMapping = new HashMap<>(); columns.forEach(c -> { final CheckMenuItem columnVisibleMenuItem = new CheckMenuItem(c.getText()); columnVisibleMenuItem.setId(c.getId()); columnVisibleMenuItem.setSelected(true); menuItemMapping.put(c.getId(), columnVisibleMenuItem); c.setContextMenu(headerContextMenu); columnVisibleMenuItem.selectedProperty().addListener((obs, oldV, selected) -> { c.setVisible(selected); final List<? extends TableColumnBase<T, ?>> visibleColumnHeaders = TableUtils.getVisibleColumnHeaders(columns); if(!selected && visibleColumnHeaders.size() == 1) { final Optional<MenuItem> lastSelectedMenuItem = headerContextMenu.getItems().stream().filter( mi -> ((CheckMenuItem)mi).isSelected()).findFirst(); if(lastSelectedMenuItem.isPresent()) { lastSelectedMenuItem.get().setDisable(true); } } else if(selected && visibleColumnHeaders.size() == 2) { headerContextMenu.getItems().stream().filter(mi -> mi instanceof CheckMenuItem && ((CheckMenuItem)mi).isSelected()).forEach(mi -> mi.setDisable(false)); } }); Platform.runLater(() -> { final Optional<? extends TableColumnBase<T, ?>> columnId = columns.stream().filter( tc -> tc.getId().equals(c.getId())).findFirst(); columnVisibleMenuItem.setSelected(columnId.isPresent()? tableState.getColumnVisibilityMapping().get(columnId.get().getId()) : false); }); }); tableState.getColumnMappings().values().forEach(c -> headerContextMenu.getItems().add(menuItemMapping.get(c.getId()))); final List<CheckMenuItem> checkMenuItems = headerContextMenu.getItems().stream().map( mi -> mi instanceof CheckMenuItem? (CheckMenuItem)mi : null).filter(Objects::nonNull).collect(Collectors.toList()); final MenuItem resetHeadersMenuItem = new MenuItem("_Reset"); resetHeadersMenuItem.setOnAction(e -> TableUtils.resetTableHeader(columns, checkMenuItems, tableState, columnResizer)); headerContextMenu.getItems().addAll(new SeparatorMenuItem(), resetHeadersMenuItem); }
private static final <T> List<? extends TableColumnBase<T, ?>> getVisibleColumnHeaders( final ObservableList<? extends TableColumnBase<T, ?>> tableColumns) { return tableColumns.stream().filter(TableColumnBase::isVisible).collect(Collectors.toList()); }
public final LinkedHashMap<String, ? extends TableColumnBase<T, ?>> getColumnMappings() { return columnMappings; }
@Override protected HBox getColumnHeader(TableColumnBase<?, ?> column, String title, ProfileContext context) { return null; }
/** * Adds an appropriate {@link ColumnMenuItem} to the column view menu item list. * * @param column the {@link TableColumnBase} for which a menu item will be added * @param title the display title for the column * @param context the {@link ProfileContext} of the profile for which the column contains data, or null for a Diff * column which contains comparison data between both profiles in a Diff */ private void addColumnMenuItem(TableColumnBase<?, ?> column, String title, ProfileContext context) { columnViewItems.add(new ColumnMenuItem(column, getColumnHeader(column, title, context))); }
/** * Adds a menu item which controls the visibility of a group of columns. * * @param title the display title for the menu item * @param visible the visibility of the columns after the menu item has been selected * @param the columns in the group */ protected void addColumnGroupMenuItem(String title, boolean visible, TableColumnBase<?, ?>... columns) { columnViewGroupItems.add(new ColumnGroupMenuItem(title, visible, columns)); }
/** * Adds a menu item which controls the visibility of a group of columns. * * @param title the display title for the menu item * @param visible the visibility of the columns after the menu item has been selected * @param a number of {@link Collection}s containing the columns in the group */ protected void addColumnGroupMenuItem(String title, boolean visible, Collection<TableColumnBase<?, ?>> columns) { columnViewGroupItems.add(new ColumnGroupMenuItem(title, visible, columns)); }
/** * Get the contents of the column header. This method is abstract because different implementing controllers have * different requirements. E.g. the Diff views need to include indications of which of the profiles being compared * the column data is for. * <p> * If null is passed as {@link ProfileContext}, this indicates to the subclass that the data for the column is a * comparison between both profiles from a {@link DiffEntry}. * <p> * The method should return null if the column has a fixed text header defined in the FXML (or elsewhere). * <p> * * @param <C> the type of the column * @param column the column for which the header contents should be set * @param title the display title for the column * @param context the {@link ProfileContext} of the profile for which the column contains data, or null for a Diff * column which contains comparison data between both profiles in a Diff * @return an {@link HBox} which can be used as header for the column */ protected abstract HBox getColumnHeader(TableColumnBase<?, ?> column, String title, ProfileContext context);