1 module editorevents;
2 
3 import document;
4 import clipboard;
5 
6 public import pixelperfectengine.concrete.eventchainsystem;
7 public import pixelperfectengine.graphics.layers;
8 public import pixelperfectengine.map.mapformat;
9 
10 import pixelperfectengine.system.file;
11 
12 import std.stdio;
13 import std.conv : to;
14 import sdlang;
15 
16 public class WriteToMapVoidFill : UndoableEvent {
17 	ITileLayer target;
18 	Coordinate area;
19 	MappingElement me;
20 	MappingElement[] original;
21 	public this(ITileLayer target, Coordinate area, MappingElement me){
22 		this.target = target;
23 		this.area = area;
24 		this.me = me;
25 		//mask.length = area.area;
26 	}
27 	public void redo() {
28 		original.length = 0;
29 		original.reserve(area.area);
30 		for (int y = area.top ; y <= area.bottom ; y++) {
31 			for (int x = area.left ; x <= area.right ; x++) {
32 				MappingElement o = target.readMapping(x,y);
33 				original ~= o;
34 				if (o.tileID == 0xFFFF)
35 					target.writeMapping(x,y,me);
36 			}
37 		}
38 	}
39 	public void undo() {
40 		size_t pos;
41 		for (int y = area.top ; y <= area.bottom ; y++) {
42 			for (int x = area.left ; x <= area.right ; x++) {
43 				target.writeMapping(x,y,original[pos]);
44 				pos++;
45 			}
46 		}
47 	}
48 }
49 
50 public class WriteToMapOverwrite : UndoableEvent {
51 	ITileLayer target;
52 	Coordinate area;
53 	MappingElement me;
54 	MappingElement[] original;
55 	public this(ITileLayer target, Coordinate area, MappingElement me){
56 		this.target = target;
57 		this.area = area;
58 		this.me = me;
59 		//original.length = (area.width + 1) * (area.height + 1);
60 	}
61 	public void redo() {
62 		original.length = 0;
63 		original.reserve(area.area);
64 		for(int y = area.top ; y <= area.bottom ; y++){
65 			for(int x = area.left ; x <= area.right ; x++){
66 				original ~= target.readMapping(x,y);
67 				target.writeMapping(x,y,me);
68 			}
69 		}
70 	}
71 	public void undo() {
72 		size_t pos;
73 		for (int y = area.top ; y <= area.bottom ; y++) {
74 			for (int x = area.left ; x <= area.right ; x++) {
75 				target.writeMapping(x,y,original[pos]);
76 				pos++;
77 			}
78 		}
79 	}
80 }
81 
82 public class WriteToMapSingle : UndoableEvent {
83 	ITileLayer target;
84 	int x;
85 	int y;
86 	MappingElement me;
87 	MappingElement original;
88 	public this(ITileLayer target, int x, int y, MappingElement me) {
89 		this.target = target;
90 		this.x = x;
91 		this.y = y;
92 		this.me = me;
93 	}
94 	public void redo() {
95 		original = target.readMapping(x,y);
96 		target.writeMapping(x,y,me);
97 		/*debug {
98 			import std.stdio : writeln;
99 			writeln("Layer was written at position ", x, ";", y," with values ", target.readMapping(x,y));
100 		}*/
101 	}
102 	public void undo() {
103 		target.writeMapping(x,y,original);
104 	}
105 }
106 
107 public class CreateTileLayerEvent : UndoableEvent {
108 	TileLayer creation;
109 	MapDocument target;
110 	int tX;
111 	int tY;
112 	int mX;
113 	int mY;
114 	int pri;
115 	string name;
116 	string file;
117 	bool embed;
118 	Tag backup;
119 
120 	public this(MapDocument target, int tX, int tY, int mX, int mY, dstring name, string file, bool embed) {
121 		import std.utf : toUTF8;
122 		creation = new TileLayer(tX, tY);
123 		creation.setRenderingMode(RenderingMode.Copy);
124 		this.target = target;
125 		//this.md = md;
126 		this.tX = tX;
127 		this.tY = tY;
128 		this.mX = mX;
129 		this.mY = mY;
130 		this.name = toUTF8(name);
131 		this.file = file;
132 		this.embed = embed;
133 		//this.imageReturnFunc = imageReturnFunc;
134 	}
135 	public void redo() {
136 		import std.file : exists, isFile;
137 		import std.path : baseName;
138 		import std.utf : toUTF8;
139 		import pixelperfectengine.system.etc : intToHex;
140 		if (backup) {	//If a backup exists, then re-add that to the document, then return.
141 			target.mainDoc.addNewLayer(pri, backup, creation);
142 			//target.outputWindow.addLayer(pri);
143 			return;
144 		}
145 		try {
146 			const int nextLayer = target.nextLayerNumber;
147 
148 			//handle the following instances for mapping:
149 			//file == null AND embed
150 			//file == existing file AND embed
151 			//file == existing file AND !embed
152 			//file == nonexisting file
153 			if ((!exists(file) || !isFile(file)) && embed) {	//create new instance for the map by embedding data into the SDLang file
154 				//selDoc.mainDoc.tld[nextLayer] = new
155 				MappingElement[] me;
156 				me.length = mX * mY;
157 				creation.loadMapping(mX, mY, me);
158 				target.mainDoc.addNewTileLayer(nextLayer, tX, tY, mX, mY, name, creation);
159 				target.mainDoc.addEmbeddedMapData(nextLayer, me);
160 			} else if (!exists(file)) {	//Create empty file
161 				File f = File(file, "wb");
162 				MappingElement[] me;
163 				me.length = mX * mY;
164 				creation.loadMapping(mX, mY, me);
165 				target.mainDoc.addNewTileLayer(nextLayer, tX, tY, mX, mY, name, creation);
166 				saveMapFile(MapDataHeader(mX, mY), me, f);
167 				target.mainDoc.addMapDataFile(nextLayer, file);
168 			} else {	//load mapping, embed data into current file if needed
169 				MapDataHeader mdh;
170 				MappingElement[] me = loadMapFile(File(file), mdh);
171 				creation.loadMapping(mdh.sizeX, mdh.sizeY, me);
172 				target.mainDoc.addNewTileLayer(nextLayer, tX, tY, mX, mY, name, creation);
173 				if (embed)
174 					target.mainDoc.addEmbeddedMapData(nextLayer, me);
175 				else
176 					target.mainDoc.addMapDataFile(nextLayer, file);
177 			}
178 
179 			//handle the following instances for materials:
180 			//res == image file
181 			//TODO: check if material resource file has any embedded resource data
182 			//TODO: enable importing from SDLang map files (*.xmf)
183 			//TODO: generate dummy tiles for nonexistent material
184 			/+if (exists(res)) {
185 				//load the resource file and test if it's the correct size (through an exception)
186 				source = loadImage(File(res));
187 				ABitmap[] tilesheet;
188 				switch (source.getBitdepth()) {
189 					case 4:
190 						Bitmap4Bit[] output = loadBitmapSheetFromImage!Bitmap4Bit(source, tX, tY);
191 						foreach(p; output)
192 							tilesheet ~= p;
193 						break;
194 					case 8:
195 						Bitmap8Bit[] output = loadBitmapSheetFromImage!Bitmap8Bit(source, tX, tY);
196 						foreach(p; output)
197 							tilesheet ~= p;
198 						break;
199 					case 16:
200 						Bitmap16Bit[] output = loadBitmapSheetFromImage!Bitmap16Bit(source, tX, tY);
201 						foreach(p; output)
202 							tilesheet ~= p;
203 						break;
204 					case 32:
205 						Bitmap32Bit[] output = loadBitmapSheetFromImage!Bitmap32Bit(source, tX, tY);
206 						foreach(p; output)
207 							tilesheet ~= p;
208 						break;
209 					default:
210 						throw new Exception("Unsupported bitdepth!");
211 
212 				}
213 				if (tilesheet.length == 0) throw new Exception("No tiles were imported!");
214 				target.addTileSet(nextLayer, tilesheet);
215 				target.mainDoc.addsourceFile(nextLayer, res);
216 				{
217 					TileInfo[] idList;
218 					string nameBase = baseName(res);
219 					for (int id ; id < tilesheet.length ; id++) {
220 						idList ~= TileInfo(cast(wchar)id, id, nameBase ~ "0x" ~ intToHex(id, 4));
221 						//writeln(idList);
222 					}
223 					target.mainDoc.addTileInfo(nextLayer, idList, res);
224 				}
225 				if (source.isIndexed) {
226 					Color[] palette;
227 					/*foreach (color ; source.palette) {
228 						palette ~= Color(color.a, color.r, color.g, color.b);
229 						debug writeln(color);
230 					}*/
231 					auto sourcePalette = source.palette;
232 					palette.reserve(sourcePalette.length);
233 					for (ushort i ; i < sourcePalette.length ; i++){
234 						const auto origC = sourcePalette[i];
235 						const Color c = Color(origC.a, origC.r, origC.g, origC.b);
236 						palette ~= c;
237 					}
238 					target.mainDoc.addPaletteFile(res, "", cast(int)target.outputWindow.palette.length);
239 					target.outputWindow.palette = target.outputWindow.palette ~ palette;
240 					//debug writeln(target.outputWindow.palette);
241 				}
242 
243 			}+/
244 			//target.outputWindow.addLayer(nextLayer);
245 			target.selectedLayer = nextLayer;
246 			pri = nextLayer;
247 			target.updateLayerList();
248 			target.updateMaterialList();
249 		} catch (Exception e) {
250 			writeln(e);
251 		}
252 	}
253 	public void undo() {
254 		//Just remove the added layer from the layerlists
255 		//target.outputWindow.removeLayer(pri);
256 		backup = target.mainDoc.removeLayer(pri);
257 		target.updateLayerList();
258 		target.updateMaterialList();
259 	}
260 }
261 public class ResizeTileMapEvent : UndoableEvent {
262 	MappingElement[] backup, destMap;
263 	int mX, mY, offsetX, offsetY, newX, newY;
264 	MapDocument targetDoc;
265 	int layer;
266 	bool patternRepeat;
267 	this(int[6] params, MapDocument targetDoc, int layer, bool patternRepeat) {
268 		mX = params[0];
269 		mY = params[1];
270 		offsetX = params[2];
271 		offsetY = params[3];
272 		newX = params[4];
273 		newY = params[5];
274 		this.targetDoc = targetDoc;
275 		this.layer = layer;
276 		this.patternRepeat = patternRepeat;
277 	}
278 	public void redo() {
279 		//backup current layer data
280 		ITileLayer targetLayer = cast(ITileLayer)targetDoc.mainDoc.layeroutput[layer];
281 		backup = targetLayer.getMapping();
282 		destMap.length = newX * newY;
283 		//writeln(destMap.length);
284 		if(patternRepeat) {
285 			int sX = offsetX % mX, sY = offsetY % mY;
286 			for (int iY ; iY < newY ; iY++) {
287 				for (int iX ; iX < newX ; iX++) {
288 					destMap[iX + (iY * newX)] = backup[sX + (sY * mX)];
289 					sX++;
290 					if (sX >= mX) sX = 0;
291 				}
292 				sY++;
293 				if (sY >= mY) sY = 0;
294 			}
295 		} else {
296 			for (int iY ; iY < mY ; iY++) {
297 				//Do a boundscheck, if falls outside of it do nothing. If inside, copy.
298 				if (iY + offsetY < newY && iY + offsetY >= 0) {
299 					for (int iX ; iX < mX ; iX++) {
300 						//Do a boundscheck, if falls outside of it do nothing. If inside, copy.
301 						if (iX + offsetX < newX && iX + offsetX >= 0) {
302 							destMap[iX + offsetX + ((iY + offsetY) * newY)] = backup[iX + (iY * mY)];
303 						} else if (iX + offsetX >= newX) break;
304 					}
305 				} else if (iY + offsetY >= newY) break;
306 			}
307 		}
308 		targetLayer.loadMapping(newX, newY, destMap);
309 		targetDoc.mainDoc.alterTileLayerInfo(layer, 4, newX);
310 		targetDoc.mainDoc.alterTileLayerInfo(layer, 5, newY);
311 	}
312 	public void undo() {
313 		ITileLayer targetLayer = cast(ITileLayer)targetDoc.mainDoc.layeroutput[layer];
314 		targetLayer.loadMapping(mX, mY, backup);
315 		targetDoc.mainDoc.alterTileLayerInfo(layer, 4, mX);
316 		targetDoc.mainDoc.alterTileLayerInfo(layer, 5, mY);
317 	}
318 }
319 public class AddTileSheetEvent : UndoableEvent {
320 	Image source;
321 	MapDocument targetDoc;
322 	int layer;
323 	int paletteOffset;
324 	int paletteShift;
325 	string preName, afterName, fileName;
326 	int numFrom;
327 	uint numStyle;		///0: decimal, 1: hex, 2: octal
328 	Tag backup;
329 	this(Image source, MapDocument targetDoc, int layer, int paletteOffset, int paletteShift, string[3] name, int numFrom, 
330 			uint numStyle) {
331 		this.source = source;
332 		this.targetDoc = targetDoc;
333 		this.layer = layer;
334 		this.paletteOffset = paletteOffset;
335 		this.paletteShift = paletteShift;
336 		preName = name[0];
337 		afterName = name[1];
338 		fileName = name[2];
339 		this.numFrom = numFrom;
340 		this.numStyle = numStyle;
341 	}
342 	public void redo() {
343 		import pixelperfectengine.system.etc : intToHex, intToOct;
344 		//Copy palette if exists (-1 means no palette or do not import palette)
345 		if (paletteOffset >= 0) {
346 			//Color[] targetPalette = targetDoc.outputWindow.paletteLocal;
347 			Color[] importedPalette = loadPaletteFromImage(source);
348 			assert(importedPalette.length);
349 			const ushort offset = cast(ushort)(paletteOffset << paletteShift);
350 			targetDoc.outputWindow.loadPaletteChunk(importedPalette, offset);
351 			targetDoc.mainDoc.addPaletteFile(fileName, "", paletteOffset << paletteShift, paletteShift);
352 		}
353 		if (backup is null) {
354 			//TODO: check if material resource file has any embedded resource data
355 			//TODO: enable importing from SDLang map files (*.xmf)
356 			//TODO: generate dummy tiles for nonexistent material
357 			
358 			//load the resource file and test if it's the correct size (through an exception)
359 			//source = loadImage(File(res));
360 			ITileLayer itl = cast(ITileLayer)targetDoc.mainDoc.layeroutput[layer];
361 			const int tX = itl.getTileWidth, tY = itl.getTileHeight;
362 			ABitmap[] tilesheet;
363 			switch (source.getBitdepth()) {
364 				case 4:
365 					Bitmap4Bit[] output = loadBitmapSheetFromImage!Bitmap4Bit(source, tX, tY);
366 					foreach(p; output)
367 						tilesheet ~= p;
368 					break;
369 				case 8:
370 					Bitmap8Bit[] output = loadBitmapSheetFromImage!Bitmap8Bit(source, tX, tY);
371 					foreach(p; output)
372 						tilesheet ~= p;
373 					break;
374 				case 16:
375 					Bitmap16Bit[] output = loadBitmapSheetFromImage!Bitmap16Bit(source, tX, tY);
376 					foreach(p; output)
377 						tilesheet ~= p;
378 					break;
379 				case 32:
380 					Bitmap32Bit[] output = loadBitmapSheetFromImage!Bitmap32Bit(source, tX, tY);
381 					foreach(p; output)
382 						tilesheet ~= p;
383 					break;
384 				default:
385 					throw new Exception("Unsupported bitdepth!");
386 			}
387 			if (tilesheet.length == 0) throw new Exception("No tiles were imported!");
388 			targetDoc.addTileSet(layer, tilesheet);
389 			targetDoc.mainDoc.addTileSourceFile(layer, fileName, null, 0);
390 			{
391 				TileInfo[] idList;
392 				for (int id ; id < tilesheet.length ; id++) {
393 					//idList ~= TileInfo(cast(wchar)id, id, nameBase ~ "0x" ~ intToHex(id, 4));
394 					string tilename = preName;
395 					switch(numStyle & 0x3) {
396 						case 1:
397 							tilename ~= intToHex(id + numFrom, numStyle>>>8);
398 							break;
399 						case 2:
400 							tilename ~= intToOct(id + numFrom, numStyle>>>8);
401 							break;
402 						default:
403 							string num = to!string(id);
404 							for (int i ; i < (numStyle>>>8) - num.length ; i++) {
405 								tilename ~= '0';
406 							}
407 							tilename ~= num;
408 							break;
409 					}
410 					tilename ~= afterName;
411 					idList ~= TileInfo(cast(wchar)id, cast(ushort)paletteShift, id, tilename);
412 					//writeln(idList);
413 				}
414 				targetDoc.mainDoc.addTileInfo(layer, idList, fileName, null);
415 			}			
416 		} else {
417 			
418 		}
419 	}
420 	public void undo() {
421 		//remove palette if exists
422 		if (paletteOffset >= 0) {
423 			const ushort offset = cast(ushort)(paletteOffset << paletteShift);
424 			targetDoc.outputWindow.clearPaletteChunk(cast(ushort)source.palette.length, offset);
425 		}
426 		
427 	}
428 }
429 /**
430  * Cuts a selected portion of tiles out from a tilelayer.
431  */
432 public class CutFromTileLayerEvent : UndoableEvent {
433 	ITileLayer target;
434 	Coordinate area;
435 	MappingElement[] original;
436 	public this(ITileLayer target, Coordinate area){
437 		this.target = target;
438 		this.area = area;
439 	}
440 	public void redo() {
441 		original.length = 0;
442 		original.reserve(area.area);
443 		for (int y = area.top ; y < area.bottom ; y++) {
444 			for (int x = area.left ; x < area.right ; x++) {
445 				MappingElement o = target.readMapping(x,y);
446 				original ~= o;
447 				target.writeMapping(x,y,MappingElement.init);
448 			}
449 		}
450 	}
451 	public void undo() {
452 		size_t pos;
453 		for (int y = area.top ; y < area.bottom ; y++) {
454 			for (int x = area.left ; x < area.right ; x++) {
455 				target.writeMapping(x,y,original[pos]);
456 				pos++;
457 			}
458 		}
459 	}
460 }
461 /**
462  * Pastes a copied area to a given tilelayer.
463  */
464 public class PasteIntoTileLayerEvent : UndoableEvent {
465 	MapClipboard.Item		item;
466 	ITileLayer				target;
467 	Point					position;
468 	MappingElement[]		original;
469 	bool					overwrite;
470 	public this (MapClipboard.Item item, ITileLayer target, Point position, bool overwrite = true) {
471 		this.item = item;
472 		this.target = target;
473 		this.position = position;
474 		this.overwrite = overwrite;
475 	}
476 
477 	public void redo() {
478 		original.length = 0;
479 		original.reserve(item.width * item.height);
480 		if (overwrite) {
481 			for (int y = position.y, y0 ; y0 < item.height ; y++, y0++) {
482 				for (int x = position.x, x0 ; x0 < item.width ; x++, x0++) {
483 					MappingElement o = target.readMapping(x,y);
484 					original ~= o;
485 					target.writeMapping(x,y,item.map[x0 + (y0 * item.width)]);
486 				}
487 			}
488 		} else {
489 			for (int y = position.y, y0 ; y0 < item.height ; y++, y0++) {
490 				for (int x = position.x, x0 ; x0 < item.width ; x++, x0++) {
491 					MappingElement o = target.readMapping(x,y), n = item.map[x0 + (y0 * item.width)];
492 					original ~= o;
493 					if (n.tileID != 0xFFFF)
494 						target.writeMapping(x,y,n);
495 				}
496 			}
497 		}
498 	}
499 
500 	public void undo() {
501 		size_t pos;
502 		for (int y = position.y ; y < position.y + item.height ; y++) {
503 			for (int x = position.x ; x < position.x + item.width ; x++) {
504 				target.writeMapping(x,y,original[pos]);
505 				pos++;
506 			}
507 		}
508 	}
509 }
510 /**
511  * Removes a single tile.
512  */
513 public class RemoveTile : UndoableEvent {
514 	int id;
515 	MapDocument targetDoc;
516 	int layer;
517 	Tag infobackup;
518 	ABitmap bitmapbackup;
519 	string source, dpkSource;
520 	this (int id, MapDocument targetDoc, int layer) {
521 		this.id = id;
522 		this.targetDoc = targetDoc;
523 		this.layer = layer;
524 	}
525 
526 	public void redo() {
527 		infobackup = targetDoc.mainDoc.removeTile(layer, id, source, dpkSource);
528 		TileLayer tl = cast(TileLayer)targetDoc.mainDoc.layeroutput[layer];
529 		bitmapbackup = tl.getTile(cast(wchar)id);
530 		tl.removeTile(cast(wchar)id);
531 		targetDoc.updateMaterialList();
532 	}
533 
534 	public void undo() {
535 		targetDoc.mainDoc.addTile(layer, infobackup, source, dpkSource);
536 		Tag t = targetDoc.mainDoc.getTileSourceTag(layer, source, dpkSource);
537 		TileLayer tl = cast(TileLayer)targetDoc.mainDoc.layeroutput[layer];
538 		tl.addTile(bitmapbackup, cast(wchar)id, cast(ubyte)(t.getAttribute!int("palShift")));
539 		targetDoc.updateMaterialList();
540 	}
541 }
542 /**
543  * Renames a single tile
544  */
545 public class RenameTile : UndoableEvent {
546 	int id;
547 	MapDocument targetDoc;
548 	int layer;
549 	string oldName, newName;
550 
551 	this (int id, MapDocument targetDoc, int layer, string newName) {
552 		this.id = id;
553 		this.targetDoc = targetDoc;
554 		this.layer = layer;
555 		this.newName = newName;
556 	}
557 
558 	public void redo() {
559 		oldName = targetDoc.mainDoc.renameTile(layer, id, newName);
560 		targetDoc.updateMaterialList();
561 	}
562 
563 	public void undo() {
564 		targetDoc.mainDoc.renameTile(layer, id, oldName);
565 		targetDoc.updateMaterialList();
566 	}
567 }
568 /**
569  * Removes a layer.
570  */
571 public class RemoveLayer : UndoableEvent {
572 	MapDocument targetDoc;
573 	int layer;
574 	Tag infobackup;
575 	Layer databackup;
576 	this (MapDocument targetDoc, int layer) {
577 		this.targetDoc = targetDoc;
578 		this.layer = layer;
579 	}
580 
581 	public void redo() {
582 		infobackup = targetDoc.mainDoc.layerData.remove(layer);
583 		databackup = targetDoc.mainDoc.layeroutput.remove(layer);
584 		targetDoc.updateLayerList();
585 	}
586 
587 	public void undo() {
588 		targetDoc.mainDoc.layerData[layer] = infobackup;
589 		targetDoc.mainDoc.layeroutput[layer] = databackup;
590 		targetDoc.updateLayerList();
591 	}
592 }
593 /**
594  * Renames a layer.
595  */
596 public class RenameLayer : UndoableEvent {
597 	MapDocument targetDoc;
598 	int layer;
599 	string oldName, newName;
600 	this (MapDocument targetDoc, int layer, string newName) {
601 		this.targetDoc = targetDoc;
602 		this.layer = layer;
603 		this.newName = newName;
604 	}
605 
606 	public void redo() {
607 		try {
608 			Tag t = targetDoc.mainDoc.layerData[layer];
609 			oldName = t.values[0].get!string();
610 			t.values[0] = Value(newName);
611 		} catch (DOMException e) {
612 			debug writeln(e);
613 		} catch (Exception e) {
614 			debug writeln(e);
615 		}
616 		targetDoc.updateLayerList();
617 	}
618 	
619 	public void undo() {
620 		try {
621 			Tag t = targetDoc.mainDoc.layerData[layer];
622 			//newName = t.values[0].get!string();
623 			t.values[0] = Value(oldName);
624 		} catch (DOMException e) {
625 			debug writeln(e);
626 		} catch (Exception e) {
627 			debug writeln(e);
628 		}
629 		targetDoc.updateLayerList();
630 	}
631 }
632 /**
633  * Moves a layer in the priority list.
634  */
635 public class ChangeLayerPriority : UndoableEvent {
636 	MapDocument targetDoc;
637 	int layer;
638 	int newPri;
639 	this (MapDocument targetDoc, int layer, int newPri) {
640 		this.targetDoc = targetDoc;
641 		this.layer = layer;
642 		this.newPri = newPri;
643 	}
644 
645 	public void redo() {
646 		targetDoc.mainDoc.layerData[newPri] = targetDoc.mainDoc.layerData.remove(layer);
647 		targetDoc.mainDoc.layeroutput[newPri] = targetDoc.mainDoc.layeroutput.remove(layer);
648 		targetDoc.mainDoc.layerData[newPri].values[1] = Value(newPri);
649 		targetDoc.selectedLayer = newPri;
650 		targetDoc.updateLayerList();
651 	}
652 	
653 	public void undo() {
654 		targetDoc.mainDoc.layerData[layer] = targetDoc.mainDoc.layerData.remove(newPri);
655 		targetDoc.mainDoc.layeroutput[layer] = targetDoc.mainDoc.layeroutput.remove(newPri);
656 		targetDoc.mainDoc.layerData[layer].values[1] = Value(layer);
657 		targetDoc.selectedLayer = layer;
658 		targetDoc.updateLayerList();
659 	}
660 }
661 /**
662  * Flips all selected tiles horizontally.
663  */
664 public class FlipSelTilesH : UndoableEvent {
665 	ITileLayer			target;
666 	Box					area;
667 	MappingElement[]	backup;
668 
669 	this (ITileLayer target, Box area) {
670 		this.target = target;
671 		this.area = area;
672 		backup.length = area.area;
673 		int i;
674 		for (int y = area.top ; y <= area.bottom ; y++) {
675 			for (int x = area.left ; x <= area.right ; x++, i++) {
676 				MappingElement w = target.readMapping(x, y);
677 				backup[i] = w;
678 			}
679 		}
680 	}
681 
682 	public void redo() {
683 		for (int y = area.top ; y <= area.bottom ; y++) {
684 			for (int x = area.left ; x <= area.right ; x++) {
685 				MappingElement w = target.readMapping(x, y);
686 				w.attributes.horizMirror = !w.attributes.horizMirror;
687 				target.writeMapping(x, y, w);
688 			}
689 		}
690 	}
691 
692 	public void undo() {
693 		int i;
694 		for (int y = area.top ; y <= area.bottom ; y++) {
695 			for (int x = area.left ; x <= area.right ; x++, i++) {
696 				target.writeMapping(x, y, backup[i]);
697 			}
698 		}
699 	}
700 }
701 /**
702  * Flips all selected tiles vertically.
703  */
704 public class FlipSelTilesV : UndoableEvent {
705 	ITileLayer			target;
706 	Box					area;
707 	MappingElement[]	backup;
708 
709 	this (ITileLayer target, Box area) {
710 		this.target = target;
711 		this.area = area;
712 		backup.length = area.area;
713 		int i;
714 		for (int y = area.top ; y <= area.bottom ; y++) {
715 			for (int x = area.left ; x <= area.right ; x++, i++) {
716 				MappingElement w = target.readMapping(x, y);
717 				backup[i] = w;
718 			}
719 		}
720 	}
721 
722 	public void redo() {
723 		for (int y = area.top ; y <= area.bottom ; y++) {
724 			for (int x = area.left ; x <= area.right ; x++) {
725 				MappingElement w = target.readMapping(x, y);
726 				w.attributes.vertMirror = !w.attributes.vertMirror;
727 				target.writeMapping(x, y, w);
728 			}
729 		}
730 	}
731 
732 	public void undo() {
733 		int i;
734 		for (int y = area.top ; y <= area.bottom ; y++) {
735 			for (int x = area.left ; x <= area.right ; x++, i++) {
736 				target.writeMapping(x, y, backup[i]);
737 			}
738 		}
739 	}
740 }
741 /**
742  * Mirrors selection horizontally. (Tile Layer)
743  */
744 public class MirrorSelHTL : UndoableEvent {
745 	ITileLayer			target;
746 	Box					area;
747 	MappingElement[]	backup;
748 
749 	this (ITileLayer target, Box area) {
750 		this.target = target;
751 		this.area = area;
752 		int i;
753 		backup.length = area.area;
754 		for (int y = area.top ; y <= area.bottom ; y++) {
755 			for (int x = area.left ; x <= area.right ; x++, i++) {
756 				backup[i] = target.readMapping(x, y);
757 			}
758 		}
759 	}
760 
761 	public void redo() {
762 		for (int y0 = area.top ; y0 <= area.bottom ; y0++) {
763 			for (int x0 = area.left, x1 = area.right ; x0 < x1 ; x0++, x1--) {
764 				MappingElement w = target.readMapping(x0, y0);
765 				target.writeMapping(x0, y0, target.readMapping(x1, y0));
766 				target.writeMapping(x1, y0, w);
767 			}
768 		}
769 	}
770 
771 	public void undo() {
772 		int i;
773 		for (int y = area.top ; y <= area.bottom ; y++) {
774 			for (int x = area.left ; x <= area.right ; x++, i++) {
775 				target.writeMapping(x, y, backup[i]);
776 			}
777 		}
778 	}
779 }
780 /**
781  * Mirrors selection vertically. (Tile Layer)
782  */
783 public class MirrorSelVTL : UndoableEvent {
784 	ITileLayer			target;
785 	Box					area;
786 	MappingElement[]	backup;
787 
788 	this (ITileLayer target, Box area) {
789 		this.target = target;
790 		this.area = area;
791 		int i;
792 		backup.length = area.area;
793 		for (int y = area.top ; y <= area.bottom ; y++) {
794 			for (int x = area.left ; x <= area.right ; x++, i++) {
795 				backup[i] = target.readMapping(x, y);
796 			}
797 		}
798 	}
799 
800 	public void redo() {
801 		for (int y0 = area.top, y1 = area.bottom ; y0 < y1 ; y0++, y1--) {
802 			for (int x0 = area.left ; x0 <= area.right ; x0++) {
803 				MappingElement w = target.readMapping(x0, y0);
804 				target.writeMapping(x0, y0, target.readMapping(x0, y1));
805 				target.writeMapping(x0, y1, w);
806 			}
807 		}
808 	}
809 
810 	public void undo() {
811 		int i;
812 		for (int y = area.top ; y <= area.bottom ; y++) {
813 			for (int x = area.left ; x <= area.right ; x++, i++) {
814 				target.writeMapping(x, y, backup[i++]);
815 			}
816 		}
817 	}
818 }
819 /**
820  * Mirrors selection both horizontally and vertically. (Tile Layer)
821  */
822 public class MirrorSelBTL : UndoableEvent {
823 	ITileLayer			target;
824 	Box					area;
825 	MappingElement[]	backup;
826 
827 	this (ITileLayer target, Box area) {
828 		this.target = target;
829 		this.area = area;
830 		int i;
831 		backup.length = area.area;
832 		for (int y = area.top ; y <= area.bottom ; y++) {
833 			for (int x = area.left ; x <= area.right ; x++, i++) {
834 				backup[i] = target.readMapping(x, y);
835 			}
836 		}
837 	}
838 
839 	public void redo() {
840 		for (int y0 = area.top, y1 = area.bottom ; y0 < y1 ; y0++, y1--) {
841 			for (int x0 = area.left, x1 = area.right ; x0 < x1 ; x0++, x1--) {
842 				MappingElement w = target.readMapping(x0, y0);
843 				target.writeMapping(x0, y0, target.readMapping(x1, y1));
844 				target.writeMapping(x1, y1, w);
845 			}
846 		}
847 	}
848 
849 	public void undo() {
850 		int i;
851 		for (int y = area.top ; y <= area.bottom ; y++) {
852 			for (int x = area.left ; x <= area.right ; x++, i++) {
853 				target.writeMapping(x, y, backup[i]);
854 			}
855 		}
856 	}
857 }
858 /**
859  * Imports mapping data to a layer.
860  */
861 public class ImportLayerData : UndoableEvent {
862 	ITileLayer target;
863 	Tag dataTarget;
864 	MappingElement[] newData;
865 	MappingElement[] backup;
866 	int width, height;
867 	int buw, buh;
868 
869 	this(ITileLayer target, Tag dataTarget, MappingElement[] newData, int width, int height) {
870 		this.target = target;
871 		this.dataTarget = dataTarget;
872 		this.newData = newData;
873 		this.width = width;
874 		this.height = height;
875 		buw = target.getMX();
876 		buh = target.getMY();
877 		backup = target.getMapping();
878 	}
879 
880 	public void redo() {
881 		dataTarget.values[4] = Value(width);
882 		dataTarget.values[5] = Value(height);
883 		target.loadMapping(width, height, newData);
884 	}
885 
886 	public void undo() {
887 		dataTarget.values[4] = Value(buw);
888 		dataTarget.values[5] = Value(buh);
889 		target.loadMapping(buw, buh, backup);
890 	}
891 }