/* For general Scribus (>=1.3.2) copyright and licensing information please refer to the COPYING file provided with the program. Following this notice may exist a copyright and/or license notice that predates the release of Scribus 1.3.2 for which a new license (GPL+exception) is in place. */ #include "cmdgetsetprop.h" #include "cmdutil.h" #include #include #include #include #include QObject* getQObjectFromPyArg(PyObject* arg) { if (PyString_Check(arg)) // It's a string. Look for a pageItem by that name. Do NOT accept a // selection. return getPageItemByName(QString::fromUtf8(PyString_AsString(arg))); else if (PyCObject_Check(arg)) { // It's a PyCObject, ie a wrapped pointer. Check it's not NULL // and return it. // FIXME: Try to check that its a pointer to a QObject instance QObject* tempObject = (QObject*)PyCObject_AsVoidPtr(arg); if (!tempObject) { PyErr_SetString(PyExc_TypeError, "INTERNAL: Passed NULL PyCObject"); return NULL; } else return tempObject; } else { // It's not a type we know what to do with PyErr_SetString(PyExc_TypeError, QObject::tr("Argument must be page item name, or PyCObject instance").toLocal8Bit().constData()); return NULL; } } PyObject* wrapQObject(QObject* obj) { return PyCObject_FromVoidPtr((void*)obj, NULL); } const char* getpropertytype(QObject* obj, const char* propname, bool includesuper) { const QMetaObject* objmeta = obj->metaObject(); int i = objmeta->indexOfProperty(propname); if (i == -1) return NULL; QMetaProperty propmeta = objmeta->property(i); if (!propmeta.isValid()) return NULL; const char* type = propmeta.typeName(); return type; } PyObject* scribus_propertyctype(PyObject* /*self*/, PyObject* args, PyObject* kw) { PyObject* objArg = NULL; char* propertyname = NULL; int includesuper = 1; char* kwargs[] = {const_cast("object"), const_cast("property"), const_cast("includesuper"), NULL}; if (!PyArg_ParseTupleAndKeywords(args, kw, "Oes|i", kwargs, &objArg, "ascii", &propertyname, &includesuper)) return NULL; // Get the QObject* the object argument refers to QObject* obj = getQObjectFromPyArg(objArg); if (!obj) return NULL; objArg = NULL; // no need to decref, it's borrowed // Look up the property and retrive its type information const char* type = getpropertytype( (QObject*)obj, propertyname, includesuper); if (type == NULL) { PyErr_SetString(PyExc_KeyError, QObject::tr("Property not found").toLocal8Bit().constData()); return NULL; } return PyString_FromString(type); } PyObject* convert_QStringList_to_PyListObject(QStringList& origlist) { PyObject* resultList = PyList_New(0); if (!resultList) return NULL; for ( QStringList::Iterator it = origlist.begin(); it != origlist.end(); ++it ) if (PyList_Append(resultList, PyString_FromString((*it).toUtf8().data())) == -1) return NULL; return resultList; } PyObject* convert_QObjectList_to_PyListObject(QObjectList* origlist) { PyObject* resultList = PyList_New(0); if (!resultList) return NULL; PyObject* objPtr = NULL; // Loop over the objects in the list and add them to the python // list wrapped in PyCObjects . for (int i = 0; i < origlist->count(); ++i) { // Wrap up the object pointer objPtr = wrapQObject(origlist->at(i)); if (!objPtr) { // Failed to wrap the object. An exception is already set. Py_DECREF(resultList); return NULL; } // and add it to the list if (PyList_Append(resultList, (PyObject*)objPtr) == -1) return NULL; } return resultList; } /*Qt4 we either need to copy QObject::qChildHelper or rewrite this PyObject* scribus_getchildren(PyObject* , PyObject* args, PyObject* kw) { PyObject* objArg = NULL; char* ofclass = NULL; char* ofname = NULL; int recursive = 0; int regexpmatch = 0; char* kwnames[] = {const_cast("object"), const_cast("ofclass"), const_cast("ofname"), const_cast("regexpmatch"), const_cast("recursive"), NULL}; if (!PyArg_ParseTupleAndKeywords(args, kw, "O|esesii", kwnames, &objArg, "ascii", &ofclass, "ascii", &ofname, ®expmatch, &recursive)) return NULL; // Get the QObject* the object argument refers to QObject* obj = getQObjectFromPyArg(objArg); if (!obj) return NULL; objArg = NULL; // no need to decref, it's borrowed // Our job is to return a Python list containing the children of this // widget (as PyCObjects). //qt4 FIXME QObjectList* children; //qt4 FIXME children = obj->queryList(ofclass, ofname, regexpmatch, recursive); PyObject* itemlist = 0; //qt4 FIXME itemlist = convert_QObjectList_to_PyListObject(children); //qt4 FIXME delete children; return itemlist; } // Perform a recursive (by default) search for the named child, possibly of a // select class. PyObject* scribus_getchild(PyObject* , PyObject* args, PyObject* kw) { PyObject* objArg = NULL; char* childname = NULL; char* ofclass = NULL; bool recursive = true; char* kwnames[] = {const_cast("object"), const_cast("childname"), const_cast("ofclass"), const_cast("recursive"), NULL}; if (!PyArg_ParseTupleAndKeywords(args, kw, "Oes|esi", kwnames, &objArg, "ascii", &childname, "ascii", &ofclass, &recursive)) return NULL; // Get the QObject* the object argument refers to QObject* obj = getQObjectFromPyArg(objArg); if (!obj) return NULL; objArg = NULL; // no need to decref, it's borrowed // Search for the child, possibly restricting the search to children // of a particular type, and possibly recursively searching through // grandchildren etc. QObject* child = obj->child(childname, ofclass, recursive); if (child == NULL) { PyErr_SetString(PyExc_KeyError, QObject::tr("Child not found").toLocal8Bit().constData()); return NULL; } return wrapQObject(child); } */ PyObject* scribus_getpropertynames(PyObject* /*self*/, PyObject* args, PyObject* kw) { PyObject* objArg = NULL; int includesuper = 1; char* kwargs[] = {const_cast("object"), const_cast("includesuper"), NULL}; if (!PyArg_ParseTupleAndKeywords(args, kw, "O|i", kwargs, &objArg, &includesuper)) return NULL; // Get the QObject* the object argument refers to QObject* obj = getQObjectFromPyArg(objArg); if (!obj) return NULL; objArg = NULL; // no need to decref, it's borrowed // Retrive the object's meta object so we can query it const QMetaObject* objmeta = obj->metaObject(); if (!objmeta) return NULL; // Return the list of properties QStringList propertyNames; int propertyOffset = includesuper ? 0 : objmeta->propertyOffset(); for(int i = propertyOffset; i < objmeta->propertyCount(); ++i) { QString propName = objmeta->property(i).name(); propertyNames << QString::fromLatin1(objmeta->property(i).name()); } return convert_QStringList_to_PyListObject(propertyNames); } PyObject* scribus_getproperty(PyObject* /*self*/, PyObject* args, PyObject* kw) { PyObject* objArg = NULL; char* propertyName = NULL; char* kwargs[] = {const_cast("object"), const_cast("property"), NULL}; if (!PyArg_ParseTupleAndKeywords(args, kw, "Oes", kwargs, &objArg, "ascii", &propertyName)) return NULL; // Get the QObject* the object argument refers to QObject* obj = getQObjectFromPyArg(objArg); if (!obj) return NULL; objArg = NULL; // no need to decref, it's borrowed // Get the QMetaProperty for the property, so we can check // if it's a set/enum and do name/value translation. const QMetaObject* objmeta = obj->metaObject(); int i = objmeta->indexOfProperty(propertyName); if (i == -1) { PyErr_SetString(PyExc_ValueError, QObject::tr("Property not found").toLocal8Bit().data()); return NULL; } QMetaProperty propmeta = objmeta->property(i); if (!propmeta.isValid()) { PyErr_SetString(PyExc_ValueError, QObject::tr("Invalid property").toLocal8Bit().data()); return NULL; } // Get the property value as a variant type QVariant prop = obj->property(propertyName); // Convert the property to an instance of the closest matching Python type. PyObject* resultobj = NULL; // NUMERIC TYPES if (prop.type() == QVariant::Int) resultobj = PyLong_FromLong(prop.toInt()); else if (prop.type() == QVariant::Double) resultobj = PyFloat_FromDouble(prop.toDouble()); // BOOLEAN else if (prop.type() == QVariant::Bool) resultobj = PyBool_FromLong(prop.toBool()); // STRING TYPES else if (prop.type() == QVariant::ByteArray) resultobj = PyString_FromString(prop.toByteArray().data()); else if (prop.type() == QVariant::String) resultobj = PyString_FromString(prop.toString().toUtf8().data()); // HIGHER ORDER TYPES else if (prop.type() == QVariant::Point) { // Return a QPoint as an (x,y) tuple. QPoint pt = prop.toPoint(); return Py_BuildValue("(ii)", pt.x(), pt.y()); } else if (prop.type() == QVariant::Rect) { // Return a QRect as an (x,y,width,height) tuple. // FIXME: We should really construct and return an object that // matches the API of QRect and has properties to keep // left/top/right/bottom and x/y/width/height in sync. QRect r = prop.toRect(); return Py_BuildValue("(iiii)", r.x(), r.y(), r.width(), r.height()); } else if (prop.type() == QVariant::StringList) { QStringList tmp = prop.toStringList(); return convert_QStringList_to_PyListObject(tmp); } // UNHANDLED TYPE else { PyErr_SetString(PyExc_TypeError, QObject::tr("Couldn't convert result type '%1'.").arg(prop.typeName()).toLocal8Bit().constData() ); return resultobj; } // Return the resulting Python object if (resultobj == NULL) { // An exception was set while assigning to resultobj assert(PyErr_Occurred()); return NULL; } else return resultobj; } PyObject* scribus_setproperty(PyObject* /*self*/, PyObject* args, PyObject* kw) { PyObject* objArg = NULL; char* propertyName = NULL; PyObject* objValue = NULL; char* kwargs[] = {const_cast("object"), const_cast("property"), const_cast("value"), NULL}; if (!PyArg_ParseTupleAndKeywords(args, kw, "OesO", kwargs, &objArg, "ascii", &propertyName, &objValue)) return NULL; // We're going to hang on to the value object for a while, so // claim a reference to it. Py_INCREF(objValue); // Get the QObject* the object argument refers to QObject* obj = getQObjectFromPyArg(objArg); if (!obj) return NULL; objArg = NULL; // no need to decref, it's borrowed const char* propertyTypeName = getpropertytype(obj, propertyName, true); if (propertyTypeName == NULL) return NULL; QString propertyType = QString::fromLatin1(propertyTypeName); // Did we know how to convert the value argument to the right type? bool matched = false; // Did the set call succceed? bool success = false; // Check the C++ type of the property, and try to convert the passed // PyObject to something sensible looking for it. // FIXME: handle enums/sets // NUMERIC TYPES // These are unfortuately a TOTAL PITA because of the multitude of // C and Python numeric types. TODO This needs to be broken out into a subroutine. if (propertyType == "bool") { matched = true; if (PyObject_IsTrue(objValue) == 0) success = obj->setProperty(propertyName, 0); else if (PyObject_IsTrue(objValue) == 1) success = obj->setProperty(propertyName, 1); else if (PyInt_Check(objValue)) success = obj->setProperty(propertyName, PyInt_AsLong(objValue) == 0); else if (PyLong_Check(objValue)) success = obj->setProperty(propertyName, PyLong_AsLong(objValue) == 0); else matched = false; } else if (propertyType == "int") { matched = true; if (PyObject_IsTrue(objValue) == 0) success = obj->setProperty(propertyName, 0); else if (PyObject_IsTrue(objValue) == 1) success = obj->setProperty(propertyName, 1); else if (PyInt_Check(objValue)) success = obj->setProperty(propertyName, (int)PyInt_AsLong(objValue)); else if (PyLong_Check(objValue)) success = obj->setProperty(propertyName, (int)PyLong_AsLong(objValue)); else matched = false; } else if (propertyType == "double") { matched = true; // FIXME: handle int, long and bool too if (PyFloat_Check(objValue)) success = obj->setProperty(propertyName, PyFloat_AsDouble(objValue)); else matched = false; } // STRING TYPES else if (propertyType == "QString") { matched = true; if (PyString_Check(objValue)) success = obj->setProperty(propertyName, QString::fromUtf8(PyString_AsString(objValue))); else if (PyUnicode_Check(objValue)) { // Get a pointer to the internal buffer of the Py_Unicode object, which is UCS2 formatted const unsigned short * ucs2Data = (const unsigned short *)PyUnicode_AS_UNICODE(objValue); // and make a new QString from it (the string is copied) success = obj->setProperty(propertyName, QString::fromUtf16(ucs2Data)); } else matched = false; } else if (propertyType == "QCString") { matched = true; if (PyString_Check(objValue)) { // FIXME: should raise an exception instead of mangling the string when // out of charset chars present. QString utfString = QString::fromUtf8(PyString_AsString(objValue)); success = obj->setProperty(propertyName, utfString.toAscii()); } else if (PyUnicode_Check(objValue)) { // Get a pointer to the internal buffer of the Py_Unicode object, which is UCS2 formatted const unsigned short * utf16Data = (const unsigned short *)PyUnicode_AS_UNICODE(objValue); // and make a new QString from it (the string is copied) success = obj->setProperty(propertyName, QString::fromUtf16(utf16Data).toAscii()); } else matched = false; } // HIGHER ORDER TYPES // ... which I can't be stuffed supporting yet. FIXME. else { Py_DECREF(objValue); PyErr_SetString(PyExc_TypeError, QObject::tr("Property type '%1' not supported").arg(propertyType).toLocal8Bit().constData()); return NULL; } // If `matched' is false, we recognised the C type but weren't able to // convert the passed Python value to anything suitable. if (!matched) { // Get a string representation of the object PyObject* objRepr = PyObject_Repr(objValue); Py_DECREF(objValue); // We're done with it now if (!objRepr) return NULL; // Extract the repr() string QString reprString = QString::fromUtf8(PyString_AsString(objRepr)); Py_DECREF(objRepr); // And return an error PyErr_SetString(PyExc_TypeError, QObject::tr("Couldn't convert '%1' to property type '%2'").arg(reprString).arg(propertyType).toLocal8Bit().constData()); return NULL; } // `success' is the return value of the setProperty() call if (!success) { Py_DECREF(objValue); PyErr_SetString(PyExc_ValueError, QObject::tr("Types matched, but setting property failed.").toLocal8Bit().constData()); return NULL; } Py_DECREF(objValue); // Py_INCREF(Py_None); // return Py_None; Py_RETURN_NONE; } /*! HACK: this removes "warning: 'blah' defined but not used" compiler warnings with header files structure untouched (docstrings are kept near declarations) PV */ void cmdgetsetpropdocwarnings() { QStringList s; s << scribus_propertyctype__doc__ << scribus_getpropertynames__doc__ << scribus_getproperty__doc__ << scribus_setproperty__doc__; //Qt4 << scribus_getchildren__doc__ << scribus_getchild__doc__; }