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 assert(false, "Unexpected tag: "~ reader.front.value); 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 422 buff ~= indenter.format(["/**", "*/"]); 423 bool firstSignal = true; 424 425 foreach ( func; functions ) 426 { 427 if ( func.noCode || func.isVariadic() || func.type == GtkFunctionType.Callback ) 428 continue; 429 430 if ( isInterface() && func.type == GtkFunctionType.Constructor ) 431 continue; 432 433 if ( func.type == GtkFunctionType.Signal ) 434 { 435 buff ~= "\n"; 436 437 if ( firstSignal ) 438 { 439 buff ~= indenter.format("int[string] connectedSignals;"); 440 buff ~= "\n"; 441 firstSignal = false; 442 } 443 444 if ( isInterface() ) 445 { 446 string[] prop; 447 448 prop ~= func.getDelegateDecleration() ~"[] _on"~ func.getSignalName() ~"Listeners;"; 449 prop ~= "@property "~ func.getDelegateDecleration() ~"[] on"~ func.getSignalName() ~"Listeners()"; 450 prop ~= "{"; 451 prop ~= "return _on"~ func.getSignalName() ~"Listeners;"; 452 prop ~= "}"; 453 454 buff ~= indenter.format(prop); 455 } 456 else 457 { 458 buff ~= indenter.format(func.getDelegateDecleration() ~"[] on"~ func.getSignalName() ~"Listeners;"); 459 } 460 461 buff ~= indenter.format(func.getAddListenerdeclaration()); 462 buff ~= indenter.format(func.getAddListenerBody()); 463 buff ~= indenter.format(func.getSignalCallback()); 464 465 foreach ( param; func.params ) 466 { 467 if ( param.type.name.startsWith("Gdk.Event") && param.type.name != "Gdk.Event" ) 468 { 469 buff ~= "\n"; 470 buff ~= indenter.format(getGenericEventSignal(func)); 471 472 break; 473 } 474 } 475 } 476 else 477 { 478 buff ~= "\n"; 479 buff ~= indenter.format(func.getDeclaration()); 480 buff ~= indenter.format("{"); 481 buff ~= indenter.format(func.getBody()); 482 buff ~= indenter.format("}"); 483 } 484 } 485 486 buff ~= indenter.format("}"); 487 488 if ( isInterface() ) 489 std.file.write(buildPath(wrapper.outputRoot, pack.srcDir, pack.name, name ~"T.d"), buff); 490 else 491 std.file.write(buildPath(wrapper.outputRoot, pack.srcDir, pack.name, name ~".d"), buff); 492 } 493 494 void writeInterface() 495 { 496 string buff = wrapper.licence; 497 auto indenter = new IndentedStringBuilder(); 498 499 buff ~= "module "~ pack.name ~"."~ name ~"IF;\n\n"; 500 501 writeImports(buff); 502 writeDocs(buff); 503 504 buff ~= "public interface "~ name ~"IF"; 505 buff ~= indenter.format("{"); 506 507 if ( cType ) 508 { 509 buff ~= indenter.format("/** Get the main Gtk struct */"); 510 buff ~= indenter.format("public "~ cType ~"* "~ getHandleFunc() ~"();"); 511 buff ~= "\n"; 512 513 buff ~= indenter.format("/** the main Gtk struct as a void* */"); 514 buff ~= indenter.format("protected void* getStruct();"); 515 buff ~= "\n"; 516 517 if ( !lookupInterfaceCode.empty ) 518 { 519 buff ~= indenter.format(lookupInterfaceCode); 520 buff ~= "\n"; 521 } 522 523 buff ~= indenter.format(["/**", "*/"]); 524 525 foreach ( func; functions ) 526 { 527 if ( func.noCode || func.isVariadic() || func.type == GtkFunctionType.Callback || func.type == GtkFunctionType.Constructor ) 528 continue; 529 530 if ( func.type == GtkFunctionType.Signal ) 531 { 532 buff ~= indenter.format("@property "~ func.getDelegateDecleration() ~"[] on"~ func.getSignalName() ~"Listeners();"); 533 string[] dec = func.getAddListenerdeclaration(); 534 dec[$-1] ~= ";"; 535 536 buff ~= indenter.format(dec); 537 buff ~= "\n"; 538 } 539 else 540 { 541 string[] dec = func.getDeclaration(); 542 dec[$-1] = dec[$-1].replace("override ", ""); 543 dec[$-1] ~= ";"; 544 545 buff ~= "\n"; 546 buff ~= indenter.format(dec); 547 } 548 } 549 550 buff ~= indenter.format("}"); 551 } 552 553 std.file.write(buildPath(wrapper.outputRoot, pack.srcDir, pack.name, name ~"IF.d"), buff); 554 } 555 556 void writeDStruct() 557 { 558 string buff = wrapper.licence; 559 auto indenter = new IndentedStringBuilder(); 560 561 buff ~= "module "~ pack.name ~"."~ name ~";\n\n"; 562 563 writeImports(buff); 564 writeDocs(buff); 565 566 if ( !noNamespace ) 567 { 568 buff ~= "public struct "~ name ~"\n"; 569 buff ~= indenter.format("{"); 570 } 571 572 if ( !lookupCode.empty ) 573 { 574 buff ~= indenter.format(lookupCode); 575 buff ~= "\n"; 576 } 577 578 buff ~= indenter.format(["/**", "*/"]); 579 580 foreach ( func; functions ) 581 { 582 if ( func.noCode || func.isVariadic() || !( func.type == GtkFunctionType.Function || func.type == GtkFunctionType.Method ) ) 583 continue; 584 585 buff ~= "\n"; 586 buff ~= indenter.format(func.getDeclaration()); 587 buff ~= indenter.format("{"); 588 buff ~= indenter.format(func.getBody()); 589 buff ~= indenter.format("}"); 590 } 591 592 if ( !noNamespace ) 593 buff ~= indenter.format("}"); 594 595 std.file.write(buildPath(wrapper.outputRoot, pack.srcDir, pack.name, name ~".d"), buff); 596 } 597 598 /** 599 * Return the variable name the c type is stored in. 600 */ 601 string getHandleVar() 602 { 603 if (cType.length == 0) 604 return ""; 605 606 string p = to!string(toLower(cType[0])); 607 if ( cType.endsWith("_t") ) 608 { 609 return p ~ cType[1 .. $ - 2]; 610 } else { 611 return p ~ cType[1 .. $]; 612 } 613 } 614 615 /** 616 * Returns the name of the function that returns the cType. 617 */ 618 string getHandleFunc() 619 { 620 if ( parentStruct && parentStruct.name == name ) 621 return "get"~ cast(char)pack.name[0].toUpper ~ pack.name[1..$] ~ name ~"Struct"; 622 else 623 return "get"~ name ~"Struct"; 624 } 625 626 bool isInterface() 627 { 628 if ( lookupInterface ) 629 return true; 630 if ( lookupClass ) 631 return false; 632 if ( type == GtkStructType.Interface ) 633 return true; 634 635 return false; 636 } 637 638 bool isNamespace() 639 { 640 return type == GtkStructType.Record && !(lookupClass || lookupInterface) && !noNamespace; 641 } 642 643 void merge(GtkStruct mergeStruct) 644 { 645 foreach ( func; mergeStruct.functions ) 646 { 647 func.strct = this; 648 functions[func.name] = func; 649 } 650 } 651 652 GtkStruct getAncestor() 653 { 654 if ( parent.empty ) 655 return this; 656 657 if ( !parentStruct ) 658 parentStruct = pack.getStruct(parent); 659 660 return parentStruct.getAncestor(); 661 } 662 663 private void resolveImports() 664 { 665 if ( parentStruct && parentStruct.name != name) 666 { 667 imports ~= parentStruct.pack.name ~"."~ parentStruct.name; 668 } 669 else if ( parentStruct ) 670 { 671 string QParent = parentStruct.pack.name.capitalize() ~ parentStruct.name; 672 imports ~= parentStruct.pack.name ~"."~ parentStruct.name ~" : "~ QParent ~" = "~ parentStruct.name; 673 structWrap[parent] = QParent; 674 } 675 676 imports ~= pack.bindDir ~"."~ pack.name; 677 imports ~= pack.bindDir ~"."~ pack.name ~"types"; 678 679 foreach( func; functions ) 680 { 681 if ( func.noCode ) 682 continue; 683 684 if ( func.throws ) 685 { 686 imports ~= "glib.ErrorG"; 687 imports ~= "glib.GException"; 688 } 689 690 void getReturnImport(GtkType type) 691 { 692 if ( type.name in structWrap || type.name in aliases ) 693 return; 694 695 GtkStruct dType = pack.getStruct(type.name); 696 697 if ( dType && (dType.type != GtkStructType.Record || dType.lookupClass || dType.lookupInterface) ) 698 { 699 if ( !dType.pack.name.among("cairo", "glib", "gthread") ) 700 imports ~= "gobject.ObjectG"; 701 702 if ( dType.type == GtkStructType.Interface && func.name.startsWith("new") ) 703 return; 704 705 if ( dType is this && dType.type != GtkStructType.Interface ) 706 return; 707 708 imports ~= dType.pack.name ~"."~ dType.name; 709 710 if ( dType.type == GtkStructType.Interface || dType.lookupInterface ) 711 imports ~= dType.pack.name ~"."~ dType.name ~"IF"; 712 } 713 else if ( type.name == "utf8" || type.cType.among("guchar**") ) 714 imports ~= "glib.Str"; 715 } 716 717 if ( func.returnType && func.returnType.cType !in structWrap ) 718 { 719 getReturnImport(func.returnType); 720 721 if ( func.returnType.isArray() ) 722 getReturnImport(func.returnType.elementType); 723 } 724 725 void getParamImport(GtkType type) 726 { 727 if ( type.name in structWrap || type.name in aliases ) 728 return; 729 730 GtkStruct dType = pack.getStruct(type.name); 731 732 if ( dType is this ) 733 return; 734 735 if ( func.type == GtkFunctionType.Signal && type.name.startsWith("Gdk.Event") ) 736 imports ~= "gdk.Event"; 737 738 if ( dType && (dType.type != GtkStructType.Record || dType.lookupClass || dType.lookupInterface) ) 739 { 740 if ( dType.type == GtkStructType.Interface || dType.lookupInterface ) 741 { 742 imports ~= dType.pack.name ~"."~ dType.name ~"IF"; 743 744 if ( func.type == GtkFunctionType.Signal ) 745 imports ~= dType.pack.name ~"."~ dType.name; 746 } 747 else 748 { 749 imports ~= dType.pack.name ~"."~ dType.name; 750 } 751 } 752 else if ( type.name == "utf8" || type.cType.among("guchar**") ) 753 imports ~= "glib.Str"; 754 } 755 756 foreach ( param; func.params ) 757 { 758 if ( param.type.cType in structWrap ) 759 continue; 760 761 getParamImport(param.type); 762 763 if ( param.type.elementType ) 764 getParamImport(param.type.elementType); 765 766 if ( param.direction != GtkParamDirection.Default ) 767 getReturnImport(param.type); 768 } 769 770 if ( func.type == GtkFunctionType.Signal ) 771 { 772 imports ~= "gobject.Signals"; 773 imports ~= "gtkc.gdktypes"; 774 } 775 776 if ( func.type == GtkFunctionType.Constructor ) 777 imports ~= "glib.ConstructionException"; 778 } 779 780 foreach ( interf; implements ) 781 { 782 if ( parentStruct && parentStruct.implements.canFind(interf) ) 783 continue; 784 785 GtkStruct strct = pack.getStruct(interf); 786 787 if ( strct ) 788 { 789 imports ~= strct.pack.name ~"."~ strct.name ~"IF"; 790 imports ~= strct.pack.name ~"."~ strct.name ~"T"; 791 } 792 } 793 794 imports = uniq(sort(imports)).array; 795 } 796 797 private void writeImports(ref string buff, bool _public = false) 798 { 799 foreach ( imp; imports ) 800 { 801 if ( _public || imp.endsWith("types") ) 802 buff ~= "public import "~ imp ~";\n"; 803 else 804 buff ~= "private import "~ imp ~";\n"; 805 } 806 807 buff ~= "\n\n"; 808 } 809 810 private void writeDocs(ref string buff) 811 { 812 if ( doc !is null && wrapper.includeComments ) 813 { 814 buff ~= "/**\n"; 815 foreach ( line; doc.splitLines() ) 816 buff ~= " * "~ line.strip() ~"\n"; 817 818 if ( libVersion ) 819 { 820 buff ~= " *\n * Since: "~ libVersion ~"\n"; 821 } 822 823 buff ~= " */\n"; 824 } 825 } 826 827 private GtkFunction getTypeFunction(string cIdentifier) 828 { 829 GtkType returnType = new GtkType(wrapper); 830 returnType.name = "GObject.GType"; 831 returnType.cType = "GType"; 832 833 GtkFunction func = new GtkFunction(wrapper, this); 834 func.type = GtkFunctionType.Function; 835 func.name = "get_type"; 836 func.cType = cIdentifier; 837 func.returnType = returnType; 838 839 if ( type == GtkStructType.Interface ) 840 func.noCode = true; 841 842 return func; 843 } 844 845 /** 846 * Get an overload of events that accept an generic Gdk Event 847 * instead of the spesific type listed in the gir files. 848 * 849 * This for backwards compatibility with the documentation based wrapper. 850 */ 851 private string[] getGenericEventSignal(GtkFunction func) 852 { 853 GtkFunction signal = func.dup(); 854 string[] buff; 855 856 for ( size_t i; i < signal.params.length; i++ ) 857 { 858 if ( signal.params[i].type.name.startsWith("Gdk.Event") ) 859 { 860 GtkType eventType = new GtkType(wrapper); 861 eventType.name = "Gdk.Event"; 862 863 GtkParam newParam = new GtkParam(wrapper); 864 newParam.name = signal.params[i].name; 865 newParam.doc = signal.params[i].doc; 866 newParam.type = eventType; 867 868 signal.params[i] = newParam; 869 870 break; 871 } 872 } 873 874 string[] declaration = signal.getAddListenerdeclaration(); 875 signal.name = signal.name ~ "-generic-event"; 876 877 buff ~= func.getDelegateDecleration() ~"[] on"~ signal.getSignalName() ~"Listeners;"; 878 buff ~= declaration; 879 buff ~= signal.getAddListenerBody(); 880 buff ~= signal.getSignalCallback(); 881 882 return buff; 883 } 884 } 885 886 final class GtkField 887 { 888 string name; 889 string doc; 890 GtkType type; 891 int bits = -1; 892 893 GtkFunction callback; 894 GtkUnion gtkUnion; 895 GtkStruct gtkStruct; 896 897 GtkWrapper wrapper; 898 899 this(GtkWrapper wrapper) 900 { 901 this.wrapper = wrapper; 902 } 903 904 void parse(T)(XMLReader!T reader) 905 { 906 name = reader.front.attributes["name"]; 907 908 if ( "bits" in reader.front.attributes ) 909 bits = to!int(reader.front.attributes["bits"]); 910 911 //TODO: readable private? 912 913 reader.popFront(); 914 915 while( !reader.empty && !reader.endTag("field") ) 916 { 917 if ( reader.front.type == XMLNodeType.EndTag ) 918 { 919 reader.popFront(); 920 continue; 921 } 922 923 switch(reader.front.value) 924 { 925 case "doc": 926 reader.popFront(); 927 doc ~= reader.front.value; 928 reader.popFront(); 929 break; 930 case "doc-deprecated": 931 reader.popFront(); 932 doc ~= "\n\nDeprecated: "~ reader.front.value; 933 reader.popFront(); 934 break; 935 case "array": 936 case "type": 937 type = new GtkType(wrapper); 938 type.parse(reader); 939 break; 940 case "callback": 941 callback = new GtkFunction(wrapper, null); 942 callback.parse(reader); 943 break; 944 default: 945 assert(false, name ~": Unexpected tag: "~ reader.front.value); 946 } 947 reader.popFront(); 948 } 949 } 950 951 /** 952 * A special case for fields, we need to know about all of then 953 * to properly construct the bitfields. 954 */ 955 static string[] getFieldDeclarations(GtkField[] fields, GtkWrapper wrapper) 956 { 957 string[] buff; 958 int bitcount; 959 960 void endBitfield() 961 { 962 //AFAIK: C bitfields are padded to a multiple of sizeof uint. 963 int padding = 32 - (bitcount % 32); 964 965 if ( padding > 0 && padding < 32) 966 { 967 buff[buff.length-1] ~= ","; 968 buff ~= "uint, \"\", "~ to!string(padding); 969 buff ~= "));"; 970 } 971 else 972 { 973 buff ~= "));"; 974 } 975 976 bitcount = 0; 977 } 978 979 foreach ( field; fields ) 980 { 981 if ( field.callback ) 982 { 983 if ( bitcount > 0 ) 984 endBitfield(); 985 buff ~= field.callback.getFunctionPointerDecleration(); 986 continue; 987 } 988 989 if ( field.gtkUnion ) 990 { 991 if ( bitcount > 0 ) 992 endBitfield(); 993 buff ~= field.gtkUnion.getUnionDeclaration(); 994 continue; 995 } 996 997 if ( field.gtkStruct ) 998 { 999 if ( bitcount > 0 ) 1000 endBitfield(); 1001 buff ~= field.gtkStruct.getStructDeclaration(); 1002 buff ~= stringToGtkD(field.gtkStruct.cType ~" "~ field.gtkStruct.name ~";", wrapper.aliasses); 1003 continue; 1004 } 1005 1006 if ( field.bits > 0 ) 1007 { 1008 if ( bitcount == 0 ) 1009 { 1010 buff ~= "import std.bitmanip: bitfields;"; 1011 buff ~= "mixin(bitfields!("; 1012 } 1013 else 1014 { 1015 buff[buff.length-1] ~= ","; 1016 } 1017 1018 bitcount += field.bits; 1019 buff ~=stringToGtkD(field.type.cType ~", \""~ field.name ~"\", "~ to!string(field.bits), wrapper.aliasses); 1020 continue; 1021 } 1022 else if ( bitcount > 0) 1023 { 1024 endBitfield(); 1025 } 1026 1027 if ( field.doc !is null && wrapper.includeComments && field.bits < 0 ) 1028 { 1029 buff ~= "/**"; 1030 foreach ( line; field.doc.splitLines() ) 1031 buff ~= " * "~ line.strip(); 1032 buff ~= " */"; 1033 } 1034 1035 string dType; 1036 1037 if ( field.type.size == -1 ) 1038 { 1039 if ( field.type.cType.empty ) 1040 dType = stringToGtkD(field.type.name, wrapper.aliasses); 1041 else 1042 dType = stringToGtkD(field.type.cType, wrapper.aliasses); 1043 } 1044 else if ( field.type.elementType.cType.empty ) 1045 { 1046 //Special case for GObject.Value. 1047 dType = stringToGtkD(field.type.elementType.name, wrapper.aliasses); 1048 dType ~= "["~ to!string(field.type.size) ~"]"; 1049 } 1050 else 1051 { 1052 dType = stringToGtkD(field.type.elementType.cType, wrapper.aliasses); 1053 dType ~= "["~ to!string(field.type.size) ~"]"; 1054 } 1055 1056 buff ~= dType ~" "~ tokenToGtkD(field.name, wrapper.aliasses) ~";"; 1057 } 1058 1059 if ( bitcount > 0) 1060 { 1061 endBitfield(); 1062 } 1063 1064 return buff; 1065 } 1066 } 1067 1068 final class GtkUnion 1069 { 1070 string name; 1071 string doc; 1072 GtkField[] fields; 1073 1074 GtkWrapper wrapper; 1075 1076 this(GtkWrapper wrapper) 1077 { 1078 this.wrapper = wrapper; 1079 } 1080 1081 void parse(T)(XMLReader!T reader) 1082 { 1083 if ( "name" in reader.front.attributes ) 1084 name = reader.front.attributes["name"]; 1085 1086 reader.popFront(); 1087 1088 while( !reader.empty && !reader.endTag("union") ) 1089 { 1090 switch(reader.front.value) 1091 { 1092 case "doc": 1093 reader.popFront(); 1094 doc ~= reader.front.value; 1095 reader.popFront(); 1096 break; 1097 case "doc-deprecated": 1098 reader.popFront(); 1099 doc ~= "\n\nDeprecated: "~ reader.front.value; 1100 reader.popFront(); 1101 break; 1102 case "field": 1103 GtkField field = new GtkField(wrapper); 1104 field.parse(reader); 1105 fields ~= field; 1106 break; 1107 case "record": 1108 GtkField field = new GtkField(wrapper); 1109 GtkStruct strct = new GtkStruct(wrapper, null); 1110 strct.parse(reader); 1111 strct.cType = strct.cType.toUpper()[0..1] ~ strct.cType[1 .. $]; 1112 field.gtkStruct = strct; 1113 fields ~= field; 1114 break; 1115 default: 1116 import std.stdio; 1117 writeln(name); 1118 assert(false, "Unexpected tag: "~ reader.front.value); 1119 } 1120 reader.popFront(); 1121 } 1122 } 1123 1124 string[] getUnionDeclaration() 1125 { 1126 string[] buff; 1127 if ( doc !is null && wrapper.includeComments ) 1128 { 1129 buff ~= "/**"; 1130 foreach ( line; doc.splitLines() ) 1131 buff ~= " * "~ line.strip(); 1132 buff ~= " */"; 1133 } 1134 1135 if ( name ) 1136 buff ~= "union "~ tokenToGtkD(name.toUpper()[0..1] ~ name[1 .. $], wrapper.aliasses); 1137 else 1138 buff ~= "union"; 1139 1140 buff ~= "{"; 1141 buff ~= GtkField.getFieldDeclarations(fields, wrapper); 1142 buff ~= "}"; 1143 1144 if ( name ) 1145 buff ~= tokenToGtkD(name.toUpper()[0..1] ~ name[1 .. $], wrapper.aliasses) ~" "~ tokenToGtkD(name.toLower(), wrapper.aliasses) ~";"; 1146 1147 return buff; 1148 } 1149 }