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