1 /****************************************************************************
2 **
3 ** DQt - D bindings for the Qt Toolkit
4 **
5 ** GNU Lesser General Public License Usage
6 ** This file may be used under the terms of the GNU Lesser
7 ** General Public License version 3 as published by the Free Software
8 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
9 ** packaging of this file. Please review the following information to
10 ** ensure the GNU Lesser General Public License version 3 requirements
11 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
12 **
13 ****************************************************************************/
14 module qt.widgets.ui;
15 
16 import qt.helpers;
17 import std.array;
18 
19 private void writeStringLiteral(R)(ref Appender!string code, R str)
20 {
21     code.put("\"");
22     foreach(dchar c; str)
23     {
24         if(c == '\"')
25             code.put("\\\"");
26         else if(c == '\n')
27             code.put("\\n\" ~\n\"");
28         else if(c == '\r')
29             code.put("\\r");
30         else if(c == '\\')
31             code.put("\\\\");
32         else
33             code.put(c);
34     }
35     code.put("\"");
36 }
37 
38 private struct UICodeWriter()
39 {
40     import qt.widgets.internal.dxml.dom;
41     import qt.widgets.internal.dxml.util;
42     import std.ascii;
43     import std.uni;
44     import std.conv;
45     import std.algorithm;
46     import std.exception;
47     alias DOMEntity = qt.widgets.internal.dxml.dom.DOMEntity!string;
48 
49     string customWidgetPackage;
50     Appender!string codeVars;
51     Appender!string codeSetup;
52     Appender!string codeSetupAddActions;
53     Appender!string codeSetupConnect;
54     Appender!string codeSetupDelayed;
55     Appender!string codeRetranslate;
56     size_t[string] tmpCountSetup;
57     size_t[string] tmpCountRetranslate;
58     string[string] widgetTypes;
59 
60     string createTmpVar(ref size_t[string] tmpCount, string prefix)
61     {
62         string r;
63         if(prefix !in tmpCount)
64         {
65             r = prefix;
66             tmpCount[prefix] = 1;
67         }
68         else
69         {
70             r = text(prefix, tmpCount[prefix]);
71             tmpCount[prefix]++;
72         }
73         return r;
74     }
75 
76     struct WidgetInfo
77     {
78         string name;
79         string className;
80     }
81 
82     WidgetInfo rootWidgetInfo;
83 
84     WidgetInfo getWidgetInfo(DOMEntity widget)
85     {
86         WidgetInfo info;
87         foreach(attr; widget.attributes)
88         {
89             if(attr.name == "class")
90                 info.className = attr.value;
91             else if(attr.name == "name")
92                 info.name = attr.value;
93         }
94         if(widget.name == "action")
95             info.className = "QAction";
96         if(widget.name == "spacer")
97             info.className = "QSpacerItem";
98         return info;
99     }
100 
101     string getWidgetModule(string name)
102     {
103         if(name == "QSpacerItem")
104             return "qt.widgets.layoutitem";
105         if(name == "QVBoxLayout")
106             return "qt.widgets.boxlayout";
107         if(name == "QHBoxLayout")
108             return "qt.widgets.boxlayout";
109         if(name == "QTimeEdit")
110             return "qt.widgets.datetimeedit";
111         if(name == "QDateEdit")
112             return "qt.widgets.datetimeedit";
113         if(name == "QDoubleSpinBox")
114             return "qt.widgets.spinbox";
115 
116         if(name.startsWith("Q"))
117             return "qt.widgets." ~ std.uni.toLower(name[1..$]);
118 
119         if(customWidgetPackage.length)
120             return customWidgetPackage ~ "." ~ name.toLower;
121         else
122             return name.toLower;
123     }
124 
125     void addProperty(DOMEntity property, string widgetName, string widgetType, bool isTopLevel, string extraArg)
126     {
127         string name;
128         foreach(attr; property.attributes)
129         {
130             if(attr.name == "name")
131                 name = attr.value;
132         }
133 
134         enforce(property.children.length == 1);
135 
136         bool noTr;
137         string comment;
138         foreach(attr; property.children[0].attributes)
139         {
140             if(attr.name == "notr" && attr.value == "true")
141                 noTr = true;
142             if(attr.name == "comment")
143                 comment = attr.value;
144         }
145 
146         Appender!string* code = &codeSetup;
147         size_t[string]* tmpCount = &tmpCountSetup;
148         if(property.children[0].name == "string" && !noTr)
149         {
150             code = &codeRetranslate;
151             tmpCount = &tmpCountRetranslate;
152         }
153         else if(name == "currentIndex")
154             code = &codeSetupDelayed;
155 
156         if(widgetType == "Line" && name == "orientation")
157         {
158             enforce(property.children[0].name == "enum");
159             enforce(property.children[0].children.length == 1);
160             enforce(property.children[0].children[0].type == EntityType.text);
161             string value = property.children[0].children[0].text;
162 
163             code.put("        ");
164             code.put(widgetName);
165             code.put(".setFrameShape(imported!q{qt.widgets.frame}.QFrame.Shape.");
166             if(value == "Qt::Horizontal")
167                 code.put("HLine");
168             else if(value == "Qt::Vertical")
169                 code.put("VLine");
170             else
171                 enforce(false);
172             code.put(");\n");
173 
174             code.put("        ");
175             code.put(widgetName);
176             code.put(".");
177             code.put("setFrameShadow(imported!q{qt.widgets.frame}.QFrame.Shadow.Sunken);\n");
178             return;
179         }
180 
181         Appender!string codeValue;
182         bool needTmp;
183         string methodName = "set" ~ std.ascii.toUpper(name[0]) ~ name[1..$];
184         if(property.children[0].name == "rect")
185         {
186             if(isTopLevel)
187             {
188                 methodName = "resize";
189                 foreach(i, c; property.children[0].children)
190                 {
191                     if(i < 2)
192                         continue;
193                     if(i > 2)
194                         codeValue.put(", ");
195                     enforce(c.children.length == 1);
196                     enforce(c.children[0].type == EntityType.text);
197                     codeValue.put(c.children[0].text);
198                 }
199             }
200             else
201             {
202                 codeValue.put("imported!q{qt.core.rect}.QRect(");
203                 foreach(i, c; property.children[0].children)
204                 {
205                     if(i)
206                         codeValue.put(", ");
207                     enforce(c.children.length == 1);
208                     enforce(c.children[0].type == EntityType.text);
209                     codeValue.put(c.children[0].text);
210                 }
211                 codeValue.put(")");
212                 needTmp = true;
213             }
214         }
215         else if(property.children[0].name == "color")
216         {
217             codeValue.put("imported!q{qt.gui.color}.QColor(");
218             foreach(i, c; property.children[0].children)
219             {
220                 if(i)
221                     codeValue.put(", ");
222                 enforce(c.children.length == 1);
223                 enforce(c.children[0].type == EntityType.text);
224                 codeValue.put(c.children[0].text);
225             }
226             codeValue.put(")");
227         }
228         else if(property.children[0].name.among("bool", "number", "double"))
229         {
230             enforce(property.children[0].children.length == 1);
231             enforce(property.children[0].children[0].type == EntityType.text);
232             codeValue.put(property.children[0].children[0].text);
233         }
234         else if(property.children[0].name == "set")
235         {
236             enforce(property.children[0].children.length == 1);
237             enforce(property.children[0].children[0].type == EntityType.text);
238             codeValue.put("imported!q{qt.core.flags}.flagsFromStaticString!(typeof(");
239             codeValue.put(widgetName);
240             codeValue.put(".");
241             codeValue.put(name);
242             codeValue.put("()), q{");
243             codeValue.put(property.children[0].children[0].text);
244             codeValue.put("})");
245         }
246         else if(property.children[0].name == "enum")
247         {
248             enforce(property.children[0].children.length == 1);
249             enforce(property.children[0].children[0].type == EntityType.text);
250             codeValue.put("imported!q{qt.core.flags}.enumFromStaticString!(typeof(");
251             codeValue.put(widgetName);
252             codeValue.put(".");
253             codeValue.put(name);
254             codeValue.put("()), q{");
255             codeValue.put(property.children[0].children[0].text);
256             codeValue.put("})");
257         }
258         else if(property.children[0].name == "string")
259         {
260             string value;
261             if(property.children[0].children.length)
262             {
263                 enforce(property.children[0].children.length == 1);
264                 enforce(property.children[0].children[0].type == EntityType.text);
265                 value = property.children[0].children[0].text;
266             }
267             if(name == "title" && property.name == "attribute")
268                 methodName = "setTabText";
269             if(name == "text" && widgetType == "QComboBox")
270                 methodName = "setItemText";
271             if(value.length == 0)
272             {
273                 if(widgetType == "QTreeWidget")
274                     return;
275                 codeValue.put("imported!q{qt.core.string}.QString.create()");
276             }
277             else if(noTr)
278             {
279                 codeValue.put("imported!q{qt.core.string}.QString.fromUtf8(");
280                 writeStringLiteral(codeValue, value.asDecodedXML);
281                 codeValue.put(")");
282             }
283             else
284             {
285                 codeValue.put("imported!q{qt.core.coreapplication}.QCoreApplication.translate(\"");
286                 codeValue.put(rootWidgetInfo.name);
287                 codeValue.put("\", ");
288                 writeStringLiteral(codeValue, value.asDecodedXML);
289                 codeValue.put(", ");
290                 if(comment.length)
291                     writeStringLiteral(codeValue, comment.asDecodedXML);
292                 else
293                     codeValue.put("null");
294                 codeValue.put(")");
295             }
296         }
297         else if(property.children[0].name == "sizepolicy")
298         {
299             string hsizetype="Expanding", vsizetype="Expanding";
300             foreach(attr; property.children[0].attributes)
301             {
302                 if(attr.name == "hsizetype")
303                     hsizetype = attr.value;
304                 if(attr.name == "vsizetype")
305                     vsizetype = attr.value;
306             }
307 
308             string sizePolicyVar = createTmpVar(*tmpCount, "sizePolicy");
309             code.put("        auto ");
310             code.put(sizePolicyVar);
311             code.put(" = imported!q{qt.widgets.sizepolicy}.QSizePolicy(imported!q{qt.widgets.sizepolicy}.QSizePolicy.Policy.");
312             code.put(hsizetype);
313             code.put(", imported!q{qt.widgets.sizepolicy}.QSizePolicy.Policy.");
314             code.put(vsizetype);
315             code.put(");\n");
316 
317             foreach(i, c; property.children[0].children)
318             {
319                 enforce(c.children.length == 1);
320                 enforce(c.children[0].type == EntityType.text);
321                 code.put("        ");
322                 code.put(sizePolicyVar);
323                 code.put(".");
324                 if(c.name == "horstretch")
325                     code.put("setHorizontalStretch");
326                 else if(c.name == "verstretch")
327                     code.put("setVerticalStretch");
328                 else
329                     enforce(false);
330                 code.put("(");
331                 code.put(c.children[0].text);
332                 code.put(");\n");
333             }
334 
335             code.put("        ");
336             code.put(sizePolicyVar);
337             code.put(".");
338             code.put("setHeightForWidth");
339             code.put("(");
340             code.put(widgetName);
341             code.put(".sizePolicy().hasHeightForWidth());\n");
342 
343             codeValue.put(sizePolicyVar);
344         }
345         else
346             enforce(false, "Unsupported property type " ~ property.children[0].name);
347 
348         code.put("        ");
349         string tmpVar;
350         if(needTmp)
351         {
352             tmpVar = createTmpVar(*tmpCount, "tmp");
353             code.put("auto ");
354             code.put(tmpVar);
355             code.put(" = ");
356             code.put(codeValue.data);
357             code.put("; ");
358         }
359         code.put(widgetName);
360         code.put(".");
361         code.put(methodName);
362         code.put("(");
363         code.put(extraArg);
364         if(needTmp)
365             code.put(tmpVar);
366         else
367             code.put(codeValue.data);
368         code.put(");\n");
369     }
370 
371     WidgetInfo addItem(DOMEntity widget, string parentName, string parentType, bool isRoot, size_t itemIndex)
372     {
373         WidgetInfo info = getWidgetInfo(widget);
374 
375         string extraArg;
376         string name;
377         if(parentType == "QComboBox")
378         {
379             codeSetup.put("        ");
380             codeSetup.put(parentName);
381             codeSetup.put(".addItem(imported!q{qt.core.string}.QString.create());\n");
382             extraArg = text(itemIndex, ", ");
383             name = parentName;
384         }
385         else if(parentType == "QTreeWidget")
386         {
387             name = createTmpVar(tmpCountSetup, "__qtreewidgetitem");
388             codeSetup.put("        imported!q{qt.widgets.treewidget}.QTreeWidgetItem ");
389             codeSetup.put(name);
390             codeSetup.put(" = cpp_new!(imported!q{qt.widgets.treewidget}.QTreeWidgetItem)(");
391             codeSetup.put(parentName);
392             codeSetup.put(");\n");
393 
394             codeRetranslate.put("        imported!q{qt.widgets.treewidget}.QTreeWidgetItem ");
395             codeRetranslate.put(name);
396             codeRetranslate.put(" = ");
397             codeRetranslate.put(parentName);
398             codeRetranslate.put(isRoot ? ".topLevelItem(" : ".child(");
399             codeRetranslate.put(text(itemIndex));
400             codeRetranslate.put(");\n");
401         }
402         else if(parentType == "column")
403         {
404             extraArg = text(itemIndex, ", ");
405             name = parentName;
406         }
407         size_t[string] columnByProperty;
408         foreach(property; widget.children)
409         {
410             if(parentType == "QTreeWidget")
411             {
412                 string propName;
413                 foreach(attr; property.attributes)
414                 {
415                     if(attr.name == "name")
416                         propName = attr.value;
417                 }
418                 if(propName == "flags")
419                     extraArg = "";
420                 else
421                 {
422                     size_t col = columnByProperty.get(propName, 0);
423                     extraArg = text(col, ", ");
424                     columnByProperty[propName] = col + 1;
425                 }
426             }
427             if(property.name == "property")
428                 addProperty(property, name, parentType, false, extraArg);
429         }
430         size_t childItemCount;
431         foreach(c; widget.children)
432         {
433             if(c.name == "item")
434             {
435                 addItem(c, name, parentType, false, childItemCount);
436                 childItemCount++;
437             }
438         }
439         return info;
440     }
441 
442     WidgetInfo addWidget(DOMEntity widget, string parentName, bool parentIsLayout)
443     {
444         WidgetInfo info = getWidgetInfo(widget);
445 
446         if(info.name.length)
447         {
448             widgetTypes[info.name] = info.className;
449         }
450 
451         if(widget.name == "spacer")
452         {
453             string orientation;
454             string sizeType = "Expanding";
455             string width = "0";
456             string height = "0";
457 
458             foreach(property; widget.children)
459             {
460                 if(property.name == "property")
461                 {
462                     string propName;
463                     foreach(attr; property.attributes)
464                     {
465                         if(attr.name == "name")
466                             propName = attr.value;
467                     }
468 
469                     if(propName == "orientation")
470                     {
471                         enforce(property.children.length == 1);
472                         enforce(property.children[0].name == "enum");
473                         enforce(property.children[0].children.length == 1);
474                         orientation = property.children[0].children[0].text;
475                     }
476                     else if(propName == "sizeType")
477                     {
478                         enforce(property.children.length == 1);
479                         enforce(property.children[0].name == "enum");
480                         enforce(property.children[0].children.length == 1);
481                         sizeType = property.children[0].children[0].text;
482                         enforce(sizeType.startsWith("QSizePolicy::"));
483                         sizeType = sizeType["QSizePolicy::".length..$];
484                     }
485                     else if(propName == "sizeHint")
486                     {
487                         enforce(property.children.length == 1);
488                         enforce(property.children[0].name == "size");
489                         enforce(property.children[0].children.length == 2);
490                         enforce(property.children[0].children[0].name == "width");
491                         enforce(property.children[0].children[1].name == "height");
492                         width = property.children[0].children[0].children[0].text;
493                         height = property.children[0].children[1].children[0].text;
494                     }
495                 }
496             }
497 
498             codeVars.put("    ");
499             codeVars.put("imported!q{");
500             codeVars.put(getWidgetModule(info.className));
501             codeVars.put("}.");
502             codeVars.put(info.className);
503             codeVars.put(" ");
504             codeVars.put(info.name);
505             codeVars.put(";\n");
506 
507             codeSetup.put("        ");
508             codeSetup.put(info.name);
509             codeSetup.put(" = cpp_new!(imported!q{");
510             codeSetup.put(getWidgetModule(info.className));
511             codeSetup.put("}.");
512             codeSetup.put(info.className);
513             codeSetup.put(")(");
514             codeSetup.put(width);
515             codeSetup.put(", ");
516             codeSetup.put(height);
517             codeSetup.put(", ");
518             if(orientation == "Qt::Horizontal")
519             {
520                 codeSetup.put("imported!q{qt.widgets.sizepolicy}.QSizePolicy.Policy.");
521                 codeSetup.put(sizeType);
522                 codeSetup.put(", ");
523                 codeSetup.put("imported!q{qt.widgets.sizepolicy}.QSizePolicy.Policy.Minimum");
524             }
525             else if(orientation == "Qt::Vertical")
526             {
527                 codeSetup.put("imported!q{qt.widgets.sizepolicy}.QSizePolicy.Policy.Minimum");
528                 codeSetup.put(", ");
529                 codeSetup.put("imported!q{qt.widgets.sizepolicy}.QSizePolicy.Policy.");
530                 codeSetup.put(sizeType);
531             }
532             else
533                 enforce(false);
534             codeSetup.put(");\n");
535             return info;
536         }
537 
538         if(info.name.length)
539         {
540             codeVars.put("    ");
541             if(info.className == "Line")
542             {
543                 codeVars.put("imported!q{qt.widgets.frame}.QFrame");
544             }
545             else
546             {
547                 codeVars.put("imported!q{");
548                 codeVars.put(getWidgetModule(info.className));
549                 codeVars.put("}.");
550                 codeVars.put(info.className);
551             }
552             codeVars.put(" ");
553             codeVars.put(info.name);
554             codeVars.put(";\n");
555 
556             codeSetup.put("        ");
557             codeSetup.put(info.name);
558             codeSetup.put(" = cpp_new!(");
559             if(info.className == "Line")
560             {
561                 codeSetup.put("imported!q{qt.widgets.frame}.QFrame");
562             }
563             else
564             {
565                 codeSetup.put("imported!q{");
566                 codeSetup.put(getWidgetModule(info.className));
567                 codeSetup.put("}.");
568                 codeSetup.put(info.className);
569             }
570             codeSetup.put(")(");
571             if(!(parentIsLayout && widget.name == "layout"))
572                 codeSetup.put(parentName);
573             codeSetup.put(");\n");
574         }
575         string sortingEnabledVar;
576         if(info.className == "QTreeWidget")
577         {
578             string headerName = createTmpVar(tmpCountSetup, "__qtreewidgetitem");
579             codeSetup.put("        imported!q{qt.widgets.treewidget}.QTreeWidgetItem ");
580             codeSetup.put(headerName);
581             codeSetup.put(" = cpp_new!(imported!q{qt.widgets.treewidget}.QTreeWidgetItem)(");
582             codeSetup.put(");\n");
583 
584             codeRetranslate.put("        imported!q{qt.widgets.treewidget}.QTreeWidgetItem ");
585             codeRetranslate.put(headerName);
586             codeRetranslate.put(" = ");
587             codeRetranslate.put(info.name);
588             codeRetranslate.put(".headerItem();\n");
589 
590             size_t childItemCount;
591             foreach(c; widget.children)
592             {
593                 if(c.name == "column")
594                 {
595                     WidgetInfo childInfo = addItem(c, headerName, "column", true, childItemCount);
596                     childItemCount++;
597                 }
598             }
599 
600             codeSetup.put("        ");
601             codeSetup.put(info.name);
602             codeSetup.put(".setHeaderItem(");
603             codeSetup.put(headerName);
604             codeSetup.put(");\n");
605 
606             sortingEnabledVar = createTmpVar(tmpCountRetranslate, "__sortingEnabled");
607             codeRetranslate.put("        const(bool) ");
608             codeRetranslate.put(sortingEnabledVar);
609             codeRetranslate.put(" = ");
610             codeRetranslate.put(info.name);
611             codeRetranslate.put(".isSortingEnabled();\n");
612 
613             codeRetranslate.put("        ");
614             codeRetranslate.put(info.name);
615             codeRetranslate.put(".setSortingEnabled(false);\n");
616         }
617         size_t childItemCount;
618         foreach(c; widget.children)
619         {
620             if(c.name == "item" && widget.name != "layout")
621             {
622                 WidgetInfo childInfo = addItem(c, info.name, info.className, true, childItemCount);
623                 childItemCount++;
624             }
625         }
626         if(info.name.length)
627         {
628             codeSetup.put("        ");
629             codeSetup.put(info.name);
630             codeSetup.put(".setObjectName(\"");
631             codeSetup.put(info.name);
632             codeSetup.put("\");\n");
633         }
634 
635         string constructorArg = info.name;
636         if(info.className == "QTabWidget")
637             constructorArg = "";
638         if(info.className == "QScrollArea")
639             constructorArg = "";
640         if(widget.name == "layout")
641             constructorArg = parentName;
642         if(widget.name == "item")
643             constructorArg = parentName;
644         foreach(property; widget.children)
645         {
646             if(property.name == "property")
647                 addProperty(property, info.name, info.className, false, "");
648         }
649         foreach(c; widget.children)
650         {
651             WidgetInfo childInfo = addChildWidget(widget, info, c, constructorArg, widget.name == "layout");
652             if(info.className == "QTabWidget")
653             {
654                 foreach(property; c.children)
655                 {
656                     if(property.name == "attribute")
657                         addProperty(property, info.name, info.className, false, info.name ~ ".indexOf(" ~ childInfo.name ~ "), ");
658                 }
659             }
660         }
661         if(info.className == "QTreeWidget")
662         {
663             codeRetranslate.put("        ");
664             codeRetranslate.put(info.name);
665             codeRetranslate.put(".setSortingEnabled(");
666             codeRetranslate.put(sortingEnabledVar);
667             codeRetranslate.put(");\n");
668         }
669         return info;
670     }
671 
672     WidgetInfo addChildWidget(DOMEntity widget, WidgetInfo info, DOMEntity c, string constructorArg, bool parentIsLayout)
673     {
674         if(c.name == "item" && widget.name == "layout")
675         {
676             enforce(c.children.length == 1);
677             WidgetInfo childInfo = addWidget(c.children[0], constructorArg, parentIsLayout);
678 
679             string row, column, rowspan = "1", colspan = "1";
680             foreach(attr; c.attributes)
681             {
682                 if(attr.name == "row")
683                     row = attr.value;
684                 if(attr.name == "column")
685                     column = attr.value;
686                 if(attr.name == "rowspan")
687                     rowspan = attr.value;
688                 if(attr.name == "colspan")
689                     colspan = attr.value;
690             }
691 
692             codeSetup.put("\n        ");
693             codeSetup.put(info.name);
694             if(info.className == "QFormLayout")
695             {
696                 if(c.children[0].name == "layout")
697                     codeSetup.put(".setLayout(");
698                 else if(c.children[0].name == "spacer")
699                     codeSetup.put(".setItem(");
700                 else
701                     codeSetup.put(".setWidget(");
702 
703                 codeSetup.put(row);
704                 codeSetup.put(", imported!q{qt.widgets.formlayout}.QFormLayout.ItemRole.");
705                 codeSetup.put((column == "1") ? "FieldRole" : "LabelRole");
706                 codeSetup.put(", ");
707             }
708             else
709             {
710                 if(c.children[0].name == "layout")
711                     codeSetup.put(".addLayout(");
712                 else if(c.children[0].name == "spacer")
713                     codeSetup.put(".addItem(");
714                 else
715                     codeSetup.put(".addWidget(");
716             }
717             codeSetup.put(childInfo.name);
718             if(info.className == "QGridLayout")
719             {
720                 codeSetup.put(", ");
721                 codeSetup.put(row);
722                 codeSetup.put(", ");
723                 codeSetup.put(column);
724                 codeSetup.put(", ");
725                 codeSetup.put(rowspan);
726                 codeSetup.put(", ");
727                 codeSetup.put(colspan);
728             }
729             codeSetup.put(");\n\n");
730             return childInfo;
731         }
732         else if(c.name == "widget" || c.name == "layout")
733         {
734             WidgetInfo childInfo = addWidget(c, constructorArg, parentIsLayout);
735 
736             if(info.className == "QTabWidget")
737             {
738                 codeSetup.put("        ");
739                 codeSetup.put(info.name);
740                 codeSetup.put(".addTab(");
741                 codeSetup.put(childInfo.name);
742                 codeSetup.put(", ");
743                 codeSetup.put("imported!q{qt.core.string}.QString.create()"); // TODO: not translateable strings
744                 codeSetup.put(");\n");
745             }
746             else if(info.className == "QMainWindow")
747             {
748                 if(childInfo.className != "QMenuBar" && childInfo.className != "QStatusBar")
749                     codeSetup.put("\n");
750                 codeSetup.put("        ");
751                 codeSetup.put(info.name);
752                 if(childInfo.className == "QMenuBar")
753                     codeSetup.put(".setMenuBar(");
754                 else if(childInfo.className == "QStatusBar")
755                     codeSetup.put(".setStatusBar(");
756                 else
757                     codeSetup.put(".setCentralWidget(");
758                 codeSetup.put(childInfo.name);
759                 codeSetup.put(");\n");
760             }
761             else if(info.className == "QSplitter")
762             {
763                 codeSetup.put("        ");
764                 codeSetup.put(info.name);
765                 codeSetup.put(".addWidget(");
766                 codeSetup.put(childInfo.name);
767                 codeSetup.put(");\n");
768             }
769             else if(info.className == "QScrollArea")
770             {
771                 codeSetup.put("        ");
772                 codeSetup.put(info.name);
773                 codeSetup.put(".setWidget(");
774                 codeSetup.put(childInfo.name);
775                 codeSetup.put(");\n");
776             }
777             return childInfo;
778         }
779         else if(c.name == "addaction")
780         {
781             string name;
782             foreach(attr; c.attributes)
783             {
784                 if(attr.name == "name")
785                     name = attr.value;
786             }
787 
788             codeSetupAddActions.put("        ");
789             codeSetupAddActions.put(info.name);
790             codeSetupAddActions.put(".addAction(");
791             codeSetupAddActions.put(name);
792             if(name in widgetTypes && widgetTypes[name].among("QMenu", "QMenuBar"))
793                 codeSetupAddActions.put(".menuAction()");
794             codeSetupAddActions.put(");\n");
795         }
796         return WidgetInfo.init;
797     }
798 
799     void addConnection(DOMEntity connection)
800     {
801         string sender, signal, receiver, slot;
802         foreach(ref c; connection.children)
803         {
804             if(c.name == "sender")
805             {
806                 enforce(c.children.length == 1);
807                 enforce(c.children[0].type == EntityType.text);
808                 sender = c.children[0].text;
809             }
810             else if(c.name == "signal")
811             {
812                 enforce(c.children.length == 1);
813                 enforce(c.children[0].type == EntityType.text);
814                 signal = c.children[0].text;
815             }
816             else if(c.name == "receiver")
817             {
818                 enforce(c.children.length == 1);
819                 enforce(c.children[0].type == EntityType.text);
820                 receiver = c.children[0].text;
821             }
822             else if(c.name == "slot")
823             {
824                 enforce(c.children.length == 1);
825                 enforce(c.children[0].type == EntityType.text);
826                 slot = c.children[0].text;
827             }
828         }
829 
830         // Ignore parameter types for signal and slot for now.
831         foreach(i, char c; signal)
832             if(c == '(')
833             {
834                 signal = signal[0..i];
835                 break;
836             }
837         foreach(i, char c; slot)
838             if(c == '(')
839             {
840                 slot = slot[0..i];
841                 break;
842             }
843 
844         codeSetupConnect.put("        imported!q{qt.core.object}.QObject.connect(");
845         codeSetupConnect.put(sender);
846         codeSetupConnect.put(".signal!\"");
847         codeSetupConnect.put(signal);
848         codeSetupConnect.put("\", ");
849         codeSetupConnect.put(receiver);
850         codeSetupConnect.put(".slot!\"");
851         codeSetupConnect.put(slot);
852         codeSetupConnect.put("\");\n");
853     }
854 
855     string convert(string xml, string customWidgetPackage)
856     {
857         this.customWidgetPackage = customWidgetPackage;
858         auto dom = parseDOM!simpleXML(xml);
859         assert(dom.type == EntityType.elementStart);
860 
861         enforce(dom.children.length == 1);
862         auto root = dom.children[0];
863         enforce(root.name == "ui");
864 
865         DOMEntity* rootWidget;
866         foreach(ref c; root.children)
867         {
868             if(c.name == "widget")
869             {
870                 enforce(rootWidget is null, "Multpile root widgets");
871                 rootWidget = &c;
872             }
873         }
874         enforce(rootWidget !is null);
875         rootWidgetInfo = getWidgetInfo(*rootWidget);
876 
877         codeSetup.put("        if (");
878         codeSetup.put(rootWidgetInfo.name);
879         codeSetup.put(".objectName().isEmpty())\n");
880         codeSetup.put("            ");
881         codeSetup.put(rootWidgetInfo.name);
882         codeSetup.put(".setObjectName(\"");
883         codeSetup.put(rootWidgetInfo.name);
884         codeSetup.put("\");\n");
885 
886         foreach(c; rootWidget.children)
887         {
888             if(c.name == "property")
889                 addProperty(c, rootWidgetInfo.name, rootWidgetInfo.className, true, "");
890         }
891 
892         foreach(c; rootWidget.children)
893         {
894             if(c.name == "action")
895             {
896                 addWidget(c, rootWidgetInfo.name, false);
897             }
898         }
899 
900         foreach(c; rootWidget.children)
901         {
902             addChildWidget(*rootWidget, rootWidgetInfo, c, rootWidgetInfo.name, false);
903         }
904 
905         foreach(c; root.children)
906         {
907             if(c.name == "connections")
908             {
909                 foreach(c2; c.children)
910                 {
911                     if(c2.name == "connection")
912                     {
913                         addConnection(c2);
914                     }
915                 }
916             }
917         }
918 
919         codeVars.put("\n");
920         codeVars.put("    void setupUi(");
921         codeVars.put("imported!q{");
922         codeVars.put(getWidgetModule(rootWidgetInfo.className));
923         codeVars.put("}.");
924         codeVars.put(rootWidgetInfo.className);
925         codeVars.put(" ");
926         codeVars.put(rootWidgetInfo.name);
927         codeVars.put(")\n");
928         codeVars.put("    {\n");
929         codeVars.put("        import core.stdcpp.new_: cpp_new;\n");
930         codeVars.put(codeSetup.data);
931         codeVars.put(codeSetupAddActions.data);
932         codeVars.put("\n        retranslateUi(");
933         codeVars.put(rootWidgetInfo.name);
934         codeVars.put(");\n");
935         codeVars.put(codeSetupConnect.data);
936         codeVars.put(codeSetupDelayed.data);
937         codeVars.put("\n        imported!q{qt.core.objectdefs}.QMetaObject.connectSlotsByName(");
938         codeVars.put(rootWidgetInfo.name);
939         codeVars.put(");\n");
940         codeVars.put("    } // setupUi\n");
941         codeVars.put("\n");
942         codeVars.put("    void retranslateUi(");
943         codeVars.put("imported!q{");
944         codeVars.put(getWidgetModule(rootWidgetInfo.className));
945         codeVars.put("}.");
946         codeVars.put(rootWidgetInfo.className);
947         codeVars.put(" ");
948         codeVars.put(rootWidgetInfo.name);
949         codeVars.put(")\n");
950         codeVars.put("    {\n");
951         codeVars.put(codeRetranslate.data);
952         codeVars.put("    } // retranslateUi\n");
953         return codeVars.data;
954     }
955 }
956 
957 string generateUICode()(string xml, string customWidgetPackage = "")
958 {
959     return UICodeWriter!()().convert(xml, customWidgetPackage);
960 }
961 
962 struct UIStruct(string filename, string customWidgetPackage = "")
963 {
964     mixin(generateUICode(import(filename), customWidgetPackage));
965 }