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