1 /*******************************************************************************
2 * Copyright (c) 2017 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * Semantum Oy - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.diagram.elements;
14 import java.awt.geom.Point2D;
15 import java.awt.geom.Rectangle2D;
16 import java.util.LinkedList;
17 import java.util.List;
19 import org.simantics.db.common.utils.Logger;
20 import org.simantics.diagram.elements.EditorState.ModificationClass;
21 import org.simantics.g2d.canvas.ICanvasContext;
22 import org.simantics.g2d.element.IElement;
23 import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent;
24 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent;
25 import org.simantics.scenegraph.g2d.nodes.SingleElementNode;
26 import org.simantics.scl.runtime.function.Function1;
27 import org.simantics.scl.runtime.function.Function3;
28 import org.simantics.scl.runtime.tuple.Tuple4;
30 import com.kitfox.svg.SVGDiagram;
31 import com.kitfox.svg.SVGElement;
32 import com.kitfox.svg.SVGException;
33 import com.kitfox.svg.Text;
34 import com.kitfox.svg.Tspan;
35 import com.kitfox.svg.animation.AnimationElement;
38 * @author Antti Villberg
41 class EditorStateManager {
43 static String TERM_STRING = "-----";
44 static String EDITOR_CLASS = "edit";
45 static String EDITOR_ID = "edit";
48 private LinkedList<EditorState> editorState = null;
49 private int editorStateIndex = 0;
51 static class SVGMeasurementContextImpl implements SVGMeasurementContext {
53 public SVGMeasurementContextImpl(SVGNode node) {
57 public Tuple4 getBoundingBox(String id) {
59 Rectangle2D rect = node.getElementBounds(id);
60 if(rect == null) return null;
61 return new Tuple4(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
62 } catch (SVGException e) {
68 EditorStateManager(SVGNode node) {
72 public boolean isEditMode() {
73 return editorState != null;
76 public EditorState currentState() {
77 return editorState.get(editorStateIndex);
80 public int currentHash() {
81 if(!isEditMode()) return 0;
82 return currentState().hashCode();
85 public void activateEditMode(SVGDiagram diagram, Text text) {
87 if(isEditMode()) return;
89 if(text.getId().length() == 0) return;
91 EditorState es = new EditorState();
92 es.base = new EditorStateStatic();
93 es.base.textElementId = text.getId();
95 Tspan span = (Tspan)text.getContent().get(0);
96 String currentText = span.getText();
98 SingleElementNode sne = node.getSingleElementNode();
102 Function1<String,String> fullTextFunction = sne.getParameter("textEditorFullText");
103 if(fullTextFunction != null)
104 es.currentText = fullTextFunction.apply(es.base.textElementId);
105 if(es.currentText == null) {
106 es.currentText = currentText;
109 es.caretPosition = es.currentText.length();
110 es.selectionOtherPosition = 0;
112 // Measure the Y-dimensions of the font
116 diagram.updateTime(0);
117 es.base.verticalDimensions = text.getBoundingBox();
118 span.setText(TERM_STRING);
120 diagram.updateTime(0);
121 es.base.termStringWidth = text.getBoundingBox().getWidth();
122 span.setText(currentText);
124 diagram.updateTime(0);
125 } catch (SVGException e) {
129 ICanvasContext ctx = DiagramNodeUtil.getCanvasContext(node);
130 IElement ie = DiagramNodeUtil.getElement(ctx, sne);
132 EditDataNode data = EditDataNode.getNode(node);
133 deactivateEdit(data, null);
134 TextEditActivation result = new TextEditActivation(0, ie, ctx);
135 data.setTextEditActivation(result);
137 editorState = new LinkedList<>();
138 editorState.push(es);
139 editorStateIndex = 0;
145 private TextEditActivation editActivation;
148 SingleElementNode sne = node.getSingleElementNode();
150 EditorState es = currentState();
151 Function3<SVGMeasurementContext,String,String,Object> editor = sne.getParameter("textEditor");
153 editor.apply(new SVGMeasurementContextImpl(node), es.base.textElementId, es.currentText);
158 protected boolean deactivateEdit() {
159 boolean result = deactivateEdit( editActivation );
160 result |= editActivation != null;
161 editActivation = null;
167 protected boolean deactivateEdit(TextEditActivation activation) {
168 return deactivateEdit( EditDataNode.getNode(node), activation );
171 protected boolean deactivateEdit(EditDataNode data, TextEditActivation activation) {
172 TextEditActivation previous = data.getTextEditActivation();
173 if (previous != null && (previous == activation || activation == null)) {
175 data.setTextEditActivation(null);
181 protected boolean keyPressed(KeyPressedEvent e) {
183 EditorState es = currentState();
184 EditorState nes = es.copy();
185 if(nes.keyPressed(this, e)) {
187 // This key actually terminated editing
190 if(nes.shouldReplace(es)) {
193 while(editorState.size() > (editorStateIndex + 1))
194 editorState.removeLast();
195 editorState.add(nes);
196 editorStateIndex = editorState.size() - 1;
205 public boolean tryToStartEditMode(SVGDiagram diagram) {
206 SVGElement element = diagram.getElement(EDITOR_ID);
207 if(element != null && element instanceof Text) {
208 activateEditMode(diagram, (Text)element);
214 public boolean tryToStartEditMode(SVGDiagram diagram, MouseClickEvent e) {
216 if(diagram != null) {
218 Point2D local = node.controlToLocal( e.controlPosition );
219 // FIXME: once the event coordinate systems are cleared up, remove this workaround
220 local = node.parentToLocal(local);
222 double tolerance = 2.0;
223 Rectangle2D pickRect = new Rectangle2D.Double(local.getX()-tolerance, local.getY()-tolerance, 2*tolerance, 2*tolerance);
226 List<?> retVec = diagram.pick(pickRect, null);
227 for(int i=0;i<retVec.size();i++) {
228 List<?> l = (List<?>)retVec.get(i);
229 for(int j=0;j<l.size();j++) {
230 SVGElement element = (SVGElement)l.get(j);
231 if(element instanceof Tspan) {
234 if(element instanceof Text) {
235 Text text = (Text)element;
236 if(text.hasAttribute("class", AnimationElement.AT_XML)) {
237 String clazz = text.getPresAbsolute("class").getStringValue();
238 if(clazz.contains(EDITOR_CLASS)) {
239 activateEditMode(diagram, text);
247 } catch (SVGException e1) {
248 Logger.defaultLogError(e1);
256 boolean applyEditMode(SVGDiagram diagram) throws SVGException {
259 EditorState es = currentState();
260 es.applyEditMode(diagram);
269 node.cleanDiagramCache();
274 while(editorStateIndex > 0 && currentState().modificationClass.equals(ModificationClass.NO_EDIT)) {
277 if(editorStateIndex > 0)
283 while(editorStateIndex < editorState.size() - 1 && currentState().modificationClass.equals(ModificationClass.NO_EDIT)) {
286 if(editorStateIndex < editorState.size() - 1) {