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 createEditableValueColumn() { TableColumn 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 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 { private TextField textField; public EditableValueCell() { setConverter(new StringConverter() { @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 converter) { // Helper method for future enhancements } } }