1#---------------------------------------------------------------------------
2# Name:        etg/dataobj.py
3# Author:      Kevin Ollivier
4#
5# Created:     10-Sept-2011
6# Copyright:   (c) 2011 by Kevin Ollivier
7# Copyright:   (c) 2011-2018 by Total Control Software
8# License:     wxWindows License
9#---------------------------------------------------------------------------
10
11import etgtools
12import etgtools.tweaker_tools as tools
13
14PACKAGE   = "wx"
15MODULE    = "_core"
16NAME      = "dataobj"   # Base name of the file to generate to for this script
17DOCSTRING = ""
18
19# The classes and/or the basename of the Doxygen XML files to be processed by
20# this script.
21ITEMS  = [ 'wxDataFormat',
22           'wxDataObject',
23           'wxDataObjectSimple',
24           'wxCustomDataObject',
25           'wxDataObjectComposite',
26           'wxBitmapDataObject',
27           'wxTextDataObject',
28           'wxURLDataObject',
29           'wxFileDataObject',
30           'wxHTMLDataObject',
31           ]
32
33
34#---------------------------------------------------------------------------
35
36def addGetAllFormats(klass, pureVirtual=False):
37    # Replace the GetAllFormats method with an implementation that returns
38    # the formats as a Python list
39    m = klass.findItem('GetAllFormats')
40    if m:
41        m.ignore()
42
43    pyArgs = '(dir=Get)' if klass.name == 'wxDataObject' else '(dir=DataObject.Get)'
44
45    klass.addCppMethod('PyObject*', 'GetAllFormats', '(wxDataObject::Direction dir=wxDataObject::Get)',
46        cppSignature='void (wxDataFormat* formats, Direction dir)',
47        pyArgsString=pyArgs,
48        isVirtual=True,
49        isPureVirtual=pureVirtual,
50        isConst=True,
51        doc="""\
52            Returns a list of wx.DataFormat objects which this data object
53            supports transferring in the given direction.""",
54        body="""\
55            size_t count = self->GetFormatCount(dir);
56            wxDataFormat* formats = new wxDataFormat[count];
57            self->GetAllFormats(formats, dir);
58            wxPyThreadBlocker blocker;
59            PyObject* list = PyList_New(count);
60            for (size_t i=0; i<count; i++) {
61                wxDataFormat* format = new wxDataFormat(formats[i]);
62                PyObject* obj = wxPyConstructObject((void*)format, wxT("wxDataFormat"), true);
63                PyList_SET_ITEM(list, i, obj); // PyList_SET_ITEM steals a reference
64            }
65            delete [] formats;
66            return list;
67            """,
68
69        # This code will be used in the function that calls a Python implementation
70        # of this method. So we need to translate between the real C++ siganture
71        # and the Python signature.
72        virtualCatcherCode="""\
73            // VirtualCatcherCode for wx.DataObject.GetAllFormats
74            PyObject *resObj = sipCallMethod(0,sipMethod,"F",dir,sipType_wxDataObject_Direction);
75            if (resObj) {
76                if (!PySequence_Check(resObj)) {
77                    PyErr_SetString(PyExc_TypeError, "Should return a list of wx.DataFormat objects.");
78                    // or this?  sipBadCatcherResult(sipMethod);
79                }
80                else {
81                    Py_ssize_t len = PySequence_Length(resObj);
82                    Py_ssize_t idx;
83                    for (idx=0; idx<len; idx+=1) {
84                        PyObject* item = PySequence_GetItem(resObj, idx);
85                        if (! sipCanConvertToType(item, sipType_wxDataFormat, SIP_NOT_NONE)) {
86                            PyErr_SetString(PyExc_TypeError, "List of wx.DataFormat objects expected.");
87                            // or this?  sipBadCatcherResult(sipMethod);
88                            Py_DECREF(item);
89                            break;
90                        }
91                        wxDataFormat* fmt;
92                        int err = 0;
93                        fmt = (wxDataFormat*)sipConvertToType(
94                                                item, sipType_wxDataFormat, NULL,
95                                                SIP_NOT_NONE|SIP_NO_CONVERTORS, NULL, &err);
96                        formats[idx] = *fmt;
97                        Py_DECREF(item);
98                    }
99                }
100            }
101            if (PyErr_Occurred())
102                PyErr_Print();
103            Py_XDECREF(resObj);
104            """ if pureVirtual else "")
105
106
107def addBaseVirtuals(c):
108    # The overloading of SetData in wxDataObjectSimple and derived classes
109    # really confuses things, so in case SetData is overridden in a Python
110    # class then assume they want the one in DataObjectSimple without the
111    # format arg, and make the base class version always call the C++
112    # implementation instead of trying to call the Python method.
113    c.addCppMethod('bool', 'SetData', '(const wxDataFormat& format, wxPyBuffer* buf)',
114        cppSignature='bool (const wxDataFormat& format, size_t len, const void* buf)',
115        isVirtual=True,
116        doc="",
117        body="return self->SetData(*format, buf->m_len, buf->m_ptr);",
118        virtualCatcherCode="""\
119            {0}* self = static_cast<{0}*>(wxPyGetCppPtr(sipPySelf));
120            sipRes = self->{0}::SetData(format, len, buf);
121            """.format(c.name))
122
123    # We need to let SIP know that the pure virtuals in the base class have
124    # implementations in C even though they will not be used much (if at
125    # all.)
126    if not c.findItem('GetFormatCount'):
127        c.addItem(
128            etgtools.WigCode(code="virtual size_t GetFormatCount(Direction dir = Get) const;"))
129
130    c.addItem(etgtools.WigCode(code="""\
131        virtual wxDataFormat GetPreferredFormat(Direction dir = Get) const;
132        private:
133        virtual size_t GetDataSize(const wxDataFormat& format) const;
134        virtual bool   GetDataHere(const wxDataFormat& format, void* buf) const;
135        public:
136        """))
137
138
139def addSimpleVirtuals(c):
140    # Same thing for classes derived from wxDataObjectSimple.
141    c.addItem(etgtools.WigCode(code="""\
142        virtual bool GetDataHere(void* buf) const;
143        virtual size_t GetDataSize() const;
144        virtual bool SetData(size_t len, const void *buf);
145        """))
146
147#---------------------------------------------------------------------------
148
149def run():
150    # Parse the XML file(s) building a collection of Extractor objects
151    module = etgtools.ModuleDef(PACKAGE, MODULE, NAME, DOCSTRING)
152    etgtools.parseDoxyXML(module, ITEMS)
153
154    #-----------------------------------------------------------------
155    # Tweak the parsed meta objects in the module object as needed for
156    # customizing the generated code and docstrings.
157
158    c = module.find('wxDataFormat')
159    assert isinstance(c, etgtools.ClassDef)
160    c.find('GetType').setCppCode("return static_cast<wxDataFormatId>(self->GetType());")
161
162    item = module.find('wxFormatInvalid')
163    module.items.remove(item)
164    module.insertItemAfter(c, item)
165
166    module.addPyCode("""\
167        def CustomDataFormat(format):
168            return wx.DataFormat(format)
169        CustomDataFormat = wx.deprecated(CustomDataFormat, "Use wx.DataFormat instead.")
170        """)
171
172    #------------------------------------------------------------
173    c = module.find('wxDataObject')
174    c.addPrivateCopyCtor()
175
176    addGetAllFormats(c, True)
177
178    # For initial testing only.  TODO: Remove later
179    c.addPublic()
180    c.addCppMethod('void', '_testGetAllFormats', '()',
181        body="""\
182            size_t count = self->GetFormatCount();
183            wxDataFormat* fmts = new wxDataFormat[count];
184            self->GetAllFormats(fmts);
185            """)
186
187    # Replace the GetDataHere method with a version that uses a smarter
188    # Python buffer object instead of a stupid void pointer.
189    c.find('GetDataHere').ignore()
190    c.addCppMethod('bool', 'GetDataHere', '(const wxDataFormat& format, wxPyBuffer* buf)',
191        cppSignature='bool (const wxDataFormat& format, void* buf)',
192        isVirtual=True, isPureVirtual=True,
193        isConst=True,
194        doc="Copies this data object's data in the requested format to the buffer provided.",
195        body="""\
196            if (!buf->checkSize(self->GetDataSize(*format)))
197                return false;
198            return self->GetDataHere(*format, buf->m_ptr);
199            """,
200
201        # This code will be used in the function that calls a Python implementation
202        # of this method.
203        virtualCatcherCode="""\
204            // Call self.GetDataSize() to find out how big the buffer should be
205            PyObject* self = NULL;
206            PyObject* fmtObj = NULL;
207            PyObject* sizeObj = NULL;
208            PyObject* buffer = NULL;
209            PyObject* resObj = NULL;
210            Py_ssize_t size = 0;
211
212            self = wxPyMethod_Self(sipMethod); // this shouldn't fail, and the reference is borrowed
213
214            fmtObj = wxPyConstructObject((void*)&format, "wxDataFormat", false);
215            if (!fmtObj) goto error;
216            sizeObj = PyObject_CallMethod(self, "GetDataSize", "(O)", fmtObj, NULL);
217            if (!sizeObj) goto error;
218            size = wxPyInt_AsSsize_t(sizeObj);
219
220            // Make a buffer that big using the pointer passed to us, and then
221            // call the Python method.
222            buffer = wxPyMakeBuffer(buf, size);
223            resObj = sipCallMethod(0, sipMethod, "SS", fmtObj, buffer);
224
225            if (!resObj || sipParseResult(0,sipMethod,resObj,"b",&sipRes) < 0)
226                PyErr_Print();
227
228            error:
229            Py_XDECREF(resObj);
230            Py_XDECREF(buffer);
231            Py_XDECREF(fmtObj);
232            Py_XDECREF(sizeObj);
233            """)
234
235    # Replace the SetData method with an implementation that uses Python
236    # buffer objects.
237    c.find('SetData').ignore()
238    c.addCppMethod('bool', 'SetData', '(const wxDataFormat& format, wxPyBuffer* buf)',
239        cppSignature='bool (const wxDataFormat& format, size_t len, const void* buf)',
240        isVirtual=True,
241        doc="Copies data from the provided buffer to this data object for the specified format.",
242        body="return self->SetData(*format, buf->m_len, buf->m_ptr);",
243
244        # This code will be used in the function that calls a Python implementation
245        # of this method.
246        virtualCatcherCode="""\
247            PyObject* buffer = wxPyMakeBuffer((void*)buf, len);
248            PyObject *resObj = sipCallMethod(0,sipMethod,"NS",
249                                   new wxDataFormat(format),sipType_wxDataFormat,NULL,
250                                   buffer);
251            if (!resObj || sipParseResult(0,sipMethod,resObj,"b",&sipRes) < 0)
252                PyErr_Print();
253            Py_XDECREF(resObj);
254            Py_XDECREF(buffer);
255            """)
256
257
258    #------------------------------------------------------------
259    c = module.find('wxDataObjectSimple')
260
261    c.addCppCtor_sip('(const wxString& formatName)',
262        body='sipCpp = new sipwxDataObjectSimple(wxDataFormat(*formatName));')
263
264    # As in wxDataObject above replace GetDataHere and SetData with methods
265    # that use buffer objects instead of void*, but this time we do not pass
266    # a DataFormat object with it.
267    c.find('GetDataHere').ignore()
268    c.addCppMethod('bool', 'GetDataHere', '(wxPyBuffer* buf)',
269        cppSignature='bool (void* buf)',
270        isVirtual=True,
271        isConst=True,
272        doc="Copies this data object's data bytes to the given buffer",
273        body="""\
274            if (!buf->checkSize(self->GetDataSize()))
275                return false;
276            return self->GetDataHere(buf->m_ptr);
277            """,
278        virtualCatcherCode="""\
279            // Call self.GetDataSize() to find out how big the buffer should be
280            PyObject* self = NULL;
281            PyObject* sizeObj = NULL;
282            PyObject* buffer = NULL;
283            PyObject* resObj = NULL;
284            Py_ssize_t size = 0;
285
286            self = wxPyMethod_Self(sipMethod);
287
288            sizeObj = PyObject_CallMethod(self, "GetDataSize", "", NULL);
289            if (!sizeObj) goto error;
290            size = wxPyInt_AsSsize_t(sizeObj);
291
292            // Make a buffer that big using the pointer passed to us, and then
293            // call the Python method.
294            buffer = wxPyMakeBuffer(buf, size);
295            resObj = sipCallMethod(0, sipMethod, "S", buffer);
296
297            if (!resObj || sipParseResult(0,sipMethod,resObj,"b",&sipRes) < 0)
298                PyErr_Print();
299
300            error:
301            Py_XDECREF(resObj);
302            Py_XDECREF(buffer);
303            Py_XDECREF(sizeObj);
304            """)
305
306    c.find('SetData').ignore()
307    c.addCppMethod('bool', 'SetData', '(wxPyBuffer* buf)',
308        cppSignature='bool (size_t len, const void* buf)',
309        isVirtual=True,
310        doc="Copies data from the provided buffer to this data object.",
311        body="return self->SetData(buf->m_len, buf->m_ptr);",
312        virtualCatcherCode="""\
313            PyObject* buffer = wxPyMakeBuffer((void*)buf, len);
314            PyObject *resObj = sipCallMethod(0,sipMethod,"S",buffer);
315            if (!resObj || sipParseResult(0,sipMethod,resObj,"b",&sipRes) < 0)
316                PyErr_Print();
317            Py_XDECREF(resObj);
318            Py_XDECREF(buffer);
319            """)
320
321    addGetAllFormats(c)
322    addBaseVirtuals(c)
323
324
325    #------------------------------------------------------------
326    c = module.find('wxCustomDataObject')
327    tools.removeVirtuals(c)
328
329    c.addCppCtor_sip('(const wxString& formatName)',
330        body='sipCpp = new sipwxCustomDataObject(wxDataFormat(*formatName));')
331
332    # remove the methods having to do with allocating or owning the data buffer
333    c.find('Alloc').ignore()
334    c.find('Free').ignore()
335    c.find('TakeData').ignore()
336
337    c.find('GetData').ignore()
338    c.addCppMethod('PyObject*', 'GetData', '()', isConst=True,
339        doc="Returns a reference to the data buffer.",
340        body="return wxPyMakeBuffer(self->GetData(), self->GetSize());")
341
342    c.find('SetData').ignore()
343    c.addCppMethod('bool', 'SetData', '(wxPyBuffer* buf)',
344        cppSignature='bool (size_t len, const void* buf)',
345        isVirtual=True,
346        doc="Copies data from the provided buffer to this data object's buffer",
347        body="return self->SetData(buf->m_len, buf->m_ptr);")
348
349    addGetAllFormats(c)
350    addBaseVirtuals(c)
351    addSimpleVirtuals(c)
352
353    #------------------------------------------------------------
354    c = module.find('wxDataObjectComposite')
355
356    c.find('Add.dataObject').transfer = True
357
358    addGetAllFormats(c)
359    addBaseVirtuals(c)
360
361
362
363    #------------------------------------------------------------
364    c = module.find('wxTextDataObject')
365    addGetAllFormats(c)
366    addBaseVirtuals(c)
367    addSimpleVirtuals(c)
368
369
370    #------------------------------------------------------------
371    c = module.find('wxURLDataObject')
372
373    # wxURLDataObject derives from wxDataObjectComposite on some platforms,
374    # and wxTextDataObject on others, so we need to take a least common
375    # denominator approach here to be able to work on all platforms.
376    c.bases = ['wxDataObject']
377
378    addGetAllFormats(c)
379    addBaseVirtuals(c)
380
381    # It also causes mismatches regarding what virtuals are available
382    # in the base classes. For now just ignore them for this class. If
383    # they really are needed then something more creative will need to
384    # be done.
385    #addSimpleVirtuals(c)
386
387
388    #------------------------------------------------------------
389    c = module.find('wxBitmapDataObject')
390    addGetAllFormats(c)
391    addBaseVirtuals(c)
392    addSimpleVirtuals(c)
393
394    #------------------------------------------------------------
395    c = module.find('wxFileDataObject')
396    addGetAllFormats(c)
397    addBaseVirtuals(c)
398    addSimpleVirtuals(c)
399
400    #------------------------------------------------------------
401    c = module.find('wxHTMLDataObject')
402    addGetAllFormats(c)
403    addBaseVirtuals(c)
404    addSimpleVirtuals(c)
405
406
407
408    #------------------------------------------------------------
409    module.addPyCode("PyDataObjectSimple = wx.deprecated(DataObjectSimple), 'Use DataObjectSimple instead.'")
410    module.addPyCode("PyTextDataObject = wx.deprecated(TextDataObject, 'Use TextDataObject instead.')")
411    module.addPyCode("PyBitmapDataObject = wx.deprecated(BitmapDataObject, 'Use TextDataObject instead.')")
412
413    #-----------------------------------------------------------------
414    tools.doCommonTweaks(module)
415    tools.runGenerators(module)
416
417
418#---------------------------------------------------------------------------
419if __name__ == '__main__':
420    run()
421
422