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: among, sort, uniq, startsWith, endsWith, canFind;
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 				buff ~= indenter.format(func.getDelegateWrapperDeclaration());
450 				buff ~= indenter.format("protected " ~ func.getDelegateWrapperName() ~ "[] on" ~ func.getSignalName() ~"Listeners;");
451 
452 				buff ~= "\n";
453 				buff ~= indenter.format(func.getAddListenerDeclaration());
454 				buff ~= indenter.format(func.getAddListenerBody());
455 				buff ~= indenter.format(func.getSignalCallback());
456 				buff ~= indenter.format(func.getSignalDestroyCallback());
457 				buff ~= "\n";
458 				/*
459 				buff ~= indenter.format(func.getRemoveListenerDeclaration());
460 				buff ~= indenter.format(func.getRemoveListenerBody());
461 				buff ~= "\n";
462 				*/
463 				buff ~= indenter.format(func.getInternalRemoveListenerDeclaration());
464 				buff ~= indenter.format(func.getInternalRemoveListenerBody());
465 
466 				foreach ( param; func.params )
467 				{
468 					if ( param.type.name.startsWith("Gdk.Event") && param.type.name != "Gdk.Event" )
469 					{
470 						buff ~= "\n";
471 						buff ~= indenter.format(getGenericEventSignal(func));
472 
473 						break;
474 					}
475 				}
476 			}
477 			else
478 			{
479 				buff ~= "\n";
480 				buff ~= indenter.format(func.getDeclaration());
481 				buff ~= indenter.format("{");
482 				buff ~= indenter.format(func.getBody());
483 				buff ~= indenter.format("}");
484 			}
485 		}
486 
487 		buff ~= indenter.format("}");
488 
489 		if ( isInterface() )
490 			write(buildPath(wrapper.outputRoot, pack.srcDir, pack.name, name ~"T.d"), buff);
491 		else
492 			write(buildPath(wrapper.outputRoot, pack.srcDir, pack.name, name ~".d"), buff);
493 	}
494 
495 	void writeInterface()
496 	{
497 		string buff = wrapper.licence;
498 		auto indenter = new IndentedStringBuilder();
499 
500 		buff ~= "module "~ pack.name ~"."~ name ~"IF;\n\n";
501 
502 		writeImports(buff);
503 		writeDocs(buff);
504 
505 		buff ~= "public interface "~ name ~"IF";
506 		buff ~= indenter.format("{");
507 
508 		if ( cType )
509 		{
510 			buff ~= indenter.format("/** Get the main Gtk struct */");
511 			buff ~= indenter.format("public "~ cType ~"* "~ getHandleFunc() ~"();");
512 			buff ~= "\n";
513 
514 			buff ~= indenter.format("/** the main Gtk struct as a void* */");
515 			buff ~= indenter.format("protected void* getStruct();");
516 			buff ~= "\n";
517 
518 			if ( !lookupInterfaceCode.empty )
519 			{
520 				buff ~= indenter.format(lookupInterfaceCode);
521 				buff ~= "\n";
522 
523 				buff ~= indenter.format(["/**", "*/"]);
524 			}
525 
526 			foreach ( func; functions )
527 			{
528 				if ( func.noCode || func.isVariadic() || func.type == GtkFunctionType.Callback || func.type == GtkFunctionType.Constructor )
529 					continue;
530 
531 				if ( func.type == GtkFunctionType.Signal )
532 				{
533 					buff ~= indenter.format(func.getAddListenerDeclaration() ~ ";");
534 					buff ~= "\n";
535 					/*
536 					buff ~= indenter.format(func.getRemoveListenerDeclaration() ~ ";");
537 					buff ~= "\n";
538 					*/
539 				}
540 				else
541 				{
542 					string[] dec = func.getDeclaration();
543 					dec[$-1] = dec[$-1].replace("override ", "");
544 					dec[$-1] ~= ";";
545 
546 					buff ~= "\n";
547 					buff ~= indenter.format(dec);
548 				}
549 			}
550 
551 			buff ~= indenter.format("}");
552 		}
553 
554 		write(buildPath(wrapper.outputRoot, pack.srcDir, pack.name, name ~"IF.d"), buff);
555 	}
556 
557 	void writeDStruct()
558 	{
559 		string buff = wrapper.licence;
560 		auto indenter = new IndentedStringBuilder();
561 
562 		buff ~= "module "~ pack.name ~"."~ name ~";\n\n";
563 
564 		writeImports(buff);
565 		writeDocs(buff);
566 
567 		if ( !noNamespace )
568 		{
569 			buff ~= "public struct "~ name ~"\n";
570 			buff ~= indenter.format("{");
571 		}
572 
573 		if ( !lookupCode.empty )
574 		{
575 			buff ~= indenter.format(lookupCode);
576 			buff ~= "\n";
577 
578 			buff ~= indenter.format(["/**", "*/"]);
579 		}
580 
581 		foreach ( func; functions )
582 		{
583 			if ( func.noCode || func.isVariadic() || !( func.type == GtkFunctionType.Function || func.type == GtkFunctionType.Method ) )
584 				continue;
585 
586 			buff ~= "\n";
587 			buff ~= indenter.format(func.getDeclaration());
588 			buff ~= indenter.format("{");
589 			buff ~= indenter.format(func.getBody());
590 			buff ~= indenter.format("}");
591 		}
592 
593 		if ( !noNamespace )
594 			buff ~= indenter.format("}");
595 
596 		write(buildPath(wrapper.outputRoot, pack.srcDir, pack.name, name ~".d"), buff);
597 	}
598 
599 	/**
600 	 * Return the variable name the c type is stored in.
601 	 */
602 	string getHandleVar()
603 	{
604 		if (cType.length == 0)
605 			return "";
606 
607 		string p = to!string(toLower(cType[0]));
608 		if ( cType.endsWith("_t") )
609 		{
610 			return p ~ cType[1 .. $ - 2];
611 		} else {
612 			return p ~ cType[1 .. $];
613 		}
614 	}
615 
616 	/**
617 	 * Returns the name of the function that returns the cType.
618 	 */
619 	string getHandleFunc()
620 	{
621 		if ( parentStruct && parentStruct.name == name )
622 			return "get"~ cast(char)pack.name[0].toUpper ~ pack.name[1..$] ~ name ~"Struct";
623 		else
624 			return "get"~ name ~"Struct";
625 	}
626 
627 	bool isInterface()
628 	{
629 		if ( lookupInterface )
630 			return true;
631 		if ( lookupClass )
632 			return false;
633 		if ( type == GtkStructType.Interface )
634 			return true;
635 
636 		return false;
637 	}
638 
639 	bool isNamespace()
640 	{
641 		return type == GtkStructType.Record && !(lookupClass || lookupInterface) && !noNamespace;
642 	}
643 
644 	void merge(GtkStruct mergeStruct)
645 	{
646 		foreach ( func; mergeStruct.functions )
647 		{
648 			func.strct = this;
649 			functions[func.name] = func;
650 		}
651 	}
652 
653 	GtkStruct getAncestor()
654 	{
655 		if ( parent.empty )
656 			return this;
657 
658 		if ( !parentStruct )
659 			parentStruct = pack.getStruct(parent);
660 
661 		return parentStruct.getAncestor();
662 	}
663 
664 	private void resolveImports()
665 	{
666 		if ( parentStruct && parentStruct.name != name)
667 		{
668 			imports ~= parentStruct.pack.name ~"."~ parentStruct.name;
669 		}
670 		else if ( parentStruct )
671 		{
672 			string QParent = parentStruct.pack.name.capitalize() ~ parentStruct.name;
673 			imports ~= parentStruct.pack.name ~"."~ parentStruct.name ~" : "~ QParent ~" = "~ parentStruct.name;
674 			structWrap[parent] = QParent;
675 		}
676 
677 		imports ~= pack.bindDir ~"."~ pack.name;
678 		imports ~= pack.bindDir ~"."~ pack.name ~"types";
679 
680 		foreach( func; functions )
681 		{
682 			if ( func.noCode )
683 				continue;
684 
685 			if ( func.throws )
686 			{
687 				imports ~= "glib.ErrorG";
688 				imports ~= "glib.GException";
689 			}
690 
691 			void getReturnImport(GtkType type)
692 			{
693 				if ( type.name in structWrap || type.name in aliases )
694 					return;
695 
696 				GtkStruct dType = pack.getStruct(type.name);
697 
698 				if ( dType && (dType.type != GtkStructType.Record || dType.lookupClass || dType.lookupInterface) )
699 				{
700 					if ( !dType.pack.name.among("cairo", "glib", "gthread") )
701 						imports ~= "gobject.ObjectG";
702 
703 					if ( dType.type == GtkStructType.Interface && func.name.startsWith("new") )
704 						return;
705 
706 					if ( dType is this && dType.type != GtkStructType.Interface )
707 						return;
708 
709 					imports ~= dType.pack.name ~"."~ dType.name;
710 
711 					if ( dType.type == GtkStructType.Interface || dType.lookupInterface )
712 						imports ~= dType.pack.name ~"."~ dType.name ~"IF";
713 				}
714 				else if ( type.name.among("utf8", "filename") || type.cType.among("guchar**") )
715 					imports ~= "glib.Str";
716 			}
717 
718 			if ( func.returnType && func.returnType.cType !in structWrap )
719 			{
720 				getReturnImport(func.returnType);
721 
722 				if ( func.returnType.isArray() )
723 					getReturnImport(func.returnType.elementType);
724 			}
725 
726 			void getParamImport(GtkType type)
727 			{
728 				if ( type.name in structWrap || type.name in aliases )
729 					return;
730 
731 				GtkStruct dType = pack.getStruct(type.name);
732 
733 				if ( dType is this )
734 					return;
735 			
736 				if ( func.type == GtkFunctionType.Signal && type.name.startsWith("Gdk.Event") )
737 					imports ~= "gdk.Event";
738 
739 				if ( dType && (dType.type != GtkStructType.Record || dType.lookupClass || dType.lookupInterface) )
740 				{
741 					if ( dType.type == GtkStructType.Interface || dType.lookupInterface )
742 					{
743 						imports ~= dType.pack.name ~"."~ dType.name ~"IF";
744 
745 						if ( func.type == GtkFunctionType.Signal )
746 							imports ~= dType.pack.name ~"."~ dType.name;
747 					}
748 					else
749 					{
750 						imports ~= dType.pack.name ~"."~ dType.name;
751 					}
752 				}
753 				else if ( type.name.among("utf8", "filename") || type.cType.among("guchar**") )
754 					imports ~= "glib.Str";
755 			}
756 
757 			foreach ( param; func.params )
758 			{
759 				if ( param.type.cType in structWrap )
760 					continue;
761 
762 				getParamImport(param.type);
763 
764 				if ( param.type.elementType )
765 					getParamImport(param.type.elementType);
766 
767 				if ( param.direction != GtkParamDirection.Default )
768 					getReturnImport(param.type);
769 			}
770 
771 			if ( func.type == GtkFunctionType.Signal )
772 			{
773 				imports ~= "std.algorithm";
774 				imports ~= "gobject.Signals";
775 				imports ~= "gtkc.gdktypes";
776 
777 			}
778 
779 			if ( func.type == GtkFunctionType.Constructor )
780 				imports ~= "glib.ConstructionException";
781 		}
782 
783 		foreach ( interf; implements )
784 		{
785 			if ( parentStruct && parentStruct.implements.canFind(interf) )
786 				continue;
787 
788 			GtkStruct strct = pack.getStruct(interf);
789 
790 			if ( strct )
791 			{
792 				imports ~= strct.pack.name ~"."~ strct.name ~"IF";
793 				imports ~= strct.pack.name ~"."~ strct.name ~"T";
794 			}
795 		}
796 
797 		imports = uniq(sort(imports)).array;
798 	}
799 
800 	private void writeImports(ref string buff, bool _public = false)
801 	{
802 		foreach ( imp; imports )
803 		{
804 			if ( _public || imp.endsWith("types") )
805 				buff ~= "public  import "~ imp ~";\n";
806 			else
807 				buff ~= "private import "~ imp ~";\n";
808 		}
809 
810 		buff ~= "\n\n";
811 	}
812 
813 	private void writeDocs(ref string buff)
814 	{
815 		if ( doc !is null && wrapper.includeComments )
816 		{
817 			buff ~= "/**\n";
818 			foreach ( line; doc.splitLines() )
819 				buff ~= " * "~ line.strip() ~"\n";
820 
821 			if ( libVersion )
822 			{
823 				buff ~= " *\n * Since: "~ libVersion ~"\n";
824 			}
825 
826 			buff ~= " */\n";
827 		}
828 		else if ( wrapper.includeComments )
829 		{
830 			buff ~= "/** */\n";
831 		}
832 	}
833 
834 	private GtkFunction getTypeFunction(string cIdentifier)
835 	{
836 		GtkType returnType = new GtkType(wrapper);
837 		returnType.name = "GObject.GType";
838 		returnType.cType = "GType";
839 
840 		GtkFunction func = new GtkFunction(wrapper, this);
841 		func.type = GtkFunctionType.Function;
842 		func.name = "get_type";
843 		func.cType = cIdentifier;
844 		func.returnType = returnType;
845 
846 		if ( type == GtkStructType.Interface )
847 			func.noCode = true;
848 
849 		return func;
850 	}
851 
852 	/**
853 	 * Get an overload of events that accept an generic Gdk Event
854 	 * instead of the spesific type listed in the gir files.
855 	 * 
856 	 * This for backwards compatibility with the documentation based wrapper.
857 	 */
858 	private string[] getGenericEventSignal(GtkFunction func)
859 	{
860 		GtkFunction signal = func.dup();
861 		string[] buff;
862 		
863 		for ( size_t i; i < signal.params.length; i++ )
864 		{
865 			if ( signal.params[i].type.name.startsWith("Gdk.Event") )
866 			{
867 				GtkType eventType = new GtkType(wrapper);
868 				eventType.name = "Gdk.Event";
869 				
870 				GtkParam newParam = new GtkParam(wrapper);
871 				newParam.name = signal.params[i].name;
872 				newParam.doc  = signal.params[i].doc;
873 				newParam.type = eventType;
874 				
875 				signal.params[i] = newParam;
876 				
877 				break;
878 			}
879 		}
880 
881 		string[] declaration = signal.getAddListenerDeclaration();
882 		signal.name = signal.name ~ "-generic-event";
883 
884 		buff ~= signal.getDelegateWrapperDeclaration();
885 		buff ~= "protected " ~ signal.getDelegateWrapperName() ~ "[] on" ~ signal.getSignalName() ~"Listeners;";
886 		buff ~= "\n";
887 		
888 		buff ~= declaration;
889 		buff ~= signal.getAddListenerBody();
890 		buff ~= signal.getSignalCallback();
891 		buff ~= signal.getSignalDestroyCallback();
892 		/*
893 		buff ~= signal.getRemoveListenerDeclaration();
894 		buff ~= signal.getRemoveListenerBody();
895 		*/
896 		buff ~= signal.getInternalRemoveListenerDeclaration();
897 		buff ~= signal.getInternalRemoveListenerBody();
898 		
899 		return buff;
900 	}
901 }
902 
903 final class GtkField
904 {
905 	string name;
906 	string doc;
907 	GtkType type;
908 	int bits = -1;
909 
910 	GtkFunction callback;
911 	GtkUnion gtkUnion;
912 	GtkStruct gtkStruct;
913 
914 	GtkWrapper wrapper;
915 
916 	this(GtkWrapper wrapper)
917 	{
918 		this.wrapper = wrapper;
919 	}
920 
921 	void parse(T)(XMLReader!T reader)
922 	{
923 		name = reader.front.attributes["name"];
924 
925 		if ( "bits" in reader.front.attributes )
926 			bits = to!int(reader.front.attributes["bits"]);
927 
928 		//TODO: readable private?
929 
930 		reader.popFront();
931 
932 		while( !reader.empty && !reader.endTag("field") )
933 		{
934 			if ( reader.front.type == XMLNodeType.EndTag )
935 			{
936 				reader.popFront();
937 				continue;
938 			}
939 
940 			switch(reader.front.value)
941 			{
942 				case "doc":
943 					reader.popFront();
944 					doc ~= reader.front.value;
945 					reader.popFront();
946 					break;
947 				case "doc-deprecated":
948 					reader.popFront();
949 					doc ~= "\n\nDeprecated: "~ reader.front.value;
950 					reader.popFront();
951 					break;
952 				case "array":
953 				case "type":
954 					type = new GtkType(wrapper);
955 					type.parse(reader);
956 					break;
957 				case "callback":
958 					callback = new GtkFunction(wrapper, null);
959 					callback.parse(reader);
960 					break;
961 				default:
962 					throw new XMLException(reader, "Unexpected tag: "~ reader.front.value ~" in GtkField: "~ name);
963 			}
964 			reader.popFront();
965 		}
966 	}
967 
968 	/**
969 	 * A special case for fields, we need to know about all of then
970 	 * to properly construct the bitfields.
971 	 */
972 	static string[] getFieldDeclarations(GtkField[] fields, GtkWrapper wrapper)
973 	{
974 		string[] buff;
975 		int bitcount;
976 
977 		void endBitfield()
978 		{
979 			//AFAIK: C bitfields are padded to a multiple of sizeof uint.
980 			int padding = 32 - (bitcount % 32);
981 
982 			if ( padding > 0 && padding < 32)
983 			{
984 				buff[buff.length-1] ~= ",";
985 				buff ~= "uint, \"\", "~ to!string(padding);
986 				buff ~= "));";
987 			}
988 			else
989 			{
990 				buff ~= "));";
991 			}
992 
993 			bitcount = 0;
994 		}
995 
996 		foreach ( field; fields )
997 		{
998 			if ( field.callback )
999 			{
1000 				if ( bitcount > 0 )
1001 					endBitfield();
1002 				buff ~= field.callback.getFunctionPointerDecleration();
1003 				continue;
1004 			}
1005 
1006 			if ( field.gtkUnion )
1007 			{
1008 				if ( bitcount > 0 )
1009 					endBitfield();
1010 				buff ~= field.gtkUnion.getUnionDeclaration();
1011 				continue;
1012 			}
1013 
1014 			if ( field.gtkStruct )
1015 			{
1016 				if ( bitcount > 0 )
1017 					endBitfield();
1018 				buff ~= field.gtkStruct.getStructDeclaration();
1019 				buff ~= stringToGtkD(field.gtkStruct.cType ~" "~ field.gtkStruct.name ~";", wrapper.aliasses);
1020 				continue;
1021 			}
1022 
1023 			if ( field.bits > 0 )
1024 			{
1025 				if ( bitcount == 0 )
1026 				{
1027 					buff ~= "import std.bitmanip: bitfields;";
1028 					buff ~= "mixin(bitfields!(";
1029 				}
1030 				else
1031 				{
1032 					buff[buff.length-1] ~= ",";
1033 				}
1034 
1035 				bitcount += field.bits;
1036 				buff ~=stringToGtkD(field.type.cType ~", \""~ field.name ~"\", "~ to!string(field.bits), wrapper.aliasses);
1037 				continue;
1038 			}
1039 			else if ( bitcount > 0)
1040 			{
1041 				endBitfield();
1042 			}
1043 
1044 			if ( field.doc !is null && wrapper.includeComments && field.bits < 0 )
1045 			{
1046 				buff ~= "/**";
1047 				foreach ( line; field.doc.splitLines() )
1048 					buff ~= " * "~ line.strip();
1049 				buff ~= " */";
1050 			}
1051 
1052 			string dType;
1053 
1054 			if ( field.type.size == -1 )
1055 			{
1056 				if ( field.type.cType.empty )
1057 					dType = stringToGtkD(field.type.name, wrapper.aliasses);
1058 				else
1059 					dType = stringToGtkD(field.type.cType, wrapper.aliasses);
1060 			}
1061 			else if ( field.type.elementType.cType.empty )
1062 			{
1063 				//Special case for GObject.Value.
1064 				dType = stringToGtkD(field.type.elementType.name, wrapper.aliasses);
1065 				dType ~= "["~ to!string(field.type.size) ~"]";
1066 			}
1067 			else
1068 			{
1069 				dType = stringToGtkD(field.type.elementType.cType, wrapper.aliasses);
1070 				dType ~= "["~ to!string(field.type.size) ~"]";
1071 			}
1072 
1073 			buff ~= dType ~" "~ tokenToGtkD(field.name, wrapper.aliasses) ~";";
1074 		}
1075 
1076 		if ( bitcount > 0)
1077 		{
1078 			endBitfield();
1079 		}
1080 
1081 		return buff;
1082 	}
1083 }
1084 
1085 final class GtkUnion
1086 {
1087 	string name;
1088 	string doc;
1089 	GtkField[] fields;
1090 
1091 	GtkWrapper wrapper;
1092 
1093 	this(GtkWrapper wrapper)
1094 	{
1095 		this.wrapper = wrapper;
1096 	}
1097 
1098 	void parse(T)(XMLReader!T reader)
1099 	{
1100 		if ( "name" in reader.front.attributes )
1101 			name = reader.front.attributes["name"];
1102 
1103 		reader.popFront();
1104 
1105 		while( !reader.empty && !reader.endTag("union") )
1106 		{
1107 			switch(reader.front.value)
1108 			{
1109 				case "doc":
1110 					reader.popFront();
1111 					doc ~= reader.front.value;
1112 					reader.popFront();
1113 					break;
1114 				case "doc-deprecated":
1115 					reader.popFront();
1116 					doc ~= "\n\nDeprecated: "~ reader.front.value;
1117 					reader.popFront();
1118 					break;
1119 				case "field":
1120 					GtkField field = new GtkField(wrapper);
1121 					field.parse(reader);
1122 					fields ~= field;
1123 					break;
1124 				case "record":
1125 					GtkField field = new GtkField(wrapper);
1126 					GtkStruct strct = new GtkStruct(wrapper, null);
1127 					strct.parse(reader);
1128 					strct.cType = strct.cType.toUpper()[0..1] ~ strct.cType[1 .. $];
1129 					field.gtkStruct = strct;
1130 					fields ~= field;
1131 					break;
1132 				default:
1133 					throw new XMLException(reader, "Unexpected tag: "~ reader.front.value ~" in GtkUnion: "~ name);
1134 			}
1135 			reader.popFront();
1136 		}
1137 	}
1138 
1139 	string[] getUnionDeclaration()
1140 	{
1141 		string[] buff;
1142 		if ( doc !is null && wrapper.includeComments )
1143 		{
1144 			buff ~= "/**";
1145 			foreach ( line; doc.splitLines() )
1146 				buff ~= " * "~ line.strip();
1147 			buff ~= " */";
1148 		}
1149 
1150 		if ( name )
1151 			buff ~= "union "~ tokenToGtkD(name.toUpper()[0..1] ~ name[1 .. $], wrapper.aliasses);
1152 		else
1153 			buff ~= "union";
1154 
1155 		buff ~= "{";
1156 		buff ~= GtkField.getFieldDeclarations(fields, wrapper);
1157 		buff ~= "}";
1158 
1159 		if ( name )
1160 			buff ~= tokenToGtkD(name.toUpper()[0..1] ~ name[1 .. $], wrapper.aliasses) ~" "~ tokenToGtkD(name.toLower(), wrapper.aliasses) ~";";
1161 
1162 		return buff;
1163 	}
1164 }