diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramGenerator.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramGenerator.xtend index 74242565c..9183829ca 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramGenerator.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramGenerator.xtend @@ -20,8 +20,6 @@ import de.cau.cs.kieler.klighd.internal.util.KlighdInternalProperties import de.cau.cs.kieler.klighd.kgraph.KEdge import de.cau.cs.kieler.klighd.kgraph.KGraphData import de.cau.cs.kieler.klighd.kgraph.KGraphElement -import de.cau.cs.kieler.klighd.kgraph.KGraphFactory -import de.cau.cs.kieler.klighd.kgraph.KIdentifier import de.cau.cs.kieler.klighd.kgraph.KLabel import de.cau.cs.kieler.klighd.kgraph.KNode import de.cau.cs.kieler.klighd.kgraph.KPort @@ -31,7 +29,6 @@ import de.cau.cs.kieler.klighd.krendering.KImage import de.cau.cs.kieler.klighd.krendering.KRendering import de.cau.cs.kieler.klighd.krendering.KRenderingLibrary import de.cau.cs.kieler.klighd.krendering.KRenderingUtil -import de.cau.cs.kieler.klighd.krendering.KText import de.cau.cs.kieler.klighd.lsp.model.ImageData import de.cau.cs.kieler.klighd.lsp.model.SKEdge import de.cau.cs.kieler.klighd.lsp.model.SKGraph @@ -44,16 +41,13 @@ import de.cau.cs.kieler.klighd.util.KlighdPredicates import de.cau.cs.kieler.klighd.util.KlighdProperties import de.cau.cs.kieler.klighd.util.RenderingContextData import java.util.ArrayList -import java.util.HashMap import java.util.HashSet import java.util.List -import java.util.Map import org.apache.log4j.Logger import org.eclipse.elk.alg.layered.options.LayeredOptions import org.eclipse.elk.alg.rectpacking.options.RectPackingOptions import org.eclipse.elk.core.options.CoreOptions import org.eclipse.emf.ecore.EObject -import org.eclipse.emf.ecore.util.EcoreUtil import org.eclipse.sprotty.Dimension import org.eclipse.sprotty.SEdge import org.eclipse.sprotty.SGraph @@ -70,10 +64,7 @@ import org.eclipse.xtext.util.CancelIndicator * synthesis to {@link KNode} for KLighD. * For translation first call the {@link translateModel} function to translate the EObject to a KNode and then call * {@link generate(KNode, String, CancelIndicator)} with that KNode to translate it to an SGraph. - * During translation a map for mapping between the source and target element types is generated as well as - * a list of all generated texts in Sprotty and a map to each text in the source model. - * The function {@link #generateTextDiagram} can be called after the translation to get a simpler SGraph, that contains - * only labels with all texts in the source KNode. + * During translation a map for mapping between the source and target element types is generated. * Based on the yang-lsp implementation by TypeFox. * * @author nre @@ -92,22 +83,6 @@ class KGraphDiagramGenerator implements IDiagramGenerator { @Accessors(PUBLIC_GETTER) var BiMap kGraphToSModelElementMap - /** - * A list containing all texts from the source KGraph inside Sprotty labels. Used for the simpler texts-only SGraph. - * @see #generateTextDiagram - * @see KGraphDiagramState - */ - @Accessors(PUBLIC_GETTER) - var List modelLabels - - /** - * A map containing all {@link KText}s from the source KGraph under the key of their ID in the texts-only SGraph. - * @see #generateTextDiagram - * @see KGraphDiagramState - */ - @Accessors(PUBLIC_GETTER) - var Map textMapping - /** * The data of all {@link KImage}s contained in the view model. */ @@ -164,8 +139,6 @@ class KGraphDiagramGenerator implements IDiagramGenerator { LOG.info("Generating diagram for input: '" + uri + "'") kGraphToSModelElementMap = HashBiMap.create - textMapping = new HashMap - modelLabels = new ArrayList images = new HashSet idGen = new KGraphElementIdGenerator edgesToGenerate = new ArrayList @@ -263,7 +236,7 @@ class KGraphDiagramGenerator implements IDiagramGenerator { private def List createLabels(List labels) { val List labelElements = new ArrayList for (label : labels) { - val SLabel labelElement = generateLabel(label, true) + val SLabel labelElement = generateLabel(label) labelElements.add(labelElement) kGraphToSModelElementMap.put(label, labelElement) } @@ -383,28 +356,19 @@ class KGraphDiagramGenerator implements IDiagramGenerator { /** * Creates a Sprotty label corresponding to the given {@link KLabel}. - * - * @param isMainGraphElement Describes, if the generated label will be part of the main generated {@link SGraph}. */ - private def SKLabel generateLabel(KLabel label, boolean isMainGraphElement) { - val id = isMainGraphElement ? idGen.getId(label) : label.data.filter(KIdentifier).head.id - val SKLabel labelElement = configSElement(SKLabel, id) + private def SKLabel generateLabel(KLabel label) { + val SKLabel labelElement = configSElement(SKLabel, idGen.getId(label)) labelElement.tooltip = label.getProperty(KlighdProperties.TOOLTIP) labelElement.text = label.text val renderings = label.data.filter[KRendering.isAssignableFrom(it.class)].toList - if (isMainGraphElement) { - // remember KLabel element for later size estimation - findSpecialRenderings(renderings) - // activate the element by default if it does not have an active/inactive status yet. - val renderingContextData = RenderingContextData.get(label) - if (!renderingContextData.containsPoperty(KlighdInternalProperties.ACTIVE)) { - renderingContextData.setProperty(KlighdInternalProperties.ACTIVE, true) - } - } else { - // Add the renderings here already to the element. - labelElement.data = renderings + findSpecialRenderings(renderings) + // activate the element by default if it does not have an active/inactive status yet. + val renderingContextData = RenderingContextData.get(label) + if (!renderingContextData.containsPoperty(KlighdInternalProperties.ACTIVE)) { + renderingContextData.setProperty(KlighdInternalProperties.ACTIVE, true) } return labelElement } @@ -491,9 +455,6 @@ class KGraphDiagramGenerator implements IDiagramGenerator { /** * Looks through the data of elements and searches for special renderings that are needed to be pre-processed before * rendering: - * Finds all KText and KLabel elements within the renderings in dataList and puts them as new labels in the - * {@code modelLabels} field. - * Remembers the mapping to the KText elements from the source model in the textMapping field. * Stores all {@link KImage}s in the {@code images} field. */ private def void findSpecialRenderings(List datas) { @@ -503,56 +464,11 @@ class KGraphDiagramGenerator implements IDiagramGenerator { } /** - * Finds all {@link KText}, {@link KLabel} and {@link KImage} elements within the renderings in {@code dataList} and - * stores them. Also remembers the mapping to the KText elements from the source model in the {@code textMapping} - * field. + * Finds all {@link KImage} elements within the renderings in {@code dataList} and stores them. */ private def void findSpecialRenderings(KGraphData data) { - val List dataLabels = newArrayList var ImageData imageData = null - if (data instanceof KText) { - // create a new Label with data as its text for each line in the original text. - // KTexts in Labels have their texts stored inside their ancestor KLabel, not in the KText itself - var container = data.eContainer - while (container instanceof KRendering) { - container = container.eContainer - } - var String text - if (container instanceof KLabel) { - text = container.text - } else { - text = data.text - } - // Found the original text, split it up into individual labels for each line. - // If there is no text, ignore this KText. - val lines = text?.split("\\r?\\n", -1) - lines?.forEach [ line, index | - val newLabel = KGraphFactory.eINSTANCE.createKLabel - newLabel.text = line - // need to put a copy of the text inside the new label because otherwise inserting it into the label will - // modify the eContainer feature of the Text, which should not be changed - val newData = EcoreUtil.copy(data) - newData.text = line - newLabel.data += newData - val identifier = KGraphFactory.eINSTANCE.createKIdentifier - var uniqueId = "" - do { - uniqueId = diagramRoot.id + KGraphElementIdGenerator.ID_SEPARATOR + "texts-only" + - KGraphElementIdGenerator.ID_SEPARATOR + KGraphElementIdGenerator.LABEL_SEPARATOR - + Math.random + KGraphElementIdGenerator.ID_SEPARATOR + index - } while (textMapping.get(uniqueId) !== null) - identifier.id = uniqueId - newLabel.data += identifier - - // generate a new Label as if it would belong to the main model - val sKLabel = generateLabel(newLabel, false) - // All lines point towards the same original data. For matching, the index in the ID has to be taken - // into account as well to match it to its line. - textMapping.put(sKLabel.id, data) - - dataLabels += sKLabel - ] - } else if (data instanceof KContainerRendering) { + if (data instanceof KContainerRendering) { // KImages are container renderings themselves, so also look for their child renderings. if (data instanceof KImage) { imageData = ImageData.of(data) @@ -568,9 +484,6 @@ class KGraphDiagramGenerator implements IDiagramGenerator { } } } - if (!dataLabels.empty) { - modelLabels.addAll(dataLabels) - } if (imageData !== null) { images.add(imageData) } @@ -597,25 +510,4 @@ class KGraphDiagramGenerator implements IDiagramGenerator { default: throw new IllegalArgumentException("Unknown SModelElement type: "+ element?.class) } } - - /** - * Generates a simple text-only {@link SGraph} for a Graph with only the given labels. - * The {@code label}s are expected to come from the {@code modelLabels} field after the {@link #toSGraph} - * translation. - * - * @param labels A list of all labels, this SGraph should contain. - * @param parentId The ID of the graph containing all these labels. - */ - static def SGraph generateTextDiagram(List labels, String parentId) { - // equivalent for the SRootElement - val root = new SKGraph => [ - type = 'graph' - id = parentId + KGraphElementIdGenerator.ID_SEPARATOR + "texts-only" - children = new ArrayList - ] - - root.children = new ArrayList - root.children += labels - return root - } } diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramServer.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramServer.xtend index a91e66c6f..76cdca9ae 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramServer.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramServer.xtend @@ -3,7 +3,7 @@ * * http://rtsys.informatik.uni-kiel.de/kieler * - * Copyright 2018,2020 by + * Copyright 2018-2021 by * + Kiel University * + Department of Computer Science * + Real-Time and Embedded Systems Group @@ -21,30 +21,22 @@ import de.cau.cs.kieler.klighd.IAction.ActionContext import de.cau.cs.kieler.klighd.Klighd import de.cau.cs.kieler.klighd.KlighdDataManager import de.cau.cs.kieler.klighd.ViewContext -import de.cau.cs.kieler.klighd.kgraph.KLabel import de.cau.cs.kieler.klighd.kgraph.KNode -import de.cau.cs.kieler.klighd.krendering.KRendering -import de.cau.cs.kieler.klighd.krendering.KText import de.cau.cs.kieler.klighd.lsp.interactive.layered.LayeredInteractiveActionHandler import de.cau.cs.kieler.klighd.lsp.interactive.rectpacking.RectpackingInteractiveActionHandler import de.cau.cs.kieler.klighd.lsp.launch.AbstractLanguageServer import de.cau.cs.kieler.klighd.lsp.model.CheckImagesAction import de.cau.cs.kieler.klighd.lsp.model.CheckedImagesAction -import de.cau.cs.kieler.klighd.lsp.model.ComputedTextBoundsAction import de.cau.cs.kieler.klighd.lsp.model.LayoutOptionUIData import de.cau.cs.kieler.klighd.lsp.model.PerformActionAction import de.cau.cs.kieler.klighd.lsp.model.RefreshDiagramAction import de.cau.cs.kieler.klighd.lsp.model.RefreshLayoutAction -import de.cau.cs.kieler.klighd.lsp.model.RequestTextBoundsAction import de.cau.cs.kieler.klighd.lsp.model.SKGraph import de.cau.cs.kieler.klighd.lsp.model.SetSynthesisAction import de.cau.cs.kieler.klighd.lsp.model.StoreImagesAction import de.cau.cs.kieler.klighd.lsp.model.UpdateDiagramOptionsAction import de.cau.cs.kieler.klighd.lsp.model.ValuedSynthesisOption import de.cau.cs.kieler.klighd.lsp.utils.KRenderingIdGenerator -import de.cau.cs.kieler.klighd.lsp.utils.SprottyProperties -import de.cau.cs.kieler.klighd.microlayout.Bounds -import de.cau.cs.kieler.klighd.util.KlighdProperties import java.io.FileNotFoundException import java.io.InputStream import java.util.ArrayList @@ -108,11 +100,6 @@ class KGraphDiagramServer extends LanguageAwareDiagramServer { */ protected SModelRoot currentRoot - /** - * Flag indicating if the texts of the current root are updated. - */ - protected boolean textsUpdated = false - /** * Flag indicating if the images of the current root are updated on the client. */ @@ -147,34 +134,20 @@ class KGraphDiagramServer extends LanguageAwareDiagramServer { } /** - * Prepares the client side update of the model by processing the potentially needed text sizes and images on the - * client. Requests the calculation of text sizes by the client via the {@link RequestTextBoundsAction} and checks + * Prepares the client side update of the model by processing the potentially needed images on the client. Checks * for client-side cached images with the {@link CheckImagesAction}. If the corresponding response to the * {@link CheckImagesAction} requires images to be sent, a {@link SendImagesAction} is sent first. After receiving * the result back, updates the model with default Sprotty behavior via the {@link #updateModel} function. * Also handles updating the diagram options on the client. * - * @param newRoot the diagram to request the text sizes for. + * @param newRoot the diagram to request the images for. */ protected def prepareUpdateModel(SModelRoot newRoot) { synchronized (modelLock) { currentRoot = newRoot if (newRoot !== null) { - textsUpdated = false imagesUpdated = false - // text handling - val texts = diagramState.getTexts(newRoot.id) - if (texts === null) { - throw new NullPointerException("The id of the SGraph was not found in the diagramState") - } else if (texts.empty) { - textsUpdated = true - } else { - val textDiagram = KGraphDiagramGenerator.generateTextDiagram(texts, newRoot.id) - dispatch(new RequestTextBoundsAction(textDiagram)) - // the setOrUpdateModel is then executed after the client returns with its ComputedTextBoundsAction - } - // image handling val imageData = diagramState.getImageData(newRoot.id) if (imageData === null) { @@ -191,8 +164,8 @@ class KGraphDiagramServer extends LanguageAwareDiagramServer { } else { setOrUpdateModel } - // If the texts and the images are already updated, the model is ready to be sent to the client. - if (textsUpdated && imagesUpdated) { + // If the images are already updated, the model is ready to be sent to the client. + if (imagesUpdated) { setOrUpdateModel } } @@ -257,11 +230,8 @@ class KGraphDiagramServer extends LanguageAwareDiagramServer { try { val clientId = getClientId(); if (clientId !== null && clientId.equals(message.getClientId())) { - // call the handle function for the ComputedTextBoundsAction or forward the call to the super implementation val Action action = message.getAction(); - if (action.getKind() === ComputedTextBoundsAction.KIND) { - handle(action as ComputedTextBoundsAction) - } else if (action.getKind() === PerformActionAction.KIND) { + if (action.getKind() === PerformActionAction.KIND) { handle(action as PerformActionAction) } else if (action.getKind() === SetSynthesisAction.KIND) { handle(action as SetSynthesisAction) @@ -392,79 +362,6 @@ class KGraphDiagramServer extends LanguageAwareDiagramServer { } } - /** - * Called when a {@link ComputedTextBoundsAction} is received. - * Maps the bounds for all texts referenced in the action back to their corresponding {@link KText} elements - * and updates the model on the client. - */ - protected def handle(ComputedTextBoundsAction action) { - synchronized (modelLock) { - if (currentRoot.getRevision() !== action.getRevision()) { - return - } - // Assume the model is still stored in 'currentRoot', since the ComputedTextBoundsAction only gets issued - // after a RequestTextBoundsAction, where it got stored before. Only applies if no other diagram revision - // is issued first. - - val textMapping = diagramState.getTextMapping(currentRoot.id) - // Add the bounds for each label to the text's properties and remember which KTexts have been modified. - // Also calculate the widths and heights of all texts on the fly to be applied later. - val List texts = newArrayList - val Map textWidths = newHashMap - val Map textHeights = newHashMap - for (elementAndBound : action.bounds) { - val elementId = elementAndBound.elementId - val newSize = elementAndBound.newSize - if (newSize === null) { - throw new NullPointerException("Estimated Size for a KText is null!") - } - val kText = textMapping.get(elementId) - if (kText === null) { - LOG.info("The textMapping does not contain the referenced Text anymore. The model has changed before" + - "completion of the request. Terminating this request.") - return - } - val index = Integer.parseInt(elementId.split("\\$").last) // Matches the character '$' - if (!texts.contains(kText)) { - var container = kText.eContainer - while (container instanceof KRendering) { - container = container.eContainer - } - var String text - if (container instanceof KLabel) { - text = container.text - } else { - text = kText.text - } - if (text !== null) { - var lines = text.split("\\r?\\n", -1).size - texts.add(kText) - val widths = newFloatArrayOfSize(lines) - widths.set(index, newSize.width as float) - textWidths.put(kText, widths) - val heights = newFloatArrayOfSize(lines) - heights.set(index, newSize.height as float) - textHeights.put(kText, heights) - } - } else { - textWidths.get(kText).set(index, newSize.width as float) - textHeights.get(kText).set(index, newSize.height as float) - } - } - // Apply the text's bounds. - for (text : texts) { - text.properties.put(KlighdProperties.CALCULATED_TEXT_BOUNDS, - new Bounds(0, 0, textWidths.get(text).max, textHeights.get(text).fold(0f, [ a, b | a + b ]))) - text.properties.put(SprottyProperties.CALCULATED_TEXT_LINE_WIDTHS, textWidths.get(text)) - text.properties.put(SprottyProperties.CALCULATED_TEXT_LINE_HEIGHTS, textHeights.get(text)) - } - textsUpdated = true - if (imagesUpdated) { - setOrUpdateModel - } - } - } - /** * Taken from {@code DefaultDiagramServer.handle(RequestModelAction)} to use this getModel. * Needed for KeithUpdateModelAction @@ -598,9 +495,7 @@ class KGraphDiagramServer extends LanguageAwareDiagramServer { dispatch(new StoreImagesAction(images)) imagesUpdated = true } - if (textsUpdated) { - setOrUpdateModel - } + setOrUpdateModel } } diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramState.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramState.xtend index 4aaed4d6f..5016075a4 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramState.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramState.xtend @@ -21,13 +21,10 @@ import de.cau.cs.kieler.klighd.ViewContext import de.cau.cs.kieler.klighd.internal.ISynthesis import de.cau.cs.kieler.klighd.kgraph.KGraphElement import de.cau.cs.kieler.klighd.krendering.KImage -import de.cau.cs.kieler.klighd.krendering.KText import de.cau.cs.kieler.klighd.lsp.model.ImageData -import de.cau.cs.kieler.klighd.lsp.model.SKLabel import java.net.URLDecoder import java.util.HashMap import java.util.HashSet -import java.util.List import java.util.Map import java.util.Set import org.eclipse.elk.core.LayoutConfigurator @@ -65,18 +62,6 @@ class KGraphDiagramState { */ Map> imageData = new HashMap - /** - * A list containing all texts from the source KGraph in Sprotty labels. - * Mapped by the URI this map belongs to. - */ - Map> texts = new HashMap - - /** - * A map containing all KTexts from the source KGraph under the key of their id. - * Mapped by the URI this map belongs to. - */ - Map> textMapping = new HashMap - /** * Contains the model of the currently drawn snapshot for the URI of the model, if available. */ @@ -195,44 +180,6 @@ class KGraphDiagramState { imageData.put(uri, value) } - /** - * Getter to access the value stored in the texts map. - * - * @param uri The identifying URI of the graph to access the value in the map. - */ - def List getTexts(String uri) { - texts.get(uri) - } - - /** - * Put method to put a new value in the texts map. - * - * @param uri The identifying URI of the graph to access the map. - * @param value The value to be stored in the map. - */ - def putTexts(String uri, List value) { - texts.put(uri, value) - } - - /** - * Getter to access the value stored in the textMapping map. - * - * @param uri The identifying URI of the graph to access the value in the map. - */ - def Map getTextMapping(String uri) { - textMapping.get(uri) - } - - /** - * Put method to put a new value in the textMapping map. - * - * @param uri The identifying URI of the graph to access the map. - * @param value The value to be stored in the map. - */ - def putTextMapping(String uri, Map value) { - textMapping.put(uri, value) - } - /** * Getter to access the value stored in the snapshotModel map. * @@ -386,8 +333,6 @@ class KGraphDiagramState { kGraphContexts.remove(URLDecoder.decode(uri, "UTF-8")) kGraphToSModelElementMap.remove(uri) idToKGraphElementMap.remove(uri) - texts.remove(uri) - textMapping.remove(uri) snapshotModelMapping.remove(uri) layoutConfigMapping.remove(uri) synthesisIdMapping.remove(uri) diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramUpdater.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramUpdater.xtend index dae0fd5cc..a54e43d05 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramUpdater.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/KGraphDiagramUpdater.xtend @@ -277,8 +277,6 @@ class KGraphDiagramUpdater extends DiagramUpdater { synchronized (diagramState) { diagramState.putKGraphToSModelElementMap(uri, diagramGenerator.getKGraphToSModelElementMap) diagramState.putIdToKGraphElementMap(uri, diagramGenerator.idToKGraphElementMap) - diagramState.putTexts(uri, diagramGenerator.getModelLabels) - diagramState.putTextMapping(uri, diagramGenerator.getTextMapping) diagramState.putImageData(uri, diagramGenerator.images) } diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/gson_utils/EObjectSerializer.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/gson_utils/EObjectSerializer.xtend index de797ad0d..9ef762ee0 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/gson_utils/EObjectSerializer.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/gson_utils/EObjectSerializer.xtend @@ -3,7 +3,7 @@ * * http://rtsys.informatik.uni-kiel.de/kieler * - * Copyright 2019 by + * Copyright 2019-2021 by * + Kiel University * + Department of Computer Science * + Real-Time and Embedded Systems Group @@ -109,13 +109,13 @@ class EObjectSerializer implements JsonSerializer { jsonObject.add("calculatedTextBounds", context.serialize( propertyHolder.getProperty(KlighdProperties.CALCULATED_TEXT_BOUNDS))) } - if (propertyHolder.hasProperty(SprottyProperties.CALCULATED_TEXT_LINE_WIDTHS)) { + if (propertyHolder.hasProperty(KlighdProperties.CALCULATED_TEXT_LINE_WIDTHS)) { jsonObject.add("calculatedTextLineWidths", context.serialize( - propertyHolder.getProperty(SprottyProperties.CALCULATED_TEXT_LINE_WIDTHS))) + propertyHolder.getProperty(KlighdProperties.CALCULATED_TEXT_LINE_WIDTHS))) } - if (propertyHolder.hasProperty(SprottyProperties.CALCULATED_TEXT_LINE_HEIGHTS)) { + if (propertyHolder.hasProperty(KlighdProperties.CALCULATED_TEXT_LINE_HEIGHTS)) { jsonObject.add("calculatedTextLineHeights", context.serialize( - propertyHolder.getProperty(SprottyProperties.CALCULATED_TEXT_LINE_HEIGHTS))) + propertyHolder.getProperty(KlighdProperties.CALCULATED_TEXT_LINE_HEIGHTS))) } if (propertyHolder.hasProperty(KlighdProperties.IS_NODE_TITLE)) { jsonObject.add("isNodeTitle", context.serialize( diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/gson_utils/KGraphTypeAdapterUtil.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/gson_utils/KGraphTypeAdapterUtil.xtend index c8599e4d5..9c7761532 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/gson_utils/KGraphTypeAdapterUtil.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/gson_utils/KGraphTypeAdapterUtil.xtend @@ -3,7 +3,7 @@ * * http://rtsys.informatik.uni-kiel.de/kieler * - * Copyright 2018, 2020 by + * Copyright 2018-2021 by * + Kiel University * + Department of Computer Science * + Real-Time and Embedded Systems Group @@ -24,7 +24,6 @@ import de.cau.cs.kieler.klighd.lsp.interactive.rectpacking.RectpackingDeletePosi import de.cau.cs.kieler.klighd.lsp.interactive.rectpacking.RectpackingSetPositionConstraintAction import de.cau.cs.kieler.klighd.lsp.interactive.rectpacking.SetAspectRatioAction import de.cau.cs.kieler.klighd.lsp.model.CheckedImagesAction -import de.cau.cs.kieler.klighd.lsp.model.ComputedTextBoundsAction import de.cau.cs.kieler.klighd.lsp.model.PerformActionAction import de.cau.cs.kieler.klighd.lsp.model.RefreshDiagramAction import de.cau.cs.kieler.klighd.lsp.model.RefreshLayoutAction @@ -43,9 +42,8 @@ class KGraphTypeAdapterUtil { gsonBuilder .registerTypeAdapterFactory( new ActionTypeAdapter.Factory => [ - // General sprotty action + // General Sprotty actions addActionKind(CheckedImagesAction.KIND, CheckedImagesAction) - addActionKind(ComputedTextBoundsAction.KIND, ComputedTextBoundsAction) addActionKind(PerformActionAction.KIND, PerformActionAction) addActionKind(SetSynthesisAction.KIND, SetSynthesisAction) addActionKind(RefreshDiagramAction.KIND, RefreshDiagramAction) diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/launch/AbstractLsCreator.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/launch/AbstractLsCreator.xtend index ba49adb9d..97e1588df 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/launch/AbstractLsCreator.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/launch/AbstractLsCreator.xtend @@ -3,7 +3,7 @@ * * http://rtsys.informatik.uni-kiel.de/kieler * - * Copyright 2019,2020 by + * Copyright 2019-2021 by * + Kiel University * + Department of Computer Science * + Real-Time and Embedded Systems Group @@ -25,6 +25,11 @@ import de.cau.cs.kieler.klighd.lsp.LSPUtil import de.cau.cs.kieler.klighd.lsp.SprottyViewer import de.cau.cs.kieler.klighd.lsp.gson_utils.KGraphTypeAdapterUtil import de.cau.cs.kieler.klighd.standalone.KlighdStandaloneSetup +import java.awt.Font +import java.awt.FontFormatException +import java.awt.GraphicsEnvironment +import java.io.File +import java.io.IOException import java.io.InputStream import java.io.OutputStream import java.util.Collection @@ -104,6 +109,7 @@ abstract class AbstractLsCreator implements ILsCreator { ExecutorService executorService, Function wrapper, boolean socket ) { this.injector = injector + // Setup KLighD. KlighdStandaloneSetup.initialize // Programmatically register the SprottyViewer. It is not registered via service, as it will only ever be used diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/model/Actions.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/model/Actions.xtend index a5b2ab99f..6425c96fd 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/model/Actions.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/model/Actions.xtend @@ -3,7 +3,7 @@ * * http://rtsys.informatik.uni-kiel.de/kieler * - * Copyright 2018, 2020 by + * Copyright 2018-2021 by * + Kiel University * + Department of Computer Science * + Real-Time and Embedded Systems Group @@ -18,7 +18,6 @@ import java.util.List import java.util.Set import java.util.function.Consumer import org.eclipse.sprotty.Action -import org.eclipse.sprotty.ElementAndBounds import org.eclipse.sprotty.RequestAction import org.eclipse.sprotty.ResponseAction import org.eclipse.sprotty.SModelRoot @@ -27,36 +26,6 @@ import org.eclipse.xtend.lib.annotations.Accessors import org.eclipse.xtend.lib.annotations.EqualsHashCode import org.eclipse.xtend.lib.annotations.ToString -/** - * Sent from the server to the client to request bounds for the given texts. The texts are rendered - * invisibly so the bounds can derived from the DOM. The response is a {@link ComputedTextBoundsAction}. - * - * @author nre - */ -@Accessors -@EqualsHashCode -@ToString(skipNulls = true) -class RequestTextBoundsAction implements RequestAction { - public static val KIND = 'requestTextBounds' - String kind = KIND - - SModelRoot textDiagram - String requestId - - new() {} - new(Consumer initializer) { - initializer.accept(this) - } - - /** - * Constructor to call when creating this. The {@code textDiagram} should contain a sprotty Diagram with all texts, - * whose bounds should be requested. - */ - new(SModelRoot textDiagram) { - this.textDiagram = textDiagram - } -} - /** * Sent from the server to the client to send a list of all available syntheses for the current model. * @@ -154,8 +123,7 @@ class UpdateDiagramOptionsAction implements Action { } /** - * Constructor to call when creating this. The {@code textDiagram} should contain a sprotty Diagram with all texts, - * whose bounds should be requested. + * Constructor to call when creating this. */ new(List valuedSynthesisOptions, List layoutOptions, List actions, String modelUri) { @@ -208,29 +176,6 @@ class SetSynthesisAction implements Action { } } -/** - * Sent from the client to the server to transmit the result of text bounds computation as a response - * to a {@link RequestTextBoundsAction}. - * - * @author nre - */ -@Accessors -@EqualsHashCode -@ToString(skipNulls = true) -class ComputedTextBoundsAction implements ResponseAction { - public static val KIND = 'computedTextBounds' - String kind = KIND - int revision - - List bounds - String responseId - - new() {} - new(Consumer initializer) { - initializer.accept(this) - } -} - /** * Sent from the client to the server to request a KlighD action to be performed on the current model. * diff --git a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/utils/SprottyProperties.xtend b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/utils/SprottyProperties.xtend index eee076f2d..70a1a9747 100644 --- a/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/utils/SprottyProperties.xtend +++ b/plugins/de.cau.cs.kieler.klighd.lsp/src/de/cau/cs/kieler/klighd/lsp/utils/SprottyProperties.xtend @@ -23,7 +23,7 @@ import org.eclipse.elk.graph.properties.Property /** * A collection of {@link IProperty IProperties} that may be used to persist some additional data on - * {@link KGraphElement}s to allow functionality with a sprotty viewer. + * {@link KGraphElement}s to allow functionality with a Sprotty viewer. * * @author nre */ @@ -68,18 +68,6 @@ class SprottyProperties { public static final IProperty EXPANDED = new Property("klighd.lsp.expanded", true); - /** - * Property determining the pre-calculated widths of each line in a {@link KText}. - */ - public static final IProperty CALCULATED_TEXT_LINE_WIDTHS = - new Property("klighd.calculated.text.line.widths", null); - - /** - * Property determining the pre-calculated heights of each line in a {@link KText}. - */ - public static final IProperty CALCULATED_TEXT_LINE_HEIGHTS = - new Property("klighd.calculated.text.line.heights", null); - /** * The unique identifier of a {@link KRendering}, built hierarchically from the root rendering of an element using * the ID of the parent rendering, a separator "$" and a local ID for the child rendering. diff --git a/plugins/de.cau.cs.kieler.klighd.standalone/build.properties b/plugins/de.cau.cs.kieler.klighd.standalone/build.properties index b107977f4..19f676e24 100644 --- a/plugins/de.cau.cs.kieler.klighd.standalone/build.properties +++ b/plugins/de.cau.cs.kieler.klighd.standalone/build.properties @@ -1,3 +1,4 @@ source.. = src/ bin.includes = META-INF/,\ - . + .,\ + resources/ diff --git a/plugins/de.cau.cs.kieler.klighd.standalone/resources/fonts/LICENSE_FONTS.txt b/plugins/de.cau.cs.kieler.klighd.standalone/resources/fonts/LICENSE_FONTS.txt new file mode 100644 index 000000000..ca4e7efdb --- /dev/null +++ b/plugins/de.cau.cs.kieler.klighd.standalone/resources/fonts/LICENSE_FONTS.txt @@ -0,0 +1,47 @@ +## License + +Copyright 2016 Red Hat, Inc., + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +#### SIL OPEN FONT LICENSE +Version 1.1 - 26 February 2007 + +--- + +#### PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. + +The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. + +#### DEFINITIONS +“Font Software” refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. + +“Reserved Font Name” refers to any names specified as such after the copyright statement(s). + +“Original Version” refers to the collection of Font Software components as distributed by the Copyright Holder(s). + +“Modified Version” refers to any derivative made by adding to, deleting, or substituting—in part or in whole—any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. + +“Author” refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. + +#### PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. + +5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. + +#### TERMINATION +This license becomes null and void if any of the above conditions are not met. + +#### DISCLAIMER +THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. \ No newline at end of file diff --git a/plugins/de.cau.cs.kieler.klighd.standalone/resources/fonts/overpass-mono-regular.otf b/plugins/de.cau.cs.kieler.klighd.standalone/resources/fonts/overpass-mono-regular.otf new file mode 100644 index 000000000..80a4b839b Binary files /dev/null and b/plugins/de.cau.cs.kieler.klighd.standalone/resources/fonts/overpass-mono-regular.otf differ diff --git a/plugins/de.cau.cs.kieler.klighd.standalone/resources/fonts/overpass-regular.otf b/plugins/de.cau.cs.kieler.klighd.standalone/resources/fonts/overpass-regular.otf new file mode 100644 index 000000000..3a7c095fa Binary files /dev/null and b/plugins/de.cau.cs.kieler.klighd.standalone/resources/fonts/overpass-regular.otf differ diff --git a/plugins/de.cau.cs.kieler.klighd.standalone/src/de/cau/cs/kieler/klighd/standalone/KlighdStandaloneSetup.java b/plugins/de.cau.cs.kieler.klighd.standalone/src/de/cau/cs/kieler/klighd/standalone/KlighdStandaloneSetup.java index 58be9e93d..5f53cc6e3 100644 --- a/plugins/de.cau.cs.kieler.klighd.standalone/src/de/cau/cs/kieler/klighd/standalone/KlighdStandaloneSetup.java +++ b/plugins/de.cau.cs.kieler.klighd.standalone/src/de/cau/cs/kieler/klighd/standalone/KlighdStandaloneSetup.java @@ -9,6 +9,9 @@ */ package de.cau.cs.kieler.klighd.standalone; +import java.awt.Font; +import java.awt.GraphicsEnvironment; +import java.io.InputStream; import java.util.Collection; import org.eclipse.elk.alg.force.options.ForceMetaDataProvider; @@ -46,6 +49,7 @@ public void doInitialize() { EPackage.Registry.INSTANCE.put(KGraphPackage.eNS_URI, KGraphPackage.eINSTANCE); EPackage.Registry.INSTANCE.put(KRenderingPackage.eNS_URI, KRenderingPackage.eINSTANCE); Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("kgx", new XMIResourceFactoryImpl()); + registerFonts(); ElkReflect.register(ExpansionAwareLayoutOption.ExpansionAwareLayoutOptionData.class, () -> new ExpansionAwareLayoutOption.ExpansionAwareLayoutOptionData(), @@ -111,4 +115,20 @@ protected ILayoutMetaDataProvider getRadialMetaDataProvider() { return null; } } + + /** + * Registers all TrueType (.ttf) and OpenType (.otf) font files placed in the resources/fonts folder to the AWT + * {@link GraphicsEnvironment} to be used as the default font in external/standalone applications. + */ + protected void registerFonts() { + String[] filePaths = {"/resources/fonts/overpass-regular.otf", "/resources/fonts/overpass-mono-regular.otf"}; + final GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + for (int i = 0; i < filePaths.length; ++i) { + try (InputStream fontStream = this.getClass().getResourceAsStream(filePaths[i])) { + ge.registerFont(Font.createFont(Font.TRUETYPE_FONT, fontStream)); + } catch (Throwable e) { + System.out.println("could not load font file " + filePaths[i]); + } + } + } } diff --git a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/KlighdConstants.java b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/KlighdConstants.java index 69afb6cee..a772f4c1b 100644 --- a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/KlighdConstants.java +++ b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/KlighdConstants.java @@ -112,13 +112,22 @@ public final class KlighdConstants { * * Note: This is a AWT constant! */ - public static final String DEFAULT_FONT_NAME = Klighd.IS_WINDOWS ? "Arial" : Font.SANS_SERIF; + public static final String DEFAULT_FONT_NAME = Klighd.IS_PLATFORM_RUNNING + ? Klighd.IS_WINDOWS + ? "Arial" + : Font.SANS_SERIF + : "Overpass"; /** * A platform independent font name identifier for a default monospaced font. */ - public static final String DEFAULT_MONOSPACE_FONT_NAME = Klighd.IS_WINDOWS ? "Consolas" - : Klighd.IS_MACOSX ? "Monaco" : "Monospace"; + public static final String DEFAULT_MONOSPACE_FONT_NAME = Klighd.IS_PLATFORM_RUNNING + ? Klighd.IS_WINDOWS + ? "Consolas" + : Klighd.IS_MACOSX + ? "Monaco" + : "Monospace" + : "Overpass Mono"; /** * This font size is used for {@link de.cau.cs.kieler.klighd.krendering.KText KTexts}, if no diff --git a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/microlayout/PlacementUtil.java b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/microlayout/PlacementUtil.java index 5b5fdc883..be4564ca1 100644 --- a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/microlayout/PlacementUtil.java +++ b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/microlayout/PlacementUtil.java @@ -932,13 +932,51 @@ public static Bounds estimateTextSize(final KText kText) { * @return the minimal bounds for the string */ public static Bounds estimateTextSize(final KText kText, final String text) { - final Bounds testSize = getTestingTextSize(kText); + Bounds size = getTestingTextSize(kText); + + float[] textWidths = null; + float[] textHeights = null; - if (testSize != null) { - return testSize; - } else { - return estimateTextSize(fontDataFor(kText, null), text); + if (size == null) { + FontData fontData = fontDataFor(kText, null); + + if (Klighd.IS_PLATFORM_RUNNING) { + // In the platform case, SWT is used and handles the text size estimation just fine. + size = estimateTextSize(fontData, text); + } else { + String[] lines = text.split("\n"); + // In the non-platform case when AWT is used, AWT needs some help to estimate the text line by line. + textWidths = new float[lines.length]; + textHeights = new float[lines.length]; + float y = 0; + float maxWidth = Float.MIN_VALUE; + float totalHeight = 0; + // Estimate size for each line individually. + for (int i = 0; i < lines.length; ++i) { + Bounds lineSize = estimateTextSize(fontData, lines[i]); + + // The first y value is the y value of the multiline text. + if (i == 0) y = lineSize.y; + // Track the max width. + if (lineSize.width > maxWidth) maxWidth = lineSize.width; + // Track the total height. + totalHeight += lineSize.height; + + textWidths[i] = lineSize.width; + textHeights[i] = lineSize.height; + } + // Calculate the complete bounding box of the multiline text. + size = new Bounds(0, y, maxWidth, totalHeight); + } + } + if (kText != null && textWidths != null && textHeights != null) { + // persist the line-wise estimations on the KText. + kText.getProperties().put(KlighdProperties.CALCULATED_TEXT_BOUNDS, Bounds.of(size)); + kText.getProperties().put(KlighdProperties.CALCULATED_TEXT_LINE_WIDTHS, textWidths); + kText.getProperties().put(KlighdProperties.CALCULATED_TEXT_LINE_HEIGHTS, textHeights); } + + return size; } /** @@ -953,13 +991,6 @@ public static Bounds estimateTextSize(final KText kText, final String text) { */ public static Bounds getTestingTextSize(final KText kText) { if (kText != null) { - // If the KText has already estimated bounds, use them. - if (kText.hasProperty(KlighdProperties.CALCULATED_TEXT_BOUNDS)) { - // The bounds need to be copied, because otherwise they would be changed by the caller of this method, - // specifically the GridPlacementUtil.estimateGridSize::501 changes its cellSize (which is this object). - return new Bounds(kText.getProperty(KlighdProperties.CALCULATED_TEXT_BOUNDS)); - } - // special handling required for the regression tests // I don't trust in the different SWT implementations to // provide the same size of a text on different platforms diff --git a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/KlighdProperties.java b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/KlighdProperties.java index c1a2a6d86..15c3b28f3 100644 --- a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/KlighdProperties.java +++ b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/util/KlighdProperties.java @@ -356,6 +356,18 @@ public static boolean isSelectable(final EObject viewElement) { */ public static final IProperty CALCULATED_TEXT_BOUNDS = new Property("klighd.calculated.text.bounds", null); + + /** + * Property determining the pre-calculated widths of each line in a {@link KText}. + */ + public static final IProperty CALCULATED_TEXT_LINE_WIDTHS = + new Property("klighd.calculated.text.line.widths", null); + + /** + * Property determining the pre-calculated heights of each line in a {@link KText}. + */ + public static final IProperty CALCULATED_TEXT_LINE_HEIGHTS = + new Property("klighd.calculated.text.line.heights", null); /**