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