PythonQtClassInfo.cpp
884 lines
| 26.6 KiB
| text/x-c
|
CppLexer
/ src / PythonQtClassInfo.cpp
ezust
|
r0 | /* | |
* | |||
florianlink
|
r133 | * Copyright (C) 2010 MeVis Medical Solutions AG All Rights Reserved. | |
ezust
|
r0 | * | |
* This library is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 2.1 of the License, or (at your option) any later version. | |||
* | |||
* This library is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* Further, this software is distributed without any warranty that it is | |||
* free of the rightful claim of any third person regarding infringement | |||
* or the like. Any license provided herein, whether implied or | |||
* otherwise, applies only to this software file. Patent licenses, if | |||
* any, provided herein do not apply to combinations of this program with | |||
* other software, or any other product whatsoever. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public | |||
* License along with this library; if not, write to the Free Software | |||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |||
* | |||
florianlink
|
r133 | * Contact information: MeVis Medical Solutions AG, Universitaetsallee 29, | |
ezust
|
r0 | * 28359 Bremen, Germany or: | |
* | |||
* http://www.mevis.de | |||
* | |||
*/ | |||
//---------------------------------------------------------------------------------- | |||
/*! | |||
// \file PythonQt.cpp | |||
// \author Florian Link | |||
// \author Last changed by $Author: florian $ | |||
// \date 2006-05 | |||
*/ | |||
//---------------------------------------------------------------------------------- | |||
#include "PythonQtClassInfo.h" | |||
#include "PythonQtMethodInfo.h" | |||
#include "PythonQt.h" | |||
#include <QMetaMethod> | |||
florianlink
|
r51 | #include <QMetaObject> | |
#include <QMetaEnum> | |||
ezust
|
r0 | ||
QHash<QByteArray, int> PythonQtMethodInfo::_parameterTypeDict; | |||
florianlink
|
r24 | PythonQtClassInfo::PythonQtClassInfo() { | |
_meta = NULL; | |||
ezust
|
r0 | _constructors = NULL; | |
florianlink
|
r24 | _destructor = NULL; | |
florianlink
|
r10 | _decoratorProvider = NULL; | |
_decoratorProviderCB = NULL; | |||
florianlink
|
r18 | _pythonQtClassWrapper = NULL; | |
florianlink
|
r24 | _shellSetInstanceWrapperCB = NULL; | |
_metaTypeId = -1; | |||
florianlink
|
r119 | _typeSlots = 0; | |
florianlink
|
r24 | _isQObject = false; | |
florianlink
|
r51 | _enumsCreated = false; | |
ezust
|
r0 | } | |
PythonQtClassInfo::~PythonQtClassInfo() | |||
florianlink
|
r10 | { | |
clearCachedMembers(); | |||
florianlink
|
r24 | ||
if (_constructors) { | |||
_constructors->deleteOverloadsAndThis(); | |||
} | |||
if (_destructor) { | |||
_destructor->deleteOverloadsAndThis(); | |||
} | |||
foreach(PythonQtSlotInfo* info, _decoratorSlots) { | |||
info->deleteOverloadsAndThis(); | |||
} | |||
} | |||
void PythonQtClassInfo::setupQObject(const QMetaObject* meta) | |||
{ | |||
// _wrappedClassName is already set earlier in the class setup | |||
_isQObject = true; | |||
_meta = meta; | |||
} | |||
void PythonQtClassInfo::setupCPPObject(const QByteArray& classname) | |||
{ | |||
_isQObject = false; | |||
_wrappedClassName = classname; | |||
_metaTypeId = QMetaType::type(classname); | |||
florianlink
|
r10 | } | |
void PythonQtClassInfo::clearCachedMembers() | |||
ezust
|
r0 | { | |
QHashIterator<QByteArray, PythonQtMemberInfo> i(_cachedMembers); | |||
while (i.hasNext()) { | |||
PythonQtMemberInfo member = i.next().value(); | |||
florianlink
|
r173 | if (member._type== PythonQtMemberInfo::Slot || member._type== PythonQtMemberInfo::Signal) { | |
ezust
|
r0 | PythonQtSlotInfo* info = member._slot; | |
while (info) { | |||
PythonQtSlotInfo* next = info->nextInfo(); | |||
delete info; | |||
info = next; | |||
} | |||
} | |||
} | |||
} | |||
int PythonQtClassInfo::findCharOffset(const char* sigStart, char someChar) | |||
{ | |||
const char* sigEnd = sigStart; | |||
char c; | |||
do { | |||
c = *sigEnd++; | |||
} while (c!=someChar && c!=0); | |||
return sigEnd-sigStart-1; | |||
} | |||
florianlink
|
r10 | bool PythonQtClassInfo::lookForPropertyAndCache(const char* memberName) | |
{ | |||
florianlink
|
r85 | if (!_meta) return false; | |
florianlink
|
r10 | bool found = false; | |
bool nameMapped = false; | |||
const char* attributeName = memberName; | |||
// look for properties | |||
int i = _meta->indexOfProperty(attributeName); | |||
if (i==-1) { | |||
// try to map name to objectName | |||
if (qstrcmp(attributeName, "name")==0) { | |||
attributeName = "objectName"; | |||
nameMapped = true; | |||
i = _meta->indexOfProperty(attributeName); | |||
} | |||
} | |||
if (i!=-1) { | |||
PythonQtMemberInfo newInfo(_meta->property(i)); | |||
_cachedMembers.insert(attributeName, newInfo); | |||
if (nameMapped) { | |||
_cachedMembers.insert(memberName, newInfo); | |||
} | |||
#ifdef PYTHONQT_DEBUG | |||
std::cout << "caching property " << memberName << " on " << _meta->className() << std::endl; | |||
#endif | |||
found = true; | |||
} | |||
return found; | |||
} | |||
florianlink
|
r24 | PythonQtSlotInfo* PythonQtClassInfo::recursiveFindDecoratorSlotsFromDecoratorProvider(const char* memberName, PythonQtSlotInfo* inputInfo, bool &found, QHash<QByteArray, PythonQtMemberInfo>& memberCache, int upcastingOffset) | |
{ | |||
inputInfo = findDecoratorSlotsFromDecoratorProvider(memberName, inputInfo, found, memberCache, upcastingOffset); | |||
foreach(const ParentClassInfo& info, _parentClasses) { | |||
inputInfo = info._parent->recursiveFindDecoratorSlotsFromDecoratorProvider(memberName, inputInfo, found, memberCache, upcastingOffset+info._upcastingOffset); | |||
} | |||
return inputInfo; | |||
} | |||
PythonQtSlotInfo* PythonQtClassInfo::findDecoratorSlotsFromDecoratorProvider(const char* memberName, PythonQtSlotInfo* tail, bool &found, QHash<QByteArray, PythonQtMemberInfo>& memberCache, int upcastingOffset) { | |||
florianlink
|
r10 | QObject* decoratorProvider = decorator(); | |
florianlink
|
r163 | int memberNameLen = static_cast<int>(strlen(memberName)); | |
florianlink
|
r10 | if (decoratorProvider) { | |
florianlink
|
r24 | //qDebug()<< "looking " << decoratorProvider->metaObject()->className() << " " << memberName << " " << upcastingOffset; | |
florianlink
|
r10 | const QMetaObject* meta = decoratorProvider->metaObject(); | |
int numMethods = meta->methodCount(); | |||
int startFrom = QObject::staticMetaObject.methodCount(); | |||
for (int i = startFrom; i < numMethods; i++) { | |||
QMetaMethod m = meta->method(i); | |||
if ((m.methodType() == QMetaMethod::Method || | |||
m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public) { | |||
const char* sigStart = m.signature(); | |||
bool isClassDeco = false; | |||
if (qstrncmp(sigStart, "static_", 7)==0) { | |||
// skip the static_classname_ part of the string | |||
sigStart += 7 + 1 + strlen(className()); | |||
isClassDeco = true; | |||
} else if (qstrncmp(sigStart, "new_", 4)==0) { | |||
isClassDeco = true; | |||
} else if (qstrncmp(sigStart, "delete_", 7)==0) { | |||
isClassDeco = true; | |||
} | |||
// find the first '(' | |||
int offset = findCharOffset(sigStart, '('); | |||
// XXX no checking is currently done if the slots have correct first argument or not... | |||
// check if same length and same name | |||
if (memberNameLen == offset && qstrncmp(memberName, sigStart, offset)==0) { | |||
found = true; | |||
florianlink
|
r54 | PythonQtSlotInfo* info = new PythonQtSlotInfo(this, m, i, decoratorProvider, isClassDeco?PythonQtSlotInfo::ClassDecorator:PythonQtSlotInfo::InstanceDecorator); | |
florianlink
|
r24 | info->setUpcastingOffset(upcastingOffset); | |
//qDebug()<< "adding " << decoratorProvider->metaObject()->className() << " " << memberName << " " << upcastingOffset; | |||
florianlink
|
r10 | if (tail) { | |
tail->setNextInfo(info); | |||
} else { | |||
PythonQtMemberInfo newInfo(info); | |||
memberCache.insert(memberName, newInfo); | |||
} | |||
tail = info; | |||
} | |||
} | |||
} | |||
} | |||
florianlink
|
r24 | ||
tail = findDecoratorSlots(memberName, memberNameLen, tail, found, memberCache, upcastingOffset); | |||
florianlink
|
r10 | ||
florianlink
|
r180 | // now look for slots/signals/methods on this level of the meta object | |
florianlink
|
r10 | if (_meta) { | |
int numMethods = _meta->methodCount(); | |||
florianlink
|
r180 | // start from methodOffset, to only add slots which are located in this class, | |
// and not in the parent class, which is traversed recursively later on. | |||
// (if the class in not a QObject, we are working with a script wrapper QObject | |||
// and need to read all slots/signals starting from 0). | |||
int methodOffset = _isQObject?_meta->methodOffset():0; | |||
for (int i = methodOffset; i < numMethods; i++) { | |||
florianlink
|
r10 | QMetaMethod m = _meta->method(i); | |
florianlink
|
r69 | if (((m.methodType() == QMetaMethod::Method || | |
florianlink
|
r180 | m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public) | |
florianlink
|
r69 | || m.methodType()==QMetaMethod::Signal) { | |
florianlink
|
r180 | ||
const char* sigStart = m.signature(); | |||
// find the first '(' | |||
int offset = findCharOffset(sigStart, '('); | |||
// check if same length and same name | |||
if (memberNameLen == offset && qstrncmp(memberName, sigStart, offset)==0) { | |||
found = true; | |||
PythonQtSlotInfo* info = new PythonQtSlotInfo(this, m, i); | |||
if (tail) { | |||
tail->setNextInfo(info); | |||
} else { | |||
PythonQtMemberInfo newInfo(info); | |||
memberCache.insert(memberName, newInfo); | |||
} | |||
tail = info; | |||
florianlink
|
r10 | } | |
} | |||
} | |||
} | |||
florianlink
|
r180 | return tail; | |
} | |||
bool PythonQtClassInfo::lookForMethodAndCache(const char* memberName) | |||
{ | |||
bool found = false; | |||
PythonQtSlotInfo* tail = NULL; | |||
florianlink
|
r10 | ||
// look for dynamic decorators in this class and in derived classes | |||
florianlink
|
r180 | // (do this first to allow overloading of existing slots with generated wrappers, | |
// e.g. QDialog::accept is overloaded with PythonQtWrapper_QDialog::accept decorator) | |||
florianlink
|
r24 | tail = recursiveFindDecoratorSlotsFromDecoratorProvider(memberName, tail, found, _cachedMembers, 0); | |
florianlink
|
r180 | ||
florianlink
|
r10 | return found; | |
} | |||
bool PythonQtClassInfo::lookForEnumAndCache(const QMetaObject* meta, const char* memberName) | |||
{ | |||
bool found = false; | |||
// look for enum values | |||
int enumCount = meta->enumeratorCount(); | |||
for (int i=0;i<enumCount; i++) { | |||
QMetaEnum e = meta->enumerator(i); | |||
florianlink
|
r55 | // we do not want flags, they will cause our values to appear two times | |
if (e.isFlag()) continue; | |||
florianlink
|
r10 | for (int j=0; j < e.keyCount(); j++) { | |
if (qstrcmp(e.key(j), memberName)==0) { | |||
florianlink
|
r51 | PyObject* enumType = findEnumWrapper(e.name()); | |
if (enumType) { | |||
PythonQtObjectPtr enumValuePtr; | |||
florianlink
|
r56 | enumValuePtr.setNewRef(PythonQtPrivate::createEnumValueInstance(enumType, e.value(j))); | |
florianlink
|
r51 | PythonQtMemberInfo newInfo(enumValuePtr); | |
_cachedMembers.insert(memberName, newInfo); | |||
florianlink
|
r10 | #ifdef PYTHONQT_DEBUG | |
florianlink
|
r51 | std::cout << "caching enum " << memberName << " on " << meta->className() << std::endl; | |
florianlink
|
r10 | #endif | |
florianlink
|
r51 | found = true; | |
break; | |||
} else { | |||
std::cout << "enum " << e.name() << " not found on " << className() << std::endl; | |||
} | |||
florianlink
|
r10 | } | |
} | |||
} | |||
return found; | |||
} | |||
ezust
|
r0 | PythonQtMemberInfo PythonQtClassInfo::member(const char* memberName) | |
{ | |||
PythonQtMemberInfo info = _cachedMembers.value(memberName); | |||
if (info._type != PythonQtMemberInfo::Invalid) { | |||
return info; | |||
} else { | |||
bool found = false; | |||
florianlink
|
r10 | ||
found = lookForPropertyAndCache(memberName); | |||
if (!found) { | |||
found = lookForMethodAndCache(memberName); | |||
ezust
|
r0 | } | |
florianlink
|
r10 | if (!found) { | |
if (_meta) { | |||
// check enums in our meta object directly | |||
found = lookForEnumAndCache(_meta, memberName); | |||
ezust
|
r0 | } | |
florianlink
|
r10 | if (!found) { | |
// check enums in the class hierachy of CPP classes | |||
// look for dynamic decorators in this class and in derived classes | |||
florianlink
|
r24 | QList<QObject*> decoObjects; | |
recursiveCollectDecoratorObjects(decoObjects); | |||
foreach(QObject* deco, decoObjects) { | |||
// call on ourself for caching, but with different metaObject(): | |||
found = lookForEnumAndCache(deco->metaObject(), memberName); | |||
if (found) { | |||
break; | |||
ezust
|
r0 | } | |
} | |||
} | |||
} | |||
florianlink
|
r51 | if (!found) { | |
florianlink
|
r67 | // maybe it is an enum wrapper? | |
florianlink
|
r51 | PyObject* p = findEnumWrapper(memberName); | |
if (p) { | |||
info._type = PythonQtMemberInfo::EnumWrapper; | |||
info._enumWrapper = p; | |||
_cachedMembers.insert(memberName, info); | |||
found = true; | |||
} | |||
} | |||
florianlink
|
r67 | if (!found) { | |
// since python keywords can not be looked up, we check if the name contains a single trailing _ | |||
// and remove that and look again, so that we e.g. find exec on an exec_ lookup | |||
QByteArray mbrName(memberName); | |||
if ((mbrName.length()>2) && | |||
(mbrName.at(mbrName.length()-1) == '_') && | |||
(mbrName.at(mbrName.length()-2) != '_')) { | |||
mbrName = mbrName.mid(0,mbrName.length()-1); | |||
found = lookForMethodAndCache(mbrName.constData()); | |||
if (found) { | |||
return _cachedMembers.value(mbrName); | |||
} | |||
} | |||
} | |||
ezust
|
r0 | if (!found) { | |
florianlink
|
r10 | // we store a NotFound member, so that we get a quick result for non existing members (e.g. operator_equal lookup) | |
info._type = PythonQtMemberInfo::NotFound; | |||
_cachedMembers.insert(memberName, info); | |||
ezust
|
r0 | } | |
} | |||
florianlink
|
r10 | ||
return _cachedMembers.value(memberName); | |||
ezust
|
r0 | } | |
florianlink
|
r24 | void PythonQtClassInfo::recursiveCollectDecoratorObjects(QList<QObject*>& decoratorObjects) { | |
QObject* deco = decorator(); | |||
if (deco) { | |||
decoratorObjects.append(deco); | |||
} | |||
foreach(const ParentClassInfo& info, _parentClasses) { | |||
info._parent->recursiveCollectDecoratorObjects(decoratorObjects); | |||
} | |||
} | |||
void PythonQtClassInfo::recursiveCollectClassInfos(QList<PythonQtClassInfo*>& classInfoObjects) { | |||
classInfoObjects.append(this); | |||
foreach(const ParentClassInfo& info, _parentClasses) { | |||
info._parent->recursiveCollectClassInfos(classInfoObjects); | |||
} | |||
} | |||
PythonQtSlotInfo* PythonQtClassInfo::findDecoratorSlots(const char* memberName, int memberNameLen, PythonQtSlotInfo* tail, bool &found, QHash<QByteArray, PythonQtMemberInfo>& memberCache, int upcastingOffset) | |||
ezust
|
r0 | { | |
florianlink
|
r24 | QListIterator<PythonQtSlotInfo*> it(_decoratorSlots); | |
ezust
|
r0 | while (it.hasNext()) { | |
PythonQtSlotInfo* infoOrig = it.next(); | |||
const char* sigStart = infoOrig->metaMethod()->signature(); | |||
if (qstrncmp("static_", sigStart, 7)==0) { | |||
sigStart += 7; | |||
sigStart += findCharOffset(sigStart, '_')+1; | |||
} | |||
int offset = findCharOffset(sigStart, '('); | |||
if (memberNameLen == offset && qstrncmp(memberName, sigStart, offset)==0) { | |||
//make a copy, otherwise we will have trouble on overloads! | |||
PythonQtSlotInfo* info = new PythonQtSlotInfo(*infoOrig); | |||
florianlink
|
r24 | info->setUpcastingOffset(upcastingOffset); | |
ezust
|
r0 | found = true; | |
if (tail) { | |||
tail->setNextInfo(info); | |||
} else { | |||
PythonQtMemberInfo newInfo(info); | |||
florianlink
|
r24 | memberCache.insert(memberName, newInfo); | |
ezust
|
r0 | } | |
tail = info; | |||
} | |||
} | |||
return tail; | |||
} | |||
florianlink
|
r10 | void PythonQtClassInfo::listDecoratorSlotsFromDecoratorProvider(QStringList& list, bool metaOnly) { | |
QObject* decoratorProvider = decorator(); | |||
if (decoratorProvider) { | |||
const QMetaObject* meta = decoratorProvider->metaObject(); | |||
int numMethods = meta->methodCount(); | |||
int startFrom = QObject::staticMetaObject.methodCount(); | |||
for (int i = startFrom; i < numMethods; i++) { | |||
QMetaMethod m = meta->method(i); | |||
if ((m.methodType() == QMetaMethod::Method || | |||
m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public) { | |||
const char* sigStart = m.signature(); | |||
bool isClassDeco = false; | |||
if (qstrncmp(sigStart, "static_", 7)==0) { | |||
// skip the static_classname_ part of the string | |||
sigStart += 7 + 1 + strlen(className()); | |||
isClassDeco = true; | |||
} else if (qstrncmp(sigStart, "new_", 4)==0) { | |||
florianlink
|
r16 | continue; | |
florianlink
|
r10 | } else if (qstrncmp(sigStart, "delete_", 7)==0) { | |
florianlink
|
r16 | continue; | |
florianlink
|
r116 | } else if (qstrncmp(sigStart, "py_", 3)==0) { | |
// hide everything that starts with py_ | |||
continue; | |||
florianlink
|
r10 | } | |
// find the first '(' | |||
int offset = findCharOffset(sigStart, '('); | |||
// XXX no checking is currently done if the slots have correct first argument or not... | |||
if (!metaOnly || isClassDeco) { | |||
list << QString::fromLatin1(sigStart, offset); | |||
} | |||
} | |||
} | |||
} | |||
florianlink
|
r24 | ||
// look for global decorator slots | |||
QListIterator<PythonQtSlotInfo*> it(_decoratorSlots); | |||
while (it.hasNext()) { | |||
PythonQtSlotInfo* slot = it.next(); | |||
if (metaOnly) { | |||
if (slot->isClassDecorator()) { | |||
QByteArray first = slot->slotName(); | |||
if (first.startsWith("static_")) { | |||
int idx = first.indexOf('_'); | |||
idx = first.indexOf('_', idx+1); | |||
first = first.mid(idx+1); | |||
} | |||
list << first; | |||
} | |||
} else { | |||
list << slot->slotName(); | |||
} | |||
} | |||
florianlink
|
r10 | } | |
florianlink
|
r34 | QStringList PythonQtClassInfo::propertyList() | |
{ | |||
ezust
|
r0 | QStringList l; | |
florianlink
|
r34 | if (_isQObject && _meta) { | |
ezust
|
r0 | int i; | |
int numProperties = _meta->propertyCount(); | |||
for (i = 0; i < numProperties; i++) { | |||
QMetaProperty p = _meta->property(i); | |||
l << QString(p.name()); | |||
} | |||
} | |||
florianlink
|
r34 | return l; | |
} | |||
florianlink
|
r173 | QStringList PythonQtClassInfo::memberList() | |
florianlink
|
r34 | { | |
decorator(); | |||
QStringList l; | |||
QString h; | |||
florianlink
|
r10 | // normal slots of QObject (or wrapper QObject) | |
florianlink
|
r173 | if (_meta) { | |
ezust
|
r0 | int numMethods = _meta->methodCount(); | |
florianlink
|
r24 | bool skipQObj = !_isQObject; | |
ezust
|
r0 | for (int i = skipQObj?QObject::staticMetaObject.methodCount():0; i < numMethods; i++) { | |
QMetaMethod m = _meta->method(i); | |||
florianlink
|
r69 | if (((m.methodType() == QMetaMethod::Method || | |
m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public) | |||
|| m.methodType()==QMetaMethod::Signal) { | |||
florianlink
|
r54 | QByteArray signa(m.signature()); | |
signa = signa.left(signa.indexOf('(')); | |||
l << signa; | |||
ezust
|
r0 | } | |
} | |||
} | |||
florianlink
|
r10 | ||
{ | |||
// look for dynamic decorators in this class and in derived classes | |||
florianlink
|
r24 | QList<PythonQtClassInfo*> infos; | |
recursiveCollectClassInfos(infos); | |||
foreach(PythonQtClassInfo* info, infos) { | |||
florianlink
|
r173 | info->listDecoratorSlotsFromDecoratorProvider(l, false); | |
ezust
|
r0 | } | |
} | |||
florianlink
|
r10 | ||
// List enumerator keys... | |||
QList<const QMetaObject*> enumMetaObjects; | |||
if (_meta) { | |||
enumMetaObjects << _meta; | |||
} | |||
// check enums in the class hierachy of CPP classes | |||
florianlink
|
r24 | QList<QObject*> decoObjects; | |
recursiveCollectDecoratorObjects(decoObjects); | |||
foreach(QObject* deco, decoObjects) { | |||
enumMetaObjects << deco->metaObject(); | |||
florianlink
|
r10 | } | |
foreach(const QMetaObject* meta, enumMetaObjects) { | |||
for (int i = 0; i<meta->enumeratorCount(); i++) { | |||
QMetaEnum e = meta->enumerator(i); | |||
florianlink
|
r51 | l << e.name(); | |
florianlink
|
r55 | // we do not want flags, they will cause our values to appear two times | |
if (e.isFlag()) continue; | |||
ezust
|
r0 | for (int j=0; j < e.keyCount(); j++) { | |
l << QString(e.key(j)); | |||
} | |||
} | |||
} | |||
florianlink
|
r26 | ||
return QSet<QString>::fromList(l).toList(); | |||
ezust
|
r0 | } | |
const char* PythonQtClassInfo::className() | |||
{ | |||
florianlink
|
r24 | return _wrappedClassName.constData(); | |
} | |||
void* PythonQtClassInfo::castTo(void* ptr, const char* classname) | |||
{ | |||
if (ptr==NULL) { | |||
return NULL; | |||
} | |||
if (_wrappedClassName == classname) { | |||
return ptr; | |||
} | |||
foreach(const ParentClassInfo& info, _parentClasses) { | |||
void* result = info._parent->castTo((char*)ptr + info._upcastingOffset, classname); | |||
if (result) { | |||
return result; | |||
} | |||
ezust
|
r0 | } | |
florianlink
|
r24 | return NULL; | |
ezust
|
r0 | } | |
bool PythonQtClassInfo::inherits(const char* name) | |||
{ | |||
florianlink
|
r24 | if (_wrappedClassName == name) { | |
return true; | |||
} | |||
foreach(const ParentClassInfo& info, _parentClasses) { | |||
if (info._parent->inherits(name)) { | |||
return true; | |||
florianlink
|
r10 | } | |
florianlink
|
r24 | } | |
return false; | |||
} | |||
bool PythonQtClassInfo::inherits(PythonQtClassInfo* classInfo) | |||
{ | |||
if (classInfo == this) { | |||
return true; | |||
} | |||
foreach(const ParentClassInfo& info, _parentClasses) { | |||
if (info._parent->inherits(classInfo)) { | |||
return true; | |||
ezust
|
r0 | } | |
} | |||
return false; | |||
} | |||
QString PythonQtClassInfo::help() | |||
{ | |||
florianlink
|
r10 | decorator(); | |
ezust
|
r0 | QString h; | |
h += QString("--- ") + QString(className()) + QString(" ---\n"); | |||
florianlink
|
r24 | if (_isQObject) { | |
ezust
|
r0 | h += "Properties:\n"; | |
int i; | |||
int numProperties = _meta->propertyCount(); | |||
for (i = 0; i < numProperties; i++) { | |||
QMetaProperty p = _meta->property(i); | |||
h += QString(p.name()) + " (" + QString(p.typeName()) + " )\n"; | |||
} | |||
} | |||
if (constructors()) { | |||
h += "Constructors:\n"; | |||
PythonQtSlotInfo* constr = constructors(); | |||
while (constr) { | |||
florianlink
|
r23 | h += constr->fullSignature() + "\n"; | |
ezust
|
r0 | constr = constr->nextInfo(); | |
} | |||
} | |||
h += "Slots:\n"; | |||
h += "QString help()\n"; | |||
h += "QString className()\n"; | |||
florianlink
|
r10 | if (_meta) { | |
int numMethods = _meta->methodCount(); | |||
for (int i = 0; i < numMethods; i++) { | |||
QMetaMethod m = _meta->method(i); | |||
if ((m.methodType() == QMetaMethod::Method || | |||
m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public) { | |||
florianlink
|
r54 | PythonQtSlotInfo slot(this, m, i); | |
florianlink
|
r23 | h += slot.fullSignature()+ "\n"; | |
florianlink
|
r10 | } | |
ezust
|
r0 | } | |
} | |||
florianlink
|
r10 | ||
// TODO xxx : decorators and enums from decorator() are missing... | |||
// maybe we can reuse memberlist()? | |||
if (_meta && _meta->enumeratorCount()) { | |||
ezust
|
r0 | h += "Enums:\n"; | |
for (int i = 0; i<_meta->enumeratorCount(); i++) { | |||
QMetaEnum e = _meta->enumerator(i); | |||
h += QString(e.name()) + " {"; | |||
for (int j=0; j < e.keyCount(); j++) { | |||
if (j) { h+= ", "; } | |||
h += e.key(j); | |||
} | |||
h += " }\n"; | |||
} | |||
} | |||
florianlink
|
r24 | if (_isQObject && _meta) { | |
ezust
|
r0 | int numMethods = _meta->methodCount(); | |
if (numMethods>0) { | |||
h += "Signals:\n"; | |||
florianlink
|
r10 | for (int i = 0; i < numMethods; i++) { | |
ezust
|
r0 | QMetaMethod m = _meta->method(i); | |
if (m.methodType() == QMetaMethod::Signal) { | |||
h += QString(m.signature()) + "\n"; | |||
} | |||
} | |||
} | |||
} | |||
return h; | |||
} | |||
PythonQtSlotInfo* PythonQtClassInfo::constructors() | |||
{ | |||
if (!_constructors) { | |||
florianlink
|
r10 | // force creation of lazy decorator, which will register the decorators | |
decorator(); | |||
ezust
|
r0 | } | |
return _constructors; | |||
} | |||
florianlink
|
r24 | PythonQtSlotInfo* PythonQtClassInfo::destructor() | |
{ | |||
if (!_destructor) { | |||
// force creation of lazy decorator, which will register the decorators | |||
decorator(); | |||
} | |||
return _destructor; | |||
} | |||
void PythonQtClassInfo::addConstructor(PythonQtSlotInfo* info) | |||
{ | |||
PythonQtSlotInfo* prev = constructors(); | |||
if (prev) { | |||
info->setNextInfo(prev->nextInfo()); | |||
prev->setNextInfo(info); | |||
} else { | |||
_constructors = info; | |||
} | |||
} | |||
void PythonQtClassInfo::addDecoratorSlot(PythonQtSlotInfo* info) | |||
{ | |||
_decoratorSlots.append(info); | |||
} | |||
void PythonQtClassInfo::setDestructor(PythonQtSlotInfo* info) | |||
{ | |||
if (_destructor) { | |||
_destructor->deleteOverloadsAndThis(); | |||
} | |||
_destructor = info; | |||
} | |||
ezust
|
r0 | void PythonQtClassInfo::setMetaObject(const QMetaObject* meta) | |
{ | |||
_meta = meta; | |||
florianlink
|
r10 | clearCachedMembers(); | |
} | |||
QObject* PythonQtClassInfo::decorator() | |||
{ | |||
if (!_decoratorProvider && _decoratorProviderCB) { | |||
_decoratorProvider = (*_decoratorProviderCB)(); | |||
if (_decoratorProvider) { | |||
_decoratorProvider->setParent(PythonQt::priv()); | |||
florianlink
|
r54 | // setup enums early, since they might be needed by the constructor decorators: | |
if (!_enumsCreated) { | |||
createEnumWrappers(); | |||
} | |||
florianlink
|
r10 | PythonQt::priv()->addDecorators(_decoratorProvider, PythonQtPrivate::ConstructorDecorator | PythonQtPrivate::DestructorDecorator); | |
} | |||
} | |||
florianlink
|
r54 | // check if enums need to be created and create them if they are not yet created | |
florianlink
|
r51 | if (!_enumsCreated) { | |
createEnumWrappers(); | |||
} | |||
florianlink
|
r10 | return _decoratorProvider; | |
ezust
|
r0 | } | |
florianlink
|
r15 | ||
florianlink
|
r163 | void* PythonQtClassInfo::recursiveCastDownIfPossible(void* ptr, const char** resultClassName) | |
florianlink
|
r26 | { | |
if (!_polymorphicHandlers.isEmpty()) { | |||
foreach(PythonQtPolymorphicHandlerCB* cb, _polymorphicHandlers) { | |||
void* resultPtr = (*cb)(ptr, resultClassName); | |||
if (resultPtr) { | |||
return resultPtr; | |||
} | |||
} | |||
} | |||
foreach(const ParentClassInfo& info, _parentClasses) { | |||
if (!info._parent->isQObject()) { | |||
void* resultPtr = info._parent->recursiveCastDownIfPossible((char*)ptr + info._upcastingOffset, resultClassName); | |||
if (resultPtr) { | |||
return resultPtr; | |||
} | |||
} | |||
} | |||
return NULL; | |||
} | |||
void* PythonQtClassInfo::castDownIfPossible(void* ptr, PythonQtClassInfo** resultClassInfo) | |||
{ | |||
florianlink
|
r163 | const char* className; | |
florianlink
|
r31 | // this would do downcasting recursively... | |
// void* resultPtr = recursiveCastDownIfPossible(ptr, &className); | |||
// we only do downcasting on the base object, not on the whole inheritance tree... | |||
void* resultPtr = NULL; | |||
if (!_polymorphicHandlers.isEmpty()) { | |||
foreach(PythonQtPolymorphicHandlerCB* cb, _polymorphicHandlers) { | |||
resultPtr = (*cb)(ptr, &className); | |||
if (resultPtr) { | |||
break; | |||
} | |||
} | |||
} | |||
florianlink
|
r26 | if (resultPtr) { | |
*resultClassInfo = PythonQt::priv()->getClassInfo(className); | |||
} else { | |||
*resultClassInfo = this; | |||
resultPtr = ptr; | |||
} | |||
return resultPtr; | |||
} | |||
florianlink
|
r41 | ||
florianlink
|
r64 | PyObject* PythonQtClassInfo::findEnumWrapper(const QByteArray& name, PythonQtClassInfo* localScope, bool* isLocalEnum) | |
florianlink
|
r41 | { | |
florianlink
|
r64 | if (isLocalEnum) { | |
*isLocalEnum = true; | |||
} | |||
florianlink
|
r41 | int scopePos = name.lastIndexOf("::"); | |
if (scopePos != -1) { | |||
florianlink
|
r64 | if (isLocalEnum) { | |
*isLocalEnum = false; | |||
} | |||
// split into scope and enum name | |||
florianlink
|
r41 | QByteArray enumScope = name.mid(0,scopePos); | |
QByteArray enumName = name.mid(scopePos+2); | |||
PythonQtClassInfo* info = PythonQt::priv()->getClassInfo(enumScope); | |||
if (info) { | |||
florianlink
|
r55 | return info->findEnumWrapper(enumName); | |
florianlink
|
r41 | } else{ | |
florianlink
|
r55 | return NULL; | |
florianlink
|
r41 | } | |
} | |||
if (localScope) { | |||
florianlink
|
r55 | return localScope->findEnumWrapper(name); | |
florianlink
|
r41 | } else { | |
florianlink
|
r55 | return NULL; | |
florianlink
|
r41 | } | |
} | |||
florianlink
|
r51 | void PythonQtClassInfo::createEnumWrappers(const QMetaObject* meta) | |
{ | |||
for (int i = meta->enumeratorOffset();i<meta->enumeratorCount();i++) { | |||
QMetaEnum e = meta->enumerator(i); | |||
PythonQtObjectPtr p; | |||
florianlink
|
r56 | p.setNewRef(PythonQtPrivate::createNewPythonQtEnumWrapper(e.name(), _pythonQtClassWrapper)); | |
florianlink
|
r51 | _enumWrappers.append(p); | |
} | |||
} | |||
void PythonQtClassInfo::createEnumWrappers() | |||
{ | |||
if (!_enumsCreated) { | |||
_enumsCreated = true; | |||
if (_meta) { | |||
createEnumWrappers(_meta); | |||
} | |||
if (decorator()) { | |||
createEnumWrappers(decorator()->metaObject()); | |||
} | |||
foreach(const ParentClassInfo& info, _parentClasses) { | |||
info._parent->createEnumWrappers(); | |||
} | |||
} | |||
} | |||
PyObject* PythonQtClassInfo::findEnumWrapper(const char* name) { | |||
florianlink
|
r56 | // force enum creation | |
if (!_enumsCreated) { | |||
createEnumWrappers(); | |||
} | |||
florianlink
|
r51 | foreach(const PythonQtObjectPtr& p, _enumWrappers) { | |
const char* className = ((PyTypeObject*)p.object())->tp_name; | |||
if (qstrcmp(className, name)==0) { | |||
return p.object(); | |||
} | |||
} | |||
foreach(const ParentClassInfo& info, _parentClasses) { | |||
PyObject* p = info._parent->findEnumWrapper(name); | |||
if (p) return p; | |||
} | |||
return NULL; | |||
} | |||
florianlink
|
r157 | void PythonQtClassInfo::setDecoratorProvider( PythonQtQObjectCreatorFunctionCB* cb ) | |
{ | |||
_decoratorProviderCB = cb; | |||
_decoratorProvider = NULL; | |||
_enumsCreated = false; | |||
} | |||
void PythonQtClassInfo::clearNotFoundCachedMembers() | |||
{ | |||
// remove all not found entries, since a new decorator means new slots, | |||
// which might have been cached as "NotFound" already. | |||
QMutableHashIterator<QByteArray, PythonQtMemberInfo> it(_cachedMembers); | |||
while (it.hasNext()) { | |||
it.next(); | |||
if (it.value()._type == PythonQtMemberInfo::NotFound) { | |||
it.remove(); | |||
} | |||
} | |||
} | |||
florianlink
|
r173 | ||
//------------------------------------------------------------------------- | |||
PythonQtMemberInfo::PythonQtMemberInfo( PythonQtSlotInfo* info ) | |||
{ | |||
if (info->metaMethod()->methodType() == QMetaMethod::Signal) { | |||
_type = Signal; | |||
} else { | |||
_type = Slot; | |||
} | |||
_slot = info; | |||
_enumValue = NULL; | |||
} | |||
PythonQtMemberInfo::PythonQtMemberInfo( const PythonQtObjectPtr& enumValue ) | |||
{ | |||
_type = EnumValue; | |||
_slot = NULL; | |||
_enumValue = enumValue; | |||
_enumWrapper = NULL; | |||
} | |||
PythonQtMemberInfo::PythonQtMemberInfo( const QMetaProperty& prop ) | |||
{ | |||
_type = Property; | |||
_slot = NULL; | |||
_enumValue = NULL; | |||
_property = prop; | |||
_enumWrapper = NULL; | |||
} |