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