1 module serializer;
2 
3 import sdlang;
4 import editor;
5 import types;
6 import PixelPerfectEngine.graphics.common;
7 import PixelPerfectEngine.concrete.elements;
8 import std.utf;
9 import std.stdio;
10 import conv = std.conv;
11 
12 public class WindowSerializer{
13 	Tag root;
14 	string filename;
15 	public this(){
16 		root = new Tag(null, null);
17 		Tag window = new Tag(root, null, "Window", [Value("window")]);
18 		new Tag(window, null, "title", [Value("New Window")]);
19 		new Tag(window, "size", "x", [Value(640)]);
20 		new Tag(window, "size", "y", [Value(480)]);
21 		new Tag(window, null, "extraButtons");
22 	}
23 	public this(string filename){
24 		this.filename = filename;
25 	}
26 	public void store(string filename){
27 		this.filename = filename;
28 		store();
29 	}
30 	public @property @nogc string getFilename(){
31 		return filename;
32 	}
33 	public void store(){
34 		import std..string : toStringz;
35 		string fileout = root.toSDLDocument("\t",1);
36 		debug writeln(fileout);
37 		File filestream = File(filename, "w");
38 		//filestream.open();
39 		filestream.write(fileout);
40 		filestream.close();
41 	}
42 	private Coordinate parseCoordinate(Tag t){
43 		return Coordinate(t.values[0].get!int,t.values[1].get!int,t.values[2].get!int,t.values[3].get!int);
44 	}
45 	private string parseCoordinateIntoString(Tag t){
46 		return conv.to!string(t.values[0].get!int) ~ ", " ~ conv.to!string(t.values[1].get!int) ~ ", " ~
47 				conv.to!string(t.values[2].get!int) ~ ", " ~ conv.to!string(t.values[3].get!int);
48 	}
49 	public void deserialize(DummyWindow dw, Editor e){
50 		root = parseFile(filename);
51 		foreach(t0; root.all.tags){
52 			switch(t0.getFullName.toString){
53 				case "Label":
54 					WindowElement we = new Label(toUTF32(t0.expectTagValue!string("text")), t0.expectTagValue!string("source"),
55 							parseCoordinate(t0.expectTag("position")));
56 					e.elements[t0.name] = we;
57 					dw.addElement(we,0);
58 					break;
59 				case "Button":
60 					WindowElement we = new Button(toUTF32(t0.expectTagValue!string("text")), t0.expectTagValue!string("source"),
61 							parseCoordinate(t0.expectTag("position")));
62 					e.elements[t0.name] = we;
63 					dw.addElement(we,0);
64 					break;
65 				case "TextBox":
66 					WindowElement we = new TextBox(toUTF32(t0.expectTagValue!string("text")), t0.expectTagValue!string("source"),
67 							parseCoordinate(t0.expectTag("position")));
68 					e.elements[t0.name] = we;
69 					dw.addElement(we,0);
70 					break;
71 				case "CheckBox":
72 					WindowElement we = new CheckBox(toUTF32(t0.expectTagValue!string("text")), t0.expectTagValue!string("source"),
73 							parseCoordinate(t0.expectTag("position")));
74 					e.elements[t0.name] = we;
75 					dw.addElement(we,0);
76 					break;
77 				case "ListBox":
78 					int[] columnWidths;
79 					dstring[] columnTexts;
80 					foreach(t1; t0.expectTag("header").tags){
81 						columnTexts ~= toUTF32(t1.values[0].get!string);
82 						columnWidths ~= t1.values[1].get!int;
83 					}
84 					WindowElement we = new ListBox(t0.expectTagValue!string("source"), parseCoordinate(t0.expectTag("position")), [],
85 							new ListBoxHeader(columnTexts, columnWidths));
86 					e.elements[t0.name] = we;
87 					dw.addElement(we,0);
88 					break;
89 				case "RadioButtonGroup":
90 					dstring[] options;
91 					Value[] vals = t0.expectTag("options").values;
92 					foreach(v; vals){
93 						options ~= toUTF32(v.get!string);
94 					}
95 					WindowElement we = new RadioButtonGroup(toUTF32(t0.expectTagValue!string("text")), t0.expectTagValue!string("source"),
96 							parseCoordinate(t0.expectTag("position")), options, 16, 0);
97 					e.elements[t0.name] = we;
98 					dw.addElement(we,0);
99 					break;
100 				case "Window":
101 					dw.setTitle(toUTF32(t0.expectTagValue!string("title")));
102 					dw.setSize(t0.expectTagValue!int("size:x"),t0.expectTagValue!int("size:y"));
103 					break;
104 				default:
105 					break;
106 			}
107 		}
108 		e.updateElementList;
109 	}
110 	public void generateDCode(string outputFile){
111 		string outputCode = "import PixelPerfectEngine.concrete.window \n\n", windowCtor, elementCtors, typeDefs;
112 		foreach(t0; root.all.tags){
113 			if(t0.getFullName.toString != "Window"){
114 				elementCtors ~= "\t\t" ~ t0.getValue!string() ~ " = ";
115 				typeDefs ~= "\t" ~ t0.name ~ " " ~ t0.getValue!string() ~ ";\n";
116 			}
117 			switch(t0.getFullName.toString){
118 				case "Button", "Label", "TextBox", "CheckBox":
119 					elementCtors ~= "new " ~ t0.getFullName.toString ~ "(\"" ~ t0.getTagValue!string("text") ~ "\"w, \"" ~
120 							t0.getTagValue!string("source") ~ "\", Coordinate(" ~ parseCoordinateIntoString(t0.getTag("position")) ~ "));\n";
121 					break;
122 				case "RadioButtonGroup":
123 					string options = "[";
124 					foreach(v; t0.getTagValues("options")){
125 						options ~= "\"" ~ v.get!string() ~ "\"w, ";
126 					}
127 					if(options != "[")
128 						options.length -= 2;
129 					options ~= "]";
130 					elementCtors ~= "\t\tnew RadioButtonGroup(\"" ~ t0.getTagValue!string("text") ~ "\"w, \"" ~ t0.getTagValue!string("source")
131 							~ "\", Coordinate(" ~ parseCoordinateIntoString(t0.getTag("position")) ~ "), " ~ options ~ ",16,0);\n";
132 					break;
133 				case "HSlider", "VSlider":
134 					elementCtors ~= "\t\tnew " ~ t0.getFullName.toString ~ "(\"" ~ conv.to!string(t0.getTagValue!int("maxValue")) ~
135 							", " ~ conv.to!string(t0.getTagValue!int("barLength")) ~ ", " ~ t0.getTagValue!string("source") ~
136 							"\", Coordinate(" ~ parseCoordinateIntoString(t0.getTag("position")) ~ "));\n";
137 					break;
138 				case "ListBox":
139 					string headerCtorA = "new ListBoxHeader([", headerCtorB = "], [";
140 					foreach(t1; t0.expectTag("header").tags){
141 						headerCtorA ~= "\"" ~ t0.values[0].get!string ~ "\"w, ";
142 						headerCtorB ~= conv.to!string(t0.values[1].get!int) ~ ", ";
143 					}
144 					headerCtorA.length -= 2;
145 					headerCtorB.length -= 2;
146 					elementCtors ~= "\t\tnew ListBox(\"" ~ t0.getTagValue!string("source")
147 							~ "\", Coordinate(" ~ parseCoordinateIntoString(t0.getTag("position")) ~ "), [], " ~
148 							headerCtorA ~ headerCtorB ~ "]));\n";
149 					break;
150 				case "Window":
151 					outputCode ~= "public class " ~ t0.getValue!string() ~ " : Window {\n";
152 					string extraButtons;
153 					Tag t1 = t0.getTag("extraButtons", null);
154 					if(t1 !is null){
155 						if(t1.values.length){
156 							extraButtons = ", [";
157 							foreach(Value v; t1.values){
158 								extraButtons ~= v.get!string() ~ ", ";
159 							}
160 							extraButtons.length -= 2;
161 							extraButtons = "]";
162 						}
163 					}
164 					windowCtor = "super(\"" ~ t0.getTagValue!string("title") ~ "\"w, Coordinate(0, 0, " ~
165 							conv.to!string(t0.getTagValue!int("size:x")) ~ ", " ~ conv.to!string(t0.getTagValue!int("size:y")) ~ " )" ~
166 							extraButtons ~ ");\n";
167 					break;
168 				default:
169 					break;
170 			}
171 		}
172 		outputCode ~= typeDefs ~ "\tpublic this(){\n\t\t" ~ windowCtor ~ elementCtors ~ "\t}\n}\n";
173 		debug writeln(outputCode);
174 		File filestream = File(outputFile, "w");
175 		//filestream.open();
176 		filestream.write(outputCode);
177 		filestream.close();
178 	}
179 	/**
180 	 * Returns a complete tag for editing a tree, etc.
181 	 */
182 	public Tag getTag(string target, string property){
183 		foreach(t0; root.all.tags){
184 			if(t0.getValue!string() == target){
185 				return t0.getTag(property);
186 			}
187 		}
188 		return null;
189 	}
190 	/**
191 	 * Edits the value of an element.
192 	 * For MenuBar PopUpMenu trees, use the getTag function instead.
193 	 */
194 	public Value[] editValue(string target, string property, Value[] val){
195 		Value[] result;
196 		foreach(t0; root.all.tags){
197 			if(t0.getValue!string() == target){
198 				result = t0.getTagValues(property);
199 				t0.getTag(property).values = val;
200 				return result;
201 			}
202 		}
203 		return result;
204 	}
205 	public Value[] getValue(string target, string property){
206 		foreach(t0; root.all.tags){
207 			if(t0.getValue!string() == target){
208 				return t0.getTagValues(property);
209 			}
210 		}
211 		return null;
212 	}
213 	public string renameWindow(string name){
214 		string oldname = root.getTag("Window").getValue!string();
215 		root.getTag("Window").values[0] = Value(name);
216 		return oldname;
217 	}
218 	public string getWindowName(){
219 		return root.getTag("Window").getValue!string();
220 	}
221 	public Value[] editWindowValue(string property, Value[] val){
222 		Value[] result = root.getTag("Window").getTag(property).values;
223 		root.getTag("Window").getTag(property).values = val;
224 		return result;
225 	}
226 	public Value[] getWindowValue(string property){
227 		return root.getTag("Window").getTag(property).values;
228 	}
229 	public void renameElement(string oldName, string newName){
230 		foreach(t; root.tags){
231 			if(t.getValue!string() == oldName){
232 				t.values[0] = Value(newName);
233 				return;
234 			}
235 		}
236 	}
237 	public void addElement(ElementType type, string name, Coordinate initPos){
238 		foreach(t; root.tags){
239 			if(t.getValue!string() == name)
240 				throw new ElementCollisionException("Similarly named element already exists!");
241 		}
242 		Tag t1;
243 		switch(type){
244 			case ElementType.Label:
245 				t1 = new Tag(root, null, "Label", [Value(name)]);
246 				new Tag(t1, null, "text", [Value(name)]);
247 				break;
248 			case ElementType.Button:
249 				t1 = new Tag(root, null, "Button", [Value(name)]);
250 				new Tag(t1, null, "text", [Value(name)]);
251 				new Tag(t1, null, "icon", [Value("null")]);
252 				break;
253 			case ElementType.TextBox:
254 				t1 = new Tag(root, null, "TextBox", [Value(name)]);
255 				new Tag(t1, null, "text", [Value(name)]);
256 				break;
257 			case ElementType.ListBox:
258 				t1 = new Tag(root, null, "ListBox", [Value(name)]);
259 				Tag t2 = new Tag(t1, null, "header");
260 				new Tag(t2, null, null, [Value("col0"), Value(40)]);
261 				new Tag(t2, null, null, [Value("col1"), Value(40)]);
262 				break;
263 			case ElementType.RadioButtonGroup:
264 				t1 = new Tag(root, null, "RadioButtonGroup", [Value(name)]);
265 				new Tag(t1, null, "text", [Value(name)]);
266 				new Tag(t1, null, "options", [Value("opt0"), Value("opt1")]);
267 				break;
268 			case ElementType.CheckBox:
269 				t1 = new Tag(root, null, "CheckBox", [Value(name)]);
270 				new Tag(t1, null, "text", [Value(name)]);
271 				break;
272 			case ElementType.HSlider:
273 				t1 = new Tag(root, null, "HSlider", [Value(name)]);
274 				new Tag(t1, null, "barLength", [Value(1)]);
275 				new Tag(t1, null, "maxValue", [Value(16)]);
276 				break;
277 			case ElementType.VSlider:
278 				t1 = new Tag(root, null, "VSlider", [Value(name)]);
279 				new Tag(t1, null, "barLength", [Value(1)]);
280 				new Tag(t1, null, "maxValue", [Value(16)]);
281 				break;
282 			case ElementType.MenuBar:
283 				t1 = new Tag(root, null, "MenuBar", [Value(name)]);
284 				break;
285 			default:
286 				break;
287 		}
288 		new Tag(t1, null, "source", [Value(name)]);
289 		new Tag(t1, null, "position", [Value(initPos.left), Value(initPos.top), Value(initPos.right), Value(initPos.bottom)]);
290 		//debug writeln(t1);
291 	}
292 	public void addElement(Tag tag){
293 		foreach(t; root.tags){
294 			if(t.values[0].get!string() == tag.values[0].get!string())
295 				throw new ElementCollisionException("Similarly named element already exists!");
296 		}
297 		root.add(tag);
298 	}
299 	public Tag removeElement(string name){
300 		foreach(t; root.tags){
301 			if(t.values[0].get!string() == name){
302 				t.remove;
303 				return t;
304 			}
305 		}
306 		return null;
307 	}
308 }
309 class ElementCollisionException : Exception{
310 	@nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null)
311     {
312         super(msg, file, line, nextInChain);
313     }
314 
315     @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__)
316     {
317         super(msg, file, line, nextInChain);
318     }
319 }