2

How can I disable any cell editable in parent row in treetableview? Please look the pictures and check the sample code. Shortly I want to disable row editable if row is expandable (root row or sub root row)

this picture is correct enter image description here

but this is not correct enter image description here

**Example code **

import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
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.TreeItemPropertyValueFactory;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Callback;

public class TreeTableExample extends Application {

    public static void main(String[] args) {
        Application.launch(args);
    }

    @Override
    @SuppressWarnings("unchecked")
    public void start(Stage stage) {

        HBox root = new HBox(createTable());
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.setTitle("Using a TreeTableView");
        stage.show();
    }

    public TreeTableView createTable() {

        TreeTableView<Person> treeTable = new TreeTableView<>();
        treeTable.setEditable(true);

        Callback<TreeTableColumn<Person, String>, 
            TreeTableCell<Person, String>> cellFactory
                = (TreeTableColumn<Person, String> p) -> new EditingCell();

        TreeTableColumn<Person, String> firstName = new TreeTableColumn<>("First Name");
        firstName.setCellValueFactory(new TreeItemPropertyValueFactory<>("firstName"));
        firstName.setCellFactory(cellFactory);
        firstName.setOnEditCommit((TreeTableColumn.CellEditEvent<Person, String> event) -> {
            if(event.getNewValue()!=null)
                event.getRowValue().getValue().setFirstName(event.getNewValue());
        });

        TreeTableColumn<Person, String> lastName = new TreeTableColumn<>("Last Name");
        lastName.setCellValueFactory(new TreeItemPropertyValueFactory<>("lastName"));
        lastName.setCellFactory(cellFactory);
        lastName.setOnEditCommit((TreeTableColumn.CellEditEvent<Person, String> event) -> {
            if(event.getNewValue()!=null)
                event.getRowValue().getValue().setLastName(event.getNewValue());
        });

        treeTable.getColumns().addAll(firstName, lastName);
        TreeItem<Person> root = new TreeItem<>();
        for (int i = 0; i < 5; i++) {
            root.getChildren().add(new TreeItem<>(new Person()));
        }
        treeTable.setRoot(root);
        return treeTable;
    }

    public class Person {

        private SimpleStringProperty firstName;
        private SimpleStringProperty lastName;

        public Person(){
            firstName = new SimpleStringProperty(this, "firstName");
            lastName = new SimpleStringProperty(this, "lastName");
        };

        public String getFirstName() {
            return firstName.get();
        }

        public void setFirstName(String fName) {
            firstName.set(fName);
        }

        public String getLastName() {
            return lastName.get();
        }

        public void setLastName(String fName) {
            lastName.set(fName);
        }

    }

    class EditingCell extends TreeTableCell<Person, String> {

        private TextField textField;

        public EditingCell() {
        }

        @Override
        public void startEdit() {
            if (!isEmpty()) {
                super.startEdit();
                createTextField();
                setText(null);
                setGraphic(textField);
                textField.selectAll();
            }
        }

        @Override
        public void cancelEdit() {
            super.cancelEdit();

            setText((String) getItem());
            setGraphic(null);
        }

        @Override
        public void updateItem(String item, boolean empty) {
            super.updateItem(item, empty);

            if (empty) {
                setText(null);
                setGraphic(null);
            } else if (isEditing()) {
                if(!getTreeTableView().getTreeItem(getIndex()).isLeaf())
                    setEditable(false);
                if (textField != null) {
                    textField.setText(getString());
                }
                setText(null);
                setGraphic(textField);
            } else {
                setText(getString());
                setGraphic(null);
            }
        }

        private void createTextField() {
            textField = new TextField(getString());
            textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
            textField.focusedProperty().addListener(
                    (ObservableValue<? extends Boolean> arg0,
                            Boolean arg1, Boolean arg2) -> {
                        if (!arg2) {
                            commitEdit(textField.getText());
                        }
                    });
        }

        private String getString() {
            return getItem() == null ? "" : getItem();
        }
    }
}

just run it and double click on the root item

make-individual-cell-editable-in-javafx-tableview I checked the solution works for tableview but for treetaleview does not work.

Community
  • 1
  • 1
