Merge branch 'master' of git://scm.dev.nokia.troll.no/qt/qtdeclarative
[profile/ivi/qtdeclarative.git] / src / declarative / qml / qdeclarativeobjectscriptclass.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "private/qdeclarativeobjectscriptclass_p.h"
43
44 #include "private/qdeclarativeengine_p.h"
45 #include "private/qdeclarativecontext_p.h"
46 #include "private/qdeclarativedata_p.h"
47 #include "private/qdeclarativetypenamescriptclass_p.h"
48 #include "private/qdeclarativelistscriptclass_p.h"
49 #include "private/qdeclarativebinding_p.h"
50 #include "private/qdeclarativeguard_p.h"
51 #include "private/qdeclarativevmemetaobject_p.h"
52
53 #include <QtCore/qtimer.h>
54 #include <QtCore/qvarlengtharray.h>
55 #include <QtScript/qscriptcontextinfo.h>
56
57 Q_DECLARE_METATYPE(QScriptValue)
58
59 #if defined(__GNUC__)
60 # if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405
61 // The code in this file does not violate strict aliasing, but GCC thinks it does
62 // so turn off the warnings for us to have a clean build
63 #  pragma GCC diagnostic ignored "-Wstrict-aliasing"
64 # endif
65 #endif
66
67 QT_BEGIN_NAMESPACE
68
69 struct ObjectData : public QScriptDeclarativeClass::Object {
70     ObjectData(QObject *o, int t) : object(o), type(t) {
71         if (o) {
72             QDeclarativeData *ddata = QDeclarativeData::get(object, true);
73             if (ddata) ddata->objectDataRefCount++;
74         }
75     }
76
77     virtual ~ObjectData() {
78         if (object && !object->parent()) {
79             QDeclarativeData *ddata = QDeclarativeData::get(object, false);
80             if (ddata && !ddata->indestructible && 0 == --ddata->objectDataRefCount) 
81                 object->deleteLater();
82         }
83     }
84
85     QDeclarativeGuard<QObject> object;
86     int type;
87 };
88
89 /*
90     The QDeclarativeObjectScriptClass handles property access for QObjects
91     via QtScript. It is also used to provide a more useful API in
92     QtScript for QML.
93  */
94 QDeclarativeObjectScriptClass::QDeclarativeObjectScriptClass(QDeclarativeEngine *bindEngine)
95 : QScriptDeclarativeClass(QDeclarativeEnginePrivate::getScriptEngine(bindEngine)),
96   methods(bindEngine), lastData(0), engine(bindEngine)
97 {
98     QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
99
100     m_destroy = scriptEngine->newFunction(destroy);
101     m_destroyId = createPersistentIdentifier(QLatin1String("destroy"));
102     m_toString = scriptEngine->newFunction(tostring);
103     m_toStringId = createPersistentIdentifier(QLatin1String("toString"));
104 }
105
106 QDeclarativeObjectScriptClass::~QDeclarativeObjectScriptClass()
107 {
108 }
109
110 QScriptValue QDeclarativeObjectScriptClass::newQObject(QObject *object, int type)
111 {
112     QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
113
114     if (!object)
115         return scriptEngine->nullValue();
116 //        return newObject(scriptEngine, this, new ObjectData(object, type));
117
118     if (QObjectPrivate::get(object)->wasDeleted)
119        return scriptEngine->undefinedValue();
120
121     QDeclarativeData *ddata = QDeclarativeData::get(object, true);
122
123     if (!ddata) {
124        return scriptEngine->undefinedValue();
125     } else if (!ddata->indestructible && !object->parent()) {
126         return newObject(scriptEngine, this, new ObjectData(object, type));
127     } else if (!ddata->scriptValue) {
128         ddata->scriptValue = new QScriptValue(newObject(scriptEngine, this, new ObjectData(object, type)));
129         return *ddata->scriptValue;
130     } else if (ddata->scriptValue->engine() == QDeclarativeEnginePrivate::getScriptEngine(engine)) {
131         return *ddata->scriptValue;
132     } else {
133         return newObject(scriptEngine, this, new ObjectData(object, type));
134     }
135 }
136
137 QObject *QDeclarativeObjectScriptClass::toQObject(const QScriptValue &value) const
138 {
139     return value.toQObject();
140 }
141
142 int QDeclarativeObjectScriptClass::objectType(const QScriptValue &value) const
143 {
144     if (scriptClass(value) != this)
145         return QVariant::Invalid;
146
147     Object *o = object(value);
148     return ((ObjectData*)(o))->type;
149 }
150
151 QScriptClass::QueryFlags
152 QDeclarativeObjectScriptClass::queryProperty(Object *object, const Identifier &name,
153                                     QScriptClass::QueryFlags flags)
154 {
155     return queryProperty(toQObject(object), name, flags, 0);
156 }
157
158 QScriptClass::QueryFlags
159 QDeclarativeObjectScriptClass::queryProperty(QObject *obj, const Identifier &name,
160                                              QScriptClass::QueryFlags flags, QDeclarativeContextData *evalContext,
161                                              QueryHints hints)
162 {
163     Q_UNUSED(flags);
164     lastData = 0;
165     lastTNData = 0;
166
167     if (name == m_destroyId.identifier ||
168         name == m_toStringId.identifier)
169         return QScriptClass::HandlesReadAccess;
170
171     if (!obj)
172         return 0;
173
174     QDeclarativeEnginePrivate *enginePrivate = QDeclarativeEnginePrivate::get(engine);
175     lastData = QDeclarativePropertyCache::property(engine, obj, name, local);
176     if ((hints & ImplicitObject) && lastData && lastData->revision != 0) {
177
178         QDeclarativeData *ddata = QDeclarativeData::get(obj);
179         if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(lastData)) 
180             return 0;
181     }
182
183     if (lastData)
184         return QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess;
185
186     if (!(hints & SkipAttachedProperties)) {
187         if (!evalContext && context()) {
188             // Global object, QScriptContext activation object, QDeclarativeContext object
189             QScriptValue scopeNode = scopeChainValue(context(), -3);
190             if (scopeNode.isValid()) {
191                 Q_ASSERT(scriptClass(scopeNode) == enginePrivate->contextClass);
192
193                 evalContext = enginePrivate->contextClass->contextFromValue(scopeNode);
194             }
195         }
196
197         if (evalContext && evalContext->imports) {
198             QDeclarativeTypeNameCache::Data *data = evalContext->imports->data(name);
199             if (data) {
200                 lastTNData = data;
201                 return QScriptClass::HandlesReadAccess;
202             }
203         }
204     }
205
206     if (!(hints & ImplicitObject)) {
207         local.coreIndex = -1;
208         lastData = &local;
209         return QScriptClass::HandlesWriteAccess;
210     }
211
212     return 0;
213 }
214
215 QDeclarativeObjectScriptClass::Value
216 QDeclarativeObjectScriptClass::property(Object *object, const Identifier &name)
217 {
218     return property(toQObject(object), name);
219 }
220
221 QDeclarativeObjectScriptClass::Value
222 QDeclarativeObjectScriptClass::property(QObject *obj, const Identifier &name)
223 {
224     QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
225
226     if (name == m_destroyId.identifier)
227         return Value(scriptEngine, m_destroy);
228     else if (name == m_toStringId.identifier)
229         return Value(scriptEngine, m_toString);
230
231     if (lastData && !lastData->isValid())
232         return Value();
233
234     Q_ASSERT(obj);
235
236     QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine);
237
238     if (lastTNData) {
239
240         if (lastTNData->type)
241             return Value(scriptEngine, enginePriv->typeNameClass->newObject(obj, lastTNData->type));
242         else
243             return Value(scriptEngine, enginePriv->typeNameClass->newObject(obj, lastTNData->typeNamespace));
244
245     } else if (lastData->flags & QDeclarativePropertyCache::Data::IsFunction) {
246         if (lastData->flags & QDeclarativePropertyCache::Data::IsVMEFunction) {
247             return Value(scriptEngine, ((QDeclarativeVMEMetaObject *)(obj->metaObject()))->vmeMethod(lastData->coreIndex));
248         } else {
249             // Uncomment to use QtScript method call logic
250             // QScriptValue sobj = scriptEngine->newQObject(obj);
251             // return Value(scriptEngine, sobj.property(toString(name)));
252             return Value(scriptEngine, methods.newMethod(obj, lastData));
253         }
254     } else {
255         if (enginePriv->captureProperties && !(lastData->flags & QDeclarativePropertyCache::Data::IsConstant)) {
256             if (lastData->coreIndex == 0) {
257                 enginePriv->capturedProperties <<
258                     QDeclarativeEnginePrivate::CapturedProperty(QDeclarativeData::get(obj, true)->objectNameNotifier());
259             } else {
260                 enginePriv->capturedProperties <<
261                     QDeclarativeEnginePrivate::CapturedProperty(obj, lastData->coreIndex, lastData->notifyIndex);
262             }
263         }
264
265         if (QDeclarativeValueTypeFactory::isValueType((uint)lastData->propType)) {
266             QDeclarativeValueType *valueType = enginePriv->valueTypes[lastData->propType];
267             if (valueType)
268                 return Value(scriptEngine, enginePriv->valueTypeClass->newObject(obj, lastData->coreIndex, valueType));
269         }
270
271         if (lastData->flags & QDeclarativePropertyCache::Data::IsQList) {
272             return Value(scriptEngine, enginePriv->listClass->newList(obj, lastData->coreIndex, lastData->propType));
273         } else if (lastData->flags & QDeclarativePropertyCache::Data::IsQObjectDerived) {
274             QObject *rv = 0;
275             void *args[] = { &rv, 0 };
276             QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
277             return Value(scriptEngine, newQObject(rv, lastData->propType));
278         } else if (lastData->flags & QDeclarativePropertyCache::Data::IsQScriptValue) {
279             QScriptValue rv = scriptEngine->nullValue();
280             void *args[] = { &rv, 0 };
281             QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
282             return Value(scriptEngine, rv);
283         } else if (lastData->propType == QMetaType::QReal) {
284             qreal rv = 0;
285             void *args[] = { &rv, 0 };
286             QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
287             return Value(scriptEngine, rv);
288         } else if (lastData->propType == QMetaType::Int || lastData->flags & QDeclarativePropertyCache::Data::IsEnumType) {
289             int rv = 0;
290             void *args[] = { &rv, 0 };
291             QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
292             return Value(scriptEngine, rv);
293         } else if (lastData->propType == QMetaType::Bool) {
294             bool rv = false;
295             void *args[] = { &rv, 0 };
296             QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
297             return Value(scriptEngine, rv);
298         } else if (lastData->propType == QMetaType::QString) {
299             QString rv;
300             void *args[] = { &rv, 0 };
301             QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
302             return Value(scriptEngine, rv);
303         } else if (lastData->propType == QMetaType::UInt) {
304             uint rv = 0;
305             void *args[] = { &rv, 0 };
306             QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
307             return Value(scriptEngine, rv);
308         } else if (lastData->propType == QMetaType::Float) {
309             float rv = 0;
310             void *args[] = { &rv, 0 };
311             QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
312             return Value(scriptEngine, rv);
313         } else if (lastData->propType == QMetaType::Double) {
314             double rv = 0;
315             void *args[] = { &rv, 0 };
316             QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
317             return Value(scriptEngine, rv);
318         } else {
319             QVariant var = obj->metaObject()->property(lastData->coreIndex).read(obj);
320             return Value(scriptEngine, enginePriv->scriptValueFromVariant(var));
321         }
322     }
323 }
324
325 void QDeclarativeObjectScriptClass::setProperty(Object *object,
326                                        const Identifier &name,
327                                        const QScriptValue &value)
328 {
329     return setProperty(toQObject(object), name, value, context());
330 }
331
332 void QDeclarativeObjectScriptClass::setProperty(QObject *obj,
333                                                 const Identifier &name,
334                                                 const QScriptValue &value,
335                                                 QScriptContext *context,
336                                                 QDeclarativeContextData *evalContext)
337 {
338     Q_UNUSED(name);
339
340     Q_ASSERT(obj);
341     Q_ASSERT(lastData);
342     Q_ASSERT(context);
343
344     if (!lastData->isValid()) {
345         QString error = QLatin1String("Cannot assign to non-existent property \"") +
346                         toString(name) + QLatin1Char('\"');
347         context->throwError(error);
348         return;
349     }
350
351     if (!(lastData->flags & QDeclarativePropertyCache::Data::IsWritable) && 
352         !(lastData->flags & QDeclarativePropertyCache::Data::IsQList)) {
353         QString error = QLatin1String("Cannot assign to read-only property \"") +
354                         toString(name) + QLatin1Char('\"');
355         context->throwError(error);
356         return;
357     }
358
359     QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine);
360
361     if (!evalContext) {
362         // Global object, QScriptContext activation object, QDeclarativeContext object
363         QScriptValue scopeNode = scopeChainValue(context, -3);
364         if (scopeNode.isValid()) {
365             Q_ASSERT(scriptClass(scopeNode) == enginePriv->contextClass);
366
367             evalContext = enginePriv->contextClass->contextFromValue(scopeNode);
368         }
369     }
370
371     QDeclarativeBinding *newBinding = 0;
372     if (value.isFunction() && !value.isRegExp()) {
373         QScriptContextInfo ctxtInfo(context);
374         QDeclarativePropertyCache::ValueTypeData valueTypeData;
375
376         newBinding = new QDeclarativeBinding(value, obj, evalContext);
377         newBinding->setSourceLocation(ctxtInfo.fileName(), ctxtInfo.functionStartLineNumber());
378         newBinding->setTarget(QDeclarativePropertyPrivate::restore(*lastData, valueTypeData, obj, evalContext));
379         if (newBinding->expression().contains(QLatin1String("this")))
380             newBinding->setEvaluateFlags(newBinding->evaluateFlags() | QDeclarativeBinding::RequiresThisObject);
381     }
382
383     QDeclarativeAbstractBinding *delBinding =
384         QDeclarativePropertyPrivate::setBinding(obj, lastData->coreIndex, -1, newBinding);
385     if (delBinding)
386         delBinding->destroy();
387
388     if (value.isNull() && lastData->flags & QDeclarativePropertyCache::Data::IsQObjectDerived) {
389         QObject *o = 0;
390         int status = -1;
391         int flags = 0;
392         void *argv[] = { &o, 0, &status, &flags };
393         QMetaObject::metacall(obj, QMetaObject::WriteProperty, lastData->coreIndex, argv);
394     } else if (value.isUndefined() && lastData->flags & QDeclarativePropertyCache::Data::IsResettable) {
395         void *a[] = { 0 };
396         QMetaObject::metacall(obj, QMetaObject::ResetProperty, lastData->coreIndex, a);
397     } else if (value.isUndefined() && lastData->propType == qMetaTypeId<QVariant>()) {
398         QDeclarativePropertyPrivate::write(obj, *lastData, QVariant(), evalContext);
399     } else if (value.isUndefined()) {
400         QString error = QLatin1String("Cannot assign [undefined] to ") +
401                         QLatin1String(QMetaType::typeName(lastData->propType));
402         context->throwError(error);
403     } else if (value.isFunction() && !value.isRegExp()) {
404         // this is handled by the binding creation above
405     } else {
406         //### expand optimization for other known types
407         if (lastData->propType == QMetaType::Int && value.isNumber()) {
408             int rawValue = qRound(value.toNumber());
409             int status = -1;
410             int flags = 0;
411             void *a[] = { (void *)&rawValue, 0, &status, &flags };
412             QMetaObject::metacall(obj, QMetaObject::WriteProperty,
413                                   lastData->coreIndex, a);
414             return;
415         } else if (lastData->propType == QMetaType::QReal && value.isNumber()) {
416             qreal rawValue = qreal(value.toNumber());
417             int status = -1;
418             int flags = 0;
419             void *a[] = { (void *)&rawValue, 0, &status, &flags };
420             QMetaObject::metacall(obj, QMetaObject::WriteProperty,
421                                   lastData->coreIndex, a);
422             return;
423         } else if (lastData->propType == QMetaType::QString && value.isString()) {
424             const QString &rawValue = value.toString();
425             int status = -1;
426             int flags = 0;
427             void *a[] = { (void *)&rawValue, 0, &status, &flags };
428             QMetaObject::metacall(obj, QMetaObject::WriteProperty,
429                                   lastData->coreIndex, a);
430             return;
431         }
432
433         QVariant v;
434         if (lastData->flags & QDeclarativePropertyCache::Data::IsQList)
435             v = enginePriv->scriptValueToVariant(value, qMetaTypeId<QList<QObject *> >());
436         else
437             v = enginePriv->scriptValueToVariant(value, lastData->propType);
438
439         if (!QDeclarativePropertyPrivate::write(obj, *lastData, v, evalContext)) {
440             const char *valueType = 0;
441             if (v.userType() == QVariant::Invalid) valueType = "null";
442             else valueType = QMetaType::typeName(v.userType());
443
444             QString error = QLatin1String("Cannot assign ") +
445                             QLatin1String(valueType) +
446                             QLatin1String(" to ") +
447                             QLatin1String(QMetaType::typeName(lastData->propType));
448             context->throwError(error);
449         }
450     }
451 }
452
453 bool QDeclarativeObjectScriptClass::isQObject() const
454 {
455     return true;
456 }
457
458 QObject *QDeclarativeObjectScriptClass::toQObject(Object *object, bool *ok)
459 {
460     if (ok) *ok = true;
461
462     ObjectData *data = (ObjectData*)object;
463     return data->object.data();
464 }
465
466 QScriptValue QDeclarativeObjectScriptClass::tostring(QScriptContext *context, QScriptEngine *)
467 {
468     QObject* obj = context->thisObject().toQObject();
469
470     QString ret;
471     if(obj){
472         QString objectName = obj->objectName();
473
474         ret += QString::fromUtf8(obj->metaObject()->className());
475         ret += QLatin1String("(0x");
476         ret += QString::number((quintptr)obj,16);
477
478         if (!objectName.isEmpty()) {
479             ret += QLatin1String(", \"");
480             ret += objectName;
481             ret += QLatin1Char('\"');
482         }
483
484         ret += QLatin1Char(')');
485     }else{
486         ret += QLatin1String("null");
487     }
488     return QScriptValue(ret);
489 }
490
491 QScriptValue QDeclarativeObjectScriptClass::destroy(QScriptContext *context, QScriptEngine *engine)
492 {
493     QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine);
494     QScriptValue that = context->thisObject();
495
496     if (scriptClass(that) != p->objectClass)
497         return engine->undefinedValue();
498
499     ObjectData *data = (ObjectData *)p->objectClass->object(that);
500     if (!data->object)
501         return engine->undefinedValue();
502
503     QDeclarativeData *ddata = QDeclarativeData::get(data->object, false);
504     if (!ddata || ddata->indestructible)
505         return engine->currentContext()->throwError(QLatin1String("Invalid attempt to destroy() an indestructible object"));
506
507     QObject *obj = data->object;
508     int delay = 0;
509     if (context->argumentCount() > 0)
510         delay = context->argument(0).toInt32();
511     if (delay > 0)
512         QTimer::singleShot(delay, obj, SLOT(deleteLater()));
513     else
514         obj->deleteLater();
515
516     return engine->undefinedValue();
517 }
518
519 QStringList QDeclarativeObjectScriptClass::propertyNames(Object *object)
520 {
521     QObject *obj = toQObject(object);
522     if (!obj)
523         return QStringList();
524
525     QDeclarativeEnginePrivate *enginePrivate = QDeclarativeEnginePrivate::get(engine);
526
527     QDeclarativePropertyCache *cache = 0;
528     QDeclarativeData *ddata = QDeclarativeData::get(obj);
529     if (ddata)
530         cache = ddata->propertyCache;
531     if (!cache) {
532         cache = enginePrivate->cache(obj);
533         if (cache) {
534             if (ddata) { cache->addref(); ddata->propertyCache = cache; }
535         } else {
536             // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
537             // XXX QDeclarativeOpenMetaObject has a cache, so this is suboptimal.
538             // XXX This is a workaround for QTBUG-9420.
539             const QMetaObject *mo = obj->metaObject();
540             QStringList r;
541             int pc = mo->propertyCount();
542             int po = mo->propertyOffset();
543             for (int i=po; i<pc; ++i)
544                 r += QString::fromUtf8(mo->property(i).name());
545             return r;
546         }
547     }
548     return cache->propertyNames();
549 }
550
551 bool QDeclarativeObjectScriptClass::compare(Object *o1, Object *o2)
552 {
553     ObjectData *d1 = (ObjectData *)o1;
554     ObjectData *d2 = (ObjectData *)o2;
555
556     return d1 == d2 || d1->object == d2->object;
557 }
558
559 struct MethodData : public QScriptDeclarativeClass::Object {
560     MethodData(QObject *o, const QDeclarativePropertyCache::Data &d) : object(o), data(d) {}
561
562     QDeclarativeGuard<QObject> object;
563     QDeclarativePropertyCache::Data data;
564 };
565
566 QDeclarativeObjectMethodScriptClass::QDeclarativeObjectMethodScriptClass(QDeclarativeEngine *bindEngine)
567 : QScriptDeclarativeClass(QDeclarativeEnginePrivate::getScriptEngine(bindEngine)),
568   engine(bindEngine)
569 {
570     qRegisterMetaType<QList<QObject *> >("QList<QObject *>");
571
572     setSupportsCall(true);
573
574     QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
575
576     m_connect = scriptEngine->newFunction(connect);
577     m_connectId = createPersistentIdentifier(QLatin1String("connect"));
578     m_disconnect = scriptEngine->newFunction(disconnect);
579     m_disconnectId = createPersistentIdentifier(QLatin1String("disconnect"));
580 }
581
582 QDeclarativeObjectMethodScriptClass::~QDeclarativeObjectMethodScriptClass()
583 {
584 }
585
586 QScriptValue QDeclarativeObjectMethodScriptClass::newMethod(QObject *object, const QDeclarativePropertyCache::Data *method)
587 {
588     QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
589
590     return newObject(scriptEngine, this, new MethodData(object, *method));
591 }
592
593 QScriptValue QDeclarativeObjectMethodScriptClass::connect(QScriptContext *context, QScriptEngine *engine)
594 {
595     QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine);
596
597     QScriptValue that = context->thisObject();
598     if (&p->objectClass->methods != scriptClass(that))
599         return engine->undefinedValue();
600
601     MethodData *data = (MethodData *)object(that);
602
603     if (!data->object || context->argumentCount() == 0)
604         return engine->undefinedValue();
605
606     QByteArray signal("2");
607     signal.append(data->object->metaObject()->method(data->data.coreIndex).signature());
608
609     if (context->argumentCount() == 1) {
610         qScriptConnect(data->object, signal.constData(), QScriptValue(), context->argument(0));
611     } else {
612         qScriptConnect(data->object, signal.constData(), context->argument(0), context->argument(1));
613     }
614
615     return engine->undefinedValue();
616 }
617
618 QScriptValue QDeclarativeObjectMethodScriptClass::disconnect(QScriptContext *context, QScriptEngine *engine)
619 {
620     QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine);
621
622     QScriptValue that = context->thisObject();
623     if (&p->objectClass->methods != scriptClass(that))
624         return engine->undefinedValue();
625
626     MethodData *data = (MethodData *)object(that);
627
628     if (!data->object || context->argumentCount() == 0)
629         return engine->undefinedValue();
630
631     QByteArray signal("2");
632     signal.append(data->object->metaObject()->method(data->data.coreIndex).signature());
633
634     if (context->argumentCount() == 1) {
635         qScriptDisconnect(data->object, signal.constData(), QScriptValue(), context->argument(0));
636     } else {
637         qScriptDisconnect(data->object, signal.constData(), context->argument(0), context->argument(1));
638     }
639
640     return engine->undefinedValue();
641 }
642
643 QScriptClass::QueryFlags
644 QDeclarativeObjectMethodScriptClass::queryProperty(Object *, const Identifier &name,
645                                                    QScriptClass::QueryFlags flags)
646 {
647     Q_UNUSED(flags);
648     if (name == m_connectId.identifier || name == m_disconnectId.identifier)
649         return QScriptClass::HandlesReadAccess;
650     else
651         return 0;
652
653 }
654
655 QDeclarativeObjectMethodScriptClass::Value
656 QDeclarativeObjectMethodScriptClass::property(Object *, const Identifier &name)
657 {
658     QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
659
660     if (name == m_connectId.identifier)
661         return Value(scriptEngine, m_connect);
662     else if (name == m_disconnectId.identifier)
663         return Value(scriptEngine, m_disconnect);
664     else
665         return Value();
666 }
667
668 namespace {
669 struct MetaCallArgument {
670     inline MetaCallArgument();
671     inline ~MetaCallArgument();
672     inline void *dataPtr();
673
674     inline void initAsType(int type, QDeclarativeEngine *);
675     void fromScriptValue(int type, QDeclarativeEngine *, const QScriptValue &);
676     inline QScriptDeclarativeClass::Value toValue(QDeclarativeEngine *);
677
678 private:
679     MetaCallArgument(const MetaCallArgument &);
680
681     inline void cleanup();
682
683     char data[4 * sizeof(void *)];
684     int type;
685     bool isObjectType;
686 };
687 }
688
689 MetaCallArgument::MetaCallArgument()
690 : type(QVariant::Invalid), isObjectType(false)
691 {
692 }
693
694 MetaCallArgument::~MetaCallArgument()
695 {
696     cleanup();
697 }
698
699 void MetaCallArgument::cleanup()
700 {
701     if (type == QMetaType::QString) {
702         ((QString *)&data)->~QString();
703     } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
704         ((QVariant *)&data)->~QVariant();
705     } else if (type == qMetaTypeId<QScriptValue>()) {
706         ((QScriptValue *)&data)->~QScriptValue();
707     } else if (type == qMetaTypeId<QList<QObject *> >()) {
708         ((QList<QObject *> *)&data)->~QList<QObject *>();
709     }
710 }
711
712 void *MetaCallArgument::dataPtr()
713 {
714     if (type == -1)
715         return ((QVariant *)data)->data();
716     else
717         return (void *)&data;
718 }
719
720 void MetaCallArgument::initAsType(int callType, QDeclarativeEngine *e)
721 {
722     if (type != 0) { cleanup(); type = 0; }
723     if (callType == 0) return;
724
725     QScriptEngine *engine = QDeclarativeEnginePrivate::getScriptEngine(e);
726
727     if (callType == qMetaTypeId<QScriptValue>()) {
728         new (&data) QScriptValue(engine->undefinedValue());
729         type = callType;
730     } else if (callType == QMetaType::Int ||
731                callType == QMetaType::UInt ||
732                callType == QMetaType::Bool ||
733                callType == QMetaType::Double ||
734                callType == QMetaType::Float) {
735         type = callType;
736     } else if (callType == QMetaType::QObjectStar) {
737         *((QObject **)&data) = 0;
738         type = callType;
739     } else if (callType == QMetaType::QString) {
740         new (&data) QString();
741         type = callType;
742     } else if (callType == qMetaTypeId<QVariant>()) {
743         type = callType;
744         new (&data) QVariant();
745     } else if (callType == qMetaTypeId<QList<QObject *> >()) {
746         type = callType;
747         new (&data) QList<QObject *>();
748     } else {
749         type = -1;
750         new (&data) QVariant(callType, (void *)0);
751     }
752 }
753
754 void MetaCallArgument::fromScriptValue(int callType, QDeclarativeEngine *engine, const QScriptValue &value)
755 {
756     if (type != 0) { cleanup(); type = 0; }
757
758     if (callType == qMetaTypeId<QScriptValue>()) {
759         new (&data) QScriptValue(value);
760         type = qMetaTypeId<QScriptValue>();
761     } else if (callType == QMetaType::Int) {
762         *((int *)&data) = int(value.toInt32());
763         type = callType;
764     } else if (callType == QMetaType::UInt) {
765         *((uint *)&data) = uint(value.toUInt32());
766         type = callType;
767     } else if (callType == QMetaType::Bool) {
768         *((bool *)&data) = value.toBool();
769         type = callType;
770     } else if (callType == QMetaType::Double) {
771         *((double *)&data) = double(value.toNumber());
772         type = callType;
773     } else if (callType == QMetaType::Float) {
774         *((float *)&data) = float(value.toNumber());
775         type = callType;
776     } else if (callType == QMetaType::QString) {
777         if (value.isNull() || value.isUndefined())
778             new (&data) QString();
779         else
780             new (&data) QString(value.toString());
781         type = callType;
782     } else if (callType == QMetaType::QObjectStar) {
783         *((QObject **)&data) = value.toQObject();
784         type = callType;
785     } else if (callType == qMetaTypeId<QVariant>()) {
786         new (&data) QVariant(QDeclarativeEnginePrivate::get(engine)->scriptValueToVariant(value));
787         type = callType;
788     } else if (callType == qMetaTypeId<QList<QObject*> >()) {
789         QList<QObject *> *list = new (&data) QList<QObject *>(); 
790         if (value.isArray()) {
791             int length = value.property(QLatin1String("length")).toInt32();
792             for (int ii = 0; ii < length; ++ii) {
793                 QScriptValue arrayItem = value.property(ii);
794                 QObject *d = arrayItem.toQObject();
795                 list->append(d);
796             }
797         } else if (QObject *d = value.toQObject()) {
798             list->append(d);
799         }
800         type = callType;
801     } else {
802         new (&data) QVariant();
803         type = -1;
804
805         QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(engine);
806         QVariant v = priv->scriptValueToVariant(value);
807         if (v.userType() == callType) {
808             *((QVariant *)&data) = v;
809         } else if (v.canConvert((QVariant::Type)callType)) {
810             *((QVariant *)&data) = v;
811             ((QVariant *)&data)->convert((QVariant::Type)callType);
812         } else if (const QMetaObject *mo = priv->rawMetaObjectForType(callType)) {
813             QObject *obj = priv->toQObject(v);
814             
815             if (obj) {
816                 const QMetaObject *objMo = obj->metaObject();
817                 while (objMo && objMo != mo) objMo = objMo->superClass();
818                 if (!objMo) obj = 0;
819             }
820
821             *((QVariant *)&data) = QVariant(callType, &obj);
822         } else {
823             *((QVariant *)&data) = QVariant(callType, (void *)0);
824         }
825     }
826 }
827
828 QScriptDeclarativeClass::Value MetaCallArgument::toValue(QDeclarativeEngine *e)
829 {
830     QScriptEngine *engine = QDeclarativeEnginePrivate::getScriptEngine(e);
831
832     if (type == qMetaTypeId<QScriptValue>()) {
833         return QScriptDeclarativeClass::Value(engine, *((QScriptValue *)&data));
834     } else if (type == QMetaType::Int) {
835         return QScriptDeclarativeClass::Value(engine, *((int *)&data));
836     } else if (type == QMetaType::UInt) {
837         return QScriptDeclarativeClass::Value(engine, *((uint *)&data));
838     } else if (type == QMetaType::Bool) {
839         return QScriptDeclarativeClass::Value(engine, *((bool *)&data));
840     } else if (type == QMetaType::Double) {
841         return QScriptDeclarativeClass::Value(engine, *((double *)&data));
842     } else if (type == QMetaType::Float) {
843         return QScriptDeclarativeClass::Value(engine, *((float *)&data));
844     } else if (type == QMetaType::QString) {
845         return QScriptDeclarativeClass::Value(engine, *((QString *)&data));
846     } else if (type == QMetaType::QObjectStar) {
847         QObject *object = *((QObject **)&data);
848         if (object)
849             QDeclarativeData::get(object, true)->setImplicitDestructible();
850         QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(e);
851         return QScriptDeclarativeClass::Value(engine, priv->objectClass->newQObject(object));
852     } else if (type == qMetaTypeId<QList<QObject *> >()) {
853         QList<QObject *> &list = *(QList<QObject *>*)&data;
854         QScriptValue rv = engine->newArray(list.count());
855         QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(e);
856         for (int ii = 0; ii < list.count(); ++ii) {
857             QObject *object = list.at(ii);
858             QDeclarativeData::get(object, true)->setImplicitDestructible();
859             rv.setProperty(ii, priv->objectClass->newQObject(object));
860         }
861         return QScriptDeclarativeClass::Value(engine, rv);
862     } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
863         QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(e);
864         QScriptValue rv = ep->scriptValueFromVariant(*((QVariant *)&data));
865         if (rv.isQObject()) {
866             QObject *object = rv.toQObject();
867             if (object)
868                 QDeclarativeData::get(object, true)->setImplicitDestructible();
869         }
870         return QScriptDeclarativeClass::Value(engine, rv);
871     } else {
872         return QScriptDeclarativeClass::Value();
873     }
874 }
875
876 int QDeclarativeObjectMethodScriptClass::enumType(const QMetaObject *meta, const QString &strname)
877 {
878     QByteArray str = strname.toUtf8();
879     QByteArray scope;
880     QByteArray name;
881     int scopeIdx = str.lastIndexOf("::");
882     if (scopeIdx != -1) {
883         scope = str.left(scopeIdx);
884         name = str.mid(scopeIdx + 2);
885     } else { 
886         name = str;
887     }
888     for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
889         QMetaEnum m = meta->enumerator(i);
890         if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope)))
891             return QVariant::Int;
892     }
893     return QVariant::Invalid;
894 }
895
896 QDeclarativeObjectMethodScriptClass::Value QDeclarativeObjectMethodScriptClass::call(Object *o, QScriptContext *ctxt)
897 {
898     MethodData *method = static_cast<MethodData *>(o);
899
900     if (method->data.relatedIndex == -1)
901         return callPrecise(method->object, method->data, ctxt);
902     else
903         return callOverloaded(method, ctxt);
904 }
905
906 QDeclarativeObjectMethodScriptClass::Value 
907 QDeclarativeObjectMethodScriptClass::callPrecise(QObject *object, const QDeclarativePropertyCache::Data &data, 
908                                                  QScriptContext *ctxt)
909 {
910     if (data.flags & QDeclarativePropertyCache::Data::HasArguments) {
911
912         QMetaMethod m = object->metaObject()->method(data.coreIndex);
913         QList<QByteArray> argTypeNames = m.parameterTypes();
914         QVarLengthArray<int, 9> argTypes(argTypeNames.count());
915
916         // ### Cache
917         for (int ii = 0; ii < argTypeNames.count(); ++ii) {
918             argTypes[ii] = QMetaType::type(argTypeNames.at(ii));
919             if (argTypes[ii] == QVariant::Invalid) 
920                 argTypes[ii] = enumType(object->metaObject(), QString::fromLatin1(argTypeNames.at(ii)));
921             if (argTypes[ii] == QVariant::Invalid) 
922                 return Value(ctxt, ctxt->throwError(QString::fromLatin1("Unknown method parameter type: %1").arg(QLatin1String(argTypeNames.at(ii)))));
923         }
924
925         if (argTypes.count() > ctxt->argumentCount())
926             return Value(ctxt, ctxt->throwError(QLatin1String("Insufficient arguments")));
927
928         return callMethod(object, data.coreIndex, data.propType, argTypes.count(), argTypes.data(), ctxt);
929
930     } else {
931
932         return callMethod(object, data.coreIndex, data.propType, 0, 0, ctxt);
933
934     }
935 }
936
937 QDeclarativeObjectMethodScriptClass::Value 
938 QDeclarativeObjectMethodScriptClass::callMethod(QObject *object, int index, 
939                                                 int returnType, int argCount, int *argTypes, 
940                                                 QScriptContext *ctxt)
941 {
942     if (argCount > 0) {
943
944         QVarLengthArray<MetaCallArgument, 9> args(argCount + 1);
945         args[0].initAsType(returnType, engine);
946
947         for (int ii = 0; ii < argCount; ++ii)
948             args[ii + 1].fromScriptValue(argTypes[ii], engine, ctxt->argument(ii));
949
950         QVarLengthArray<void *, 9> argData(args.count());
951         for (int ii = 0; ii < args.count(); ++ii)
952             argData[ii] = args[ii].dataPtr();
953
954         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
955
956         return args[0].toValue(engine);
957
958     } else if (returnType != 0) {
959         
960         MetaCallArgument arg;
961         arg.initAsType(returnType, engine);
962
963         void *args[] = { arg.dataPtr() };
964
965         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
966
967         return arg.toValue(engine);
968
969     } else {
970
971         void *args[] = { 0 };
972         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
973         return Value();
974
975     }
976 }
977
978 /*!
979 Resolve the overloaded method to call.  The algorithm works conceptually like this:
980     1.  Resolve the set of overloads it is *possible* to call.
981         Impossible overloads include those that have too many parameters or have parameters 
982         of unknown type.  
983     2.  Filter the set of overloads to only contain those with the closest number of 
984         parameters.
985         For example, if we are called with 3 parameters and there are 2 overloads that
986         take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
987     3.  Find the best remaining overload based on its match score.  
988         If two or more overloads have the same match score, call the last one.  The match
989         score is constructed by adding the matchScore() result for each of the parameters.
990 */
991 QDeclarativeObjectMethodScriptClass::Value
992 QDeclarativeObjectMethodScriptClass::callOverloaded(MethodData *method, QScriptContext *ctxt)
993 {
994     int argumentCount = ctxt->argumentCount();
995
996     QDeclarativePropertyCache::Data *best = 0;
997     int bestParameterScore = INT_MAX;
998     int bestMatchScore = INT_MAX;
999
1000     QDeclarativePropertyCache::Data dummy;
1001     QDeclarativePropertyCache::Data *attempt = &method->data;
1002
1003     do {
1004         QList<QByteArray> methodArgTypeNames;
1005
1006         if (attempt->flags & QDeclarativePropertyCache::Data::HasArguments)
1007             methodArgTypeNames = method->object->metaObject()->method(attempt->coreIndex).parameterTypes();
1008
1009         int methodArgumentCount = methodArgTypeNames.count();
1010
1011         if (methodArgumentCount > argumentCount)
1012             continue; // We don't have sufficient arguments to call this method
1013
1014         int methodParameterScore = argumentCount - methodArgumentCount;
1015         if (methodParameterScore > bestParameterScore)
1016             continue; // We already have a better option
1017
1018         int methodMatchScore = 0;
1019         QVarLengthArray<int, 9> methodArgTypes(methodArgumentCount);
1020
1021         bool unknownArgument = false;
1022         for (int ii = 0; ii < methodArgumentCount; ++ii) {
1023             methodArgTypes[ii] = QMetaType::type(methodArgTypeNames.at(ii));
1024             if (methodArgTypes[ii] == QVariant::Invalid) 
1025                 methodArgTypes[ii] = enumType(method->object->metaObject(), 
1026                                               QString::fromLatin1(methodArgTypeNames.at(ii)));
1027             if (methodArgTypes[ii] == QVariant::Invalid) {
1028                 unknownArgument = true;
1029                 break;
1030             }
1031             methodMatchScore += matchScore(ctxt->argument(ii), methodArgTypes[ii], methodArgTypeNames.at(ii));
1032         }
1033         if (unknownArgument)
1034             continue; // We don't understand all the parameters
1035
1036         if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1037             best = attempt;
1038             bestParameterScore = methodParameterScore;
1039             bestMatchScore = methodMatchScore;
1040         }
1041
1042         if (bestParameterScore == 0 && bestMatchScore == 0)
1043             break; // We can't get better than that
1044
1045     } while((attempt = relatedMethod(method->object, attempt, dummy)) != 0);
1046
1047     if (best) {
1048         return callPrecise(method->object, *best, ctxt);
1049     } else {
1050         QString error = QLatin1String("Unable to determine callable overload.  Candidates are:");
1051         QDeclarativePropertyCache::Data *candidate = &method->data;
1052         while (candidate) {
1053             error += QLatin1String("\n    ") + QString::fromUtf8(method->object->metaObject()->method(candidate->coreIndex).signature());
1054             candidate = relatedMethod(method->object, candidate, dummy);
1055         }
1056         return Value(ctxt, ctxt->throwError(error));
1057     }
1058 }
1059
1060 /*!
1061     Returns the match score for converting \a actual to be of type \a conversionType.  A 
1062     zero score means "perfect match" whereas a higher score is worse.
1063
1064     The conversion table is copied out of the QtScript callQtMethod() function.
1065 */
1066 int QDeclarativeObjectMethodScriptClass::matchScore(const QScriptValue &actual, int conversionType, 
1067                                                     const QByteArray &conversionTypeName)
1068 {
1069     if (actual.isNumber()) {
1070         switch (conversionType) {
1071         case QMetaType::Double:
1072             return 0;
1073         case QMetaType::Float:
1074             return 1;
1075         case QMetaType::LongLong:
1076         case QMetaType::ULongLong:
1077             return 2;
1078         case QMetaType::Long:
1079         case QMetaType::ULong:
1080             return 3;
1081         case QMetaType::Int:
1082         case QMetaType::UInt:
1083             return 4;
1084         case QMetaType::Short:
1085         case QMetaType::UShort:
1086             return 5;
1087             break;
1088         case QMetaType::Char:
1089         case QMetaType::UChar:
1090             return 6;
1091         default:
1092             return 10;
1093         }
1094     } else if (actual.isString()) {
1095         switch (conversionType) {
1096         case QMetaType::QString:
1097             return 0;
1098         default:
1099             return 10;
1100         }
1101     } else if (actual.isBoolean()) {
1102         switch (conversionType) {
1103         case QMetaType::Bool:
1104             return 0;
1105         default:
1106             return 10;
1107         }
1108     } else if (actual.isDate()) {
1109         switch (conversionType) {
1110         case QMetaType::QDateTime:
1111             return 0;
1112         case QMetaType::QDate:
1113             return 1;
1114         case QMetaType::QTime:
1115             return 2;
1116         default:
1117             return 10;
1118         }
1119     } else if (actual.isRegExp()) {
1120         switch (conversionType) {
1121         case QMetaType::QRegExp:
1122             return 0;
1123         default:
1124             return 10;
1125         }
1126     } else if (actual.isVariant()) {
1127         if (conversionType == qMetaTypeId<QVariant>())
1128             return 0;
1129         else if (actual.toVariant().userType() == conversionType)
1130             return 0;
1131         else
1132             return 10;
1133     } else if (actual.isArray()) {
1134         switch (conversionType) {
1135         case QMetaType::QStringList:
1136         case QMetaType::QVariantList:
1137             return 5;
1138         default:
1139             return 10;
1140         }
1141     } else if (actual.isQObject()) {
1142         switch (conversionType) {
1143         case QMetaType::QObjectStar:
1144             return 0;
1145         default:
1146             return 10;
1147         }
1148     } else if (actual.isNull()) {
1149         switch (conversionType) {
1150         case QMetaType::VoidStar:
1151         case QMetaType::QObjectStar:
1152             return 0;
1153         default:
1154             if (!conversionTypeName.endsWith('*'))
1155                 return 10;
1156             else
1157                 return 0;
1158         }
1159     } else {
1160         return 10;
1161     }
1162 }
1163
1164 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1165 {
1166     struct Private
1167     {
1168         int revision;
1169         int className;
1170         int classInfoCount, classInfoData;
1171         int methodCount, methodData;
1172     };
1173
1174     return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1175 }
1176
1177 static QByteArray QMetaMethod_name(const QMetaMethod &m)
1178 {
1179     QByteArray sig = m.signature();
1180     int paren = sig.indexOf('(');
1181     if (paren == -1)
1182         return sig;
1183     else
1184         return sig.left(paren);
1185 }
1186
1187 /*!
1188 Returns the next related method, if one, or 0.
1189 */
1190 QDeclarativePropertyCache::Data *
1191 QDeclarativeObjectMethodScriptClass::relatedMethod(QObject *object, QDeclarativePropertyCache::Data *current, 
1192                                                    QDeclarativePropertyCache::Data &dummy)
1193 {
1194     QDeclarativePropertyCache *cache = QDeclarativeData::get(object)->propertyCache;
1195     if (current->relatedIndex == -1)
1196         return 0;
1197
1198     if (cache) {
1199         return cache->method(current->relatedIndex);
1200     } else {
1201         const QMetaObject *mo = object->metaObject();
1202         int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1203
1204         while (methodOffset > current->relatedIndex) {
1205             mo = mo->superClass();
1206             methodOffset -= QMetaObject_methods(mo);
1207         }
1208
1209         QMetaMethod method = mo->method(current->relatedIndex);
1210         dummy.load(method);
1211         
1212         // Look for overloaded methods
1213         QByteArray methodName = QMetaMethod_name(method);
1214         for (int ii = current->relatedIndex - 1; ii >= methodOffset; --ii) {
1215             if (methodName == QMetaMethod_name(mo->method(ii))) {
1216                 dummy.relatedIndex = ii;
1217                 return &dummy;
1218             }
1219         }
1220
1221         return &dummy;
1222     }
1223 }
1224
1225 QT_END_NAMESPACE
1226