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 		button_Ok = new Button("Ok"d, "button_Ok", Coordinate(91, 206, 161, 226));
48 		label3 = new Label("name:"d, "label3", Coordinate(5, 110, 79, 126));
49 		textBox_name = new TextBox(""d, "textBox_name", Coordinate(45, 108, 160, 128));
50 		label4 = new Label("Num style and from:"d, "label4", Coordinate(5, 130, 120, 150));
51 		textBox_numStyle = new TextBox("h0000"d, "textBox_numStyle", Coordinate(120, 130, 160, 150));
52 		this.sizeX = sizeX;
53 		this.sizeY = sizeY;
54 		addElement(label0);
55 		addElement(button_Source);
56 		button_Source.onMouseLClick = &button_Source_onClick;
57 		addElement(textBox_Source);
58 		textBox_Source.onTextInput = &textBox_Source_onTextInput;
59 		addElement(label1);
60 		addElement(textBox_palShift);
61 		addElement(checkBox_palImport);
62 		addElement(checkBox_embDat);
63 		addElement(label2);
64 		addElement(textBox_palOffset);
65 		addElement(button_Ok);
66 		button_Ok.onMouseLClick = &button_Ok_onClick;
67 		addElement(label3);
68 		addElement(textBox_name);
69 		addElement(label4);
70 		addElement(textBox_numStyle);
71 		this.editor = editor;
72 	}
73 	private void button_Source_onClick(Event e) {
74 		handler.addWindow(new FileDialog("Import Tile Source"d, "fileDialog_TSBrowse", &fileDialog_TSBrowse_event,
75 				[FileDialog.FileAssociationDescriptor("All supported formats", ["*.tga", "*.png", "*.bmp"]),
76 					/+FileDialog.FileAssociationDescriptor("PPE Extendible Map file", ["*.xmf"]),+/
77 					FileDialog.FileAssociationDescriptor("Targa Graphics File", ["*.tga"]),
78 					FileDialog.FileAssociationDescriptor("Windows Bitmap File", ["*.bmp"]),
79 					FileDialog.FileAssociationDescriptor("Portable Network Graphics File", ["*.png"]),], "./"));
80 	}
81 	private void textBox_palShift_onTextInput(Event e) {
82 		//validate input type
83 		import PixelPerfectEngine.system.etc : isInteger;
84 		if (!isInteger(textBox_palShift.getText.text)) {
85 			handler.message("Bad format!"d, "Inputted value must be integer!"d);
86 		}
87 		const int value = to!int(textBox_palShift.getText.text);
88 		if (value < 1 || value > 8) {
89 			handler.message("Bad value!"d, "Inputted value must be between 1 and 8!"d);
90 		}
91 	}
92 	private void fileDialog_TSBrowse_event(Event ev) {
93 		FileEvent e = cast(FileEvent)ev;
94 		if (!loadFile(e.getFullPath)) {
95 			textBox_Source.setText(to!dstring(e.getFullPath));
96 		}
97 	}
98 	private void textBox_Source_onTextInput(Event e) {
99 		if (loadFile(to!string(textBox_Source.getText.text))) {
100 			textBox_Source.setText(""d);
101 		}
102 		
103 	}
104 	private int loadFile(string path) {
105 		import std.path : extension;
106 		import std.stdio : File;
107 		import PixelPerfectEngine.system.etc : nextPow2;
108 		File f = File(path);
109 		try {
110 			switch (path.extension) {
111 				case ".xmp"://TO DO: enable importing material data from other map files
112 					break;
113 				case ".png":
114 					source = PNG.load(f);
115 					break;
116 				case ".tga":
117 					source = TGA.load!(File, true, true)(f);
118 					break;
119 				case ".bmp":
120 					source = BMP.load(f);
121 					break;
122 				default:
123 					handler.message("Unsupported file format!"d, "The specified file format is not supported!"d);
124 					return -1;
125 			}
126 		} catch (Exception ex) {
127 			import std.conv : to;
128 			handler.message("Error!"d, to!dstring(ex.msg));
129 			return -1;
130 		}
131 		if (source.width % sizeX || source.height % sizeY) {
132 			handler.message("Tile size Mismatch!"d, "Supplied bitmap file is unsuitable for this layer as a tile source!"d);
133 			source = null;
134 			return -1;
135 		}
136 		if(source.isIndexed) {
137 			checkBox_palImport.check();
138 			const int paletteLengthPOw2 = cast(int)nextPow2(source.palette.length);
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 		}
167 		return 0;
168 	}
169 	private void button_Ok_onClick(Event e) {
170 		import PixelPerfectEngine.system.etc : parseHex, parseOct, parseDec;
171 		import editorevents : AddTileSheetEvent;
172 		//detect numbering style
173 		uint numStyle0;
174 		int numFrom;
175 		dstring numStyle = textBox_numStyle.getText.text;
176 		if (numStyle.length) {
177 			if (numStyle[0] == 'h' || numStyle[$-1] == 'h') { 
178 				numStyle0 = 1;
179 				numStyle0 |= cast(uint)((numStyle.length - 1) << 8);
180 			}
181 			else if (numStyle[0] == 'o' || numStyle[$-1] == 'o') { 
182 				numStyle0 = 2;
183 				numStyle0 |= cast(uint)((numStyle.length - 1) << 8);
184 			}
185 			else if (numStyle.length >= 2) { 
186 				if (numStyle[0..2] == "0x")	{
187 					numStyle0 = 1;
188 					numStyle0 |= cast(uint)((numStyle.length - 2) << 8);
189 				}
190 				else if (numStyle[0..2] == "0o") {
191 					numStyle0 = 2;
192 					numStyle0 |= cast(uint)((numStyle.length - 2) << 8);
193 				}
194 			} else numStyle0 = cast(uint)(numStyle.length << 8);
195 			switch (numStyle0) {
196 				case 1: numFrom = parseHex(numStyle); break;
197 				case 2: numFrom = parseOct(numStyle); break;
198 				default: numFrom = parseDec(numStyle); break;
199 			}
200 			string name0 = to!string(textBox_name.getText.text);
201 			string[3] name;
202 			if (name0.length) {
203 				for (size_t i ; i < name0.length ; i++) {
204 					if (name0[i] == '#') {
205 						name[0] = name0[0..i];
206 						if (i + 2 < name0.length) {
207 							name[1] = name0[i+2..$];
208 						}
209 					}
210 				}
211 			}
212 			if(!name[0].length) name[0] = name0;
213 			name[2] = to!string(textBox_Source.getText.text);
214 			const int paletteShift = checkBox_palImport.isChecked ? to!int(textBox_palShift.getText.text) : -1;
215 			const int paletteOffset = to!int(textBox_palOffset.getText.text);
216 			editor.selDoc.events.addToTop(new AddTileSheetEvent(source, editor.selDoc, editor.selDoc.selectedLayer, paletteOffset, 
217 					paletteShift, name, numFrom, numStyle0));
218 			editor.selDoc.updateMaterialList;
219 			this.close;
220 		} else {
221 			handler.message("Error!"d, "Numbering style must be specified in this case!");
222 		}
223 	}
224 }