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 buff ~= indenter.format(func.getDelegateWrapperDeclaration()); 450 buff ~= indenter.format("protected " ~ func.getDelegateWrapperName() ~ "[] on" ~ func.getSignalName() ~"Listeners;"); 451 452 buff ~= "\n"; 453 buff ~= indenter.format(func.getAddListenerDeclaration()); 454 buff ~= indenter.format(func.getAddListenerBody()); 455 buff ~= indenter.format(func.getSignalCallback()); 456 buff ~= indenter.format(func.getSignalDestroyCallback()); 457 buff ~= "\n"; 458 /* 459 buff ~= indenter.format(func.getRemoveListenerDeclaration()); 460 buff ~= indenter.format(func.getRemoveListenerBody()); 461 buff ~= "\n"; 462 */ 463 buff ~= indenter.format(func.getInternalRemoveListenerDeclaration()); 464 buff ~= indenter.format(func.getInternalRemoveListenerBody()); 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 write(buildPath(wrapper.outputRoot, pack.srcDir, pack.name, name ~"T.d"), buff); 491 else 492 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(func.getAddListenerDeclaration() ~ ";"); 534 buff ~= "\n"; 535 /* 536 buff ~= indenter.format(func.getRemoveListenerDeclaration() ~ ";"); 537 buff ~= "\n"; 538 */ 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 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 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.among("utf8", "filename") || 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.among("utf8", "filename") || 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 ~= "std.algorithm"; 774 imports ~= "gobject.Signals"; 775 imports ~= "gtkc.gdktypes"; 776 777 } 778 779 if ( func.type == GtkFunctionType.Constructor ) 780 imports ~= "glib.ConstructionException"; 781 } 782 783 foreach ( interf; implements ) 784 { 785 if ( parentStruct && parentStruct.implements.canFind(interf) ) 786 continue; 787 788 GtkStruct strct = pack.getStruct(interf); 789 790 if ( strct ) 791 { 792 imports ~= strct.pack.name ~"."~ strct.name ~"IF"; 793 imports ~= strct.pack.name ~"."~ strct.name ~"T"; 794 } 795 } 796 797 imports = uniq(sort(imports)).array; 798 } 799 800 private void writeImports(ref string buff, bool _public = false) 801 { 802 foreach ( imp; imports ) 803 { 804 if ( _public || imp.endsWith("types") ) 805 buff ~= "public import "~ imp ~";\n"; 806 else 807 buff ~= "private import "~ imp ~";\n"; 808 } 809 810 buff ~= "\n\n"; 811 } 812 813 private void writeDocs(ref string buff) 814 { 815 if ( doc !is null && wrapper.includeComments ) 816 { 817 buff ~= "/**\n"; 818 foreach ( line; doc.splitLines() ) 819 buff ~= " * "~ line.strip() ~"\n"; 820 821 if ( libVersion ) 822 { 823 buff ~= " *\n * Since: "~ libVersion ~"\n"; 824 } 825 826 buff ~= " */\n"; 827 } 828 else if ( wrapper.includeComments ) 829 { 830 buff ~= "/** */\n"; 831 } 832 } 833 834 private GtkFunction getTypeFunction(string cIdentifier) 835 { 836 GtkType returnType = new GtkType(wrapper); 837 returnType.name = "GObject.GType"; 838 returnType.cType = "GType"; 839 840 GtkFunction func = new GtkFunction(wrapper, this); 841 func.type = GtkFunctionType.Function; 842 func.name = "get_type"; 843 func.cType = cIdentifier; 844 func.returnType = returnType; 845 846 if ( type == GtkStructType.Interface ) 847 func.noCode = true; 848 849 return func; 850 } 851 852 /** 853 * Get an overload of events that accept an generic Gdk Event 854 * instead of the spesific type listed in the gir files. 855 * 856 * This for backwards compatibility with the documentation based wrapper. 857 */ 858 private string[] getGenericEventSignal(GtkFunction func) 859 { 860 GtkFunction signal = func.dup(); 861 string[] buff; 862 863 for ( size_t i; i < signal.params.length; i++ ) 864 { 865 if ( signal.params[i].type.name.startsWith("Gdk.Event") ) 866 { 867 GtkType eventType = new GtkType(wrapper); 868 eventType.name = "Gdk.Event"; 869 870 GtkParam newParam = new GtkParam(wrapper); 871 newParam.name = signal.params[i].name; 872 newParam.doc = signal.params[i].doc; 873 newParam.type = eventType; 874 875 signal.params[i] = newParam; 876 877 break; 878 } 879 } 880 881 string[] declaration = signal.getAddListenerDeclaration(); 882 signal.name = signal.name ~ "-generic-event"; 883 884 buff ~= signal.getDelegateWrapperDeclaration(); 885 buff ~= "protected " ~ signal.getDelegateWrapperName() ~ "[] on" ~ signal.getSignalName() ~"Listeners;"; 886 buff ~= "\n"; 887 888 buff ~= declaration; 889 buff ~= signal.getAddListenerBody(); 890 buff ~= signal.getSignalCallback(); 891 buff ~= signal.getSignalDestroyCallback(); 892 /* 893 buff ~= signal.getRemoveListenerDeclaration(); 894 buff ~= signal.getRemoveListenerBody(); 895 */ 896 buff ~= signal.getInternalRemoveListenerDeclaration(); 897 buff ~= signal.getInternalRemoveListenerBody(); 898 899 return buff; 900 } 901 } 902 903 final class GtkField 904 { 905 string name; 906 string doc; 907 GtkType type; 908 int bits = -1; 909 910 GtkFunction callback; 911 GtkUnion gtkUnion; 912 GtkStruct gtkStruct; 913 914 GtkWrapper wrapper; 915 916 this(GtkWrapper wrapper) 917 { 918 this.wrapper = wrapper; 919 } 920 921 void parse(T)(XMLReader!T reader) 922 { 923 name = reader.front.attributes["name"]; 924 925 if ( "bits" in reader.front.attributes ) 926 bits = to!int(reader.front.attributes["bits"]); 927 928 //TODO: readable private? 929 930 reader.popFront(); 931 932 while( !reader.empty && !reader.endTag("field") ) 933 { 934 if ( reader.front.type == XMLNodeType.EndTag ) 935 { 936 reader.popFront(); 937 continue; 938 } 939 940 switch(reader.front.value) 941 { 942 case "doc": 943 reader.popFront(); 944 doc ~= reader.front.value; 945 reader.popFront(); 946 break; 947 case "doc-deprecated": 948 reader.popFront(); 949 doc ~= "\n\nDeprecated: "~ reader.front.value; 950 reader.popFront(); 951 break; 952 case "array": 953 case "type": 954 type = new GtkType(wrapper); 955 type.parse(reader); 956 break; 957 case "callback": 958 callback = new GtkFunction(wrapper, null); 959 callback.parse(reader); 960 break; 961 default: 962 throw new XMLException(reader, "Unexpected tag: "~ reader.front.value ~" in GtkField: "~ name); 963 } 964 reader.popFront(); 965 } 966 } 967 968 /** 969 * A special case for fields, we need to know about all of then 970 * to properly construct the bitfields. 971 */ 972 static string[] getFieldDeclarations(GtkField[] fields, GtkWrapper wrapper) 973 { 974 string[] buff; 975 int bitcount; 976 977 void endBitfield() 978 { 979 //AFAIK: C bitfields are padded to a multiple of sizeof uint. 980 int padding = 32 - (bitcount % 32); 981 982 if ( padding > 0 && padding < 32) 983 { 984 buff[buff.length-1] ~= ","; 985 buff ~= "uint, \"\", "~ to!string(padding); 986 buff ~= "));"; 987 } 988 else 989 { 990 buff ~= "));"; 991 } 992 993 bitcount = 0; 994 } 995 996 foreach ( field; fields ) 997 { 998 if ( field.callback ) 999 { 1000 if ( bitcount > 0 ) 1001 endBitfield(); 1002 buff ~= field.callback.getFunctionPointerDecleration(); 1003 continue; 1004 } 1005 1006 if ( field.gtkUnion ) 1007 { 1008 if ( bitcount > 0 ) 1009 endBitfield(); 1010 buff ~= field.gtkUnion.getUnionDeclaration(); 1011 continue; 1012 } 1013 1014 if ( field.gtkStruct ) 1015 { 1016 if ( bitcount > 0 ) 1017 endBitfield(); 1018 buff ~= field.gtkStruct.getStructDeclaration(); 1019 buff ~= stringToGtkD(field.gtkStruct.cType ~" "~ field.gtkStruct.name ~";", wrapper.aliasses); 1020 continue; 1021 } 1022 1023 if ( field.bits > 0 ) 1024 { 1025 if ( bitcount == 0 ) 1026 { 1027 buff ~= "import std.bitmanip: bitfields;"; 1028 buff ~= "mixin(bitfields!("; 1029 } 1030 else 1031 { 1032 buff[buff.length-1] ~= ","; 1033 } 1034 1035 bitcount += field.bits; 1036 buff ~=stringToGtkD(field.type.cType ~", \""~ field.name ~"\", "~ to!string(field.bits), wrapper.aliasses); 1037 continue; 1038 } 1039 else if ( bitcount > 0) 1040 { 1041 endBitfield(); 1042 } 1043 1044 if ( field.doc !is null && wrapper.includeComments && field.bits < 0 ) 1045 { 1046 buff ~= "/**"; 1047 foreach ( line; field.doc.splitLines() ) 1048 buff ~= " * "~ line.strip(); 1049 buff ~= " */"; 1050 } 1051 1052 string dType; 1053 1054 if ( field.type.size == -1 ) 1055 { 1056 if ( field.type.cType.empty ) 1057 dType = stringToGtkD(field.type.name, wrapper.aliasses); 1058 else 1059 dType = stringToGtkD(field.type.cType, wrapper.aliasses); 1060 } 1061 else if ( field.type.elementType.cType.empty ) 1062 { 1063 //Special case for GObject.Value. 1064 dType = stringToGtkD(field.type.elementType.name, wrapper.aliasses); 1065 dType ~= "["~ to!string(field.type.size) ~"]"; 1066 } 1067 else 1068 { 1069 dType = stringToGtkD(field.type.elementType.cType, wrapper.aliasses); 1070 dType ~= "["~ to!string(field.type.size) ~"]"; 1071 } 1072 1073 buff ~= dType ~" "~ tokenToGtkD(field.name, wrapper.aliasses) ~";"; 1074 } 1075 1076 if ( bitcount > 0) 1077 { 1078 endBitfield(); 1079 } 1080 1081 return buff; 1082 } 1083 } 1084 1085 final class GtkUnion 1086 { 1087 string name; 1088 string doc; 1089 GtkField[] fields; 1090 1091 GtkWrapper wrapper; 1092 1093 this(GtkWrapper wrapper) 1094 { 1095 this.wrapper = wrapper; 1096 } 1097 1098 void parse(T)(XMLReader!T reader) 1099 { 1100 if ( "name" in reader.front.attributes ) 1101 name = reader.front.attributes["name"]; 1102 1103 reader.popFront(); 1104 1105 while( !reader.empty && !reader.endTag("union") ) 1106 { 1107 switch(reader.front.value) 1108 { 1109 case "doc": 1110 reader.popFront(); 1111 doc ~= reader.front.value; 1112 reader.popFront(); 1113 break; 1114 case "doc-deprecated": 1115 reader.popFront(); 1116 doc ~= "\n\nDeprecated: "~ reader.front.value; 1117 reader.popFront(); 1118 break; 1119 case "field": 1120 GtkField field = new GtkField(wrapper); 1121 field.parse(reader); 1122 fields ~= field; 1123 break; 1124 case "record": 1125 GtkField field = new GtkField(wrapper); 1126 GtkStruct strct = new GtkStruct(wrapper, null); 1127 strct.parse(reader); 1128 strct.cType = strct.cType.toUpper()[0..1] ~ strct.cType[1 .. $]; 1129 field.gtkStruct = strct; 1130 fields ~= field; 1131 break; 1132 default: 1133 throw new XMLException(reader, "Unexpected tag: "~ reader.front.value ~" in GtkUnion: "~ name); 1134 } 1135 reader.popFront(); 1136 } 1137 } 1138 1139 string[] getUnionDeclaration() 1140 { 1141 string[] buff; 1142 if ( doc !is null && wrapper.includeComments ) 1143 { 1144 buff ~= "/**"; 1145 foreach ( line; doc.splitLines() ) 1146 buff ~= " * "~ line.strip(); 1147 buff ~= " */"; 1148 } 1149 1150 if ( name ) 1151 buff ~= "union "~ tokenToGtkD(name.toUpper()[0..1] ~ name[1 .. $], wrapper.aliasses); 1152 else 1153 buff ~= "union"; 1154 1155 buff ~= "{"; 1156 buff ~= GtkField.getFieldDeclarations(fields, wrapper); 1157 buff ~= "}"; 1158 1159 if ( name ) 1160 buff ~= tokenToGtkD(name.toUpper()[0..1] ~ name[1 .. $], wrapper.aliasses) ~" "~ tokenToGtkD(name.toLower(), wrapper.aliasses) ~";"; 1161 1162 return buff; 1163 } 1164 }