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();
70 Function1<String,String> fullTextFunction = sne.getParameter("textEditorFullText");
71 if(fullTextFunction != null)
72 es.currentText = fullTextFunction.apply(es.base.textElementId);
73 if(es.currentText == null) {
74 es.currentText = currentText;
77 es.caretPosition = es.currentText.length();
78 es.selectionOtherPosition = 0;
80 // Measure the Y-dimensions of the font
84 diagram.updateTime(0);
85 es.base.verticalDimensions = text.getBoundingBox();
86 span.setText(TERM_STRING);
88 diagram.updateTime(0);
89 es.base.termStringWidth = text.getBoundingBox().getWidth();
90 span.setText(currentText);
92 diagram.updateTime(0);
93 } catch (SVGException e) {
97 ICanvasContext ctx = DiagramNodeUtil.getCanvasContext(node);
98 IElement ie = DiagramNodeUtil.getElement(ctx, sne);
100 EditDataNode data = EditDataNode.getNode(node);
101 deactivateEdit(data, null);
102 TextEditActivation result = new TextEditActivation(0, ie, ctx);
103 data.setTextEditActivation(result);
105 editorState = new LinkedList<>();
106 editorState.push(es);
107 editorStateIndex = 0;
113 private TextEditActivation editActivation;
116 SingleElementNode sne = node.getSingleElementNode();
117 EditorState es = currentState();
118 Function2<String,String,Object> editor = sne.getParameter("textEditor");
120 editor.apply(es.base.textElementId, es.currentText);
124 protected boolean deactivateEdit() {
125 boolean result = deactivateEdit( editActivation );
126 result |= editActivation != null;
127 editActivation = null;
133 protected boolean deactivateEdit(TextEditActivation activation) {
134 return deactivateEdit( EditDataNode.getNode(node), activation );
137 protected boolean deactivateEdit(EditDataNode data, TextEditActivation activation) {
138 TextEditActivation previous = data.getTextEditActivation();
139 if (previous != null && (previous == activation || activation == null)) {
141 data.setTextEditActivation(null);
147 protected boolean keyPressed(KeyPressedEvent e) {
149 EditorState es = currentState();
150 EditorState nes = es.copy();
151 if(nes.keyPressed(this, e)) {
153 // This key actually terminated editing
156 if(nes.shouldReplace(es)) {
159 while(editorState.size() > (editorStateIndex + 1))
160 editorState.removeLast();
161 editorState.add(nes);
162 editorStateIndex = editorState.size() - 1;
171 public boolean tryToStartEditMode(SVGDiagram diagram) {
172 SVGElement element = diagram.getElement(EDITOR_ID);
173 if(element != null && element instanceof Text) {
174 activateEditMode(diagram, (Text)element);
180 public boolean tryToStartEditMode(SVGDiagram diagram, MouseClickEvent e) {
182 if(diagram != null) {
184 Point2D local = node.controlToLocal( e.controlPosition );
185 // FIXME: once the event coordinate systems are cleared up, remove this workaround
186 local = node.parentToLocal(local);
188 double tolerance = 2.0;
189 Rectangle2D pickRect = new Rectangle2D.Double(local.getX()-tolerance, local.getY()-tolerance, 2*tolerance, 2*tolerance);
192 List<?> retVec = diagram.pick(pickRect, null);
193 for(int i=0;i<retVec.size();i++) {
194 List<?> l = (List<?>)retVec.get(i);
195 for(int j=0;j<l.size();j++) {
196 SVGElement element = (SVGElement)l.get(j);
197 if(element instanceof Tspan) {
200 if(element instanceof Text) {
201 Text text = (Text)element;
202 if(text.hasAttribute("class", AnimationElement.AT_XML)) {
203 String clazz = text.getPresAbsolute("class").getStringValue();
204 if(clazz.contains(EDITOR_CLASS)) {
205 activateEditMode(diagram, text);
213 } catch (SVGException e1) {
214 Logger.defaultLogError(e1);
222 boolean applyEditMode(SVGDiagram diagram) throws SVGException {
225 EditorState es = currentState();
226 es.applyEditMode(diagram);
235 node.cleanDiagramCache();
240 while(editorStateIndex > 0 && currentState().modificationClass.equals(ModificationClass.NO_EDIT)) {
243 if(editorStateIndex > 0)
249 while(editorStateIndex < editorState.size() - 1 && currentState().modificationClass.equals(ModificationClass.NO_EDIT)) {
252 if(editorStateIndex < editorState.size() - 1) {