Browse Source

Added cut/copy/paste support.

Stan Hebben 6 years ago
parent
commit
7f9d5b3a7f

+ 33
- 0
Constructor/libraries/stdlib/src/Arrays.zs View File

@@ -19,6 +19,7 @@ export expand <T> T[] {
19 19
 	
20 20
 	public get first as T?;
21 21
 	public get last as T?;
22
+	public get reversed as T[];
22 23
 	
23 24
 	public map<U>(projection as function(value as T) as U) as U[] {
24 25
 		val result as U[] = new U[](this.length);
@@ -79,6 +80,38 @@ export expand <T> T[] {
79 80
 		return true;
80 81
 	}
81 82
 	
83
+	public first(predicate as function(value as T) as bool) as T? {
84
+		for value in this
85
+			if predicate(value)
86
+				return value;
87
+		
88
+		return null;
89
+	}
90
+	
91
+	public first(predicate as function(i as int, value as T) as bool) as T? {
92
+		for i, value in this
93
+			if predicate(i, value)
94
+				return value;
95
+		
96
+		return null;
97
+	}
98
+	
99
+	public last(predicate as function(value as T) as bool) as T? {
100
+		for i, value in this.reversed
101
+			if predicate(value)
102
+				return value;
103
+		
104
+		return null;
105
+	}
106
+	
107
+	public last(predicate as function(value as T) as bool) as T? {
108
+		for i, value in this.reversed
109
+			if predicate(value)
110
+				return value
111
+		
112
+		return null;
113
+	}
114
+	
82 115
 	public count(predicate as function(value as T) as bool) as int {
83 116
 		var result = 0;
84 117
 		for value in this

+ 16
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DClipboard.java View File

@@ -0,0 +1,16 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui;
7
+
8
+/**
9
+ *
10
+ * @author Hoofdgebruiker
11
+ */
12
+public interface DClipboard {
13
+	void copyAsString(String value);
14
+	
15
+	String getAsString();
16
+}

+ 2
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DDrawingContext.java View File

@@ -22,6 +22,8 @@ public interface DDrawingContext {
22 22
 	
23 23
 	DTimerHandle setTimer(int millis, Runnable target);
24 24
 	
25
+	DClipboard getClipboard();
26
+	
25 27
 	DFontMetrics getFontMetrics(DFont font);
26 28
 	
27 29
 	enum Cursor {

+ 6
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/scroll/DScrollPane.java View File

@@ -7,6 +7,7 @@ package org.openzen.drawablegui.scroll;
7 7
 
8 8
 import org.openzen.drawablegui.DRectangle;
9 9
 import org.openzen.drawablegui.DCanvas;
10
+import org.openzen.drawablegui.DClipboard;
10 11
 import org.openzen.drawablegui.DComponent;
11 12
 import org.openzen.drawablegui.DDimensionPreferences;
12 13
 import org.openzen.drawablegui.DDrawingContext;
@@ -254,6 +255,11 @@ public class DScrollPane implements DComponent {
254 255
 		public DTimerHandle setTimer(int millis, Runnable target) {
255 256
 			return context.setTimer(millis, target);
256 257
 		}
258
+
259
+		@Override
260
+		public DClipboard getClipboard() {
261
+			return context.getClipboard();
262
+		}
257 263
 	}
258 264
 	
259 265
 	private class ScrollListener implements LiveInt.Listener {

+ 46
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/swing/JavaClipboard.java View File

@@ -0,0 +1,46 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui.swing;
7
+
8
+import java.awt.Toolkit;
9
+import java.awt.datatransfer.Clipboard;
10
+import java.awt.datatransfer.DataFlavor;
11
+import java.awt.datatransfer.StringSelection;
12
+import java.awt.datatransfer.Transferable;
13
+import java.awt.datatransfer.UnsupportedFlavorException;
14
+import java.io.IOException;
15
+import org.openzen.drawablegui.DClipboard;
16
+
17
+/**
18
+ *
19
+ * @author Hoofdgebruiker
20
+ */
21
+public class JavaClipboard implements DClipboard {
22
+	private final Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
23
+
24
+	@Override
25
+	public void copyAsString(String value) {
26
+		StringSelection stringSelection = new StringSelection(value);
27
+		clipboard.setContents(stringSelection, null);
28
+	}
29
+
30
+	@Override
31
+	public String getAsString() {
32
+		Transferable contents = clipboard.getContents(null);
33
+		if (contents == null)
34
+			return null;
35
+		
36
+		if (!contents.isDataFlavorSupported(DataFlavor.stringFlavor))
37
+			return null;
38
+		
39
+		try {
40
+			return (String)contents.getTransferData(DataFlavor.stringFlavor);
41
+		} catch (UnsupportedFlavorException | IOException ex) {
42
+			ex.printStackTrace();
43
+			return null;
44
+		}
45
+	}
46
+}

+ 7
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/swing/SwingGraphicsContext.java View File

@@ -10,6 +10,7 @@ import java.awt.Graphics;
10 10
 import java.awt.geom.GeneralPath;
11 11
 import java.util.WeakHashMap;
12 12
 import javax.swing.Timer;
13
+import org.openzen.drawablegui.DClipboard;
13 14
 import org.openzen.drawablegui.DComponent;
14 15
 import org.openzen.drawablegui.DPath;
15 16
 import org.openzen.drawablegui.DDrawingContext;
@@ -26,6 +27,7 @@ public class SwingGraphicsContext implements DDrawingContext {
26 27
 	public final float scale;
27 28
 	private final WeakHashMap<DPath, GeneralPath> preparedPaths = new WeakHashMap<>();
28 29
 	private final SwingRoot root;
30
+	private final JavaClipboard clipboard = new JavaClipboard();
29 31
 	private Graphics graphics;
30 32
 	
31 33
 	public SwingGraphicsContext(float scale, SwingRoot root) {
@@ -110,6 +112,11 @@ public class SwingGraphicsContext implements DDrawingContext {
110 112
 		timer.start();
111 113
 		return () -> timer.stop();
112 114
 	}
115
+
116
+	@Override
117
+	public DClipboard getClipboard() {
118
+		return clipboard;
119
+	}
113 120
 	
114 121
 	private class PathTracer implements DPathTracer {
115 122
 		private final GeneralPath path;

+ 61
- 8
IDE/src/main/java/org/openzen/zenscript/ide/ui/view/editor/SourceEditor.java View File

@@ -314,12 +314,50 @@ public class SourceEditor implements DComponent {
314 314
 	
315 315
 	private void handleShortcut(DKeyEvent e) {
316 316
 		if (e.has(DKeyEvent.CTRL)) {
317
-			if (e.keyCode == KeyCode.S) {
318
-				save();
317
+			switch (e.keyCode) {
318
+				case S:
319
+					save();
320
+					break;
321
+				case C:
322
+					copy();
323
+					break;
324
+				case X:
325
+					cut();
326
+					break;
327
+				case V:
328
+					paste();
329
+					break;
319 330
 			}
320 331
 		}
321 332
 	}
322 333
 	
334
+	private void copy() {
335
+		String extract = tokens.extract(
336
+				SourcePosition.min(cursorStart, cursorEnd),
337
+				SourcePosition.max(cursorStart, cursorEnd));
338
+		context.getClipboard().copyAsString(extract);
339
+	}
340
+	
341
+	private void cut() {
342
+		copy();
343
+		tokens.delete(cursorStart, cursorEnd);
344
+		
345
+		SourcePosition cursor = SourcePosition.min(cursorStart, cursorEnd);
346
+		setCursor(cursor, cursor);
347
+	}
348
+	
349
+	private void paste() {
350
+		String text = context.getClipboard().getAsString();
351
+		if (text == null)
352
+			return;
353
+		
354
+		deleteSelection();
355
+		tokens.insert(cursorEnd, text);
356
+		
357
+		SourcePosition cursor = cursorEnd.advance(text.length());
358
+		setCursor(cursor, cursor);
359
+	}
360
+	
323 361
 	private void save() {
324 362
 		String content = tokens.toString();
325 363
 		sourceFile.update(content);
@@ -339,15 +377,26 @@ public class SourceEditor implements DComponent {
339 377
 		tokens.deleteCharacter(cursorEnd.line, cursorEnd.offset);
340 378
 	}
341 379
 	
342
-	private void backspace() {
343
-		if (!cursorEnd.equals(cursorStart)) {
380
+	private boolean hasSelection() {
381
+		return !cursorEnd.equals(cursorStart);
382
+	}
383
+	
384
+	private boolean deleteSelection() {
385
+		if (hasSelection()) {
344 386
 			SourcePosition min = SourcePosition.min(cursorStart, cursorEnd);
345 387
 			SourcePosition max = SourcePosition.max(cursorStart, cursorEnd);
346
-			tokens.delete(min.line, min.offset, max.line, max.offset);
388
+			tokens.delete(min, max);
347 389
 			setCursor(min, min);
348
-			return;
390
+			return true;
349 391
 		}
350 392
 		
393
+		return false;
394
+	}
395
+	
396
+	private void backspace() {
397
+		if (deleteSelection())
398
+			return;
399
+		
351 400
 		if (cursorEnd.offset == 0) {
352 401
 			if (cursorEnd.line == 0)
353 402
 				return;
@@ -370,14 +419,18 @@ public class SourceEditor implements DComponent {
370 419
 	}
371 420
 	
372 421
 	private void type(String value) {
373
-		tokens.insert(cursorEnd.line, cursorEnd.offset, value);
422
+		deleteSelection();
423
+		
424
+		tokens.insert(cursorEnd, value);
374 425
 		SourcePosition position = new SourcePosition(tokens, cursorEnd.line, cursorEnd.offset + value.length());
375 426
 		setCursor(position, position);
376 427
 	}
377 428
 	
378 429
 	private void newline() {
430
+		deleteSelection();
431
+		
379 432
 		String indent = tokens.getLine(cursorEnd.line).getIndent();
380
-		tokens.insert(cursorEnd.line, cursorEnd.offset, "\n" + indent);
433
+		tokens.insert(cursorEnd, "\n" + indent);
381 434
 		SourcePosition position = new SourcePosition(tokens, cursorEnd.line + 1, indent.length());
382 435
 		setCursor(position, position);
383 436
 	}

+ 20
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/view/editor/SourcePosition.java View File

@@ -23,6 +23,26 @@ public class SourcePosition {
23 23
 		this.offset = offset;
24 24
 	}
25 25
 	
26
+	public SourcePosition advance(int characters) {
27
+		if (characters <= 0) {
28
+			throw new IllegalArgumentException("Characters must be >= 0");
29
+		} else if (characters == 0) {
30
+			return this;
31
+		} else {
32
+			int line = this.line;
33
+			int offset = this.offset;
34
+			while (offset + characters > tokens.getLineLength(line) && line < tokens.getLineCount() - 1) {
35
+				characters -= tokens.getLineLength(line) - offset + 1; // make sure to include the newline
36
+				offset = 0;
37
+				line++;
38
+			}
39
+			if (line >= tokens.getLineCount() -1)
40
+				return new SourcePosition(tokens, tokens.getLineCount() - 1, tokens.getLineLength(tokens.getLineCount() - 1));
41
+			
42
+			return new SourcePosition(tokens, line, offset + characters);
43
+		}
44
+	}
45
+	
26 46
 	public static SourcePosition min(SourcePosition a, SourcePosition b) {
27 47
 		if (a.line < b.line)
28 48
 			return a;

+ 69
- 31
IDE/src/main/java/org/openzen/zenscript/ide/ui/view/editor/TokenModel.java View File

@@ -89,6 +89,55 @@ public class TokenModel {
89 89
 		return new Position(line, tokenLine.getTokenCount(), 0);
90 90
 	}
91 91
 	
92
+	public String extract(SourcePosition from, SourcePosition to) {
93
+		Position fromT = from.asTokenPosition();
94
+		Position toT = to.asTokenPosition();
95
+		if (from.line == to.line) {
96
+			StringBuilder result = new StringBuilder();
97
+			ZSToken fromToken = getTokenAt(fromT);
98
+			if (fromToken != null)
99
+				result.append(fromToken.getContent().substring(fromT.offset));
100
+			
101
+			TokenLine line = getLine(from.line);
102
+			for (int i = fromT.token + 1; i < toT.token; i++)
103
+				result.append(line.getToken(i).content);
104
+			
105
+			ZSToken toToken = getTokenAt(toT);
106
+			if (toToken != null)
107
+				result.append(toToken.content.substring(0, toT.offset));
108
+			
109
+			return result.toString();
110
+		} else {
111
+			StringBuilder result = new StringBuilder();
112
+			ZSToken fromToken = getTokenAt(fromT);
113
+			if (fromToken != null)
114
+				result.append(fromToken.getContent().substring(fromT.offset));
115
+			
116
+			TokenLine fromLine = getLine(from.line);
117
+			for (int i = fromT.token + 1; i < fromLine.getTokenCount(); i++)
118
+				result.append(fromLine.getToken(i).content);
119
+			
120
+			for (int i = fromT.line + 1; i < toT.line; i++) {
121
+				result.append("\n");
122
+				for (ZSToken t : fromLine.getTokens()) {
123
+					result.append(t.content);
124
+				}
125
+			}
126
+			
127
+			result.append("\n");
128
+			
129
+			TokenLine toLine = getLine(to.line);
130
+			for (int i = 0; i < toT.token; i++)
131
+				result.append(toLine.getToken(i).content);
132
+			
133
+			ZSToken toToken = getTokenAt(toT);
134
+			if (toToken != null)
135
+				result.append(toToken.content.substring(0, toT.offset));
136
+			
137
+			return result.toString();
138
+		}
139
+	}
140
+	
92 141
 	public void set(Iterator<ZSToken> tokens) {
93 142
 		lines.clear();
94 143
 		lines.add(new TokenLine());
@@ -127,48 +176,37 @@ public class TokenModel {
127 176
 		}
128 177
 	}
129 178
 	
130
-	public void delete(int fromLineIndex, int fromOffset, int toLineIndex, int toOffset) {
131
-		Position from = getPosition(fromLineIndex, fromOffset);
132
-		Position to = getPosition(toLineIndex, toOffset);
179
+	public void delete(SourcePosition from, SourcePosition to) {
180
+		Position fromT = from.asTokenPosition();
181
+		Position toT = to.asTokenPosition();
133 182
 		
134
-		ZSToken fromToken = getTokenAt(from);
135
-		ZSToken toToken = getTokenAt(to);
183
+		ZSToken fromToken = getTokenAt(fromT);
184
+		ZSToken toToken = getTokenAt(toT);
136 185
 		
137 186
 		String remainder = "";
138 187
 		if (fromToken != null)
139
-			remainder = fromToken.content.substring(0, from.offset);
140
-		if (toToken != null && to.offset > 0)
141
-			remainder += toToken.content.substring(to.offset);
188
+			remainder = fromToken.content.substring(0, fromT.offset);
189
+		if (toToken != null && toT.offset > 0)
190
+			remainder += toToken.content.substring(toT.offset);
142 191
 		
143
-		removeTokens(from.line, from.token, to.line, to.offset > 0 ? to.token + 1 : to.token);
144
-		if (!remainder.isEmpty()) {
145
-			getLine(from.line).insert(from.token, new ZSToken(ZSTokenType.INVALID, remainder));
146
-			relex(from.line, Math.max(0, from.token - 1), from.line, from.token + 1);
147
-		}
192
+		removeTokens(fromT.line, fromT.token, toT.line, toT.offset > 0 ? toT.token + 1 : toT.token);
193
+		if (!remainder.isEmpty())
194
+			getLine(fromT.line).insert(fromT.token, new ZSToken(ZSTokenType.INVALID, remainder));
195
+		
196
+		relex(fromT.line, Math.max(0, fromT.token - 1), fromT.line, fromT.token + 1);
148 197
 	}
149 198
 	
150
-	public void insert(int lineIndex, int offset, String value) {
151
-		TokenLine line = lines.get(lineIndex);
152
-		int tokenOffset = 0;
153
-		for (int i = 0; i < line.getTokenCount(); i++) {
154
-			ZSToken token = line.getToken(i);
155
-			if (tokenOffset + token.content.length() > offset) {
156
-				token = token.insert(offset - tokenOffset, value);
157
-				line.replace(i, token);
158
-				relex(lineIndex, i, lineIndex, i + 1);
159
-				return;
160
-			}
161
-			tokenOffset += token.content.length();
162
-		}
163
-
164
-		ZSToken token = line.getLastToken();
199
+	public void insert(SourcePosition position, String value) {
200
+		TokenLine line = lines.get(position.line);
201
+		Position tokenPosition = position.asTokenPosition();
202
+		ZSToken token = getTokenAt(tokenPosition);
165 203
 		if (token == null) {
166 204
 			line.add(new ZSToken(ZSTokenType.INVALID, value));
167 205
 		} else {
168
-			token = new ZSToken(token.type, token.content + value);
169
-			line.replace(line.getTokenCount() - 1, token);
206
+			token = token.insert(tokenPosition.offset, value);
207
+			line.replace(tokenPosition.token, token);
170 208
 		}
171
-		relex(lineIndex, line.getTokenCount() - 1, lineIndex, line.getTokenCount());
209
+		relex(tokenPosition.line, Math.max(0, tokenPosition.token - 1), tokenPosition.line, tokenPosition.token + 1);
172 210
 	}
173 211
 	
174 212
 	private void relex(int fromLine, int fromToken, int toLine, int toToken) {

Loading…
Cancel
Save