]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.diagram/src/org/simantics/diagram/elements/EditorStateManager.java
1223d8eaffe74fe606d0461d7b122676622736c2
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / elements / EditorStateManager.java
1 /*******************************************************************************
2  * Copyright (c) 2017 Association for Decentralized Information Management
3  * in Industry THTH ry.
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
8  *
9  * Contributors:
10  *     Semantum Oy - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.diagram.elements;
13
14 import java.awt.geom.Point2D;
15 import java.awt.geom.Rectangle2D;
16 import java.util.LinkedList;
17 import java.util.List;
18
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;
29
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;
36
37 /**
38  * @author Antti Villberg
39  * @since 1.31.0
40  */
41 class EditorStateManager {
42
43         static String TERM_STRING = "-----";
44         static String EDITOR_CLASS = "edit";
45         static String EDITOR_ID = "edit";
46         private SVGNode node;
47
48         private LinkedList<EditorState> editorState = null;
49         private int editorStateIndex = 0;
50
51         static class SVGMeasurementContextImpl implements SVGMeasurementContext {
52                 private SVGNode node;
53                 public SVGMeasurementContextImpl(SVGNode node) {
54                         this.node = node;
55                 }
56                 @Override
57                 public Tuple4 getBoundingBox(String id) {
58                         try {
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) {
63                                 return null;
64                         }
65                 }
66         }
67
68         EditorStateManager(SVGNode node) {
69                 this.node = node;
70         }
71
72         public boolean isEditMode() {
73                 return editorState != null;
74         }
75
76         public EditorState currentState() {
77                 return editorState.get(editorStateIndex);
78         }
79
80         public int currentHash() {
81                 if(!isEditMode()) return 0;
82                 return currentState().hashCode();
83         }
84
85         public void activateEditMode(SVGDiagram diagram, Text text) {
86
87                 if(isEditMode()) return;
88
89                 if(text.getId().length() == 0) return;
90
91                 EditorState es = new EditorState();
92                 es.base = new EditorStateStatic();
93                 es.base.textElementId = text.getId();
94
95                 Tspan span = (Tspan)text.getContent().get(0);
96                 String currentText = span.getText();
97
98                 SingleElementNode sne = node.getSingleElementNode();
99                 if (sne == null)
100                         return;
101
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;
107                 }
108
109                 es.caretPosition = es.currentText.length();
110                 es.selectionOtherPosition = 0;
111
112                 // Measure the Y-dimensions of the font
113                 try {
114                         span.setText("Ig");
115                         text.rebuild();
116                         diagram.updateTime(0);
117                         es.base.verticalDimensions = text.getBoundingBox(); 
118                         span.setText(TERM_STRING);
119                         text.rebuild();
120                         diagram.updateTime(0);
121                         es.base.termStringWidth = text.getBoundingBox().getWidth(); 
122                         span.setText(currentText);
123                         text.rebuild();
124                         diagram.updateTime(0);
125                 } catch (SVGException e) {
126                         e.printStackTrace();
127                 }
128
129                 ICanvasContext ctx = DiagramNodeUtil.getCanvasContext(node);
130                 IElement ie = DiagramNodeUtil.getElement(ctx, sne);
131
132                 EditDataNode data = EditDataNode.getNode(node);
133                 deactivateEdit(data, null);
134                 TextEditActivation result = new TextEditActivation(0, ie, ctx);
135                 data.setTextEditActivation(result);
136
137                 editorState = new LinkedList<>();
138                 editorState.push(es);
139                 editorStateIndex = 0;
140
141                 paint();
142
143         }
144
145         private TextEditActivation editActivation;
146
147         void applyEdit() {
148                 SingleElementNode sne = node.getSingleElementNode();
149                 if (sne != null) {
150                         EditorState es = currentState();
151                         Function3<SVGMeasurementContext,String,String,Object> editor = sne.getParameter("textEditor");
152                         if(editor != null) {
153                                 editor.apply(new SVGMeasurementContextImpl(node), es.base.textElementId, es.currentText);
154                         }
155                 }
156         }
157
158         protected boolean deactivateEdit() {
159                 boolean result = deactivateEdit( editActivation );
160                 result |= editActivation != null;
161                 editActivation = null;
162                 editorState = null;
163                 paint();
164                 return result;
165         }
166
167         protected boolean deactivateEdit(TextEditActivation activation) {
168                 return deactivateEdit( EditDataNode.getNode(node), activation );
169         }
170
171         protected boolean deactivateEdit(EditDataNode data, TextEditActivation activation) {
172                 TextEditActivation previous = data.getTextEditActivation();
173                 if (previous != null && (previous == activation || activation == null)) {
174                         previous.release();
175                         data.setTextEditActivation(null);
176                         return true;
177                 }
178                 return false;
179         }
180
181         protected boolean keyPressed(KeyPressedEvent e) {
182                 if(isEditMode()) {
183                         EditorState es = currentState();
184                         EditorState nes = es.copy();
185                         if(nes.keyPressed(this, e)) {
186                                 if(!isEditMode()) {
187                                         // This key actually terminated editing
188                                         return true;
189                                 }
190                                 if(nes.shouldReplace(es)) {
191                                         es.replace(nes);
192                                 } else {
193                                         while(editorState.size() > (editorStateIndex + 1))
194                                                 editorState.removeLast();
195                                         editorState.add(nes);
196                                         editorStateIndex = editorState.size() - 1;
197                                 }
198                                 return true; 
199                         }
200                 }
201                 return false;
202         }
203
204
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);
209                         return true;
210                 }
211                 return false;
212         }
213
214         public boolean tryToStartEditMode(SVGDiagram diagram, MouseClickEvent e) {
215
216                 if(diagram != null) {
217
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);
221
222                         double tolerance = 2.0;
223                         Rectangle2D pickRect = new Rectangle2D.Double(local.getX()-tolerance, local.getY()-tolerance, 2*tolerance, 2*tolerance); 
224
225                         try {
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) {
232                                                         return true;
233                                                 }
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);
240                                                                         return true;
241                                                                 }
242                                                         }
243                                                 }
244                                         }
245                                 }
246
247                         } catch (SVGException e1) {
248                                 Logger.defaultLogError(e1);
249                         }
250                 }
251
252                 return false;
253
254         }
255
256         boolean applyEditMode(SVGDiagram diagram) throws SVGException {
257
258                 if(isEditMode()) {
259                         EditorState es = currentState();
260                         es.applyEditMode(diagram);
261                         return true;
262                 }
263
264                 return false;
265
266         }
267
268         void paint() {
269                 node.cleanDiagramCache();
270                 node.repaint();
271         }
272
273         void undo() {
274                 while(editorStateIndex > 0 && currentState().modificationClass.equals(ModificationClass.NO_EDIT)) {
275                         editorStateIndex--;
276                 }
277                 if(editorStateIndex > 0)
278                         editorStateIndex--;
279                 paint();
280         }
281
282         void redo() {
283                 while(editorStateIndex < editorState.size() - 1 && currentState().modificationClass.equals(ModificationClass.NO_EDIT)) {
284                         editorStateIndex++;
285                 }
286                 if(editorStateIndex < editorState.size() - 1) {
287                         editorStateIndex++;
288                 }
289                 paint();
290         }
291
292 }