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.funct;
21 
22 //debug = Funct;
23 //debug = wrapType;
24 //debug = unwrapType;
25 //debug = mainStrct;
26 //debug = declaration;
27 //debug = ctor;
28 //debug = type;
29 //debug = name;
30 //debug = parm;
31 //debug = externalDec
32 //debug = callback;
33 
34 private import std.string;
35 private import std.stdio;
36 
37 private import utils.convparms;
38 private import utils.GtkDClass;
39 
40 bool contains(string[] src, string elem)
41 {
42   foreach( str; src)
43     if ( str == elem )
44       return true;
45 
46   return false;
47 }
48 
49 bool contains(Param[] params, string elem)
50 {
51   foreach( param; params)
52     if ( param.name == elem )
53       return true;
54 
55   return false;
56 }
57 
58 string contains(string[string] src, string elem)
59 {
60   foreach( key, str; src)
61     if ( str == elem )
62       return key;
63 
64   return null;
65 }
66 
67 struct Param
68 {
69 	string type;
70 	string typeWrap;
71 	string name;
72 	string convName;
73 
74 	bool constParam;
75 	bool outParam;
76 	bool refParam;
77 	bool arrayParam;
78 
79 	string lengthParamName;
80 }
81 
82 public struct Funct
83 {
84 	bool ctor;	/// when true this method was found to be a constructor
85 	string type;
86 	string typeWrap;
87 	string name;
88 	string convName;		/// name after convertion
89 
90 	Param[] params;
91 
92 	string strctVar;
93 	string strctPointer;
94 
95 	ConvParms* convParms;
96 	string[string] aliases;
97 
98 	@disable this();
99 
100 	/**
101 	 * Gets the type, name and parameters of the function
102 	 * Params:
103 	 *    	text = 	The Gtk API description of the function call
104 	 */
105 	this(string text, ConvParms* convParms, string[string] aliases)
106 	{
107 		this.convParms = convParms;
108 		this.aliases = aliases;
109 
110 		strctVar = getStrctVar();
111 		strctPointer = convParms.strct ~ "*";
112 
113 		debug(Funct) writefln("init text=%s", text);
114 
115 		int p = 0;
116 		GtkDClass.skipBlank(p,text);
117 		type = GtkDClass.untilBlank(p, text);
118 
119 		debug(type)writef("type = %s", type);
120 
121 		GtkDClass.fixType(type, p, text);
122 
123 		debug(type)writefln(" -> %s", type);
124 
125 		GtkDClass.skipBlank(p, text);
126 		name = GtkDClass.until(p, text, '(');
127 
128 		GtkDClass.adjustTypeName(type, name);
129 
130 		if ( type == "gchar**" || type == "char**" )
131 		{
132 			typeWrap = "string[]";
133 		}
134 		else if ( name in convParms.array && "Return" in convParms.array[name] )
135 		{
136 			if ( type == "gpointer" || type == "gconstpointer" )
137 				type = "void*";
138 				
139 			typeWrap = getWrappedType(type[0 .. $-1]) ~ "[]";
140 
141 			if ( typeWrap == "char[]" || typeWrap == "gchar[]" )
142 				typeWrap = "string";
143 		}
144 		else
145 		{
146 			typeWrap = getWrappedType(type);
147 		}
148 
149 		GtkDClass.skip(p, text,'(');
150 		int countBrace = 0;
151 		while ( countBrace==0 && p<text.length && text[p]!=')' )
152 		{
153 			Param param;
154 
155 			GtkDClass.skipBlank(p, text);
156 			param.type = GtkDClass.untilBlank(p, text, ",)");
157 
158 			debug(parm)writef("currParmType = %s", currParmType);
159 
160 			if ( param.type == "const")
161 			{
162 				param.constParam = true;
163 
164 				GtkDClass.skipBlank(p, text);
165 				param.type = GtkDClass.untilBlank(p, text, ",)");
166 			}
167 			else if ( std..string.indexOf(" volatile G_CONST_RETURN ", param.type) > 0 )
168 			{
169 				GtkDClass.skipBlank(p, text);
170 				param.type = GtkDClass.untilBlank(p, text, ",)");
171 			}
172 
173 			if ( param.type == "struct" )
174 			{
175 				GtkDClass.skipBlank(p, text);
176 				GtkDClass.untilBlank(p, text, ",)");
177 				param.type = "void";
178 			}
179 			else if ( param.type == "gpointer" )
180 			{
181 				param.type = "void*";
182 			}
183 			else if ( param.type == "gconstpointer" )
184 			{
185 				param.type = "void*";
186 				param.constParam = true;		
187 			}
188 			else
189 			{
190 				GtkDClass.fixType(param.type, p, text);
191 			}
192 
193 			debug(parm)writefln(" -> %s", currParmType);
194 
195 			GtkDClass.skipBlank(p, text);
196 			if ( param.type != "..." )
197 			{
198 				param.name = GtkDClass.until(p, text, "),");
199 
200 				if ( param.name == "*ref" )
201 					param.name = "*doref";
202 			}
203 			else
204 			{
205 				param.name = "";
206 			}
207 
208 			GtkDClass.adjustTypeName(param.type, param.name);
209 			param.convName = GtkDClass.idsToGtkD(param.name, convParms, aliases);
210 
211 			param.outParam = isOutParam(param.name);
212 			param.refParam = isRefParam(param.name);
213 			param.arrayParam = isArrayParam(param.name);
214 
215 			if ( param.arrayParam )
216 				param.lengthParamName = convParms.array[name][param.name];
217 
218 			param.typeWrap = getWrappedType(param);
219 
220 			if ( param.typeWrap == "string" )
221 				param.arrayParam = false;
222 
223 			params ~= param;
224 
225 			if ( p<text.length && text[p]==',') ++p;
226 		}
227 
228 		GtkDClass.skip(p, text, ';');
229 	}
230 
231 	private bool isOutParam(string currParam)
232 	{
233 		if ( name in convParms.outParms && convParms.outParms[name].contains(currParam) )
234 			return true;
235 
236 		return false;
237 	}
238 
239 	private bool isRefParam(string currParam)
240 	{
241 		if ( name in convParms.inoutParms && convParms.inoutParms[name].contains(currParam) )
242 			return true;
243 
244 		return false;
245 	}
246 
247 	private bool isArrayParam(string currParam)
248 	{
249 		if ( name in convParms.array && currParam in convParms.array[name] )
250 			return true;
251 
252 		return false;
253 	}
254 
255 	private bool isLengthParam(string currParam)
256 	{
257 		if ( name in convParms.array && convParms.array[name].contains(currParam) )
258 			return true;
259 
260 		return false;
261 	}
262 
263 	string getStrctVar()
264 	{
265 		if ( strctVar.length == 0 )
266 			return strctVar;
267 
268 		if ( convParms.strct.length > 0 )
269 			return GtkDClass.toVar(convParms.strct);
270 
271 		return "";
272 	}
273 	
274 	/**
275 	 * Checks the type against the strcutWrap table
276 	 * Returns: 
277 	 */
278 	string getWrappedType(string currType)
279 	{
280 		string wType;
281 		if ( currType == "gchar*" || currType == "char*" )
282 		{
283 			wType = "string";
284 		}
285 		else if ( convParms.structWrap.length == 0 )
286 		{
287 			// this should be the common case
288 			wType = currType;
289 		}
290 		else if ( currType in convParms.structWrap )
291 		{
292 			wType = convParms.structWrap[currType];
293 			debug(wrapType) writefln("WrappedType %s -> %s", currType, wType);
294 		}
295 		else
296 		{
297 			wType = currType;
298 		}
299 		
300 		debug(wrapType)writefln("\t wrapType: %s -> %s", currType, wType);
301 		return wType;
302 	}
303 
304 	/**
305 	 * Ditto, but for parameters, also checks for out, ref, array and const.
306 	 */
307 	string getWrappedType(Param param)
308 	{
309 		string wType;
310 
311 		if ( param.arrayParam )
312 		{
313 			if ( param.outParam )
314 				wType ~= "out "~ getWrappedType(param.type[0 .. $-2]) ~"[]";
315 			else if ( param.refParam )
316 				wType ~= "ref "~ getWrappedType(param.type[0 .. $-2]) ~"[]";
317 			else
318 				wType ~= getWrappedType(param.type[0 .. $-1]) ~"[]";
319 		}
320 		else if ( param.outParam )
321 		{
322 			wType ~= "out "~ getWrappedType(param.type[0 .. $-1]);
323 		}
324 		else if ( param.refParam )
325 		{
326 			wType ~= "ref "~ getWrappedType(param.type[0 .. $-1]);
327 		}
328 		else
329 		{
330 			wType ~= getWrappedType(param.type);
331 		}
332 
333 		if ( (wType == "char[]" || wType == "gchar[]") && param.constParam )
334 			wType = "string";
335 
336 		return wType;
337 	}
338 	
339 	/**
340 	 * Gets the gtk from the GtkD class to be used on the Gtk function call
341 	 * Params:
342 	 *    	currType = 	
343 	 *    	convParms = 	
344 	 * Returns: 
345 	 */
346 	string getUnwrappedType(string currType)
347 	{
348 		// TODO
349 		return currType;
350 	}
351 
352 	/**
353 	 * Gets the external declaration
354 	 * Params:
355 	 *    	 = 	
356 	 *    	aliases = 	
357 	 * Returns: 
358 	 */
359 	string getExternal()
360 	{
361 		
362 		string ext = (( type == "Window" ) ? "gulong" : type) ~ " function(" 
363 					~ getParameters()
364 					~ ")" 
365 					~ name
366 					;
367 		
368 		return ext;
369 	}
370 	
371 	string getParameters()
372 	{
373 		string parameters;
374 
375 		foreach( i, param; params )
376 		{
377 			if ( i>0 ) parameters ~= ", ";
378 
379 			if ( param.type != "void" || param.name.length > 0 )
380 			{
381 				//Workaround for GtkPlug/GtkSocket.
382 				if ( param.type == "Window" )
383 					parameters ~= "gulong "~ param.convName;
384 				else
385 					parameters ~= param.type ~" "~ param.convName;
386 			}
387 		}
388 
389 		return parameters;
390 	}
391 
392 	/**
393 	 * Gets the parameters for the call back functions.
394 	 * These are all but the first parameter and the last parameter will be the class.
395 	 * I'm not sure ths is valid for all callbacks.
396 	 * Params:
397 	 *    	firstParameter = ?	
398 	 * Returns: 
399 	 */
400 	string getCallbackParameters(int firstParameter = 0)
401 	{
402 		string parameters;
403 
404 		debug(callback)writefln("getCallbackParameters "~convParms.clss);
405 		debug(callback)writefln("\t %s", getExternal());
406 
407 		foreach ( i, param; params[firstParameter .. $-1] )
408 		{
409 			if ( i == 0 && GtkDClass.endsWith(param.type, '*') )
410 				parameters ~= param.type ~" "~ param.convName ~"Struct";
411 			else
412 				parameters ~= param.type ~" "~ param.convName;
413 
414 			parameters ~= ", ";
415 		}
416 
417 		if(convParms.templ.length > 0)
418 			parameters ~= convParms.interf~" _"~GtkDClass.idsToGtkD(GtkDClass.getClassVar(convParms), convParms, aliases);
419 		else
420 			parameters ~= convParms.clss~" _"~GtkDClass.idsToGtkD(GtkDClass.getClassVar(convParms), convParms, aliases);
421 
422 		return parameters.tr("-", "_");
423 	}
424 	
425 	/**
426 	 * Gets the actual parameters for the call back functions.
427 	 * These are all but the first parameter and the last parameter will be the class.
428 	 * I'm not sure ths is valid for all callbacks.
429 	 */
430 	string getCallbackVars()
431 	{
432 		string parameters;
433 
434 		debug(callback)writefln("getCallbackVars "~convParms.clss);
435 		debug(callback)writefln("\t %s", getExternal(convParms, aliases));
436 
437 		foreach ( i, param; params[1 .. $-1] )
438 		{
439 			parameters ~= parameterToGtkD(param) ~", ";
440 		}
441 
442 		parameters ~= "_"~GtkDClass.idsToGtkD(GtkDClass.getClassVar(convParms), convParms, aliases);
443 
444 		return parameters.tr("-", "_");
445 	}
446 
447 	string getDelegateDeclaration(int firstParameter = 0)
448 	{
449 		if ( GtkDClass.endsWith(typeWrap,"user_function") )
450 			typeWrap = typeWrap[0..typeWrap.length-13];
451 
452 		if ( typeWrap == "gboolean" )
453 			typeWrap = "bool";
454 
455 		string decl = typeWrap ~ " delegate(";
456 		
457 		foreach ( param; params[firstParameter .. $-1] )
458 		{
459 			decl ~= param.typeWrap ~", ";
460 		}
461 
462 		//If we are generating an interface or template
463 		//use the interface name in the delegate.
464 		if(convParms.isInterface || convParms.templ.length > 0)
465 			decl ~= convParms.interf;
466 		else
467 			decl ~= convParms.clss;
468 
469 		decl ~= ")";
470 		return decl;
471 	}
472 	
473 
474 	/**
475 	 * Gets the method header.
476 	 * If the type is a pointer to the main strcut and the name starts with "new"
477 	 * then the method is a constructor
478 	 * Returns: 
479 	 */
480 	string declaration()
481 	{
482 		string dec;
483 		string returnType;
484 
485 		if ( indexOf(typeWrap, "string") > -1 )
486 			returnType = typeWrap;
487 		else
488 			returnType = GtkDClass.stringToGtkD(typeWrap, convParms, aliases);
489 
490 		debug(ctor)writefln("declaration ctor strct = %s",convParms.strct);
491 		debug(ctor)writefln("declaration ctor realStrct = %s",convParms.realStrct);
492 		debug(ctor)writefln("declaration ctor type = %s",type);
493 		debug(ctor)writefln("declaration ctor name = %s",name);
494 
495 		convName = GtkDClass.idsToGtkD(name, convParms, aliases);
496 		
497 		if( convName == "ref" )
498 			convName = "doref";
499 
500 		convName = GtkDClass.stringToGtkD(convName, convParms, aliases);
501 
502 		debug(declaration)writefln("name=%s convName=%s", name, convName);
503 
504 		if ( convParms.strct.length>0 
505 			&& GtkDClass.startsWith(convName, "new")
506 			&& ( (type == strctPointer 
507 					|| type == convParms.ctorStrct~"*")
508 				|| /* special GObject case */
509 					(type == "gpointer" && convParms.strct == "GObject")
510 				|| /* special Gtk... that return a GtkWidget pointer */
511 					(type == "GtkWidget*")
512 				|| /* special Gtk... that return a GtkWidget pointer */
513 					(type == "GtkObject*" && convParms.strct == "GtkAdjustment")
514 				) 
515 			)
516 		{
517 			dec = "public this (";
518 			ctor = true;
519 			convName = "this";
520 		}
521 		else
522 		{
523 			if ( convName == "new" )
524 				convName ~= convParms.outFile;
525 
526 			string overr;
527 			if (convParms.needsOverride(convName) && !convParms.isInterface )
528 				overr = "override ";
529 
530 			if ( convParms.strct.length>0 
531 				&& params.length > 0 
532 				&& params[0].type == strctPointer
533 				&& !params[0].arrayParam
534 				)
535 			{
536 				dec = "public "~overr~returnType~" "~convName~"(";
537 			}
538 			else
539 			{
540 				dec = "public static "~overr~returnType~" "~convName~"(";
541 			}
542 			ctor = false;
543 		}
544 
545 		foreach ( i, param; params )
546 		{
547 			if ( i == 0 )
548 			{
549 				debug(mainStrct)writefln("1st Parm %s ?= %s",parmsType[i], convParms.strct);
550 
551 				if ( !ctor && param.type == strctPointer && !param.arrayParam )
552 				{
553 					debug(mainStrct)writefln("\tSAME <<<<<<------");
554 					continue;
555 				}
556 			}
557 
558 			if ( GtkDClass.startsWith(param.typeWrap, "GError**") && convParms.strct != "GError" )
559 				continue;
560 
561 			if ( isLengthParam(param.name) )
562 				continue;
563 
564 			if ( dec[$-1] != '(' )
565 				dec ~= ", ";
566 
567 			if ( param.type != "void" || param.name.length > 0)
568 			{
569 				if ( indexOf(param.typeWrap, "string") > -1 )
570 					dec ~= param.typeWrap;
571 				else
572 					dec ~= GtkDClass.stringToGtkD(param.typeWrap, convParms, aliases);
573 
574 				dec ~= " "~ param.convName;
575 			}
576 		}
577 		dec ~= ')';
578 
579 		debug(declaration)writefln("declaration=%s", dec);
580 		
581 		return dec;
582 	}
583 
584 	/**
585 	 * Wraps the parameters if necessary
586 	 * Params:
587 	 *    	i = 	
588 	 * Returns: 
589 	 */
590 	string parameterToGtk(Param param)
591 	{
592 		string parmToGtk;
593 
594 		if ( param.type != param.typeWrap )
595 		{
596 			if ( param.typeWrap == "string" )
597 			{
598 				if ( param.lengthParamName != "" && param.constParam )
599 				{
600 					parmToGtk = "cast(char*)"~ param.convName ~".ptr";
601 				}
602 				else
603 				{
604 					parmToGtk = "Str.toStringz("~ param.convName ~")";
605 				}
606 			}
607 			else if ( param.typeWrap == "string[]" )
608 			{
609 				parmToGtk = "Str.toStringzArray("~ param.convName ~")";
610 			}
611 			else if ( param.outParam || param.refParam )
612 			{
613 				//Is this a plian old data type.
614 				if ( param.type[0 .. $-1] == split(param.typeWrap)[1] )
615 					parmToGtk = "&" ~ param.convName;
616 				else
617 					parmToGtk = "&out" ~ param.convName;
618 			}
619 			else if ( param.arrayParam )
620 			{
621 				parmToGtk = param.convName ~".ptr";
622 			}
623 			else
624 			{
625 				if(GtkDClass.endsWith(param.typeWrap, "IF"))
626 					parmToGtk = "("~param.convName~" is null) ? null : "~param.convName~ ".get"~ param.typeWrap[0..$-2] ~ "T" ~"Struct()";
627 				else if ( param.typeWrap == "Application" &&  param.type == "GtkApplication*" )
628 					parmToGtk = "("~param.convName~" is null) ? null : "~param.convName~ ".getGtk"~ param.typeWrap ~"Struct()";
629 				else
630 					parmToGtk = "("~param.convName~" is null) ? null : "~param.convName~ ".get"~ param.typeWrap ~"Struct()";
631 			}
632 		}
633 		else
634 		{
635 			if ( name in convParms.array && convParms.array[name].contains(param.name) )
636 			{
637 				if ( name in convParms.outParms && convParms.outParms[name].contains(convParms.array[name].contains(param.name)) )
638 				{
639 					if ( GtkDClass.endsWith(param.type, "*") )
640 						parmToGtk = "&"~ param.convName;
641 					else
642 						parmToGtk ~= param.convName;
643 				}
644 				else if ( name in convParms.inoutParms && convParms.inoutParms[name].contains(convParms.array[name].contains(param.name)) )
645 				{
646 					if ( GtkDClass.endsWith(param.type, "*") )
647 						parmToGtk = "&"~ param.convName;
648 					else
649 						parmToGtk ~= param.convName;
650 				}
651 				else
652 				{
653 					string id = GtkDClass.idsToGtkD(convParms.array[name].contains(param.name), convParms, aliases);
654 					parmToGtk = "cast(int) "~ id ~".length";
655 				}
656 			}
657 			else
658 			{
659 				parmToGtk = param.convName;
660 			}
661 		}
662 		return parmToGtk;
663 	}
664 	
665 	string parameterToGtkD(Param param)
666 	{
667 		string parmToGtkD;
668 
669 		if ( param.type != param.typeWrap )
670 		{
671 			if ( param.typeWrap == "string" )
672 			{
673 				parmToGtkD = "Str.toString("~ param.convName ~")";
674 			}
675 			else if (GtkDClass.endsWith(param.typeWrap, "IF"))
676 			{
677 				parmToGtkD = "ObjectG.getDObject!("~param.typeWrap[0..$-2]~", "~param.typeWrap~")("~ param.convName ~")";
678 			}
679 			else
680 			{
681 				parmToGtkD = "ObjectG.getDObject!("~param.typeWrap~")("~ param.convName ~")";
682 			}
683 		}
684 		else
685 		{
686 			parmToGtkD = param.convName;
687 		}
688 
689 		return parmToGtkD;
690 	}
691 
692 	string getWrapParametersType()
693 	{
694 		string pw;
695 		foreach ( param; params )
696 		{
697 			if ( pw !is null )
698 				pw ~= ",";
699 
700 			pw ~= param.typeWrap;
701 		}
702 		return pw;
703 	}
704 	
705 	/**
706 	 * Gets the body of the GtkD method.
707 	 * This include the call to the Gtk function.
708 	 * If the first parameter is a pointer to the struct make it implicit and use the internal struct
709 	 * Returns: The text of the body of the function NOT including the braces
710 	 */
711 	string[] bod()
712 	{
713 		string[] bd; /* Return variable. */
714 		string gtkCall;
715 		string[] end; //Code to be added to the end of the function to wrap ref/out parameters. 
716 		bool wrapError = false;
717 
718 		void checkError()
719 		{
720 			if ( wrapError )
721 			{
722 				bd ~= "";
723 				bd ~= "if (err !is null)";
724 				bd ~= "{";
725 				bd ~= "\tthrow new GException( new ErrorG(err) );";
726 				bd ~= "}";
727 				if ( end.length == 0 )
728 					bd ~= "";
729 			}
730 		}
731 		
732 		string construct(string type)
733 		{
734 			if ( convParms.outPack == "cairo" || convParms.outPack == "glib" || convParms.outPack == "gthread" )
735 				return "new "~ type;
736 			else if( GtkDClass.endsWith(type, "IF") )
737 				return "ObjectG.getDObject!("~ type[0..$-2] ~", "~ type ~")";
738 			else
739 				return "ObjectG.getDObject!("~ type ~")";
740 		}
741 
742 		/* 1st: construct the actual GTK+ call. */
743 		gtkCall ~= name ~ "("; //gtk_function(
744 
745 		foreach( i, param; params )
746 		{
747 			debug(parm) writefln("\t(%s -> %s) %s",param.type, param.typeWrap, param.name);
748 
749 			if ( i > 0 )
750 				gtkCall ~= ", ";
751 
752 			if ( !ctor && i == 0 && params[0].type == strctPointer && !(name in convParms.array && params[0].name in convParms.array[name]) )
753 			{
754 				
755 				if ( convParms.templ.length == 0 )
756 				{
757 					gtkCall ~= GtkDClass.toVar(convParms.strct);
758 				}
759 				else
760 				{
761 					gtkCall ~= "get"~convParms.clss~"Struct()";
762 				}
763 			}
764 			else if ( param.typeWrap == "GError**" && convParms.strct != "GError")
765 			{
766 				bd ~= "GError* err = null;";
767 
768 				gtkCall ~= "&err";
769 
770 				wrapError = true;
771 			}
772 			else if ( param.typeWrap == "out string" || param.typeWrap == "ref string" )
773 			{
774 				string id = param.convName;
775 
776 				if ( param.typeWrap == "out string" )
777 					bd ~= "char* out"~ id ~" = null;";
778 				else
779 					bd ~= "char* out"~ id ~" = Str.toStringz("~ id ~");";
780 
781 				gtkCall ~= "&out"~ id;
782 				end ~= id ~" = Str.toString(out"~ id ~");";
783 			}
784 			else if ( param.typeWrap == "out string[]" || param.typeWrap == "ref string[]" )
785 			{
786 				string id = param.convName;
787 				string lenid = GtkDClass.idsToGtkD(convParms.array[name][param.name], convParms, aliases);
788 
789 				if ( param.typeWrap == "out string[]" )
790 					bd ~= "char** out"~ id ~" = null;";
791 				else
792 					bd ~= "char** out"~ id ~" = Str.toStringzArray("~ id ~");";
793 
794 				if ( convParms.array[name][param.name] != "" )
795 					if ( param.typeWrap == "out string[]" )
796 						bd ~= "int "~ GtkDClass.idsToGtkD(convParms.array[name][param.name], convParms, aliases) ~";";
797 					else
798 						bd ~= "int "~ GtkDClass.idsToGtkD(convParms.array[name][param.name], convParms, aliases) ~" = cast(int) "~ id ~".length;";
799 
800 				gtkCall ~= "&out"~ id;
801 
802 				if ( convParms.array[name][param.name] == "Return" )
803 					lenid = "p";
804 
805 
806 				if ( lenid.length > 0 )
807 				{
808 					end ~= id ~" = null;";
809 					end ~= "foreach ( cstr; out"~ id ~"[0 .. "~ lenid ~"] )";
810 					end ~= "{";
811 					end ~= "	"~ id ~" ~= Str.toString(cstr);";
812 					end ~= "}";
813 				}
814 				else
815 				{
816 					end ~= id ~" = Str.toStringArray(out"~ id ~");";
817 				}
818 			}
819 			else if ( (param.outParam || param.refParam) && param.arrayParam )
820 			{
821 				string id = param.convName;
822 				string lenid = GtkDClass.idsToGtkD(convParms.array[name][param.name], convParms, aliases);
823 
824 				if ( param.type[0 .. $-2] == split(param.typeWrap)[1][0 .. $-2] )
825 				{
826 					if ( param.outParam )
827 						bd ~= split(param.typeWrap)[1][0 .. $-2] ~"* out"~ id ~ " = null;";
828 					else
829 						bd ~= split(param.typeWrap)[1][0 .. $-2] ~"* out"~ id ~ " = "~ id ~".ptr;";
830 				
831 					gtkCall ~= "&out" ~ id;
832 
833 					bool areMultipleArrays()
834 					{
835 						foreach ( parm; params[i+1 .. $] )
836 						{
837 							if ( parm.name in convParms.array[name] && convParms.array[name][param.name] == convParms.array[name][parm.name] )
838 								return true;
839 						}
840 						return false;
841 					}
842 
843 					if ( !areMultipleArrays && params.contains(convParms.array[name][param.name]) )
844 					{
845 						if (GtkDClass.startsWith(param.typeWrap, "out") )
846 							bd ~= "int "~ lenid ~";";
847 						else
848 							bd ~= "int "~ lenid ~" = cast(int) "~ id ~".length;";
849 					}
850 
851 					if ( convParms.array[name][param.name] == "Return" )
852 						lenid = "p";
853 
854 					end ~= id ~" = out"~ id ~"[0 .. " ~ lenid ~"];";
855 				}
856 				else
857 				{
858 					if ( param.outParam )
859 					{
860 						bd ~= param.type.removechars("*") ~"** out"~ id ~ " = null;";
861 					}
862 					else
863 					{
864 						bd ~= "";
865 						bd ~= param.type.removechars("*") ~ "*[] inout"~ id ~" = new "~ param.type.removechars("*") ~"*["~ id ~".length];";
866 						bd ~= "for ( int i = 0; i < "~ id ~".length ; i++ )";
867 						bd ~= "{";
868 						bd ~= "\tinout"~ id ~"[i] = "~ id~ "[i].get"~ split(param.typeWrap)[1][0 .. $-2] ~"Struct();";
869 						bd ~= "}";
870 						bd ~= "";
871 						bd ~= param.type.removechars("*") ~ "** out"~ id ~" = inout"~ id ~".ptr;";
872 					}
873 
874 					gtkCall ~= "&out" ~ id;
875 
876 					if ( params.contains(convParms.array[name][param.name]) )
877 					{
878 						if ( param.outParam )
879 							bd ~= "int "~ lenid ~";";
880 						else
881 							bd ~= "int "~ lenid ~" = cast(int) "~ id ~".length;";
882 					}
883 
884 					if ( convParms.array[name][param.name] == "Return" )
885 						lenid = "p";
886 
887 					end ~= "";
888 					end ~= id ~" = new "~ split(param.typeWrap)[1][0 .. $-2] ~"["~ lenid ~"];";
889 					end ~= "for(int i = 0; i < "~ lenid ~"; i++)";
890 					end ~= "{";
891 					end ~= "\t"~ id ~"[i] = " ~ construct(split(param.typeWrap)[1][0 .. $-2]) ~ "(cast(" ~ param.type.removechars("*") ~ "*) out"~ id ~"[i]);";
892 					end ~= "}";
893 				}
894 			}
895 			else if ( (param.outParam || param.refParam) &&
896 				param.type[0 .. $-1] != split(param.typeWrap)[1] )
897 			{
898 				string id = param.convName;
899 
900 				if ( param.outParam )
901 				{
902 					bd ~= param.type.removechars("*") ~"* out"~ id ~ " = null;";
903 				}
904 				else
905 				{
906 					bd ~= param.type.removechars("*") ~"* out"~ id ~ " = ("~id~" is null) ? null : "~id~ ".get"~ split(param.typeWrap)[1] ~"Struct();";
907 				}
908 
909 				gtkCall ~= "&out" ~ id;
910 				
911 				end ~= id ~" = "~ construct(split(param.typeWrap)[1]) ~"(out"~ id ~");";
912 			}
913 			else if ( param.arrayParam && param.type[0 .. $-1] != param.typeWrap[0 .. $-2] &&
914 				 !GtkDClass.endsWith(param.type, "[]") && param.typeWrap != "string[]" )
915 			{
916 				string id = param.convName;
917 
918 				bd ~= "";
919 				bd ~= param.type.removechars("*") ~ "*[] "~ id ~"Array = new "~ param.type.removechars("*") ~"*["~ id ~".length];";
920 				bd ~= "for ( int i = 0; i < "~ id ~".length ; i++ )";
921 				bd ~= "{";
922 				bd ~= "\t"~ id ~"Array[i] = "~ id~ "[i].get"~ param.typeWrap[0 .. $-2] ~"Struct();";
923 				bd ~= "}";
924 				bd ~= "";
925 
926 				gtkCall ~= id ~"Array.ptr";
927 			}
928 			else if ( name in convParms.array && "Return" == convParms.array[name].contains(param.name) )
929 			{
930 				string id = param.convName;
931 
932 				bd ~= GtkDClass.tokenToGtkD(param.type.chomp("*"), convParms, aliases) ~" "~ id ~";";
933 				gtkCall ~= "&"~ id;
934 			}
935 			else if ( param.typeWrap == "FILE*" ) //Phobos Workaround
936 			{
937 				gtkCall ~= "cast(void*)"~ GtkDClass.idsToGtkD(param.name, convParms, aliases);
938 			}
939 			else
940 			{
941 				if ( param.name.length > 0 )
942 				{
943 					gtkCall ~= parameterToGtk(param);
944 				}
945 			}
946 		}
947 
948 		gtkCall ~= ")"; //gtk_function(arg1...argN)
949 
950 		if ( end.length > 0 || wrapError )
951 		{
952 			bd ~= "";
953 			if ( end.length > 0 )
954 				end = [""] ~ end;
955 		}
956 
957 		/* 2nd: construct the rest of the body according to the type
958 		 * of the function. */
959 		if (type == "void")
960 		{
961 			/* If it's a void, we just need to make the call and be done
962 			 * with it. */
963 			gtkCall ~= ";";
964 			bd ~= gtkCall;
965 			checkError();
966 
967 			if ( end.length > 0 )
968 				bd ~= end;
969 
970 			return bd;
971 		}
972 		else if (type == "FILE*") //Phobos Workaround.
973 		{
974 			return bd ~= "return cast(FILE*)" ~ gtkCall ~ ";";
975 		}
976 		else
977 		{
978 			/* If the call constructs an object, call the GTK+ constructor
979 			 * and pass the object to the wrapper's constructor. */
980 			if(ctor)
981 			{
982 				/*string prepend = "this(cast(";
983 				if(convParms.realStrct.length > 0)
984 					prepend ~= convParms.realStrct ~ "*)";
985 				else
986 					prepend ~= convParms.strct ~ "*)";
987 				gtkCall = prepend ~ gtkCall*/
988 				string strct = (convParms.realStrct.length > 0) ? convParms.realStrct : convParms.strct;
989 				/* Do we need a cast? */
990 				bd ~= "auto p = " ~ gtkCall ~ ";"; //GtkStruct* p = gtk_function(arg1...argN);
991 				
992 				checkError();
993 
994 				string[] check = [	"if(p is null)",
995 								  	"{",
996 								  	"	throw new ConstructionException(\"null returned by " ~ gtkCall ~ "\");",
997 									"}"	];
998 				bd ~= check;
999 
1000 				if ( end.length > 0 )
1001 					bd ~= end;
1002 
1003 				/* What's with all the casting? */
1004 				/* A; Casting is needed because some GTK+
1005 				 *    functions can return void pointers. */
1006 				bd ~= "this(cast(" ~ strct ~ "*) p);";
1007 				//bd ~= "this(p);";				
1008 
1009 				/* The body is constructed, return. */
1010 				return bd;
1011 			}
1012 			else
1013 			{
1014 				/* Non-void call. */
1015 				if(type == typeWrap )
1016 				{
1017 					/* We return an object of the same type as the GTK+ function. */
1018 					//return gtk_function(arg1...argN);
1019 					if ( !wrapError && end.length == 0)
1020 						bd ~= "return " ~ gtkCall ~ ";";
1021 					else
1022 					{
1023 						bd ~= "auto p = " ~ gtkCall ~ ";";
1024 						checkError();
1025 
1026 						if ( end.length > 0 )
1027 							bd ~= end;
1028 
1029 						if ( !(name in convParms.array && "Return" in convParms.array[name]) )
1030 							bd ~= "return p;";
1031 					}
1032 
1033 					return bd;
1034 				}
1035 				else
1036 				{
1037 					/* We return an object of a different type, we need to wrap it
1038 					 * accordingly. */
1039 					if(typeWrap == "string")
1040 					{
1041 						/* Returned strings get special care. */
1042 						//return Str.toString(gtk_function(arg1...argN)).dup;
1043 						if ( !wrapError && !(name in convParms.array && "Return" in convParms.array[name]) )
1044 							bd ~= "return Str.toString(" ~ gtkCall ~ ");";
1045 						else
1046 						{
1047 							bd ~= "auto p = " ~ gtkCall ~ ";";
1048 							checkError();
1049 
1050 							if ( end.length > 0 )
1051 								bd ~= end;
1052 
1053 							if (name in convParms.array && "Return" in convParms.array[name])
1054 							{
1055 								string lenid = GtkDClass.idsToGtkD(convParms.array[name]["Return"], convParms, aliases);
1056 
1057 								bd ~= "return Str.toString(p, "~ lenid ~");";
1058 							}
1059 							else
1060 								bd ~= "return Str.toString(p);";
1061 						}
1062 
1063 						return bd;
1064 					}
1065 					else if(typeWrap == "string[]")
1066 					{
1067 						/* Returned strings get special care. */
1068 						//return Str.toString(gtk_function(arg1...argN)).dup;
1069 						if ( !wrapError && !(name in convParms.array && "Return" in convParms.array[name]) )
1070 							bd ~= "return Str.toStringArray(" ~ gtkCall ~ ");";
1071 						else
1072 						{
1073 							bd ~= "\nauto p = " ~ gtkCall ~ ";";
1074 							checkError();
1075 
1076 							if ( end.length > 0 )
1077 								bd ~= end;
1078 
1079 							if (name in convParms.array && "Return" in convParms.array[name])
1080 							{
1081 								string lenid = GtkDClass.idsToGtkD(convParms.array[name]["Return"], convParms, aliases);
1082 
1083 								bd ~= "";
1084 								bd ~= "string[] strArray = null;"; 
1085 								bd ~= "foreach ( cstr; p[0 .. "~ lenid ~"] )"; 
1086 								bd ~= "{"; 
1087 								bd ~= "    strArray ~= Str.toString(cstr);"; 
1088 								bd ~= "}"; 
1089 								bd ~= "";
1090 								bd ~= "return strArray;";
1091 							}
1092 							else
1093 								bd ~= "return Str.toStringArray(p);";
1094 						}
1095 
1096 						return bd;
1097 					}
1098 					else
1099 					{
1100 						/* All other objects are wrapped in their container. */
1101 						/* Why do we need a cast? */
1102 						//GtkType p = gtk_function(arg1...argN);
1103 						bd ~= "auto p = " ~ gtkCall ~ ";";
1104 
1105 						checkError();
1106 
1107 						if ( end.length > 0 )
1108 							bd ~= end;
1109 
1110 						bd ~=  [ "",
1111 								 "if(p is null)",
1112 								 "{",
1113 								 "	return null;",
1114 								 "}",
1115 								 "" ];
1116 
1117 						if ( GtkDClass.endsWith(typeWrap, "[]") )
1118 						{
1119 							string id = GtkDClass.idsToGtkD(convParms.array[name]["Return"], convParms, aliases);
1120 
1121 							if (type[0 .. $-1] == typeWrap[0 .. $-2])
1122 							{
1123 								bd ~= "return p[0 .. "~ id ~"];";
1124 							}
1125 							else
1126 							{
1127 								bd ~= typeWrap ~" arr = new "~ typeWrap[0 .. $-2] ~"["~ id ~"];";
1128 								bd ~= "for(int i = 0; i < "~ id ~"; i++)";
1129 								bd ~= "{";
1130 								bd ~= "\tarr[i] = "~ construct(typeWrap[0 .. $-2]) ~"(cast("~ type.chomp("*") ~") p[i]);";
1131 								bd ~= "}";
1132 								bd ~= "";
1133 								bd ~= "return arr;";
1134 							}
1135 						}
1136 						/* What's with all the casting? */
1137 						/* A; Casting is needed because some GTK+
1138 						 *    functions can return void pointers. */
1139 						else
1140 							bd ~= "return " ~ construct(typeWrap) ~ "(cast(" ~ type ~ ") p);";
1141 						
1142 						return bd;
1143 					}
1144 				}
1145 			}
1146 		}
1147 
1148 		/* We should never reach here. */
1149 		assert(0, "A strange function body was requested.");
1150 	}	
1151 }