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 warning("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 imports ~= "glib.c.functions"; 936 } 937 } 938 } 939 940 foreach( func; functions ) 941 { 942 if ( func.noCode ) 943 continue; 944 945 if ( func.throws ) 946 { 947 imports ~= "glib.ErrorG"; 948 imports ~= "glib.GException"; 949 } 950 951 void getReturnImport(GirType type) 952 { 953 if ( type.name in structWrap || type.name in aliases ) 954 return; 955 956 GirStruct dType = pack.getStruct(type.name); 957 958 if ( dType && dType.isDClass() ) 959 { 960 if ( !dType.pack.name.among("cairo", "glib", "gthread") ) 961 imports ~= "gobject.ObjectG"; 962 963 if ( dType.type == GirStructType.Interface && func.name.startsWith("new") ) 964 return; 965 966 if ( dType is this && dType.type != GirStructType.Interface ) 967 return; 968 969 if ( dType.type == GirStructType.Interface || dType.lookupInterface ) 970 imports ~= dType.pack.name ~"."~ dType.name ~"IF"; 971 else 972 imports ~= dType.pack.name ~"."~ dType.name; 973 } 974 else if ( type.name.among("utf8", "filename") || type.cType.among("guchar**") ) { 975 imports ~= "glib.Str"; 976 imports ~= "glib.c.functions"; 977 } 978 } 979 980 if ( func.returnType && func.returnType.cType !in structWrap ) 981 { 982 getReturnImport(func.returnType); 983 984 if ( func.returnType.isArray() ) 985 getReturnImport(func.returnType.elementType); 986 } 987 988 void getParamImport(GirType type) 989 { 990 if ( type.name in structWrap || type.name in aliases ) 991 return; 992 993 GirStruct dType = pack.getStruct(type.name); 994 995 if ( dType is this ) 996 return; 997 998 if ( func.type == GirFunctionType.Signal && type.name.startsWith("Gdk.Event") ) 999 imports ~= "gdk.Event"; 1000 1001 if ( dType && dType.isDClass() ) 1002 { 1003 if ( dType.type == GirStructType.Interface || dType.lookupInterface ) 1004 imports ~= dType.pack.name ~"."~ dType.name ~"IF"; 1005 else 1006 imports ~= dType.pack.name ~"."~ dType.name; 1007 } 1008 else if ( type.isString() || (type.isArray() && type.elementType.isString()) ) 1009 imports ~= "glib.Str"; 1010 } 1011 1012 foreach ( param; func.params ) 1013 { 1014 if ( param.type.cType in structWrap ) 1015 continue; 1016 1017 getParamImport(param.type); 1018 1019 if ( param.type.elementType ) 1020 getParamImport(param.type.elementType); 1021 1022 if ( param.direction != GirParamDirection.Default ) 1023 getReturnImport(param.type); 1024 1025 if ( param.direction == GirParamDirection.Out 1026 && !param.type.cType.endsWith("**") 1027 && pack.getStruct(param.type.name) !is null 1028 && pack.getStruct(param.type.name).isDClass() ) 1029 imports ~= "glib.MemorySlice"; 1030 1031 if ( param.direction == GirParamDirection.Out 1032 && param.type.elementType 1033 && pack.getStruct(param.type.elementType.name) !is null 1034 && pack.getStruct(param.type.elementType.name).isDClass() 1035 && param.type.size > 0 ) 1036 imports ~= "glib.MemorySlice"; 1037 } 1038 1039 if ( func.type == GirFunctionType.Signal ) 1040 { 1041 imports ~= "std.algorithm"; 1042 imports ~= "gobject.Signals"; 1043 } 1044 1045 if ( func.type == GirFunctionType.Constructor ) 1046 imports ~= "glib.ConstructionException"; 1047 } 1048 1049 foreach ( interf; implements ) 1050 { 1051 if ( parentStruct && parentStruct.implements.canFind(interf) ) 1052 continue; 1053 1054 GirStruct strct = pack.getStruct(interf); 1055 1056 if ( strct ) 1057 { 1058 imports ~= strct.pack.name ~"."~ strct.name ~"IF"; 1059 imports ~= strct.pack.name ~"."~ strct.name ~"T"; 1060 } 1061 } 1062 1063 imports = uniq(sort(imports)).array; 1064 } 1065 1066 private void writeImports(ref string buff, bool _public = false) 1067 { 1068 foreach ( imp; imports ) 1069 { 1070 if ( _public || imp.endsWith("types") ) 1071 buff ~= "public import "~ imp ~";\n"; 1072 else 1073 buff ~= "private import "~ imp ~";\n"; 1074 } 1075 1076 buff ~= "\n\n"; 1077 } 1078 1079 private void writeDocs(ref string buff) 1080 { 1081 if ( doc !is null && wrapper.includeComments ) 1082 { 1083 buff ~= "/**\n"; 1084 foreach ( line; doc.splitLines() ) 1085 buff ~= " * "~ line.strip() ~"\n"; 1086 1087 if ( libVersion ) 1088 { 1089 buff ~= " *\n * Since: "~ libVersion ~"\n"; 1090 } 1091 1092 buff ~= " */\n"; 1093 } 1094 else if ( wrapper.includeComments ) 1095 { 1096 buff ~= "/** */\n"; 1097 } 1098 } 1099 1100 private GirFunction getTypeFunction(string cIdentifier) 1101 { 1102 GirType returnType = new GirType(wrapper); 1103 returnType.name = "GObject.GType"; 1104 returnType.cType = "GType"; 1105 1106 GirFunction func = new GirFunction(wrapper, this); 1107 func.type = GirFunctionType.Function; 1108 func.name = "get_type"; 1109 func.cType = cIdentifier; 1110 func.returnType = returnType; 1111 1112 return func; 1113 } 1114 1115 /** 1116 * Get an overload of events that accept an generic Gdk Event 1117 * instead of the spesific type listed in the gir files. 1118 * 1119 * This for backwards compatibility with the documentation based wrapper. 1120 */ 1121 private string[] getGenericEventSignal(GirFunction func) 1122 { 1123 GirFunction signal = func.dup(); 1124 string[] buff; 1125 1126 for ( size_t i; i < signal.params.length; i++ ) 1127 { 1128 if ( signal.params[i].type.name.startsWith("Gdk.Event") ) 1129 { 1130 GirType eventType = new GirType(wrapper); 1131 eventType.name = "Gdk.Event"; 1132 1133 GirParam newParam = new GirParam(wrapper); 1134 newParam.name = signal.params[i].name; 1135 newParam.doc = signal.params[i].doc; 1136 newParam.type = eventType; 1137 1138 signal.params[i] = newParam; 1139 1140 break; 1141 } 1142 } 1143 1144 buff ~= signal.getAddListenerDeclaration(); 1145 buff ~= signal.getAddListenerBody(); 1146 1147 return buff; 1148 } 1149 } 1150 1151 final class GirUnion 1152 { 1153 string name; 1154 string doc; 1155 GirField[] fields; 1156 1157 GirWrapper wrapper; 1158 1159 this(GirWrapper wrapper) 1160 { 1161 this.wrapper = wrapper; 1162 } 1163 1164 void parse(T)(XMLReader!T reader) 1165 { 1166 if ( "name" in reader.front.attributes ) 1167 name = reader.front.attributes["name"]; 1168 1169 reader.popFront(); 1170 1171 while( !reader.empty && !reader.endTag("union") ) 1172 { 1173 switch(reader.front.value) 1174 { 1175 case "doc": 1176 reader.popFront(); 1177 doc ~= reader.front.value; 1178 reader.popFront(); 1179 break; 1180 case "doc-deprecated": 1181 reader.popFront(); 1182 doc ~= "\n\nDeprecated: "~ reader.front.value; 1183 reader.popFront(); 1184 break; 1185 case "field": 1186 GirField field = new GirField(wrapper, null); 1187 field.parse(reader); 1188 fields ~= field; 1189 break; 1190 case "record": 1191 GirField field = new GirField(wrapper, null); 1192 GirStruct strct = new GirStruct(wrapper, null); 1193 strct.parse(reader); 1194 strct.cType = strct.cType.toUpper()[0..1] ~ strct.cType[1 .. $]; 1195 field.gtkStruct = strct; 1196 fields ~= field; 1197 break; 1198 case "source-position": 1199 reader.skipTag(); 1200 break; 1201 default: 1202 warning("Unexpected tag: ", reader.front.value, " in GirUnion: ", name, reader); 1203 } 1204 reader.popFront(); 1205 } 1206 } 1207 1208 string[] getUnionDeclaration() 1209 { 1210 string[] buff; 1211 if ( doc !is null && wrapper.includeComments ) 1212 { 1213 buff ~= "/**"; 1214 foreach ( line; doc.splitLines() ) 1215 buff ~= " * "~ line.strip(); 1216 buff ~= " */"; 1217 } 1218 1219 if ( name ) 1220 buff ~= "union "~ tokenToGtkD(name.toUpper()[0..1] ~ name[1 .. $], wrapper.aliasses); 1221 else 1222 buff ~= "union"; 1223 1224 buff ~= "{"; 1225 buff ~= GirField.getFieldDeclarations(fields, wrapper); 1226 buff ~= "}"; 1227 1228 if ( name ) 1229 buff ~= tokenToGtkD(name.toUpper()[0..1] ~ name[1 .. $], wrapper.aliasses) ~" "~ tokenToGtkD(name.toLower(), wrapper.aliasses) ~";"; 1230 1231 return buff; 1232 } 1233 }