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 GenerateParameterNamesList(FunctionInfo info)
151 {
152     string result = "";
153     for (int i = 0; i < info.parameterNames.length; ++i)
154     {
155         if (i > 0)
156             result ~= ", ";
157         result ~= info.parameterNames[i];
158     }
159     return result;
160 }
161 
162 string GenerateMetaTypesListForSlot(FunctionInfo info)
163 {
164     string result = GenerateMetaType(info.returnType);
165     result ~= ", ";
166     result ~= GenerateMetaTypesListForSignal(info);
167     return result;
168 }
169 
170 string GenerateMetaTypesListForSignal(FunctionInfo info)
171 {
172     string result = "";
173     for (int i = 0; i < info.parameterTypes.length; ++i)
174     {
175         if (i > 0)
176             result ~= ", ";
177         result ~= GenerateMetaType(info.parameterTypes[i]);
178     }
179     return result;
180 }
181 
182 struct FunctionInfo
183 {
184     string name;
185     string returnType;
186     string[] parameterNames;
187     string[] parameterTypes;
188     string mangle;
189 }
190 
191 struct QtInfo
192 {
193     FunctionInfo[] slots;
194     FunctionInfo[] signals;
195     QtPropertyData[] properties;
196 }
197 
198 public static QtInfo GetQtUDA(T)()
199 {
200     QtInfo result;
201 
202     foreach (property; getUDAs!(T, QtProperty)) {
203         result.properties ~= property.data;
204     }
205 
206     foreach (member; __traits(derivedMembers, T)) {
207         static if (__traits(compiles, __traits(getMember, T, member))
208                    && isSomeFunction!(__traits(getMember, T, member))) {
209             // Retrieve the UDA
210             auto attributes = __traits(getAttributes, __traits(getMember, T, member));
211 
212             // Turn the tuple in an array of strings
213             string[] attributeNames;
214             foreach (attribute; attributes)
215                 attributeNames ~= typeof(attribute).stringof;
216 
217             bool isSlot = attributeNames.canFind("QtSlot");
218             bool isSignal = attributeNames.canFind("QtSignal");
219 
220             // Extract the Function Return Type and Arguments
221             if (isSlot || isSignal) {
222                 FunctionInfo info;
223                 info.mangle = __traits(getMember, T, member).mangleof;
224                 info.name = member;
225                 info.returnType = ReturnType!(__traits(getMember, T, member)).stringof;
226                 
227                 foreach (param; ParameterIdentifierTuple!(__traits(getMember, T, member)))
228                     info.parameterNames ~= param.stringof;
229                 
230                 foreach (param; Parameters!(__traits(getMember, T, member)))
231                     info.parameterTypes ~= param.stringof;
232 
233                 if (isSlot)
234                     result.slots ~= info;
235 
236                 if (isSignal)
237                     result.signals ~= info;
238             }
239         }
240     }
241 
242     return result;
243 }
244 
245 public static string GenerateMetaObject(string qobjectSuperClassName, QtInfo info)
246 {
247     string result =
248         "shared static this() { m_staticMetaObject = createMetaObject(); }\n" ~
249         "private static QMetaObject m_staticMetaObject;\n" ~
250         "public static QMetaObject staticMetaObject() { return m_staticMetaObject; }\n" ~
251         "public override QMetaObject metaObject() { return staticMetaObject(); }\n"~
252         "private static QMetaObject createMetaObject() {\n" ~
253         "  QMetaObject superMetaObject = " ~ qobjectSuperClassName ~ ".staticMetaObject();\n" ~
254         "  SignalDefinition[] signals = [];\n" ~
255         "  SlotDefinition[] slots = [];\n" ~
256         "  PropertyDefinition[] properties = [];\n";
257 
258     foreach(FunctionInfo signal; info.signals) {
259         string name = signal.name;
260         string parameterNames = GenerateParameterNamesList(signal);
261         string parameterTypes = GenerateMetaTypesListForSignal(signal);
262         result ~= format("  signals ~= SignalDefinition(\"%s\",[%s], [%s]);\n", signal.name, parameterNames, parameterTypes);
263     }
264 
265     foreach(FunctionInfo slot; info.slots) {
266         string name = slot.name;
267         string returnType = GenerateMetaType(slot.returnType);
268         string parameterNames = GenerateParameterNamesList(slot);
269         string parameterTypes = GenerateMetaTypesListForSignal(slot);
270         result ~= format("  slots ~= SlotDefinition(\"%s\", %s, [%s], [%s]);\n", slot.name, returnType, parameterNames, parameterTypes);
271     }
272 
273     foreach(QtPropertyData property; info.properties) {
274         string name = property.name;
275         string type = GenerateMetaType(property.type);
276         string read = property.read;
277         string write = property.write;
278         string notify = property.notify;
279         result ~= format("  properties ~= PropertyDefinition(\"%s\", %s, \"%s\", \"%s\", \"%s\");\n", name, type, read, write, notify);
280     }
281 
282     result ~=
283         "  return new QMetaObject(superMetaObject, typeof(this).stringof, signals, slots, properties);\n}\n";
284     return result;
285 }
286 
287 public static string QObjectSuperClass(T)()
288 {
289     foreach (Type; BaseClassesTuple!T) {
290         static if (__traits(compiles, Type.staticMetaObject())) {
291             return Type.stringof;
292         }
293     }
294 }
295 
296 public mixin template Q_OBJECT()
297 {
298     private static string GenerateCode()
299     {
300         alias outerType = typeof(this);
301         alias info = GetQtUDA!outerType;
302         string result;
303         result ~= GenerateMetaObject(QObjectSuperClass!outerType, info);
304         result ~= GenerateOnSlotCalled(info);
305         result ~= GenerateSignals(info);
306         return result;
307     }
308     mixin(GenerateCode);
309 }