##// END OF EJS Templates
improved enum overload handling...
florianlink -
r57:50a844a82951
parent child
Show More
@@ -1,1129 +1,1131
1 1 /*
2 2 *
3 3 * Copyright (C) 2006 MeVis Research GmbH All Rights Reserved.
4 4 *
5 5 * This library is free software; you can redistribute it and/or
6 6 * modify it under the terms of the GNU Lesser General Public
7 7 * License as published by the Free Software Foundation; either
8 8 * version 2.1 of the License, or (at your option) any later version.
9 9 *
10 10 * This library is distributed in the hope that it will be useful,
11 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 13 * Lesser General Public License for more details.
14 14 *
15 15 * Further, this software is distributed without any warranty that it is
16 16 * free of the rightful claim of any third person regarding infringement
17 17 * or the like. Any license provided herein, whether implied or
18 18 * otherwise, applies only to this software file. Patent licenses, if
19 19 * any, provided herein do not apply to combinations of this program with
20 20 * other software, or any other product whatsoever.
21 21 *
22 22 * You should have received a copy of the GNU Lesser General Public
23 23 * License along with this library; if not, write to the Free Software
24 24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 25 *
26 26 * Contact information: MeVis Research GmbH, Universitaetsallee 29,
27 27 * 28359 Bremen, Germany or:
28 28 *
29 29 * http://www.mevis.de
30 30 *
31 31 */
32 32
33 33 //----------------------------------------------------------------------------------
34 34 /*!
35 35 // \file PythonQtConversion.cpp
36 36 // \author Florian Link
37 37 // \author Last changed by $Author: florian $
38 38 // \date 2006-05
39 39 */
40 40 //----------------------------------------------------------------------------------
41 41
42 42 #include "PythonQtConversion.h"
43 43 #include "PythonQtVariants.h"
44 44 #include <QDateTime>
45 45 #include <QTime>
46 46 #include <QDate>
47 47
48 48 PythonQtValueStorage<qint64, 128> PythonQtConv::global_valueStorage;
49 49 PythonQtValueStorage<void*, 128> PythonQtConv::global_ptrStorage;
50 50 PythonQtValueStorage<QVariant, 32> PythonQtConv::global_variantStorage;
51 51
52 52 QHash<int, PythonQtConvertMetaTypeToPythonCB*> PythonQtConv::_metaTypeToPythonConverters;
53 53 QHash<int, PythonQtConvertPythonToMetaTypeCB*> PythonQtConv::_pythonToMetaTypeConverters;
54 54
55 55 PyObject* PythonQtConv::GetPyBool(bool val)
56 56 {
57 57 PyObject* r = val?Py_True:Py_False;
58 58 Py_INCREF(r);
59 59 return r;
60 60 }
61 61
62 62 PyObject* PythonQtConv::ConvertQtValueToPython(const PythonQtMethodInfo::ParameterInfo& info, const void* data) {
63 63 // is it an enum value?
64 64 if (info.enumWrapper) {
65 65 if (!info.isPointer) {
66 66 return PythonQtPrivate::createEnumValueInstance(info.enumWrapper, *((unsigned int*)data));
67 67 } else {
68 68 // we do not support pointers to enums (who needs them?)
69 69 Py_INCREF(Py_None);
70 70 return Py_None;
71 71 }
72 72 }
73 73
74 74 if (info.typeId == QMetaType::Void) {
75 75 Py_INCREF(Py_None);
76 76 return Py_None;
77 77 } else if (info.isPointer && (info.typeId == QMetaType::Char)) {
78 78 // a char ptr will probably be a null terminated string, so we support that:
79 79 return PyString_FromString(*((char**)data));
80 80 } else if ((info.typeId == PythonQtMethodInfo::Unknown || info.typeId >= QMetaType::User) &&
81 81 info.name.startsWith("QList<")) {
82 82 // it is a QList template:
83 83 // (TODO: check what happens if this is a pointer type?!)
84 84 QByteArray innerType = info.name.mid(6,info.name.length()-7);
85 85 if (innerType.endsWith("*")) {
86 86 innerType.truncate(innerType.length()-1);
87 87 return ConvertQListOfPointerTypeToPythonList((QList<void*>*)data, innerType);
88 88 }
89 89 }
90 90
91 91 if (info.typeId >= QMetaType::User) {
92 92 // if a converter is registered, we use is:
93 93 PythonQtConvertMetaTypeToPythonCB* converter = _metaTypeToPythonConverters.value(info.typeId);
94 94 if (converter) {
95 95 return (*converter)(data, info.typeId);
96 96 }
97 97 }
98 98
99 99 // special handling did not match, so we convert the usual way (either pointer or value version):
100 100 if (info.isPointer) {
101 101 // convert the pointer to a Python Object (we can handle ANY C++ object, in the worst case we just know the type and the pointer)
102 102 return PythonQt::priv()->wrapPtr(*((void**)data), info.name);
103 103 } else {
104 104 // handle values that are not yet handled and not pointers
105 105 return ConvertQtValueToPythonInternal(info.typeId, data);
106 106 }
107 107 }
108 108
109 109 PyObject* PythonQtConv::ConvertQtValueToPythonInternal(int type, const void* data) {
110 110 switch (type) {
111 111 case QMetaType::Void:
112 112 Py_INCREF(Py_None);
113 113 return Py_None;
114 114 case QMetaType::Char:
115 115 return PyInt_FromLong(*((char*)data));
116 116 case QMetaType::UChar:
117 117 return PyInt_FromLong(*((unsigned char*)data));
118 118 case QMetaType::Short:
119 119 return PyInt_FromLong(*((short*)data));
120 120 case QMetaType::UShort:
121 121 return PyInt_FromLong(*((unsigned short*)data));
122 122 case QMetaType::Long:
123 123 return PyInt_FromLong(*((long*)data));
124 124 case QMetaType::ULong:
125 125 // does not fit into simple int of python
126 126 return PyLong_FromUnsignedLong(*((unsigned long*)data));
127 127 case QMetaType::Bool:
128 128 return PythonQtConv::GetPyBool(*((bool*)data));
129 129 case QMetaType::Int:
130 130 return PyInt_FromLong(*((int*)data));
131 131 case QMetaType::UInt:
132 132 return PyInt_FromLong(*((unsigned int*)data));
133 133 case QMetaType::QChar:
134 134 return PyInt_FromLong(*((short*)data));
135 135 case QMetaType::Float:
136 136 return PyFloat_FromDouble(*((float*)data));
137 137 case QMetaType::Double:
138 138 return PyFloat_FromDouble(*((double*)data));
139 139 case QMetaType::LongLong:
140 140 return PyLong_FromLongLong(*((qint64*)data));
141 141 case QMetaType::ULongLong:
142 142 return PyLong_FromUnsignedLongLong(*((quint64*)data));
143 143 case QMetaType::QByteArray: {
144 144 QByteArray* v = (QByteArray*) data;
145 145 return PyString_FromStringAndSize(*v, v->size());
146 146 }
147 147 case QMetaType::QVariantMap:
148 148 return PythonQtConv::QVariantMapToPyObject(*((QVariantMap*)data));
149 149 case QMetaType::QVariantList:
150 150 return PythonQtConv::QVariantListToPyObject(*((QVariantList*)data));
151 151 case QMetaType::QString:
152 152 return PythonQtConv::QStringToPyObject(*((QString*)data));
153 153 case QMetaType::QStringList:
154 154 return PythonQtConv::QStringListToPyObject(*((QStringList*)data));
155 155
156 156 case PythonQtMethodInfo::Variant:
157 157 return PythonQtConv::QVariantToPyObject(*((QVariant*)data));
158 158 case QMetaType::QObjectStar:
159 159 case QMetaType::QWidgetStar:
160 160 return PythonQt::priv()->wrapQObject(*((QObject**)data));
161 161
162 162 default:
163 163 if (PythonQt::priv()->isPythonQtObjectPtrMetaId(type)) {
164 164 // special case, it is a PythonQtObjectPtr which contains a PyObject, take it directly:
165 165 PyObject* o = ((PythonQtObjectPtr*)data)->object();
166 166 Py_INCREF(o);
167 167 return o;
168 168 } else {
169 169 if (type > 0) {
170 170 // if the type is known, we can construct it via QMetaType::construct
171 171 void* newCPPObject = QMetaType::construct(type, data);
172 172 // XXX this could be optimized by using metatypeid directly
173 173 PythonQtInstanceWrapper* wrap = (PythonQtInstanceWrapper*)PythonQt::priv()->wrapPtr(newCPPObject, QMetaType::typeName(type));
174 174 wrap->_ownedByPythonQt = true;
175 175 wrap->_useQMetaTypeDestroy = true;
176 176 return (PyObject*)wrap;
177 177 }
178 178 std::cerr << "Unknown type that can not be converted to Python: " << type << ", in " << __FILE__ << ":" << __LINE__ << std::endl;
179 179 }
180 180 }
181 181 Py_INCREF(Py_None);
182 182 return Py_None;
183 183 }
184 184
185 185 void* PythonQtConv::CreateQtReturnValue(const PythonQtMethodInfo::ParameterInfo& info) {
186 186 void* ptr = NULL;
187 187 if (info.isPointer) {
188 188 PythonQtValueStorage_ADD_VALUE(global_ptrStorage, void*, NULL, ptr);
189 189 } else if (info.enumWrapper) {
190 190 // create enum return value
191 191 PythonQtValueStorage_ADD_VALUE(PythonQtConv::global_valueStorage, long, 0, ptr);
192 192 } else {
193 193 switch (info.typeId) {
194 194 case QMetaType::Char:
195 195 case QMetaType::UChar:
196 196 case QMetaType::Short:
197 197 case QMetaType::UShort:
198 198 case QMetaType::Long:
199 199 case QMetaType::ULong:
200 200 case QMetaType::Bool:
201 201 case QMetaType::Int:
202 202 case QMetaType::UInt:
203 203 case QMetaType::QChar:
204 204 case QMetaType::Float:
205 205 case QMetaType::Double:
206 206 PythonQtValueStorage_ADD_VALUE(global_valueStorage, long, 0, ptr);
207 207 break;
208 208 case PythonQtMethodInfo::Variant:
209 209 PythonQtValueStorage_ADD_VALUE(global_variantStorage, QVariant, 0, ptr);
210 210 // return the ptr to the variant
211 211 break;
212 212 default:
213 213 if (info.typeId == PythonQtMethodInfo::Unknown) {
214 214 // check if we have a QList of pointers, which we can circumvent with a QList<void*>
215 215 if (info.name.startsWith("QList<")) {
216 216 QByteArray innerType = info.name.mid(6,info.name.length()-7);
217 217 if (innerType.endsWith("*")) {
218 218 static int id = QMetaType::type("QList<void*>");
219 219 PythonQtValueStorage_ADD_VALUE(global_variantStorage, QVariant, QVariant::Type(id), ptr);
220 220 // return the constData pointer that will be filled with the result value later on
221 221 ptr = (void*)((QVariant*)ptr)->constData();
222 222 }
223 223 }
224 224 }
225 225
226 226 if (!ptr && info.typeId!=PythonQtMethodInfo::Unknown) {
227 227 // everything else is stored in a QVariant, if we know the meta type...
228 228 PythonQtValueStorage_ADD_VALUE(global_variantStorage, QVariant, QVariant::Type(info.typeId), ptr);
229 229 // return the constData pointer that will be filled with the result value later on
230 230 ptr = (void*)((QVariant*)ptr)->constData();
231 231 }
232 232 }
233 233 }
234 234 return ptr;
235 235 }
236 236
237 237 void* PythonQtConv::castWrapperTo(PythonQtInstanceWrapper* wrapper, const QByteArray& className, bool& ok)
238 238 {
239 239 void* object;
240 240 if (wrapper->classInfo()->isCPPWrapper()) {
241 241 object = wrapper->_wrappedPtr;
242 242 } else {
243 243 QObject* tmp = wrapper->_obj;
244 244 object = tmp;
245 245 }
246 246 if (object) {
247 247 // if we can be upcasted to the given name, we pass the casted pointer in:
248 248 object = wrapper->classInfo()->castTo(object, className);
249 249 ok = object!=NULL;
250 250 } else {
251 251 // if it is a NULL ptr, we need to check if it inherits, so that we might pass the NULL ptr
252 252 ok = wrapper->classInfo()->inherits(className);
253 253 }
254 254 return object;
255 255 }
256 256
257 257 void* PythonQtConv::ConvertPythonToQt(const PythonQtMethodInfo::ParameterInfo& info, PyObject* obj, bool strict, PythonQtClassInfo* /*classInfo*/, void* alreadyAllocatedCPPObject)
258 258 {
259 259 bool ok;
260 260 void* ptr = NULL;
261 261 if (PyObject_TypeCheck(obj, &PythonQtInstanceWrapper_Type) && info.typeId != PythonQtMethodInfo::Variant) {
262 262 // if we have a Qt wrapper object and if we do not need a QVariant, we do the following:
263 263 // (the Variant case is handled below in a switch)
264 264
265 265 // a C++ wrapper (can be passed as pointer or reference)
266 266 PythonQtInstanceWrapper* wrap = (PythonQtInstanceWrapper*)obj;
267 267 bool ok;
268 268 void* object = castWrapperTo(wrap, info.name, ok);
269 269 if (ok) {
270 270 if (info.isPointer) {
271 271 // store the wrapped pointer in an extra pointer and let ptr point to the extra pointer
272 272 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_ptrStorage, void*, object, ptr);
273 273 } else {
274 274 // store the wrapped pointer directly, since we are a reference
275 275 ptr = object;
276 276 }
277 277 } else {
278 278 // not matching
279 279 }
280 280 } else if (info.isPointer) {
281 281 // a pointer
282 282 if (info.typeId == QMetaType::Char || info.typeId == QMetaType::UChar)
283 283 {
284 284 QString str = PyObjGetString(obj, strict, ok);
285 285 if (ok) {
286 286 void* ptr2 = NULL;
287 287 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_variantStorage, QVariant, QVariant(str.toUtf8()), ptr2);
288 288 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_ptrStorage, void*, (((QByteArray*)((QVariant*)ptr2)->constData())->data()), ptr);
289 289 }
290 290 } else if (info.name == "PyObject") {
291 291 // handle low level PyObject directly
292 292 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_ptrStorage, void*, obj, ptr);
293 293 } else if (obj == Py_None) {
294 294 // None is treated as a NULL ptr
295 295 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_ptrStorage, void*, NULL, ptr);
296 296 } else {
297 297 // if we are not strict, we try if we are passed a 0 integer
298 298 if (!strict) {
299 299 bool ok;
300 300 int value = PyObjGetInt(obj, true, ok);
301 301 if (ok && value==0) {
302 // TODOXXX is this wise? or should it be expected from the programmer to use None?
302 303 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_ptrStorage, void*, NULL, ptr);
303 304 }
304 305 }
305 306 }
306 307 } else {
307 308 // not a pointer
308 309 switch (info.typeId) {
309 310 case QMetaType::Char:
310 311 {
311 312 int val = PyObjGetInt(obj, strict, ok);
312 313 if (ok) {
313 314 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, char, val, ptr);
314 315 }
315 316 }
316 317 break;
317 318 case QMetaType::UChar:
318 319 {
319 320 int val = PyObjGetInt(obj, strict, ok);
320 321 if (ok) {
321 322 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, unsigned char, val, ptr);
322 323 }
323 324 }
324 325 break;
325 326 case QMetaType::Short:
326 327 {
327 328 int val = PyObjGetInt(obj, strict, ok);
328 329 if (ok) {
329 330 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, short, val, ptr);
330 331 }
331 332 }
332 333 break;
333 334 case QMetaType::UShort:
334 335 {
335 336 int val = PyObjGetInt(obj, strict, ok);
336 337 if (ok) {
337 338 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, unsigned short, val, ptr);
338 339 }
339 340 }
340 341 break;
341 342 case QMetaType::Long:
342 343 {
343 344 long val = (long)PyObjGetLongLong(obj, strict, ok);
344 345 if (ok) {
345 346 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, long, val, ptr);
346 347 }
347 348 }
348 349 break;
349 350 case QMetaType::ULong:
350 351 {
351 352 unsigned long val = (unsigned long)PyObjGetLongLong(obj, strict, ok);
352 353 if (ok) {
353 354 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, unsigned long, val, ptr);
354 355 }
355 356 }
356 357 break;
357 358 case QMetaType::Bool:
358 359 {
359 360 bool val = PyObjGetBool(obj, strict, ok);
360 361 if (ok) {
361 362 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, bool, val, ptr);
362 363 }
363 364 }
364 365 break;
365 366 case QMetaType::Int:
366 367 {
367 368 int val = PyObjGetInt(obj, strict, ok);
368 369 if (ok) {
369 370 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, int, val, ptr);
370 371 }
371 372 }
372 373 break;
373 374 case QMetaType::UInt:
374 375 {
375 376 unsigned int val = (unsigned int)PyObjGetLongLong(obj, strict, ok);
376 377 if (ok) {
377 378 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, unsigned int, val, ptr);
378 379 }
379 380 }
380 381 break;
381 382 case QMetaType::QChar:
382 383 {
383 384 int val = PyObjGetInt(obj, strict, ok);
384 385 if (ok) {
385 386 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, short, val, ptr);
386 387 }
387 388 }
388 389 break;
389 390 case QMetaType::Float:
390 391 {
391 392 float val = (float)PyObjGetDouble(obj, strict, ok);
392 393 if (ok) {
393 394 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, float, val, ptr);
394 395 }
395 396 }
396 397 break;
397 398 case QMetaType::Double:
398 399 {
399 400 double val = (double)PyObjGetDouble(obj, strict, ok);
400 401 if (ok) {
401 402 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, double, val, ptr);
402 403 }
403 404 }
404 405 break;
405 406 case QMetaType::LongLong:
406 407 {
407 408 qint64 val = PyObjGetLongLong(obj, strict, ok);
408 409 if (ok) {
409 410 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, qint64, val, ptr);
410 411 }
411 412 }
412 413 break;
413 414 case QMetaType::ULongLong:
414 415 {
415 416 quint64 val = PyObjGetULongLong(obj, strict, ok);
416 417 if (ok) {
417 418 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, quint64, val, ptr);
418 419 }
419 420 }
420 421 break;
421 422 case QMetaType::QByteArray:
422 423 {
423 424 QByteArray bytes = PyObjGetBytes(obj, strict, ok);
424 425 if (ok) {
425 426 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_variantStorage, QVariant, QVariant(bytes), ptr);
426 427 ptr = (void*)((QVariant*)ptr)->constData();
427 428 }
428 429 }
429 430 break;
430 431 case QMetaType::QString:
431 432 {
432 433 QString str = PyObjGetString(obj, strict, ok);
433 434 if (ok) {
434 435 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_variantStorage, QVariant, QVariant(str), ptr);
435 436 ptr = (void*)((QVariant*)ptr)->constData();
436 437 }
437 438 }
438 439 break;
439 440 case QMetaType::QStringList:
440 441 {
441 442 QStringList l = PyObjToStringList(obj, strict, ok);
442 443 if (ok) {
443 444 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_variantStorage, QVariant, QVariant(l), ptr);
444 445 ptr = (void*)((QVariant*)ptr)->constData();
445 446 }
446 447 }
447 448 break;
448 449
449 450 case PythonQtMethodInfo::Variant:
450 451 {
451 452 QVariant v = PyObjToQVariant(obj);
452 453 if (v.isValid()) {
453 454 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_variantStorage, QVariant, v, ptr);
454 455 }
455 456 }
456 457 break;
457 458 default:
458 459 {
459 460 // check for enum case
460 461 if (info.enumWrapper) {
461 462 unsigned int val;
462 463 if ((PyObject*)obj->ob_type == info.enumWrapper) {
463 // we have a direct enum type match:
464 // we have a exact enum type match:
464 465 val = PyInt_AS_LONG(obj);
465 466 ok = true;
466 } else {
467 // we try to get an integer, and in strict mode, it may not be a derived int class, so that no other enum can be taken as an int
468 val = (unsigned int)PyObjGetLongLong(obj, strict, ok);
467 } else if (!strict) {
468 // we try to get any integer, when not being strict. If we are strict, integers are not wanted because
469 // we want an integer overload to be taken first!
470 val = (unsigned int)PyObjGetLongLong(obj, false, ok);
469 471 }
470 472 if (ok) {
471 473 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, unsigned int, val, ptr);
472 474 return ptr;
473 475 } else {
474 476 return NULL;
475 477 }
476 478 }
477 479
478 480 if (info.typeId == PythonQtMethodInfo::Unknown || info.typeId >= QMetaType::User) {
479 481 // check for QList<AnyPtr*> case, where we will use a QList<void*> QVariant
480 482 if (info.name.startsWith("QList<")) {
481 483 QByteArray innerType = info.name.mid(6,info.name.length()-7);
482 484 if (innerType.endsWith("*")) {
483 485 innerType.truncate(innerType.length()-1);
484 486 static int id = QMetaType::type("QList<void*>");
485 487 if (!alreadyAllocatedCPPObject) {
486 488 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_variantStorage, QVariant, QVariant::Type(id), ptr);
487 489 ptr = (void*)((QVariant*)ptr)->constData();
488 490 } else {
489 491 ptr = alreadyAllocatedCPPObject;
490 492 }
491 493 ok = ConvertPythonListToQListOfPointerType(obj, (QList<void*>*)ptr, innerType, strict);
492 494 if (ok) {
493 495 return ptr;
494 496 } else {
495 497 return NULL;
496 498 }
497 499 }
498 500 }
499 501 }
500 502
501 503 // We only do this for registered type > QMetaType::User for performance reasons.
502 504 if (info.typeId >= QMetaType::User) {
503 505 // Maybe we have a special converter that is registered for that type:
504 506 PythonQtConvertPythonToMetaTypeCB* converter = _pythonToMetaTypeConverters.value(info.typeId);
505 507 if (converter) {
506 508 if (!alreadyAllocatedCPPObject) {
507 509 // create a new empty variant of concrete type:
508 510 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_variantStorage, QVariant, QVariant::Type(info.typeId), ptr);
509 511 ptr = (void*)((QVariant*)ptr)->constData();
510 512 } else {
511 513 ptr = alreadyAllocatedCPPObject;
512 514 }
513 515 // now call the converter, passing the internal object of the variant
514 516 ok = (*converter)(obj, ptr, info.typeId, strict);
515 517 if (ok) {
516 518 return ptr;
517 519 } else {
518 520 return NULL;
519 521 }
520 522 }
521 523 }
522 524 // if no type id is available, conversion to a QVariant makes no sense/is not possible
523 525 if (info.typeId != PythonQtMethodInfo::Unknown) {
524 526 // for all other types, we use the same qvariant conversion and pass out the constData of the variant:
525 527 QVariant v = PyObjToQVariant(obj, info.typeId);
526 528 if (v.isValid()) {
527 529 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_variantStorage, QVariant, v, ptr);
528 530 ptr = (void*)((QVariant*)ptr)->constData();
529 531 }
530 532 }
531 533 }
532 534 }
533 535 }
534 536 return ptr;
535 537 }
536 538
537 539
538 540 QStringList PythonQtConv::PyObjToStringList(PyObject* val, bool strict, bool& ok) {
539 541 QStringList v;
540 542 ok = false;
541 543 // if we are strict, we do not want to convert a string to a stringlist
542 544 // (strings in python are detected to be sequences)
543 545 if (strict &&
544 546 (val->ob_type == &PyString_Type ||
545 547 PyUnicode_Check(val))) {
546 548 ok = false;
547 549 return v;
548 550 }
549 551 if (PySequence_Check(val)) {
550 552 int count = PySequence_Size(val);
551 553 for (int i = 0;i<count;i++) {
552 554 PyObject* value = PySequence_GetItem(val,i);
553 555 v.append(PyObjGetString(value,false,ok));
554 556 }
555 557 ok = true;
556 558 }
557 559 return v;
558 560 }
559 561
560 562 QString PythonQtConv::PyObjGetRepresentation(PyObject* val)
561 563 {
562 564 QString r;
563 565 PyObject* str = PyObject_Repr(val);
564 566 if (str) {
565 567 r = QString(PyString_AS_STRING(str));
566 568 Py_DECREF(str);
567 569 }
568 570 return r;
569 571 }
570 572
571 573 QString PythonQtConv::PyObjGetString(PyObject* val, bool strict, bool& ok) {
572 574 QString r;
573 575 ok = true;
574 576 if (val->ob_type == &PyString_Type) {
575 577 r = QString(PyString_AS_STRING(val));
576 578 } else if (PyUnicode_Check(val)) {
577 579 #ifdef WIN32
578 580 r = QString::fromUtf16(PyUnicode_AS_UNICODE(val));
579 581 #else
580 582 PyObject *ptmp = PyUnicode_AsUTF8String(val);
581 583 if(ptmp) {
582 584 r = QString::fromUtf8(PyString_AS_STRING(ptmp));
583 585 Py_DECREF(ptmp);
584 586 }
585 587 #endif
586 588 } else if (!strict) {
587 589 // EXTRA: could also use _Unicode, but why should we?
588 590 PyObject* str = PyObject_Str(val);
589 591 if (str) {
590 592 r = QString(PyString_AS_STRING(str));
591 593 Py_DECREF(str);
592 594 } else {
593 595 ok = false;
594 596 }
595 597 } else {
596 598 ok = false;
597 599 }
598 600 return r;
599 601 }
600 602
601 603 QByteArray PythonQtConv::PyObjGetBytes(PyObject* val, bool /*strict*/, bool& ok) {
602 604 QByteArray r;
603 605 ok = true;
604 606 if (val->ob_type == &PyString_Type) {
605 607 long size = PyString_GET_SIZE(val);
606 608 r = QByteArray(PyString_AS_STRING(val), size);
607 609 } else {
608 610 ok = false;
609 611 }
610 612 return r;
611 613 }
612 614
613 615 bool PythonQtConv::PyObjGetBool(PyObject* val, bool strict, bool &ok) {
614 616 bool d = false;
615 617 ok = false;
616 618 if (val == Py_False) {
617 619 d = false;
618 620 ok = true;
619 621 } else if (val == Py_True) {
620 622 d = true;
621 623 ok = true;
622 624 } else if (!strict) {
623 625 d = PyObjGetInt(val, false, ok)!=0;
624 626 ok = true;
625 627 }
626 628 return d;
627 629 }
628 630
629 631 int PythonQtConv::PyObjGetInt(PyObject* val, bool strict, bool &ok) {
630 632 int d = 0;
631 633 ok = true;
632 634 if (val->ob_type == &PyInt_Type) {
633 635 d = PyInt_AS_LONG(val);
634 636 } else if (!strict) {
635 637 if (PyObject_TypeCheck(val, &PyInt_Type)) {
636 638 // support for derived int classes, e.g. for our enums
637 639 d = PyInt_AS_LONG(val);
638 640 } else if (val->ob_type == &PyFloat_Type) {
639 641 d = floor(PyFloat_AS_DOUBLE(val));
640 642 } else if (val->ob_type == &PyLong_Type) {
641 643 // handle error on overflow!
642 644 d = PyLong_AsLong(val);
643 645 } else if (val == Py_False) {
644 646 d = 0;
645 647 } else if (val == Py_True) {
646 648 d = 1;
647 649 } else {
648 650 ok = false;
649 651 }
650 652 } else {
651 653 ok = false;
652 654 }
653 655 return d;
654 656 }
655 657
656 658 qint64 PythonQtConv::PyObjGetLongLong(PyObject* val, bool strict, bool &ok) {
657 659 qint64 d = 0;
658 660 ok = true;
659 661 if (val->ob_type == &PyInt_Type) {
660 662 d = PyInt_AS_LONG(val);
661 663 } else if (val->ob_type == &PyLong_Type) {
662 664 d = PyLong_AsLongLong(val);
663 665 } else if (!strict) {
664 666 if (PyObject_TypeCheck(val, &PyInt_Type)) {
665 667 // support for derived int classes, e.g. for our enums
666 668 d = PyInt_AS_LONG(val);
667 669 } else if (val->ob_type == &PyFloat_Type) {
668 670 d = floor(PyFloat_AS_DOUBLE(val));
669 671 } else if (val == Py_False) {
670 672 d = 0;
671 673 } else if (val == Py_True) {
672 674 d = 1;
673 675 } else {
674 676 ok = false;
675 677 }
676 678 } else {
677 679 ok = false;
678 680 }
679 681 return d;
680 682 }
681 683
682 684 quint64 PythonQtConv::PyObjGetULongLong(PyObject* val, bool strict, bool &ok) {
683 685 quint64 d = 0;
684 686 ok = true;
685 687 if (PyObject_TypeCheck(val, &PyInt_Type)) {
686 688 d = PyInt_AS_LONG(val);
687 689 } else if (val->ob_type == &PyLong_Type) {
688 690 d = PyLong_AsLongLong(val);
689 691 } else if (!strict) {
690 692 if (PyObject_TypeCheck(val, &PyInt_Type)) {
691 693 // support for derived int classes, e.g. for our enums
692 694 d = PyInt_AS_LONG(val);
693 695 } else if (val->ob_type == &PyFloat_Type) {
694 696 d = floor(PyFloat_AS_DOUBLE(val));
695 697 } else if (val == Py_False) {
696 698 d = 0;
697 699 } else if (val == Py_True) {
698 700 d = 1;
699 701 } else {
700 702 ok = false;
701 703 }
702 704 } else {
703 705 ok = false;
704 706 }
705 707 return d;
706 708 }
707 709
708 710 double PythonQtConv::PyObjGetDouble(PyObject* val, bool strict, bool &ok) {
709 711 double d = 0;
710 712 ok = true;
711 713 if (val->ob_type == &PyFloat_Type) {
712 714 d = PyFloat_AS_DOUBLE(val);
713 715 } else if (!strict) {
714 716 if (PyObject_TypeCheck(val, &PyInt_Type)) {
715 717 d = PyInt_AS_LONG(val);
716 718 } else if (val->ob_type == &PyLong_Type) {
717 719 d = PyLong_AsLong(val);
718 720 } else if (val == Py_False) {
719 721 d = 0;
720 722 } else if (val == Py_True) {
721 723 d = 1;
722 724 } else {
723 725 ok = false;
724 726 }
725 727 } else {
726 728 ok = false;
727 729 }
728 730 return d;
729 731 }
730 732
731 733 QVariant PythonQtConv::PyObjToQVariant(PyObject* val, int type)
732 734 {
733 735 QVariant v;
734 736 bool ok = true;
735 737
736 738 if (type==-1) {
737 739 // no special type requested
738 740 if (val->ob_type==&PyString_Type || val->ob_type==&PyUnicode_Type) {
739 741 type = QVariant::String;
740 742 } else if (PyObject_TypeCheck(val, &PyInt_Type)) {
741 743 type = QVariant::Int;
742 744 } else if (val->ob_type==&PyLong_Type) {
743 745 type = QVariant::LongLong;
744 746 } else if (val->ob_type==&PyFloat_Type) {
745 747 type = QVariant::Double;
746 748 } else if (val == Py_False || val == Py_True) {
747 749 type = QVariant::Bool;
748 750 } else if (PyObject_TypeCheck(val, &PythonQtInstanceWrapper_Type)) {
749 751 PythonQtInstanceWrapper* wrap = (PythonQtInstanceWrapper*)val;
750 752 // c++ wrapper, check if the class names of the c++ objects match
751 753 if (wrap->classInfo()->isCPPWrapper()) {
752 754 if (wrap->classInfo()->metaTypeId()>0) {
753 755 // construct a new variant from the C++ object if it has a meta type (this will COPY the object!)
754 756 v = QVariant(wrap->classInfo()->metaTypeId(), wrap->_wrappedPtr);
755 757 } else {
756 758 // TODOXXX we could as well check if there is a registered meta type for "classname*", so that we may pass
757 759 // the pointer here...
758 760 // is this worth anything? we loose the knowledge of the cpp object type
759 761 v = qVariantFromValue(wrap->_wrappedPtr);
760 762 }
761 763 } else {
762 764 // this gives us a QObject pointer
763 765 QObject* myObject = wrap->_obj;
764 766 v = qVariantFromValue(myObject);
765 767 }
766 768 return v;
767 769 } else if (val->ob_type==&PyDict_Type) {
768 770 type = QVariant::Map;
769 771 } else if (val->ob_type==&PyList_Type || val->ob_type==&PyTuple_Type || PySequence_Check(val)) {
770 772 type = QVariant::List;
771 773 } else if (val == Py_None) {
772 774 // none is invalid
773 775 type = QVariant::Invalid;
774 776 } else {
775 777 // this used to be:
776 778 // type = QVariant::String;
777 779 // but now we want to transport the Python Objects directly:
778 780 PythonQtObjectPtr o(val);
779 781 v = qVariantFromValue(o);
780 782 return v;
781 783 }
782 784 }
783 785 // special type request:
784 786 switch (type) {
785 787 case QVariant::Invalid:
786 788 return v;
787 789 break;
788 790 case QVariant::Int:
789 791 {
790 792 int d = PyObjGetInt(val, false, ok);
791 793 if (ok) return QVariant(d);
792 794 }
793 795 break;
794 796 case QVariant::UInt:
795 797 {
796 798 int d = PyObjGetInt(val, false,ok);
797 799 if (ok) v = QVariant((unsigned int)d);
798 800 }
799 801 break;
800 802 case QVariant::Bool:
801 803 {
802 804 int d = PyObjGetBool(val,false,ok);
803 805 if (ok) v = QVariant((bool)(d!=0));
804 806 }
805 807 break;
806 808 case QVariant::Double:
807 809 {
808 810 double d = PyObjGetDouble(val,false,ok);
809 811 if (ok) v = QVariant(d);
810 812 break;
811 813 }
812 814 case QMetaType::Float:
813 815 {
814 816 float d = (float) PyObjGetDouble(val,false,ok);
815 817 if (ok) v = qVariantFromValue(d);
816 818 break;
817 819 }
818 820 case QMetaType::Long:
819 821 {
820 822 long d = (long) PyObjGetLongLong(val,false,ok);
821 823 if (ok) v = qVariantFromValue(d);
822 824 break;
823 825 }
824 826 case QMetaType::ULong:
825 827 {
826 828 unsigned long d = (unsigned long) PyObjGetLongLong(val,false,ok);
827 829 if (ok) v = qVariantFromValue(d);
828 830 break;
829 831 }
830 832 case QMetaType::Short:
831 833 {
832 834 short d = (short) PyObjGetInt(val,false,ok);
833 835 if (ok) v = qVariantFromValue(d);
834 836 break;
835 837 }
836 838 case QMetaType::UShort:
837 839 {
838 840 unsigned short d = (unsigned short) PyObjGetInt(val,false,ok);
839 841 if (ok) v = qVariantFromValue(d);
840 842 break;
841 843 }
842 844 case QMetaType::Char:
843 845 {
844 846 char d = (char) PyObjGetInt(val,false,ok);
845 847 if (ok) v = qVariantFromValue(d);
846 848 break;
847 849 }
848 850 case QMetaType::UChar:
849 851 {
850 852 unsigned char d = (unsigned char) PyObjGetInt(val,false,ok);
851 853 if (ok) v = qVariantFromValue(d);
852 854 break;
853 855 }
854 856
855 857 case QVariant::ByteArray:
856 858 case QVariant::String:
857 859 {
858 860 bool ok;
859 861 v = QVariant(PyObjGetString(val, false, ok));
860 862 }
861 863 break;
862 864
863 865 // these are important for MeVisLab
864 866 case QVariant::Map:
865 867 {
866 868 if (PyMapping_Check(val)) {
867 869 QMap<QString,QVariant> map;
868 870 PyObject* items = PyMapping_Items(val);
869 871 if (items) {
870 872 int count = PyList_Size(items);
871 873 PyObject* value;
872 874 PyObject* key;
873 875 PyObject* tuple;
874 876 for (int i = 0;i<count;i++) {
875 877 tuple = PyList_GetItem(items,i);
876 878 key = PyTuple_GetItem(tuple, 0);
877 879 value = PyTuple_GetItem(tuple, 1);
878 880 map.insert(PyObjGetString(key), PyObjToQVariant(value,-1));
879 881 }
880 882 Py_DECREF(items);
881 883 v = map;
882 884 }
883 885 }
884 886 }
885 887 break;
886 888 case QVariant::List:
887 889 if (PySequence_Check(val)) {
888 890 QVariantList list;
889 891 int count = PySequence_Size(val);
890 892 PyObject* value;
891 893 for (int i = 0;i<count;i++) {
892 894 value = PySequence_GetItem(val,i);
893 895 list.append(PyObjToQVariant(value, -1));
894 896 }
895 897 v = list;
896 898 }
897 899 break;
898 900 case QVariant::StringList:
899 901 {
900 902 bool ok;
901 903 QStringList l = PyObjToStringList(val, false, ok);
902 904 if (ok) {
903 905 v = l;
904 906 }
905 907 }
906 908 break;
907 909
908 910 default:
909 911 if (PyObject_TypeCheck(val, &PythonQtInstanceWrapper_Type)) {
910 912 PythonQtInstanceWrapper* wrap = (PythonQtInstanceWrapper*)val;
911 913 if (wrap->classInfo()->isCPPWrapper() && wrap->classInfo()->metaTypeId() == type) {
912 914 // construct a new variant from the C++ object if it has the same meta type
913 915 v = QVariant(type, wrap->_wrappedPtr);
914 916 } else {
915 917 v = QVariant();
916 918 }
917 919 } else {
918 920 v = QVariant();
919 921 }
920 922 }
921 923 return v;
922 924 }
923 925
924 926 PyObject* PythonQtConv::QStringToPyObject(const QString& str)
925 927 {
926 928 if (str.isNull()) {
927 929 return PyString_FromString("");
928 930 } else {
929 931 #ifdef WIN32
930 932 // return PyString_FromString(str.toLatin1().data());
931 933 return PyUnicode_FromUnicode(str.utf16(), str.length());
932 934 #else
933 935 return PyUnicode_DecodeUTF16((const char*)str.utf16(), str.length()*2, NULL, NULL);
934 936 #endif
935 937 }
936 938 }
937 939
938 940 PyObject* PythonQtConv::QStringListToPyObject(const QStringList& list)
939 941 {
940 942 PyObject* result = PyTuple_New(list.count());
941 943 int i = 0;
942 944 QString str;
943 945 foreach (str, list) {
944 946 PyTuple_SET_ITEM(result, i, PythonQtConv::QStringToPyObject(str));
945 947 i++;
946 948 }
947 949 // why is the error state bad after this?
948 950 PyErr_Clear();
949 951 return result;
950 952 }
951 953
952 954 PyObject* PythonQtConv::QStringListToPyList(const QStringList& list)
953 955 {
954 956 PyObject* result = PyList_New(list.count());
955 957 int i = 0;
956 958 for (QStringList::ConstIterator it = list.begin(); it!=list.end(); ++it) {
957 959 PyList_SET_ITEM(result, i, PythonQtConv::QStringToPyObject(*it));
958 960 i++;
959 961 }
960 962 return result;
961 963 }
962 964
963 965 PyObject* PythonQtConv::QVariantToPyObject(const QVariant& v)
964 966 {
965 967 return ConvertQtValueToPythonInternal(v.userType(), (void*)v.constData());
966 968 }
967 969
968 970 PyObject* PythonQtConv::QVariantMapToPyObject(const QVariantMap& m) {
969 971 PyObject* result = PyDict_New();
970 972 QVariantMap::const_iterator t = m.constBegin();
971 973 PyObject* key;
972 974 PyObject* val;
973 975 for (;t!=m.end();t++) {
974 976 key = QStringToPyObject(t.key());
975 977 val = QVariantToPyObject(t.value());
976 978 PyDict_SetItem(result, key, val);
977 979 Py_DECREF(key);
978 980 Py_DECREF(val);
979 981 }
980 982 return result;
981 983 }
982 984
983 985 PyObject* PythonQtConv::QVariantListToPyObject(const QVariantList& l) {
984 986 PyObject* result = PyTuple_New(l.count());
985 987 int i = 0;
986 988 QVariant v;
987 989 foreach (v, l) {
988 990 PyTuple_SET_ITEM(result, i, PythonQtConv::QVariantToPyObject(v));
989 991 i++;
990 992 }
991 993 // why is the error state bad after this?
992 994 PyErr_Clear();
993 995 return result;
994 996 }
995 997
996 998 PyObject* PythonQtConv::ConvertQListOfPointerTypeToPythonList(QList<void*>* list, const QByteArray& typeName)
997 999 {
998 1000 PyObject* result = PyTuple_New(list->count());
999 1001 int i = 0;
1000 1002 foreach (void* value, *list) {
1001 1003 PyTuple_SET_ITEM(result, i, PythonQt::priv()->wrapPtr(value, typeName));
1002 1004 i++;
1003 1005 }
1004 1006 return result;
1005 1007 }
1006 1008
1007 1009 bool PythonQtConv::ConvertPythonListToQListOfPointerType(PyObject* obj, QList<void*>* list, const QByteArray& type, bool /*strict*/)
1008 1010 {
1009 1011 bool result = false;
1010 1012 if (PySequence_Check(obj)) {
1011 1013 result = true;
1012 1014 int count = PySequence_Size(obj);
1013 1015 PyObject* value;
1014 1016 for (int i = 0;i<count;i++) {
1015 1017 value = PySequence_GetItem(obj,i);
1016 1018 if (PyObject_TypeCheck(value, &PythonQtInstanceWrapper_Type)) {
1017 1019 PythonQtInstanceWrapper* wrap = (PythonQtInstanceWrapper*)value;
1018 1020 bool ok;
1019 1021 void* object = castWrapperTo(wrap, type, ok);
1020 1022 if (ok) {
1021 1023 list->append(object);
1022 1024 } else {
1023 1025 result = false;
1024 1026 break;
1025 1027 }
1026 1028 }
1027 1029 }
1028 1030 }
1029 1031 return result;
1030 1032 }
1031 1033
1032 1034 int PythonQtConv::getInnerTemplateMetaType(const QByteArray& typeName)
1033 1035 {
1034 1036 int idx = typeName.indexOf("<");
1035 1037 if (idx>0) {
1036 1038 int idx2 = typeName.indexOf(">");
1037 1039 if (idx2>0) {
1038 1040 QByteArray innerType = typeName.mid(idx+1,idx2-idx-1);
1039 1041 return QMetaType::type(innerType.constData());
1040 1042 }
1041 1043 }
1042 1044 return QMetaType::Void;
1043 1045 }
1044 1046
1045 1047
1046 1048
1047 1049 QString PythonQtConv::qVariantToString(const QVariant& v) {
1048 1050 return CPPObjectToString(v.userType(), v.constData());
1049 1051 }
1050 1052
1051 1053 QString PythonQtConv::CPPObjectToString(int type, const void* data) {
1052 1054 QString r;
1053 1055 switch (type) {
1054 1056 case QVariant::Size: {
1055 1057 const QSize* s = static_cast<const QSize*>(data);
1056 1058 r = QString::number(s->width()) + ", " + QString::number(s->height());
1057 1059 }
1058 1060 break;
1059 1061 case QVariant::SizeF: {
1060 1062 const QSizeF* s = static_cast<const QSizeF*>(data);
1061 1063 r = QString::number(s->width()) + ", " + QString::number(s->height());
1062 1064 }
1063 1065 break;
1064 1066 case QVariant::Point: {
1065 1067 const QPoint* s = static_cast<const QPoint*>(data);
1066 1068 r = QString::number(s->x()) + ", " + QString::number(s->y());
1067 1069 }
1068 1070 break;
1069 1071 case QVariant::PointF: {
1070 1072 const QPointF* s = static_cast<const QPointF*>(data);
1071 1073 r = QString::number(s->x()) + ", " + QString::number(s->y());
1072 1074 }
1073 1075 break;
1074 1076 case QVariant::Rect: {
1075 1077 const QRect* s = static_cast<const QRect*>(data);
1076 1078 r = QString::number(s->x()) + ", " + QString::number(s->y());
1077 1079 r += ", " + QString::number(s->width()) + ", " + QString::number(s->height());
1078 1080 }
1079 1081 break;
1080 1082 case QVariant::RectF: {
1081 1083 const QRectF* s = static_cast<const QRectF*>(data);
1082 1084 r = QString::number(s->x()) + ", " + QString::number(s->y());
1083 1085 r += ", " + QString::number(s->width()) + ", " + QString::number(s->height());
1084 1086 }
1085 1087 break;
1086 1088 case QVariant::Date: {
1087 1089 const QDate* s = static_cast<const QDate*>(data);
1088 1090 r = s->toString(Qt::ISODate);
1089 1091 }
1090 1092 break;
1091 1093 case QVariant::DateTime: {
1092 1094 const QDateTime* s = static_cast<const QDateTime*>(data);
1093 1095 r = s->toString(Qt::ISODate);
1094 1096 }
1095 1097 break;
1096 1098 case QVariant::Time: {
1097 1099 const QTime* s = static_cast<const QTime*>(data);
1098 1100 r = s->toString(Qt::ISODate);
1099 1101 }
1100 1102 break;
1101 1103 case QVariant::Pixmap:
1102 1104 {
1103 1105 const QPixmap* s = static_cast<const QPixmap*>(data);
1104 1106 r = QString("Pixmap ") + QString::number(s->width()) + ", " + QString::number(s->height());
1105 1107 }
1106 1108 break;
1107 1109 case QVariant::Image:
1108 1110 {
1109 1111 const QImage* s = static_cast<const QImage*>(data);
1110 1112 r = QString("Image ") + QString::number(s->width()) + ", " + QString::number(s->height());
1111 1113 }
1112 1114 break;
1113 1115 case QVariant::Url:
1114 1116 {
1115 1117 const QUrl* s = static_cast<const QUrl*>(data);
1116 1118 r = s->toString();
1117 1119 }
1118 1120 break;
1119 1121 //TODO: add more printing for other variant types
1120 1122 default:
1121 1123 // this creates a copy, but that should not be expensive for typical simple variants
1122 1124 // (but we do not want to do this for our won user types!
1123 1125 if (type>0 && type < (int)QVariant::UserType) {
1124 1126 QVariant v(type, data);
1125 1127 r = v.toString();
1126 1128 }
1127 1129 }
1128 1130 return r;
1129 1131 }
General Comments 0
You need to be logged in to leave comments. Login now