From 19ffabc39cbaf23fe55ad591f9ccf4eb064709a2 Mon Sep 17 00:00:00 2001
From: Andreas Bayha <bayha@fortiss.org>
Date: Tue, 14 Jul 2020 17:25:27 +0200
Subject: [PATCH] Annotations: Text Editing for Annotation View works

Editing textual-based annotation entries in the table works now.
Also unintended cancel operations (e.g. when clicking onto another cell)
are catched and handled correctly.

It remains to make the implementation more genierc to be reusable for
other views.

Issue-Ref: 4014
Issue-Url: https://af3-developer.fortiss.org/issues/4014
Signed-off-by: Andreas Bayha <bayha@fortiss.org>
---
 .../view/fx/AnnotationViewFXController.java   | 157 +++++++++++++++---
 1 file changed, 133 insertions(+), 24 deletions(-)

diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/view/fx/AnnotationViewFXController.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/view/fx/AnnotationViewFXController.java
index 9a0366b6a..2f58fff09 100644
--- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/view/fx/AnnotationViewFXController.java
+++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/view/fx/AnnotationViewFXController.java
@@ -59,12 +59,18 @@ import javafx.collections.ObservableList;
 import javafx.fxml.FXML;
 import javafx.scene.Node;
 import javafx.scene.control.SplitPane;
+import javafx.scene.control.TextField;
 import javafx.scene.control.TreeItem;
 import javafx.scene.control.TreeTableCell;
 import javafx.scene.control.TreeTableColumn;
+import javafx.scene.control.TreeTableRow;
 import javafx.scene.control.TreeTableView;
 import javafx.scene.control.cell.ComboBoxTreeTableCell;
+import javafx.scene.control.cell.TextFieldTreeTableCell;
+import javafx.scene.input.KeyCode;
 import javafx.util.Callback;
