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