opcua-service/main/java/de/opcua/app/ui/EditableValueCellFactory.java
2026-05-11 19:40:18 +02:00

194 lines
6.2 KiB
Java

package de.opcua.app.ui;
import de.opcua.app.model.NodeRow;
import de.opcua.app.opc.OpcUaService;
import javafx.scene.control.*;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.util.StringConverter;
/**
* Makes the Value column editable with OPC UA write-back
*/
public class EditableValueCellFactory {
private final OpcUaService opcService;
public EditableValueCellFactory(OpcUaService opcService) {
this.opcService = opcService;
}
/**
* Create editable value column
*/
public TableColumn<NodeRow, String> createEditableValueColumn() {
TableColumn<NodeRow, String> col = new TableColumn<>("Value");
col.setCellValueFactory(c -> c.getValue().valueProperty());
col.setCellFactory(tc -> new EditableValueCell());
col.setEditable(true);
col.setPrefWidth(150);
col.setOnEditCommit(event -> handleValueEdit(event));
return col;
}
private void handleValueEdit(TableColumn.CellEditEvent<NodeRow, String> event) {
NodeRow row = event.getRowValue();
String newValue = event.getNewValue();
String oldValue = event.getOldValue();
if (newValue == null || newValue.equals(oldValue)) {
return;
}
// Attempt to write to OPC UA
if (!opcService.isConnected()) {
showError("Not connected to OPC UA server");
row.setValue(oldValue); // Revert
return;
}
if (!row.isReadable()) {
showError("Node is not writable");
row.setValue(oldValue); // Revert
return;
}
// Write to OPC UA
opcService.writeValue(row.getNodeId(), newValue)
.thenAccept(success -> {
javafx.application.Platform.runLater(() -> {
if (success) {
row.setValue(newValue);
System.out.println("[OPC UA] Write successful: " + row.getNodeId() + " = " + newValue);
} else {
showError("Write failed for " + row.getNodeId());
row.setValue(oldValue); // Revert
}
});
})
.exceptionally(ex -> {
javafx.application.Platform.runLater(() -> {
showError("Write error: " + ex.getMessage());
row.setValue(oldValue); // Revert
});
return null;
});
}
private void showError(String message) {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("Write Error");
alert.setHeaderText("Failed to write value");
alert.setContentText(message);
alert.show();
}
/**
* Custom editable cell with visual feedback
*/
private class EditableValueCell extends TableCell<NodeRow, String> {
private TextField textField;
public EditableValueCell() {
setConverter(new StringConverter<String>() {
@Override
public String toString(String object) {
return object == null ? "" : object;
}
@Override
public String fromString(String string) {
return string;
}
});
}
@Override
public void startEdit() {
NodeRow row = getTableRow().getItem();
if (row == null || !row.isReadable()) {
// Not editable - show tooltip
Tooltip tooltip = new Tooltip("This node is read-only");
setTooltip(tooltip);
return;
}
if (!opcService.isConnected()) {
Tooltip tooltip = new Tooltip("Not connected to OPC UA");
setTooltip(tooltip);
return;
}
super.startEdit();
if (textField == null) {
createTextField();
}
textField.setText(getItem());
setText(null);
setGraphic(textField);
textField.selectAll();
textField.requestFocus();
}
@Override
public void cancelEdit() {
super.cancelEdit();
setText(getItem());
setGraphic(null);
}
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
setStyle("");
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getItem());
}
setText(null);
setGraphic(textField);
} else {
setText(item);
setGraphic(null);
// Visual indication of editability
NodeRow row = getTableRow().getItem();
if (row != null && row.isReadable() && opcService.isConnected()) {
setStyle("-fx-background-color: #f0fff0;"); // Light green
setTooltip(new Tooltip("Double-click to edit"));
} else {
setStyle("");
setTooltip(null);
}
}
}
}
private void createTextField() {
textField = new TextField(getItem());
textField.setMinWidth(getWidth() - getGraphicTextGap() * 2);
textField.setOnAction(event -> {
commitEdit(textField.getText());
});
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if (!isNowFocused) {
commitEdit(textField.getText());
}
});
}
private void setConverter(StringConverter<String> converter) {
// Helper method for future enhancements
}
}
}