1 package org.simantics.diagram.elements;
3 import java.awt.geom.Point2D;
4 import java.awt.geom.Rectangle2D;
5 import java.util.LinkedList;
8 import org.simantics.db.common.utils.Logger;
9 import org.simantics.diagram.elements.EditorState.ModificationClass;
10 import org.simantics.g2d.canvas.ICanvasContext;
11 import org.simantics.g2d.element.IElement;
12 import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent;
13 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent;
14 import org.simantics.scenegraph.g2d.nodes.SingleElementNode;
15 import org.simantics.scl.runtime.function.Function1;
16 import org.simantics.scl.runtime.function.Function2;
18 import com.kitfox.svg.SVGDiagram;
19 import com.kitfox.svg.SVGElement;
20 import com.kitfox.svg.SVGException;
21 import com.kitfox.svg.Text;
22 import com.kitfox.svg.Tspan;
23 import com.kitfox.svg.animation.AnimationElement;
26 * @author Antti Villberg
29 class EditorStateManager {
31 static String TERM_STRING = "-----";
32 static String EDITOR_CLASS = "edit";
33 static String EDITOR_ID = "edit";
36 private LinkedList<EditorState> editorState = null;
37 private int editorStateIndex = 0;
39 EditorStateManager(SVGNode node) {
43 public boolean isEditMode() {
44 return editorState != null;
47 public EditorState currentState() {
48 return editorState.get(editorStateIndex);
51 public int currentHash() {
52 if(!isEditMode()) return 0;
53 return currentState().hashCode();
56 public void activateEditMode(SVGDiagram diagram, Text text) {
58 if(isEditMode()) return;
60 if(text.getId().length() == 0) return;
62 EditorState es = new EditorState();
63 es.base = new EditorStateStatic();
64 es.base.textElementId = text.getId();
66 Tspan span = (Tspan)text.getContent().get(0);
67 String currentText = span.getText();
69 SingleElementNode sne = node.getSingleElementNode();
73 Function1<String,String> fullTextFunction = sne.getParameter("textEditorFullText");
74 if(fullTextFunction != null)
75 es.currentText = fullTextFunction.apply(es.base.textElementId);
76 if(es.currentText == null) {
77 es.currentText = currentText;
80 es.caretPosition = es.currentText.length();
81 es.selectionOtherPosition = 0;
83 // Measure the Y-dimensions of the font
87 diagram.updateTime(0);
88 es.base.verticalDimensions = text.getBoundingBox();
89 span.setText(TERM_STRING);
91 diagram.updateTime(0);
92 es.base.termStringWidth = text.getBoundingBox().getWidth();
93 span.setText(currentText);
95 diagram.updateTime(0);
96 } catch (SVGException e) {
100 ICanvasContext ctx = DiagramNodeUtil.getCanvasContext(node);
101 IElement ie = DiagramNodeUtil.getElement(ctx, sne);
103 EditDataNode data = EditDataNode.getNode(node);
104 deactivateEdit(data, null);
105 TextEditActivation result = new TextEditActivation(0, ie, ctx);
106 data.setTextEditActivation(result);
108 editorState = new LinkedList<>();
109 editorState.push(es);
110 editorStateIndex = 0;
116 private TextEditActivation editActivation;
119 SingleElementNode sne = node.getSingleElementNode();
121 EditorState es = currentState();
122 Function2<String,String,Object> editor = sne.getParameter("textEditor");
124 editor.apply(es.base.textElementId, es.currentText);
129 protected boolean deactivateEdit() {
130 boolean result = deactivateEdit( editActivation );
131 result |= editActivation != null;
132 editActivation = null;
138 protected boolean deactivateEdit(TextEditActivation activation) {
139 return deactivateEdit( EditDataNode.getNode(node), activation );
142 protected boolean deactivateEdit(EditDataNode data, TextEditActivation activation) {
143 TextEditActivation previous = data.getTextEditActivation();
144 if (previous != null && (previous == activation || activation == null)) {
146 data.setTextEditActivation(null);
152 protected boolean keyPressed(KeyPressedEvent e) {
154 EditorState es = currentState();
155 EditorState nes = es.copy();
156 if(nes.keyPressed(this, e)) {
158 // This key actually terminated editing
161 if(nes.shouldReplace(es)) {
164 while(editorState.size() > (editorStateIndex + 1))
165 editorState.removeLast();
166 editorState.add(nes);
167 editorStateIndex = editorState.size() - 1;
176 public boolean tryToStartEditMode(SVGDiagram diagram) {
177 SVGElement element = diagram.getElement(EDITOR_ID);
178 if(element != null && element instanceof Text) {
179 activateEditMode(diagram, (Text)element);
185 public boolean tryToStartEditMode(SVGDiagram diagram, MouseClickEvent e) {
187 if(diagram != null) {
189 Point2D local = node.controlToLocal( e.controlPosition );
190 // FIXME: once the event coordinate systems are cleared up, remove this workaround
191 local = node.parentToLocal(local);
193 double tolerance = 2.0;
194 Rectangle2D pickRect = new Rectangle2D.Double(local.getX()-tolerance, local.getY()-tolerance, 2*tolerance, 2*tolerance);
197 List<?> retVec = diagram.pick(pickRect, null);
198 for(int i=0;i<retVec.size();i++) {
199 List<?> l = (List<?>)retVec.get(i);
200 for(int j=0;j<l.size();j++) {
201 SVGElement element = (SVGElement)l.get(j);
202 if(element instanceof Tspan) {
205 if(element instanceof Text) {
206 Text text = (Text)element;
207 if(text.hasAttribute("class", AnimationElement.AT_XML)) {
208 String clazz = text.getPresAbsolute("class").getStringValue();
209 if(clazz.contains(EDITOR_CLASS)) {
210 activateEditMode(diagram, text);
218 } catch (SVGException e1) {
219 Logger.defaultLogError(e1);
227 boolean applyEditMode(SVGDiagram diagram) throws SVGException {
230 EditorState es = currentState();
231 es.applyEditMode(diagram);
240 node.cleanDiagramCache();
245 while(editorStateIndex > 0 && currentState().modificationClass.equals(ModificationClass.NO_EDIT)) {
248 if(editorStateIndex > 0)
254 while(editorStateIndex < editorState.size() - 1 && currentState().modificationClass.equals(ModificationClass.NO_EDIT)) {
257 if(editorStateIndex < editorState.size() - 1) {