1 module dqml.qobjectgenerators;
2 
3 import std.traits;
4 import std.algorithm;
5 import std.string;
6 import std.stdio;
7 
8 struct QtPropertyData
9 {
10     public string type;
11     public string name;
12     public string read;
13     public string write;
14     public string notify;
15 }
16 
17 struct QtProperty(T)
18 {
19     public QtPropertyData data;
20 
21     this(string name, string read, string write, string notify)
22     {
23         data.type = T.stringof;
24         data.name = name;
25         data.read = read;
26         data.write = write;
27         data.notify = notify;
28     }
29 }
30 
31 struct QtSlot {}
32 struct QtSignal {}
33 
34 string GenerateVariantConversionCall(string typeName)
35 {
36     switch (typeName)
37     {
38     case "string":
39         return ".toString()";
40     case "int":
41         return ".toInt()";
42     case "bool":
43         return ".toBool()";
44     case "float":
45         return ".toFloat()";
46     case "double":
47         return ".toDouble()";
48     case "QVariant":
49         return "";
50     default:
51         throw new Exception("Unknown conversion function from Qvariant to " ~ typeName);
52     }
53 }
54 
55 string GenerateArgumentList(string[] typeNames)
56 {
57     string result = "";
58     for (int i = 0; i < typeNames.length; ++i)
59     {
60         auto typeName = typeNames[i];
61         auto variantCall = GenerateVariantConversionCall(typeName);
62         result ~= i > 0 ? "," : "";
63         result ~= format("arguments[%d]%s", i+1, variantCall);
64     }
65     return result;
66 }
67 
68 string GenerateSlotCall(FunctionInfo info)
69 {
70     auto args = GenerateArgumentList(info.parameterTypes);
71     auto call = format("%s(%s)", info.name, args);
72     auto formatStr = info.returnType != "void" ? "arguments[0].setValue(%s)" : "%s";
73     return format(formatStr, call);
74 }
75 
76 string GenerateCaseBlock(FunctionInfo info)
77 {
78     string result = "";
79     result ~= format("case \"%s\":\n", info.name);
80     result ~= format("%s;\n", GenerateSlotCall(info));
81     result ~= "break;\n";
82     return result;
83 }
84 
85 string GenerateOnSlotCalled(QtInfo info)
86 {
87     string result = "protected override void onSlotCalled(QVariant slotName, QVariant[] arguments)\n";
88     result ~= "{\n";
89     result ~= "switch(slotName.toString())\n";
90     result ~= "{\n";
91     foreach (slot; info.slots)
92         result ~= GenerateCaseBlock(slot);
93     result ~= "default: super.onSlotCalled(slotName, arguments);\n";
94     result ~= "}\n"; //
95     result ~= "}";
96     return result;
97 }
98 
99 string GenerateSignalCall(FunctionInfo info)
100 {
101     string args = "";
102     string vars = "";
103     for (int i = 0; i < info.parameterTypes.length; ++i) {
104         if (i > 0) {
105             args ~= ",";
106             vars ~= ",";
107         }
108         args ~= format("%s val%d", info.parameterTypes[i], i);
109         vars ~= format("val%d", i);
110     }
111 
112     string result = format("pragma(mangle,\"%s\")\n", info.mangle);
113     result ~= format("public %s %s(%s) { emit(\"%s\", %s); }", info.returnType, info.name, args, info.name, vars);
114     return result;
115 }
116 
117 string GenerateSignals(QtInfo info)
118 {
119     string result = "";
120     foreach (signal; info.signals)
121         result ~= GenerateSignalCall(signal) ~ "\n";
122     return result;
123 }
124 
125 string GenerateMetaType(string typeName)
126 {
127     switch(typeName)
128     {
129     case "void":
130         return "QMetaType.Void";
131     case "int":
132         return "QMetaType.Int";
133     case "string":
134         return "QMetaType.String";
135     case "QObject":
136         return "QMetaType.QObject";
137     case "QVariant":
138         return "QMetaType.QVariant";
139     case "bool":
140         return "QMetaType.Bool";
141     case "float":
142         return "QMetaType.Float";
143     case "double":
144         return "QMetaType.Double";
145     default:
146         throw new Exception(format("Unknown conversion from %s to QMetaType", typeName));
147     }
148 }
149 
150 string GenerateMetaTypesListForSlot(FunctionInfo info)
151 {
152     string result = GenerateMetaType(info.returnType);
153     result ~= ", ";
154     result ~= GenerateMetaTypesListForSignal(info);
155     return result;
156 }
157 
158 string GenerateMetaTypesListForSignal(FunctionInfo info)
159 {
160     string result = "";
161     for (int i = 0; i < info.parameterTypes.length; ++i)
162     {
163         if (i > 0)
164             result ~= ", ";
165         result ~= GenerateMetaType(info.parameterTypes[i]);
166     }
167     return result;
168 }
169 
170 struct FunctionInfo
171 {
172     string name;
173     string returnType;
174     string[] parameterTypes;
175     string mangle;
176 }
177 
178 struct QtInfo
179 {
180     FunctionInfo[] slots;
181     FunctionInfo[] signals;
182     QtPropertyData[] properties;
183 }
184 
185 public static QtInfo GetQtUDA(T)()
186 {
187     QtInfo result;
188 
189     foreach (property; getUDAs!(T, QtProperty)) {
190         result.properties ~= property.data;
191     }
192 
193     foreach (member; __traits(derivedMembers, T)) {
194         static if (__traits(compiles, __traits(getMember, T, member))
195                    && isSomeFunction!(__traits(getMember, T, member))) {
196             // Retrieve the UDA
197             auto attributes = __traits(getAttributes, __traits(getMember, T, member));
198 
199             // Turn the tuple in an array of strings
200             string[] attributeNames;
201             foreach (attribute; attributes)
202                 attributeNames ~= typeof(attribute).stringof;
203 
204             bool isSlot = attributeNames.canFind("QtSlot");
205             bool isSignal = attributeNames.canFind("QtSignal");
206 
207             // Extract the Function Return Type and Arguments
208             if (isSlot || isSignal) {
209                 FunctionInfo info;
210                 info.mangle = __traits(getMember, T, member).mangleof;
211                 info.name = member;
212                 info.returnType = ReturnType!(__traits(getMember, T, member)).stringof;
213 
214                 foreach (param; ParameterTypeTuple!(__traits(getMember, T, member)))
215                     info.parameterTypes ~= param.stringof;
216 
217                 if (isSlot)
218                     result.slots ~= info;
219 
220                 if (isSignal)
221                     result.signals ~= info;
222             }
223         }
224     }
225 
226     return result;
227 }
228 
229 public static string GenerateMetaObject(string qobjectSuperClassName, QtInfo info)
230 {
231     string result =
232         "shared static this() { m_staticMetaObject = createMetaObject(); }\n" ~
233         "private static QMetaObject m_staticMetaObject;\n" ~
234         "public static QMetaObject staticMetaObject() { return m_staticMetaObject; }\n" ~
235         "public override QMetaObject metaObject() { return staticMetaObject(); }\n"~
236         "private static QMetaObject createMetaObject() {\n" ~
237         "  QMetaObject superMetaObject = " ~ qobjectSuperClassName ~ ".staticMetaObject();\n" ~
238         "  SignalDefinition[] signals = [];\n" ~
239         "  SlotDefinition[] slots = [];\n" ~
240         "  PropertyDefinition[] properties = [];\n";
241 
242     foreach(FunctionInfo signal; info.signals) {
243         result ~= format("  signals ~= SignalDefinition(\"%s\",[%s]);\n", signal.name, GenerateMetaTypesListForSignal(signal));
244     }
245 
246     foreach(FunctionInfo slot; info.slots) {
247         string name = slot.name;
248         string returnType = GenerateMetaType(slot.returnType);
249         string parameters = GenerateMetaTypesListForSignal(slot);
250         result ~= format("  slots ~= SlotDefinition(\"%s\", %s, [%s]);\n", slot.name, returnType, parameters);
251     }
252 
253     foreach(QtPropertyData property; info.properties) {
254         string name = property.name;
255         string type = GenerateMetaType(property.type);
256         string read = property.read;
257         string write = property.write;
258         string notify = property.notify;
259         result ~= format("  properties ~= PropertyDefinition(\"%s\", %s, \"%s\", \"%s\", \"%s\");\n", name, type, read, write, notify);
260     }
261 
262     result ~=
263         "  return new QMetaObject(superMetaObject, typeof(this).stringof, signals, slots, properties);\n}\n";
264     return result;
265 }
266 
267 public static string QObjectSuperClass(T)()
268 {
269     foreach (Type; BaseClassesTuple!T) {
270         static if (__traits(compiles, Type.staticMetaObject())) {
271             return Type.stringof;
272         }
273     }
274 }
275 
276 public mixin template Q_OBJECT()
277 {
278     private static string GenerateCode()
279     {
280         alias outerType = typeof(this);
281         alias info = GetQtUDA!outerType;
282         string result;
283         result ~= GenerateMetaObject(QObjectSuperClass!outerType, info);
284         result ~= GenerateOnSlotCalled(info);
285         result ~= GenerateSignals(info);
286         return result;
287     }
288     mixin(GenerateCode);
289 }