1 // This is the implementation of the various Chimera helpers.
2 //
3 // Copyright (c) 2021 Riverbank Computing Limited <info@riverbankcomputing.com>
4 //
5 // This file is part of PyQt5.
6 //
7 // This file may be used under the terms of the GNU General Public License
8 // version 3.0 as published by the Free Software Foundation and appearing in
9 // the file LICENSE included in the packaging of this file.  Please review the
10 // following information to ensure the GNU General Public License version 3.0
11 // requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 //
13 // If you do not wish to use this file under the terms of the GPL version 3.0
14 // then you may purchase a commercial license.  For more information contact
15 // info@riverbankcomputing.com.
16 //
17 // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
18 // WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 
20 
21 #include <Python.h>
22 
23 #include <QList>
24 #include <QMetaType>
25 #include <QObject>
26 
27 #include "qpyquick_chimera_helpers.h"
28 
29 #include "sipAPIQtQuick.h"
30 
31 
32 // Forward declarations.
33 static int QList_QObject_metatype();
34 static bool to_QList_QObject(PyObject *obj, QList<QObject *>&cpp);
35 
36 
37 // Convert a QVariant to a Python object.
qpyquick_from_qvariant_convertor(const QVariant * varp,PyObject ** objp)38 bool qpyquick_from_qvariant_convertor(const QVariant *varp, PyObject **objp)
39 {
40     // Check we handle the meta-type.
41     if (varp->userType() != QList_QObject_metatype())
42         return false;
43 
44     const QList<QObject *> *cpp = reinterpret_cast<const QList<QObject *> *>(varp->data());
45 
46     PyObject *obj = PyList_New(cpp->count());
47 
48     if (obj)
49     {
50         for (int i = 0; i < cpp->count(); ++i)
51         {
52             PyObject *itm = sipConvertFromType(cpp->at(i), sipType_QObject, 0);
53 
54             if (!itm)
55             {
56                 Py_DECREF(obj);
57                 obj = 0;
58                 break;
59             }
60 
61             PyList_SetItem(obj, i, itm);
62         }
63     }
64 
65     *objp = obj;
66 
67     return true;
68 }
69 
70 
71 // Convert a Python object to a QVariant.
qpyquick_to_qvariant_convertor(PyObject * obj,QVariant & var,bool * okp)72 bool qpyquick_to_qvariant_convertor(PyObject *obj, QVariant &var, bool *okp)
73 {
74     int metatype = QList_QObject_metatype();
75 
76     // Check the meta-type has been defined.
77     if (metatype == 0)
78         return false;
79 
80     QList<QObject *> qlo;
81 
82     // A failure to convert isn't an error, just the wrong type of Python
83     // object.
84     if (!to_QList_QObject(obj, qlo))
85         return false;
86 
87     var = QVariant(metatype, &qlo);
88     *okp = true;
89 
90     return true;
91 }
92 
93 
94 // Convert a Python object to QVariant data.
qpyquick_to_qvariant_data_convertor(PyObject * obj,void * data,int metatype,bool * okp)95 bool qpyquick_to_qvariant_data_convertor(PyObject *obj, void *data,
96         int metatype, bool *okp)
97 {
98     // Check we handle the meta-type.
99     if (metatype != QList_QObject_metatype())
100         return false;
101 
102     QList<QObject *> qlo;
103 
104     // A failure to convert isn't an error, just the wrong type of Python
105     // object.
106     if (!to_QList_QObject(obj, qlo))
107         return false;
108 
109     *reinterpret_cast<QList<QObject *> *>(data) = qlo;
110     *okp = true;
111 
112     return true;
113 }
114 
115 
116 // Get the metatype for QList<QObject *> or 0 if it hasn't been registered.
QList_QObject_metatype()117 static int QList_QObject_metatype()
118 {
119     static int lo_metatype = 0;
120 
121     // We look each time until we find it rather than rely on any particular
122     // behaviour from QtQuick.
123     if (lo_metatype == 0)
124         lo_metatype = QMetaType::type("QList<QObject*>");
125 
126     return lo_metatype;
127 }
128 
129 
130 // Convert a Python list object to a QList<QObject*> and return true if the
131 // conversion was successful.  An empty list is never converted and left to the
132 // QtCore module to handle.
to_QList_QObject(PyObject * obj,QList<QObject * > & cpp)133 static bool to_QList_QObject(PyObject *obj, QList<QObject *>&cpp)
134 {
135     if (!PyList_CheckExact(obj) || PyList_Size(obj) == 0)
136         return false;
137 
138     for (Py_ssize_t i = 0; i < PyList_Size(obj); ++i)
139     {
140         PyObject *val_obj = PyList_GetItem(obj, i);
141 
142         if (!val_obj)
143             return false;
144 
145         int iserr = 0;
146 
147         QObject *val = reinterpret_cast<QObject *>(sipForceConvertToType(
148                 val_obj, sipType_QObject, 0, SIP_NO_CONVERTORS, 0, &iserr));
149 
150         if (iserr)
151             return false;
152 
153         cpp.append(val);
154     }
155 
156     return true;
157 }
158