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