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 }