1 /*
2  * This file is part of gtkD.
3  *
4  * gtkD is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License
6  * as published by the Free Software Foundation; either version 3
7  * of the License, or (at your option) any later version, with
8  * some exceptions, please read the COPYING file.
9  *
10  * gtkD is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with gtkD; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
18  */
19 
20 module utils.GtkStruct;
21 
22 import std.algorithm: sort, uniq, endsWith;
23 import std.array : replace;
24 import std.conv;
25 import std.file : write;
26 import std.path: buildPath;
27 import std.uni: toUpper, toLower;
28 import std.range;
29 import std.string: capitalize, splitLines, strip, chomp;
30 
31 import utils.GtkFunction;
32 import utils.GtkPackage;
33 import utils.GtkType;
34 import utils.GtkWrapper;
35 import utils.XML;
36 import utils.LinkedHasMap: Map = LinkedHashMap;
37 import utils.IndentedStringBuilder;
38 
39 enum GtkStructType : string
40 {
41 	Class = "class",
42 	Interface = "interface",
43 	Record = "record",
44 	Union = "union"
45 }
46 
47 final class GtkStruct
48 {
49 	string name;
50 	GtkStructType type;
51 	string doc;
52 	string cType;
53 	string parent;
54 	string libVersion;
55 
56 	bool lookupClass = false;
57 	bool lookupInterface = false;
58 	bool lookupParent = false;  // is the parent set with the lookup file.
59 	bool noCode = false;
60 	bool noDecleration = false;
61 	bool noExternal = false;
62 	bool noNamespace = false;
63 	string[string] structWrap;
64 	string[string] aliases;
65 	string[] lookupCode;
66 	string[] lookupInterfaceCode;
67 
68 	string[] implements;
69 	string[] imports;
70 	GtkField[] fields;
71 	string[] virtualFunctions;
72 	Map!(string, GtkFunction) functions;
73 
74 	GtkWrapper wrapper;
75 	GtkPackage pack;
76 	GtkStruct parentStruct;
77 
78 	this(GtkWrapper wrapper, GtkPackage pack)
79 	{
80 		this.wrapper = wrapper;
81 		this.pack = pack;
82 	}
83 
84 	GtkStruct dup()
85 	{
86 		GtkStruct copy = new GtkStruct(wrapper, pack);
87 
88 		foreach ( i, field; this.tupleof )
89 			copy.tupleof[i] = field;
90 
91 		return copy;
92 	}
93 
94 	void parse(T)(XMLReader!T reader)
95 	{
96 		name = reader.front.attributes["name"];
97 		type = cast(GtkStructType)reader.front.value;
98 
99 		if ( "c:type" in reader.front.attributes )
100 			cType = reader.front.attributes["c:type"];
101 		if ( "parent" in reader.front.attributes )
102 			parent = reader.front.attributes["parent"];
103 		if ( "version" in reader.front.attributes )
104 			libVersion = reader.front.attributes["version"];
105 
106 		if ( !parent.empty )
107 		{
108 			if ( parent == "GObject.InitiallyUnowned" )
109 				parent = "GObject.Object";
110 			else if ( parent == "InitiallyUnowned" )
111 				parent = "Object";
112 		}
113 
114 		if ( pack && pack.name != "glib" && "glib:get-type" in reader.front.attributes && reader.front.attributes["glib:get-type"].endsWith("_get_type") )
115 			functions["get_type"] = getTypeFunction(reader.front.attributes["glib:get-type"]);
116 
117 		if ( reader.front.type == XMLNodeType.EmptyTag )
118 			return;
119 
120 		reader.popFront();
121 
122 		while( !reader.empty && !reader.endTag("class", "interface", "record", "union") )
123 		{
124 			switch(reader.front.value)
125 			{
126 				case "doc":
127 					reader.popFront();
128 					doc ~= reader.front.value;
129 					reader.popFront();
130 					break;
131 				case "doc-deprecated":
132 					reader.popFront();
133 					doc ~= "\n\nDeprecated: "~ reader.front.value;
134 					reader.popFront();
135 					break;
136 				case "field":
137 					GtkField field = new GtkField(wrapper);
138 					field.parse(reader);
139 					fields ~= field;
140 					break;
141 				case "record":
142 					GtkField field = new GtkField(wrapper);
143 					GtkStruct strct = new GtkStruct(wrapper, null);
144 					strct.parse(reader);
145 					strct.cType = strct.cType.toUpper()[0..1] ~ strct.cType[1 .. $];
146 					field.gtkStruct = strct;
147 					fields ~= field;
148 					break;
149 				case "union":
150 					GtkField field = new GtkField(wrapper);
151 					GtkUnion uni = new GtkUnion(wrapper);
152 					uni.parse(reader);
153 					field.gtkUnion = uni;
154 					fields ~= field;
155 					break;
156 				case "constructor":
157 				case "method":
158 				case "glib:signal":
159 					if ( type == GtkStructType.Record )
160 						type = GtkStructType.Class;
161 					goto case "function";
162 				case "function":
163 					GtkFunction func = new GtkFunction(wrapper, this);
164 					func.parse(reader);
165 					if ( func.type == GtkFunctionType.Signal )
166 						functions[func.name~"-signal"] = func;
167 					else
168 						functions[func.name] = func;
169 					break;
170 				case "virtual-method":
171 					// Virtual methods in the gir file are mirrored
172 					// as regular methods, so we only collect whitch are virtual;
173 					virtualFunctions ~= reader.front.attributes["name"];
174 					reader.skipTag();
175 					break;
176 				case "implements":
177 					implements ~= reader.front.attributes["name"];
178 					break;
179 				case "prerequisite": // Determines whitch base class the implementor of an interface must implement.
180 				case "property":
181 					reader.skipTag();
182 					break;
183 				default:
184 					assert(false, "Unexpected tag: "~ reader.front.value);
185 			}
186 
187 			reader.popFront();
188 		}
189 
190 		foreach( func; virtualFunctions )
191 		{
192 			if ( auto vFunc = func in functions )
193 				vFunc.virtual = true;
194 		}
195 
196 		if ( type == GtkStructType.Union )
197 		{
198 			GtkField field = new GtkField(wrapper);
199 			GtkUnion uni = new GtkUnion(wrapper);
200 			uni.fields = fields;
201 			field.gtkUnion = uni;
202 			fields = [field];
203 
204 			//special case for "_Value__data__union"
205 			if ( cType.empty )
206 				cType = name;
207 
208 			type = GtkStructType.Record;
209 
210 			foreach ( funct; functions )
211 			{
212 				if ( funct.type != GtkFunctionType.Function )
213 					type = GtkStructType.Class;
214 			}
215 		}
216 	}
217 
218 	string[] getStructDeclaration()
219 	{
220 		if ( noExternal || cType.empty )
221 			return null;
222 
223 		string[] buff;
224 
225 		if ( doc !is null && wrapper.includeComments && type == GtkStructType.Record )
226 		{
227 			buff ~= "/**";
228 			foreach ( line; doc.splitLines() )
229 				buff ~= " * "~ line.strip();
230 
231 			if ( libVersion )
232 			{
233 				buff ~= " *";
234 				buff ~= " * Since: "~ libVersion;
235 			}
236 
237 			buff ~= " */";
238 		}
239 
240 		if ( !fields.empty )
241 		{
242 			buff ~= "struct "~ tokenToGtkD(cType, wrapper.aliasses);
243 			buff ~= "{";
244 			buff ~= GtkField.getFieldDeclarations(fields, wrapper);
245 			buff ~= "}";
246 		}
247 		else
248 		{
249 			buff ~= "struct "~ tokenToGtkD(cType, wrapper.aliasses) ~";";
250 		}
251 
252 		return buff;
253 	}
254 
255 	void writeClass()
256 	{
257 		bool[string] ctors;
258 
259 		if ( noCode )
260 			return;
261 
262 		if ( type == GtkStructType.Record && !(lookupClass || lookupInterface) && (functions.empty && lookupCode.empty ) )
263 			return;
264 
265 		parentStruct = pack.getStruct(parent);
266 		resolveImports();
267 
268 		if ( type == GtkStructType.Record && !(lookupClass || lookupInterface) )
269 		{
270 			writeDStruct();
271 			return;
272 		}
273 
274 		if ( isInterface() )
275 			writeInterface();
276 
277 		string buff = wrapper.licence;
278 		auto indenter = new IndentedStringBuilder();
279 
280 		if ( isInterface() )
281 			buff ~= "module "~ pack.name ~"."~ name ~"T;\n\n";
282 		else
283 			buff ~= "module "~ pack.name ~"."~ name ~";\n\n";
284 
285 		writeImports(buff, isInterface() );
286 		writeDocs(buff);
287 
288 	if ( isInterface() )
289 			buff ~= "public template "~ name ~"T(TStruct)";
290 		else
291 			buff ~= "public class "~ name;
292 
293 		if ( lookupParent && !parentStruct )
294 			buff ~= " : "~ parent;
295 		else if ( parentStruct && parentStruct.name != name )
296 			buff ~= " : "~ parentStruct.name;
297 		else if ( parentStruct )
298 			buff ~= " : "~ parentStruct.pack.name.capitalize() ~ parentStruct.name;
299 
300 		bool first = !parentStruct;
301 
302 		foreach ( interf; implements )
303 		{
304 			if ( parentStruct && parentStruct.implements.canFind(interf) )
305 				continue;
306 
307 			// If the parentStruct is in an different package compare without package name.
308 			if ( parentStruct && interf.canFind(".") && parentStruct.implements.canFind(interf.split('.')[1]) )
309 				continue;
310 
311 			GtkStruct strct = pack.getStruct(interf);
312 
313 			if ( strct && first )
314 			{
315 				buff ~= " :";
316 				first = false;
317 			}
318 			else if ( strct )
319 				buff ~= ",";
320 
321 			if ( strct )
322 				buff ~= " "~ strct.name ~"IF";
323 		}
324 
325 		buff ~= "\n";
326 		buff ~= indenter.format("{");
327 
328 		if ( !cType.empty )
329 		{
330 			if ( !isInterface() )
331 			{
332 				buff ~= indenter.format("/** the main Gtk struct */");
333 				buff ~= indenter.format("protected "~ cType ~"* "~ getHandleVar() ~";");
334 				buff ~= "\n";
335 			}
336 			buff ~= indenter.format("/** Get the main Gtk struct */");
337 			buff ~= indenter.format("public "~ cType ~"* "~ getHandleFunc() ~"()");
338 			buff ~= indenter.format("{");
339 
340 			if ( isInterface() )
341 				buff ~= indenter.format("return cast("~ cType ~"*)getStruct();");
342 			else
343 				buff ~= indenter.format("return "~ getHandleVar ~";");
344 
345 			buff ~= indenter.format("}");
346 			buff ~= "\n";
347 
348 			if ( !isInterface() )
349 			{
350 				buff ~= indenter.format("/** the main Gtk struct as a void* */");
351 
352 				if ( parentStruct )
353 					buff ~= indenter.format("protected override void* getStruct()");
354 				else
355 					buff ~= indenter.format("protected void* getStruct()");
356 
357 				buff ~= indenter.format("{");
358 				buff ~= indenter.format("return cast(void*)"~ getHandleVar ~";");
359 				buff ~= indenter.format("}");
360 				buff ~= "\n";
361 			}
362 
363 			if ( !isInterface() && cType != "GObject" && cType != "cairo_t" )
364 			{
365 				if ( parentStruct && pack.name != "cairo" )
366 				{
367 					buff ~= indenter.format("protected override void setStruct(GObject* obj)");
368 					buff ~= indenter.format("{");
369 					buff ~= indenter.format(getHandleVar ~" = cast("~ cType ~"*)obj;");
370 					buff ~= indenter.format("super.setStruct(obj);");
371 					buff ~= indenter.format("}");
372 					buff ~= "\n";
373 				}
374 
375 				buff ~= indenter.format("/**");
376 				buff ~= indenter.format(" * Sets our main struct and passes it to the parent class.");
377 				buff ~= indenter.format(" */");
378 
379 				if ( parentStruct && getAncestor().name == "ObjectG" )
380 					buff ~= indenter.format("public this ("~ cType ~"* "~ getHandleVar() ~", bool ownedRef = false)");
381 				else
382 					buff ~= indenter.format("public this ("~ cType ~"* "~ getHandleVar() ~")");
383 
384 				buff ~= indenter.format("{");
385 				buff ~= indenter.format("this."~ getHandleVar() ~" = "~ getHandleVar() ~";");
386 
387 				if ( parentStruct && getAncestor().name == "ObjectG" )
388 					buff ~= indenter.format("super(cast("~ parentStruct.cType ~"*)"~ getHandleVar() ~", ownedRef);");
389 				else if ( parentStruct )
390 					buff ~= indenter.format("super(cast("~ parentStruct.cType ~"*)"~ getHandleVar() ~");");
391 
392 				buff ~= indenter.format("}");
393 				buff ~= "\n";
394 			}
395 
396 			foreach ( interf; implements )
397 			{
398 				if ( parentStruct && parentStruct.implements.canFind(interf) )
399 					continue;
400 
401 				if ( parentStruct && interf.canFind(".") && parentStruct.implements.canFind(interf.split('.')[1]) )
402 					continue;
403 
404 				GtkStruct strct = pack.getStruct(interf);
405 
406 				if ( strct )
407 				{
408 					buff ~= indenter.format("// add the "~ strct.name ~" capabilities");
409 					buff ~= indenter.format("mixin "~ strct.name ~"T!("~ cType.chomp("*") ~");");
410 					buff ~= "\n";
411 				}
412 			}
413 
414 		}
415 
416 		if ( !lookupCode.empty )
417 		{
418 			buff ~= indenter.format(lookupCode);
419 			buff ~= "\n";
420 		}
421 
422 		buff ~= indenter.format(["/**", "*/"]);
423 		bool firstSignal = true;
424 
425 		foreach ( func; functions )
426 		{
427 			if ( func.noCode || func.isVariadic() || func.type == GtkFunctionType.Callback )
428 				continue;
429 
430 			if ( isInterface() && func.type == GtkFunctionType.Constructor )
431 				continue;
432 
433 			if ( func.type == GtkFunctionType.Signal )
434 			{
435 				buff ~= "\n";
436 
437 				if ( firstSignal )
438 				{
439 					buff ~= indenter.format("int[string] connectedSignals;");
440 					buff ~= "\n";
441 					firstSignal = false;
442 				}
443 
444 				if ( isInterface() )
445 				{
446 					string[] prop;
447 
448 					prop ~= func.getDelegateDecleration() ~"[] _on"~ func.getSignalName() ~"Listeners;";
449 					prop ~= "@property "~ func.getDelegateDecleration() ~"[] on"~ func.getSignalName() ~"Listeners()";
450 					prop ~= "{";
451 					prop ~= "return _on"~ func.getSignalName() ~"Listeners;";
452 					prop ~= "}";
453 
454 					buff ~= indenter.format(prop);
455 				}
456 				else
457 				{
458 					buff ~= indenter.format(func.getDelegateDecleration() ~"[] on"~ func.getSignalName() ~"Listeners;");
459 				}
460 
461 				buff ~= indenter.format(func.getAddListenerdeclaration());
462 				buff ~= indenter.format(func.getAddListenerBody());
463 				buff ~= indenter.format(func.getSignalCallback());
464 
465 				foreach ( param; func.params )
466 				{
467 					if ( param.type.name.startsWith("Gdk.Event") && param.type.name != "Gdk.Event" )
468 					{
469 						buff ~= "\n";
470 						buff ~= indenter.format(getGenericEventSignal(func));
471 
472 						break;
473 					}
474 				}
475 			}
476 			else
477 			{
478 				buff ~= "\n";
479 				buff ~= indenter.format(func.getDeclaration());
480 				buff ~= indenter.format("{");
481 				buff ~= indenter.format(func.getBody());
482 				buff ~= indenter.format("}");
483 			}
484 		}
485 
486 		buff ~= indenter.format("}");
487 
488 		if ( isInterface() )
489 			std.file.write(buildPath(wrapper.outputRoot, pack.srcDir, pack.name, name ~"T.d"), buff);
490 		else
491 			std.file.write(buildPath(wrapper.outputRoot, pack.srcDir, pack.name, name ~".d"), buff);
492 	}
493 
494 	void writeInterface()
495 	{
496 		string buff = wrapper.licence;
497 		auto indenter = new IndentedStringBuilder();
498 
499 		buff ~= "module "~ pack.name ~"."~ name ~"IF;\n\n";
500 
501 		writeImports(buff);
502 		writeDocs(buff);
503 
504 		buff ~= "public interface "~ name ~"IF";
505 		buff ~= indenter.format("{");
506 
507 		if ( cType )
508 		{
509 			buff ~= indenter.format("/** Get the main Gtk struct */");
510 			buff ~= indenter.format("public "~ cType ~"* "~ getHandleFunc() ~"();");
511 			buff ~= "\n";
512 
513 			buff ~= indenter.format("/** the main Gtk struct as a void* */");
514 			buff ~= indenter.format("protected void* getStruct();");
515 			buff ~= "\n";
516 
517 			if ( !lookupInterfaceCode.empty )
518 			{
519 				buff ~= indenter.format(lookupInterfaceCode);
520 				buff ~= "\n";
521 			}
522 
523 			buff ~= indenter.format(["/**", "*/"]);
524 
525 			foreach ( func; functions )
526 			{
527 				if ( func.noCode || func.isVariadic() || func.type == GtkFunctionType.Callback || func.type == GtkFunctionType.Constructor )
528 					continue;
529 
530 				if ( func.type == GtkFunctionType.Signal )
531 				{
532 					buff ~= indenter.format("@property "~ func.getDelegateDecleration() ~"[] on"~ func.getSignalName() ~"Listeners();");
533 					string[] dec = func.getAddListenerdeclaration();
534 					dec[$-1] ~= ";";
535 
536 					buff ~= indenter.format(dec);
537 					buff ~= "\n";
538 				}
539 				else
540 				{
541 					string[] dec = func.getDeclaration();
542 					dec[$-1] = dec[$-1].replace("override ", "");
543 					dec[$-1] ~= ";";
544 
545 					buff ~= "\n";
546 					buff ~= indenter.format(dec);
547 				}
548 			}
549 
550 			buff ~= indenter.format("}");
551 		}
552 
553 		std.file.write(buildPath(wrapper.outputRoot, pack.srcDir, pack.name, name ~"IF.d"), buff);
554 	}
555 
556 	void writeDStruct()
557 	{
558 		string buff = wrapper.licence;
559 		auto indenter = new IndentedStringBuilder();
560 
561 		buff ~= "module "~ pack.name ~"."~ name ~";\n\n";
562 
563 		writeImports(buff);
564 		writeDocs(buff);
565 
566 		if ( !noNamespace )
567 		{
568 			buff ~= "public struct "~ name ~"\n";
569 			buff ~= indenter.format("{");
570 		}
571 
572 		if ( !lookupCode.empty )
573 		{
574 			buff ~= indenter.format(lookupCode);
575 			buff ~= "\n";
576 		}
577 
578 		buff ~= indenter.format(["/**", "*/"]);
579 
580 		foreach ( func; functions )
581 		{
582 			if ( func.noCode || func.isVariadic() || !( func.type == GtkFunctionType.Function || func.type == GtkFunctionType.Method ) )
583 				continue;
584 
585 			buff ~= "\n";
586 			buff ~= indenter.format(func.getDeclaration());
587 			buff ~= indenter.format("{");
588 			buff ~= indenter.format(func.getBody());
589 			buff ~= indenter.format("}");
590 		}
591 
592 		if ( !noNamespace )
593 			buff ~= indenter.format("}");
594 
595 		std.file.write(buildPath(wrapper.outputRoot, pack.srcDir, pack.name, name ~".d"), buff);
596 	}
597 
598 	/**
599 	 * Return the variable name the c type is stored in.
600 	 */
601 	string getHandleVar()
602 	{
603 		if (cType.length == 0)
604 			return "";
605 
606 		string p = to!string(toLower(cType[0]));
607 		if ( cType.endsWith("_t") )
608 		{
609 			return p ~ cType[1 .. $ - 2];
610 		} else {
611 			return p ~ cType[1 .. $];
612 		}
613 	}
614 
615 	/**
616 	 * Returns the name of the function that returns the cType.
617 	 */
618 	string getHandleFunc()
619 	{
620 		if ( parentStruct && parentStruct.name == name )
621 			return "get"~ cast(char)pack.name[0].toUpper ~ pack.name[1..$] ~ name ~"Struct";
622 		else
623 			return "get"~ name ~"Struct";
624 	}
625 
626 	bool isInterface()
627 	{
628 		if ( lookupInterface )
629 			return true;
630 		if ( lookupClass )
631 			return false;
632 		if ( type == GtkStructType.Interface )
633 			return true;
634 
635 		return false;
636 	}
637 
638 	bool isNamespace()
639 	{
640 		return type == GtkStructType.Record && !(lookupClass || lookupInterface) && !noNamespace;
641 	}
642 
643 	void merge(GtkStruct mergeStruct)
644 	{
645 		foreach ( func; mergeStruct.functions )
646 		{
647 			func.strct = this;
648 			functions[func.name] = func;
649 		}
650 	}
651 
652 	GtkStruct getAncestor()
653 	{
654 		if ( parent.empty )
655 			return this;
656 
657 		if ( !parentStruct )
658 			parentStruct = pack.getStruct(parent);
659 
660 		return parentStruct.getAncestor();
661 	}
662 
663 	private void resolveImports()
664 	{
665 		if ( parentStruct && parentStruct.name != name)
666 		{
667 			imports ~= parentStruct.pack.name ~"."~ parentStruct.name;
668 		}
669 		else if ( parentStruct )
670 		{
671 			string QParent = parentStruct.pack.name.capitalize() ~ parentStruct.name;
672 			imports ~= parentStruct.pack.name ~"."~ parentStruct.name ~" : "~ QParent ~" = "~ parentStruct.name;
673 			structWrap[parent] = QParent;
674 		}
675 
676 		imports ~= pack.bindDir ~"."~ pack.name;
677 		imports ~= pack.bindDir ~"."~ pack.name ~"types";
678 
679 		foreach( func; functions )
680 		{
681 			if ( func.noCode )
682 				continue;
683 
684 			if ( func.throws )
685 			{
686 				imports ~= "glib.ErrorG";
687 				imports ~= "glib.GException";
688 			}
689 
690 			void getReturnImport(GtkType type)
691 			{
692 				if ( type.name in structWrap || type.name in aliases )
693 					return;
694 
695 				GtkStruct dType = pack.getStruct(type.name);
696 
697 				if ( dType && (dType.type != GtkStructType.Record || dType.lookupClass || dType.lookupInterface) )
698 				{
699 					if ( !dType.pack.name.among("cairo", "glib", "gthread") )
700 						imports ~= "gobject.ObjectG";
701 
702 					if ( dType.type == GtkStructType.Interface && func.name.startsWith("new") )
703 						return;
704 
705 					if ( dType is this && dType.type != GtkStructType.Interface )
706 						return;
707 
708 					imports ~= dType.pack.name ~"."~ dType.name;
709 
710 					if ( dType.type == GtkStructType.Interface || dType.lookupInterface )
711 						imports ~= dType.pack.name ~"."~ dType.name ~"IF";
712 				}
713 				else if ( type.name == "utf8" || type.cType.among("guchar**") )
714 					imports ~= "glib.Str";
715 			}
716 
717 			if ( func.returnType && func.returnType.cType !in structWrap )
718 			{
719 				getReturnImport(func.returnType);
720 
721 				if ( func.returnType.isArray() )
722 					getReturnImport(func.returnType.elementType);
723 			}
724 
725 			void getParamImport(GtkType type)
726 			{
727 				if ( type.name in structWrap || type.name in aliases )
728 					return;
729 
730 				GtkStruct dType = pack.getStruct(type.name);
731 
732 				if ( dType is this )
733 					return;
734 			
735 				if ( func.type == GtkFunctionType.Signal && type.name.startsWith("Gdk.Event") )
736 					imports ~= "gdk.Event";
737 
738 				if ( dType && (dType.type != GtkStructType.Record || dType.lookupClass || dType.lookupInterface) )
739 				{
740 					if ( dType.type == GtkStructType.Interface || dType.lookupInterface )
741 					{
742 						imports ~= dType.pack.name ~"."~ dType.name ~"IF";
743 
744 						if ( func.type == GtkFunctionType.Signal )
745 							imports ~= dType.pack.name ~"."~ dType.name;
746 					}
747 					else
748 					{
749 						imports ~= dType.pack.name ~"."~ dType.name;
750 					}
751 				}
752 				else if ( type.name == "utf8" || type.cType.among("guchar**") )
753 					imports ~= "glib.Str";
754 			}
755 
756 			foreach ( param; func.params )
757 			{
758 				if ( param.type.cType in structWrap )
759 					continue;
760 
761 				getParamImport(param.type);
762 
763 				if ( param.type.elementType )
764 					getParamImport(param.type.elementType);
765 
766 				if ( param.direction != GtkParamDirection.Default )
767 					getReturnImport(param.type);
768 			}
769 
770 			if ( func.type == GtkFunctionType.Signal )
771 			{
772 				imports ~= "gobject.Signals";
773 				imports ~= "gtkc.gdktypes";
774 			}
775 
776 			if ( func.type == GtkFunctionType.Constructor )
777 				imports ~= "glib.ConstructionException";
778 		}
779 
780 		foreach ( interf; implements )
781 		{
782 			if ( parentStruct && parentStruct.implements.canFind(interf) )
783 				continue;
784 
785 			GtkStruct strct = pack.getStruct(interf);
786 
787 			if ( strct )
788 			{
789 				imports ~= strct.pack.name ~"."~ strct.name ~"IF";
790 				imports ~= strct.pack.name ~"."~ strct.name ~"T";
791 			}
792 		}
793 
794 		imports = uniq(sort(imports)).array;
795 	}
796 
797 	private void writeImports(ref string buff, bool _public = false)
798 	{
799 		foreach ( imp; imports )
800 		{
801 			if ( _public || imp.endsWith("types") )
802 				buff ~= "public  import "~ imp ~";\n";
803 			else
804 				buff ~= "private import "~ imp ~";\n";
805 		}
806 
807 		buff ~= "\n\n";
808 	}
809 
810 	private void writeDocs(ref string buff)
811 	{
812 		if ( doc !is null && wrapper.includeComments )
813 		{
814 			buff ~= "/**\n";
815 			foreach ( line; doc.splitLines() )
816 				buff ~= " * "~ line.strip() ~"\n";
817 
818 			if ( libVersion )
819 			{
820 				buff ~= " *\n * Since: "~ libVersion ~"\n";
821 			}
822 
823 			buff ~= " */\n";
824 		}
825 	}
826 
827 	private GtkFunction getTypeFunction(string cIdentifier)
828 	{
829 		GtkType returnType = new GtkType(wrapper);
830 		returnType.name = "GObject.GType";
831 		returnType.cType = "GType";
832 
833 		GtkFunction func = new GtkFunction(wrapper, this);
834 		func.type = GtkFunctionType.Function;
835 		func.name = "get_type";
836 		func.cType = cIdentifier;
837 		func.returnType = returnType;
838 
839 		if ( type == GtkStructType.Interface )
840 			func.noCode = true;
841 
842 		return func;
843 	}
844 
845 	/**
846 	 * Get an overload of events that accept an generic Gdk Event
847 	 * instead of the spesific type listed in the gir files.
848 	 * 
849 	 * This for backwards compatibility with the documentation based wrapper.
850 	 */
851 	private string[] getGenericEventSignal(GtkFunction func)
852 	{
853 		GtkFunction signal = func.dup();
854 		string[] buff;
855 		
856 		for ( size_t i; i < signal.params.length; i++ )
857 		{
858 			if ( signal.params[i].type.name.startsWith("Gdk.Event") )
859 			{
860 				GtkType eventType = new GtkType(wrapper);
861 				eventType.name = "Gdk.Event";
862 				
863 				GtkParam newParam = new GtkParam(wrapper);
864 				newParam.name = signal.params[i].name;
865 				newParam.doc  = signal.params[i].doc;
866 				newParam.type = eventType;
867 				
868 				signal.params[i] = newParam;
869 				
870 				break;
871 			}
872 		}
873 
874 		string[] declaration = signal.getAddListenerdeclaration();
875 		signal.name = signal.name ~ "-generic-event";
876 		
877 		buff ~= func.getDelegateDecleration() ~"[] on"~ signal.getSignalName() ~"Listeners;";
878 		buff ~= declaration;
879 		buff ~= signal.getAddListenerBody();
880 		buff ~= signal.getSignalCallback();
881 		
882 		return buff;
883 	}
884 }
885 
886 final class GtkField
887 {
888 	string name;
889 	string doc;
890 	GtkType type;
891 	int bits = -1;
892 
893 	GtkFunction callback;
894 	GtkUnion gtkUnion;
895 	GtkStruct gtkStruct;
896 
897 	GtkWrapper wrapper;
898 
899 	this(GtkWrapper wrapper)
900 	{
901 		this.wrapper = wrapper;
902 	}
903 
904 	void parse(T)(XMLReader!T reader)
905 	{
906 		name = reader.front.attributes["name"];
907 
908 		if ( "bits" in reader.front.attributes )
909 			bits = to!int(reader.front.attributes["bits"]);
910 
911 		//TODO: readable private?
912 
913 		reader.popFront();
914 
915 		while( !reader.empty && !reader.endTag("field") )
916 		{
917 			if ( reader.front.type == XMLNodeType.EndTag )
918 			{
919 				reader.popFront();
920 				continue;
921 			}
922 
923 			switch(reader.front.value)
924 			{
925 				case "doc":
926 					reader.popFront();
927 					doc ~= reader.front.value;
928 					reader.popFront();
929 					break;
930 				case "doc-deprecated":
931 					reader.popFront();
932 					doc ~= "\n\nDeprecated: "~ reader.front.value;
933 					reader.popFront();
934 					break;
935 				case "array":
936 				case "type":
937 					type = new GtkType(wrapper);
938 					type.parse(reader);
939 					break;
940 				case "callback":
941 					callback = new GtkFunction(wrapper, null);
942 					callback.parse(reader);
943 					break;
944 				default:
945 					assert(false, name ~": Unexpected tag: "~ reader.front.value);
946 			}
947 			reader.popFront();
948 		}
949 	}
950 
951 	/**
952 	 * A special case for fields, we need to know about all of then
953 	 * to properly construct the bitfields.
954 	 */
955 	static string[] getFieldDeclarations(GtkField[] fields, GtkWrapper wrapper)
956 	{
957 		string[] buff;
958 		int bitcount;
959 
960 		void endBitfield()
961 		{
962 			//AFAIK: C bitfields are padded to a multiple of sizeof uint.
963 			int padding = 32 - (bitcount % 32);
964 
965 			if ( padding > 0 && padding < 32)
966 			{
967 				buff[buff.length-1] ~= ",";
968 				buff ~= "uint, \"\", "~ to!string(padding);
969 				buff ~= "));";
970 			}
971 			else
972 			{
973 				buff ~= "));";
974 			}
975 
976 			bitcount = 0;
977 		}
978 
979 		foreach ( field; fields )
980 		{
981 			if ( field.callback )
982 			{
983 				if ( bitcount > 0 )
984 					endBitfield();
985 				buff ~= field.callback.getFunctionPointerDecleration();
986 				continue;
987 			}
988 
989 			if ( field.gtkUnion )
990 			{
991 				if ( bitcount > 0 )
992 					endBitfield();
993 				buff ~= field.gtkUnion.getUnionDeclaration();
994 				continue;
995 			}
996 
997 			if ( field.gtkStruct )
998 			{
999 				if ( bitcount > 0 )
1000 					endBitfield();
1001 				buff ~= field.gtkStruct.getStructDeclaration();
1002 				buff ~= stringToGtkD(field.gtkStruct.cType ~" "~ field.gtkStruct.name ~";", wrapper.aliasses);
1003 				continue;
1004 			}
1005 
1006 			if ( field.bits > 0 )
1007 			{
1008 				if ( bitcount == 0 )
1009 				{
1010 					buff ~= "import std.bitmanip: bitfields;";
1011 					buff ~= "mixin(bitfields!(";
1012 				}
1013 				else
1014 				{
1015 					buff[buff.length-1] ~= ",";
1016 				}
1017 
1018 				bitcount += field.bits;
1019 				buff ~=stringToGtkD(field.type.cType ~", \""~ field.name ~"\", "~ to!string(field.bits), wrapper.aliasses);
1020 				continue;
1021 			}
1022 			else if ( bitcount > 0)
1023 			{
1024 				endBitfield();
1025 			}
1026 
1027 			if ( field.doc !is null && wrapper.includeComments && field.bits < 0 )
1028 			{
1029 				buff ~= "/**";
1030 				foreach ( line; field.doc.splitLines() )
1031 					buff ~= " * "~ line.strip();
1032 				buff ~= " */";
1033 			}
1034 
1035 			string dType;
1036 
1037 			if ( field.type.size == -1 )
1038 			{
1039 				if ( field.type.cType.empty )
1040 					dType = stringToGtkD(field.type.name, wrapper.aliasses);
1041 				else
1042 					dType = stringToGtkD(field.type.cType, wrapper.aliasses);
1043 			}
1044 			else if ( field.type.elementType.cType.empty )
1045 			{
1046 				//Special case for GObject.Value.
1047 				dType = stringToGtkD(field.type.elementType.name, wrapper.aliasses);
1048 				dType ~= "["~ to!string(field.type.size) ~"]";
1049 			}
1050 			else
1051 			{
1052 				dType = stringToGtkD(field.type.elementType.cType, wrapper.aliasses);
1053 				dType ~= "["~ to!string(field.type.size) ~"]";
1054 			}
1055 
1056 			buff ~= dType ~" "~ tokenToGtkD(field.name, wrapper.aliasses) ~";";
1057 		}
1058 
1059 		if ( bitcount > 0)
1060 		{
1061 			endBitfield();
1062 		}
1063 
1064 		return buff;
1065 	}
1066 }
1067 
1068 final class GtkUnion
1069 {
1070 	string name;
1071 	string doc;
1072 	GtkField[] fields;
1073 
1074 	GtkWrapper wrapper;
1075 
1076 	this(GtkWrapper wrapper)
1077 	{
1078 		this.wrapper = wrapper;
1079 	}
1080 
1081 	void parse(T)(XMLReader!T reader)
1082 	{
1083 		if ( "name" in reader.front.attributes )
1084 			name = reader.front.attributes["name"];
1085 
1086 		reader.popFront();
1087 
1088 		while( !reader.empty && !reader.endTag("union") )
1089 		{
1090 			switch(reader.front.value)
1091 			{
1092 				case "doc":
1093 					reader.popFront();
1094 					doc ~= reader.front.value;
1095 					reader.popFront();
1096 					break;
1097 				case "doc-deprecated":
1098 					reader.popFront();
1099 					doc ~= "\n\nDeprecated: "~ reader.front.value;
1100 					reader.popFront();
1101 					break;
1102 				case "field":
1103 					GtkField field = new GtkField(wrapper);
1104 					field.parse(reader);
1105 					fields ~= field;
1106 					break;
1107 				case "record":
1108 					GtkField field = new GtkField(wrapper);
1109 					GtkStruct strct = new GtkStruct(wrapper, null);
1110 					strct.parse(reader);
1111 					strct.cType = strct.cType.toUpper()[0..1] ~ strct.cType[1 .. $];
1112 					field.gtkStruct = strct;
1113 					fields ~= field;
1114 					break;
1115 				default:
1116 					import std.stdio;
1117 					writeln(name);
1118 					assert(false, "Unexpected tag: "~ reader.front.value);
1119 			}
1120 			reader.popFront();
1121 		}
1122 	}
1123 
1124 	string[] getUnionDeclaration()
1125 	{
1126 		string[] buff;
1127 		if ( doc !is null && wrapper.includeComments )
1128 		{
1129 			buff ~= "/**";
1130 			foreach ( line; doc.splitLines() )
1131 				buff ~= " * "~ line.strip();
1132 			buff ~= " */";
1133 		}
1134 
1135 		if ( name )
1136 			buff ~= "union "~ tokenToGtkD(name.toUpper()[0..1] ~ name[1 .. $], wrapper.aliasses);
1137 		else
1138 			buff ~= "union";
1139 
1140 		buff ~= "{";
1141 		buff ~= GtkField.getFieldDeclarations(fields, wrapper);
1142 		buff ~= "}";
1143 
1144 		if ( name )
1145 			buff ~= tokenToGtkD(name.toUpper()[0..1] ~ name[1 .. $], wrapper.aliasses) ~" "~ tokenToGtkD(name.toLower(), wrapper.aliasses) ~";";
1146 
1147 		return buff;
1148 	}
1149 }