]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.diagram/src/org/simantics/diagram/elements/EditorState.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / elements / EditorState.java
1 package org.simantics.diagram.elements;
2
3 import java.awt.Toolkit;
4 import java.awt.datatransfer.Clipboard;
5 import java.awt.datatransfer.DataFlavor;
6 import java.awt.datatransfer.StringSelection;
7 import java.awt.datatransfer.UnsupportedFlavorException;
8 import java.awt.geom.Rectangle2D;
9 import java.io.IOException;
10
11 import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent;
12
13 import com.kitfox.svg.Group;
14 import com.kitfox.svg.Line;
15 import com.kitfox.svg.Rect;
16 import com.kitfox.svg.SVGDiagram;
17 import com.kitfox.svg.SVGException;
18 import com.kitfox.svg.Text;
19 import com.kitfox.svg.Tspan;
20 import com.kitfox.svg.animation.AnimationElement;
21
22 /**
23  * @author Antti Villberg
24  * @since 1.31.0
25  */
26 class EditorState {
27
28         enum ModificationClass {
29                 SINGLE_INSERT, AREA_INSERT,SINGLE_DELETE,AREA_DELETE,NO_EDIT
30         }
31
32         EditorStateStatic base;
33         ModificationClass modificationClass = ModificationClass.NO_EDIT;
34         int caretPosition = -1;
35         int selectionOtherPosition = -1;
36         String currentText = null;
37
38         private String selectedText() {
39                 if(editModeHasSelection()) {
40                         int min = Math.min(caretPosition, selectionOtherPosition);
41                         int max = Math.max(caretPosition, selectionOtherPosition);
42                         return currentText.substring(min, max);
43                 }
44                 return null;
45         }
46
47         private boolean editModeHasSelection() {
48                 return selectionOtherPosition != -1;
49         }
50
51         private void editModeClearSelection() {
52                 selectionOtherPosition = -1;
53         }
54
55         private void deleteCurrentSelection() {
56                 int min = Math.min(caretPosition, selectionOtherPosition);
57                 int max = Math.max(caretPosition, selectionOtherPosition);
58                 currentText = currentText.substring(0, min) + currentText.substring(max, currentText.length());
59                 caretPosition = min;
60                 editModeClearSelection();
61         }
62
63         public void applyEditMode(SVGDiagram diagram) throws SVGException {
64
65                 Text text = (Text)diagram.getElement(base.textElementId);
66                 Tspan span = (Tspan)text.getContent().get(0);
67
68                 // Measure the X-dimensions of the font - append TERM_STRING to account for trailing whitespace
69                 span.setText(currentText + EditorStateManager.TERM_STRING);
70                 text.rebuild();
71                 diagram.updateTime(0);
72                 double textWidth = text.getBoundingBox().getWidth() - base.termStringWidth;
73
74                 // Measure the caret position
75                 span.setText(currentText.substring(0, caretPosition) + EditorStateManager.TERM_STRING);
76                 text.rebuild();
77                 diagram.updateTime(0);
78                 double caretRectWidth = text.getBoundingBox().getWidth() - base.termStringWidth;
79
80                 double selectionOtherWidth = 0;
81                 if(selectionOtherPosition != -1) {
82                         span.setText(currentText.substring(0, selectionOtherPosition) +  EditorStateManager.TERM_STRING);
83                         text.rebuild();
84                         diagram.updateTime(0);
85                         selectionOtherWidth = text.getBoundingBox().getWidth() - base.termStringWidth;
86                 }
87
88
89                 // Finally the actual text
90                 span.setText(currentText);
91                 text.rebuild();
92                 diagram.updateTime(0);
93                 Rectangle2D finalBB = text.getBoundingBox();
94
95                 Group group = (Group)text.getParent();
96                 Line line = new Line();
97                 try { 
98
99                         group.removeChild(text);
100
101                         double xPadding = 0;
102
103                         double minY = (base.verticalDimensions.getMinY()-1);
104                         double height = (base.verticalDimensions.getHeight()+2);
105
106                         Rect rect = new Rect();
107                         rect.addAttribute("x", AnimationElement.AT_XML, "" + (finalBB.getMinX()-xPadding));
108                         rect.addAttribute("y", AnimationElement.AT_XML, "" + minY);
109                         rect.addAttribute("width", AnimationElement.AT_XML, "" + (textWidth+xPadding));
110                         rect.addAttribute("height", AnimationElement.AT_XML, "" + height);
111                         rect.addAttribute("fill", AnimationElement.AT_XML, "#ccc");
112                         group.loaderAddChild(null, rect);
113
114                         double caretX = finalBB.getMinX() + caretRectWidth;
115
116                         if(selectionOtherPosition != -1) {
117                                 double selectionX = finalBB.getMinX() + selectionOtherWidth;
118                                 Rect selection = new Rect();
119                                 if(selectionOtherPosition < caretPosition) {
120                                         selection.addAttribute("x", AnimationElement.AT_XML, "" + selectionX);
121                                         selection.addAttribute("y", AnimationElement.AT_XML, "" + minY);
122                                         selection.addAttribute("width", AnimationElement.AT_XML, "" + (caretX-selectionX));
123                                         selection.addAttribute("height", AnimationElement.AT_XML, "" + height);
124                                         selection.addAttribute("fill", AnimationElement.AT_XML, "#888");
125                                 } else {
126                                         selection.addAttribute("x", AnimationElement.AT_XML, "" + caretX);
127                                         selection.addAttribute("y", AnimationElement.AT_XML, "" + minY);
128                                         selection.addAttribute("width", AnimationElement.AT_XML, "" + (selectionX-caretX));
129                                         selection.addAttribute("height", AnimationElement.AT_XML, "" + height);
130                                         selection.addAttribute("fill", AnimationElement.AT_XML, "#888");
131                                 }
132                                 group.loaderAddChild(null, selection);
133                         }
134
135                         line.addAttribute("x1", AnimationElement.AT_XML, "" + caretX);
136                         line.addAttribute("x2", AnimationElement.AT_XML, "" + caretX);
137                         line.addAttribute("y1", AnimationElement.AT_XML, "" + (base.verticalDimensions.getMinY()-1));
138                         line.addAttribute("y2", AnimationElement.AT_XML, "" + (base.verticalDimensions.getMaxY()+1));
139                         line.addAttribute("stroke", AnimationElement.AT_XML, "black");
140                         line.addAttribute("stroke-width", AnimationElement.AT_XML, "0.5");
141                         group.loaderAddChild(null, line);
142
143                         group.loaderAddChild(null, text);
144
145                 } finally {
146
147                 }
148
149                 diagram.updateTime(0);
150
151         }
152
153         boolean keyPressed(EditorStateManager esm, KeyPressedEvent e) {
154                 boolean result = keyPressedInternal(esm, e);
155                 if(selectionOtherPosition == caretPosition)
156                         editModeClearSelection();
157                 return result;
158         }
159
160         private void performDelete() {
161                 if(editModeHasSelection()) {
162                         deleteCurrentSelection();
163                         modificationClass = ModificationClass.AREA_DELETE;
164                 } else {
165                         if(caretPosition < currentText.length()) {
166                                 currentText = currentText.substring(0, caretPosition) + currentText.substring(caretPosition+1, currentText.length());
167                         }
168                         modificationClass = ModificationClass.SINGLE_DELETE;
169                 }
170         }
171
172         private void performCopy() {
173                 String selection = selectedText();
174                 if(selection == null) return;
175                 Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
176                 clipboard.setContents(new StringSelection(selection), null);
177         }
178
179         boolean keyPressedInternal(EditorStateManager esm, KeyPressedEvent e) {
180
181                 if(e.keyCode == java.awt.event.KeyEvent.VK_BACK_SPACE) {
182                         if(editModeHasSelection()) {
183                                 deleteCurrentSelection();
184                                 modificationClass = ModificationClass.AREA_DELETE;
185                         } else {
186                                 if(caretPosition > 0) {
187                                         currentText = currentText.substring(0, caretPosition-1) + currentText.substring(caretPosition, currentText.length());
188                                         caretPosition--;
189                                 }
190                                 modificationClass = ModificationClass.SINGLE_DELETE;
191                         }
192                 } else if (java.awt.event.KeyEvent.VK_DELETE == e.keyCode) {
193                         performDelete();
194                 } else if (java.awt.event.KeyEvent.VK_C == e.keyCode && e.isControlDown()) {
195                         performCopy();
196                         return false;
197                 } else if (java.awt.event.KeyEvent.VK_X == e.keyCode && e.isControlDown()) {
198                         performCopy();
199                         performDelete();
200                 } else if (java.awt.event.KeyEvent.VK_V == e.keyCode && e.isControlDown()) {
201                         Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
202                         DataFlavor dataFlavor = DataFlavor.stringFlavor;
203                         if (clipboard.isDataFlavorAvailable(dataFlavor)) {
204                                 try {
205                                         String text = clipboard.getData(dataFlavor).toString();
206                                         if(editModeHasSelection())
207                                                 deleteCurrentSelection();
208
209                                         currentText = currentText.substring(0, caretPosition) + text + currentText.substring(caretPosition, currentText.length());
210                                         caretPosition += text.length();
211                                         modificationClass = ModificationClass.AREA_INSERT;
212                                 } catch (UnsupportedFlavorException | IOException e1) {
213                                 }
214                         } else {
215                                 return false;
216                         }
217                 } else if (java.awt.event.KeyEvent.VK_A == e.keyCode && e.isControlDown()) {
218                         caretPosition = 0;
219                         selectionOtherPosition = currentText.length();
220                 } else if (java.awt.event.KeyEvent.VK_Z == e.keyCode && e.isControlDown()) {
221                         esm.undo();
222                         return false;
223                 } else if (java.awt.event.KeyEvent.VK_Y == e.keyCode && e.isControlDown()) {
224                         esm.redo();
225                         return false;
226                 } else if (java.awt.event.KeyEvent.VK_ESCAPE == e.keyCode) {
227                         esm.deactivateEdit();
228                 } else if (java.awt.event.KeyEvent.VK_LEFT == e.keyCode) {
229                         if(!e.isShiftDown() && editModeHasSelection()) {
230                                 if(selectionOtherPosition < caretPosition) {
231                                         caretPosition = selectionOtherPosition;
232                                 }
233                                 editModeClearSelection();
234                         } else {
235                                 if(e.isShiftDown() && !editModeHasSelection()) {
236                                         selectionOtherPosition = caretPosition;
237                                 }
238                                 if(caretPosition > 0) {
239                                         caretPosition--;
240                                 }
241                         }
242                 } else if (java.awt.event.KeyEvent.VK_RIGHT == e.keyCode) {
243                         if(!e.isShiftDown() && editModeHasSelection()) {
244                                 if(selectionOtherPosition > caretPosition) {
245                                         caretPosition = selectionOtherPosition;
246                                 }
247                                 editModeClearSelection();
248                         } else {
249                                 if(e.isShiftDown() && !editModeHasSelection()) {
250                                         selectionOtherPosition = caretPosition;
251                                 }
252                                 if(caretPosition < currentText.length()) {
253                                         caretPosition++;
254                                 }
255                         }
256                 } else if (java.awt.event.KeyEvent.VK_END == e.keyCode) {
257                         if(e.isShiftDown()) {
258                                 if(!editModeHasSelection()) {
259                                         selectionOtherPosition = caretPosition;
260                                 }
261                         } else {
262                                 editModeClearSelection();
263                         }
264                         caretPosition = currentText.length();
265                 } else if (java.awt.event.KeyEvent.VK_HOME == e.keyCode) {
266                         if(e.isShiftDown()) {
267                                 if(!editModeHasSelection()) {
268                                         selectionOtherPosition = caretPosition;
269                                 }
270                         } else {
271                                 editModeClearSelection();
272                         }
273                         caretPosition = 0;
274                 } else if (java.awt.event.KeyEvent.VK_ENTER == e.keyCode) {
275                         esm.applyEdit();
276                         esm.deactivateEdit();
277                 } else if(isAllowedCharacter(e)) {
278                         if(editModeHasSelection())
279                                 deleteCurrentSelection();
280                         currentText = currentText.substring(0, caretPosition) + e.character + currentText.substring(caretPosition, currentText.length());
281                         caretPosition++;
282                         modificationClass = ModificationClass.SINGLE_INSERT;
283                 } else {
284                         return false;
285                 }
286
287                 esm.paint();
288
289                 return true;
290
291         }
292
293         void replace(EditorState other) {
294                 base = other.base;
295                 caretPosition = other.caretPosition;
296                 currentText = other.currentText;
297                 selectionOtherPosition = other.selectionOtherPosition;
298         }
299
300         boolean shouldReplace(EditorState comparedTo) {
301                 return modificationClass.equals(comparedTo.modificationClass);
302         }
303
304         EditorState copy() {
305                 EditorState result = new EditorState();
306                 result.replace(this);
307                 return result;
308         }
309
310         private boolean isAllowedCharacter(KeyPressedEvent e) {
311                 char c = e.character;
312                 if (c == 65535 || Character.getType(c) == Character.CONTROL) {
313                         return false;
314                 }
315                 return true;
316         }
317
318         @Override
319         public int hashCode() {
320                 final int prime = 31;
321                 int result = 1;
322                 result = prime * result + ((base == null) ? 0 : base.hashCode());
323                 result = prime * result + caretPosition;
324                 result = prime * result + ((currentText == null) ? 0 : currentText.hashCode());
325                 result = prime * result + ((modificationClass == null) ? 0 : modificationClass.hashCode());
326                 result = prime * result + selectionOtherPosition;
327                 return result;
328         }
329
330         @Override
331         public boolean equals(Object obj) {
332                 if (this == obj)
333                         return true;
334                 if (obj == null)
335                         return false;
336                 if (getClass() != obj.getClass())
337                         return false;
338                 EditorState other = (EditorState) obj;
339                 if (base == null) {
340                         if (other.base != null)
341                                 return false;
342                 } else if (!base.equals(other.base))
343                         return false;
344                 if (caretPosition != other.caretPosition)
345                         return false;
346                 if (currentText == null) {
347                         if (other.currentText != null)
348                                 return false;
349                 } else if (!currentText.equals(other.currentText))
350                         return false;
351                 if (modificationClass != other.modificationClass)
352                         return false;
353                 if (selectionOtherPosition != other.selectionOtherPosition)
354                         return false;
355                 return true;
356         }
357
358 }