// Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "pysideqmllistproperty_p.h" #include "pysideqmlregistertype_p.h" #include #include #include #include #include #include #include // This is the user data we store in the property. class QmlListPropertyPrivate : public PySidePropertyPrivate { public: void metaCall(PyObject *source, QMetaObject::Call call, void **args) override; PyTypeObject *type = nullptr; PyObject *append = nullptr; PyObject *count = nullptr; PyObject *at = nullptr; PyObject *clear = nullptr; PyObject *replace = nullptr; PyObject *removeLast = nullptr; }; extern "C" { static PyObject *propList_tp_new(PyTypeObject *subtype, PyObject * /* args */, PyObject * /* kwds */) { auto *me = PepExt_TypeCallAlloc(subtype, 0); me->d = new QmlListPropertyPrivate; return reinterpret_cast(me); } static int propListTpInit(PyObject *self, PyObject *args, PyObject *kwds) { static const char *kwlist[] = {"type", "append", "count", "at", "clear", "replace", "removeLast", "doc", "notify", // PySideProperty "designable", "scriptable", "stored", "user", "constant", "final", nullptr}; auto *pySelf = reinterpret_cast(self); auto *data = static_cast(pySelf->d); char *doc{}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOOOOOsObbbbbb:QtQml.ListProperty", const_cast(kwlist), &data->type, &data->append, &data->count, &data->at, &data->clear, &data->replace, &data->removeLast, /*s*/ &doc, /*O*/ &(data->notify), // PySideProperty /*bbb*/ &(data->designable), &(data->scriptable), &(data->stored), /*bbb*/ &(data->user), &(data->constant), &(data->final))) { return -1; } if (doc) data->doc = doc; else data->doc.clear(); PyTypeObject *qobjectType = qObjectType(); if (!PySequence_Contains(data->type->tp_mro, reinterpret_cast(qobjectType))) { PyErr_Format(PyExc_TypeError, "A type inherited from %s expected, got %s.", qobjectType->tp_name, data->type->tp_name); return -1; } if ((data->append && data->append != Py_None && !PyCallable_Check(data->append)) || (data->count && data->count != Py_None && !PyCallable_Check(data->count)) || (data->at && data->at != Py_None && !PyCallable_Check(data->at)) || (data->clear && data->clear != Py_None && !PyCallable_Check(data->clear)) || (data->replace && data->replace != Py_None && !PyCallable_Check(data->replace)) || (data->removeLast && data->removeLast != Py_None && !PyCallable_Check(data->removeLast))) { PyErr_Format(PyExc_TypeError, "Non-callable parameter given"); return -1; } data->typeName = QByteArrayLiteral("QQmlListProperty"); return 0; } static PyTypeObject *createPropertyListType() { PyType_Slot PropertyListType_slots[] = { {Py_tp_new, reinterpret_cast(propList_tp_new)}, {Py_tp_init, reinterpret_cast(propListTpInit)}, {0, nullptr} }; PyType_Spec PropertyListType_spec = { "2:PySide6.QtQml.ListProperty", sizeof(PySideProperty), 0, Py_TPFLAGS_DEFAULT, PropertyListType_slots, }; Shiboken::AutoDecRef bases(Py_BuildValue("(O)", PySideProperty_TypeF())); return SbkType_FromSpecWithBases(&PropertyListType_spec, bases.object()); } PyTypeObject *PropertyList_TypeF(void) { // PYSIDE-2230: This was a wrong replacement by static AutoDecref. // Never do that, deletes things way too late. static PyTypeObject *type = createPropertyListType(); return type; } } // extern "C" // Implementation of QQmlListProperty::AppendFunction callback void propListAppender(QQmlListProperty *propList, QObject *item) { Shiboken::GilState state; Shiboken::AutoDecRef args(PyTuple_New(2)); PyTypeObject *qobjectType = qObjectType(); PyTuple_SetItem(args, 0, Shiboken::Conversions::pointerToPython(qobjectType, propList->object)); PyTuple_SetItem(args, 1, Shiboken::Conversions::pointerToPython(qobjectType, item)); auto *data = reinterpret_cast(propList->data); Shiboken::AutoDecRef retVal(PyObject_CallObject(data->append, args)); if (PyErr_Occurred()) PyErr_Print(); } // Implementation of QQmlListProperty::CountFunction callback qsizetype propListCount(QQmlListProperty *propList) { Shiboken::GilState state; Shiboken::AutoDecRef args(PyTuple_New(1)); PyTuple_SetItem(args, 0, Shiboken::Conversions::pointerToPython(qObjectType(), propList->object)); auto *data = reinterpret_cast(propList->data); Shiboken::AutoDecRef retVal(PyObject_CallObject(data->count, args)); // Check return type if (PyErr_Occurred()) { PyErr_Print(); return 0; } qsizetype cppResult = 0; auto *converter = Shiboken::Conversions::PrimitiveTypeConverter(); if (auto *pythonToCpp = Shiboken::Conversions::isPythonToCppConvertible(converter, retVal)) pythonToCpp(retVal, &cppResult); return cppResult; } // Implementation of QQmlListProperty::AtFunction callback QObject *propListAt(QQmlListProperty *propList, qsizetype index) { Shiboken::GilState state; Shiboken::AutoDecRef args(PyTuple_New(2)); PyTypeObject *qobjectType = qObjectType(); PyTuple_SetItem(args, 0, Shiboken::Conversions::pointerToPython(qobjectType, propList->object)); auto *converter = Shiboken::Conversions::PrimitiveTypeConverter(); PyTuple_SetItem(args, 1, Shiboken::Conversions::copyToPython(converter, &index)); auto *data = reinterpret_cast(propList->data); Shiboken::AutoDecRef retVal(PyObject_CallObject(data->at, args)); QObject *result = nullptr; if (PyErr_Occurred()) PyErr_Print(); else if (PyType_IsSubtype(Py_TYPE(retVal), data->type)) Shiboken::Conversions::pythonToCppPointer(qobjectType, retVal, &result); return result; } // Implementation of QQmlListProperty::ClearFunction callback void propListClear(QQmlListProperty * propList) { Shiboken::GilState state; Shiboken::AutoDecRef args(PyTuple_New(1)); PyTypeObject *qobjectType = qObjectType(); PyTuple_SetItem(args, 0, Shiboken::Conversions::pointerToPython(qobjectType, propList->object)); auto *data = reinterpret_cast(propList->data); Shiboken::AutoDecRef retVal(PyObject_CallObject(data->clear, args)); if (PyErr_Occurred()) PyErr_Print(); } // Implementation of QQmlListProperty::ReplaceFunction callback void propListReplace(QQmlListProperty *propList, qsizetype index, QObject *value) { Shiboken::GilState state; Shiboken::AutoDecRef args(PyTuple_New(3)); PyTypeObject *qobjectType = qObjectType(); PyTuple_SetItem(args, 0, Shiboken::Conversions::pointerToPython(qobjectType, propList->object)); auto *converter = Shiboken::Conversions::PrimitiveTypeConverter(); PyTuple_SetItem(args, 1, Shiboken::Conversions::copyToPython(converter, &index)); PyTuple_SetItem(args, 2, Shiboken::Conversions::pointerToPython(qobjectType, value)); auto *data = reinterpret_cast(propList->data); Shiboken::AutoDecRef retVal(PyObject_CallObject(data->replace, args)); if (PyErr_Occurred()) PyErr_Print(); } // Implementation of QQmlListProperty::RemoveLastFunction callback void propListRemoveLast(QQmlListProperty *propList) { Shiboken::GilState state; Shiboken::AutoDecRef args(PyTuple_New(1)); PyTypeObject *qobjectType = qObjectType(); PyTuple_SetItem(args, 0, Shiboken::Conversions::pointerToPython(qobjectType, propList->object)); auto *data = reinterpret_cast(propList->data); Shiboken::AutoDecRef retVal(PyObject_CallObject(data->removeLast, args)); if (PyErr_Occurred()) PyErr_Print(); } // qt_metacall specialization for ListProperties void QmlListPropertyPrivate::metaCall(PyObject *source, QMetaObject::Call call, void **args) { if (call != QMetaObject::ReadProperty) return; QObject *qobj{}; PyTypeObject *qobjectType = qObjectType(); Shiboken::Conversions::pythonToCppPointer(qobjectType, source, &qobj); QQmlListProperty declProp( qobj, this, append && append != Py_None ? &propListAppender : nullptr, count && count != Py_None ? &propListCount : nullptr, at && at != Py_None ? &propListAt : nullptr, clear && clear != Py_None ? &propListClear : nullptr, replace && replace != Py_None ? &propListReplace : nullptr, removeLast && removeLast != Py_None ? &propListRemoveLast : nullptr); // Copy the data to the memory location requested by the meta call void *v = args[0]; *reinterpret_cast *>(v) = declProp; } static const char *PropertyList_SignatureStrings[] = { "PySide6.QtQml.ListProperty(self,type:type," "append:typing.Optional[typing.Callable[...,typing.Any]]=None," "at:typing.Optional[typing.Callable[...,typing.Any]]=None," "clear:typing.Optional[typing.Callable[...,typing.Any]]=None," "count:typing.Optional[typing.Callable[...,typing.Any]]=None)", nullptr // Sentinel }; namespace PySide::Qml { void initQtQmlListProperty(PyObject *module) { // Export QmlListProperty type if (InitSignatureStrings(PropertyList_TypeF(), PropertyList_SignatureStrings) < 0) { PyErr_Print(); qWarning() << "Error initializing PropertyList type."; return; } // Register QQmlListProperty metatype for use in QML qRegisterMetaType>(); Py_INCREF(reinterpret_cast(PropertyList_TypeF())); PyModule_AddObject(module, PepType_GetNameStr(PropertyList_TypeF()), reinterpret_cast(PropertyList_TypeF())); } } // namespace PySide::Qml