package javaapplication10; import com.sun.javafx.stage.EmbeddedWindow; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javafx.application.Application; import javafx.beans.InvalidationListener; import javafx.beans.Observable; import javafx.beans.property.BooleanProperty; import javafx.beans.property.DoubleProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleDoubleProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.ComboBox; import javafx.scene.control.ContentDisplay; import javafx.scene.control.Control; import javafx.scene.control.Label; import javafx.scene.control.ListView; import javafx.scene.control.MultipleSelectionModel; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.TextField; import javafx.scene.control.TreeItem; import javafx.scene.control.TreeTableCell; import javafx.scene.control.TreeTableColumn; import javafx.scene.control.TreeTableView; import javafx.scene.control.cell.CheckBoxTableCell; import javafx.scene.control.cell.CheckBoxTreeTableCell; import javafx.scene.control.cell.ChoiceBoxTableCell; import javafx.scene.control.cell.ChoiceBoxTreeTableCell; import javafx.scene.control.cell.ComboBoxTableCell; import javafx.scene.control.cell.ComboBoxTreeTableCell; import javafx.scene.control.cell.MapValueFactory; import javafx.scene.control.cell.ProgressBarTableCell; import javafx.scene.control.cell.ProgressBarTreeTableCell; import javafx.scene.control.cell.TextFieldTreeTableCell; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.layout.FlowPane; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.stage.Stage; import javafx.util.Callback; import javafx.util.StringConverter; public class TableCellEditing extends Application { public static final String TREE_TABLE_VIEW_ID = "TREE_TABLE_VIEW_ID"; public static final String TABLE_VIEW_ID = "TABLE_VIEW_ID"; public static final String TREE_TABLE_EDIT_ID = "TREE_TABLE_EDIT_ID"; public static final String TABLE_EDIT_ID = "TABLE_EDIT_ID"; public static final String TREE_TABLE_FACTORY_CHOICE_ID = "TREE_TABLE_FACTORY_CHOICE_ID"; public static final String TABLE_FACTORY_CHOICE_ID = "TABLE_FACTORY_CHOICE_ID"; public static final String CLEAR_BTN_ID = "CLEAR_BTN_ID"; public static final String RESET_SCENE_BTN_ID = "RESET_SCENE"; public static final String ERROR_LABEL_ID = "ERROR_LBL_ID"; public static final String Column1MapKey = "Data1"; public static final String Column2MapKey = "Data2"; public static final ObservableList data = FXCollections.observableArrayList(); public static final ObservableList someValues = FXCollections.observableArrayList(); public static final List ContentOfMaps = new ArrayList(); public static final int DATA_ITEMS_SIZE = 10; public static final StringConverter converter = new CellCustomStringConverter(); public static enum CellType { CustomCell, ChoiceBox, ComboBox, CheckBox, TextField, MapValue, ProgressBar, Default }; public static class TableCellsScene extends Scene { Label error; final TreeTableView treeTableView = new TreeTableView(); final TableView tableView = new TableView(); public TableCellsScene() { super(new VBox(), 800, 400); someValues.addAll(new DataItem("Data item A"), new DataItem("Data item B"), new DataItem("Data item C")); for (int i = 0; i < DATA_ITEMS_SIZE; i++) { data.add(new DataItem(i)); } VBox box = (VBox) getRoot(); FlowPane eventStatusPane = new FlowPane(); eventStatusPane.setHgap(5); eventStatusPane.setVgap(5); box.getChildren().add(eventStatusPane); Button eventStatusClean = new ClearButton("Clear status"); eventStatusPane.getChildren().add(eventStatusClean); Label errorLabel = new Label("Last error: "); eventStatusPane.getChildren().add(errorLabel); error = new Label(); error.setId(ERROR_LABEL_ID); eventStatusPane.getChildren().add(error); HBox container = new HBox(); LayoutSize layout = new LayoutSize(400, 300, 400, 300, 400, 300); // ------------------------- TREE TABLE VIEW --------------------- final VBox treeTableBox = new VBox(); container.getChildren().add(treeTableBox); treeTableView.setId(TREE_TABLE_VIEW_ID); treeTableView.setEditable(true); ComboBox cbTreeTableView = new ComboBox(); cbTreeTableView.getItems().addAll(FXCollections.observableArrayList(CellType.values())); cbTreeTableView.setId(TREE_TABLE_FACTORY_CHOICE_ID); treeTableBox.getChildren().addAll(treeTableView, cbTreeTableView); layout.apply(treeTableView); final TreeTableColumn treeTableColumn = new TreeTableColumn("field"); treeTableColumn.setPrefWidth(400); treeTableColumn.setCellValueFactory(new Callback, ObservableValue>() { public ObservableValue call(TreeTableColumn.CellDataFeatures p) { return new SimpleObjectProperty(p.getValue()); } }); treeTableColumn.setOnEditCommit(new EventHandler>() { @Override public void handle(TreeTableColumn.CellEditEvent t) { t.getTreeTableView().getRoot().getChildren().get(t.getTreeTablePosition().getRow()).setValue(t.getNewValue()); } }); cbTreeTableView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { public void changed(ObservableValue ov, CellType t, CellType t1) { switch (t1) { case CustomCell: treeTableColumn.setPrefWidth(390); treeTableColumn.setCellFactory(new Callback, TreeTableCell>() { public TreeTableCell call(TreeTableColumn p) { return new TextFieldCustomTreeTableCell(); } }); break; case ChoiceBox: treeTableColumn.setPrefWidth(390); treeTableColumn.setCellFactory(ChoiceBoxTreeTableCell.forTreeTableColumn(converter, someValues)); break; case ComboBox: treeTableColumn.setPrefWidth(390); treeTableColumn.setCellFactory(ComboBoxTreeTableCell.forTreeTableColumn(converter, someValues)); break; case CheckBox: treeTableColumn.setPrefWidth(390); Callback> callback1 = new Callback>() { public ObservableValue call(final Integer p) { return treeTableColumn.getTreeTableView().getRoot().getChildren().get(p).getValue().choiceBoxChecker; } }; treeTableColumn.setCellFactory(CheckBoxTreeTableCell.forTreeTableColumn(callback1, converter)); break; case TextField: treeTableColumn.setPrefWidth(390); treeTableColumn.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn(converter)); break; case ProgressBar: treeTableColumn.setPrefWidth(5); TreeTableColumn treeTableProgressBarColumn = new TreeTableColumn(); treeTableProgressBarColumn.setCellValueFactory(new Callback, ObservableValue>() { public ObservableValue call(TreeTableColumn.CellDataFeatures p) { return (ObservableValue) p.getValue().getValue().progressBarValue; } }); treeTableProgressBarColumn.setCellFactory(ProgressBarTreeTableCell.forTreeTableColumn()); treeTableProgressBarColumn.setPrefWidth(390); if (treeTableView.getColumns().size() < 2) { treeTableView.getColumns().add(treeTableProgressBarColumn); } else { treeTableView.getColumns().set(1, treeTableProgressBarColumn); } break; case Default: treeTableColumn.setCellFactory(new TreeTableColumn().getCellFactory()); default: throw new IllegalStateException("Unknown type of cell"); } } }); final Button startTreeTableEdit = new Button("Start Edit") { @Override public void fire() { treeTableView.edit(treeTableView.getSelectionModel().getSelectedIndex(), treeTableColumn); } }; startTreeTableEdit.setDisable(true); treeTableBox.getChildren().add(startTreeTableEdit); final MultipleSelectionModel treeTableSelectionModel = treeTableView.getSelectionModel(); treeTableSelectionModel.selectedIndexProperty().addListener(new InvalidationListener() { public void invalidated(Observable ov) { startTreeTableEdit.setDisable(treeTableSelectionModel.getSelectedIndex() < 0); } }); treeTableView.getColumns().add(treeTableColumn); ObservableList> treeTableData = FXCollections.>observableArrayList(); for (DataItem item : data) { treeTableData.add(new TreeItem(item)); } treeTableView.setRoot(new TreeItem(new DataItem(null))); treeTableView.setShowRoot(false); treeTableView.getRoot().getChildren().addAll(treeTableData); // ------------------------- TABLE VIEW --------------------- final VBox tableBox = new VBox(); container.getChildren().add(tableBox); tableView.setId(TABLE_VIEW_ID); tableView.setEditable(true); ComboBox cbTableView = new ComboBox(); cbTableView.getItems().addAll(FXCollections.observableArrayList(CellType.values())); cbTableView.setId(TABLE_FACTORY_CHOICE_ID); tableBox.getChildren().addAll(tableView, cbTableView); layout.apply(tableView); final TableColumn tableColumn = new TableColumn("field"); tableColumn.setPrefWidth(400); tableColumn.setCellValueFactory(new Callback, ObservableValue>() { public ObservableValue call(TableColumn.CellDataFeatures p) { return new SimpleObjectProperty(p.getValue()); } }); tableColumn.setOnEditCommit(new EventHandler>() { @Override public void handle(TableColumn.CellEditEvent t) { t.getTableView().getItems().set(t.getTablePosition().getRow(), t.getNewValue()); } }); cbTableView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { public void changed(ObservableValue ov, CellType t, CellType t1) { switch (t1) { case CustomCell: tableColumn.setPrefWidth(390); tableColumn.setCellFactory(new Callback, TableCell>() { public TableCell call(TableColumn p) { return new TextFieldCustomTableCell(); } }); break; case ChoiceBox: tableColumn.setPrefWidth(390); tableColumn.setCellFactory(ChoiceBoxTableCell.forTableColumn(converter, someValues)); break; case ComboBox: tableColumn.setPrefWidth(390); tableColumn.setCellFactory(ComboBoxTableCell.forTableColumn(converter, someValues)); break; case CheckBox: tableColumn.setPrefWidth(390); Callback> callback1 = new Callback>() { public ObservableValue call(final Integer p) { return tableColumn.getTableView().getItems().get(p).choiceBoxChecker; } }; tableColumn.setCellFactory(CheckBoxTableCell.forTableColumn(callback1, converter)); break; case TextField: tableColumn.setPrefWidth(390); tableColumn.setCellFactory(javafx.scene.control.cell.TextFieldTableCell.forTableColumn(converter)); break; case ProgressBar: tableColumn.setPrefWidth(5); TableColumn tableProgressBarColumn = new TableColumn(); tableProgressBarColumn.setCellValueFactory(new Callback, ObservableValue>() { public ObservableValue call(TableColumn.CellDataFeatures p) { return (ObservableValue) p.getValue().progressBarValue; } }); tableProgressBarColumn.setCellFactory(ProgressBarTableCell.forTableColumn()); tableProgressBarColumn.setPrefWidth(390); if (tableView.getColumns().size() < 2) { tableView.getColumns().add(tableProgressBarColumn); } else { tableView.getColumns().set(1, tableProgressBarColumn); } break; case MapValue: TableColumn firstDataColumn = new TableColumn("Data One"); TableColumn secondDataColumn = new TableColumn("Data Two"); firstDataColumn.setCellValueFactory(new MapValueFactory(Column1MapKey)); secondDataColumn.setCellValueFactory(new MapValueFactory(Column2MapKey)); TableView tableView = new TableView(generateDataLikeMap()); tableView.setId(TABLE_VIEW_ID); tableView.setEditable(true); tableView.getSelectionModel().setCellSelectionEnabled(true); tableView.getColumns().setAll(firstDataColumn, secondDataColumn); tableBox.getChildren().set(0, tableView); firstDataColumn.setCellValueFactory(new Callback, ObservableValue>() { public ObservableValue call(TableColumn.CellDataFeatures p) { return new SimpleStringProperty((String) p.getValue().get(Column1MapKey)); } }); Callback, TableCell> cellFactoryForMap = new Callback, TableCell>() { public TableCell call(TableColumn p) { return new MapStringTextFieldTableCell(); } }; firstDataColumn.setCellFactory(cellFactoryForMap); firstDataColumn.setOnEditCommit(new EventHandler>() { public void handle(TableColumn.CellEditEvent t) { t.getRowValue().put(Column1MapKey, t.getNewValue()); } }); break; case Default: tableColumn.setCellFactory(new ListView().getCellFactory()); default: throw new IllegalStateException("Unknown type of cell"); } } }); final Button startTableEdit = new Button("Start Edit") { @Override public void fire() { tableView.edit(tableView.getSelectionModel().getSelectedIndex(), tableColumn); } }; startTableEdit.setDisable(true); tableBox.getChildren().add(startTableEdit); final MultipleSelectionModel tableSelectionModel = tableView.getSelectionModel(); tableSelectionModel.selectedIndexProperty().addListener(new InvalidationListener() { public void invalidated(Observable ov) { startTableEdit.setDisable(tableSelectionModel.getSelectedIndex() < 0); } }); tableView.getColumns().add(tableColumn); tableView.setItems(data); Button resetButton = new Button("Reset"); resetButton.setId(RESET_SCENE_BTN_ID); resetButton.setOnAction(new EventHandler() { public void handle(ActionEvent t) { data.clear(); someValues.clear(); ContentOfMaps.clear(); if (getWindow() instanceof EmbeddedWindow) { ((EmbeddedWindow) getWindow()).setScene(new TableCellsScene()); } else { ((Stage) getWindow()).setScene(new TableCellsScene()); } } }); box.getChildren().add(container); box.getChildren().add(resetButton); } private ObservableList generateDataLikeMap() { int max = 10; ContentOfMaps.clear(); ObservableList allData = FXCollections.observableArrayList(); for (int i = 0; i < max; i++) { Map dataRow = new HashMap(); String key1 = Column1MapKey; String key2 = Column2MapKey; String value1 = "Data 1 - " + i; String value2 = "Data 2 - " + i; dataRow.put(key1, value1); dataRow.put(key2, value2); ContentOfMaps.add(value1); ContentOfMaps.add(value2); allData.add(dataRow); } return allData; } // --------- CELLS FOR TREE TABLE VIEW -------------- //It is not implemented yet. // public class MapStringTextFieldTreeTableCell extends TreeTableCell { // // private TextField textField; // // public MapStringTextFieldTreeTableCell() { // } // // @Override // public void startEdit() { // super.startEdit(); // if (isEmpty()) { // return; // } // // if (textField == null) { // createTextBox(); // } else { // textField.setText(getItem()); // } // // setGraphic(textField); // setContentDisplay(ContentDisplay.GRAPHIC_ONLY); // //// textField.requestFocus(); //// textField.selectAll(); // } // // @Override // public void cancelEdit() { // super.cancelEdit(); // setContentDisplay(ContentDisplay.TEXT_ONLY); // } // // @Override // public void updateItem(String item, boolean empty) { // super.updateItem(item, empty); // if (!isEmpty()) { // if (textField != null) { // textField.setText(item); // } // setText(item); // } // } // // private void createTextBox() { // textField = new TextField(getItem()); // textField.setId(TREE_TABLE_EDIT_ID); // textField.setOnKeyReleased(new EventHandler() { // @Override // public void handle(KeyEvent t) { // if (t.getCode() == KeyCode.ENTER) { // commitEdit(textField.getText()); // } else if (t.getCode() == KeyCode.ESCAPE) { // cancelEdit(); // } // } // }); // } // } public class TextFieldCustomTreeTableCell extends TreeTableCell> { private TextField textField; public TextFieldCustomTreeTableCell() { super(); } @Override public void startEdit() { super.startEdit(); if (isEmpty()) { return; } if (textField == null) { createTextBox(); } else { textField.setText(converter.toString(getItem())); } setGraphic(textField); setContentDisplay(ContentDisplay.GRAPHIC_ONLY); // textField.requestFocus(); // textField.selectAll(); } @Override public void cancelEdit() { super.cancelEdit(); setContentDisplay(ContentDisplay.TEXT_ONLY); } @Override public void updateItem(TreeItem item, boolean empty) { super.updateItem(item, empty); if (!isEmpty()) { if (textField != null) { textField.setText(converter.toString(item)); } setText(converter.toString(getItem())); } } private void createTextBox() { textField = new TextField(converter.toString(getItem())); textField.setId(TREE_TABLE_EDIT_ID); textField.setOnKeyReleased(new EventHandler() { @Override public void handle(KeyEvent t) { if (t.getCode() == KeyCode.ENTER) { commitEdit(new TreeItem(new DataItem(converter.fromString(textField.getText()).toString()))); } else if (t.getCode() == KeyCode.ESCAPE) { cancelEdit(); } } }); } } // --------- CELLS FOR TABLE VIEW ------------------- public class MapStringTextFieldTableCell extends TableCell { private TextField textField; public MapStringTextFieldTableCell() { } @Override public void startEdit() { super.startEdit(); if (isEmpty()) { return; } if (textField == null) { createTextBox(); } else { textField.setText(getItem()); } setGraphic(textField); setContentDisplay(ContentDisplay.GRAPHIC_ONLY); // textField.requestFocus(); // textField.selectAll(); } @Override public void cancelEdit() { super.cancelEdit(); setContentDisplay(ContentDisplay.TEXT_ONLY); } @Override public void updateItem(String item, boolean empty) { super.updateItem(item, empty); if (!isEmpty()) { if (textField != null) { textField.setText(item); } setText(item); } } private void createTextBox() { textField = new TextField(getItem()); textField.setId(TABLE_EDIT_ID); textField.setOnKeyReleased(new EventHandler() { @Override public void handle(KeyEvent t) { if (t.getCode() == KeyCode.ENTER) { commitEdit(textField.getText()); } else if (t.getCode() == KeyCode.ESCAPE) { cancelEdit(); } } }); } } public class TextFieldCustomTableCell extends TableCell { private TextField textField; public TextFieldCustomTableCell() { super(); } @Override public void startEdit() { super.startEdit(); if (isEmpty()) { return; } if (textField == null) { createTextBox(); } else { textField.setText(converter.toString(getItem())); } setGraphic(textField); setContentDisplay(ContentDisplay.GRAPHIC_ONLY); // textField.requestFocus(); // textField.selectAll(); } @Override public void cancelEdit() { super.cancelEdit(); setContentDisplay(ContentDisplay.TEXT_ONLY); } @Override public void updateItem(DataItem item, boolean empty) { super.updateItem(item, empty); if (!isEmpty()) { if (textField != null) { textField.setText(converter.toString(item)); } setText(converter.toString(getItem())); } } private void createTextBox() { textField = new TextField(converter.toString(getItem())); textField.setId(TABLE_EDIT_ID); textField.setOnKeyReleased(new EventHandler() { @Override public void handle(KeyEvent t) { if (t.getCode() == KeyCode.ENTER) { commitEdit(new DataItem(converter.fromString(textField.getText()).toString())); } else if (t.getCode() == KeyCode.ESCAPE) { cancelEdit(); } } }); } } class ClearButton extends Button { private ClearButton(String string) { super(string); setId(CLEAR_BTN_ID); } @Override public void fire() { } }; } @Override public void start(Stage stage) { stage.setScene(getScene()); stage.show(); } protected Scene getScene() { return new TableCellsScene(); } public static void main(String[] args) { launch(TableCellEditing.class, args); } public static class DataItem { public String str; public BooleanProperty choiceBoxChecker = new SimpleBooleanProperty(); public static final DoubleProperty progressBarValue = new SimpleDoubleProperty(0.5); public DataItem(String str) { this.str = str; } public DataItem(int i) { str = "Data item " + i; } public DataItem(int i, boolean initialValue) { choiceBoxChecker.setValue(initialValue); str = "Data item " + i; } public void setString(String str) { this.str = str; } public void setChecker(boolean newCheckerValue) { choiceBoxChecker.setValue(newCheckerValue); } public String getString() { return str; } public boolean getCheckerValue() { return choiceBoxChecker.getValue(); } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass().isAssignableFrom(obj.getClass())) { return str.equals(((DataItem) obj).str); } return false; } @Override public int hashCode() { int hash = 7; hash = 41 * hash + (this.str != null ? this.str.hashCode() : 0); return hash; } @Override public String toString() { return "Data: <" + getString() + ">"; } } public static class LayoutSize { public double minWidth; public double minHeight; public double prefWidth; public double prefHeight; public double maxWidth; public double maxHeight; public LayoutSize(double min_width, double min_height, double pref_width, double pref_height, double max_width, double max_height) { minWidth = min_width; minHeight = min_height; prefWidth = pref_width; prefHeight = pref_height; maxWidth = max_width; maxHeight = max_height; } public void apply(Control control) { control.setMinWidth(minWidth); control.setMinHeight(minHeight); control.setPrefWidth(prefWidth); control.setPrefHeight(prefHeight); control.setMaxWidth(maxWidth); control.setMaxHeight(maxHeight); } } public static class CellCustomStringConverter extends StringConverter { public static final String TO_STRING_PREFIX = "tsc "; public static final String FROM_STRING_PREFIX = "fsc "; int toStringCounter = 0; int fromStringCounter = 0; @Override public String toString(Object t) { return TO_STRING_PREFIX + (t != null ? t.toString() : "null"); } @Override public Object fromString(String string) { return new DataItem(FROM_STRING_PREFIX + string); } } }