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