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 ( lenid.length > 0 )
803 				{
804 					end ~= id ~" = null;";
805 					end ~= "foreach ( cstr; out"~ id ~"[0 .. "~ lenid ~"] )";
806 					end ~= "{";
807 					end ~= "	"~ id ~" ~= Str.toString(cstr);";
808 					end ~= "}";
809 				}
810 				else
811 				{
812 					end ~= id ~" = Str.toStringArray(out"~ id ~");";
813 				}
814 			}
815 			else if ( (param.outParam || param.refParam) && param.arrayParam )
816 			{
817 				string id = param.convName;
818 				string lenid = GtkDClass.idsToGtkD(convParms.array[name][param.name], convParms, aliases);
819 
820 				if ( param.type[0 .. $-2] == split(param.typeWrap)[1][0 .. $-2] )
821 				{
822 					if ( param.outParam )
823 						bd ~= split(param.typeWrap)[1][0 .. $-2] ~"* out"~ id ~ " = null;";
824 					else
825 						bd ~= split(param.typeWrap)[1][0 .. $-2] ~"* out"~ id ~ " = "~ id ~".ptr;";
826 				
827 					gtkCall ~= "&out" ~ id;
828 
829 					bool areMultipleArrays()
830 					{
831 						foreach ( parm; params[i+1 .. $] )
832 						{
833 							if ( parm.name in convParms.array[name] && convParms.array[name][param.name] == convParms.array[name][parm.name] )
834 								return true;
835 						}
836 						return false;
837 					}
838 
839 					if ( !areMultipleArrays && params.contains(convParms.array[name][param.name]) )
840 					{
841 						if (GtkDClass.startsWith(param.typeWrap, "out") )
842 							bd ~= "int "~ lenid ~";";
843 						else
844 							bd ~= "int "~ lenid ~" = cast(int) "~ id ~".length;";
845 					}
846 
847 					if ( convParms.array[name][param.name] == "Return" )
848 						lenid = "p";
849 
850 					end ~= id ~" = out"~ id ~"[0 .. " ~ lenid ~"];";
851 				}
852 				else
853 				{
854 					if ( param.outParam )
855 					{
856 						bd ~= param.type.removechars("*") ~"** out"~ id ~ " = null;";
857 					}
858 					else
859 					{
860 						bd ~= "";
861 						bd ~= param.type.removechars("*") ~ "*[] inout"~ id ~" = new "~ param.type.removechars("*") ~"*["~ id ~".length];";
862 						bd ~= "for ( int i = 0; i < "~ id ~".length ; i++ )";
863 						bd ~= "{";
864 						bd ~= "\tinout"~ id ~"[i] = "~ id~ "[i].get"~ split(param.typeWrap)[1][0 .. $-2] ~"Struct();";
865 						bd ~= "}";
866 						bd ~= "";
867 						bd ~= param.type.removechars("*") ~ "** out"~ id ~" = inout"~ id ~".ptr;";
868 					}
869 
870 					gtkCall ~= "&out" ~ id;
871 
872 					if ( params.contains(convParms.array[name][param.name]) )
873 					{
874 						if ( param.outParam )
875 							bd ~= "int "~ lenid ~";";
876 						else
877 							bd ~= "int "~ lenid ~" = cast(int) "~ id ~".length;";
878 					}
879 
880 					if ( convParms.array[name][param.name] == "Return" )
881 						lenid = "p";
882 
883 					end ~= "";
884 					end ~= id ~" = new "~ split(param.typeWrap)[1][0 .. $-2] ~"["~ lenid ~"];";
885 					end ~= "for(int i = 0; i < "~ lenid ~"; i++)";
886 					end ~= "{";
887 					end ~= "\t"~ id ~"[i] = " ~ construct(split(param.typeWrap)[1][0 .. $-2]) ~ "(cast(" ~ param.type.removechars("*") ~ "*) out"~ id ~"[i]);";
888 					end ~= "}";
889 				}
890 			}
891 			else if ( (param.outParam || param.refParam) &&
892 				param.type[0 .. $-1] != split(param.typeWrap)[1] )
893 			{
894 				string id = param.convName;
895 
896 				if ( param.outParam )
897 				{
898 					bd ~= param.type.removechars("*") ~"* out"~ id ~ " = null;";
899 				}
900 				else
901 				{
902 					bd ~= param.type.removechars("*") ~"* out"~ id ~ " = ("~id~" is null) ? null : "~id~ ".get"~ split(param.typeWrap)[1] ~"Struct();";
903 				}
904 
905 				gtkCall ~= "&out" ~ id;
906 				
907 				end ~= id ~" = "~ construct(split(param.typeWrap)[1]) ~"(out"~ id ~");";
908 			}
909 			else if ( param.arrayParam && param.type[0 .. $-1] != param.typeWrap[0 .. $-2] &&
910 				 !GtkDClass.endsWith(param.type, "[]") && param.typeWrap != "string[]" )
911 			{
912 				string id = param.convName;
913 
914 				bd ~= "";
915 				bd ~= param.type.removechars("*") ~ "*[] "~ id ~"Array = new "~ param.type.removechars("*") ~"*["~ id ~".length];";
916 				bd ~= "for ( int i = 0; i < "~ id ~".length ; i++ )";
917 				bd ~= "{";
918 				bd ~= "\t"~ id ~"Array[i] = "~ id~ "[i].get"~ param.typeWrap[0 .. $-2] ~"Struct();";
919 				bd ~= "}";
920 				bd ~= "";
921 
922 				gtkCall ~= id ~"Array.ptr";
923 			}
924 			else if ( name in convParms.array && "Return" == convParms.array[name].contains(param.name) )
925 			{
926 				string id = param.convName;
927 
928 				bd ~= GtkDClass.tokenToGtkD(param.type.chomp("*"), convParms, aliases) ~" "~ id ~";";
929 				gtkCall ~= "&"~ id;
930 			}
931 			else if ( param.typeWrap == "FILE*" ) //Phobos Workaround
932 			{
933 				gtkCall ~= "cast(void*)"~ GtkDClass.idsToGtkD(param.name, convParms, aliases);
934 			}
935 			else
936 			{
937 				if ( param.name.length > 0 )
938 				{
939 					gtkCall ~= parameterToGtk(param);
940 				}
941 			}
942 		}
943 
944 		gtkCall ~= ")"; //gtk_function(arg1...argN)
945 
946 		if ( end.length > 0 || wrapError )
947 		{
948 			bd ~= "";
949 			if ( end.length > 0 )
950 				end = [""] ~ end;
951 		}
952 
953 		/* 2nd: construct the rest of the body according to the type
954 		 * of the function. */
955 		if (type == "void")
956 		{
957 			/* If it's a void, we just need to make the call and be done
958 			 * with it. */
959 			gtkCall ~= ";";
960 			bd ~= gtkCall;
961 			checkError();
962 
963 			if ( end.length > 0 )
964 				bd ~= end;
965 
966 			return bd;
967 		}
968 		else if (type == "FILE*") //Phobos Workaround.
969 		{
970 			return bd ~= "return cast(FILE*)" ~ gtkCall ~ ";";
971 		}
972 		else
973 		{
974 			/* If the call constructs an object, call the GTK+ constructor
975 			 * and pass the object to the wrapper's constructor. */
976 			if(ctor)
977 			{
978 				/*string prepend = "this(cast(";
979 				if(convParms.realStrct.length > 0)
980 					prepend ~= convParms.realStrct ~ "*)";
981 				else
982 					prepend ~= convParms.strct ~ "*)";
983 				gtkCall = prepend ~ gtkCall*/
984 				string strct = (convParms.realStrct.length > 0) ? convParms.realStrct : convParms.strct;
985 				/* Do we need a cast? */
986 				bd ~= "auto p = " ~ gtkCall ~ ";"; //GtkStruct* p = gtk_function(arg1...argN);
987 				
988 				checkError();
989 
990 				string[] check = [	"if(p is null)",
991 								  	"{",
992 								  	"	throw new ConstructionException(\"null returned by " ~ gtkCall ~ "\");",
993 									"}"	];
994 				bd ~= check;
995 
996 				if ( end.length > 0 )
997 					bd ~= end;
998 
999 				/* What's with all the casting? */
1000 				/* A; Casting is needed because some GTK+
1001 				 *    functions can return void pointers. */
1002 				bd ~= "this(cast(" ~ strct ~ "*) p);";
1003 				//bd ~= "this(p);";				
1004 
1005 				/* The body is constructed, return. */
1006 				return bd;
1007 			}
1008 			else
1009 			{
1010 				/* Non-void call. */
1011 				if(type == typeWrap )
1012 				{
1013 					/* We return an object of the same type as the GTK+ function. */
1014 					//return gtk_function(arg1...argN);
1015 					if ( !wrapError && end.length == 0)
1016 						bd ~= "return " ~ gtkCall ~ ";";
1017 					else
1018 					{
1019 						bd ~= "auto p = " ~ gtkCall ~ ";";
1020 						checkError();
1021 
1022 						if ( end.length > 0 )
1023 							bd ~= end;
1024 
1025 						if ( !(name in convParms.array && "Return" in convParms.array[name]) )
1026 							bd ~= "return p;";
1027 					}
1028 
1029 					return bd;
1030 				}
1031 				else
1032 				{
1033 					/* We return an object of a different type, we need to wrap it
1034 					 * accordingly. */
1035 					if(typeWrap == "string")
1036 					{
1037 						/* Returned strings get special care. */
1038 						//return Str.toString(gtk_function(arg1...argN)).dup;
1039 						if ( !wrapError && !(name in convParms.array && "Return" in convParms.array[name]) )
1040 							bd ~= "return Str.toString(" ~ gtkCall ~ ");";
1041 						else
1042 						{
1043 							bd ~= "auto p = " ~ gtkCall ~ ";";
1044 							checkError();
1045 
1046 							if ( end.length > 0 )
1047 								bd ~= end;
1048 
1049 							if (name in convParms.array && "Return" in convParms.array[name])
1050 							{
1051 								string lenid = GtkDClass.idsToGtkD(convParms.array[name]["Return"], convParms, aliases);
1052 
1053 								bd ~= "return Str.toString(p, "~ lenid ~");";
1054 							}
1055 							else
1056 								bd ~= "return Str.toString(p);";
1057 						}
1058 
1059 						return bd;
1060 					}
1061 					else if(typeWrap == "string[]")
1062 					{
1063 						/* Returned strings get special care. */
1064 						//return Str.toString(gtk_function(arg1...argN)).dup;
1065 						if ( !wrapError && !(name in convParms.array && "Return" in convParms.array[name]) )
1066 							bd ~= "return Str.toStringArray(" ~ gtkCall ~ ");";
1067 						else
1068 						{
1069 							bd ~= "\nauto p = " ~ gtkCall ~ ";";
1070 							checkError();
1071 
1072 							if ( end.length > 0 )
1073 								bd ~= end;
1074 
1075 							if (name in convParms.array && "Return" in convParms.array[name])
1076 							{
1077 								string lenid = GtkDClass.idsToGtkD(convParms.array[name]["Return"], convParms, aliases);
1078 
1079 								bd ~= "";
1080 								bd ~= "string[] strArray = null;"; 
1081 								bd ~= "foreach ( cstr; p[0 .. "~ lenid ~"] )"; 
1082 								bd ~= "{"; 
1083 								bd ~= "    strArray ~= Str.toString(cstr);"; 
1084 								bd ~= "}"; 
1085 								bd ~= "";
1086 								bd ~= "return strArray;";
1087 							}
1088 							else
1089 								bd ~= "return Str.toStringArray(p);";
1090 						}
1091 
1092 						return bd;
1093 					}
1094 					else
1095 					{
1096 						/* All other objects are wrapped in their container. */
1097 						/* Why do we need a cast? */
1098 						//GtkType p = gtk_function(arg1...argN);
1099 						bd ~= "auto p = " ~ gtkCall ~ ";";
1100 
1101 						checkError();
1102 
1103 						if ( end.length > 0 )
1104 							bd ~= end;
1105 
1106 						bd ~=  [ "",
1107 								 "if(p is null)",
1108 								 "{",
1109 								 "	return null;",
1110 								 "}",
1111 								 "" ];
1112 
1113 						if ( GtkDClass.endsWith(typeWrap, "[]") )
1114 						{
1115 							string id = GtkDClass.idsToGtkD(convParms.array[name]["Return"], convParms, aliases);
1116 
1117 							if (type[0 .. $-1] == typeWrap[0 .. $-2])
1118 							{
1119 								bd ~= "return p[0 .. "~ id ~"];";
1120 							}
1121 							else
1122 							{
1123 								bd ~= typeWrap ~" arr = new "~ typeWrap[0 .. $-2] ~"["~ id ~"];";
1124 								bd ~= "for(int i = 0; i < "~ id ~"; i++)";
1125 								bd ~= "{";
1126 								bd ~= "\tarr[i] = "~ construct(typeWrap[0 .. $-2]) ~"(cast("~ type.chomp("*") ~") p[i]);";
1127 								bd ~= "}";
1128 								bd ~= "";
1129 								bd ~= "return arr;";
1130 							}
1131 						}
1132 						/* What's with all the casting? */
1133 						/* A; Casting is needed because some GTK+
1134 						 *    functions can return void pointers. */
1135 						else
1136 							bd ~= "return " ~ construct(typeWrap) ~ "(cast(" ~ type ~ ") p);";
1137 						
1138 						return bd;
1139 					}
1140 				}
1141 			}
1142 		}
1143 
1144 		/* We should never reach here. */
1145 		assert(0, "A strange function body was requested.");
1146 	}	
1147 }