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