1 module windows.addtiles; 2 3 import pixelperfectengine.concrete.window; 4 import pixelperfectengine.concrete.dialogs.filedialog; 5 import dimage; 6 7 import editor; 8 9 import std.conv : to; 10 11 public class AddTiles : Window { 12 public enum Mode { 13 Regular, 14 RestrictTo4Bit, 15 RestrictTo8Bit, 16 RestrictTo16Bit, 17 RestrictTo32Bit, 18 } 19 Label label0; 20 Button button_Source; 21 TextBox textBox_Source; 22 Label label1; 23 TextBox textBox_palShift; 24 CheckBox checkBox_palImport; 25 CheckBox checkBox_embDat; 26 Label label2; 27 TextBox textBox_palOffset; 28 Button button_Ok; 29 Label label3; 30 TextBox textBox_name; 31 Label label4; 32 TextBox textBox_numStyle; 33 Editor editor; 34 Image source; 35 int sizeX, sizeY; 36 public this(Editor editor, int sizeX, int sizeY, Mode mode = Mode.Regular) { 37 super(Coordinate(0, 0, 165, 230), "Add Tiles"d); 38 label0 = new Label("Source:"d, "label0", Coordinate(5, 21, 88, 40)); 39 button_Source = new Button("Browse"d, "button_Source", Coordinate(90, 20, 160, 40)); 40 textBox_Source = new TextBox(""d, "textBox_Source", Coordinate(5, 42, 160, 62)); 41 label1 = new Label("paletteShift:"d, "label1", Coordinate(5, 64, 88, 82)); 42 textBox_palShift = new TextBox("8"d, "textBox_palShift", Coordinate(100, 64, 160, 84)); 43 checkBox_palImport = new CheckBox("Import palette"d, "CheckBox0", Coordinate(5, 156, 160, 172)); 44 checkBox_embDat = new CheckBox("Use embedded mat data"d, "CheckBox1", Coordinate(5, 173, 160, 189)); 45 label2 = new Label("paletteOffset:"d, "label2", Coordinate(5, 88, 99, 105)); 46 textBox_palOffset = new TextBox("0"d, "textBox_palOffset", Coordinate(100, 86, 160, 106)); 47 textBox_palOffset.setFilter(TextInputFieldType.IntegerP); 48 button_Ok = new Button("Ok"d, "button_Ok", Coordinate(91, 206, 161, 226)); 49 label3 = new Label("name:"d, "label3", Coordinate(5, 110, 79, 126)); 50 textBox_name = new TextBox(""d, "textBox_name", Coordinate(45, 108, 160, 128)); 51 label4 = new Label("Num style and from:"d, "label4", Coordinate(5, 130, 120, 150)); 52 textBox_numStyle = new TextBox("h0000"d, "textBox_numStyle", Coordinate(120, 130, 160, 150)); 53 this.sizeX = sizeX; 54 this.sizeY = sizeY; 55 addElement(label0); 56 addElement(button_Source); 57 button_Source.onMouseLClick = &button_Source_onClick; 58 addElement(textBox_Source); 59 textBox_Source.onTextInput = &textBox_Source_onTextInput; 60 addElement(label1); 61 addElement(textBox_palShift); 62 addElement(checkBox_palImport); 63 addElement(checkBox_embDat); 64 addElement(label2); 65 addElement(textBox_palOffset); 66 addElement(button_Ok); 67 button_Ok.onMouseLClick = &button_Ok_onClick; 68 addElement(label3); 69 addElement(textBox_name); 70 addElement(label4); 71 addElement(textBox_numStyle); 72 checkBox_embDat.state = ElementState.Disabled; 73 this.editor = editor; 74 } 75 private void button_Source_onClick(Event e) { 76 handler.addWindow(new FileDialog("Import Tile Source"d, "fileDialog_TSBrowse", &fileDialog_TSBrowse_event, 77 [FileDialog.FileAssociationDescriptor("All supported formats", ["*.tga", "*.png", "*.bmp"]), 78 /+FileDialog.FileAssociationDescriptor("PPE Extendible Map file", ["*.xmf"]),+/ 79 FileDialog.FileAssociationDescriptor("Targa Graphics File", ["*.tga"]), 80 FileDialog.FileAssociationDescriptor("Windows Bitmap File", ["*.bmp"]), 81 FileDialog.FileAssociationDescriptor("Portable Network Graphics File", ["*.png"]),], "./")); 82 } 83 private void textBox_palShift_onTextInput(Event e) { 84 //validate input type 85 import pixelperfectengine.system.etc : isInteger; 86 const int value = to!int(textBox_palShift.getText.text); 87 if (value < 1 || value > 8) { 88 handler.message("Bad value!"d, "Value must be between 1 and 8!"d); 89 } 90 } 91 private void fileDialog_TSBrowse_event(Event ev) { 92 FileEvent e = cast(FileEvent)ev; 93 if (!loadFile(e.getFullPath)) { 94 textBox_Source.setText(to!dstring(e.getFullPath)); 95 } 96 } 97 private void textBox_Source_onTextInput(Event e) { 98 if (loadFile(to!string(textBox_Source.getText.text))) { 99 textBox_Source.setText(""d); 100 } 101 102 } 103 private int loadFile(string path) { 104 import std.path : extension; 105 import std.stdio : File; 106 import pixelperfectengine.system.etc : nextPow2; 107 File f = File(path); 108 try { 109 switch (path.extension) { 110 case ".xmp"://TO DO: enable importing material data from other map files 111 break; 112 case ".png": 113 source = PNG.load(f); 114 break; 115 case ".tga": 116 source = TGA.load!(File, true, true)(f); 117 break; 118 case ".bmp": 119 source = BMP.load(f); 120 break; 121 default: 122 handler.message("Unsupported file format!"d, "The specified file format is not supported!"d); 123 return -1; 124 } 125 } catch (Exception ex) { 126 import std.conv : to; 127 handler.message("Error!"d, to!dstring(ex.msg)); 128 return -1; 129 } 130 if (source.width % sizeX || source.height % sizeY) { 131 handler.message("Tile size Mismatch!"d, "Supplied bitmap file is unsuitable for this layer as a tile source!"d); 132 source = null; 133 return -1; 134 } 135 if(source.isIndexed) { 136 checkBox_palImport.check(); 137 const int paletteLengthPOw2 = cast(int)nextPow2(source.palette.length); 138 textBox_palShift.state = ElementState.Enabled; 139 switch (paletteLengthPOw2) { 140 case 2: 141 textBox_palShift.setText("1"); 142 break; 143 case 4: 144 textBox_palShift.setText("2"); 145 break; 146 case 8: 147 textBox_palShift.setText("3"); 148 break; 149 case 16: 150 textBox_palShift.setText("4"); 151 break; 152 case 32: 153 textBox_palShift.setText("5"); 154 break; 155 case 64: 156 textBox_palShift.setText("6"); 157 break; 158 case 128: 159 textBox_palShift.setText("7"); 160 break; 161 case 256: 162 textBox_palShift.setText("8"); 163 break; 164 default: break; 165 } 166 } else { 167 textBox_palShift.setText(""); 168 textBox_palShift.state = ElementState.Disabled; 169 } 170 return 0; 171 } 172 private void button_Ok_onClick(Event e) { 173 import pixelperfectengine.system.etc : parseHex, parseOct, parseDec; 174 import editorevents : AddTileSheetEvent; 175 //detect numbering style 176 uint numStyle0; 177 int numFrom; 178 dstring numStyle = textBox_numStyle.getText.text; 179 if (numStyle.length) { 180 if (numStyle[0] == 'h' || numStyle[$-1] == 'h') { 181 numStyle0 = 1; 182 numStyle0 |= cast(uint)((numStyle.length - 1) << 8); 183 } 184 else if (numStyle[0] == 'o' || numStyle[$-1] == 'o') { 185 numStyle0 = 2; 186 numStyle0 |= cast(uint)((numStyle.length - 1) << 8); 187 } 188 else if (numStyle.length >= 2) { 189 if (numStyle[0..2] == "0x") { 190 numStyle0 = 1; 191 numStyle0 |= cast(uint)((numStyle.length - 2) << 8); 192 } 193 else if (numStyle[0..2] == "0o") { 194 numStyle0 = 2; 195 numStyle0 |= cast(uint)((numStyle.length - 2) << 8); 196 } 197 } else numStyle0 = cast(uint)(numStyle.length << 8); 198 switch (numStyle0) { 199 case 1: numFrom = parseHex(numStyle); break; 200 case 2: numFrom = parseOct(numStyle); break; 201 default: numFrom = parseDec(numStyle); break; 202 } 203 string name0 = to!string(textBox_name.getText.text); 204 string[3] name; 205 if (name0.length) { 206 for (size_t i ; i < name0.length ; i++) { 207 if (name0[i] == '#') { 208 name[0] = name0[0..i]; 209 if (i + 2 < name0.length) { 210 name[1] = name0[i+2..$]; 211 } 212 } 213 } 214 } 215 if(!name[0].length) name[0] = name0; 216 name[2] = to!string(textBox_Source.getText.text); 217 const int paletteShift = checkBox_palImport.isChecked ? to!int(textBox_palShift.getText.text) : -1; 218 const int paletteOffset = to!int(textBox_palOffset.getText.text); 219 editor.selDoc.events.addToTop(new AddTileSheetEvent(source, editor.selDoc, editor.selDoc.selectedLayer, paletteOffset, 220 paletteShift, name, numFrom, numStyle0)); 221 editor.selDoc.updateMaterialList; 222 this.close; 223 } else { 224 handler.message("Error!"d, "Numbering style must be specified in this case!"); 225 } 226 } 227 }