1 package org.simantics.diagram.elements;
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;
11 import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent;
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;
23 * @author Antti Villberg
28 enum ModificationClass {
29 SINGLE_INSERT, AREA_INSERT,SINGLE_DELETE,AREA_DELETE,NO_EDIT
32 EditorStateStatic base;
33 ModificationClass modificationClass = ModificationClass.NO_EDIT;
34 int caretPosition = -1;
35 int selectionOtherPosition = -1;
36 String currentText = null;
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);
47 private boolean editModeHasSelection() {
48 return selectionOtherPosition != -1;
51 private void editModeClearSelection() {
52 selectionOtherPosition = -1;
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());
60 editModeClearSelection();
63 public void applyEditMode(SVGDiagram diagram) throws SVGException {
65 Text text = (Text)diagram.getElement(base.textElementId);
66 Tspan span = (Tspan)text.getContent().get(0);
68 // Measure the X-dimensions of the font - append TERM_STRING to account for trailing whitespace
69 span.setText(currentText + EditorStateManager.TERM_STRING);
71 diagram.updateTime(0);
72 double textWidth = text.getBoundingBox().getWidth() - base.termStringWidth;
74 // Measure the caret position
75 span.setText(currentText.substring(0, caretPosition) + EditorStateManager.TERM_STRING);
77 diagram.updateTime(0);
78 double caretRectWidth = text.getBoundingBox().getWidth() - base.termStringWidth;
80 double selectionOtherWidth = 0;
81 if(selectionOtherPosition != -1) {
82 span.setText(currentText.substring(0, selectionOtherPosition) + EditorStateManager.TERM_STRING);
84 diagram.updateTime(0);
85 selectionOtherWidth = text.getBoundingBox().getWidth() - base.termStringWidth;
89 // Finally the actual text
90 span.setText(currentText);
92 diagram.updateTime(0);
93 Rectangle2D finalBB = text.getBoundingBox();
95 Group group = (Group)text.getParent();
96 Line line = new Line();
99 group.removeChild(text);
103 double minY = (base.verticalDimensions.getMinY()-1);
104 double height = (base.verticalDimensions.getHeight()+2);
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);
114 double caretX = finalBB.getMinX() + caretRectWidth;
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");
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");
132 group.loaderAddChild(null, selection);
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);
143 group.loaderAddChild(null, text);
149 diagram.updateTime(0);
153 boolean keyPressed(EditorStateManager esm, KeyPressedEvent e) {
154 boolean result = keyPressedInternal(esm, e);
155 if(selectionOtherPosition == caretPosition)
156 editModeClearSelection();
160 private void performDelete() {
161 if(editModeHasSelection()) {
162 deleteCurrentSelection();
163 modificationClass = ModificationClass.AREA_DELETE;
165 if(caretPosition < currentText.length()) {
166 currentText = currentText.substring(0, caretPosition) + currentText.substring(caretPosition+1, currentText.length());
168 modificationClass = ModificationClass.SINGLE_DELETE;
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);
179 boolean keyPressedInternal(EditorStateManager esm, KeyPressedEvent e) {
181 if(e.keyCode == java.awt.event.KeyEvent.VK_BACK_SPACE) {
182 if(editModeHasSelection()) {
183 deleteCurrentSelection();
184 modificationClass = ModificationClass.AREA_DELETE;
186 if(caretPosition > 0) {
187 currentText = currentText.substring(0, caretPosition-1) + currentText.substring(caretPosition, currentText.length());
190 modificationClass = ModificationClass.SINGLE_DELETE;
192 } else if (java.awt.event.KeyEvent.VK_DELETE == e.keyCode) {
194 } else if (java.awt.event.KeyEvent.VK_C == e.keyCode && e.isControlDown()) {
197 } else if (java.awt.event.KeyEvent.VK_X == e.keyCode && e.isControlDown()) {
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)) {
205 String text = clipboard.getData(dataFlavor).toString();
206 if(editModeHasSelection())
207 deleteCurrentSelection();
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) {
217 } else if (java.awt.event.KeyEvent.VK_A == e.keyCode && e.isControlDown()) {
219 selectionOtherPosition = currentText.length();
220 } else if (java.awt.event.KeyEvent.VK_Z == e.keyCode && e.isControlDown()) {
223 } else if (java.awt.event.KeyEvent.VK_Y == e.keyCode && e.isControlDown()) {
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;
233 editModeClearSelection();
235 if(e.isShiftDown() && !editModeHasSelection()) {
236 selectionOtherPosition = caretPosition;
238 if(caretPosition > 0) {
242 } else if (java.awt.event.KeyEvent.VK_RIGHT == e.keyCode) {
243 if(!e.isShiftDown() && editModeHasSelection()) {
244 if(selectionOtherPosition > caretPosition) {
245 caretPosition = selectionOtherPosition;
247 editModeClearSelection();
249 if(e.isShiftDown() && !editModeHasSelection()) {
250 selectionOtherPosition = caretPosition;
252 if(caretPosition < currentText.length()) {
256 } else if (java.awt.event.KeyEvent.VK_END == e.keyCode) {
257 if(e.isShiftDown()) {
258 if(!editModeHasSelection()) {
259 selectionOtherPosition = caretPosition;
262 editModeClearSelection();
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;
271 editModeClearSelection();
274 } else if (java.awt.event.KeyEvent.VK_ENTER == e.keyCode) {
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());
282 modificationClass = ModificationClass.SINGLE_INSERT;
293 void replace(EditorState other) {
295 caretPosition = other.caretPosition;
296 currentText = other.currentText;
297 selectionOtherPosition = other.selectionOtherPosition;
300 boolean shouldReplace(EditorState comparedTo) {
301 return modificationClass.equals(comparedTo.modificationClass);
305 EditorState result = new EditorState();
306 result.replace(this);
310 private boolean isAllowedCharacter(KeyPressedEvent e) {
311 char c = e.character;
312 if (c == 65535 || Character.getType(c) == Character.CONTROL) {
319 public int hashCode() {
320 final int prime = 31;
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;
331 public boolean equals(Object obj) {
336 if (getClass() != obj.getClass())
338 EditorState other = (EditorState) obj;
340 if (other.base != null)
342 } else if (!base.equals(other.base))
344 if (caretPosition != other.caretPosition)
346 if (currentText == null) {
347 if (other.currentText != null)
349 } else if (!currentText.equals(other.currentText))
351 if (modificationClass != other.modificationClass)
353 if (selectionOtherPosition != other.selectionOtherPosition)