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 					throw new XMLException(reader, "Unexpected tag: "~ reader.front.value ~" in GtkStruct: "~ name);
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 			buff ~= indenter.format(["/**", "*/"]);
422 		}
423 
424 		bool firstSignal = true;
425 
426 		foreach ( func; functions )
427 		{
428 			if ( func.noCode || func.isVariadic() || func.type == GtkFunctionType.Callback )
429 				continue;
430 
431 			if ( isInterface() && func.type == GtkFunctionType.Constructor )
432 				continue;
433 
434 			if ( func.type == GtkFunctionType.Signal )
435 			{
436 				buff ~= "\n";
437 
438 				if ( firstSignal )
439 				{
440 					buff ~= indenter.format("int[string] connectedSignals;");
441 					buff ~= "\n";
442 					firstSignal = false;
443 				}
444 
445 				if ( isInterface() )
446 				{
447 					string[] prop;
448 
449 					prop ~= func.getDelegateDecleration() ~"[] _on"~ func.getSignalName() ~"Listeners;";
450 					prop ~= "@property "~ func.getDelegateDecleration() ~"[] on"~ func.getSignalName() ~"Listeners()";
451 					prop ~= "{";
452 					prop ~= "return _on"~ func.getSignalName() ~"Listeners;";
453 					prop ~= "}";
454 
455 					buff ~= indenter.format(prop);
456 				}
457 				else
458 				{
459 					buff ~= indenter.format(func.getDelegateDecleration() ~"[] on"~ func.getSignalName() ~"Listeners;");
460 				}
461 
462 				buff ~= indenter.format(func.getAddListenerdeclaration());
463 				buff ~= indenter.format(func.getAddListenerBody());
464 				buff ~= indenter.format(func.getSignalCallback());
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 			std.file.write(buildPath(wrapper.outputRoot, pack.srcDir, pack.name, name ~"T.d"), buff);
491 		else
492 			std.file.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("@property "~ func.getDelegateDecleration() ~"[] on"~ func.getSignalName() ~"Listeners();");
534 					string[] dec = func.getAddListenerdeclaration();
535 					dec[$-1] ~= ";";
536 
537 					buff ~= indenter.format(dec);
538 					buff ~= "\n";
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 		std.file.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 		std.file.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 == "utf8" || 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 == "utf8" || 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 ~= "gobject.Signals";
774 				imports ~= "gtkc.gdktypes";
775 			}
776 
777 			if ( func.type == GtkFunctionType.Constructor )
778 				imports ~= "glib.ConstructionException";
779 		}
780 
781 		foreach ( interf; implements )
782 		{
783 			if ( parentStruct && parentStruct.implements.canFind(interf) )
784 				continue;
785 
786 			GtkStruct strct = pack.getStruct(interf);
787 
788 			if ( strct )
789 			{
790 				imports ~= strct.pack.name ~"."~ strct.name ~"IF";
791 				imports ~= strct.pack.name ~"."~ strct.name ~"T";
792 			}
793 		}
794 
795 		imports = uniq(sort(imports)).array;
796 	}
797 
798 	private void writeImports(ref string buff, bool _public = false)
799 	{
800 		foreach ( imp; imports )
801 		{
802 			if ( _public || imp.endsWith("types") )
803 				buff ~= "public  import "~ imp ~";\n";
804 			else
805 				buff ~= "private import "~ imp ~";\n";
806 		}
807 
808 		buff ~= "\n\n";
809 	}
810 
811 	private void writeDocs(ref string buff)
812 	{
813 		if ( doc !is null && wrapper.includeComments )
814 		{
815 			buff ~= "/**\n";
816 			foreach ( line; doc.splitLines() )
817 				buff ~= " * "~ line.strip() ~"\n";
818 
819 			if ( libVersion )
820 			{
821 				buff ~= " *\n * Since: "~ libVersion ~"\n";
822 			}
823 
824 			buff ~= " */\n";
825 		}
826 		else if ( wrapper.includeComments )
827 		{
828 			buff ~= "/** */\n";
829 		}
830 	}
831 
832 	private GtkFunction getTypeFunction(string cIdentifier)
833 	{
834 		GtkType returnType = new GtkType(wrapper);
835 		returnType.name = "GObject.GType";
836 		returnType.cType = "GType";
837 
838 		GtkFunction func = new GtkFunction(wrapper, this);
839 		func.type = GtkFunctionType.Function;
840 		func.name = "get_type";
841 		func.cType = cIdentifier;
842 		func.returnType = returnType;
843 
844 		if ( type == GtkStructType.Interface )
845 			func.noCode = true;
846 
847 		return func;
848 	}
849 
850 	/**
851 	 * Get an overload of events that accept an generic Gdk Event
852 	 * instead of the spesific type listed in the gir files.
853 	 * 
854 	 * This for backwards compatibility with the documentation based wrapper.
855 	 */
856 	private string[] getGenericEventSignal(GtkFunction func)
857 	{
858 		GtkFunction signal = func.dup();
859 		string[] buff;
860 		
861 		for ( size_t i; i < signal.params.length; i++ )
862 		{
863 			if ( signal.params[i].type.name.startsWith("Gdk.Event") )
864 			{
865 				GtkType eventType = new GtkType(wrapper);
866 				eventType.name = "Gdk.Event";
867 				
868 				GtkParam newParam = new GtkParam(wrapper);
869 				newParam.name = signal.params[i].name;
870 				newParam.doc  = signal.params[i].doc;
871 				newParam.type = eventType;
872 				
873 				signal.params[i] = newParam;
874 				
875 				break;
876 			}
877 		}
878 
879 		string[] declaration = signal.getAddListenerdeclaration();
880 		signal.name = signal.name ~ "-generic-event";
881 		
882 		buff ~= func.getDelegateDecleration() ~"[] on"~ signal.getSignalName() ~"Listeners;";
883 		buff ~= declaration;
884 		buff ~= signal.getAddListenerBody();
885 		buff ~= signal.getSignalCallback();
886 		
887 		return buff;
888 	}
889 }
890 
891 final class GtkField
892 {
893 	string name;
894 	string doc;
895 	GtkType type;
896 	int bits = -1;
897 
898 	GtkFunction callback;
899 	GtkUnion gtkUnion;
900 	GtkStruct gtkStruct;
901 
902 	GtkWrapper wrapper;
903 
904 	this(GtkWrapper wrapper)
905 	{
906 		this.wrapper = wrapper;
907 	}
908 
909 	void parse(T)(XMLReader!T reader)
910 	{
911 		name = reader.front.attributes["name"];
912 
913 		if ( "bits" in reader.front.attributes )
914 			bits = to!int(reader.front.attributes["bits"]);
915 
916 		//TODO: readable private?
917 
918 		reader.popFront();
919 
920 		while( !reader.empty && !reader.endTag("field") )
921 		{
922 			if ( reader.front.type == XMLNodeType.EndTag )
923 			{
924 				reader.popFront();
925 				continue;
926 			}
927 
928 			switch(reader.front.value)
929 			{
930 				case "doc":
931 					reader.popFront();
932 					doc ~= reader.front.value;
933 					reader.popFront();
934 					break;
935 				case "doc-deprecated":
936 					reader.popFront();
937 					doc ~= "\n\nDeprecated: "~ reader.front.value;
938 					reader.popFront();
939 					break;
940 				case "array":
941 				case "type":
942 					type = new GtkType(wrapper);
943 					type.parse(reader);
944 					break;
945 				case "callback":
946 					callback = new GtkFunction(wrapper, null);
947 					callback.parse(reader);
948 					break;
949 				default:
950 					throw new XMLException(reader, "Unexpected tag: "~ reader.front.value ~" in GtkField: "~ name);
951 			}
952 			reader.popFront();
953 		}
954 	}
955 
956 	/**
957 	 * A special case for fields, we need to know about all of then
958 	 * to properly construct the bitfields.
959 	 */
960 	static string[] getFieldDeclarations(GtkField[] fields, GtkWrapper wrapper)
961 	{
962 		string[] buff;
963 		int bitcount;
964 
965 		void endBitfield()
966 		{
967 			//AFAIK: C bitfields are padded to a multiple of sizeof uint.
968 			int padding = 32 - (bitcount % 32);
969 
970 			if ( padding > 0 && padding < 32)
971 			{
972 				buff[buff.length-1] ~= ",";
973 				buff ~= "uint, \"\", "~ to!string(padding);
974 				buff ~= "));";
975 			}
976 			else
977 			{
978 				buff ~= "));";
979 			}
980 
981 			bitcount = 0;
982 		}
983 
984 		foreach ( field; fields )
985 		{
986 			if ( field.callback )
987 			{
988 				if ( bitcount > 0 )
989 					endBitfield();
990 				buff ~= field.callback.getFunctionPointerDecleration();
991 				continue;
992 			}
993 
994 			if ( field.gtkUnion )
995 			{
996 				if ( bitcount > 0 )
997 					endBitfield();
998 				buff ~= field.gtkUnion.getUnionDeclaration();
999 				continue;
1000 			}
1001 
1002 			if ( field.gtkStruct )
1003 			{
1004 				if ( bitcount > 0 )
1005 					endBitfield();
1006 				buff ~= field.gtkStruct.getStructDeclaration();
1007 				buff ~= stringToGtkD(field.gtkStruct.cType ~" "~ field.gtkStruct.name ~";", wrapper.aliasses);
1008 				continue;
1009 			}
1010 
1011 			if ( field.bits > 0 )
1012 			{
1013 				if ( bitcount == 0 )
1014 				{
1015 					buff ~= "import std.bitmanip: bitfields;";
1016 					buff ~= "mixin(bitfields!(";
1017 				}
1018 				else
1019 				{
1020 					buff[buff.length-1] ~= ",";
1021 				}
1022 
1023 				bitcount += field.bits;
1024 				buff ~=stringToGtkD(field.type.cType ~", \""~ field.name ~"\", "~ to!string(field.bits), wrapper.aliasses);
1025 				continue;
1026 			}
1027 			else if ( bitcount > 0)
1028 			{
1029 				endBitfield();
1030 			}
1031 
1032 			if ( field.doc !is null && wrapper.includeComments && field.bits < 0 )
1033 			{
1034 				buff ~= "/**";
1035 				foreach ( line; field.doc.splitLines() )
1036 					buff ~= " * "~ line.strip();
1037 				buff ~= " */";
1038 			}
1039 
1040 			string dType;
1041 
1042 			if ( field.type.size == -1 )
1043 			{
1044 				if ( field.type.cType.empty )
1045 					dType = stringToGtkD(field.type.name, wrapper.aliasses);
1046 				else
1047 					dType = stringToGtkD(field.type.cType, wrapper.aliasses);
1048 			}
1049 			else if ( field.type.elementType.cType.empty )
1050 			{
1051 				//Special case for GObject.Value.
1052 				dType = stringToGtkD(field.type.elementType.name, wrapper.aliasses);
1053 				dType ~= "["~ to!string(field.type.size) ~"]";
1054 			}
1055 			else
1056 			{
1057 				dType = stringToGtkD(field.type.elementType.cType, wrapper.aliasses);
1058 				dType ~= "["~ to!string(field.type.size) ~"]";
1059 			}
1060 
1061 			buff ~= dType ~" "~ tokenToGtkD(field.name, wrapper.aliasses) ~";";
1062 		}
1063 
1064 		if ( bitcount > 0)
1065 		{
1066 			endBitfield();
1067 		}
1068 
1069 		return buff;
1070 	}
1071 }
1072 
1073 final class GtkUnion
1074 {
1075 	string name;
1076 	string doc;
1077 	GtkField[] fields;
1078 
1079 	GtkWrapper wrapper;
1080 
1081 	this(GtkWrapper wrapper)
1082 	{
1083 		this.wrapper = wrapper;
1084 	}
1085 
1086 	void parse(T)(XMLReader!T reader)
1087 	{
1088 		if ( "name" in reader.front.attributes )
1089 			name = reader.front.attributes["name"];
1090 
1091 		reader.popFront();
1092 
1093 		while( !reader.empty && !reader.endTag("union") )
1094 		{
1095 			switch(reader.front.value)
1096 			{
1097 				case "doc":
1098 					reader.popFront();
1099 					doc ~= reader.front.value;
1100 					reader.popFront();
1101 					break;
1102 				case "doc-deprecated":
1103 					reader.popFront();
1104 					doc ~= "\n\nDeprecated: "~ reader.front.value;
1105 					reader.popFront();
1106 					break;
1107 				case "field":
1108 					GtkField field = new GtkField(wrapper);
1109 					field.parse(reader);
1110 					fields ~= field;
1111 					break;
1112 				case "record":
1113 					GtkField field = new GtkField(wrapper);
1114 					GtkStruct strct = new GtkStruct(wrapper, null);
1115 					strct.parse(reader);
1116 					strct.cType = strct.cType.toUpper()[0..1] ~ strct.cType[1 .. $];
1117 					field.gtkStruct = strct;
1118 					fields ~= field;
1119 					break;
1120 				default:
1121 					throw new XMLException(reader, "Unexpected tag: "~ reader.front.value ~" in GtkUnion: "~ name);
1122 			}
1123 			reader.popFront();
1124 		}
1125 	}
1126 
1127 	string[] getUnionDeclaration()
1128 	{
1129 		string[] buff;
1130 		if ( doc !is null && wrapper.includeComments )
1131 		{
1132 			buff ~= "/**";
1133 			foreach ( line; doc.splitLines() )
1134 				buff ~= " * "~ line.strip();
1135 			buff ~= " */";
1136 		}
1137 
1138 		if ( name )
1139 			buff ~= "union "~ tokenToGtkD(name.toUpper()[0..1] ~ name[1 .. $], wrapper.aliasses);
1140 		else
1141 			buff ~= "union";
1142 
1143 		buff ~= "{";
1144 		buff ~= GtkField.getFieldDeclarations(fields, wrapper);
1145 		buff ~= "}";
1146 
1147 		if ( name )
1148 			buff ~= tokenToGtkD(name.toUpper()[0..1] ~ name[1 .. $], wrapper.aliasses) ~" "~ tokenToGtkD(name.toLower(), wrapper.aliasses) ~";";
1149 
1150 		return buff;
1151 	}
1152 }