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 import pixelperfectengine.system.timer; 32 33 //import converterdialog; 34 import windows.resizemap; 35 import windows.about; 36 import editorevents; 37 public import windows.layerlist; 38 public import windows.materiallist; 39 import document; 40 import windows.rasterwindow; 41 import windows.newtilelayer; 42 import clipboard; 43 44 45 46 public class NewDocumentDialog : Window{ 47 public Editor ie; 48 private TextBox[] textBoxes; 49 public this(Coordinate size, dstring title){ 50 super(size, title); 51 } 52 public this(Editor ie){ 53 this(Box(10,10,220,150),"New Document"d); 54 this.ie = ie; 55 Button[] buttons; 56 Label[] labels; 57 buttons ~= new Button("Ok", "ok", Box(150,110,200,130)); 58 59 labels ~= new Label("Name:","",Box(5,20,80,39)); 60 labels ~= new Label("RasterX:","",Box(5,40,80,59)); 61 labels ~= new Label("RasterY:","",Box(5,60,80,79)); 62 //labels ~= new Label("N. of colors:","",Coordinate(5,80,120,99)); 63 textBoxes ~= new TextBox("newdocument","name",Box(81,20,200,39)); 64 textBoxes ~= new TextBox("424","rX",Box(121,40,200,59)); 65 textBoxes ~= new TextBox("240","rY",Box(121,60,200,79)); 66 //textBoxes ~= new TextBox("","pal",Coordinate(121,80,200,99)); 67 addElement(buttons[0]); 68 foreach(WindowElement we; labels){ 69 addElement(we); 70 } 71 foreach(TextBox we; textBoxes){ 72 //we.addTextInputHandler(inputhandler); 73 addElement(we); 74 } 75 buttons[0].onMouseLClick = &buttonOn_onMouseLClickRel; 76 } 77 78 public void buttonOn_onMouseLClickRel(Event event){ 79 ie.createNewDocument(textBoxes[0].getText().text, to!int(textBoxes[1].getText().text), to!int(textBoxes[2].getText().text)); 80 81 close(); 82 } 83 } 84 85 public class TopLevelWindow : Window { 86 public this(int width, int height, Editor prg) { 87 Text mt(dstring text) @safe nothrow { 88 return new Text(text, globalDefaultStyle.getChrFormatting("menuBar")); 89 } 90 super(Box(0, 0, width, height), ""d, [], null); 91 MenuBar mb; 92 { 93 PopUpMenuElement[] menuElements; 94 menuElements ~= new PopUpMenuElement("file", mt("FILE")); 95 96 menuElements[0].setLength(7); 97 menuElements[0][0] = new PopUpMenuElement("new", "New PPE map"); 98 menuElements[0][1] = new PopUpMenuElement("newTemp", "New PPE map from template"); 99 menuElements[0][2] = new PopUpMenuElement("load", "Load PPE map"); 100 menuElements[0][3] = new PopUpMenuElement("save", "Save PPE map"); 101 menuElements[0][4] = new PopUpMenuElement("saveAs", "Save PPE map as"); 102 menuElements[0][5] = new PopUpMenuElement("saveTemp", "Save PPE map as template"); 103 menuElements[0][6] = new PopUpMenuElement("exit", "Exit application"); 104 105 menuElements ~= new PopUpMenuElement("edit", mt("EDIT")); 106 107 menuElements[1].setLength(7); 108 menuElements[1][0] = new PopUpMenuElement("undo", "Undo"); 109 menuElements[1][1] = new PopUpMenuElement("redo", "Redo"); 110 menuElements[1][2] = new PopUpMenuElement("copy", "Copy"); 111 menuElements[1][3] = new PopUpMenuElement("cut", "Cut"); 112 menuElements[1][4] = new PopUpMenuElement("paste", "Paste"); 113 menuElements[1][5] = new PopUpMenuElement("editorSetup", "Editor settings"); 114 menuElements[1][6] = new PopUpMenuElement("docSetup", "Document settings"); 115 116 menuElements ~= new PopUpMenuElement("view", mt("VIEW")); 117 118 //menuElements[2].setLength(2); 119 menuElements[2] ~= new PopUpMenuElement("layerList", "Layers"); 120 menuElements[2] ~= new PopUpMenuElement("materialList", "Materials"); 121 menuElements[2] ~= new PopUpMenuElement("viewgrid", "Grid"); 122 menuElements[2] ~= new PopUpMenuElement("viewobj", "Objects"); 123 menuElements[2] ~= new PopUpMenuElement("resetLayers", "Reset layer display"); 124 //menuElements[2][2] = new PopUpMenuElement("layerTools", "Layer tools", "Alt + T"); 125 126 menuElements ~= new PopUpMenuElement("layers", mt("LAYERS")); 127 128 //menuElements[3].setLength(5); 129 menuElements[3] ~= new PopUpMenuElement("newLayer", "New layer"); 130 menuElements[3] ~= new PopUpMenuElement("delLayer", "Delete layer"); 131 menuElements[3] ~= new PopUpMenuElement("\\submenu\\", "Import layerdata", ">"); 132 menuElements[3][2] ~= new PopUpMenuElement("tiledcsvi", "Tiled CSV file"); 133 menuElements[3][2] ~= new PopUpMenuElement("ppebinmapi", "PPE binary map file"); 134 menuElements[3] ~= new PopUpMenuElement("\\submenu\\", "Export layerdata", ">"); 135 menuElements[3][3] ~= new PopUpMenuElement("tiledcsve", "Tiled CSV file"); 136 menuElements[3][3] ~= new PopUpMenuElement("ppebinmape", "PPE binary map file"); 137 menuElements[3] ~= new PopUpMenuElement("layerSrc", "Layer resources"); 138 menuElements[3] ~= new PopUpMenuElement("resizeLayer", "Resize layer"); 139 140 menuElements ~= new PopUpMenuElement("tools", mt("TOOLS")); 141 142 menuElements[4].setLength(2); 143 menuElements[4][0] = new PopUpMenuElement("tgaTool", "TGA Toolkit"); 144 menuElements[4][1] = new PopUpMenuElement("bmfontTool", "BMFont Toolkit"); 145 146 menuElements ~= new PopUpMenuElement("help", mt("HELP")); 147 148 menuElements[5].setLength(2); 149 menuElements[5][0] = new PopUpMenuElement("helpFile", "Content"); 150 menuElements[5][1] = new PopUpMenuElement("about", "About"); 151 152 mb = new MenuBar("mb", Box(0,0, width - 1, 15), menuElements); 153 154 mb.onMenuEvent = &prg.menuEvent; 155 } 156 addElement(mb); 157 } 158 public override void draw(bool drawHeaderOnly = false) { 159 output.drawFilledBox(position, 0); 160 foreach (WindowElement we; elements) { 161 we.draw(); 162 } 163 } 164 public override void drawHeader() { 165 166 } 167 ///Passes mouse click event 168 public override void passMCE(MouseEventCommons mec, MouseClickEvent mce) { 169 lastMousePos = Point(mce.x - position.left, mce.y - position.top); 170 foreach (WindowElement we; elements) { 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 foreach (ISmallButton sb; smallButtons) { 180 WindowElement we = cast(WindowElement)sb; 181 if (we.getPosition.isBetween(lastMousePos)) { 182 lastMouseEventTarget = we; 183 mce.x = lastMousePos.x; 184 mce.y = lastMousePos.y; 185 we.passMCE(mec, mce); 186 return; 187 } 188 } 189 lastMouseEventTarget = null; 190 } 191 ///Passes mouse move event 192 public override void passMME(MouseEventCommons mec, MouseMotionEvent mme) { 193 lastMousePos = Point(mme.x - position.left, mme.y - position.top); 194 if (lastMouseEventTarget) { 195 mme.x = lastMousePos.x; 196 mme.y = lastMousePos.y; 197 lastMouseEventTarget.passMME(mec, mme); 198 if (!lastMouseEventTarget.getPosition.isBetween(mme.x, mme.y)) { 199 lastMouseEventTarget = null; 200 } 201 } else { 202 foreach (WindowElement we; elements) { 203 if (we.getPosition.isBetween(lastMousePos)) { 204 lastMouseEventTarget = we; 205 mme.x = lastMousePos.x; 206 mme.y = lastMousePos.y; 207 we.passMME(mec, mme); 208 return; 209 } 210 } 211 } 212 } 213 } 214 215 public class Editor : InputListener, SystemEventListener { 216 public OutputScreen[] ow; 217 public Raster rasters; 218 public InputHandler input; 219 public wchar selectedTile; 220 public BitmapAttrib selectedTileAttrib; 221 public int selectedLayer; 222 public SpriteLayer windowing; 223 public SpriteLayer bitmapPreview; 224 public bool onexit, exitDialog, newLayerDialog, mouseState, rasterRefresh; 225 public Window test; 226 public WindowHandler wh; 227 //public EffectLayer selectionLayer; 228 //public ForceFeedbackHandler ffb; 229 //private uint[5] framecounter; 230 public char[40] windowTitle; 231 public ConfigurationProfile configFile; 232 private int mouseX, mouseY; 233 private Coordinate selection, selectedTiles; 234 //public PlacementMode pm; 235 //public UndoableStack undoStack; 236 //public PaletteManager palman; 237 public MapDocument[dstring] documents; 238 public MapDocument selDoc; 239 public LayerList layerList; 240 public MaterialList materialList; 241 public MapClipboard mapClipboard; 242 243 public this(string[] args){ 244 ConfigurationProfile.setVaultPath("ZILtoid1991","PixelPerfectEditor"); 245 if (args.length > 1) { 246 if (args[1] == "--restore") { 247 ConfigurationProfile.restoreDefaults; 248 } 249 } 250 configFile = new ConfigurationProfile(); 251 252 windowing = new SpriteLayer(RenderingMode.Copy); 253 bitmapPreview = new SpriteLayer(); 254 255 wh = new WindowHandler(1696,960,848,480,windowing); 256 //wh.ie = this; 257 258 //Initialize the Concrete framework 259 INIT_CONCRETE(); 260 //writeln(globalDefaultStyle.drawParameters); 261 //Initialize custom GUI elements 262 { 263 Bitmap8Bit[] customGUIElems = loadBitmapSheetFromFile!Bitmap8Bit("../system/concreteGUIE1.tga", 16, 16); 264 globalDefaultStyle.setImage(customGUIElems[0], "menuButtonA"); 265 globalDefaultStyle.setImage(customGUIElems[1], "menuButtonB"); 266 globalDefaultStyle.setImage(customGUIElems[2], "fullSizeButtonA"); 267 globalDefaultStyle.setImage(customGUIElems[3], "fullSizeButtonB"); 268 globalDefaultStyle.setImage(customGUIElems[4], "smallSizeButtonA"); 269 globalDefaultStyle.setImage(customGUIElems[5], "smallSizeButtonB"); 270 globalDefaultStyle.setImage(customGUIElems[6], "newDocumentButtonA"); 271 globalDefaultStyle.setImage(customGUIElems[7], "newDocumentButtonB"); 272 globalDefaultStyle.setImage(customGUIElems[8], "saveDocumentButtonA"); 273 globalDefaultStyle.setImage(customGUIElems[9], "saveDocumentButtonB"); 274 globalDefaultStyle.setImage(customGUIElems[10], "loadDocumentButtonA"); 275 globalDefaultStyle.setImage(customGUIElems[11], "loadDocumentButtonB"); 276 globalDefaultStyle.setImage(customGUIElems[12], "settingsButtonA"); 277 globalDefaultStyle.setImage(customGUIElems[13], "settingsButtonB"); 278 globalDefaultStyle.setImage(customGUIElems[14], "blankButtonA"); 279 globalDefaultStyle.setImage(customGUIElems[15], "blankButtonB"); 280 } 281 { 282 Bitmap8Bit[] customGUIElems = loadBitmapSheetFromFile!Bitmap8Bit("../system/concreteGUIE4.tga", 16, 16); 283 globalDefaultStyle.setImage(customGUIElems[0], "addMaterialA"); 284 globalDefaultStyle.setImage(customGUIElems[1], "addMaterialB"); 285 globalDefaultStyle.setImage(customGUIElems[2], "removeMaterialA"); 286 globalDefaultStyle.setImage(customGUIElems[3], "removeMaterialB"); 287 globalDefaultStyle.setImage(customGUIElems[4], "horizMirrorA"); 288 globalDefaultStyle.setImage(customGUIElems[5], "horizMirrorB"); 289 globalDefaultStyle.setImage(customGUIElems[6], "vertMirrorA"); 290 globalDefaultStyle.setImage(customGUIElems[7], "vertMirrorB"); 291 globalDefaultStyle.setImage(customGUIElems[8], "ovrwrtInsA"); 292 globalDefaultStyle.setImage(customGUIElems[9], "ovrwrtInsB"); 293 //globalDefaultStyle.setImage(customGUIElems[10], ""); 294 //globalDefaultStyle.setImage(customGUIElems[11], ""); 295 globalDefaultStyle.setImage(customGUIElems[12], "paletteDownA"); 296 globalDefaultStyle.setImage(customGUIElems[13], "paletteDownB"); 297 globalDefaultStyle.setImage(customGUIElems[14], "paletteUpA"); 298 globalDefaultStyle.setImage(customGUIElems[15], "paletteUpB"); 299 } 300 { 301 Bitmap8Bit[] customGUIElems = loadBitmapSheetFromFile!Bitmap8Bit("../system/concreteGUIE3.tga", 16, 16); 302 globalDefaultStyle.setImage(customGUIElems[0], "trashButtonA"); 303 globalDefaultStyle.setImage(customGUIElems[1], "trashButtonB"); 304 globalDefaultStyle.setImage(customGUIElems[2], "visibilityButtonA"); 305 globalDefaultStyle.setImage(customGUIElems[3], "visibilityButtonB"); 306 globalDefaultStyle.setImage(customGUIElems[4], "newTileLayerButtonA"); 307 globalDefaultStyle.setImage(customGUIElems[5], "newTileLayerButtonB"); 308 globalDefaultStyle.setImage(customGUIElems[6], "newSpriteLayerButtonA"); 309 globalDefaultStyle.setImage(customGUIElems[7], "newSpriteLayerButtonB"); 310 globalDefaultStyle.setImage(customGUIElems[8], "newTransformableTileLayerButtonA"); 311 globalDefaultStyle.setImage(customGUIElems[9], "newTransformableTileLayerButtonB"); 312 globalDefaultStyle.setImage(customGUIElems[10], "importLayerDataButtonA"); 313 globalDefaultStyle.setImage(customGUIElems[11], "importLayerDataButtonB"); 314 globalDefaultStyle.setImage(customGUIElems[12], "importMaterialDataButtonA"); 315 globalDefaultStyle.setImage(customGUIElems[13], "importMaterialDataButtonB"); 316 globalDefaultStyle.setImage(customGUIElems[14], "paletteButtonA"); 317 globalDefaultStyle.setImage(customGUIElems[15], "paletteButtonB"); 318 } 319 { 320 Bitmap8Bit[] customGUIElems = loadBitmapSheetFromFile!Bitmap8Bit("../system/concreteGUIE5.tga", 16, 16); 321 globalDefaultStyle.setImage(customGUIElems[0], "percentButtonA"); 322 globalDefaultStyle.setImage(customGUIElems[1], "percentButtonB"); 323 globalDefaultStyle.setImage(customGUIElems[2], "tileButtonA"); 324 globalDefaultStyle.setImage(customGUIElems[3], "tileButtonB"); 325 globalDefaultStyle.setImage(customGUIElems[4], "selMoveButtonA"); 326 globalDefaultStyle.setImage(customGUIElems[5], "selMoveButtonB"); 327 globalDefaultStyle.setImage(customGUIElems[6], "tilePlacementButtonA"); 328 globalDefaultStyle.setImage(customGUIElems[7], "tilePlacementButtonB"); 329 globalDefaultStyle.setImage(customGUIElems[8], "objPlacementButtonA"); 330 globalDefaultStyle.setImage(customGUIElems[9], "objPlacementButtonB"); 331 globalDefaultStyle.setImage(customGUIElems[10], "sprtPlacementButtonA"); 332 globalDefaultStyle.setImage(customGUIElems[11], "sprtPlacementButtonB"); 333 //globalDefaultStyle.setImage(customGUIElems[12], "importMaterialDataButtonA"); 334 //globalDefaultStyle.setImage(customGUIElems[13], "importMaterialDataButtonB"); 335 globalDefaultStyle.setImage(customGUIElems[14], "soloButtonA"); 336 globalDefaultStyle.setImage(customGUIElems[15], "soloButtonB"); 337 } 338 339 //wh.initGUI(); 340 341 input = new InputHandler(); 342 //input.ml ~= this; 343 input.mouseListener = wh; 344 input.inputListener = this; 345 input.systemEventListener = this; 346 //input.kb ~= KeyBinding(0, SDL_SCANCODE_ESCAPE, 0, "sysesc", Devicetype.KEYBOARD); 347 //input.kb ~= configFile.keyBindingList; 348 input.addBinding(InputHandler.getSysEscKey, InputBinding(InputHandler.sysescCode)); 349 configFile.loadBindings(input); 350 351 WindowElement.inputHandler = input; 352 353 ow ~= new OutputScreen("Pixel Perfect Editor", 1696, 960); 354 355 rasters = new Raster(848, 480, ow[0], 0, 2); 356 ow[0].setMainRaster(rasters); 357 rasters.addLayer(windowing, 0); 358 rasters.addLayer(bitmapPreview, 1); 359 rasters.loadPalette(loadPaletteFromFile("../system/concreteGUIE1.tga")); 360 wh.setBaseWindow(new TopLevelWindow(848, 480, this)); 361 wh.addBackground(loadBitmapFromFile!Bitmap32Bit("../system/background.png")); 362 mapClipboard = new MapClipboard(10); 363 openMaterialList(); 364 openLayerList(); 365 } 366 public void menuEvent(Event ev) { 367 if (ev.type == EventType.Menu){ 368 MenuEvent mev = cast(MenuEvent)ev; 369 switch (mev.itemSource) { 370 case "save": 371 onSave(); 372 break; 373 case "saveAs": 374 onSaveAs(); 375 break; 376 case "load": 377 onLoad(); 378 break; 379 case "newLayer": 380 initNewTileLayer(); 381 break; 382 case "new": 383 //TileLayerEditor tle = new TileLayerEditor(this); 384 //wh.addWindow(tle); 385 onNewDocument(); 386 break; 387 case "resizeLayer": 388 initResizeLayer(); 389 break; 390 case "undo": 391 onUndo(); 392 break; 393 case "redo": 394 onRedo(); 395 break; 396 case "exit": 397 onQuit(); 398 break; 399 case "layerList": 400 openLayerList(); 401 break; 402 case "materialList": 403 openMaterialList(); 404 break; 405 case "tiledcsvi": 406 if (selDoc) { 407 if (selDoc.getLayerInfo(selDoc.selectedLayer).type == LayerType.Tile || 408 selDoc.getLayerInfo(selDoc.selectedLayer).type == LayerType.TransformableTile) { 409 import pixelperfectengine.concrete.dialogs.filedialog; 410 wh.addWindow(new FileDialog("Import layer from CSV", "tiledcsvi", &tiledCSVImport, 411 [FileDialog.FileAssociationDescriptor("Tiled CSV file", ["*.csv"])], "./",)); 412 } 413 } 414 break; 415 case "tiledcsve": 416 if (selDoc) { 417 if (selDoc.getLayerInfo(selDoc.selectedLayer).type == LayerType.Tile || 418 selDoc.getLayerInfo(selDoc.selectedLayer).type == LayerType.TransformableTile) { 419 import pixelperfectengine.concrete.dialogs.filedialog; 420 wh.addWindow(new FileDialog("Export layer as CSV", "tiledcsve", &tiledCSVExport, 421 [FileDialog.FileAssociationDescriptor("Tiled CSV file", ["*.csv"])], "./", true)); 422 } 423 } 424 break; 425 case "ppebinmapi": 426 if (selDoc) { 427 if (selDoc.getLayerInfo(selDoc.selectedLayer).type == LayerType.Tile || 428 selDoc.getLayerInfo(selDoc.selectedLayer).type == LayerType.TransformableTile) { 429 import pixelperfectengine.concrete.dialogs.filedialog; 430 wh.addWindow(new FileDialog("Import layer from MBF", "ppebinmapi", &ppeBinImport, 431 [FileDialog.FileAssociationDescriptor("PixelPerfectEngine map binary file", ["*.mbf"])], "./",)); 432 } 433 } 434 break; 435 case "ppebinmape": 436 if (selDoc) { 437 if (selDoc.getLayerInfo(selDoc.selectedLayer).type == LayerType.Tile || 438 selDoc.getLayerInfo(selDoc.selectedLayer).type == LayerType.TransformableTile) { 439 import pixelperfectengine.concrete.dialogs.filedialog; 440 wh.addWindow(new FileDialog("Export layer as MBF", "ppebinmape", &ppeBinExport, 441 [FileDialog.FileAssociationDescriptor("PixelPerfectEngine map binary file", ["*.mbf"])], "./", true)); 442 } 443 } 444 break; 445 case "resetLayers": 446 if (selDoc) { 447 selDoc.outputWindow.clearDisplayLists(); 448 } 449 break; 450 case "copy": 451 onCopy(); 452 break; 453 case "cut": 454 onCut(); 455 break; 456 case "paste": 457 onPaste(); 458 break; 459 default: 460 break; 461 } 462 } 463 } 464 private void tiledCSVImport(Event ev) { 465 import csvconv : fromCSV; 466 try { 467 if (selDoc) { 468 if (selDoc.getLayerInfo(selDoc.selectedLayer).type == LayerType.Tile || 469 selDoc.getLayerInfo(selDoc.selectedLayer).type == LayerType.TransformableTile) { 470 ITileLayer target = cast(ITileLayer)(selDoc.mainDoc.layeroutput[selDoc.selectedLayer]); 471 FileEvent fev = cast(FileEvent)ev; 472 fromCSV(fev.getFullPath, selDoc); 473 } 474 } 475 } catch (Exception e) { 476 wh.message("CSV Import Error!", to!dstring(e.msg)); 477 } 478 } 479 private void tiledCSVExport(Event ev) { 480 import csvconv : toCSV; 481 try { 482 if (selDoc) { 483 if (selDoc.getLayerInfo(selDoc.selectedLayer).type == LayerType.Tile || 484 selDoc.getLayerInfo(selDoc.selectedLayer).type == LayerType.TransformableTile) { 485 ITileLayer target = cast(ITileLayer)(selDoc.mainDoc.layeroutput[selDoc.selectedLayer]); 486 FileEvent fev = cast(FileEvent)ev; 487 toCSV(fev.getFullPath, target); 488 } 489 } 490 } catch (Exception e) { 491 wh.message("CSV Export Error!", to!dstring(e.msg)); 492 } 493 } 494 private void ppeBinImport(Event ev) { 495 import pixelperfectengine.map.mapdata; 496 try { 497 if (selDoc) { 498 if (selDoc.getLayerInfo(selDoc.selectedLayer).type == LayerType.Tile || 499 selDoc.getLayerInfo(selDoc.selectedLayer).type == LayerType.TransformableTile) { 500 FileEvent fev = cast(FileEvent)ev; 501 File source = File(fev.getFullPath, "rb"); 502 MapDataHeader header; 503 MappingElement[] map = loadMapFile(source, header); 504 selDoc.assignImportedTilemap(map, header.sizeX, header.sizeY); 505 } 506 } 507 } catch (Exception e) { 508 wh.message("MBF Import Error!", to!dstring(e.msg)); 509 } 510 } 511 private void ppeBinExport(Event ev) { 512 import pixelperfectengine.map.mapdata; 513 try { 514 if (selDoc) { 515 if (selDoc.getLayerInfo(selDoc.selectedLayer).type == LayerType.Tile || 516 selDoc.getLayerInfo(selDoc.selectedLayer).type == LayerType.TransformableTile) { 517 ITileLayer source = cast(ITileLayer)(selDoc.mainDoc.layeroutput[selDoc.selectedLayer]); 518 FileEvent fev = cast(FileEvent)ev; 519 File target = File(fev.getFullPath, "wb"); 520 MapDataHeader header = MapDataHeader(source.getMX, source.getMY); 521 saveMapFile(header, source.getMapping, target); 522 } 523 } 524 } catch (Exception e) { 525 wh.message("MBF Export Error!", to!dstring(e.msg)); 526 } 527 } 528 /** 529 * Called when a keybinding event is generated. 530 * The `id` should be generated from a string, usually the name of the binding. 531 * `code` is a duplicate of the code used for fast lookup of the binding, which also contains other info (deviceID, etc). 532 * `timestamp` is the time lapsed since the start of the program, can be used to measure time between keypresses. 533 * NOTE: Hat events on joysticks don't generate keyReleased events, instead they generate keyPressed events on release. 534 */ 535 public void keyEvent(uint id, BindingCode code, uint timestamp, bool isPressed) { 536 import pixelperfectengine.system.etc : hashCalc; 537 switch (id) { 538 case hashCalc("copy"): 539 if (!isPressed) 540 onCopy; 541 break; 542 case hashCalc("cut"): 543 if (!isPressed) 544 onCut; 545 break; 546 case hashCalc("paste"): 547 if (!isPressed) 548 onPaste; 549 break; 550 case hashCalc("undo"): 551 if (!isPressed) 552 onUndo; 553 break; 554 case hashCalc("redo"): 555 if (!isPressed) 556 onRedo; 557 break; 558 case hashCalc("save"): 559 if (!isPressed) 560 onSave; 561 break; 562 case hashCalc("saveAs"): 563 if (!isPressed) 564 onSaveAs; 565 break; 566 case hashCalc("insert"): 567 if (!isPressed){ 568 if (materialList) 569 materialList.ovrwrtIns.toggle(); 570 else if (selDoc) 571 selDoc.voidfill = !selDoc.voidfill; 572 } 573 break; 574 case hashCalc("delArea"): 575 if (selDoc && isPressed) 576 selDoc.deleteArea(); 577 break; 578 case hashCalc("palUp"): 579 if (isPressed) { 580 if (materialList) 581 materialList.palUp_onClick(null); 582 else if (selDoc) 583 selDoc.tileMaterial_PaletteUp; 584 } 585 break; 586 case hashCalc("palDown"): 587 if (isPressed) { 588 if (materialList) 589 materialList.palDown_onClick(null); 590 else if (selDoc) 591 selDoc.tileMaterial_PaletteDown; 592 } 593 break; 594 case hashCalc("hMirror"): 595 if (selDoc && !isPressed) { 596 if (materialList) 597 materialList.horizMirror.toggle; 598 else 599 selDoc.tileMaterial_FlipHorizontal; 600 } 601 break; 602 case hashCalc("selFlipHoriz"): 603 if (selDoc && !isPressed) { 604 selDoc.flipTilesHoriz(); 605 } 606 break; 607 case hashCalc("selFlipVert"): 608 if (selDoc && !isPressed) { 609 selDoc.flipTilesVert(); 610 } 611 break; 612 case hashCalc("selMirrorHoriz"): 613 if (selDoc && !isPressed) { 614 selDoc.selMirrorHoriz(); 615 } 616 break; 617 case hashCalc("selMirrorVert"): 618 if (selDoc && !isPressed) { 619 selDoc.selMirrorVert(); 620 } 621 break; 622 case hashCalc("vMirror"): 623 if (selDoc && !isPressed) { 624 if (materialList) 625 materialList.vertMirror.toggle; 626 else 627 selDoc.tileMaterial_FlipVertical; 628 } 629 break; 630 case hashCalc("place"): 631 if (selDoc && !isPressed) 632 selDoc.fillSelectedArea(); 633 break; 634 case hashCalc("nextTile"): 635 if (selDoc && isPressed) { 636 if (materialList) { 637 materialList.nextTile(); 638 } else { 639 selDoc.tileMaterial_Up(); 640 } 641 } 642 break; 643 case hashCalc("prevTile"): 644 if (selDoc && isPressed) { 645 if (materialList) { 646 materialList.prevTile(); 647 } else { 648 selDoc.tileMaterial_Down(); 649 } 650 } 651 break; 652 case hashCalc("moveUp"): 653 if (selDoc && isPressed) 654 selDoc.moveSelection(0, -1); 655 break; 656 case hashCalc("moveDown"): 657 if (selDoc && isPressed) 658 selDoc.moveSelection(0, 1); 659 break; 660 case hashCalc("moveLeft"): 661 if (selDoc && isPressed) 662 selDoc.moveSelection(-1, 0); 663 break; 664 case hashCalc("moveRight"): 665 if (selDoc && isPressed) 666 selDoc.moveSelection(1, 0); 667 break; 668 case hashCalc("scrollUp"): 669 if (selDoc) { 670 if (isPressed) 671 selDoc.sYAmount = -1; 672 else 673 selDoc.sYAmount = 0; 674 } 675 break; 676 case hashCalc("scrollDown"): 677 if (selDoc) { 678 if (isPressed) 679 selDoc.sYAmount = 1; 680 else 681 selDoc.sYAmount = 0; 682 } 683 break; 684 case hashCalc("scrollLeft"): 685 if (selDoc) { 686 if (isPressed) 687 selDoc.sXAmount = 1; 688 else 689 selDoc.sXAmount = 0; 690 } 691 break; 692 case hashCalc("scrollRight"): 693 if (selDoc) { 694 if (isPressed) 695 selDoc.sXAmount = -1; 696 else 697 selDoc.sXAmount = 0; 698 } 699 break; 700 case hashCalc("resetLayers"): 701 if (selDoc && !isPressed) { 702 selDoc.outputWindow.clearDisplayLists(); 703 } 704 break; 705 case hashCalc("nextLayer"): 706 if (selDoc && !isPressed) { 707 if (layerList) 708 layerList.nextLayer(); 709 } 710 break; 711 case hashCalc("prevLayer"): 712 if (selDoc && !isPressed) { 713 if (layerList) 714 layerList.prevLayer(); 715 } 716 break; 717 case hashCalc("hideLayer"): 718 if (selDoc && !isPressed) { 719 if (layerList) 720 layerList.checkBox_Hide.toggle(); 721 } 722 break; 723 case hashCalc("soloLayer"): 724 if (selDoc && !isPressed) { 725 if (layerList) 726 layerList.checkBox_Solo.toggle(); 727 } 728 break; 729 default: 730 break; 731 } 732 } 733 /** 734 * Called when an axis is being operated. 735 * The `id` should be generated from a string, usually the name of the binding. 736 * `code` is a duplicate of the code used for fast lookup of the binding, which also contains other info (deviceID, etc). 737 * `timestamp` is the time lapsed since the start of the program, can be used to measure time between keypresses. 738 * `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 739 * triggers. 740 */ 741 public void axisEvent(uint id, BindingCode code, uint timestamp, float value) { 742 743 } 744 public void onUndo () { 745 if(selDoc !is null){ 746 selDoc.events.undo; 747 selDoc.outputWindow.updateRaster; 748 } 749 } 750 public void onRedo () { 751 if(selDoc !is null){ 752 selDoc.events.redo; 753 selDoc.outputWindow.updateRaster; 754 } 755 } 756 public void onLoad () { 757 import pixelperfectengine.concrete.dialogs.filedialog; 758 FileDialog fd = new FileDialog("Load document","docLoad",&onLoadDialog,[FileDialog.FileAssociationDescriptor( 759 "PPE map file", ["*.xmf"])],"./",false); 760 wh.addWindow(fd); 761 } 762 public void onNewDocument () { 763 wh.addWindow(new NewDocumentDialog(this)); 764 } 765 public void onLoadDialog (Event ev) { 766 import std.utf : toUTF32; 767 try { 768 FileEvent event = cast(FileEvent)ev; 769 selDoc = new MapDocument(event.getFullPath); 770 dstring name = toUTF32(selDoc.mainDoc.getName); 771 RasterWindow w = new RasterWindow(selDoc.mainDoc.getHorizontalResolution, selDoc.mainDoc.getVerticalResolution, 772 rasters.palette.ptr, name, selDoc); 773 selDoc.outputWindow = w; 774 wh.addWindow(w); 775 documents[name] = selDoc; 776 selDoc.updateLayerList(); 777 selDoc.updateMaterialList(); 778 selDoc.mainDoc.loadTiles(w); 779 selDoc.mainDoc.loadMappingData(); 780 w.loadLayers(); 781 w.updateRaster(); 782 } catch (Exception e) { 783 debug writeln(e); 784 } 785 786 } 787 public void onSave () { 788 if (selDoc) { 789 if (selDoc.filename) { 790 try { 791 selDoc.mainDoc.save(selDoc.filename); 792 } catch (Exception e) { 793 debug writeln(e); 794 } 795 } else { 796 onSaveAs(); 797 } 798 } 799 } 800 public void onSaveAs () { 801 import pixelperfectengine.concrete.dialogs.filedialog; 802 FileDialog fd = new FileDialog("Save document as","docSave",&onSaveDialog,[FileDialog.FileAssociationDescriptor( 803 "PPE map file", ["*.xmf"])],"./",true); 804 wh.addWindow(fd); 805 } 806 public void onSaveDialog(Event ev) { 807 import std.path : extension; 808 import std.ascii : toLower; 809 FileEvent event = cast(FileEvent)ev; 810 selDoc.filename = event.getFullPath(); 811 if(extension(selDoc.filename) != ".xmf"){ 812 selDoc.filename ~= ".xmf"; 813 } 814 try { 815 selDoc.mainDoc.save(selDoc.filename); 816 } catch (Exception e) { 817 debug writeln(e); 818 } 819 } 820 public void onCopy() { 821 if (selDoc !is null) { 822 selDoc.copy(); 823 } 824 } 825 public void onCut() { 826 if (selDoc !is null) { 827 selDoc.cut(); 828 } 829 } 830 public void onPaste() { 831 if (selDoc !is null) { 832 selDoc.paste(); 833 } 834 } 835 836 public void onQuit(){onExit();} 837 public void controllerRemoved(uint ID){} 838 public void controllerAdded(uint ID){} 839 public void initResizeLayer() { 840 //import resizeMap; 841 if (selDoc !is null) { 842 wh.addWindow(new ResizeMap(selDoc)); 843 } 844 } 845 846 847 /** 848 * Opens a window to ask the user for the data on the new tile layer 849 */ 850 public void initNewTileLayer(){ 851 if (selDoc !is null) 852 wh.addWindow(new NewTileLayerDialog(this)); 853 } 854 /** 855 * Opens a window to ask the user for input on materials to be added 856 */ 857 public void initAddMaterials() { 858 import windows.addtiles; 859 if (selDoc !is null) { 860 if (selDoc.mainDoc.getLayerInfo(selDoc.selectedLayer).type == LayerType.Tile || 861 selDoc.mainDoc.getLayerInfo(selDoc.selectedLayer).type == LayerType.TransformableTile) { 862 ITileLayer itl = cast(ITileLayer)selDoc.mainDoc.layeroutput[selDoc.selectedLayer]; 863 const int tileX = itl.getTileWidth, tileY = itl.getTileHeight; 864 wh.addWindow(new AddTiles(this, tileX, tileY)); 865 } 866 } 867 } 868 /** 869 * Creates a new tile layer with the given data. 870 * 871 * file: Optional field. If given, it specifies the external file for binary map data. If it specifies an already 872 * existing file, then that file will be loaded. If null, then the map data will be embedded as a BASE64 chunk. 873 * tmplt: Optional field. Specifies the initial tile source data from a map file alongside with the name of the layer 874 */ 875 public void newTileLayer(int tX, int tY, int mX, int mY, dstring name, string file, bool embed) { 876 selDoc.events.addToTop(new CreateTileLayerEvent(selDoc, tX, tY, mX, mY, name, file, embed)); 877 } 878 public void setRasterRefresh(){ 879 rasterRefresh = true; 880 } 881 public void whereTheMagicHappens(){ 882 //rasters.refresh(); 883 while(!onexit){ 884 input.test(); 885 timer.test(); 886 rasters.refresh(); 887 if (selDoc) { 888 selDoc.contScrollLayer(); 889 } 890 } 891 configFile.store(); 892 } 893 public void onExit(){ 894 import pixelperfectengine.concrete.dialogs.defaultdialog; 895 exitDialog=true; 896 DefaultDialog dd = new DefaultDialog(Coordinate(10,10,220,75), "exitdialog","Exit application", ["Are you sure?"], 897 ["Yes","No","Pls save"],["ok","close","save"]); 898 899 dd.output = &confirmExit; 900 wh.addWindow(dd); 901 902 } 903 private void confirmExit(Event ev) { 904 WindowElement we = cast(WindowElement)ev.sender; 905 if (we.getSource == "ok") { 906 onexit = true; 907 } 908 } 909 /+public void newDocument(){ 910 NewDocumentDialog ndd = new NewDocumentDialog(input); 911 ndd.ie = this; 912 wh.addWindow(ndd); 913 }+/ 914 public void createNewDocument(dstring name, int rX, int rY){ 915 import std.utf : toUTF8; 916 MapDocument md = new MapDocument(toUTF8(name), rX, rY); 917 RasterWindow w = new RasterWindow(rX, rY, rasters.palette.ptr, name, md); 918 md.outputWindow = w; 919 wh.addWindow(w); 920 documents[name] = md; 921 selDoc = md; 922 } 923 public void openLayerList() { 924 if (!layerList) { 925 layerList = new LayerList(0, 16, &onLayerListClosed); 926 wh.addWindow(layerList); 927 } 928 } 929 private void onLayerListClosed() { 930 layerList = null; 931 } 932 public void openMaterialList() { 933 if (!materialList) { 934 materialList = new MaterialList(0, 230, &onMaterialListClosed); 935 wh.addWindow(materialList); 936 } 937 } 938 private void onMaterialListClosed() { 939 materialList = null; 940 } 941 }