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.GirStruct; 19 20 import std.algorithm: among, sort, uniq, startsWith, endsWith, canFind; 21 import std.array : replace; 22 import std.conv; 23 import std.file : write; 24 import std.path: buildNormalizedPath; 25 import std.uni: toUpper, toLower; 26 import std.range; 27 import std.string: capitalize, splitLines, strip, chomp; 28 29 import gtd.GirConstant; 30 import gtd.GirField; 31 import gtd.GirFunction; 32 import gtd.GirPackage; 33 import gtd.GirType; 34 import gtd.GirVersion; 35 import gtd.GirWrapper; 36 import gtd.IndentedStringBuilder; 37 import gtd.LinkedHasMap: Map = LinkedHashMap; 38 import gtd.Log; 39 import gtd.XMLReader; 40 41 enum GirStructType : string 42 { 43 Class = "class", 44 Interface = "interface", 45 Record = "record", 46 Union = "union" 47 } 48 49 final class GirStruct 50 { 51 string name; 52 GirStructType type; 53 string doc; 54 string cType; 55 string parent; 56 string libVersion; 57 58 bool lookupClass = false; 59 bool lookupInterface = false; 60 bool lookupParent = false; /// is the parent set with the lookup file. 61 bool noCode = false; /// Only generate the C declarations. 62 bool noDeclaration = false; /// Don't generate a Declaration of the C struct. 63 bool noExternal = false; /// Don't generate a Declaration of the C struct. And don't generate the C function declarations. 64 bool noNamespace = false; /// Generate the functions as global functions. 65 string[string] structWrap; 66 string[string] aliases; 67 string[] lookupCode; 68 string[] lookupInterfaceCode; 69 70 string[] implements; 71 string[] imports; 72 GirField[] fields; 73 string[] virtualFunctions; 74 Map!(string, GirFunction) functions; 75 bool disguised = false; 76 77 GirWrapper wrapper; 78 GirPackage pack; 79 80 private GirStruct parentStruct; 81 82 this(GirWrapper wrapper, GirPackage pack) 83 { 84 this.wrapper = wrapper; 85 this.pack = pack; 86 } 87 88 GirStruct dup() 89 { 90 GirStruct copy = new GirStruct(wrapper, pack); 91 92 foreach ( i, field; this.tupleof ) 93 copy.tupleof[i] = field; 94 95 return copy; 96 } 97 98 pure string getFullyQualifiedDName() { 99 return "D"~ parentStruct.pack.name.capitalize() ~ parentStruct.name; 100 } 101 102 void parse(T)(XMLReader!T reader) 103 { 104 name = reader.front.attributes["name"]; 105 type = cast(GirStructType)reader.front.value; 106 107 if ( "c:type" in reader.front.attributes ) 108 cType = reader.front.attributes["c:type"]; 109 else if ( "glib:type-name" in reader.front.attributes ) 110 cType = reader.front.attributes["glib:type-name"]; 111 112 if ( "parent" in reader.front.attributes ) 113 parent = reader.front.attributes["parent"]; 114 if ( "version" in reader.front.attributes ) 115 { 116 libVersion = reader.front.attributes["version"]; 117 pack.checkVersion(libVersion); 118 } 119 120 if ( !parent.empty ) 121 { 122 if ( parent == "GObject.InitiallyUnowned" ) 123 parent = "GObject.Object"; 124 else if ( parent == "InitiallyUnowned" ) 125 parent = "Object"; 126 } 127 128 if ( pack && pack.name != "glib" && "glib:get-type" in reader.front.attributes && reader.front.attributes["glib:get-type"].endsWith("_get_type") ) 129 functions["get_type"] = getTypeFunction(reader.front.attributes["glib:get-type"]); 130 if ( auto disg = "disguised" in reader.front.attributes ) 131 disguised = *disg == "1"; 132 133 if ( reader.front.type == XMLNodeType.EmptyTag ) 134 return; 135 136 reader.popFront(); 137 138 while( !reader.empty && !reader.endTag("class", "interface", "record", "union") ) 139 { 140 switch(reader.front.value) 141 { 142 case "attribute": 143 //TODO: Do we need these attibutes? 144 //dbus.name ccode.ordering deprecated replacement. 145 reader.skipTag(); 146 break; 147 case "constant": 148 GirConstant constant = new GirConstant(wrapper, pack); 149 constant.parse(reader); 150 pack.collectedConstants[constant.name] = constant; 151 constant.name = name.toUpper() ~"_"~ constant.name; 152 break; 153 case "doc": 154 reader.popFront(); 155 doc ~= reader.front.value; 156 reader.popFront(); 157 break; 158 case "doc-deprecated": 159 reader.popFront(); 160 doc ~= "\n\nDeprecated: "~ reader.front.value; 161 reader.popFront(); 162 break; 163 case "field": 164 GirField field = new GirField(wrapper, this); 165 field.parse(reader); 166 fields ~= field; 167 break; 168 case "record": 169 GirField field = new GirField(wrapper, this); 170 GirStruct strct = new GirStruct(wrapper, null); 171 strct.parse(reader); 172 strct.cType = strct.cType.toUpper()[0..1] ~ strct.cType[1 .. $]; 173 field.gtkStruct = strct; 174 fields ~= field; 175 break; 176 case "union": 177 GirField field = new GirField(wrapper, this); 178 GirUnion uni = new GirUnion(wrapper); 179 uni.parse(reader); 180 field.gtkUnion = uni; 181 fields ~= field; 182 break; 183 case "callback": 184 GirFunction callback = new GirFunction(wrapper, null); 185 callback.parse(reader); 186 pack.collectedCallbacks[callback.name] = callback; 187 callback.name = name.toUpper() ~ callback.name; 188 break; 189 case "constructor": 190 case "method": 191 case "glib:signal": 192 if ( type == GirStructType.Record ) 193 type = GirStructType.Class; 194 goto case "function"; 195 case "function": 196 GirFunction func = new GirFunction(wrapper, this); 197 func.parse(reader); 198 if ( func.type == GirFunctionType.Signal ) 199 functions[func.name~"-signal"] = func; 200 else 201 functions[func.name] = func; 202 break; 203 case "virtual-method": 204 // Virtual methods in the gir file are mirrored 205 // as regular methods, so we only collect whitch are virtual; 206 virtualFunctions ~= reader.front.attributes["name"]; 207 reader.skipTag(); 208 break; 209 case "implements": 210 implements ~= reader.front.attributes["name"]; 211 break; 212 case "prerequisite": // Determines whitch base class the implementor of an interface must implement. 213 case "property": 214 case "source-position": 215 reader.skipTag(); 216 break; 217 default: 218 error("Unexpected tag: ", reader.front.value, " in GirStruct: ", name, reader); 219 } 220 221 reader.popFront(); 222 } 223 224 foreach( func; virtualFunctions ) 225 { 226 if ( auto vFunc = func in functions ) 227 vFunc.virtual = true; 228 } 229 230 if ( type == GirStructType.Union ) 231 { 232 GirField field = new GirField(wrapper, this); 233 GirUnion uni = new GirUnion(wrapper); 234 uni.fields = fields; 235 field.gtkUnion = uni; 236 fields = [field]; 237 238 //special case for "_Value__data__union" 239 if ( cType.empty ) 240 cType = name; 241 242 type = GirStructType.Record; 243 244 foreach ( funct; functions ) 245 { 246 if ( funct.type != GirFunctionType.Function ) 247 type = GirStructType.Class; 248 } 249 } 250 } 251 252 GirStruct getParent() 253 { 254 if ( !parentStruct ) 255 parentStruct = pack.getStruct(parent); 256 257 return parentStruct; 258 } 259 260 string[] getStructDeclaration() 261 { 262 if ( noExternal || cType.empty ) 263 return null; 264 265 string[] buff; 266 267 if ( doc !is null && wrapper.includeComments && type == GirStructType.Record ) 268 { 269 buff ~= "/**"; 270 foreach ( line; doc.splitLines() ) 271 buff ~= " * "~ line.strip(); 272 273 if ( libVersion ) 274 { 275 buff ~= " *"; 276 buff ~= " * Since: "~ libVersion; 277 } 278 279 buff ~= " */"; 280 } 281 282 if ( !fields.empty ) 283 { 284 buff ~= "struct "~ tokenToGtkD(cType, wrapper.aliasses, false); 285 buff ~= "{"; 286 buff ~= GirField.getFieldDeclarations(fields, wrapper); 287 buff ~= "}"; 288 } 289 else 290 { 291 buff ~= "struct "~ tokenToGtkD(cType, wrapper.aliasses, false) ~";"; 292 } 293 294 return buff; 295 } 296 297 void writeClass() 298 { 299 if ( noCode ) 300 return; 301 302 if ( type == GirStructType.Record && !(lookupClass || lookupInterface) && (functions.empty && lookupCode.empty ) ) 303 return; 304 305 parentStruct = pack.getStruct(parent); 306 resolveImports(); 307 308 if ( type == GirStructType.Record && !(lookupClass || lookupInterface) && !("get_type" in functions && isSimpleStruct()) ) 309 { 310 writeDStruct(); 311 return; 312 } 313 314 if ( isInterface() ) 315 writeInterface(); 316 317 string buff = wrapper.licence; 318 auto indenter = new IndentedStringBuilder(); 319 320 if ( isInterface() ) 321 buff ~= "module "~ pack.name ~"."~ name ~"T;\n\n"; 322 else 323 buff ~= "module "~ pack.name ~"."~ name ~";\n\n"; 324 325 writeImports(buff, isInterface() ); 326 writeDocs(buff); 327 328 if ( isInterface() ) 329 buff ~= "public template "~ name ~"T(TStruct)"; 330 else if ( isSimpleStruct() ) 331 buff ~= "public final class "~ name; 332 else 333 buff ~= "public class "~ name; 334 335 if ( lookupParent && !parentStruct ) 336 buff ~= " : "~ parent; 337 else if ( parentStruct && parentStruct.name != name ) 338 buff ~= " : "~ parentStruct.name; 339 else if ( parentStruct ) 340 buff ~= " : "~ getFullyQualifiedDName(); 341 342 bool first = !parentStruct; 343 344 foreach ( interf; implements ) 345 { 346 if ( parentStruct && parentStruct.implements.canFind(interf) ) 347 continue; 348 349 // If the parentStruct is in an different package compare without package name. 350 if ( parentStruct && interf.canFind(".") && parentStruct.implements.canFind(interf.split('.')[1]) ) 351 continue; 352 353 GirStruct strct = pack.getStruct(interf); 354 355 if ( strct && first ) 356 { 357 buff ~= " :"; 358 first = false; 359 } 360 else if ( strct ) 361 buff ~= ","; 362 363 if ( strct ) 364 buff ~= " "~ strct.name ~"IF"; 365 } 366 367 buff ~= "\n"; 368 buff ~= indenter.format("{"); 369 370 if ( !cType.empty ) 371 { 372 if ( !isInterface() ) 373 { 374 buff ~= indenter.format("/** the main Gtk struct */"); 375 buff ~= indenter.format("protected "~ cType ~"* "~ getHandleVar() ~";"); 376 377 if ( !parentStruct ) 378 { 379 buff ~= indenter.format("protected bool ownedRef;"); 380 } 381 382 buff ~= "\n"; 383 } 384 buff ~= indenter.format("/** Get the main Gtk struct */"); 385 buff ~= indenter.format("public "~ cType ~"* "~ getHandleFunc() ~"(bool transferOwnership = false)"); 386 buff ~= indenter.format("{"); 387 buff ~= indenter.format("if (transferOwnership)"); 388 buff ~= indenter.format("ownedRef = false;"); 389 390 if ( isInterface() ) 391 buff ~= indenter.format("return cast("~ cType ~"*)getStruct();"); 392 else 393 buff ~= indenter.format("return "~ getHandleVar ~";"); 394 395 buff ~= indenter.format("}"); 396 buff ~= "\n"; 397 398 if ( !isInterface() ) 399 { 400 buff ~= indenter.format("/** the main Gtk struct as a void* */"); 401 402 if ( parentStruct ) 403 buff ~= indenter.format("protected override void* getStruct()"); 404 else 405 buff ~= indenter.format("protected void* getStruct()"); 406 407 buff ~= indenter.format("{"); 408 buff ~= indenter.format("return cast(void*)"~ getHandleVar ~";"); 409 buff ~= indenter.format("}"); 410 buff ~= "\n"; 411 } 412 413 if ( !isInterface() && !hasDefaultConstructor() ) 414 { 415 buff ~= indenter.format("/**"); 416 buff ~= indenter.format(" * Sets our main struct and passes it to the parent class."); 417 buff ~= indenter.format(" */"); 418 419 buff ~= indenter.format("public this ("~ cType ~"* "~ getHandleVar() ~", bool ownedRef = false)"); 420 buff ~= indenter.format("{"); 421 buff ~= indenter.format("this."~ getHandleVar() ~" = "~ getHandleVar() ~";"); 422 423 if ( parentStruct ) 424 buff ~= indenter.format("super(cast("~ parentStruct.cType ~"*)"~ getHandleVar() ~", ownedRef);"); 425 else 426 buff ~= indenter.format("this.ownedRef = ownedRef;"); 427 428 buff ~= indenter.format("}"); 429 buff ~= "\n"; 430 431 if ( shouldFree() ) 432 { 433 buff ~= indenter.format("~this ()"); 434 buff ~= indenter.format("{"); 435 436 if ( wrapper.useRuntimeLinker ) 437 buff ~= indenter.format("if ( Linker.isLoaded(LIBRARY_"~ pack.name.replace(".","").toUpper() ~") && ownedRef )"); 438 else 439 buff ~= indenter.format("if ( ownedRef )"); 440 441 if ( "unref" in functions ) 442 buff ~= indenter.format(functions["unref"].cType ~"("~ getHandleVar ~");"); 443 else 444 buff ~= indenter.format(functions["free"].cType ~"("~ getHandleVar ~");"); 445 446 buff ~= indenter.format("}"); 447 buff ~= "\n"; 448 } 449 else if ( isSimpleStruct() ) 450 { 451 buff ~= indenter.format("~this ()"); 452 buff ~= indenter.format("{"); 453 454 if ( wrapper.useRuntimeLinker ) 455 buff ~= indenter.format("if ( Linker.isLoaded(LIBRARY_"~ pack.name.replace(".","").toUpper() ~") && ownedRef )"); 456 else 457 buff ~= indenter.format("if ( ownedRef )"); 458 459 buff ~= indenter.format("sliceFree("~ getHandleVar ~");"); 460 buff ~= indenter.format("}"); 461 buff ~= "\n"; 462 } 463 } 464 465 foreach ( interf; implements ) 466 { 467 if ( parentStruct && parentStruct.implements.canFind(interf) ) 468 continue; 469 470 if ( parentStruct && interf.canFind(".") && parentStruct.implements.canFind(interf.split('.')[1]) ) 471 continue; 472 473 GirStruct strct = pack.getStruct(interf); 474 475 if ( strct ) 476 { 477 buff ~= indenter.format("// add the "~ strct.name ~" capabilities"); 478 buff ~= indenter.format("mixin "~ strct.name ~"T!("~ cType.chomp("*") ~");"); 479 buff ~= "\n"; 480 } 481 } 482 483 } 484 485 if ( !lookupCode.empty ) 486 { 487 buff ~= indenter.format(lookupCode); 488 buff ~= "\n"; 489 490 buff ~= indenter.format(["/**", "*/"]); 491 } 492 493 if ( isSimpleStruct() ) 494 { 495 foreach( field; fields ) 496 { 497 if ( field.name.startsWith("dummy") ) 498 continue; 499 500 buff ~= "\n"; 501 buff ~= indenter.format(field.getProperty()); 502 } 503 } 504 505 foreach ( func; functions ) 506 { 507 if ( func.noCode || func.isVariadic() || func.type == GirFunctionType.Callback ) 508 continue; 509 510 if ( isInterface() && func.type == GirFunctionType.Constructor ) 511 continue; 512 513 if ( isInterface() && func.isStatic() ) 514 continue; 515 516 if ( func.type == GirFunctionType.Signal ) 517 { 518 buff ~= "\n"; 519 buff ~= indenter.format(func.getAddListenerDeclaration()); 520 buff ~= indenter.format(func.getAddListenerBody()); 521 522 foreach ( param; func.params ) 523 { 524 if ( param.type.name.startsWith("Gdk.Event") && param.type.name != "Gdk.Event" ) 525 { 526 buff ~= "\n"; 527 buff ~= indenter.format(getGenericEventSignal(func)); 528 529 break; 530 } 531 } 532 } 533 else 534 { 535 buff ~= "\n"; 536 537 if ( func.name.among("delete", "export", "foreach", "union") ) 538 buff ~= indenter.format("alias "~ func.name[0..$-1] ~" = "~ tokenToGtkD(func.name, wrapper.aliasses) ~";"); 539 else if ( func.name == "ref" ) 540 buff ~= indenter.format("alias doref = "~ tokenToGtkD(func.name, wrapper.aliasses) ~";"); 541 542 buff ~= indenter.format(func.getDeclaration()); 543 buff ~= indenter.format("{"); 544 buff ~= indenter.format(func.getBody()); 545 buff ~= indenter.format("}"); 546 } 547 } 548 549 buff ~= indenter.format("}"); 550 551 if ( isInterface() ) 552 wrapper.writeFile(buildNormalizedPath(wrapper.outputDir, pack.srcDir, pack.name.replace(".","/"), name ~"T.d"), buff); 553 else 554 wrapper.writeFile(buildNormalizedPath(wrapper.outputDir, pack.srcDir, pack.name.replace(".","/"), name ~".d"), buff); 555 } 556 557 void writeInterface() 558 { 559 string buff = wrapper.licence; 560 auto indenter = new IndentedStringBuilder(); 561 562 buff ~= "module "~ pack.name ~"."~ name ~"IF;\n\n"; 563 564 writeImports(buff); 565 writeDocs(buff); 566 567 buff ~= "public interface "~ name ~"IF"; 568 buff ~= indenter.format("{"); 569 570 if ( cType ) 571 { 572 buff ~= indenter.format("/** Get the main Gtk struct */"); 573 buff ~= indenter.format("public "~ cType ~"* "~ getHandleFunc() ~"(bool transferOwnership = false);"); 574 buff ~= "\n"; 575 576 buff ~= indenter.format("/** the main Gtk struct as a void* */"); 577 buff ~= indenter.format("protected void* getStruct();"); 578 buff ~= "\n"; 579 580 if ( !lookupInterfaceCode.empty ) 581 { 582 buff ~= indenter.format(lookupInterfaceCode); 583 buff ~= "\n"; 584 585 buff ~= indenter.format(["/**", "*/"]); 586 } 587 588 foreach ( func; functions ) 589 { 590 if ( func.noCode || func.isVariadic() || func.type == GirFunctionType.Callback || func.type == GirFunctionType.Constructor ) 591 continue; 592 593 if ( func.type == GirFunctionType.Signal ) 594 { 595 string[] dec = func.getAddListenerDeclaration(); 596 dec[$-1] ~= ";"; 597 598 buff ~= "\n"; 599 buff ~= indenter.format(dec); 600 } 601 else if ( !func.isStatic() ) 602 { 603 string[] dec = func.getDeclaration(); 604 dec[$-1] = dec[$-1].replace("override ", ""); 605 dec[$-1] ~= ";"; 606 607 buff ~= "\n"; 608 609 if ( func.name.among("delete", "export", "foreach", "union") ) 610 buff ~= indenter.format("alias "~ func.name[0..$-1] ~" = "~ tokenToGtkD(func.name, wrapper.aliasses) ~";"); 611 612 buff ~= indenter.format(dec); 613 } 614 else 615 { 616 buff ~= "\n"; 617 buff ~= indenter.format(func.getDeclaration()); 618 buff ~= indenter.format("{"); 619 buff ~= indenter.format(func.getBody()); 620 buff ~= indenter.format("}"); 621 } 622 } 623 624 buff ~= indenter.format("}"); 625 } 626 627 wrapper.writeFile(buildNormalizedPath(wrapper.outputDir, pack.srcDir, pack.name.replace(".","/"), name ~"IF.d"), buff); 628 } 629 630 void writeDStruct() 631 { 632 string buff = wrapper.licence; 633 auto indenter = new IndentedStringBuilder(); 634 635 buff ~= "module "~ pack.name ~"."~ name ~";\n\n"; 636 637 writeImports(buff); 638 writeDocs(buff); 639 640 if ( !noNamespace ) 641 { 642 buff ~= "public struct "~ name ~"\n"; 643 buff ~= indenter.format("{"); 644 } 645 646 if ( !lookupCode.empty ) 647 { 648 buff ~= indenter.format(lookupCode); 649 buff ~= "\n"; 650 651 buff ~= indenter.format(["/**", "*/"]); 652 } 653 654 foreach ( func; functions ) 655 { 656 if ( func.noCode || func.isVariadic() || !( func.type == GirFunctionType.Function || func.type == GirFunctionType.Method ) ) 657 continue; 658 659 buff ~= "\n"; 660 661 if ( func.name.among("delete", "export", "foreach", "union") ) 662 buff ~= indenter.format("alias "~ func.name[0..$-1] ~" = "~ tokenToGtkD(func.name, wrapper.aliasses) ~";"); 663 664 buff ~= indenter.format(func.getDeclaration()); 665 buff ~= indenter.format("{"); 666 buff ~= indenter.format(func.getBody()); 667 buff ~= indenter.format("}"); 668 } 669 670 if ( !noNamespace ) 671 buff ~= indenter.format("}"); 672 673 wrapper.writeFile(buildNormalizedPath(wrapper.outputDir, pack.srcDir, pack.name.replace(".","/"), name ~".d"), buff); 674 } 675 676 /** 677 * Return the variable name the c type is stored in. 678 */ 679 string getHandleVar() 680 { 681 if (cType.length == 0) 682 return ""; 683 684 string p = to!string(toLower(cType[0])); 685 if ( cType.endsWith("_t") ) 686 { 687 return p ~ cType[1 .. $ - 2]; 688 } else { 689 return p ~ cType[1 .. $]; 690 } 691 } 692 693 /** 694 * Returns the name of the function that returns the cType. 695 */ 696 string getHandleFunc() 697 { 698 if ( parent && !parentStruct ) 699 parentStruct = getParent(); 700 701 if ( parentStruct && parentStruct.name == name ) 702 return "get"~ cast(char)pack.name[0].toUpper ~ pack.name[1..$] ~ name ~"Struct"; 703 else 704 return "get"~ name ~"Struct"; 705 } 706 707 bool isInterface() 708 { 709 if ( lookupInterface ) 710 return true; 711 if ( lookupClass ) 712 return false; 713 if ( type == GirStructType.Interface ) 714 return true; 715 716 return false; 717 } 718 719 bool isNamespace() 720 { 721 return type == GirStructType.Record && !(lookupClass || lookupInterface) && !noNamespace; 722 } 723 724 void merge(GirStruct mergeStruct) 725 { 726 foreach ( func; mergeStruct.functions ) 727 { 728 func.strct = this; 729 functions[func.name] = func; 730 } 731 } 732 733 GirStruct getAncestor() 734 { 735 if ( parent.empty ) 736 return this; 737 738 if ( !parentStruct ) 739 parentStruct = pack.getStruct(parent); 740 741 return parentStruct.getAncestor(); 742 } 743 744 bool hasFunction(string funct) 745 { 746 if ( funct in functions ) 747 return true; 748 749 if ( parent.empty ) 750 return false; 751 752 if ( !parentStruct ) 753 parentStruct = pack.getStruct(parent); 754 755 if ( !parentStruct ) 756 return false; 757 758 return parentStruct.hasFunction(funct); 759 } 760 761 private bool hasDefaultConstructor() 762 { 763 foreach ( line; lookupCode ) 764 { 765 //TODO: Whitespace differences? 766 if ( line.strip == "public this ("~ cType ~"* "~ getHandleVar() ~", bool ownedRef = false)" ) 767 return true; 768 } 769 770 return false; 771 } 772 773 bool shouldFree() 774 { 775 if ( !parent.empty && parent != "Boxed" ) 776 return false; 777 if ( name.among("Object", "Boxed") ) 778 return false; 779 780 if ( auto u = "unref" in functions ) 781 { 782 if ( u.noCode == false && u.params.empty ) 783 return true; 784 } 785 786 if ( auto f = "free" in functions ) 787 { 788 if ( f.noCode == false && f.params.empty ) 789 return true; 790 } 791 return false; 792 } 793 794 bool isSimpleStruct() 795 { 796 //TODO: don't use this workaround. 797 //TODO: For TestLogMsg, GArray and GByteArray implement array properties that are not zero terminated. 798 if ( cType == "PangoAttribute" || cType == "GTestLogMsg" || cType == "GArray" || cType == "GByteArray" || cType == "GtkTreeIter" ) 799 return false; 800 801 if ( pack.name == "cairo" ) 802 return false; 803 804 if ( lookupClass || lookupInterface || noDeclaration || noNamespace ) 805 return false; 806 807 if ( disguised || fields.length == 0 ) 808 return false; 809 810 if ( !fields.empty && fields[0].type ) 811 { 812 // If the first field is wraped as a D class and isn't declared 813 // as a pointer we assume its the parent instance. 814 GirStruct dStruct = pack.getStruct(fields[0].type.name); 815 if ( dStruct && dStruct.isDClass() && !fields[0].type.cType.endsWith("*") ) 816 return false; 817 } 818 819 foreach ( field; fields ) 820 { 821 if ( !field.writable ) 822 return false; 823 } 824 825 return true; 826 } 827 828 bool isDClass() 829 { 830 if ( type.among(GirStructType.Class, GirStructType.Interface) ) 831 return true; 832 if ( type == GirStructType.Record && (lookupClass || lookupInterface) ) 833 return true; 834 if ( "get_type" in functions && isSimpleStruct() ) 835 return true; 836 837 return false; 838 } 839 840 string[] usedNamespaces() 841 { 842 string[] namespaces; 843 844 string getNamespace(GirType type) 845 { 846 if ( type.isArray() ) 847 type = type.elementType; 848 849 if ( type.cType in wrapper.aliasses || type.cType in aliases ) 850 return null; 851 852 if ( type.name.canFind(".") ) 853 return type.name.split(".")[0]; 854 855 return null; 856 } 857 858 if ( parent.canFind(".") ) 859 namespaces ~= parent.split(".")[0]; 860 861 foreach ( func; functions ) 862 { 863 namespaces ~= getNamespace(func.returnType); 864 if ( func.instanceParam ) 865 namespaces ~= getNamespace(func.instanceParam.type); 866 foreach ( param; func.params ) 867 namespaces ~= getNamespace(param.type); 868 } 869 870 return namespaces.sort().uniq.array; 871 } 872 873 private void resolveImports() 874 { 875 if ( parentStruct && parentStruct.name != name) 876 { 877 imports ~= parentStruct.pack.name ~"."~ parentStruct.name; 878 } 879 else if ( parentStruct ) 880 { 881 string QParent = getFullyQualifiedDName(); 882 imports ~= parentStruct.pack.name ~"."~ parentStruct.name ~" : "~ QParent ~" = "~ parentStruct.name; 883 structWrap[parent] = QParent; 884 } 885 886 imports ~= pack.name ~".c.functions"; 887 imports ~= pack.name ~".c.types"; 888 889 //Temporarily import the old bindDir.*types modules for backwards compatibility. 890 const string[string] bindDirs = ["atk": "gtkc", "cairo": "gtkc", "gdk": "gtkc", "gdkpixbuf": "gtkc", 891 "gio": "gtkc", "glib": "gtkc", "gobject": "gtkc", "gtk": "gtkc", "pango": "gtkc", "gsv": "gsvc", 892 "vte": "vtec", "gstinterfaces": "gstreamerc", "gstreamer": "gstreamerc"]; 893 894 if ( pack.wrapper.useBindDir ) 895 { 896 if ( auto dir = pack.name in bindDirs ) 897 imports ~= *dir ~"."~ pack.name ~"types"; 898 } 899 900 if ( isSimpleStruct() ) 901 imports ~= "glib.MemorySlice"; 902 903 if ( wrapper.useRuntimeLinker && (shouldFree() || isSimpleStruct()) ) 904 imports ~= "gtkd.Loader"; 905 906 if ( isSimpleStruct() ) 907 { 908 foreach ( field; fields ) 909 { 910 if ( field.type.name in structWrap || field.type.name in aliases ) 911 continue; 912 913 GirStruct dType; 914 915 if ( field.type.isArray() ) 916 dType = pack.getStruct(field.type.elementType.name); 917 else 918 dType = pack.getStruct(field.type.name); 919 920 if ( dType is this ) 921 continue; 922 923 if ( dType && dType.isDClass() ) 924 { 925 if ( !dType.pack.name.among("cairo", "glib", "gthread") ) 926 imports ~= "gobject.ObjectG"; 927 928 if ( dType.type == GirStructType.Interface || dType.lookupInterface ) 929 imports ~= dType.pack.name ~"."~ dType.name ~"IF"; 930 else 931 imports ~= dType.pack.name ~"."~ dType.name; 932 } 933 else if ( field.type.isString() || (field.type.isArray() && field.type.elementType.isString()) ) 934 imports ~= "glib.Str"; 935 } 936 } 937 938 foreach( func; functions ) 939 { 940 if ( func.noCode ) 941 continue; 942 943 if ( func.throws ) 944 { 945 imports ~= "glib.ErrorG"; 946 imports ~= "glib.GException"; 947 } 948 949 void getReturnImport(GirType type) 950 { 951 if ( type.name in structWrap || type.name in aliases ) 952 return; 953 954 GirStruct dType = pack.getStruct(type.name); 955 956 if ( dType && dType.isDClass() ) 957 { 958 if ( !dType.pack.name.among("cairo", "glib", "gthread") ) 959 imports ~= "gobject.ObjectG"; 960 961 if ( dType.type == GirStructType.Interface && func.name.startsWith("new") ) 962 return; 963 964 if ( dType is this && dType.type != GirStructType.Interface ) 965 return; 966 967 if ( dType.type == GirStructType.Interface || dType.lookupInterface ) 968 imports ~= dType.pack.name ~"."~ dType.name ~"IF"; 969 else 970 imports ~= dType.pack.name ~"."~ dType.name; 971 } 972 else if ( type.name.among("utf8", "filename") || type.cType.among("guchar**") ) 973 imports ~= "glib.Str"; 974 } 975 976 if ( func.returnType && func.returnType.cType !in structWrap ) 977 { 978 getReturnImport(func.returnType); 979 980 if ( func.returnType.isArray() ) 981 getReturnImport(func.returnType.elementType); 982 } 983 984 void getParamImport(GirType type) 985 { 986 if ( type.name in structWrap || type.name in aliases ) 987 return; 988 989 GirStruct dType = pack.getStruct(type.name); 990 991 if ( dType is this ) 992 return; 993 994 if ( func.type == GirFunctionType.Signal && type.name.startsWith("Gdk.Event") ) 995 imports ~= "gdk.Event"; 996 997 if ( dType && dType.isDClass() ) 998 { 999 if ( dType.type == GirStructType.Interface || dType.lookupInterface ) 1000 imports ~= dType.pack.name ~"."~ dType.name ~"IF"; 1001 else 1002 imports ~= dType.pack.name ~"."~ dType.name; 1003 } 1004 else if ( type.isString() || (type.isArray() && type.elementType.isString()) ) 1005 imports ~= "glib.Str"; 1006 } 1007 1008 foreach ( param; func.params ) 1009 { 1010 if ( param.type.cType in structWrap ) 1011 continue; 1012 1013 getParamImport(param.type); 1014 1015 if ( param.type.elementType ) 1016 getParamImport(param.type.elementType); 1017 1018 if ( param.direction != GirParamDirection.Default ) 1019 getReturnImport(param.type); 1020 1021 if ( param.direction == GirParamDirection.Out 1022 && !param.type.cType.endsWith("**") 1023 && pack.getStruct(param.type.name) !is null 1024 && pack.getStruct(param.type.name).isDClass() ) 1025 imports ~= "glib.MemorySlice"; 1026 } 1027 1028 if ( func.type == GirFunctionType.Signal ) 1029 { 1030 imports ~= "std.algorithm"; 1031 imports ~= "gobject.Signals"; 1032 } 1033 1034 if ( func.type == GirFunctionType.Constructor ) 1035 imports ~= "glib.ConstructionException"; 1036 } 1037 1038 foreach ( interf; implements ) 1039 { 1040 if ( parentStruct && parentStruct.implements.canFind(interf) ) 1041 continue; 1042 1043 GirStruct strct = pack.getStruct(interf); 1044 1045 if ( strct ) 1046 { 1047 imports ~= strct.pack.name ~"."~ strct.name ~"IF"; 1048 imports ~= strct.pack.name ~"."~ strct.name ~"T"; 1049 } 1050 } 1051 1052 imports = uniq(sort(imports)).array; 1053 } 1054 1055 private void writeImports(ref string buff, bool _public = false) 1056 { 1057 foreach ( imp; imports ) 1058 { 1059 if ( _public || imp.endsWith("types") ) 1060 buff ~= "public import "~ imp ~";\n"; 1061 else 1062 buff ~= "private import "~ imp ~";\n"; 1063 } 1064 1065 buff ~= "\n\n"; 1066 } 1067 1068 private void writeDocs(ref string buff) 1069 { 1070 if ( doc !is null && wrapper.includeComments ) 1071 { 1072 buff ~= "/**\n"; 1073 foreach ( line; doc.splitLines() ) 1074 buff ~= " * "~ line.strip() ~"\n"; 1075 1076 if ( libVersion ) 1077 { 1078 buff ~= " *\n * Since: "~ libVersion ~"\n"; 1079 } 1080 1081 buff ~= " */\n"; 1082 } 1083 else if ( wrapper.includeComments ) 1084 { 1085 buff ~= "/** */\n"; 1086 } 1087 } 1088 1089 private GirFunction getTypeFunction(string cIdentifier) 1090 { 1091 GirType returnType = new GirType(wrapper); 1092 returnType.name = "GObject.GType"; 1093 returnType.cType = "GType"; 1094 1095 GirFunction func = new GirFunction(wrapper, this); 1096 func.type = GirFunctionType.Function; 1097 func.name = "get_type"; 1098 func.cType = cIdentifier; 1099 func.returnType = returnType; 1100 1101 return func; 1102 } 1103 1104 /** 1105 * Get an overload of events that accept an generic Gdk Event 1106 * instead of the spesific type listed in the gir files. 1107 * 1108 * This for backwards compatibility with the documentation based wrapper. 1109 */ 1110 private string[] getGenericEventSignal(GirFunction func) 1111 { 1112 GirFunction signal = func.dup(); 1113 string[] buff; 1114 1115 for ( size_t i; i < signal.params.length; i++ ) 1116 { 1117 if ( signal.params[i].type.name.startsWith("Gdk.Event") ) 1118 { 1119 GirType eventType = new GirType(wrapper); 1120 eventType.name = "Gdk.Event"; 1121 1122 GirParam newParam = new GirParam(wrapper); 1123 newParam.name = signal.params[i].name; 1124 newParam.doc = signal.params[i].doc; 1125 newParam.type = eventType; 1126 1127 signal.params[i] = newParam; 1128 1129 break; 1130 } 1131 } 1132 1133 buff ~= signal.getAddListenerDeclaration(); 1134 buff ~= signal.getAddListenerBody(); 1135 1136 return buff; 1137 } 1138 } 1139 1140 final class GirUnion 1141 { 1142 string name; 1143 string doc; 1144 GirField[] fields; 1145 1146 GirWrapper wrapper; 1147 1148 this(GirWrapper wrapper) 1149 { 1150 this.wrapper = wrapper; 1151 } 1152 1153 void parse(T)(XMLReader!T reader) 1154 { 1155 if ( "name" in reader.front.attributes ) 1156 name = reader.front.attributes["name"]; 1157 1158 reader.popFront(); 1159 1160 while( !reader.empty && !reader.endTag("union") ) 1161 { 1162 switch(reader.front.value) 1163 { 1164 case "doc": 1165 reader.popFront(); 1166 doc ~= reader.front.value; 1167 reader.popFront(); 1168 break; 1169 case "doc-deprecated": 1170 reader.popFront(); 1171 doc ~= "\n\nDeprecated: "~ reader.front.value; 1172 reader.popFront(); 1173 break; 1174 case "field": 1175 GirField field = new GirField(wrapper, null); 1176 field.parse(reader); 1177 fields ~= field; 1178 break; 1179 case "record": 1180 GirField field = new GirField(wrapper, null); 1181 GirStruct strct = new GirStruct(wrapper, null); 1182 strct.parse(reader); 1183 strct.cType = strct.cType.toUpper()[0..1] ~ strct.cType[1 .. $]; 1184 field.gtkStruct = strct; 1185 fields ~= field; 1186 break; 1187 case "source-position": 1188 reader.skipTag(); 1189 break; 1190 default: 1191 error("Unexpected tag: ", reader.front.value, " in GirUnion: ", name, reader); 1192 } 1193 reader.popFront(); 1194 } 1195 } 1196 1197 string[] getUnionDeclaration() 1198 { 1199 string[] buff; 1200 if ( doc !is null && wrapper.includeComments ) 1201 { 1202 buff ~= "/**"; 1203 foreach ( line; doc.splitLines() ) 1204 buff ~= " * "~ line.strip(); 1205 buff ~= " */"; 1206 } 1207 1208 if ( name ) 1209 buff ~= "union "~ tokenToGtkD(name.toUpper()[0..1] ~ name[1 .. $], wrapper.aliasses); 1210 else 1211 buff ~= "union"; 1212 1213 buff ~= "{"; 1214 buff ~= GirField.getFieldDeclarations(fields, wrapper); 1215 buff ~= "}"; 1216 1217 if ( name ) 1218 buff ~= tokenToGtkD(name.toUpper()[0..1] ~ name[1 .. $], wrapper.aliasses) ~" "~ tokenToGtkD(name.toLower(), wrapper.aliasses) ~";"; 1219 1220 return buff; 1221 } 1222 }