1 /*
2  * Copyright (C) 2016-2017, by Laszlo Szeremi under the Boost license.
3  *
4  * Pixel Perfect Editor, graphics.outputScreen module
5  */
6 
7 module editor;
8 
9 import PixelPerfectEngine.graphics.outputScreen;
10 import PixelPerfectEngine.graphics.raster;
11 import PixelPerfectEngine.graphics.layers;
12 import PixelPerfectEngine.graphics.paletteMan;
13 //import PixelPerfectEngine.extbmp.extbmp;
14 
15 import PixelPerfectEngine.graphics.bitmap;
16 import PixelPerfectEngine.graphics.draw;
17 //import collision;
18 import PixelPerfectEngine.system.input;
19 import PixelPerfectEngine.system.file;
20 import PixelPerfectEngine.system.etc;
21 import PixelPerfectEngine.system.config;
22 import PixelPerfectEngine.system.systemUtility;
23 import std.stdio;
24 import std.conv;
25 import core.stdc..string : memcpy;
26 //import derelict.sdl2.sdl;
27 import bindbc.sdl;
28 import PixelPerfectEngine.concrete.window;
29 import PixelPerfectEngine.concrete.eventChainSystem;
30 import PixelPerfectEngine.map.mapformat;
31 
32 //import converterdialog;
33 import windows.resizemap;
34 import windows.about;
35 import editorevents;
36 public import windows.layerlist;
37 public import windows.materiallist;
38 import document;
39 import windows.rasterwindow;
40 import windows.newtilelayer;
41 
42 
43 
44 public class NewDocumentDialog : Window{
45 	public Editor ie;
46 	private TextBox[] textBoxes;
47 	public this(Coordinate size, dstring title){
48 		super(size, title);
49 	}
50 	public this(Editor ie){
51 		this(Box(10,10,220,150),"New Document"d);
52 		this.ie = ie;
53 		Button[] buttons;
54 		Label[] labels;
55 		buttons ~= new Button("Ok", "ok", Box(150,110,200,130));
56 
57 		labels ~= new Label("Name:","",Box(5,20,80,39));
58 		labels ~= new Label("RasterX:","",Box(5,40,80,59));
59 		labels ~= new Label("RasterY:","",Box(5,60,80,79));
60 		//labels ~= new Label("N. of colors:","",Coordinate(5,80,120,99));
61 		textBoxes ~= new TextBox("newdocument","name",Box(81,20,200,39));
62 		textBoxes ~= new TextBox("424","rX",Box(121,40,200,59));
63 		textBoxes ~= new TextBox("240","rY",Box(121,60,200,79));
64 		//textBoxes ~= new TextBox("","pal",Coordinate(121,80,200,99));
65 		addElement(buttons[0]);
66 		foreach(WindowElement we; labels){
67 			addElement(we);
68 		}
69 		foreach(TextBox we; textBoxes){
70 			//we.addTextInputHandler(inputhandler);
71 			addElement(we);
72 		}
73 		buttons[0].onMouseLClick = &buttonOn_onMouseLClickRel;
74 	}
75 
76 	public void buttonOn_onMouseLClickRel(Event event){
77 		ie.createNewDocument(textBoxes[0].getText().text, to!int(textBoxes[1].getText().text), to!int(textBoxes[2].getText().text));
78 
79 		close();
80 	}
81 }
82 
83 public class TopLevelWindow : Window {
84 	public this(int width, int height, Editor prg) {
85 		Text mt(dstring text) @safe nothrow {
86 			return new Text(text, globalDefaultStyle.getChrFormatting("menuBar"));
87 		}
88 		super(Box(0, 0, width, height), ""d, [], null);
89 		MenuBar mb;
90 		{
91 			PopUpMenuElement[] menuElements;
92 			menuElements ~= new PopUpMenuElement("file", mt("FILE"));
93 
94 			menuElements[0].setLength(7);
95 			menuElements[0][0] = new PopUpMenuElement("new", "New PPE map");
96 			menuElements[0][1] = new PopUpMenuElement("newTemp", "New PPE map from template");
97 			menuElements[0][2] = new PopUpMenuElement("load", "Load PPE map");
98 			menuElements[0][3] = new PopUpMenuElement("save", "Save PPE map");
99 			menuElements[0][4] = new PopUpMenuElement("saveAs", "Save PPE map as");
100 			menuElements[0][5] = new PopUpMenuElement("saveTemp", "Save PPE map as template");
101 			menuElements[0][6] = new PopUpMenuElement("exit", "Exit application");
102 
103 			menuElements ~= new PopUpMenuElement("edit", mt("EDIT"));
104 
105 			menuElements[1].setLength(7);
106 			menuElements[1][0] = new PopUpMenuElement("undo", "Undo");
107 			menuElements[1][1] = new PopUpMenuElement("redo", "Redo");
108 			menuElements[1][2] = new PopUpMenuElement("copy", "Copy");
109 			menuElements[1][3] = new PopUpMenuElement("cut", "Cut");
110 			menuElements[1][4] = new PopUpMenuElement("paste", "Paste");
111 			menuElements[1][5] = new PopUpMenuElement("editorSetup", "Editor settings");
112 			menuElements[1][6] = new PopUpMenuElement("docSetup", "Document settings");
113 
114 			menuElements ~= new PopUpMenuElement("view", mt("VIEW"));
115 
116 			menuElements[2].setLength(2);
117 			menuElements[2][0] = new PopUpMenuElement("layerList", "Layers");
118 			menuElements[2][1] = new PopUpMenuElement("materialList", "Materials");
119 			//menuElements[2][2] = new PopUpMenuElement("layerTools", "Layer tools", "Alt + T");
120 
121 			menuElements ~= new PopUpMenuElement("layers", mt("LAYERS"));
122 
123 			menuElements[3].setLength(5);
124 			menuElements[3][0] = new PopUpMenuElement("newLayer", "New layer");
125 			menuElements[3][1] = new PopUpMenuElement("delLayer", "Delete layer");
126 			menuElements[3][2] = new PopUpMenuElement("impLayer", "Import layer");
127 			menuElements[3][3] = new PopUpMenuElement("layerSrc", "Layer resources");
128 			menuElements[3][4] = new PopUpMenuElement("resizeLayer", "Resize layer");
129 
130 			menuElements ~= new PopUpMenuElement("tools", mt("TOOLS"));
131 
132 			menuElements[4].setLength(2);
133 			menuElements[4][0] = new PopUpMenuElement("tgaTool", "TGA Toolkit");
134 			menuElements[4][1] = new PopUpMenuElement("bmfontTool", "BMFont Toolkit");
135 
136 			menuElements ~= new PopUpMenuElement("help", mt("HELP"));
137 
138 			menuElements[5].setLength(2);
139 			menuElements[5][0] = new PopUpMenuElement("helpFile", "Content");
140 			menuElements[5][1] = new PopUpMenuElement("about", "About");
141 
142 			mb = new MenuBar("mb", Box(0,0, width - 1, 15), menuElements);
143 
144 			mb.onMenuEvent = &prg.menuEvent;
145 		}
146 		addElement(mb);
147 	}
148 	public override void draw(bool drawHeaderOnly = false) {
149 		output.drawFilledBox(position, 0);
150 		foreach (WindowElement we; elements) {
151 			we.draw();
152 		}
153 	}
154 	public override void drawHeader() {
155 
156 	}
157 	///Passes mouse click event
158 	public override void passMCE(MouseEventCommons mec, MouseClickEvent mce) {
159 		lastMousePos = Point(mce.x - position.left, mce.y - position.top);
160 		foreach (WindowElement we; elements) {
161 			if (we.getPosition.isBetween(lastMousePos)) {
162 				lastMouseEventTarget = we;
163 				mce.x = lastMousePos.x;
164 				mce.y = lastMousePos.y;
165 				we.passMCE(mec, mce);
166 				return;
167 			}
168 		}
169 		foreach (ISmallButton sb; smallButtons) {
170 			WindowElement we = cast(WindowElement)sb;
171 			if (we.getPosition.isBetween(lastMousePos)) {
172 				lastMouseEventTarget = we;
173 				mce.x = lastMousePos.x;
174 				mce.y = lastMousePos.y;
175 				we.passMCE(mec, mce);
176 				return;
177 			}
178 		}
179 		lastMouseEventTarget = null;
180 	}
181 	///Passes mouse move event
182 	public override void passMME(MouseEventCommons mec, MouseMotionEvent mme) {
183 		lastMousePos = Point(mme.x - position.left, mme.y - position.top);
184 		if (lastMouseEventTarget) {
185 			mme.x = lastMousePos.x;
186 			mme.y = lastMousePos.y;
187 			lastMouseEventTarget.passMME(mec, mme);
188 			if (!lastMouseEventTarget.getPosition.isBetween(mme.x, mme.y)) {
189 				lastMouseEventTarget = null;
190 			}
191 		} else {
192 			foreach (WindowElement we; elements) {
193 				if (we.getPosition.isBetween(lastMousePos)) {
194 					lastMouseEventTarget = we;
195 					mme.x = lastMousePos.x;
196 					mme.y = lastMousePos.y;
197 					we.passMME(mec, mme);
198 					return;
199 				}
200 			}
201 		}
202 	}
203 }
204 /+public enum PlacementMode : uint{
205 	NULL		=	0,
206 	NORMAL		=	1,
207 	VOIDFILL	=	2,
208 	OVERWRITE	=	3,
209 
210 }+/
211 
212 public class Editor : InputListener, SystemEventListener {
213 	public OutputScreen[] ow;
214 	public Raster rasters;
215 	public InputHandler input;
216 	public wchar selectedTile;
217 	public BitmapAttrib selectedTileAttrib;
218 	public int selectedLayer;
219 	public SpriteLayer windowing;
220 	public SpriteLayer bitmapPreview;
221 	public bool onexit, exitDialog, newLayerDialog, mouseState, rasterRefresh;
222 	public Window test;
223 	public WindowHandler wh;
224 	//public EffectLayer selectionLayer;
225 	//public ForceFeedbackHandler ffb;
226 	//private uint[5] framecounter;
227 	public char[40] windowTitle;
228 	public ConfigurationProfile configFile;
229 	private int mouseX, mouseY;
230 	private Coordinate selection, selectedTiles;
231 	//public PlacementMode pm;
232 	//public UndoableStack undoStack;
233 	public PaletteManager palman;
234 	public MapDocument[dstring] documents;
235 	public MapDocument selDoc;
236 	public LayerList layerList;
237 	public MaterialList materialList;
238 	
239 	public this(string[] args){
240 		ConfigurationProfile.setVaultPath("ZILtoid1991","PixelPerfectEditor");
241 		configFile = new ConfigurationProfile();
242 
243 		windowing = new SpriteLayer(RenderingMode.Copy);
244 		bitmapPreview = new SpriteLayer();
245 
246 		wh = new WindowHandler(1696,960,848,480,windowing);
247 		//wh.ie = this;
248 
249 		//Initialize the Concrete framework
250 		INIT_CONCRETE(wh);
251 		writeln(globalDefaultStyle.drawParameters);
252 		//Initialize custom GUI elements
253 		{
254 			Bitmap8Bit[] customGUIElems = loadBitmapSheetFromFile!Bitmap8Bit("../system/concreteGUIE1.tga", 16, 16);
255 			globalDefaultStyle.setImage(customGUIElems[0], "menuButtonA");
256 			globalDefaultStyle.setImage(customGUIElems[1], "menuButtonB");
257 			globalDefaultStyle.setImage(customGUIElems[2], "fullSizeButtonA");
258 			globalDefaultStyle.setImage(customGUIElems[3], "fullSizeButtonB");
259 			globalDefaultStyle.setImage(customGUIElems[4], "smallSizeButtonA");
260 			globalDefaultStyle.setImage(customGUIElems[5], "smallSizeButtonB");
261 			globalDefaultStyle.setImage(customGUIElems[6], "newDocumentButtonA");
262 			globalDefaultStyle.setImage(customGUIElems[7], "newDocumentButtonB");
263 			globalDefaultStyle.setImage(customGUIElems[8], "saveDocumentButtonA");
264 			globalDefaultStyle.setImage(customGUIElems[9], "saveDocumentButtonB");
265 			globalDefaultStyle.setImage(customGUIElems[10], "loadDocumentButtonA");
266 			globalDefaultStyle.setImage(customGUIElems[11], "loadDocumentButtonB");
267 			globalDefaultStyle.setImage(customGUIElems[12], "settingsButtonA");
268 			globalDefaultStyle.setImage(customGUIElems[13], "settingsButtonB");
269 			globalDefaultStyle.setImage(customGUIElems[14], "blankButtonA");
270 			globalDefaultStyle.setImage(customGUIElems[15], "blankButtonB");
271 		}
272 		{
273 			Bitmap8Bit[] customGUIElems = loadBitmapSheetFromFile!Bitmap8Bit("../system/concreteGUIE4.tga", 16, 16);
274 			globalDefaultStyle.setImage(customGUIElems[0], "addMaterialA");
275 			globalDefaultStyle.setImage(customGUIElems[1], "addMaterialB");
276 			globalDefaultStyle.setImage(customGUIElems[2], "removeMaterialA");
277 			globalDefaultStyle.setImage(customGUIElems[3], "removeMaterialB");
278 			globalDefaultStyle.setImage(customGUIElems[4], "horizMirrorA");
279 			globalDefaultStyle.setImage(customGUIElems[5], "horizMirrorB");
280 			globalDefaultStyle.setImage(customGUIElems[6], "vertMirrorA");
281 			globalDefaultStyle.setImage(customGUIElems[7], "vertMirrorB");
282 			globalDefaultStyle.setImage(customGUIElems[8], "ovrwrtInsA");
283 			globalDefaultStyle.setImage(customGUIElems[9], "ovrwrtInsB");
284 			//globalDefaultStyle.setImage(customGUIElems[10], "");
285 			//globalDefaultStyle.setImage(customGUIElems[11], "");
286 			globalDefaultStyle.setImage(customGUIElems[12], "paletteDownA");
287 			globalDefaultStyle.setImage(customGUIElems[13], "paletteDownB");
288 			globalDefaultStyle.setImage(customGUIElems[14], "paletteUpA");
289 			globalDefaultStyle.setImage(customGUIElems[15], "paletteUpB");
290 		}
291 		{
292 			Bitmap8Bit[] customGUIElems = loadBitmapSheetFromFile!Bitmap8Bit("../system/concreteGUIE3.tga", 16, 16);
293 			globalDefaultStyle.setImage(customGUIElems[0], "trashButtonA");
294 			globalDefaultStyle.setImage(customGUIElems[1], "trashButtonB");
295 			globalDefaultStyle.setImage(customGUIElems[2], "visibilityButtonA");
296 			globalDefaultStyle.setImage(customGUIElems[3], "visibilityButtonB");
297 			globalDefaultStyle.setImage(customGUIElems[4], "newTileLayerButtonA");
298 			globalDefaultStyle.setImage(customGUIElems[5], "newTileLayerButtonB");
299 			globalDefaultStyle.setImage(customGUIElems[6], "newSpriteLayerButtonA");
300 			globalDefaultStyle.setImage(customGUIElems[7], "newSpriteLayerButtonB");
301 			globalDefaultStyle.setImage(customGUIElems[8], "newTransformableTileLayerButtonA");
302 			globalDefaultStyle.setImage(customGUIElems[9], "newTransformableTileLayerButtonB");
303 			globalDefaultStyle.setImage(customGUIElems[10], "importLayerDataButtonA");
304 			globalDefaultStyle.setImage(customGUIElems[11], "importLayerDataButtonB");
305 			globalDefaultStyle.setImage(customGUIElems[12], "importMaterialDataButtonA");
306 			globalDefaultStyle.setImage(customGUIElems[13], "importMaterialDataButtonB");
307 			globalDefaultStyle.setImage(customGUIElems[14], "paletteButtonA");
308 			globalDefaultStyle.setImage(customGUIElems[15], "paletteButtonB");
309 		}
310 		{
311 			Bitmap8Bit[] customGUIElems = loadBitmapSheetFromFile!Bitmap8Bit("../system/concreteGUIE5.tga", 16, 16);
312 			globalDefaultStyle.setImage(customGUIElems[0], "percentButtonA");
313 			globalDefaultStyle.setImage(customGUIElems[1], "percentButtonB");
314 			globalDefaultStyle.setImage(customGUIElems[2], "tileButtonA");
315 			globalDefaultStyle.setImage(customGUIElems[3], "tileButtonB");
316 			globalDefaultStyle.setImage(customGUIElems[4], "selMoveButtonA");
317 			globalDefaultStyle.setImage(customGUIElems[5], "selMoveButtonB");
318 			globalDefaultStyle.setImage(customGUIElems[6], "tilePlacementButtonA");
319 			globalDefaultStyle.setImage(customGUIElems[7], "tilePlacementButtonB");
320 			globalDefaultStyle.setImage(customGUIElems[8], "objPlacementButtonA");
321 			globalDefaultStyle.setImage(customGUIElems[9], "objPlacementButtonB");
322 			globalDefaultStyle.setImage(customGUIElems[10], "sprtPlacementButtonA");
323 			globalDefaultStyle.setImage(customGUIElems[11], "sprtPlacementButtonB");
324 			//globalDefaultStyle.setImage(customGUIElems[12], "importMaterialDataButtonA");
325 			//globalDefaultStyle.setImage(customGUIElems[13], "importMaterialDataButtonB");
326 			//globalDefaultStyle.setImage(customGUIElems[14], "paletteButtonA");
327 			//globalDefaultStyle.setImage(customGUIElems[15], "paletteButtonB");
328 		}
329 
330 		//wh.initGUI();
331 
332 		input = new InputHandler();
333 		//input.ml ~= this;
334 		input.mouseListener = wh;
335 		input.inputListener = this;
336 		input.systemEventListener = this;
337 		//input.kb ~= KeyBinding(0, SDL_SCANCODE_ESCAPE, 0, "sysesc", Devicetype.KEYBOARD);
338 		//input.kb ~= configFile.keyBindingList;
339 		input.addBinding(InputHandler.getSysEscKey, InputBinding(InputHandler.sysescCode));
340 		configFile.loadBindings(input);
341 		
342 		WindowElement.inputHandler = input;
343 		
344 		ow ~= new OutputScreen("Pixel Perfect Editor", 1696, 960);
345 
346 		rasters = new Raster(848, 480, ow[0], 0, 2);
347 		ow[0].setMainRaster(rasters);
348 		rasters.addLayer(windowing, 0);
349 		rasters.addLayer(bitmapPreview, 1);
350 		//ISSUE: Copying the palette from StyleSheet.defaultPaletteForGUI doesn't work
351 		//SOLUTION: Load the palette from a file
352 		rasters.loadPalette(loadPaletteFromFile("../system/concreteGUIE1.tga"));
353 		wh.setBaseWindow(new TopLevelWindow(848, 480, this));
354 		wh.addBackground(loadBitmapFromFile!Bitmap32Bit("../system/background.png"));
355 		openMaterialList();
356 		openLayerList();
357 	}
358 	public void menuEvent(Event ev) {
359 		if (ev.type == EventType.Menu){
360 			MenuEvent mev = cast(MenuEvent)ev;
361 			switch (mev.itemSource) {
362 				case "save":
363 					onSave();
364 					break;
365 				case "saveAs":
366 					onSaveAs();
367 					break;
368 				case "load":
369 					onLoad();
370 					break;
371 				case "newLayer":
372 					initNewTileLayer();
373 					break;
374 				case "new":
375 					//TileLayerEditor tle = new TileLayerEditor(this);
376 					//wh.addWindow(tle);
377 					onNewDocument();
378 					break;
379 				case "resizeLayer":
380 					initResizeLayer();
381 					break;
382 				case "undo":
383 					onUndo();
384 					break;
385 				case "redo":
386 					onRedo();
387 					break;
388 				case "exit":
389 					onQuit();
390 					break;
391 				case "layerList":
392 					openLayerList();
393 					break;
394 				case "materialList":
395 					openMaterialList();
396 					break;
397 				default:
398 					break;
399 			}
400 		}
401 	}
402 	
403 	/**
404 	 * Called when a keybinding event is generated.
405 	 * The `id` should be generated from a string, usually the name of the binding.
406 	 * `code` is a duplicate of the code used for fast lookup of the binding, which also contains other info (deviceID, etc).
407 	 * `timestamp` is the time lapsed since the start of the program, can be used to measure time between keypresses.
408 	 * NOTE: Hat events on joysticks don't generate keyReleased events, instead they generate keyPressed events on release.
409 	 */
410 	public void keyEvent(uint id, BindingCode code, uint timestamp, bool isPressed) {
411 		import PixelPerfectEngine.system.etc : hashCalc;
412 		switch (id) {
413 			case hashCalc("copy"):
414 				break;
415 			case hashCalc("cut"):
416 				break;
417 			case hashCalc("paste"):
418 				break;
419 			case hashCalc("undo"):
420 				if (!isPressed)
421 					onUndo;
422 				break;
423 			case hashCalc("redo"):
424 				if (!isPressed)
425 					onRedo;
426 				break;
427 			case hashCalc("save"):
428 				if (!isPressed)
429 					onSave;
430 				break;
431 			case hashCalc("saveAs"):
432 				if (!isPressed)
433 					onSaveAs;
434 				break;
435 			case hashCalc("insert"):
436 				if (!isPressed){
437 					if (materialList)
438 						materialList.ovrwrtIns.toggle();
439 					else if (selDoc)
440 						selDoc.voidfill = !selDoc.voidfill;
441 				}
442 				break;
443 			case hashCalc("delArea"):
444 				
445 				break;
446 			case hashCalc("palUp"):
447 				if (isPressed) {
448 					if (materialList)
449 						materialList.palUp_onClick(null);
450 					else if (selDoc)
451 						selDoc.tileMaterial_PaletteUp;
452 				}
453 				break;
454 			case hashCalc("palDown"):
455 				if (isPressed) {
456 					if (materialList)
457 						materialList.palDown_onClick(null);
458 					else if (selDoc)
459 						selDoc.tileMaterial_PaletteDown;
460 				}
461 				break;
462 			case hashCalc("hMirror"):
463 				if (!isPressed) {
464 					if (materialList)
465 						materialList.horizMirror.toggle;
466 				}
467 				break;
468 			case hashCalc("vMirror"):
469 				if (!isPressed) {
470 					if (materialList)
471 						materialList.vertMirror.toggle;
472 				}
473 				break;
474 			case hashCalc("place"):
475 
476 				break;
477 			case hashCalc("nextTile"):
478 				break;
479 			case hashCalc("prevTile"):
480 				break;
481 			case hashCalc("moveUp"):
482 				break;
483 			case hashCalc("moveDown"):
484 				break;
485 			case hashCalc("moveLeft"):
486 				break;
487 			case hashCalc("moveRight"):
488 				break;
489 			case hashCalc("scrollUp"):
490 				if (selDoc) {
491 					if (isPressed) 
492 						selDoc.sYAmount = -1;
493 					else
494 						selDoc.sYAmount = 0;
495 				}
496 				break;
497 			case hashCalc("scrollDown"):
498 				if (selDoc) {
499 					if (isPressed) 
500 						selDoc.sYAmount = 1;
501 					else
502 						selDoc.sYAmount = 0;
503 				}
504 				break;
505 			case hashCalc("scrollLeft"):
506 				if (selDoc) {
507 					if (isPressed) 
508 						selDoc.sXAmount = 1;
509 					else
510 						selDoc.sXAmount = 0;
511 				}
512 				break;
513 			case hashCalc("scrollRight"):
514 				if (selDoc) {
515 					if (isPressed) 
516 						selDoc.sXAmount = -1;
517 					else
518 						selDoc.sXAmount = 0;
519 				}
520 				break;
521 			case hashCalc("nextLayer"):
522 				break;
523 			case hashCalc("prevLayer"):
524 				break;
525 			case hashCalc("hideLayer"):
526 				break;
527 			case hashCalc("soloLayer"):
528 				break;
529 			default:
530 				break;
531 		}
532 	}
533 	/**
534 	 * Called when an axis is being operated.
535 	 * The `id` should be generated from a string, usually the name of the binding.
536 	 * `code` is a duplicate of the code used for fast lookup of the binding, which also contains other info (deviceID, etc).
537 	 * `timestamp` is the time lapsed since the start of the program, can be used to measure time between keypresses.
538 	 * `value` is the current position of the axis normalized between -1.0 and +1.0 for joysticks, and 0.0 and +1.0 for analog
539 	 * triggers.
540 	 */
541 	public void axisEvent(uint id, BindingCode code, uint timestamp, float value) {
542 
543 	}
544 	public void onUndo () {
545 		if(selDoc !is null){
546 			selDoc.events.undo;
547 			selDoc.outputWindow.updateRaster;
548 		}
549 	}
550 	public void onRedo () {
551 		if(selDoc !is null){
552 			selDoc.events.redo;
553 			selDoc.outputWindow.updateRaster;
554 		}
555 	}
556 	public void onLoad () {
557 		import PixelPerfectEngine.concrete.dialogs.filedialog;
558 		FileDialog fd = new FileDialog("Load document","docLoad",&onLoadDialog,[FileDialog.FileAssociationDescriptor(
559 			"PPE map file", ["*.xmf"])],"./",false);
560 		wh.addWindow(fd);
561 	}
562 	public void onNewDocument () {
563 		wh.addWindow(new NewDocumentDialog(this));
564 	}
565 	public void onLoadDialog (Event ev) {
566 		import std.utf : toUTF32;
567 		try {
568 			FileEvent event = cast(FileEvent)ev;
569 			selDoc = new MapDocument(event.getFullPath);
570 			dstring name = toUTF32(selDoc.mainDoc.getName);
571 			RasterWindow w = new RasterWindow(selDoc.mainDoc.getHorizontalResolution, selDoc.mainDoc.getVerticalResolution, 
572 					rasters.palette.ptr, name, selDoc);
573 			selDoc.outputWindow = w;
574 			wh.addWindow(w);
575 			documents[name] = selDoc;
576 			selDoc.updateLayerList();
577 			selDoc.updateMaterialList();
578 			selDoc.mainDoc.loadTiles(w);
579 			selDoc.mainDoc.loadMappingData();
580 			w.loadLayers();
581 			w.updateRaster();
582 		} catch (Exception e) {
583 			debug writeln(e);
584 		}
585 		
586 	}
587 	public void onSave () {
588 		if (selDoc.filename) {
589 			try {
590 				selDoc.mainDoc.save(selDoc.filename);
591 			} catch (Exception e) {
592 				debug writeln(e);
593 			}
594 		} else {
595 			onSaveAs();
596 		}
597 	}
598 	public void onSaveAs () {
599 		import PixelPerfectEngine.concrete.dialogs.filedialog;
600 		FileDialog fd = new FileDialog("Save document as","docSave",&onSaveDialog,[FileDialog.FileAssociationDescriptor(
601 			"PPE map file", ["*.xmf"])],"./",true);
602 		wh.addWindow(fd);
603 	}
604 	public void onSaveDialog(Event ev) {
605 		import std.path : extension;
606 		import std.ascii : toLower;
607 		FileEvent event = cast(FileEvent)ev;
608 		selDoc.filename = event.getFullPath();
609 		if(extension(selDoc.filename) != ".xmf"){
610 			selDoc.filename ~= ".xmf";
611 		}
612 		try {
613 			selDoc.mainDoc.save(selDoc.filename);
614 		} catch (Exception e) {
615 			debug writeln(e);
616 		}
617 	}
618 	
619 	public void onQuit(){onExit();}
620 	public void controllerRemoved(uint ID){}
621 	public void controllerAdded(uint ID){}
622 	public void initResizeLayer() {
623 		//import resizeMap;
624 		if (selDoc !is null) {
625 			wh.addWindow(new ResizeMap(selDoc));
626 		}
627 	}
628 	
629 	
630 	/**
631 	 * Opens a window to ask the user for the data on the new tile layer
632 	 */
633 	public void initNewTileLayer(){
634 		wh.addWindow(new NewTileLayerDialog(this));
635 	}
636 	/**
637 	 * Opens a window to ask the user for input on materials to be added
638 	 */
639 	public void initAddMaterials() {
640 		import windows.addtiles;
641 		if(selDoc){
642 			ITileLayer itl = cast(ITileLayer)selDoc.mainDoc.layeroutput[selDoc.selectedLayer];
643 			const int tileX = itl.getTileWidth, tileY = itl.getTileHeight;
644 			wh.addWindow(new AddTiles(this, tileX, tileY));
645 		}
646 	}
647 	/**
648 	 * Creates a new tile layer with the given data.
649 	 *
650 	 * file: Optional field. If given, it specifies the external file for binary map data. If it specifies an already
651 	 * existing file, then that file will be loaded. If null, then the map data will be embedded as a BASE64 chunk.
652 	 * tmplt: Optional field. Specifies the initial tile source data from a map file alongside with the name of the layer
653 	 */
654 	public void newTileLayer(int tX, int tY, int mX, int mY, dstring name, string file, bool embed) {
655 		selDoc.events.addToTop(new CreateTileLayerEvent(selDoc, tX, tY, mX, mY, name, file, embed));
656 	}
657 	public void setRasterRefresh(){
658 		rasterRefresh = true;
659 	}
660 	public void whereTheMagicHappens(){
661 		//rasters.refresh();
662 		while(!onexit){
663 			input.test();
664 
665 			rasters.refresh();
666 			if (selDoc) {
667 				selDoc.contScrollLayer();
668 			}
669 		}
670 		configFile.store();
671 	}
672 	public void onExit(){
673 		import PixelPerfectEngine.concrete.dialogs.defaultdialog;
674 		exitDialog=true;
675 		DefaultDialog dd = new DefaultDialog(Coordinate(10,10,220,75), "exitdialog","Exit application", ["Are you sure?"],
676 				["Yes","No","Pls save"],["ok","close","save"]);
677 
678 		dd.output = &confirmExit;
679 		wh.addWindow(dd);
680 
681 	}
682 	private void confirmExit(Event ev) {
683 		WindowElement we = cast(WindowElement)ev.sender;
684 		if (we.getSource == "ok") {
685 			onexit = true;
686 		}
687 	}
688 	/+public void newDocument(){
689 		NewDocumentDialog ndd = new NewDocumentDialog(input);
690 		ndd.ie = this;
691 		wh.addWindow(ndd);
692 	}+/
693 	public void createNewDocument(dstring name, int rX, int rY){
694 		import std.utf : toUTF8;
695 		MapDocument md = new MapDocument(toUTF8(name), rX, rY);
696 		RasterWindow w = new RasterWindow(rX, rY, rasters.palette.ptr, name, md);
697 		md.outputWindow = w;
698 		wh.addWindow(w);
699 		documents[name] = md;
700 		selDoc = md;
701 	}
702 	public void openLayerList() {
703 		if (!layerList) {
704 			layerList = new LayerList(0, 16, &onLayerListClosed);
705 			wh.addWindow(layerList);
706 		}
707 	}
708 	private void onLayerListClosed() {
709 		layerList = null;
710 	}
711 	public void openMaterialList() {
712 		if (!materialList) {
713 			materialList = new MaterialList(0, 230, &onMaterialListClosed);
714 			wh.addWindow(materialList);
715 		}
716 	}
717 	private void onMaterialListClosed() {
718 		materialList = null;
719 	}
720 }