+import javafx.util.StringConverter;
+import javafx.util.converter.DefaultStringConverter;
 
 /**
  * 
@@ -189,49 +195,152 @@ public class AnnotationViewFXController extends CompositeFXControllerBase<SplitP
 		IAnnotationValueProvider<IAnnotatedSpecification> valueProvider = entry
 				.getAnnotationValueProvider(annotationClass);
 		IAnnotatedSpecification specification = entry.getSpecification(annotationClass);
+		EClassifier valueType = valueProvider.getEStructuralFeatureDescriptor().getEType(specification);
 
 		String columnName = valueProvider.getAnnotationName(specification);
 
 		TreeTableColumn<AnnotationEntry, String> column = annotationTreeViewer.addColumn(columnName, 150);
 		colIdxAnnotationMap.put(columnIdx, handle);
 
-		ObservableList<String> items = FXCollections.observableArrayList();
-		Callback<TreeTableColumn<AnnotationEntry, String>, TreeTableCell<AnnotationEntry, String>> comboCellFactory = ComboBoxTreeTableCell
-				.forTreeTableColumn(items);
-		column.setCellFactory(comboCellFactory);
+		if (valueType == null) {
+			column.setEditable(false);
+			return;
+		}
 
 		column.setEditable(true);
-		column.setOnEditStart(event -> {
+
+		// Case distinction for the cell factory depending on the type to be edited.
+		if (valueType instanceof EEnum) {
+			ObservableList<String> items = FXCollections.observableArrayList();
+			Callback<TreeTableColumn<AnnotationEntry, String>, TreeTableCell<AnnotationEntry, String>> comboCellFactory = ComboBoxTreeTableCell
+					.forTreeTableColumn(items);
+			column.setCellFactory(comboCellFactory);
+
+			column.setOnEditStart(event -> {
+				// We need to get the type for every row individually, as it might be different
+				// Enums for every row.
+				TreeItem<AnnotationEntry> treeItem = event.getRowValue();
+				AnnotationEntry rowEntry = treeItem.getValue();
+				ValueProviderBase<IAnnotatedSpecification> annotationValueProvider = (ValueProviderBase<IAnnotatedSpecification>) rowEntry
+						.getAnnotationValueProvider(annotationClass);
+				EStructuralFeatureDescriptor structuralFeature = annotationValueProvider
+						.getEStructuralFeatureDescriptor();
+				EClassifier type = structuralFeature.getEType(rowEntry.getSpecification(annotationClass));
+				if (type instanceof EEnum) {
+					EList<EEnumLiteral> allLiterals = ((EEnum) type).getELiterals();
+
+					items.clear();
+					items.addAll(allLiterals.stream().map(l -> l.getLiteral()).collect(toList()));
+				}
+			});
+		} else if (valueType.getInstanceClass().equals(Boolean.class)) {
+			System.out.println(valueType);
+		} else {
+			// For all other types, there is text editing.
+			column.setCellFactory(
+					ttColumn -> new AnnotationTextFieldTreeTableCell(new DefaultStringConverter(), annotationClass));
+		}
+
+		column.setOnEditCommit(event -> {
 			TreeItem<AnnotationEntry> treeItem = event.getRowValue();
 			AnnotationEntry rowEntry = treeItem.getValue();
-			ValueProviderBase<IAnnotatedSpecification> annotationValueProvider = (ValueProviderBase<IAnnotatedSpecification>) rowEntry
-					.getAnnotationValueProvider(annotationClass);
+			String newValue = event.getNewValue();
+
+			setAnnotationEntryValue(specification, rowEntry, newValue);
+		});
 
-			EStructuralFeatureDescriptor structuralFeature = annotationValueProvider.getEStructuralFeatureDescriptor();
+		column.setOnEditCancel(event -> {
+			System.out.println(event);
+		});
+	}
 
-			EClassifier type = structuralFeature.getEType(rowEntry.getSpecification(annotationClass));
-			if (type instanceof EEnum) {
-				EList<EEnumLiteral> allLiterals = ((EEnum) type).getELiterals();
+	private void setAnnotationEntryValue(IAnnotatedSpecification specification, AnnotationEntry annotationEntry,
+			String newValue) {
+		ITopLevelElement tle = IPersistencyService.getInstance().getTopLevelElementFor(selectedElement);
 
-				items.clear();
-				items.addAll(allLiterals.stream().map(l -> l.getLiteral()).collect(toList()));
+		tle.runAsCommand(() -> {
+			try {
+				annotationEntry.setSpecificationValue(newValue, specification.getClass());
+			} catch (Exception e1) {
+				// TODO Auto-generated catch block
+				e1.printStackTrace();
 			}
 		});
-		column.setOnEditCommit(event -> {
-			TreeItem<AnnotationEntry> treeItem = event.getRowValue();
-			AnnotationEntry rowEntry = treeItem.getValue();
+	}
+
+	private class AnnotationTextFieldTreeTableCell extends TextFieldTreeTableCell<AnnotationEntry, String> {
+		private final Class<IAnnotatedSpecification> annotationClass;
+
+		private boolean escapePressed = false;
+		private AnnotationEntry currentlyEditedEntry;
+		private String currentInput = "";
+
+		private AnnotationTextFieldTreeTableCell(StringConverter<String> converter,
+				Class<IAnnotatedSpecification> annotationClass) {
+			super(converter);
 
-			ITopLevelElement tle = IPersistencyService.getInstance().getTopLevelElementFor(selectedElement);
+			this.annotationClass = annotationClass;
 
-			tle.runAsCommand(() -> {
-				try {
-					rowEntry.setSpecificationValue(event.getNewValue(), specification.getClass());
-				} catch (Exception e1) {
-					// TODO Auto-generated catch block
-					e1.printStackTrace();
+			this.setOnKeyPressed(event -> {
+				if (event.getCode().equals(KeyCode.ESCAPE)) {
+					escapePressed = true;
 				}
 			});
-		});
+		}
+
+		@Override
+		public void startEdit() {
+			TreeTableRow<AnnotationEntry> row = getTreeTableRow();
+			AnnotationEntry currentEntry = row.getItem();
+
+			// We start editing only, if this cell is supposed to be editable.
+			IAnnotationValueProvider<IAnnotatedSpecification> annotationValueProvider = currentEntry
+					.getAnnotationValueProvider(annotationClass);
+			IAnnotatedSpecification currentSpecification = currentEntry.getSpecification(annotationClass);
+			if (annotationValueProvider != null && annotationValueProvider.canEdit(currentSpecification)) {
+				super.startEdit();
+
+				currentlyEditedEntry = currentEntry;
+				// Reset currentInput buffer for the new element.
+				currentInput = "";
+			} else {
+				currentlyEditedEntry = null;
+			}
+
+			// We keep the current input in the buffer variable currentInput to be able to
+			// use it during unintended cancel options. See cancelEdit().
+			TextField textField = (TextField) getGraphic();
+			if (textField != null) {
+				textField.setOnKeyReleased(event -> {
+					currentInput = textField.getText();
+				});
+			}
+		}
+
+		@Override
+		public void cancelEdit() {
+			super.cancelEdit();
+
+			setText(currentInput);
+
+			if (escapePressed) {
+				escapePressed = false;
+				currentlyEditedEntry = null;
+			} else {
+				if (currentlyEditedEntry != null) {
+					IAnnotatedSpecification specification = currentlyEditedEntry.getSpecification(annotationClass);
+					setAnnotationEntryValue(specification, currentlyEditedEntry, currentInput);
+					currentlyEditedEntry = null;
+				}
+			}
+		}
+
+		@Override
+		public void commitEdit(String newValue) {
+			currentlyEditedEntry = null;
+
+			super.commitEdit(newValue);
+		}
 	}
 
 	/**
-- 
GitLab