1 /*
2  * This file is part of gir-to-d.
3  *
4  * gir-to-d 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.
8  *
9  * gir-to-d is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with gir-to-d.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 module gtd.GirFunction;
19 
20 import std.algorithm: among, startsWith, endsWith;
21 import std.conv;
22 import std.range;
23 import std.string : chomp, splitLines, strip;
24 import std.uni: toUpper, toLower;
25 
26 import gtd.GirEnum;
27 import gtd.GirStruct;
28 import gtd.GirType;
29 import gtd.GirVersion;
30 import gtd.GirWrapper;
31 import gtd.Log;
32 import gtd.XMLReader;
33 
34 enum GirFunctionType : string
35 {
36 	Constructor = "constructor",
37 	Method = "method",
38 	Function = "function",
39 	Callback = "callback",
40 	Signal = "glib:signal"
41 }
42 
43 enum GirTransferOwnership : string
44 {
45 	None = "none",          /// Gtk owns the returned reference.
46 	Full = "full",          /// We own the returned reference.
47 	Container = "container" /// The container in which the references reside has ownership.
48 }
49 
50 final class GirFunction
51 {
52 	string name;
53 	GirFunctionType type;
54 	string doc;
55 	string cType;
56 	string libVersion;
57 	string movedTo;
58 	bool virtual = false;
59 	bool throws = false;
60 	bool lookupOverride; /// Force marking this function with override.
61 	bool noCode; /// Don't generate any class code for this function.
62 
63 	GirType returnType;
64 	GirTransferOwnership returnOwnership = GirTransferOwnership.None;
65 	GirParam instanceParam;
66 	GirParam[] params;
67 
68 	GirWrapper wrapper;
69 	GirStruct strct;
70 
71 	this (GirWrapper wrapper, GirStruct strct)
72 	{
73 		this.wrapper = wrapper;
74 		this.strct = strct;
75 	}
76 
77 	GirFunction dup()
78 	{
79 		GirFunction copy = new GirFunction(wrapper, strct);
80 		
81 		foreach ( i, field; this.tupleof )
82 			copy.tupleof[i] = field;
83 		
84 		return copy;
85 	}
86 
87 	void parse(T)(XMLReader!T reader)
88 	{
89 		name = reader.front.attributes["name"];
90 		// Special case for g_iconv wich doesnt have a name.
91 		if ( name.empty && "moved-to" in reader.front.attributes )
92 			name = reader.front.attributes["moved-to"];
93 
94 		type = cast(GirFunctionType)reader.front.value;
95 
96 		if ( "c:type" in reader.front.attributes )
97 			cType = reader.front.attributes["c:type"];
98 		if ( "c:identifier" in reader.front.attributes )
99 			cType = reader.front.attributes["c:identifier"];
100 		if ( "version" in reader.front.attributes )
101 		{
102 			libVersion = reader.front.attributes["version"];
103 			if ( strct )
104 				strct.pack.checkVersion(libVersion);
105 		}
106 		if ( "throws" in reader.front.attributes )
107 			throws = reader.front.attributes["throws"] == "1";
108 		if ( "moved-to" in reader.front.attributes )
109 			movedTo = reader.front.attributes["moved-to"];
110 
111 		reader.popFront();
112 
113 		while( !reader.empty && !reader.endTag("constructor", "method", "function", "callback", "glib:signal") )
114 		{
115 			switch ( reader.front.value )
116 			{
117 				case "attribute":
118 					//TODO: Do we need these attibutes?
119 					//dbus.name ccode.ordering deprecated replacement.
120 					reader.skipTag();
121 					break;
122 				case "doc":
123 				case "doc-stability":
124 					reader.popFront();
125 					doc ~= reader.front.value;
126 					reader.popFront();
127 					break;
128 				case "doc-deprecated":
129 					reader.popFront();
130 					doc ~= "\n\nDeprecated: "~ reader.front.value;
131 					reader.popFront();
132 					break;
133 				case "doc-version":
134 					reader.skipTag();
135 					break;
136 				case "return-value":
137 					if ( "transfer-ownership" in reader.front.attributes )
138 						returnOwnership = cast(GirTransferOwnership)reader.front.attributes["transfer-ownership"];
139 
140 					returnType = new GirType(wrapper);
141 					reader.popFront();
142 
143 					while( !reader.empty && !reader.endTag("return-value") )
144 					{
145 						switch ( reader.front.value )
146 						{
147 							case "doc":
148 								reader.popFront();
149 								returnType.doc ~= reader.front.value;
150 								reader.popFront();
151 								break;
152 							case "array":
153 							case "type":
154 								returnType.parse(reader);
155 								break;
156 							default:
157 								error("Unexpected tag: ", reader.front.value, " in GirFunction: ", name, reader);
158 						}
159 						reader.popFront();
160 					}
161 					break;
162 				case "parameters":
163 					reader.popFront();
164 					while( !reader.empty && !reader.endTag("parameters") )
165 					{
166 						switch ( reader.front.value )
167 						{
168 							case "instance-parameter":
169 								instanceParam = new GirParam(wrapper);
170 								instanceParam.parse(reader);
171 								break;
172 							case "parameter":
173 								GirParam param = new GirParam(wrapper);
174 								param.parse(reader);
175 								params ~= param;
176 								break;
177 							default:
178 								error("Unexpected tag: ", reader.front.value, " in GirFunction: ", name, reader);
179 						}
180 						reader.popFront();
181 					}
182 					break;
183 				case "source-position":
184 					reader.skipTag();
185 					break;
186 				default:
187 					error("Unexpected tag: ", reader.front.value, " in GirFunction: ", name, reader);
188 			}
189 			reader.popFront();
190 		}
191 
192 		if ( type == GirFunctionType.Function && name.startsWith("new") && returnType.cType != "void" )
193 			type = GirFunctionType.Constructor;
194 
195 		// For the case where a param is `const gchar* name[]` whitch ends up in the gir files
196 		// as an array with elementType name=utf8 c:type=gchar, missing the [].
197 		switch ( cType )
198 		{
199 			case "gtk_icon_theme_choose_icon":
200 			case "gtk_icon_theme_choose_icon_for_scale":
201 				params[0].type.cType = "char**";
202 				params[0].type.elementType.cType = "char*";
203 				break;
204 			case "g_object_getv":
205 			case "g_object_setv":
206 				params[1].type.cType = "char**";
207 				params[1].type.elementType.cType = "char*";
208 				break;
209 			case "gst_init":
210 			case "gst_init_check":
211 				params[1].type.cType = "char***";
212 				params[1].type.elementType.cType = "char**";
213 				break;
214 			case "g_object_new_with_properties":
215 				params[2].type.cType = "char**";
216 				params[2].type.elementType.cType = "char*";
217 				break;
218 			case "g_key_file_set_locale_string_list":
219 				params[3].type.cType = "char**";
220 				params[3].type.elementType.cType = "char*";
221 				break;
222 			default: break;
223 		}
224 	}
225 
226 	bool isVariadic()
227 	{
228 		if ( params.empty )
229 			return false;
230 		else if ( params[$-1].name == "..." )
231 			return true;
232 
233 		return false;
234 	}
235 
236 	/**
237 	 * Is this function a static function.
238 	 */
239 	bool isStatic()
240 	{
241 		if ( strct.noNamespace )
242 			return false;
243 
244 		if ( type == GirFunctionType.Function && !(!params.empty && isInstanceParam(params[0])) )
245 			return true;
246 
247 		if ( type == GirFunctionType.Method && strct.isNamespace() )
248 			return true;
249 
250 		return false;
251 	}
252 
253 	string[] getCallbackDeclaration()
254 	{
255 		string[] buff;
256 
257 		writeDocs(buff);
258 		buff ~= "public alias extern(C) "~ getExternalFunctionType() ~" "~ tokenToGtkD(cType, wrapper.aliasses, localAliases()) ~";";
259 
260 		return buff;
261 	}
262 
263 	string[] getFunctionPointerDeclaration()
264 	{
265 		string[] buff;
266 
267 		writeDocs(buff);
268 		buff ~= "extern(C) "~ getExternalFunctionType() ~" "~ tokenToGtkD(name, wrapper.aliasses, localAliases()) ~";";
269 
270 		return buff;
271 	}
272 
273 	string getLinkerExternal()
274 	{
275 		assert(type != GirFunctionType.Callback);
276 		assert(type != GirFunctionType.Signal);
277 
278 		if (strct.pack.name == "glgdk")
279 			return getExternalFunctionType() ~" glc_"~ cType ~";";
280 		else
281 			return getExternalFunctionType() ~" c_"~ cType ~";";
282 	}
283 
284 	string getExternal()
285 	{
286 		assert(type != GirFunctionType.Callback);
287 		assert(type != GirFunctionType.Signal);
288 
289 		string ext;
290 		string type = stringToGtkD(returnType.cType, wrapper.aliasses, localAliases());
291 
292 		if ( type.startsWith("bool") )
293 			ext ~= type.replaceFirst("bool", "int");
294 		else
295 			ext ~= type;
296 
297 		ext ~= " "~ cType ~"("~ getExternalParameters() ~");";
298 
299 		return ext;
300 	}
301 
302 	private string getExternalFunctionType()
303 	{
304 		string ext;
305 		string type = stringToGtkD(returnType.cType, wrapper.aliasses, localAliases());
306 
307 		if ( type.startsWith("bool") )
308 			ext ~= type.replaceFirst("bool", "int");
309 		else
310 			ext ~= type;
311 
312 		ext ~= " function("~ getExternalParameters() ~")";
313 
314 		return ext;
315 	}
316 
317 	private string getExternalParameters()
318 	{
319 		string ext, type;
320 
321 		if ( instanceParam )
322 		{
323 			ext ~= stringToGtkD(instanceParam.type.cType, wrapper.aliasses, localAliases(), false);
324 			ext ~= " ";
325 			ext ~= tokenToGtkD(instanceParam.name, wrapper.aliasses, localAliases());
326 		}
327 
328 		foreach ( i, param; params )
329 		{
330 			if ( i > 0 || instanceParam )
331 				ext ~= ", ";
332 
333 			type = stringToGtkD(param.type.cType, wrapper.aliasses, localAliases(), false);
334 
335 			if ( type.startsWith("bool") )
336 				ext ~= type.replaceFirst("bool", "int");
337 			else
338 				ext ~= type;
339 
340 			// Treat C fixed-size array parameters like pointers
341 			if ( param.type.isArray() && param.type.size > 0 && !type.endsWith("*") )
342 				ext ~= "*";
343 
344 			ext ~= " ";
345 			//Both name and type are ... for Variadic functions.
346 			if ( param.name != "..." )
347 				ext ~= tokenToGtkD(param.name, wrapper.aliasses, localAliases());
348 		}
349 
350 		if ( throws )
351 			ext ~= ", GError** err";
352 
353 		return ext;
354 	}
355 
356 	string[] getDeclaration()
357 	{
358 		string[] buff;
359 		string dec = "public ";
360 
361 		resolveLength();
362 		writeDocs(buff);
363 
364 		if ( type == GirFunctionType.Constructor )
365 		{
366 			dec ~= "this(";
367 		}
368 		else
369 		{
370 			if ( isStatic() )
371 				dec ~= "static ";
372 
373 			if ( lookupOverride || checkOverride() )
374 				dec ~= "override ";
375 
376 			dec ~= getType(returnType) ~" ";
377 			dec ~= tokenToGtkD(name, wrapper.aliasses, localAliases()) ~"(";
378 		}
379 
380 		size_t paramCount;
381 
382 		if ( instanceParam && ((type == GirFunctionType.Method && (strct.isNamespace() || strct.noNamespace )) || type == GirFunctionType.Constructor) )
383 		{
384 			dec ~= getType(instanceParam.type) ~" "~ tokenToGtkD(instanceParam.name, wrapper.aliasses, localAliases());
385 			paramCount++;
386 		}
387 
388 		foreach( param; params )
389 		{
390 			if ( param.lengthFor )
391 				continue;
392 
393 			if ( returnType.length > -1 && param == params[returnType.length] && params[returnType.length].direction != GirParamDirection.Default )
394 				continue;
395 
396 			if ( paramCount == 0 && strct.type == GirStructType.Record && isInstanceParam(param) )
397 				continue;
398 
399 			if ( paramCount++ > 0 )
400 				dec ~= ", ";
401 
402 			if ( param.direction == GirParamDirection.Out )
403 				dec ~= "out ";
404 			else if ( param.direction == GirParamDirection.InOut )
405 				dec ~= "ref ";
406 
407 			dec ~= getType(param.type, param.direction) ~" ";
408 			dec ~= tokenToGtkD(param.name, wrapper.aliasses, localAliases());
409 		}
410 
411 		dec ~= ")";
412 		buff ~= dec;
413 
414 		return buff;
415 	}
416 
417 	string[] getBody()
418 	{
419 		string[] buff;
420 		string[] outToD;
421 		string gtkCall = cType ~"(";
422 
423 		GirStruct returnDType;
424 
425 		if ( returnType.isArray() )
426 		{
427 			returnDType = strct.pack.getStruct(returnType.elementType.name);
428 
429 			if ( returnDType && returnType.elementType.cType.empty )
430 				returnType.elementType.cType = returnDType.cType ~"*";
431 		}
432 		else
433 		{
434 			returnDType = strct.pack.getStruct(returnType.name);
435 
436 			if ( returnDType && returnType.cType.empty )
437 				returnType.cType = returnDType.cType ~"*";
438 		}
439 
440 		if ( instanceParam || ( !params.empty && isInstanceParam(params[0])) )
441 		{
442 			GirStruct dType;
443 
444 			if ( instanceParam )
445 			{
446 				dType = strct.pack.getStruct(instanceParam.type.name);
447 
448 				if ( dType.cType != instanceParam.type.cType.removePtr() && !instanceParam.type.cType.among("gpointer", "gconstpointer") )
449 					gtkCall ~= "cast("~ stringToGtkD(instanceParam.type.cType, wrapper.aliasses, localAliases()) ~")";
450 			}
451 			else
452 			{
453 				dType = strct.pack.getStruct(params[0].type.name);
454 
455 				if ( dType.cType != params[0].type.cType.removePtr() && !params[0].type.cType.among("gpointer", "gconstpointer") )
456 					gtkCall ~= "cast("~ stringToGtkD(params[0].type.cType, wrapper.aliasses, localAliases()) ~")";
457 			}
458 
459 			if ( instanceParam && instanceParam.type.name in strct.structWrap )
460 			{
461 				GirStruct insType = strct.pack.getStruct(strct.structWrap[instanceParam.type.name]);
462 
463 				if ( insType )
464 					dType = insType;
465 			}
466 
467 			if ( type == GirFunctionType.Constructor || strct.isNamespace() || strct.noNamespace )
468 			{
469 				string id = tokenToGtkD(instanceParam.name, wrapper.aliasses, localAliases());
470 
471 				if ( dType && !(dType.isNamespace() || dType.noNamespace) )
472 					gtkCall ~= "("~ id ~" is null) ? null : "~ id ~"."~ dType.getHandleFunc() ~"()";
473 				else
474 					gtkCall ~= id;
475 			}
476 			else if ( dType.type == GirStructType.Interface || dType.lookupInterface )
477 			{
478 				gtkCall ~= strct.getHandleFunc() ~"()";
479 			}
480 			else
481 			{
482 				gtkCall ~= strct.getHandleVar();
483 			}
484 		}
485 
486 		foreach( i, param; params )
487 		{
488 			GirStruct dType;
489 			string id = tokenToGtkD(param.name, wrapper.aliasses, localAliases());
490 
491 			if ( param.type.isArray() )
492 				dType = strct.pack.getStruct(param.type.elementType.name);
493 			else if ( auto dStrct = strct.pack.getStruct(strct.structWrap.get(param.type.name, "")) )
494 				dType = dStrct;
495 			else
496 				dType = strct.pack.getStruct(param.type.name);
497 
498 			if ( i == 0 && isInstanceParam(param) )
499 				continue;
500 
501 			if ( instanceParam || i > 0 )
502 				gtkCall ~= ", ";
503 
504 			if ( param.type.isString() )
505 			{
506 				if ( isStringArray(param.type, param.direction) )
507 				{
508 					// out string[], ref string[]
509 					if ( param.direction != GirParamDirection.Default )
510 					{
511 						buff ~= "char** out"~ id ~" = ";
512 
513 						if ( param.direction == GirParamDirection.Out )
514 							buff[$-1] ~= "null;";
515 						else
516 							buff[$-1] ~= "Str.toStringzArray("~ id ~");";
517 
518 						string len = lenId(param.type);
519 						if ( !len.empty )
520 							len = ", "~ len;
521 
522 						gtkCall ~= "&out"~ id;
523 						outToD ~= id ~" = Str.toStringArray(out"~ id ~ len ~");";
524 					}
525 					// string[]
526 					else
527 					{
528 						gtkCall ~= "Str.toStringzArray("~ id ~")";
529 					}
530 				}
531 				else
532 				{
533 					if ( param.direction != GirParamDirection.Default )
534 					{
535 						string len = lenId(param.type);
536 
537 						// A buffer to fill.
538 						if ( !param.type.cType.endsWith("**") )
539 						{
540 							gtkCall ~= id ~".ptr";
541 
542 							if ( !len.empty && params[param.type.length].direction != GirParamDirection.Default )
543 								outToD ~= id ~" = "~ id ~"[0.."~ len ~"];";
544 						}
545 						// out string, ref string
546 						else
547 						{
548 							buff ~= "char* out"~ id ~" = ";
549 
550 							if ( param.direction == GirParamDirection.Out )
551 								buff[$-1] ~= "null;";
552 							else
553 								buff[$-1] ~= "Str.toStringz("~ id ~");";
554 
555 							if ( !len.empty )
556 								len = ", "~ len;
557 
558 							gtkCall ~= "&out"~ id;
559 							outToD ~= id ~" = Str.toString(out"~ id ~ len ~");";
560 						}
561 					}
562 					// string
563 					else
564 					{
565 						gtkCall ~= "Str.toStringz("~ id ~")";
566 					}
567 				}
568 			}
569 			else if ( dType && dType.isDClass() )
570 			{
571 				if ( param.type.isArray() )
572 				{
573 					GirType elementType = param.type.elementType;
574 					GirStruct dElementType = strct.pack.getStruct(elementType.name);
575 
576 					if ( elementType.cType.empty )
577 						elementType.cType = stringToGtkD(param.type.cType, wrapper.aliasses, localAliases())[0 .. $-1];
578 
579 					// out gtkdType[], ref gtkdType[]
580 					if ( param.direction != GirParamDirection.Default )
581 					{
582 						if ( param.direction == GirParamDirection.Out )
583 						{
584 							if ( param.type.size > 0 )
585 								buff ~= elementType.cType ~"* out"~ id ~" = cast("~ elementType.cType ~"*)sliceAlloc0("~ elementType.cType ~".sizeof * "~ to!string(param.type.size) ~");";
586 							else
587 								buff ~= elementType.cType ~" out"~ id ~" = null;";
588 						}
589 						else
590 						{
591 							if ( !buff.empty )
592 								buff ~= "";
593 							buff ~= elementType.cType.removePtr() ~ "**[] inout"~ id ~" = new "~ elementType.cType.removePtr() ~"*["~ id ~".length];";
594 							buff ~= "for ( int i = 0; i < "~ id ~".length; i++ )";
595 							buff ~= "{";
596 							buff ~= "inout"~ id ~"[i] = "~ id~ "[i]."~ dElementType.getHandleFunc() ~"();";
597 							buff ~= "}";
598 							buff ~= "";
599 							buff ~= elementType.cType.removePtr() ~ "** out"~ id ~" = inout"~ id ~".ptr;";
600 						}
601 
602 						if ( !elementType.cType.endsWith("*") )
603 							gtkCall ~= "out"~ id;
604 						else
605 							gtkCall ~= "&out"~ id;
606 
607 						if ( !outToD.empty )
608 							outToD ~= "";
609 						if ( param.type.size <= 0 )
610 							outToD ~= id ~" = new "~ dElementType.name ~"["~ lenId(param.type, "out"~ id) ~"];";
611 						outToD ~= "for(size_t i = 0; i < "~ lenId(param.type, "out"~ id) ~"; i++)";
612 						outToD ~= "{";
613 						if ( elementType.cType.endsWith("**") )
614 							outToD ~= id ~"[i] = " ~ construct(elementType.name) ~ "(cast(" ~ elementType.cType[0..$-1] ~ ") out"~ id ~"[i]);";
615 						else if ( !elementType.cType.endsWith("*") )
616 							outToD ~= id ~"[i] = " ~ construct(elementType.name) ~ "(cast(" ~ elementType.cType ~ "*) &out"~ id ~"[i]);";
617 						else
618 							outToD ~= id ~"[i] = " ~ construct(elementType.name) ~ "(cast(" ~ elementType.cType ~ ") &out"~ id ~"[i]);";
619 						outToD ~= "}";
620 					}
621 					// gtkdType[]
622 					else
623 					{
624 						//TODO: zero-terminated see: g_signal_chain_from_overridden
625 						if ( !buff.empty )
626 							buff ~= "";
627 						buff ~= elementType.cType ~ "[] "~ id ~"Array = new "~ elementType.cType ~"["~ id ~".length];";
628 						buff ~= "for ( int i = 0; i < "~ id ~".length; i++ )";
629 						buff ~= "{";
630 						if ( elementType.cType.endsWith("*") )
631 							buff ~= id ~"Array[i] = "~ id ~"[i]."~ dElementType.getHandleFunc() ~"();";
632 						else
633 							buff ~= id ~"Array[i] = *("~ id ~"[i]."~ dElementType.getHandleFunc() ~"());";
634 						buff ~= "}";
635 						buff ~= "";
636 
637 						gtkCall ~= id ~"Array.ptr";
638 					}
639 				}
640 				else
641 				{
642 					// out gtkdType, ref gtkdType
643 					if ( param.direction != GirParamDirection.Default && param.type.cType.endsWith("**") )
644 					{
645 						buff ~= param.type.cType.removePtr() ~"* out"~ id ~" = ";
646 
647 						if ( param.direction == GirParamDirection.Out )
648 							buff[$-1] ~= "null;";
649 						else
650 							buff[$-1] ~= id ~"."~ dType.getHandleFunc() ~"();";
651 
652 						gtkCall ~= "&out"~ id;
653 
654 						outToD ~= id ~" = "~ construct(param.type.name) ~"(out"~ id ~");";
655 					}
656 					else if ( param.direction == GirParamDirection.Out )
657 					{
658 						buff ~= param.type.cType.removePtr() ~"* out"~ id ~" = sliceNew!"~ param.type.cType.removePtr() ~"();";
659 
660 						gtkCall ~= "out"~ id;
661 
662 						outToD ~= id ~" = "~ construct(param.type.name) ~"(out"~ id ~", true);";
663 					}
664 					// gtkdType
665 					else
666 					{
667 						gtkCall ~= "("~ id ~" is null) ? null : ";
668 						if ( dType.cType != param.type.cType.removePtr() && !param.type.cType.among("gpointer", "gconstpointer") )
669 							gtkCall ~= "cast("~ stringToGtkD(param.type.cType, wrapper.aliasses, localAliases()) ~")";
670 
671 						if ( param.ownership == GirTransferOwnership.Full && dType.shouldFree() )
672 							gtkCall ~= id ~"."~ dType.getHandleFunc ~"(true)";
673 						else
674 							gtkCall ~= id ~"."~ dType.getHandleFunc ~"()";
675 					}
676 				}
677 			}
678 			else if ( param.lengthFor || (returnType.length == i && param.direction != GirParamDirection.Default ) )
679 			{
680 				string arrId;
681 				string lenType = tokenToGtkD(param.type.cType.removePtr(), wrapper.aliasses, localAliases());
682 
683 				if ( param.lengthFor )
684 					arrId = tokenToGtkD(param.lengthFor.name, wrapper.aliasses, localAliases());
685 
686 				final switch ( param.direction ) with (GirParamDirection)
687 				{
688 					case Default:
689 						gtkCall ~= "cast("~ lenType ~")"~ arrId ~".length";
690 						break;
691 					case Out:
692 						buff ~= lenType ~" "~ id ~";";
693 						gtkCall ~= "&"~id;
694 						break;
695 					case InOut:
696 						buff ~= lenType ~" "~ id ~" = cast("~ lenType ~")"~ arrId ~".length;";
697 						gtkCall ~= "&"~id;
698 						break;
699 				}
700 			}
701 			else if ( param.type.name.among("bool", "gboolean") || ( param.type.isArray && param.type.elementType.name.among("bool", "gboolean") ) )
702 			{
703 				if ( param.type.isArray() )
704 				{
705 					// out bool[], ref bool[]
706 					if ( param.direction != GirParamDirection.Default )
707 					{
708 						if ( param.direction == GirParamDirection.Out )
709 						{
710 							buff ~= "int* out"~ id ~" = null;";
711 						}
712 						else
713 						{
714 							if ( !buff.empty )
715 								buff ~= "";
716 							buff ~= "int[] inout"~ id ~" = new int["~ id ~".length];";
717 							buff ~= "for ( int i = 0; i < "~ id ~".length; i++ )";
718 							buff ~= "{";
719 							buff ~= "inout"~ id ~"[i] = "~ id~ "[i] ? 1 : 0;";
720 							buff ~= "}";
721 							buff ~= "";
722 							buff ~= "int* out"~ id ~" = inout"~ id ~".ptr;";
723 						}
724 
725 						gtkCall ~= "&out"~ id;
726 
727 						if ( !outToD.empty )
728 							outToD ~= "";
729 						outToD ~= id ~" = new bool["~ lenId(param.type, "out"~ id) ~"];";
730 						outToD ~= "for(size_t i = 0; i < "~ lenId(param.type, "out"~ id) ~"; i++)";
731 						outToD ~= "{";
732 						outToD ~= id ~"[i] = out"~ id ~"[i] != 0);";
733 						outToD ~= "}";
734 					}
735 					// bool[]
736 					else
737 					{
738 						if ( !buff.empty )
739 							buff ~= "";
740 						buff ~= "int[] "~ id ~"Array = new int["~ id ~".length];";
741 						buff ~= "for ( int i = 0; i < "~ id ~".length; i++ )";
742 						buff ~= "{";
743 						buff ~= id ~"Array[i] = "~ id ~"[i] ? 1 : 0;";
744 						buff ~= "}";
745 						buff ~= "";
746 
747 						gtkCall ~= id ~"Array.ptr";
748 					}
749 				}
750 				else
751 				{
752 					// out bool, ref bool
753 					if ( param.direction != GirParamDirection.Default )
754 					{
755 						buff ~= "int out"~ id;
756 
757 						if ( param.direction == GirParamDirection.Out )
758 							buff[$-1] ~= ";";
759 						else
760 							buff[$-1] ~= " = ("~ id ~" ? 1 : 0);";
761 
762 						gtkCall ~= "&out"~ id ~"";
763 						outToD ~= id ~" = (out"~ id ~" == 1);";
764 					}
765 					// bool
766 					else
767 					{
768 						gtkCall ~= id;
769 					}
770 				}
771 			}
772 			else
773 			{
774 				if ( param.type.isArray() )
775 				{
776 					// out T[], ref T[]
777 					if ( param.direction != GirParamDirection.Default )
778 					{
779 						string outType = param.type.elementType.cType;
780 						string outName = "out" ~ id;
781 
782 						if ( outType.empty )
783 							outType = param.type.elementType.name ~"*";
784 						else if ( param.type.isArray() && param.type.size > 0)
785 							outType = param.type.elementType.name ~ "[" ~ to!string(param.type.size) ~ "]";
786 
787 						buff ~= stringToGtkD(outType, wrapper.aliasses, localAliases) ~ " " ~ outName ~" = ";
788 
789 						if ( param.direction == GirParamDirection.Out )
790 							buff[$-1] ~= "null;";
791 						else
792 							buff[$-1] ~= id ~".ptr";
793 
794 						if ( param.type.elementType.cType.empty )
795 							gtkCall ~= "cast("~stringToGtkD(param.type.cType, wrapper.aliasses, localAliases) ~")&" ~ outName;
796 						else if ( param.type.isArray() && param.type.size > 0)
797 							gtkCall ~= outName ~ ".ptr";
798 						else
799 							gtkCall ~= "&" ~ outName;
800 
801 						outToD ~= id ~" = "~ outName ~"[0 .. "~ lenId(param.type, outName) ~"];";
802 					}
803 					// T[]
804 					else
805 					{
806 						gtkCall ~= id ~".ptr";
807 					}
808 				}
809 				else
810 				{
811 					if ( param.type.name in strct.structWrap )
812 					{
813 						gtkCall ~= "("~ id ~" is null) ? null : "~ id ~".get"~ strct.structWrap[param.type.name] ~"Struct()";
814 					}
815 					else
816 					{
817 						// out T, ref T
818 						if ( param.direction != GirParamDirection.Default )
819 						{
820 							gtkCall ~= "&"~ id;
821 						}
822 						// T
823 						else
824 						{
825 							gtkCall ~= id;
826 						}
827 					}
828 				}
829 			}
830 		}
831 
832 		if ( throws )
833 		{
834 			buff ~= "GError* err = null;";
835 			gtkCall ~= ", &err";
836 		}
837 
838 		enum throwGException = [
839 			"",
840 			"if (err !is null)",
841 			"{",
842 			"throw new GException( new ErrorG(err) );",
843 			"}"];
844 
845 		gtkCall ~= ")";
846 
847 		if ( !buff.empty && buff[$-1] != "" )
848 			buff ~= "";
849 
850 		if ( returnType.name == "none" )
851 		{
852 			buff ~= gtkCall ~";";
853 
854 			if ( throws )
855 			{
856 				buff ~= throwGException;
857 			}
858 
859 			if ( !outToD.empty )
860 			{
861 				buff ~= "";
862 				buff ~= outToD;
863 			}
864 
865 			if ( name == "free" && strct && strct.shouldFree() )
866 			{
867 				buff ~= "ownedRef = false;";
868 			}
869 
870 			return buff;
871 		}
872 		else if ( type == GirFunctionType.Constructor )
873 		{
874 			buff ~= "auto __p = " ~ gtkCall ~";";
875 
876 			if ( throws )
877 			{
878 				buff ~= throwGException;
879 			}
880 
881 			buff ~= "";
882 			buff ~= "if(__p is null)";
883 			buff ~= "{";
884 			buff ~= "throw new ConstructionException(\"null returned by " ~ name ~ "\");";
885 			buff ~= "}";
886 			buff ~= "";
887 
888 			if ( !outToD.empty )
889 			{
890 				buff ~= outToD;
891 				buff ~= "";
892 			}
893 
894 			/*
895 			 * Casting is needed because some GTK+ functions
896 			 * can return void pointers or base types.
897 			 */
898 			if ( returnOwnership == GirTransferOwnership.Full && strct.getAncestor().name == "ObjectG" )
899 				buff ~= "this(cast(" ~ strct.cType ~ "*) __p, true);";
900 			else
901 				buff ~= "this(cast(" ~ strct.cType ~ "*) __p);";
902 
903 			return buff;
904 		}
905 		else if ( returnType.isString() )
906 		{
907 			if ( outToD.empty && !throws && !(returnOwnership == GirTransferOwnership.Full) )
908 			{
909 				if ( isStringArray(returnType) )
910 					buff ~= "return Str.toStringArray(" ~ gtkCall ~");";
911 				else
912 					buff ~= "return Str.toString(" ~ gtkCall ~");";
913 
914 				return buff;
915 			}
916 
917 			buff ~= "auto retStr = "~ gtkCall ~";";
918 
919 			if ( throws )
920 			{
921 				buff ~= throwGException;
922 			}
923 
924 			buff ~= "";
925 
926 			if ( !outToD.empty )
927 			{
928 				buff ~= outToD;
929 				buff ~= "";
930 			}
931 
932 			if ( returnOwnership == GirTransferOwnership.Full )
933 			{
934 				if ( isStringArray(returnType) )
935 					buff ~= "scope(exit) Str.freeStringArray(retStr);";
936 				else
937 					buff ~= "scope(exit) Str.freeString(retStr);";
938 			}
939 
940 			string len = lenId(returnType);
941 			if ( !len.empty )
942 				len = ", "~ len;
943 
944 			if ( isStringArray(returnType) )
945 				buff ~= "return Str.toStringArray(retStr"~ len ~");";
946 			else
947 				buff ~= "return Str.toString(retStr"~ len ~");";
948 
949 			return buff;
950 		}
951 		else if ( returnDType && returnDType.isDClass() )
952 		{
953 			buff ~= "auto __p = "~ gtkCall ~";";
954 
955 			if ( throws )
956 			{
957 				buff ~= throwGException;
958 			}
959 
960 			if ( !outToD.empty )
961 			{
962 				buff ~= "";
963 				buff ~= outToD;
964 			}
965 
966 			buff ~= "";
967 			buff ~= "if(__p is null)";
968 			buff ~= "{";
969 			buff ~= "return null;";
970 			buff ~= "}";
971 			buff ~= "";
972 
973 			if ( returnType.isArray() )
974 			{
975 				buff ~= returnDType.name ~"[] arr = new "~ returnDType.name ~"["~ lenId(returnType) ~"];";
976 				buff ~= "for(int i = 0; i < "~ lenId(returnType) ~"; i++)";
977 				buff ~= "{";
978 				if ( returnType.elementType.cType.endsWith("*") )
979 					buff ~= "\tarr[i] = "~ construct(returnType.elementType.name) ~"(cast("~ returnType.elementType.cType ~") __p[i]);";
980 				else
981 					buff ~= "\tarr[i] = "~ construct(returnType.elementType.name) ~"(cast("~ returnType.elementType.cType ~"*) &__p[i]);";
982 				buff ~= "}";
983 				buff ~= "";
984 				buff ~= "return arr;";
985 			}
986 			else
987 			{
988 				if ( returnOwnership == GirTransferOwnership.Full && !(returnDType.pack.name == "cairo") )
989 					buff ~= "return "~ construct(returnType.name) ~"(cast("~ returnDType.cType ~"*) __p, true);";
990 				else
991 					buff ~= "return "~ construct(returnType.name) ~"(cast("~ returnDType.cType ~"*) __p);";
992 			}
993 
994 			return buff;
995 		}
996 		else
997 		{
998 			if ( returnType.name == "gboolean" )
999 				gtkCall ~= " != 0";
1000 
1001 			if ( !returnType.isArray && outToD.empty && !throws )
1002 			{
1003 				buff ~= "return "~ gtkCall ~";";
1004 				return buff;
1005 			}
1006 
1007 			buff ~= "auto __p = "~ gtkCall ~";";
1008 
1009 			if ( throws )
1010 			{
1011 				buff ~= throwGException;
1012 			}
1013 
1014 			if ( !outToD.empty )
1015 			{
1016 				buff ~= "";
1017 				buff ~= outToD;
1018 			}
1019 
1020 			buff ~= "";
1021 			if ( returnType.isArray() )
1022 			{
1023 				if ( returnType.elementType.name == "gboolean" )
1024 				{
1025 					buff ~= "bool[] r = new bool["~ lenId(returnType) ~"];";
1026 					buff ~= "for(size_t i = 0; i < "~ lenId(returnType) ~"; i++)";
1027 					buff ~= "{";
1028 					buff ~= "r[i] = __p[i] != 0;";
1029 					buff ~= "}";
1030 					buff ~= "return r;";
1031 				}
1032 				else if ( returnType.elementType.cType.empty && returnType.cType[0..$-1] != returnType.elementType.name )
1033 				{
1034 					buff ~= "return cast("~ getType(returnType) ~")__p[0 .. "~ lenId(returnType) ~"];";
1035 				}
1036 				else
1037 				{
1038 					buff ~= "return __p[0 .. "~ lenId(returnType) ~"];";
1039 				}
1040 			}
1041 			else
1042 				buff ~= "return __p;";
1043 
1044 			return buff;
1045 		}
1046 
1047 		assert(false, "Unexpected function: "~ name);
1048 	}
1049 
1050 	string getSignalName()
1051 	{
1052 		assert(type == GirFunctionType.Signal);
1053 
1054 		char pc;
1055 		string signalName;
1056 
1057 		foreach ( size_t count, char c; name )
1058 		{
1059 			if ( count == 0 && c != '-')
1060 			{
1061 				signalName ~= toUpper(c);
1062 			}
1063 			else
1064 			{
1065 				if ( c!='-' && c!='_' )
1066 				{
1067 					if ( pc=='-' || pc=='_' )
1068 						signalName ~= toUpper(c);
1069 					else
1070 						signalName ~= c;
1071 				}
1072 			}
1073 			pc = c;
1074 		}
1075 
1076 		if ( !signalName.among("Event", "MapEvent", "UnmapEvent", "DestroyEvent") &&
1077 		    endsWith(signalName, "Event") )
1078 		{
1079 			signalName = signalName[0..signalName.length-5];
1080 		}
1081 
1082 		return signalName;
1083 	}
1084 
1085 	string getDelegateDeclaration()
1086 	{
1087 		assert(type == GirFunctionType.Signal);
1088 
1089 		string buff = getType(returnType) ~ " delegate(";
1090 
1091 		foreach ( param; params )
1092 		{
1093 			//TODO: Signals with arrays.
1094 			if ( param.type.cType == "gpointer" && param.type.isArray() )
1095 				buff ~= "void*, ";
1096 			else
1097 				buff ~= getType(param.type) ~ ", ";
1098 		}
1099 
1100 		if ( strct.type == GirStructType.Interface )
1101 			buff ~= strct.name ~"IF)";
1102 		else
1103 			buff ~= strct.name ~")";
1104 
1105 		return buff;
1106 	}
1107 
1108 	string[] getAddListenerDeclaration()
1109 	{
1110 		string[] buff;
1111 
1112 		writeDocs(buff);
1113 		buff ~= "gulong addOn"~ getSignalName() ~"("~ getDelegateDeclaration() ~" dlg, ConnectFlags connectFlags=cast(ConnectFlags)0)";
1114 
1115 		return buff;
1116 	}
1117 
1118 	string[] getAddListenerBody()
1119 	{
1120 		string[] buff;
1121 
1122 		buff ~= "{";
1123 
1124 		if ( strct.hasFunction("add_events") )
1125 		{
1126 			switch ( name )
1127 			{
1128 				case  "button-press-event":      buff ~= "addEvents(EventMask.BUTTON_PRESS_MASK);";      break;
1129 				case  "button-release-event":    buff ~= "addEvents(EventMask.BUTTON_RELEASE_MASK);";    break;
1130 				case  "enter-notify-event":      buff ~= "addEvents(EventMask.ENTER_NOTIFY_MASK);";      break;
1131 				case  "focus-in-event":          buff ~= "addEvents(EventMask.FOCUS_CHANGE_MASK);";      break;
1132 				case  "focus-out-event":         buff ~= "addEvents(EventMask.FOCUS_CHANGE_MASK);";      break;
1133 				case  "key-press-event":         buff ~= "addEvents(EventMask.KEY_PRESS_MASK);";         break;
1134 				case  "key-release-event":       buff ~= "addEvents(EventMask.KEY_RELEASE_MASK);";       break;
1135 				case  "leave-notify-event":      buff ~= "addEvents(EventMask.LEAVE_NOTIFY_MASK);";      break;
1136 				case  "motion-notify-event":     buff ~= "addEvents(EventMask.POINTER_MOTION_MASK);";    break;
1137 				case  "property-notify-event":   buff ~= "addEvents(EventMask.PROPERTY_CHANGE_MASK);";   break;
1138 				case  "proximity-in-event":      buff ~= "addEvents(EventMask.PROXIMITY_IN_MASK);";      break;
1139 				case  "proximity-out-event":     buff ~= "addEvents(EventMask.PROXIMITY_OUT_MASK);";     break;
1140 				case  "scroll-event":            buff ~= "addEvents(EventMask.SCROLL_MASK);";            break;
1141 				case  "visibility-notify-event": buff ~= "addEvents(EventMask.VISIBILITY_NOTIFY_MASK);"; break;
1142 
1143 				default: break;
1144 			}
1145 		}
1146 
1147 		buff ~= "return Signals.connect(this, \""~ name ~"\", dlg, connectFlags ^ ConnectFlags.SWAPPED);";
1148 		buff ~= "}";
1149 
1150 		return buff;
1151 	}
1152 
1153 	
1154 	void writeDocs(ref string[] buff)
1155 	{
1156 		if ( (doc || returnType.doc) && wrapper.includeComments )
1157 		{
1158 			buff ~= "/**";
1159 			foreach ( line; doc.splitLines() )
1160 				buff ~= " * "~ line.strip();
1161 
1162 			if ( !params.empty || (instanceParam && type == GirFunctionType.Constructor) )
1163 			{
1164 				buff ~= " *";
1165 				buff ~= " * Params:";
1166 
1167 				if ( type == GirFunctionType.Constructor && instanceParam && !instanceParam.doc.empty )
1168 				{
1169 					string[] lines = instanceParam.doc.splitLines();
1170 					buff ~= " *     "~ tokenToGtkD(instanceParam.name, wrapper.aliasses, localAliases()) ~" = "~ lines[0];
1171 					foreach( line; lines[1..$] )
1172 						buff ~= " *         "~ line.strip();
1173 				}
1174 
1175 				foreach ( param; params )
1176 				{
1177 					if ( param.doc.empty )
1178 						continue;
1179 
1180 					if ( param.lengthFor )
1181 						continue;
1182 
1183 					if ( returnType.length > -1 && param == params[returnType.length] && params[returnType.length].direction != GirParamDirection.Default )
1184 						continue;
1185 
1186 					if ( isInstanceParam(param) )
1187 						continue;
1188 
1189 					string[] lines = param.doc.splitLines();
1190 					buff ~= " *     "~ tokenToGtkD(param.name, wrapper.aliasses, localAliases()) ~" = "~ lines[0];
1191 					foreach( line; lines[1..$] )
1192 						buff ~= " *         "~ line.strip();
1193 				}
1194 
1195 				if ( buff.endsWith(" * Params:") )
1196 					buff = buff[0 .. $-2];
1197 			}
1198 
1199 			if ( returnType.doc )
1200 			{
1201 				string[] lines = returnType.doc.splitLines();
1202 				if ( doc )
1203 					buff ~= " *";
1204 				buff ~= " * Returns: "~ lines[0];
1205 
1206 				foreach( line; lines[1..$] )
1207 					buff ~= " *     "~ line.strip();
1208 			}
1209 
1210 			if ( libVersion )
1211 			{
1212 				buff ~= " *";
1213 				buff ~= " * Since: "~ libVersion;
1214 			}
1215 
1216 			if ( throws || type == GirFunctionType.Constructor )
1217 				buff ~= " *";
1218 
1219 			if ( throws )
1220 				buff ~= " * Throws: GException on failure.";
1221 
1222 			if ( type == GirFunctionType.Constructor )
1223 				buff ~= " * Throws: ConstructionException GTK+ fails to create the object.";
1224 
1225 			buff ~= " */";
1226 		}
1227 		else if ( wrapper.includeComments )
1228 		{
1229 			buff ~= "/** */\n";
1230 		}
1231 	}
1232 
1233 	private void resolveLength()
1234 	{
1235 		foreach( param; params )
1236 		{
1237 			if ( param.type.length > -1 )
1238 				params[param.type.length].lengthFor = param;
1239 		}
1240 	}
1241 
1242 	private string[string] localAliases()
1243 	{
1244 		if ( strct )
1245 			return strct.aliases;
1246 
1247 		return null;
1248 	}
1249 
1250 	/**
1251 	 * Get an string representation of the type.
1252 	 */
1253 	private string getType(GirType type, GirParamDirection direction = GirParamDirection.Default)
1254 	{
1255 		if ( type.isString() )
1256 		{
1257 			if ( direction != GirParamDirection.Default && !type.cType.endsWith("**") )
1258 				return "char[]";
1259 			else if ( direction == GirParamDirection.Default && type.cType.endsWith("***") )
1260 				return "string[][]";
1261 			else if ( type.isArray && isStringArray(type.elementType, direction) )
1262 				return getType(type.elementType, direction) ~"[]";
1263 			else if ( isStringArray(type, direction) )
1264 				return "string[]";
1265 
1266 			return "string";
1267 		}
1268 		else if ( type.isArray() )
1269 		{
1270 			string size;
1271 
1272 			//Special case for GBytes and GVariant.
1273 			if ( type.cType == "gconstpointer" && type.elementType.cType == "gconstpointer" )
1274 				return "void[]";
1275 
1276 			if ( type.cType == "guchar*" )
1277 				return "char[]";
1278 
1279 			if ( type.size > -1 )
1280 				size = to!string(type.size);
1281 
1282 			string elmType = getType(type.elementType, direction);
1283 
1284 			if ( elmType == type.cType && elmType.endsWith("*") )
1285 				elmType = elmType[0..$-1];
1286 
1287 			return elmType ~"["~ size ~"]";
1288 		}
1289 		else
1290 		{
1291 			if ( type is null || type.name == "none" )
1292 				return "void";
1293 			else if ( type.name in strct.structWrap )
1294 				return strct.structWrap[type.name];
1295 			else if ( type.name == type.cType )
1296 				return stringToGtkD(type.name, wrapper.aliasses, localAliases());
1297 
1298 			GirStruct dType = strct.pack.getStruct(type.name);
1299 
1300 			if ( dType && dType.isDClass() )
1301 			{
1302 			    if ( dType.type == GirStructType.Interface )
1303 					return dType.name ~"IF";
1304 			    else
1305 			    	return dType.name;
1306 			}
1307 			else if ( type.cType.empty && dType && dType.type == GirStructType.Record )
1308 				return dType.cType ~ "*";
1309 		}
1310 
1311 		if ( type.cType.empty )
1312 		{
1313 			if ( auto enum_ = strct.pack.getEnum(type.name) )
1314 				return enum_.cName;
1315 
1316 			return stringToGtkD(type.name, wrapper.aliasses, localAliases());
1317 		}
1318 
1319 		if ( direction != GirParamDirection.Default && type.cType.endsWith("*") )
1320 			return stringToGtkD(type.cType[0..$-1], wrapper.aliasses, localAliases());
1321 
1322 		return stringToGtkD(type.cType, wrapper.aliasses, localAliases());
1323 	}
1324 
1325 	private bool isStringArray(GirType type, GirParamDirection direction = GirParamDirection.Default)
1326 	{
1327 		if ( direction == GirParamDirection.Default && type.cType.endsWith("**") )
1328 			return true;
1329 		if ( type.elementType is null )
1330 			return false;
1331 		if ( !type.elementType.cType.endsWith("*") )
1332 			return false;
1333 		if ( direction != GirParamDirection.Default && type.cType.among("char**", "gchar**", "guchar**") )
1334 			return false;
1335 
1336 		return true;
1337 	}
1338 
1339 	private bool isInstanceParam(GirParam param)
1340 	{
1341 		if ( param !is params[0] )
1342 			return false;
1343 		if ( strct is null || strct.type != GirStructType.Record )
1344 			return false;
1345 		if ( !(strct.lookupClass || strct.lookupInterface) )
1346 			return false;
1347 		if ( param.direction != GirParamDirection.Default )
1348 			return false;
1349 		if ( param.lengthFor !is null )
1350 			return false;
1351 		if ( strct.cType is null )
1352 			return false;
1353 		if ( param.type.cType == strct.cType ~"*" )
1354 			return true;
1355 
1356 		return false;
1357 	}
1358 
1359 	private string lenId(GirType type, string paramName = "__p")
1360 	{
1361 		if ( type.length > -1 && params[type.length].direction == GirParamDirection.Default && paramName != "__p" )
1362 			return "cast("~ tokenToGtkD(params[type.length].type.cType.removePtr(), wrapper.aliasses, localAliases()) ~")"~ paramName.replaceFirst("out", "") ~".length";
1363 		else if ( type.length > -1 )
1364 			return tokenToGtkD(params[type.length].name, wrapper.aliasses, localAliases());
1365 		//The c function returns the length.
1366 		else if ( type.length == -2 )
1367 			return "__p";
1368 		else if ( type.size > -1 )
1369 			return to!string(type.size);
1370 
1371 		if ( type.isString() )
1372 			return null;
1373 
1374 		return "getArrayLength("~ paramName ~")";
1375 	}
1376 
1377 	/**
1378 	 * Check if any of the ancestors contain the function functionName.
1379 	 */
1380 	private bool checkOverride()
1381 	{
1382 		if ( name == "get_type" )
1383 			return false;
1384 		if ( name == "to_string" && params.empty )
1385 			return true;
1386 
1387 		GirStruct ancestor = strct.getParent();
1388 
1389 		while(ancestor)
1390 		{
1391 			if ( name in ancestor.functions && name !in strct.aliases )
1392 			{
1393 				GirFunction func = ancestor.functions[name];
1394 
1395 				if ( !(func.noCode || func.isVariadic() || func.type == GirFunctionType.Callback) && paramsEqual(func) )
1396 					return true;
1397 			}
1398 
1399 			ancestor = ancestor.getParent();
1400 		}
1401 
1402 		return false;
1403 	}
1404 
1405 	/**
1406 	 * Return true if the params of func match the params of this function.
1407 	 */
1408 	private bool paramsEqual(GirFunction func)
1409 	{
1410 		if ( params.length != func.params.length )
1411 			return false;
1412 
1413 		foreach ( i, param; params )
1414 		{
1415 			if ( getType(param.type) != getType(func.params[i].type) )
1416 				return false;
1417 		}
1418 
1419 		return true;
1420 	}
1421 
1422 	private string construct(string type)
1423 	{
1424 		GirStruct dType = strct.pack.getStruct(type);
1425 		debug assert(dType, "Only call construct for valid GtkD types");
1426 		string name = dType.name;
1427 
1428 		if ( type in strct.structWrap )
1429 			name = strct.structWrap[type];
1430 
1431 		if ( dType.pack.name.among("cairo", "glib", "gthread") )
1432 			return "new "~name;
1433 		else if( dType.type == GirStructType.Interface )
1434 			return "ObjectG.getDObject!("~ name ~"IF)";
1435 		else
1436 			return "ObjectG.getDObject!("~ name ~")";
1437 	}
1438 }
1439 
1440 enum GirParamDirection : string
1441 {
1442 	Default = "",
1443 	Out = "out",
1444 	InOut = "inout",
1445 }
1446 
1447 final class GirParam
1448 {
1449 	string doc;
1450 	string name;
1451 	GirType type;
1452 	GirTransferOwnership ownership = GirTransferOwnership.None;
1453 	GirParamDirection direction = GirParamDirection.Default;
1454 
1455 	GirParam lengthFor;
1456 	GirWrapper wrapper;
1457 
1458 	this(GirWrapper wrapper)
1459 	{
1460 		this.wrapper = wrapper;
1461 	}
1462 
1463 	void parse(T)(XMLReader!T reader)
1464 	{
1465 		name = reader.front.attributes["name"];
1466 
1467 		if ( "transfer-ownership" in reader.front.attributes )
1468 			ownership = cast(GirTransferOwnership)reader.front.attributes["transfer-ownership"];
1469 		if ( "direction" in reader.front.attributes )
1470 			direction = cast(GirParamDirection)reader.front.attributes["direction"];
1471 
1472 		reader.popFront();
1473 
1474 		while( !reader.empty && !reader.endTag("parameter", "instance-parameter") )
1475 		{
1476 			if ( reader.front.type == XMLNodeType.EndTag )
1477 			{
1478 				reader.popFront();
1479 				continue;
1480 			}
1481 
1482 			switch(reader.front.value)
1483 			{
1484 				case "doc":
1485 					reader.popFront();
1486 					doc ~= reader.front.value;
1487 					reader.popFront();
1488 					break;
1489 				case "doc-deprecated":
1490 					reader.popFront();
1491 					doc ~= "\n\nDeprecated: "~ reader.front.value;
1492 					reader.popFront();
1493 					break;
1494 				case "array":
1495 				case "type":
1496 					type = new GirType(wrapper);
1497 					type.parse(reader);
1498 					break;
1499 				case "varargs":
1500 					type = new GirType(wrapper);
1501 					type.name = "...";
1502 					type.cType = "...";
1503 					break;
1504 				case "source-position":
1505 					reader.skipTag();
1506 					break;
1507 				default:
1508 					error("Unexpected tag: ", reader.front.value, " in GirParam: ", name, reader);
1509 			}
1510 
1511 			reader.popFront();
1512 		}
1513 
1514 		if ( direction != GirParamDirection.Default && !type.cType.endsWith("*") )
1515 			direction = GirParamDirection.Default;
1516 	}
1517 }
1518 
1519 private string removePtr(string cType)
1520 {
1521 	while ( !cType.empty && cType.back == '*' )
1522 		cType.popBack();
1523 
1524 	return cType;
1525 }