kjsembed
qobject_binding.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2005, 2006 Ian Reinhart Geiser <geiseri@kde.org> 00003 Copyright (C) 2005, 2006 Matt Broadstone <mbroadst@gmail.com> 00004 Copyright (C) 2005, 2006 Richard J. Moore <rich@kde.org> 00005 Copyright (C) 2005, 2006 Erik L. Bunce <kde@bunce.us> 00006 Copyright (C) 2007, 2008 Sebastian Sauer <mail@dipe.org> 00007 00008 This library is free software; you can redistribute it and/or 00009 modify it under the terms of the GNU Library General Public 00010 License as published by the Free Software Foundation; either 00011 version 2 of the License, or (at your option) any later version. 00012 00013 This library is distributed in the hope that it will be useful, 00014 but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 Library General Public License for more details. 00017 00018 You should have received a copy of the GNU Library General Public License 00019 along with this library; see the file COPYING.LIB. If not, write to 00020 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00021 Boston, MA 02110-1301, USA. 00022 */ 00023 #include "qobject_binding.h" 00024 00025 #include <QtCore/QObject> 00026 #include <QtCore/QArgument> 00027 #include <QtCore/QMetaEnum> 00028 #include <QtCore/QMetaType> 00029 #include <QtCore/QVariant> 00030 #include <QtCore/QVector> 00031 #include <QtCore/QUrl> 00032 #include <QtCore/QDebug> 00033 #include <QWidget> 00034 00035 #include "slotproxy.h" 00036 #include "eventproxy.h" 00037 #include "jseventmapper.h" 00038 #include "pointer.h" 00039 #include "variant_binding.h" 00040 00041 #include <kjs/array_instance.h> 00042 #include <kjs/function_object.h> 00043 00044 //#define CREATEQOBJ_DIAG 00045 00046 using namespace KJSEmbed; 00047 00048 QByteArray createSignal( const QByteArray &sig ) 00049 { 00050 return '2' + sig; 00051 } 00052 00053 QByteArray createSlot( const QByteArray &slt ) 00054 { 00055 return '1' + slt; 00056 } 00057 00058 bool validSlot(const QMetaMethod& method, QObjectBinding::AccessFlags accessflags) 00059 { 00060 switch( method.access() ) { 00061 case QMetaMethod::Private: { 00062 if(! (accessflags & QObjectBinding::PrivateSlots)) return false; 00063 } break; 00064 case QMetaMethod::Protected: { 00065 if(! (accessflags & QObjectBinding::ProtectedSlots)) return false; 00066 } break; 00067 case QMetaMethod::Public: { 00068 if(! (accessflags & QObjectBinding::PublicSlots)) return false; 00069 } break; 00070 } 00071 if(method.attributes() & QMetaMethod::Scriptable) { 00072 if(! (accessflags & QObjectBinding::ScriptableSlots)) return false; 00073 } 00074 else { 00075 if(! (accessflags & QObjectBinding::NonScriptableSlots)) return false; 00076 } 00077 return true; 00078 } 00079 00080 bool validSignal(const QMetaMethod& method, QObjectBinding::AccessFlags accessflags) 00081 { 00082 switch( method.access() ) { 00083 case QMetaMethod::Private: { 00084 if(! (accessflags & QObjectBinding::PrivateSignals)) return false; 00085 } break; 00086 case QMetaMethod::Protected: { 00087 if(! (accessflags & QObjectBinding::ProtectedSignals)) return false; 00088 } break; 00089 case QMetaMethod::Public: { 00090 if(! (accessflags & QObjectBinding::PublicSignals)) return false; 00091 } break; 00092 } 00093 if(method.attributes() & QMetaMethod::Scriptable) { 00094 if(! (accessflags & QObjectBinding::ScriptableSignals)) return false; 00095 } 00096 else { 00097 if(! (accessflags & QObjectBinding::NonScriptableSignals)) return false; 00098 } 00099 return true; 00100 } 00101 00102 bool validProperty(const QMetaProperty& property, QObjectBinding::AccessFlags accessflags) 00103 { 00104 if(property.isScriptable()) { 00105 if(! (accessflags & QObjectBinding::ScriptableProperties)) return false; 00106 } 00107 else { 00108 if(! (accessflags & QObjectBinding::NonScriptableProperties)) return false; 00109 } 00110 return true; 00111 } 00112 00113 KJS::JSValue *callConnect( KJS::ExecState *exec, KJS::JSObject *self, const KJS::List &args ) 00114 { 00115 KJSEmbed::QObjectBinding *imp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, self ); 00116 if( !imp ) // No implementation, so we need to use the first argument as we are a global static invocation. 00117 imp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, args[0] ); 00118 if( !imp ) 00119 return KJS::throwError(exec, KJS::GeneralError, i18n("Wrong object type.")); 00120 //return KJSEmbed::throwError(exec, i18n("Wrong object type.")); 00121 00122 if( args.size() > 2) 00123 { 00124 KJSEmbed::QObjectBinding *senderImp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, args[0] ); 00125 if( !senderImp ) 00126 { 00127 return KJS::throwError(exec, KJS::GeneralError, i18n("First argument must be a QObject.")); 00128 //return KJSEmbed::throwError(exec, i18n("First argument must be a QObject")); 00129 } 00130 QObject* receiver = 0; 00131 QObject* sender = senderImp->object<QObject>(); 00132 char *signal = qstrdup( createSignal(args[1]->toString(exec).ascii()).data() ); 00133 char *slot = 0; 00134 KJSEmbed::QObjectBinding *receiverImp = 0; 00135 if( args.size() >= 4) 00136 { 00137 slot = qstrdup( createSlot(args[3]->toString(exec).ascii()).data() ); 00138 receiverImp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, args[2] ); 00139 if( !receiverImp ) 00140 receiver = new SlotProxy(args[2]->toObject(exec), exec->dynamicInterpreter(), sender, args[3]->toString(exec).ascii() ); 00141 else 00142 receiver = receiverImp->object<QObject>(); 00143 } 00144 else 00145 { 00146 receiverImp = imp; 00147 receiver = imp->object<QObject>(); 00148 slot = qstrdup( createSlot(args[2]->toString(exec).ascii()).data() ); 00149 } 00150 00151 const QMetaObject *senderMetaObject = sender->metaObject(); 00152 QMetaMethod senderMetaMethod = senderMetaObject->method( senderMetaObject->indexOfSignal(signal) ); 00153 00154 const QMetaObject *receiverMetaObject = receiver->metaObject(); 00155 QMetaMethod receiverMetaMethod = receiverMetaObject->method( receiverMetaObject->indexOfSlot(slot) ); 00156 00157 if( validSignal(senderMetaMethod, senderImp->access()) && ( !receiverImp || validSlot(receiverMetaMethod, receiverImp->access()) ) ) 00158 { 00159 return KJS::jsBoolean(QObject::connect(sender, signal, receiver, slot)); 00160 } 00161 00162 return KJS::jsBoolean(false); 00163 } 00164 return KJS::throwError(exec, KJS::GeneralError, i18n("Incorrect number of arguments.")); 00165 //return KJSEmbed::throwError(exec, i18n("Incorrect number of arguments.")); 00166 } 00167 00168 QByteArray extractMemberName( const QMetaMethod &member ) 00169 { 00170 QString sig = member.signature(); 00171 return sig.left( sig.indexOf('(') ).toLatin1(); 00172 } 00173 00174 void QObjectBinding::publishQObject( KJS::ExecState *exec, KJS::JSObject *target, QObject *object) 00175 { 00176 KJSEmbed::QObjectBinding *imp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, target); 00177 Q_ASSERT(imp); 00178 00179 // Add the children the QObject has. 00180 if (imp->access() & QObjectBinding::ChildObjects) { 00181 //TODO uh, this one is dirty cause it may eat a lot of time to publish things that may not 00182 //got accessed anyway. Better solution would be to provide access to them on demand only. That 00183 //would also allow to manipulate the QObject-tree at runtime what is currently not possible. 00184 QObjectList children = object->children(); 00185 QObjectList::Iterator child = children.begin(); 00186 for( ; child != children.end(); ++child) 00187 { 00188 QString objectName = (*child)->objectName(); 00189 if( !objectName.isEmpty() ) 00190 { 00191 KJS::JSObject *childObject = KJSEmbed::createQObject(exec, *child); 00192 KJSEmbed::QObjectBinding *childImp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, childObject); 00193 if(childImp) 00194 { 00195 childImp->setAccess( imp->access() ); // inherit access from parent 00196 target->put(exec, KJS::Identifier(toUString(objectName)), childObject); 00197 } 00198 } 00199 } 00200 } 00201 00202 // Add slots of the current object. 00203 const QMetaObject *metaObject = object->metaObject(); 00204 int methods = metaObject->methodCount(); 00205 for( int idx = 0; idx < methods; ++idx ) 00206 { 00207 QMetaMethod member = metaObject->method(idx); 00208 if(validSlot(member, imp->access())) 00209 { 00210 target->put(exec, KJS::Identifier( extractMemberName( member ) ), 00211 new SlotBinding(exec,member), KJS::DontDelete|KJS::ReadOnly|KJS::Function); 00212 } 00213 } 00214 00215 // Add enums as read only uints. 00216 int enums = metaObject->enumeratorCount(); 00217 for( int idx = 0; idx < enums; ++idx ) 00218 { 00219 QMetaEnum enumerator = metaObject->enumerator(idx); 00220 int keys = enumerator.keyCount(); 00221 for( int key = 0; key < keys; ++key) 00222 { 00223 target->put(exec, KJS::Identifier( enumerator.key(key) ), 00224 KJS::jsNumber(enumerator.value(key)), KJS::DontDelete|KJS::ReadOnly); 00225 } 00226 } 00227 } 00228 00229 QObjectBinding::QObjectBinding( KJS::ExecState *exec, QObject *object ) 00230 : ObjectBinding(exec, object->metaObject()->className(), object) 00231 , m_evproxy(0) 00232 , m_access( AllSlots | AllSignals | AllProperties | AllObjects ) 00233 { 00234 if( object->parent() != 0 ) 00235 { 00236 setOwnership( ObjectBinding::QObjOwned ); 00237 } 00238 else 00239 { 00240 setOwnership( ObjectBinding::JSOwned ); 00241 } 00242 00243 m_cleanupHandler = new QObjectCleanupHandler(); 00244 watchObject(object); 00245 00246 StaticBinding::publish( exec, this, QObjectFactory::methods() ); 00247 QObjectBinding::publishQObject(exec, this, object); 00248 00249 // Make "connect" a global static method. 00250 exec->dynamicInterpreter()->globalObject()->put(exec, "connect", new StaticBinding(exec, &QObjectFactory::methods()[0]) ); 00251 } 00252 00253 QObjectBinding::~QObjectBinding() 00254 { 00255 if( m_cleanupHandler->isEmpty() ) 00256 { 00257 setOwnership( ObjectBinding::QObjOwned ); 00258 } 00259 else if( object<QObject>()->parent() != 0 ) 00260 { 00261 setOwnership( ObjectBinding::QObjOwned ); 00262 m_cleanupHandler->remove(object<QObject>()); 00263 } 00264 else if( ownership() != ObjectBinding::JSOwned ) 00265 { 00266 m_cleanupHandler->remove(object<QObject>()); 00267 } 00268 else 00269 { 00270 m_cleanupHandler->remove(object<QObject>()); 00271 } 00272 00273 delete m_cleanupHandler; 00274 } 00275 00276 void QObjectBinding::watchObject( QObject *object ) 00277 { 00278 m_cleanupHandler->add( object ); 00279 } 00280 00281 bool QObjectBinding::getOwnPropertySlot( KJS::ExecState *exec, const KJS::Identifier &propertyName, KJS::PropertySlot &slot ) 00282 { 00283 // qDebug() << "getOwnPropertySlot called"; 00284 QObject *obj = object<QObject>(); 00285 const QMetaObject *meta = obj->metaObject(); 00286 int propIndex = meta->indexOfProperty( propertyName.ascii() ); 00287 if ( propIndex != -1 ) { 00288 if(! validProperty(meta->property(propIndex), m_access)) 00289 return false; 00290 // qDebug() << "getOwnPropertySlot found the property " << propertyName.ascii(); 00291 slot.setCustom( this, propertyGetter ); 00292 return true; 00293 } 00294 return ObjectBinding::getOwnPropertySlot( exec, propertyName, slot ); 00295 } 00296 00297 KJS::JSValue *QObjectBinding::propertyGetter( KJS::ExecState *exec, KJS::JSObject*, 00298 const KJS::Identifier &propertyName, const KJS::PropertySlot&slot ) 00299 { 00300 // qDebug() << "Getter was called"; 00301 QObjectBinding *self = static_cast<QObjectBinding *>(slot.slotBase()); 00302 QObject *obj = self->object<QObject>(); 00303 00304 QVariant val = obj->property( propertyName.ascii() ); 00305 if ( val.isValid() ) { 00306 return convertToValue( exec, val ); 00307 } 00308 qDebug() << QString("propertyGetter called but no property, name was '%1'").arg(propertyName.ascii()); 00309 return 0; // ERROR 00310 } 00311 00312 QObjectBinding::AccessFlags QObjectBinding::access() const 00313 { 00314 return m_access; 00315 } 00316 00317 void QObjectBinding::setAccess(QObjectBinding::AccessFlags access) 00318 { 00319 m_access = access; 00320 } 00321 00322 void QObjectBinding::put(KJS::ExecState *exec, const KJS::Identifier &propertyName, KJS::JSValue *value, int attr ) 00323 { 00324 QObject *obj = object<QObject>(); 00325 if ( obj && !m_cleanupHandler->isEmpty() ) 00326 { 00327 // Properties 00328 const QMetaObject *meta = obj->metaObject(); 00329 00330 if ( int propIndex = meta->indexOfProperty( propertyName.ascii() ) != -1 ) 00331 { 00332 QMetaProperty prop = meta->property(propIndex); 00333 if(! validProperty(prop, m_access)) 00334 return; 00335 00336 bool propSet = false; 00337 QVariant val = convertToVariant( exec, value ); 00338 if ( prop.isEnumType() ) 00339 { 00340 propSet = obj->setProperty( propertyName.ascii(), val.toUInt() ); 00341 } 00342 else if ( val.isValid() /*&& meta->property(propIndex).isWritable() <- wtf?*/ ) 00343 { 00344 propSet = obj->setProperty( propertyName.ascii(), val ); 00345 } 00346 /* 00347 if( !propSet ) 00348 { 00349 KJSEmbed::throwError(exec, 00350 i18n("Setting property %1 failed: property invalid, read-only or does not exist").arg(propertyName.ascii())); 00351 } 00352 */ 00353 00354 } 00355 00356 if (JSEventMapper::mapper()->isEventHandler(propertyName) ) 00357 { 00358 if ( !m_evproxy ) 00359 m_evproxy = new KJSEmbed::EventProxy( this, exec->dynamicInterpreter() ); 00360 if( value ) 00361 m_evproxy->addFilter( JSEventMapper::mapper()->findEventType( propertyName ) ); 00362 else 00363 m_evproxy->removeFilter( JSEventMapper::mapper()->findEventType( propertyName ) ); 00364 } 00365 } 00366 //qDebug() << "Forward put"; 00367 // Set a property value 00368 ObjectBinding::put(exec, propertyName, value, attr); 00369 } 00370 00371 bool QObjectBinding::canPut(KJS::ExecState *exec, const KJS::Identifier &propertyName) const 00372 { 00373 QObject *obj = object<QObject>(); 00374 if ( obj && !m_cleanupHandler->isEmpty() ) 00375 { 00376 // Properties 00377 const QMetaObject *meta = obj->metaObject(); 00378 if ( int propIndex = meta->indexOfProperty( propertyName.ascii() ) != -1 ) 00379 { 00380 QMetaProperty prop = meta->property(propIndex); 00381 return validProperty(prop, m_access) && prop.isWritable(); 00382 } 00383 } 00384 return ObjectBinding::canPut(exec,propertyName); 00385 } 00386 00387 KJS::UString QObjectBinding::className() const 00388 { 00389 return toUString( typeName() ); 00390 } 00391 00392 KJS::UString QObjectBinding::toString(KJS::ExecState *exec) const 00393 { 00394 Q_UNUSED( exec ); 00395 QString s( "%1 (%2)" ); 00396 s = s.arg( object<QObject>()->objectName() ); 00397 s = s.arg( typeName() ); 00398 return toUString( s ); 00399 } 00400 00401 PointerBase *getArg( KJS::ExecState *exec, const QList<QByteArray> &types, const KJS::List &args, int idx, QString& errorText) 00402 { 00403 //qDebug("Index %d, args size %d, types size %d", idx, args.size(), types.size() ); 00404 00405 if( types.size() == 0 && idx == 0 ) 00406 return new NullPtr(); 00407 if ( args.size() <= idx ) 00408 return new NullPtr(); 00409 00410 if ( types.size() <= idx ) 00411 { 00412 const QString firstPart = i18np("The slot asked for %1 argument", "The slot asked for %1 arguments", idx); 00413 const QString secondPart = i18np("but there is only %1 available", "but there are only %1 available", types.size()); 00414 errorText = i18nc("%1 is 'the slot asked for foo arguments', %2 is 'but there are only bar available'", "%1, %2."); 00415 00416 return 0; 00417 } 00418 00419 QVariant::Type varianttype = QVariant::nameToType( types[idx].constData() ); 00420 //qDebug( QString("type=%1 argtype=%2 variantType=%3 (%4)").arg(types[idx].constData()).arg(args[idx]->type()).arg(varianttype).arg(QVariant::typeToName(varianttype)).toLatin1() ); 00421 switch( varianttype ) { 00422 case QVariant::Int: 00423 if( args[idx]->type() == KJS::NumberType ) 00424 return new Value<int>( int( args[idx]->toInteger(exec) ) ); 00425 break; 00426 case QVariant::UInt: 00427 if( args[idx]->type() == KJS::NumberType ) 00428 return new Value<uint>( uint( args[idx]->toInteger(exec) ) ); 00429 break; 00430 case QVariant::LongLong: 00431 if( args[idx]->type() == KJS::NumberType ) 00432 return new Value<qlonglong>( qlonglong( args[idx]->toInteger(exec) ) ); 00433 break; 00434 case QVariant::ULongLong: 00435 if( args[idx]->type() == KJS::NumberType ) 00436 return new Value<qulonglong>( qulonglong( args[idx]->toInteger(exec) ) ); 00437 break; 00438 case QVariant::Double: 00439 if( args[idx]->type() == KJS::NumberType ) 00440 return new Value<double>( args[idx]->toNumber(exec) ); 00441 //if ( types[idx] == "float" ) return new Value<float>( args[idx]->toNumber(exec) ); 00442 //if ( types[idx] == "qreal" ) return new Value<qreal>( args[idx]->toNumber(exec) ); 00443 break; 00444 case QVariant::Bool: 00445 if( args[idx]->type() == KJS::BooleanType ) 00446 return new Value<bool>( args[idx]->toBoolean(exec) ); 00447 break; 00448 case QVariant::ByteArray: 00449 if( args[idx]->type() == KJS::StringType ) 00450 return new Value<QByteArray>( toQString(args[idx]->toString(exec)).toUtf8() ); 00451 break; 00452 case QVariant::String: 00453 if( args[idx]->type() == KJS::StringType ) 00454 return new Value<QString>( toQString(args[idx]->toString(exec)) ); 00455 break; 00456 case QVariant::StringList: 00457 if( args[idx]->type() == KJS::ObjectType ) 00458 return new Value<QStringList>( convertArrayToStringList(exec, args[idx]) ); 00459 break; 00460 case QVariant::Size: 00461 if( VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec,args[idx]) ) 00462 return new Value<QSize>( valImp->variant().value<QSize>() ); 00463 break; 00464 case QVariant::SizeF: 00465 if( VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec,args[idx]) ) 00466 return new Value<QSizeF>( valImp->variant().value<QSizeF>() ); 00467 break; 00468 case QVariant::Point: 00469 if( VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec,args[idx]) ) 00470 return new Value<QPoint>( valImp->variant().value<QPoint>() ); 00471 break; 00472 case QVariant::PointF: 00473 if( VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec,args[idx]) ) 00474 return new Value<QPointF>( valImp->variant().value<QPointF>() ); 00475 break; 00476 case QVariant::Rect: 00477 if( VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec,args[idx]) ) 00478 return new Value<QRect>( valImp->variant().value<QRect>() ); 00479 break; 00480 case QVariant::RectF: 00481 if( VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec,args[idx]) ) 00482 return new Value<QRectF>( valImp->variant().value<QRectF>() ); 00483 break; 00484 case QVariant::Color: 00485 if( args[idx]->type() == KJS::StringType ) 00486 return new Value<QColor>( QColor(toQString(args[idx]->toString(exec))) ); 00487 if( VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec,args[idx]) ) 00488 return new Value<QColor>( valImp->variant().value<QColor>() ); 00489 break; 00490 case QVariant::Url: 00491 if( args[idx]->type() == KJS::StringType ) 00492 return new Value<QUrl>( toQString(args[idx]->toString(exec) )); 00493 if( VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec,args[idx]) ) 00494 return new Value<QUrl>( valImp->variant().value<QUrl>() ); 00495 break; 00496 case QVariant::List: 00497 if( args[idx]->type() == KJS::ObjectType ) 00498 return new Value<QVariantList>( convertArrayToList(exec, args[idx]) ); 00499 break; 00500 case QVariant::Map: 00501 if( args[idx]->type() == KJS::ObjectType ) 00502 return new Value<QVariantMap>( convertArrayToMap(exec, args[idx]) ); 00503 break; 00504 case QVariant::UserType: // fall through 00505 default: 00506 if( args[idx]->type() == KJS::NullType ) 00507 return new NullPtr(); 00508 if( args[idx]->type() == KJS::StringType ) 00509 { 00510 if( strcmp(types[idx].constData(),"KUrl") == 0 ) //downcast to QUrl 00511 return new Value<QUrl>( toQString(args[idx]->toString(exec) )); 00512 } 00513 if( args[idx]->type() == KJS::ObjectType ) 00514 { 00515 if(QObjectBinding *objImp = KJSEmbed::extractBindingImp<QObjectBinding>(exec, args[idx])) 00516 { 00517 //qDebug("\tQObjectBinding"); 00518 if( QObject* qObj = objImp->qobject<QObject>() ) 00519 return new Value<void*>(qObj); 00520 } 00521 else if(ObjectBinding *objImp = KJSEmbed::extractBindingImp<ObjectBinding>(exec, args[idx])) 00522 { 00523 //qDebug("\tObjectBinding"); 00524 return new Value<void*>(objImp->voidStar()); 00525 } 00526 if(VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec,args[idx])) 00527 { 00528 //qDebug() << "\tVariantBinding typeName=" << valImp->variant().typeName() << "type=" << valImp->variant().type() << "userType=" << valImp->variant().userType() << " variant=" << valImp->variant(); 00529 QVariant var = valImp->variant(); 00530 00531 // if the variant is the appropriate type, return its data 00532 if ((var.type() == varianttype) || 00533 ((var.type() == QVariant::UserType) && 00534 (types[idx].constData() == var.typeName()))) 00535 return new Value<void*>(valImp->variant().data()); 00536 else if ((var.type() != QVariant::UserType) && 00537 var.canConvert(varianttype)) 00538 { 00539 // is convertable type, so convert it, and return if successful 00540 if (var.convert(varianttype)) 00541 return new Value<void*>(valImp->variant().data()); 00542 } 00543 else if ((var.type() == QVariant::UserType) && 00544 var.canConvert<QObject*>()) 00545 { 00546 QObject* qObj = var.value<QObject*>(); 00547 if (!qObj) 00548 qObj = reinterpret_cast<QObject*>(var.value<QWidget*>()); 00549 if (qObj) { 00550 QByteArray typeName = types[idx].constData(); 00551 typeName.replace("*", ""); //krazy:exclude=doublequote_chars 00552 if (qObj->inherits(typeName)) 00553 return new Value<void*>(qObj); 00554 } 00555 } 00556 } 00557 } 00558 00559 QVariant v = KJSEmbed::extractVariant(exec, args[idx]); 00560 if (! v.isNull()) 00561 return new Value<QVariant>(v); 00562 00563 break; 00564 } 00565 00566 qDebug("Cast failure %s value Type %d", types[idx].constData(), args[idx]->type() ); 00567 // construct a meaningful exception message 00568 QString jsType; 00569 KJS::JSObject* jsObj = args[idx]->getObject(); 00570 if (jsObj) 00571 { 00572 const KJS::ClassInfo* ci = jsObj->classInfo(); 00573 if (ci && ci->className) 00574 jsType = ci->className; 00575 if (jsType.isEmpty()) 00576 jsType = toQString(jsObj->className()); 00577 } 00578 00579 if (jsType.isEmpty()) 00580 { 00581 switch(args[idx]->type()) 00582 { 00583 case KJS::UnspecifiedType: 00584 jsType = "jsUnspecified"; 00585 break; 00586 case KJS::NumberType: 00587 jsType = "jsNumber"; 00588 break; 00589 case KJS::BooleanType: 00590 jsType = "jsBoolean"; 00591 break; 00592 case KJS::UndefinedType: 00593 jsType = "jsUndefined"; 00594 break; 00595 case KJS::NullType: 00596 jsType = "jsNull"; 00597 break; 00598 case KJS::StringType: 00599 jsType = "jsString"; 00600 break; 00601 case KJS::ObjectType: 00602 jsType = "jsObject"; 00603 break; 00604 case KJS::GetterSetterType: 00605 jsType = "jsGetterSetter"; 00606 break; 00607 default: 00608 jsType = QString::number(args[idx]->type()); 00609 break; 00610 } 00611 } 00612 00613 errorText = i18n("Failure to cast to %1 value from Type %2 (%3)", 00614 types[idx].constData(), jsType, toQString(args[idx]->toString(exec))); 00615 00616 return 0; 00617 } 00618 00619 KJS::JSValue *SlotBinding::callAsFunction( KJS::ExecState *exec, KJS::JSObject *self, const KJS::List &args ) 00620 { 00621 QObjectBinding *imp = extractBindingImp<QObjectBinding>(exec,self); 00622 if( imp == 0 ) 00623 return KJS::jsNull(); 00624 00625 PointerBase *qtArgs[10]; 00626 void *param[11]; 00627 00628 QObject *object = imp->object<QObject>(); 00629 int count = object->metaObject()->methodCount(); 00630 QMetaMethod metaMember; 00631 int offset = 0; 00632 bool success = false; 00633 for(; offset < count; ++offset) 00634 { 00635 metaMember = object->metaObject()->method(offset); 00636 if( extractMemberName(metaMember) == m_memberName ) 00637 { 00638 if( metaMember.parameterTypes().size() == args.size() && validSlot(metaMember, imp->access()) ) 00639 { 00640 success = true; 00641 break; 00642 } 00643 } 00644 } 00645 00646 if( !success ) 00647 { 00648 return KJS::throwError(exec, KJS::GeneralError, i18n("No such method '%1'.", m_memberName.constData())); 00649 //return KJSEmbed::throwError(exec, i18n("Call to '%1' failed.").arg(m_memberName.constData())); 00650 } 00651 00652 QList<QByteArray> types = metaMember.parameterTypes(); 00653 00654 QVariant::Type returnTypeId = QVariant::nameToType( metaMember.typeName() ); 00655 int tp = QMetaType::type( metaMember.typeName() ); 00656 PointerBase *qtRet = new Value<void*>(0); 00657 00658 bool returnIsMetaType = ( 00659 returnTypeId == QVariant::UserType || 00660 returnTypeId == QVariant::Size || returnTypeId == QVariant::SizeF || 00661 returnTypeId == QVariant::Point || returnTypeId == QVariant::PointF || 00662 returnTypeId == QVariant::Rect || returnTypeId == QVariant::RectF || 00663 returnTypeId == QVariant::Color 00664 ); 00665 QVariant returnValue = returnIsMetaType ? QVariant(tp, (void*)0) : QVariant(returnTypeId); 00666 QGenericReturnArgument returnArgument(metaMember.typeName(), &returnValue); 00667 param[0] = returnIsMetaType ? qtRet->voidStar() : returnArgument.data(); 00668 00669 QString errorText; 00670 for( int idx = 0; idx < 10; ++idx) 00671 { 00672 qtArgs[idx] = getArg(exec, types, args, idx, errorText); 00673 if (!qtArgs[idx]) { 00674 for( int i = 0; i < idx; ++i) 00675 delete qtArgs[i]; 00676 delete qtRet; 00677 return KJS::throwError(exec, KJS::GeneralError, i18n("Call to method '%1' failed, unable to get argument %2: %3", m_memberName.constData(), idx, errorText)); 00678 } 00679 param[idx+1] = qtArgs[idx]->voidStar(); 00680 } 00681 00682 success = object->qt_metacall(QMetaObject::InvokeMetaMethod, offset, param) < 0; 00683 00684 KJS::JSValue *jsReturnValue = 0; 00685 if( success ) { 00686 switch( returnTypeId ) { 00687 case QVariant::Invalid: // fall through 00688 case QVariant::UserType: { 00689 switch( tp ) { 00690 case QMetaType::QWidgetStar: { 00691 QVariant v(tp, param[0]); 00692 QWidget* widget = v.value< QWidget* >(); 00693 if( widget ) 00694 jsReturnValue = KJSEmbed::createQObject(exec, widget, KJSEmbed::ObjectBinding::CPPOwned); 00695 } break; 00696 case QMetaType::QObjectStar: { 00697 QVariant v(tp,param[0]); 00698 QObject* obj = v.value< QObject* >(); 00699 if( obj ) 00700 jsReturnValue = KJSEmbed::createQObject(exec, obj, KJSEmbed::ObjectBinding::CPPOwned); 00701 } break; 00702 default: 00703 break; 00704 } 00705 } break; 00706 default: 00707 if( returnIsMetaType ) 00708 returnValue = QVariant(tp, param[0]); 00709 break; 00710 } 00711 if(! jsReturnValue) 00712 jsReturnValue = KJSEmbed::convertToValue(exec, returnValue); 00713 } 00714 00715 for( int idx = 0; idx < 10; ++idx) 00716 delete qtArgs[idx]; 00717 delete qtRet; 00718 00719 if( !success ) 00720 return KJS::throwError(exec, KJS::GeneralError, i18n("Call to '%1' failed.", m_memberName.constData())); 00721 00722 return jsReturnValue; 00723 } 00724 00725 SlotBinding::SlotBinding(KJS::ExecState *exec, const QMetaMethod &member ) 00726 : KJS::InternalFunctionImp(static_cast<KJS::FunctionPrototype*>(exec->lexicalInterpreter()->builtinFunctionPrototype()), 00727 KJS::Identifier(toUString(extractMemberName(member)))) 00728 { 00729 m_memberName = extractMemberName(member); 00730 int count = member.parameterNames().count(); 00731 putDirect( exec->propertyNames().length, count, LengthFlags ); 00732 } 00733 00734 00735 KJS::JSObject* KJSEmbed::createQObject(KJS::ExecState *exec, QObject *value, KJSEmbed::ObjectBinding::Ownership owner) 00736 { 00737 if ( 0 == value ) 00738 return new KJS::JSObject(); 00739 00740 const QMetaObject *meta = value->metaObject(); 00741 KJS::JSObject *parent = exec->dynamicInterpreter()->globalObject(); 00742 KJS::JSObject *returnValue; 00743 int pos; 00744 QString clazz; 00745 do 00746 { 00747 clazz = meta->className(); 00748 00749 #ifdef CREATEQOBJ_DIAG 00750 qDebug() << "clazz=" << clazz; 00751 #endif 00752 // strip off namespace since they aren't included 00753 if ((pos = clazz.lastIndexOf("::")) != -1) 00754 clazz.remove(0, pos + 2); 00755 #ifdef CREATEQOBJ_DIAG 00756 qDebug() << "cleaned clazz=" << clazz; 00757 #endif 00758 if ( parent->hasProperty( exec, KJS::Identifier(toUString(clazz)) ) ) 00759 { 00760 #ifdef CREATEQOBJ_DIAG 00761 qDebug() << "createQObject(): clazz=" << clazz << " value=" << value; 00762 #endif 00763 Pointer<QObject> pov(value); 00764 returnValue = StaticConstructor::bind(exec, clazz, pov); 00765 if ( returnValue ) 00766 return returnValue; 00767 00768 #ifdef CREATEQOBJ_DIAG 00769 qDebug("\tresort to construct() method."); 00770 #endif 00771 returnValue = StaticConstructor::construct( exec, parent, toUString(clazz) ); 00772 if( returnValue ) 00773 { 00774 // If it is a value type setValue 00775 KJSEmbed::QObjectBinding *imp = extractBindingImp<QObjectBinding>(exec, returnValue ); 00776 if( imp ) 00777 { 00778 imp->setObject( value ); 00779 imp->watchObject( value ); 00780 imp->setOwnership( owner ); 00781 KJSEmbed::QObjectBinding::publishQObject( exec, returnValue, value); 00782 } 00783 else 00784 { 00785 KJS::throwError(exec, KJS::TypeError, i18n("%1 is not an Object type", clazz )); 00786 return new KJS::JSObject(); 00787 } 00788 } 00789 else 00790 { 00791 KJS::throwError(exec, KJS::TypeError, i18n("Could not construct value")); 00792 return new KJS::JSObject(); 00793 } 00794 return returnValue; 00795 } 00796 else 00797 { 00798 #ifdef CREATEQOBJ_DIAG 00799 qDebug("%s not a bound type, move up the chain", meta->className() ); 00800 #endif 00801 meta = meta->superClass(); 00802 } 00803 00804 } 00805 while( meta ); 00806 00807 KJSEmbed::QObjectBinding *imp = new KJSEmbed::QObjectBinding(exec, value); 00808 imp->setOwnership( owner ); 00809 00810 return imp; 00811 } 00812 00813 START_QOBJECT_METHOD( callParent, QObject ) 00814 //TODO it would be better, if each QObjectBinding remembers it's parent rather then 00815 //creating a new instance each time. That wouldn't only be more logical, but also 00816 //does prevent losing of additional information like e.g. the access-level. 00817 if( imp->access() & QObjectBinding::GetParentObject ) 00818 { 00819 QObject *parent = imp->object<QObject>()->parent(); 00820 KJS::JSObject *parentObject = KJSEmbed::createQObject(exec, parent); 00821 KJSEmbed::QObjectBinding *parentImp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, parentObject); 00822 if( parentImp ) { 00823 parentImp->setAccess( imp->access() ); // inherit access from child since we don't know the access-level of the parent here :-( 00824 } 00825 result = parentObject; 00826 } 00827 END_QOBJECT_METHOD 00828 START_QOBJECT_METHOD( callIsWidgetType, QObject ) 00829 result = KJS::jsBoolean(object->isWidgetType()); 00830 END_QOBJECT_METHOD 00831 START_QOBJECT_METHOD( callInherits, QObject) 00832 QByteArray className = KJSEmbed::extractQString(exec, args, 0).toLatin1(); 00833 result = KJS::jsBoolean(object->inherits(className.constData())); 00834 END_QOBJECT_METHOD 00835 START_QOBJECT_METHOD( callSetParent, QObject ) 00836 if( imp->access() & QObjectBinding::SetParentObject ) 00837 { 00838 QObject *parent = KJSEmbed::extractObject<QObject>(exec, args, 0, 0); 00839 object->setParent(parent); 00840 } 00841 END_QOBJECT_METHOD 00842 START_QOBJECT_METHOD( callFindChild, QObject ) 00843 if( imp->access() & QObjectBinding::ChildObjects ) 00844 { 00845 QString childName = KJSEmbed::extractQString(exec, args, 0); 00846 QObject *child = object->findChild<QObject*>(childName); 00847 KJS::JSObject *childObject = KJSEmbed::createQObject(exec, child); 00848 KJSEmbed::QObjectBinding *childImp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, childObject); 00849 if( childImp ) { 00850 childImp->setAccess( imp->access() ); // inherit access from parent 00851 } 00852 result = childObject; 00853 } 00854 END_QOBJECT_METHOD 00855 00856 START_METHOD_LUT(QObjectFactory) 00857 {"connect", 4, KJS::DontDelete|KJS::ReadOnly, &callConnect }, 00858 {"parent", 0, KJS::DontDelete|KJS::ReadOnly, &callParent }, 00859 {"inherits", 1, KJS::DontDelete|KJS::ReadOnly, &callInherits }, 00860 {"isWidgetType", 0, KJS::DontDelete|KJS::ReadOnly, &callIsWidgetType }, 00861 {"setParent", 1, KJS::DontDelete|KJS::ReadOnly, &callSetParent }, 00862 {"findChild", 1, KJS::DontDelete|KJS::ReadOnly, &callFindChild } 00863 END_METHOD_LUT 00864 00865 NO_ENUMS( QObjectFactory ) 00866 NO_STATICS( QObjectFactory ) 00867 00868 //kate: indent-spaces on; indent-width 4; replace-tabs on; indent-mode cstyle;
KDE 4.6 API Reference