/******************************************************************************* * Copyright (c) 2017 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Semantum Oy - initial API and implementation *******************************************************************************/ package org.simantics.diagram.elements; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.LinkedList; import java.util.List; import org.simantics.db.common.utils.Logger; import org.simantics.diagram.elements.EditorState.ModificationClass; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.element.IElement; import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent; import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent; import org.simantics.scenegraph.g2d.nodes.SingleElementNode; import org.simantics.scl.runtime.function.Function1; import org.simantics.scl.runtime.function.Function3; import org.simantics.scl.runtime.tuple.Tuple4; import com.kitfox.svg.SVGDiagram; import com.kitfox.svg.SVGElement; import com.kitfox.svg.SVGException; import com.kitfox.svg.Text; import com.kitfox.svg.Tspan; import com.kitfox.svg.animation.AnimationElement; /** * @author Antti Villberg * @since 1.31.0 */ class EditorStateManager { static String TERM_STRING = "-----"; static String EDITOR_CLASS = "edit"; static String EDITOR_ID = "edit"; private SVGNode node; private LinkedList editorState = null; private int editorStateIndex = 0; static class SVGMeasurementContextImpl implements SVGMeasurementContext { private SVGNode node; public SVGMeasurementContextImpl(SVGNode node) { this.node = node; } @Override public Tuple4 getBoundingBox(String id) { try { Rectangle2D rect = node.getElementBounds(id); if(rect == null) return null; return new Tuple4(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); } catch (SVGException e) { return null; } } @Override public void modifyText(String id, String newText) { node.modifyTextElement(id, newText); } } EditorStateManager(SVGNode node) { this.node = node; } public boolean isEditMode() { return editorState != null; } public EditorState currentState() { return editorState.get(editorStateIndex); } public int currentHash() { if(!isEditMode()) return 0; return currentState().hashCode(); } public void activateEditMode(SVGDiagram diagram, Text text) { if(isEditMode()) return; if(text.getId().length() == 0) return; EditorState es = new EditorState(); es.base = new EditorStateStatic(); es.base.textElementId = text.getId(); Tspan span = (Tspan)text.getContent().get(0); String currentText = span.getText(); SingleElementNode sne = node.getSingleElementNode(); if (sne == null) return; Function1 fullTextFunction = sne.getParameter("textEditorFullText"); if(fullTextFunction != null) es.currentText = fullTextFunction.apply(es.base.textElementId); if(es.currentText == null) { es.currentText = currentText; } es.caretPosition = es.currentText.length(); es.selectionOtherPosition = 0; // Measure the Y-dimensions of the font try { span.setText("Ig"); text.rebuild(); diagram.updateTime(0); es.base.verticalDimensions = text.getBoundingBox(); span.setText(TERM_STRING); text.rebuild(); diagram.updateTime(0); es.base.termStringWidth = text.getBoundingBox().getWidth(); span.setText(currentText); text.rebuild(); diagram.updateTime(0); } catch (SVGException e) { e.printStackTrace(); } ICanvasContext ctx = DiagramNodeUtil.getCanvasContext(node); IElement ie = DiagramNodeUtil.getElement(ctx, sne); EditDataNode data = EditDataNode.getNode(node); deactivateEdit(data, null); TextEditActivation result = new TextEditActivation(0, ie, ctx); data.setTextEditActivation(result); editorState = new LinkedList<>(); editorState.push(es); editorStateIndex = 0; paint(); } private TextEditActivation editActivation; void applyEdit() { SingleElementNode sne = node.getSingleElementNode(); if (sne != null) { EditorState es = currentState(); Function3 editor = sne.getParameter("textEditor"); if(editor != null) { editor.apply(new SVGMeasurementContextImpl(node), es.base.textElementId, es.currentText); } } } protected boolean deactivateEdit() { boolean result = deactivateEdit( editActivation ); result |= editActivation != null; editActivation = null; editorState = null; paint(); return result; } protected boolean deactivateEdit(TextEditActivation activation) { return deactivateEdit( EditDataNode.getNode(node), activation ); } protected boolean deactivateEdit(EditDataNode data, TextEditActivation activation) { TextEditActivation previous = data.getTextEditActivation(); if (previous != null && (previous == activation || activation == null)) { previous.release(); data.setTextEditActivation(null); return true; } return false; } protected boolean keyPressed(KeyPressedEvent e) { if(isEditMode()) { EditorState es = currentState(); EditorState nes = es.copy(); if(nes.keyPressed(this, e)) { if(!isEditMode()) { // This key actually terminated editing return true; } if(nes.shouldReplace(es)) { es.replace(nes); } else { while(editorState.size() > (editorStateIndex + 1)) editorState.removeLast(); editorState.add(nes); editorStateIndex = editorState.size() - 1; } return true; } } return false; } public boolean tryToStartEditMode(SVGDiagram diagram) { SVGElement element = diagram.getElement(EDITOR_ID); if(element != null && element instanceof Text) { activateEditMode(diagram, (Text)element); return true; } return false; } public boolean tryToStartEditMode(SVGDiagram diagram, MouseClickEvent e) { if(diagram != null) { Point2D local = node.controlToLocal( e.controlPosition ); // FIXME: once the event coordinate systems are cleared up, remove this workaround local = node.parentToLocal(local); double tolerance = 2.0; Rectangle2D pickRect = new Rectangle2D.Double(local.getX()-tolerance, local.getY()-tolerance, 2*tolerance, 2*tolerance); try { List retVec = diagram.pick(pickRect, null); for(int i=0;i l = (List)retVec.get(i); for(int j=0;j 0 && currentState().modificationClass.equals(ModificationClass.NO_EDIT)) { editorStateIndex--; } if(editorStateIndex > 0) editorStateIndex--; paint(); } void redo() { while(editorStateIndex < editorState.size() - 1 && currentState().modificationClass.equals(ModificationClass.NO_EDIT)) { editorStateIndex++; } if(editorStateIndex < editorState.size() - 1) { editorStateIndex++; } paint(); } }