sancho
  • 598
  • 2
  • 8
  • 22
  • In your cell implementation, you can call `setEditable(true)` or `setEditable(false)` in the `updateItem(...)` method, depending on the item being displayed. – James_D Dec 31 '15 at 19:04
  • @James_D thanks for you comment but this does not solve my problem. because as you can see on the picture "Bathing area" is **TreeItem**. if I double click on S.No just view expanded or collapsed. But when I double click on the Item Name column cell is entering editing mode. I want enable cell editing mode only if row is leaf – sancho Dec 31 '15 at 19:23
  • The idea is you check in the `updateItem` method whether the tree item is a leaf or not, and set the editable flag accordingly. – James_D Dec 31 '15 at 19:36
  • I did but did not work – sancho Dec 31 '15 at 19:37
  • Then there's probably something wrong with the way you tried to do it. – James_D Dec 31 '15 at 19:38
  • `@Override public void updateItem(Integer item, boolean empty) { super.updateItem(item, empty); if (empty) { setText(null); setGraphic(null); } else{ if(!getTreeTableView().getTreeItem(getIndex()).isLeaf()) setEditable(false); ..... } }` is correct or wrong? – sancho Dec 31 '15 at 19:46
  • Impossible to tell from that. I recommend you create a [MCVE] and [edit] your question to include it. – James_D Dec 31 '15 at 19:48
  • ok I will add some example as soon as possible – sancho Dec 31 '15 at 19:51
  • unrelated: you don't need custom edit handlers if you exposed the properties on Person (the tutorial example is highly suboptimal!) – kleopatra Jan 06 '16 at 13:14

2 Answers2

2

It seems that TreeTableCell does not properly check its editable property before deciding whether or not to call startEdit(). I think that's a bug. You can work around it by checking that yourself in your startEdit() method:

@Override
public void startEdit() {
    if (isEditable() && !isEmpty()) {
        super.startEdit();
        createTextField();
        setText(null);
        setGraphic(textField);
        textField.selectAll();
    }
}

and now in your updateItem() method, you can check the current tree item from the row, and update editable as required:

@Override
public void updateItem(String item, boolean empty) {
    super.updateItem(item, empty);

    TreeItem<Person> treeItem = getTreeTableRow().getTreeItem();
    setEditable(treeItem != null &&  treeItem.isLeaf());

    if (empty) {
        setText(null);
        setGraphic(null);
    } else if (isEditing()) {
        if(!getTreeTableView().getTreeItem(getIndex()).isLeaf())
            setEditable(false);
        if (textField != null) {
            textField.setText(getString());
        }
        setText(null);
        setGraphic(textField);
    } else {
        setText(getString());
        setGraphic(null);
    }
}
James_D
  • 201,275
  • 16
  • 291
  • 322
  • thank you very much. now is working. As you mentioned problem is I did not check that the cell is editable or not in startEdit() method – sancho Jan 02 '16 at 16:14
  • 1
    unrelated: no need to expand the scope of updateItem (to public) - it's strictly for use in the class itself (polite cough :-) – kleopatra Jan 06 '16 at 12:44
1

Actually I disagree with the reasoning in the other answer: there is nothing wrong with core TreeTableCell (it does check for its editability before actually starting an edit) - instead the logic in the custom cell implementation is broken. Particularly, the part of updateItem that sets the editable property:

} else if (isEditing()) {
    if(!getTreeTableView().getTreeItem(getIndex()).isLeaf())
        setEditable(false);

Besides being incomplete in not resetting the editable back to true anywhere (remember: cells are re-used), we allow super to first start editing and only after it started, it's disabled.

This logic error is fixed (in the other answer, copied here for convenience) by unconditionally setting the editability in updateItem:

super.updateItem(item, empty);

TreeItem<Person> treeItem = getTreeTableRow().getTreeItem();
setEditable(treeItem != null &&  treeItem.isLeaf());

The other usage error (as already noted) was not fully checking cell state before actually configuring the editor. The suggested fix - check cell's editable - isn't quite complete because table/column editability might be disabled as well. To take that into account, I would tend to let super do its job and only configure the editor if editability actually changed, like

super.startEdit();
// super changed state into editing 
if (isEditing()) {
   // create and install the textField
}  
Community
  • 1
  • 1
kleopatra
  • 51,061
  • 28
  • 99
  • 211