Back to index

scribus-ng  1.3.4.dfsg+svn20071115
cmdgetsetprop.cpp
Go to the documentation of this file.
00001 /*
00002 For general Scribus (>=1.3.2) copyright and licensing information please refer
00003 to the COPYING file provided with the program. Following this notice may exist
00004 a copyright and/or license notice that predates the release of Scribus 1.3.2
00005 for which a new license (GPL+exception) is in place.
00006 */
00007 #include "cmdgetsetprop.h"
00008 #include "cmdutil.h"
00009 
00010 #include <qmetaobject.h>
00011 #include <qstrlist.h>
00012 #include <qobjectlist.h>
00013 
00014 QObject* getQObjectFromPyArg(PyObject* arg)
00015 {
00016        if (PyString_Check(arg))
00017               // It's a string. Look for a pageItem by that name. Do NOT accept a
00018               // selection.
00019               return getPageItemByName(QString::fromUtf8(PyString_AsString(arg)));
00020        else if (PyCObject_Check(arg))
00021        {
00022               // It's a PyCObject, ie a wrapped pointer. Check it's not NULL
00023               // and return it.
00024               // FIXME: Try to check that its a pointer to a QObject instance
00025               QObject* tempObject = (QObject*)PyCObject_AsVoidPtr(arg);
00026               if (!tempObject)
00027               {
00028                      PyErr_SetString(PyExc_TypeError, "INTERNAL: Passed NULL PyCObject");
00029                      return NULL;
00030               }
00031               else
00032                      return tempObject;
00033        }
00034        else
00035        {
00036               // It's not a type we know what to do with
00037               PyErr_SetString(PyExc_TypeError,
00038                      QObject::tr("Argument must be page item name, or PyCObject instance"));
00039               return NULL;
00040        }
00041 }
00042 
00043 
00044 PyObject* wrapQObject(QObject* obj)
00045 {
00046        return PyCObject_FromVoidPtr((void*)obj, NULL);
00047 }
00048 
00049 
00050 const char* getpropertytype(QObject* obj, const char* propname, bool includesuper)
00051 {
00052        QMetaObject* objmeta = obj->metaObject();
00053        int i = objmeta->findProperty(propname, includesuper);
00054        if (i == -1)
00055               return NULL;
00056        const QMetaProperty* propmeta = objmeta->property(i, includesuper);
00057        if (propmeta == NULL)
00058               return NULL;
00059        const char* type = propmeta->type();
00060        assert(type);
00061        return type;
00062 }
00063 
00064 
00065 PyObject* scribus_propertyctype(PyObject* /*self*/, PyObject* args, PyObject* kw)
00066 {
00067        PyObject* objArg = NULL;
00068        char* propertyname = NULL;
00069        int includesuper = 1;
00070        char* kwargs[] = {const_cast<char*>("object"),
00071                                      const_cast<char*>("property"),
00072                                      const_cast<char*>("includesuper"),
00073                                      NULL};
00074        if (!PyArg_ParseTupleAndKeywords(args, kw, "Oes|i", kwargs,
00075                             &objArg, "ascii", &propertyname, &includesuper))
00076               return NULL;
00077 
00078        // Get the QObject* the object argument refers to
00079        QObject* obj = getQObjectFromPyArg(objArg);
00080        if (!obj)
00081               return NULL;
00082        objArg = NULL; // no need to decref, it's borrowed
00083 
00084        // Look up the property and retrive its type information
00085        const char* type = getpropertytype( (QObject*)obj, propertyname, includesuper);
00086        if (type == NULL)
00087        {
00088               PyErr_SetString(PyExc_KeyError, QObject::tr("Property not found"));
00089               return NULL;
00090        }
00091        return PyString_FromString(type);
00092 }
00093 
00094 
00095 PyObject* convert_QStrList_to_PyListObject(QStrList& origlist)
00096 {
00097        QStrListIterator it (origlist);
00098        char* item = NULL;
00099 
00100        PyObject* resultList = PyList_New(0);
00101        if (!resultList)
00102               return NULL;
00103 
00104        while ( (item = it.current()) != 0 ) {
00105               ++it;
00106               if (PyList_Append(resultList, PyString_FromString(item)) == -1)
00107                      return NULL;
00108        }
00109        return resultList;
00110 }
00111 
00112 
00113 PyObject* convert_QStringList_to_PyListObject(QStringList& origlist)
00114 {
00115        PyObject* resultList = PyList_New(0);
00116        if (!resultList)
00117               return NULL;
00118 
00119        for ( QStringList::Iterator it = origlist.begin(); it != origlist.end(); ++it )
00120               if (PyList_Append(resultList, PyString_FromString((*it).utf8().data())) == -1)
00121                      return NULL;
00122 
00123        return resultList;
00124 }
00125 
00126 
00127 PyObject* convert_QObjectList_to_PyListObject(QObjectList* origlist)
00128 {
00129        PyObject* resultList = PyList_New(0);
00130        if (!resultList)
00131               return NULL;
00132 
00133        PyObject* objPtr = NULL;
00134        // Loop over the objects in the list and add them to the python
00135        // list wrapped in PyCObjects .
00136        for ( origlist->first(); origlist->current(); origlist->next() )
00137        {
00138               // Wrap up the object pointer
00139               objPtr = wrapQObject(origlist->current());
00140               if (!objPtr)
00141               {
00142                      // Failed to wrap the object. An exception is already set.
00143                      Py_DECREF(resultList);
00144                      return NULL;
00145               }
00146               // and add it to the list
00147               if (PyList_Append(resultList, (PyObject*)objPtr) == -1)
00148                      return NULL;
00149        }
00150        return resultList;
00151 }
00152 
00153 
00154 
00155 PyObject* scribus_getchildren(PyObject* /*self*/, PyObject* args, PyObject* kw)
00156 {
00157        PyObject* objArg = NULL;
00158        char* ofclass = NULL;
00159        char* ofname = NULL;
00160        int recursive = 0;
00161        int regexpmatch = 0;
00162        char* kwnames[] = {const_cast<char*>("object"),
00163                                       const_cast<char*>("ofclass"),
00164                                       const_cast<char*>("ofname"),
00165                                       const_cast<char*>("regexpmatch"),
00166                                       const_cast<char*>("recursive"),
00167                                       NULL};
00168        if (!PyArg_ParseTupleAndKeywords(args, kw, "O|esesii", kwnames,
00169                             &objArg, "ascii", &ofclass, "ascii", &ofname, &regexpmatch, &recursive))
00170               return NULL;
00171 
00172        // Get the QObject* the object argument refers to
00173        QObject* obj = getQObjectFromPyArg(objArg);
00174        if (!obj)
00175               return NULL;
00176        objArg = NULL; // no need to decref, it's borrowed
00177 
00178        // Our job is to return a Python list containing the children of this
00179        // widget (as PyCObjects).
00180        QObjectList* children = obj->queryList(ofclass, ofname, regexpmatch, recursive);
00181        PyObject* itemlist = convert_QObjectList_to_PyListObject(children);
00182        delete children;
00183        return itemlist;
00184 }
00185 
00186 
00187 /* Perform a recursive (by default) search for the named child, possibly of a
00188  * select class. */
00189 PyObject* scribus_getchild(PyObject* /*self*/, PyObject* args, PyObject* kw)
00190 {
00191        PyObject* objArg = NULL;
00192        char* childname = NULL;
00193        char* ofclass = NULL;
00194        bool recursive = true;
00195        char* kwnames[] = {const_cast<char*>("object"),
00196                                       const_cast<char*>("childname"),
00197                                       const_cast<char*>("ofclass"),
00198                                       const_cast<char*>("recursive"),
00199                                       NULL};
00200        if (!PyArg_ParseTupleAndKeywords(args, kw, "Oes|esi", kwnames,
00201                             &objArg, "ascii", &childname, "ascii", &ofclass, &recursive))
00202               return NULL;
00203 
00204        // Get the QObject* the object argument refers to
00205        QObject* obj = getQObjectFromPyArg(objArg);
00206        if (!obj)
00207               return NULL;
00208        objArg = NULL; // no need to decref, it's borrowed
00209 
00210        // Search for the child, possibly restricting the search to children
00211        // of a particular type, and possibly recursively searching through
00212        // grandchildren etc.
00213        QObject* child = obj->child(childname, ofclass, recursive);
00214        if (child == NULL)
00215        {
00216               PyErr_SetString(PyExc_KeyError, QObject::tr("Child not found"));
00217               return NULL;
00218        }
00219 
00220        return wrapQObject(child);
00221 }
00222 
00223 
00224 PyObject* scribus_getpropertynames(PyObject* /*self*/, PyObject* args, PyObject* kw)
00225 {
00226        PyObject* objArg = NULL;
00227        int includesuper = 1;
00228        char* kwargs[] = {const_cast<char*>("object"),
00229                                      const_cast<char*>("includesuper"),
00230                                      NULL};
00231        if (!PyArg_ParseTupleAndKeywords(args, kw, "O|i", kwargs,
00232                             &objArg, &includesuper))
00233               return NULL;
00234 
00235        // Get the QObject* the object argument refers to
00236        QObject* obj = getQObjectFromPyArg(objArg);
00237        if (!obj)
00238               return NULL;
00239        objArg = NULL; // no need to decref, it's borrowed
00240 
00241        // Retrive the object's meta object so we can query it
00242        QMetaObject* objmeta = obj->metaObject();
00243        assert(objmeta);
00244 
00245        // Return the list of properties
00246        QStrList propertyNames = objmeta->propertyNames(includesuper);
00247        return convert_QStrList_to_PyListObject(propertyNames);
00248 }
00249 
00250 
00251 PyObject* scribus_getproperty(PyObject* /*self*/, PyObject* args, PyObject* kw)
00252 {
00253        PyObject* objArg = NULL;
00254        char* propertyName = NULL;
00255        char* kwargs[] = {const_cast<char*>("object"),
00256                                      const_cast<char*>("property"),
00257                                      NULL};
00258        if (!PyArg_ParseTupleAndKeywords(args, kw, "Oes", kwargs,
00259                             &objArg, "ascii", &propertyName))
00260               return NULL;
00261 
00262        // Get the QObject* the object argument refers to
00263        QObject* obj = getQObjectFromPyArg(objArg);
00264        if (!obj)
00265               return NULL;
00266        objArg = NULL; // no need to decref, it's borrowed
00267 
00268        // Get the QMetaProperty for the property, so we can check
00269        // if it's a set/enum and do name/value translation.
00270        QMetaObject* objmeta = obj->metaObject();
00271        int i = objmeta->findProperty(propertyName, true);
00272        if (i == -1)
00273        {
00274               PyErr_SetString(PyExc_ValueError,
00275                             QObject::tr("Property not found"));
00276               return NULL;
00277        }
00278        const QMetaProperty* propmeta = objmeta->property(i, true);
00279        assert(propmeta);
00280 
00281        // Get the property value as a variant type
00282        QVariant prop = obj->property(propertyName);
00283 
00284        // Convert the property to an instance of the closest matching Python type.
00285        PyObject* resultobj = NULL;
00286        // NUMERIC TYPES
00287        if (prop.type() == QVariant::Int)
00288               resultobj = PyLong_FromLong(prop.asInt());
00289        else if (prop.type() == QVariant::Double)
00290               resultobj = PyFloat_FromDouble(prop.asDouble());
00291        // BOOLEAN
00292        else if (prop.type() == QVariant::Bool)
00293               resultobj = PyBool_FromLong(prop.asBool());
00294        // STRING TYPES
00295        else if (prop.type() == QVariant::CString)
00296               resultobj = PyString_FromString(prop.asCString().data());
00297        else if (prop.type() == QVariant::String)
00298               resultobj = PyString_FromString(prop.asString().utf8().data());
00299        // HIGHER ORDER TYPES
00300        else if (prop.type() == QVariant::Point)
00301        {
00302               // Return a QPoint as an (x,y) tuple.
00303               QPoint pt = prop.asPoint();
00304               return Py_BuildValue("(ii)", pt.x(), pt.y());
00305        }
00306        else if (prop.type() == QVariant::Rect)
00307        {
00308               // Return a QRect as an (x,y,width,height) tuple.
00309               // FIXME: We should really construct and return an object that
00310               // matches the API of QRect and has properties to keep
00311               // left/top/right/bottom and x/y/width/height in sync.
00312               QRect r = prop.asRect();
00313               return Py_BuildValue("(iiii)", r.x(), r.y(), r.width(), r.height());
00314        }
00315        else if (prop.type() == QVariant::StringList)
00316               return convert_QStringList_to_PyListObject(prop.asStringList());
00317        // UNHANDLED TYPE
00318        else
00319        {
00320               PyErr_SetString(PyExc_TypeError,
00321                             QObject::tr("Couldn't convert result type '%1'.").arg(prop.typeName()) );
00322               return NULL;
00323        }
00324 
00325        // Return the resulting Python object
00326        if (resultobj == NULL)
00327        {
00328               // An exception was set while assigning to resultobj
00329               assert(PyErr_Occurred());
00330               return NULL;
00331        }
00332        else
00333               return resultobj;
00334 }
00335 
00336 
00337 
00338 PyObject* scribus_setproperty(PyObject* /*self*/, PyObject* args, PyObject* kw)
00339 {
00340        PyObject* objArg = NULL;
00341        char* propertyName = NULL;
00342        PyObject* objValue = NULL;
00343        char* kwargs[] = {const_cast<char*>("object"),
00344                                      const_cast<char*>("property"),
00345                                      const_cast<char*>("value"),
00346                                      NULL};
00347        if (!PyArg_ParseTupleAndKeywords(args, kw, "OesO", kwargs,
00348                             &objArg, "ascii", &propertyName, &objValue))
00349               return NULL;
00350 
00351        // We're going to hang on to the value object for a while, so
00352        // claim a reference to it.
00353        Py_INCREF(objValue);
00354 
00355        // Get the QObject* the object argument refers to
00356        QObject* obj = getQObjectFromPyArg(objArg);
00357        if (!obj)
00358               return NULL;
00359        objArg = NULL; // no need to decref, it's borrowed
00360 
00361        const QString propertyType = getpropertytype(obj, propertyName, true);
00362 
00363        // Did we know how to convert the value argument to the right type?
00364        bool matched = false;
00365        // Did the set call succceed?
00366        bool success = false;
00367 
00368        // Check the C++ type of the property, and try to convert the passed
00369        // PyObject to something sensible looking for it.
00370        // FIXME: handle enums/sets
00371        // NUMERIC TYPES
00372        // These are unfortuately a TOTAL PITA because of the multitude of
00373        // C and Python numeric types. TODO This needs to be broken out into a subroutine.
00374        if (propertyType == "bool")
00375        {
00376               matched = true;
00377               if (objValue == Py_False)
00378                      success = obj->setProperty(propertyName, 0);
00379               else if (objValue == Py_True)
00380                      success = obj->setProperty(propertyName, 1);
00381               else if (PyInt_Check(objValue))
00382                      success = obj->setProperty(propertyName, PyInt_AsLong(objValue) == 0);
00383               else if (PyLong_Check(objValue))
00384                      success = obj->setProperty(propertyName, PyLong_AsLong(objValue) == 0);
00385               else
00386                      matched = false;
00387        }
00388        else if (propertyType == "int")
00389        {
00390               matched = true;
00391               if (objValue == Py_False)
00392                      success = obj->setProperty(propertyName, 0);
00393               else if (objValue == Py_True)
00394                      success = obj->setProperty(propertyName, 1);
00395               else if (PyInt_Check(objValue))
00396                      success = obj->setProperty(propertyName, (int)PyInt_AsLong(objValue));
00397               else if (PyLong_Check(objValue))
00398                      success = obj->setProperty(propertyName, (int)PyLong_AsLong(objValue));
00399               else
00400                      matched = false;
00401        }
00402        else if (propertyType == "double")
00403        {
00404               matched = true;
00405               // FIXME: handle int, long  and bool too
00406               if (PyFloat_Check(objValue))
00407                      success = obj->setProperty(propertyName, PyFloat_AsDouble(objValue));
00408               else
00409                      matched = false;
00410        }
00411        // STRING TYPES
00412        else if (propertyType == "QString")
00413        {
00414               matched = true;
00415               if (PyString_Check(objValue))
00416                      success = obj->setProperty(propertyName, QString::fromUtf8(PyString_AsString(objValue)));
00417               else if (PyUnicode_Check(objValue))
00418               {
00419                      // Get a pointer to the internal buffer of the Py_Unicode object, which is UCS2 formatted
00420                      const unsigned short * ucs2Data = (const unsigned short *)PyUnicode_AS_UNICODE(objValue);
00421                      // and make a new QString from it (the string is copied)
00422                      success = obj->setProperty(propertyName, QString::fromUcs2(ucs2Data));
00423               }
00424               else
00425                      matched = false;
00426        }
00427        else if (propertyType == "QCString")
00428        {
00429               matched = true;
00430               if (PyString_Check(objValue))
00431               {
00432                      // FIXME: should raise an exception instead of mangling the string when
00433                      // out of charset chars present.
00434                      QString utfString = QString::fromUtf8(PyString_AsString(objValue));
00435                      success = obj->setProperty(propertyName, utfString.ascii());
00436               }
00437               else if (PyUnicode_Check(objValue))
00438               {
00439                      // Get a pointer to the internal buffer of the Py_Unicode object, which is UCS2 formatted
00440                      const unsigned short * ucs2Data = (const unsigned short *)PyUnicode_AS_UNICODE(objValue);
00441                      // and make a new QString from it (the string is copied)
00442                      success = obj->setProperty(propertyName, QString::fromUcs2(ucs2Data).ascii());
00443               }
00444               else
00445                      matched = false;
00446        }
00447        // HIGHER ORDER TYPES
00448        // ... which I can't be stuffed supporting yet. FIXME.
00449        else
00450        {
00451               Py_DECREF(objValue);
00452               PyErr_SetString(PyExc_TypeError,
00453                             QObject::tr("Property type '%1' not supported").arg(propertyType));
00454               return NULL;
00455        }
00456 
00457        // If `matched' is false, we recognised the C type but weren't able to
00458        // convert the passed Python value to anything suitable.
00459        if (!matched)
00460        {
00461               // Get a string representation of the object
00462               PyObject* objRepr = PyObject_Repr(objValue);
00463               Py_DECREF(objValue); // We're done with it now
00464               if (!objRepr)
00465                      return NULL;
00466               // Extract the repr() string
00467               QString reprString = QString::fromUtf8(PyString_AsString(objRepr));
00468               Py_DECREF(objRepr);
00469 
00470               // And return an error
00471               PyErr_SetString(PyExc_TypeError,
00472                             QObject::tr("Couldn't convert '%1' to property type '%2'")
00473                             .arg(reprString).arg(propertyType));
00474               return NULL;
00475        }
00476 
00477        // `success' is the return value of the setProperty() call
00478        if (!success)
00479        {
00480               Py_DECREF(objValue);
00481               PyErr_SetString(PyExc_ValueError,
00482                             QObject::tr("Types matched, but setting property failed."));
00483               return NULL;
00484        }
00485 
00486        Py_DECREF(objValue);
00487 //     Py_INCREF(Py_None);
00488 //     return Py_None;
00489        Py_RETURN_NONE;
00490 }
00491 
00495 void cmdgetsetpropdocwarnings()
00496 {
00497     QStringList s;
00498     s << scribus_propertyctype__doc__ << scribus_getpropertynames__doc__ << scribus_getproperty__doc__ << scribus_setproperty__doc__ << scribus_getchildren__doc__ << scribus_getchild__doc__;
00499 }