X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/c8bc7bb84dcca816055d5876e4772551c48acda5..53307de4e3e1182924ecb2203bd266f401a7614f:/wxPython/src/helpers.cpp diff --git a/wxPython/src/helpers.cpp b/wxPython/src/helpers.cpp index 5d13c8f246..a1a20b66fa 100644 --- a/wxPython/src/helpers.cpp +++ b/wxPython/src/helpers.cpp @@ -4,18 +4,17 @@ // // Author: Robin Dunn // -// Created: 7/1/97 +// Created: 1-July-1997 // RCS-ID: $Id$ // Copyright: (c) 1998 by Total Control Software // Licence: wxWindows license ///////////////////////////////////////////////////////////////////////////// -#include // get the correct definition of NULL #undef DEBUG #include -#include "helpers.h" -#include "pyistream.h" +#include "wx/wxPython/wxPython_int.h" +#include "wx/wxPython/pyistream.h" #ifdef __WXMSW__ #include @@ -24,32 +23,45 @@ #endif #ifdef __WXGTK__ +#include +#include #include #include #include +#define GetXWindow(wxwin) (wxwin)->m_wxwindow ? \ + GDK_WINDOW_XWINDOW(GTK_PIZZA((wxwin)->m_wxwindow)->bin_window) : \ + GDK_WINDOW_XWINDOW((wxwin)->m_widget->window) +#include #endif +#ifdef __WXX11__ +#include "wx/x11/privx.h" +#define GetXWindow(wxwin) ((Window)(wxwin)->GetHandle()) +#endif + +#ifdef __WXMAC__ +#include +#endif + +#include +#include +#include //---------------------------------------------------------------------- -#if PYTHON_API_VERSION <= 1007 && wxUSE_UNICODE +#if PYTHON_API_VERSION < 1009 && wxUSE_UNICODE #error Python must support Unicode to use wxWindows Unicode #endif //---------------------------------------------------------------------- -#ifdef __WXGTK__ -int WXDLLEXPORT wxEntryStart( int& argc, char** argv ); -#else -int WXDLLEXPORT wxEntryStart( int argc, char** argv ); -#endif -int WXDLLEXPORT wxEntryInitGui(); -void WXDLLEXPORT wxEntryCleanup(); - wxPyApp* wxPythonApp = NULL; // Global instance of application object +bool wxPyDoCleanup = false; +bool wxPyDoingCleanup = false; #ifdef WXP_WITH_THREAD +#if !wxPyUSE_GIL_STATE struct wxPyThreadState { unsigned long tid; PyThreadState* tstate; @@ -65,22 +77,38 @@ WX_DEFINE_OBJARRAY(wxPyThreadStateArray); wxPyThreadStateArray* wxPyTStates = NULL; wxMutex* wxPyTMutex = NULL; + +#endif #endif +#define DEFAULTENCODING_SIZE 64 +static char wxPyDefaultEncoding[DEFAULTENCODING_SIZE] = "ascii"; + +static PyObject* wxPython_dict = NULL; +static PyObject* wxPyAssertionError = NULL; +static PyObject* wxPyNoAppError = NULL; + +PyObject* wxPyPtrTypeMap = NULL; + + #ifdef __WXMSW__ // If building for win32... //---------------------------------------------------------------------- // This gets run when the DLL is loaded. We just need to save a handle. //---------------------------------------------------------------------- +extern "C" BOOL WINAPI DllMain( HINSTANCE hinstDLL, // handle to DLL module DWORD fdwReason, // reason for calling function LPVOID lpvReserved // reserved ) { - wxSetInstance(hinstDLL); - return 1; + // If wxPython is embedded in another wxWidgets app then + // the instance has already been set. + if (! wxGetInstance()) + wxSetInstance(hinstDLL); + return true; } #endif @@ -88,19 +116,24 @@ BOOL WINAPI DllMain( // Classes for implementing the wxp main application shell. //---------------------------------------------------------------------- +IMPLEMENT_ABSTRACT_CLASS(wxPyApp, wxApp); + wxPyApp::wxPyApp() { -// printf("**** ctor\n"); + m_assertMode = wxPYAPP_ASSERT_EXCEPTION; + m_startupComplete = false; } + wxPyApp::~wxPyApp() { -// printf("**** dtor\n"); + wxPythonApp = NULL; + wxApp::SetInstance(NULL); } -// This one isn't acutally called... See __wxStart() +// This one isn't acutally called... We fake it with _BootstrapApp bool wxPyApp::OnInit() { - return FALSE; + return false; } @@ -109,11 +142,11 @@ int wxPyApp::MainLoop() { DeletePendingObjects(); bool initialized = wxTopLevelWindows.GetCount() != 0; -#ifdef __WXGTK__ - m_initialized = initialized; -#endif - if (initialized) { + if ( m_exitOnFrameDelete == Later ) { + m_exitOnFrameDelete = Yes; + } + retval = wxApp::MainLoop(); OnExit(); } @@ -121,191 +154,463 @@ int wxPyApp::MainLoop() { } +bool wxPyApp::OnInitGui() { + bool rval=true; + wxApp::OnInitGui(); // in this case always call the base class version + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + if (wxPyCBH_findCallback(m_myInst, "OnInitGui")) + rval = wxPyCBH_callCallback(m_myInst, Py_BuildValue("()")); + wxPyEndBlockThreads(blocked); + return rval; +} -//--------------------------------------------------------------------- -//---------------------------------------------------------------------- -#if wxUSE_UNICODE -// TODO: Is this really the right way to do these???? -static char* copyUniString(const wxChar *s) +int wxPyApp::OnExit() { + int rval=0; + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + if (wxPyCBH_findCallback(m_myInst, "OnExit")) + rval = wxPyCBH_callCallback(m_myInst, Py_BuildValue("()")); + wxPyEndBlockThreads(blocked); + wxApp::OnExit(); // in this case always call the base class version + return rval; +} + + +#ifdef __WXDEBUG__ +void wxPyApp::OnAssert(const wxChar *file, + int line, + const wxChar *cond, + const wxChar *msg) { + + // if we're not fully initialized then just log the error + if (! m_startupComplete) { + wxString buf; + buf.Alloc(4096); + buf.Printf(wxT("%s(%d): assert \"%s\" failed"), + file, line, cond); + if (msg != NULL) { + buf += wxT(": "); + buf += msg; + } + wxLogDebug(buf); + return; + } + + // If the OnAssert is overloaded in the Python class then call it... + bool found; + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + if ((found = wxPyCBH_findCallback(m_myInst, "OnAssert"))) { + PyObject* fso = wx2PyString(file); + PyObject* cso = wx2PyString(file); + PyObject* mso; + if (msg != NULL) + mso = wx2PyString(file); + else { + mso = Py_None; Py_INCREF(Py_None); + } + wxPyCBH_callCallback(m_myInst, Py_BuildValue("(OiOO)", fso, line, cso, mso)); + Py_DECREF(fso); + Py_DECREF(cso); + Py_DECREF(mso); + } + wxPyEndBlockThreads(blocked); + + // ...otherwise do our own thing with it + if (! found) { + // ignore it? + if (m_assertMode & wxPYAPP_ASSERT_SUPPRESS) + return; + + // turn it into a Python exception? + if (m_assertMode & wxPYAPP_ASSERT_EXCEPTION) { + wxString buf; + buf.Alloc(4096); + buf.Printf(wxT("C++ assertion \"%s\" failed in %s(%d)"), cond, file, line); + if (msg != NULL) { + buf += wxT(": "); + buf += msg; + } + + // set the exception + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + PyObject* s = wx2PyString(buf); + PyErr_SetObject(wxPyAssertionError, s); + Py_DECREF(s); + wxPyEndBlockThreads(blocked); + + // Now when control returns to whatever API wrapper was called from + // Python it should detect that an exception is set and will return + // NULL, signalling the exception to Python. + } + + // Send it to the normal log destination, but only if + // not _DIALOG because it will call this too + if ( (m_assertMode & wxPYAPP_ASSERT_LOG) && !(m_assertMode & wxPYAPP_ASSERT_DIALOG)) { + wxString buf; + buf.Alloc(4096); + buf.Printf(wxT("%s(%d): assert \"%s\" failed"), + file, line, cond); + if (msg != NULL) { + buf += wxT(": "); + buf += msg; + } + wxLogDebug(buf); + } + + // do the normal wx assert dialog? + if (m_assertMode & wxPYAPP_ASSERT_DIALOG) + wxApp::OnAssert(file, line, cond, msg); + } +} +#endif + + // For catching Apple Events +void wxPyApp::MacOpenFile(const wxString &fileName) { - if (s == NULL) s = wxT(""); - wxString tmpStr = wxString(s); - char *news = new char[tmpStr.Len()+1]; - for (unsigned int i=0; i 0) - return; +/*static*/ +long wxPyApp::GetMacAboutMenuItemId() { +#ifdef __WXMAC__ + return s_macAboutMenuItemId; +#else + return 0; +#endif +} +/*static*/ +long wxPyApp::GetMacPreferencesMenuItemId() { +#ifdef __WXMAC__ + return s_macPreferencesMenuItemId; +#else + return 0; +#endif +} - int argc = 0; - char** argv = NULL; - PyObject* sysargv = PySys_GetObject("argv"); - if (sysargv != NULL) { - argc = PyList_Size(sysargv); - argv = new char*[argc+1]; - int x; - for(x=0; x 0) { - PyErr_SetString(PyExc_TypeError, "Only 1 wxApp per process!"); - return NULL; - } +/*static*/ +void wxPyApp::SetMacHelpMenuTitleName(const wxString& val) { +#ifdef __WXMAC__ + s_macHelpMenuTitleName = val; #endif +} - // This is the next part of the wxEntry functionality... - int argc = 0; - wxChar** argv = NULL; - PyObject* sysargv = PySys_GetObject("argv"); - if (sysargv != NULL) { - argc = PyList_Size(sysargv); - argv = new wxChar*[argc+1]; - int x; - for(x=0; xargc = argc; - wxPythonApp->argv = argv; + // On wxGTK the locale will be changed to match the system settings, + // but Python before 2.4 needs to have LC_NUMERIC set to "C" in order + // for the floating point conversions and such to work right. +#if defined(__WXGTK__) && PY_VERSION_HEX < 0x02040000 + setlocale(LC_NUMERIC, "C"); +#endif - wxEntryInitGui(); +// wxSystemOptions::SetOption(wxT("mac.textcontrol-use-mlte"), 1); + + // The stock objects were all NULL when they were loaded into + // SWIG generated proxies, so re-init those now... + wxPy_ReinitStockObjects(3); - // Call the Python App's OnInit function - arglist = PyTuple_New(0); - result = PyEval_CallObject(onInitFunc, arglist); - if (!result) { // an exception was raised. - return NULL; + wxPyEndBlockThreads(blocked); + haveInitialized = true; + } + else { + this->argc = 0; + this->argv = NULL; } + - if (! PyInt_Check(result)) { - PyErr_SetString(PyExc_TypeError, "OnInit should return a boolean value"); - return NULL; + // It's now ok to generate exceptions for assertion errors. + wxPythonApp->SetStartupComplete(true); + + // Call the Python wxApp's OnInit function + blocked = wxPyBeginBlockThreads(); + if (wxPyCBH_findCallback(m_myInst, "OnInit")) { + + PyObject* method = m_myInst.GetLastFound(); + PyObject* argTuple = PyTuple_New(0); + retval = PyEval_CallObject(method, argTuple); + Py_DECREF(argTuple); + Py_DECREF(method); + if (retval == NULL) + goto error; + + pyint = PyNumber_Int(retval); + if (! pyint) { + PyErr_SetString(PyExc_TypeError, "OnInit should return a boolean value"); + goto error; + } + result = PyInt_AS_LONG(pyint); } - bResult = PyInt_AS_LONG(result); - if (! bResult) { - PyErr_SetString(PyExc_SystemExit, "OnInit returned FALSE, exiting..."); - return NULL; + else { + // Is it okay if there is no OnInit? Probably so... + result = true; } -#ifdef __WXGTK__ - wxTheApp->m_initialized = (wxTopLevelWindows.GetCount() > 0); + if (! result) { + PyErr_SetString(PyExc_SystemExit, "OnInit returned false, exiting..."); + } + + error: + Py_XDECREF(retval); + Py_XDECREF(pyint); + + wxPyEndBlockThreads(blocked); +}; + +//--------------------------------------------------------------------- +//---------------------------------------------------------------------- + + +#if 0 +static char* wxPyCopyCString(const wxChar* src) +{ + wxWX2MBbuf buff = (wxWX2MBbuf)wxConvCurrent->cWX2MB(src); + size_t len = strlen(buff); + char* dest = new char[len+1]; + strcpy(dest, buff); + return dest; +} + +#if wxUSE_UNICODE +static char* wxPyCopyCString(const char* src) // we need a char version too +{ + size_t len = strlen(src); + char* dest = new char[len+1]; + strcpy(dest, src); + return dest; +} +#endif + +static wxChar* wxPyCopyWString(const char *src) +{ + //wxMB2WXbuf buff = wxConvCurrent->cMB2WX(src); + wxString str(src, *wxConvCurrent); + return copystring(str); +} + +#if wxUSE_UNICODE +static wxChar* wxPyCopyWString(const wxChar *src) +{ + return copystring(src); +} +#endif +#endif + + +inline const char* dropwx(const char* name) { + if (name[0] == 'w' && name[1] == 'x') + return name+2; + else + return name; +} + +//---------------------------------------------------------------------- + +// This function is called when the wx._core_ module is imported to do some +// initial setup. (Before there is a wxApp object.) The rest happens in +// wxPyApp::_BootstrapApp +void __wxPyPreStart(PyObject* moduleDict) +{ + +#ifdef __WXMSW__ +// wxCrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF +// | _CRTDBG_CHECK_ALWAYS_DF +// | _CRTDBG_DELAY_FREE_MEM_DF +// ); +#endif + +#ifdef WXP_WITH_THREAD +#if wxPyUSE_GIL_STATE + PyEval_InitThreads(); +#else + PyEval_InitThreads(); + wxPyTStates = new wxPyThreadStateArray; + wxPyTMutex = new wxMutex; + + // Save the current (main) thread state in our array + PyThreadState* tstate = wxPyBeginAllowThreads(); + wxPyEndAllowThreads(tstate); +#endif #endif - Py_INCREF(Py_None); - return Py_None; + // Ensure that the build options in the DLL (or whatever) match this build + wxApp::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE, "wxPython"); + + // Init the stock objects to a non-NULL value so SWIG doesn't create them as None + wxPy_ReinitStockObjects(1); + + wxInitAllImageHandlers(); } -void __wxCleanup() { - wxEntryCleanup(); + +void __wxPyCleanup() { + wxPyDoingCleanup = true; + if (wxPyDoCleanup) { + wxPyDoCleanup = false; + wxEntryCleanup(); + } #ifdef WXP_WITH_THREAD +#if !wxPyUSE_GIL_STATE delete wxPyTMutex; wxPyTMutex = NULL; wxPyTStates->Empty(); delete wxPyTStates; wxPyTStates = NULL; #endif +#endif } - -static PyObject* wxPython_dict = NULL; -static PyObject* wxPyPtrTypeMap = NULL; - -PyObject* __wxSetDictionary(PyObject* /* self */, PyObject* args) +// Save a reference to the dictionary of the wx._core module, and inject +// a few more things into it. +PyObject* __wxPySetDictionary(PyObject* /* self */, PyObject* args) { if (!PyArg_ParseTuple(args, "O", &wxPython_dict)) return NULL; if (!PyDict_Check(wxPython_dict)) { - PyErr_SetString(PyExc_TypeError, "_wxSetDictionary must have dictionary object!"); + PyErr_SetString(PyExc_TypeError, + "_wxPySetDictionary must have dictionary object!"); return NULL; } @@ -313,125 +618,441 @@ PyObject* __wxSetDictionary(PyObject* /* self */, PyObject* args) wxPyPtrTypeMap = PyDict_New(); PyDict_SetItemString(wxPython_dict, "__wxPyPtrTypeMap", wxPyPtrTypeMap); + // Create an exception object to use for wxASSERTions + wxPyAssertionError = PyErr_NewException("wx._core.PyAssertionError", + PyExc_AssertionError, NULL); + PyDict_SetItemString(wxPython_dict, "PyAssertionError", wxPyAssertionError); + + // Create an exception object to use when the app object hasn't been created yet + wxPyNoAppError = PyErr_NewException("wx._core.PyNoAppError", + PyExc_RuntimeError, NULL); + PyDict_SetItemString(wxPython_dict, "PyNoAppError", wxPyNoAppError); + + #ifdef __WXMOTIF__ #define wxPlatform "__WXMOTIF__" +#define wxPlatName "wxMotif" #endif #ifdef __WXX11__ #define wxPlatform "__WXX11__" +#define wxPlatName "wxX11" #endif #ifdef __WXGTK__ #define wxPlatform "__WXGTK__" +#define wxPlatName "wxGTK" #endif -#if defined(__WIN32__) || defined(__WXMSW__) +#ifdef __WXMSW__ #define wxPlatform "__WXMSW__" +#define wxPlatName "wxMSW" #endif #ifdef __WXMAC__ #define wxPlatform "__WXMAC__" +#define wxPlatName "wxMac" +#endif + +#ifdef __WXDEBUG__ + int wxdebug = 1; +#else + int wxdebug = 0; #endif - PyDict_SetItemString(wxPython_dict, "wxPlatform", PyString_FromString(wxPlatform)); - PyDict_SetItemString(wxPython_dict, "wxUSE_UNICODE", PyInt_FromLong(wxUSE_UNICODE)); + // These should be deprecated in favor of the PlatformInfo tuple built below... + PyDict_SetItemString(wxPython_dict, "Platform", PyString_FromString(wxPlatform)); + PyDict_SetItemString(wxPython_dict, "USE_UNICODE", PyInt_FromLong(wxUSE_UNICODE)); + PyDict_SetItemString(wxPython_dict, "__WXDEBUG__", PyInt_FromLong(wxdebug)); + // Make a tuple of strings that gives more info about the platform. + PyObject* PlatInfo = PyList_New(0); + PyObject* obj; - Py_INCREF(Py_None); - return Py_None; +#define _AddInfoString(st) \ + obj = PyString_FromString(st); \ + PyList_Append(PlatInfo, obj); \ + Py_DECREF(obj) + + _AddInfoString(wxPlatform); + _AddInfoString(wxPlatName); +#if wxUSE_UNICODE + _AddInfoString("unicode"); +#else + _AddInfoString("ansi"); +#endif +#ifdef __WXGTK__ +#ifdef __WXGTK20__ + _AddInfoString("gtk2"); +#else + _AddInfoString("gtk1"); +#endif +#endif +#ifdef __WXDEBUG__ + _AddInfoString("wx-assertions-on"); +#else + _AddInfoString("wx-assertions-off"); +#endif + +#undef _AddInfoString + + PyObject* PlatInfoTuple = PyList_AsTuple(PlatInfo); + Py_DECREF(PlatInfo); + PyDict_SetItemString(wxPython_dict, "PlatformInfo", PlatInfoTuple); + + RETURN_NONE(); } //--------------------------------------------------------------------------- -// Stuff used by OOR to find the right wxPython class type to return and to -// build it. +// Python's PyInstance_Check does not return True for instances of new-style +// classes. This should get close enough for both new and old classes but I +// should re-evaluate the need for doing instance checks... +bool wxPyInstance_Check(PyObject* obj) { + return PyObject_HasAttrString(obj, "__class__") != 0; +} -// The pointer type map is used when the "pointer" type name generated by SWIG -// is not the same as the shadow class name, for example wxPyTreeCtrl -// vs. wxTreeCtrl. It needs to be referenced in Python as well as from C++, -// so we'll just make it a Python dictionary in the wx module's namespace. -void wxPyPtrTypeMap_Add(const char* commonName, const char* ptrName) { - if (! wxPyPtrTypeMap) - wxPyPtrTypeMap = PyDict_New(); - PyDict_SetItemString(wxPyPtrTypeMap, - (char*)commonName, - PyString_FromString((char*)ptrName)); + +// This one checks if the object is an instance of a SWIG proxy class (it has +// a .this attribute) +bool wxPySwigInstance_Check(PyObject* obj) { + return PyObject_HasAttrString(obj, "this") != 0; } +//--------------------------------------------------------------------------- + +// The stock objects are no longer created when the wx._core_ module is +// imported, but only after the app object has been created. The +// wxPy_ReinitStockObjects function will be called 3 times to pass the stock +// objects though various stages of evolution: +// +// pass 1: Set all the pointers to a non-NULL value so the Python proxy +// object will be created (otherwise SWIG will just use None.) +// +// pass 2: After the module has been imported and the python proxys have +// been created, then set the __class__ to be _wxPyUnbornObject so +// it will catch any access to the object and will raise an exception. +// +// pass 3: Finally, from BootstrapApp patch things up so the stock objects +// can be used. + +PyObject* __wxPyFixStockObjects(PyObject* /* self */, PyObject* args) +{ + wxPy_ReinitStockObjects(2); + RETURN_NONE(); +} -PyObject* wxPyClassExists(const char* className) { - if (!className) - return NULL; +static void rsoPass2(const char* name) +{ + static PyObject* unbornObjectClass = NULL; + PyObject* obj; + + if (unbornObjectClass == NULL) { + unbornObjectClass = PyDict_GetItemString(wxPython_dict, "_wxPyUnbornObject"); + Py_INCREF(unbornObjectClass); + } - char buff[64]; // should always be big enough... + // Find the object instance + obj = PyDict_GetItemString(wxPython_dict, (char*)dropwx(name)); + wxCHECK_RET(obj != NULL, wxT("Unable to find stock object")); + wxCHECK_RET(wxPySwigInstance_Check(obj), wxT("Not a swig instance")); - sprintf(buff, "%sPtr", className); - PyObject* classobj = PyDict_GetItemString(wxPython_dict, buff); + // Change its class + PyObject_SetAttrString(obj, "__class__", unbornObjectClass); - return classobj; // returns NULL if not found } +static void rsoPass3(const char* name, const char* classname, void* ptr) +{ + PyObject* obj; + PyObject* classobj; + PyObject* ptrobj; -#if wxUSE_UNICODE -void unicodeToChar(const wxString *src, char *dest) + // Find the object instance + obj = PyDict_GetItemString(wxPython_dict, (char*)dropwx(name)); + wxCHECK_RET(obj != NULL, wxT("Unable to find stock object")); + wxCHECK_RET(wxPySwigInstance_Check(obj), wxT("Not a swig instance")); + + // Find the class object and put it back in the instance + classobj = PyDict_GetItemString(wxPython_dict, (char*)dropwx(classname)); + wxCHECK_RET(classobj != NULL, wxT("Unable to find stock class object")); + PyObject_SetAttrString(obj, "__class__", classobj); + + // Rebuild the .this swigified pointer with the new value of the C++ pointer + ptrobj = wxPyMakeSwigPtr(ptr, wxString(classname, *wxConvCurrent)); + PyObject_SetAttrString(obj, "this", ptrobj); + Py_DECREF(ptrobj); +} + + + +void wxPy_ReinitStockObjects(int pass) { - for (unsigned int i=0; iLen(); i++) { - dest[i] = (char)(*src)[i]; + + // If there is already an App object then wxPython is probably embedded in + // a wx C++ application, so there is no need to do all this. + static bool embedded = false; + if ((pass == 1 || pass == 2) && wxTheApp) { + embedded = true; + return; } - dest[i] = '\0'; + if (pass == 3 && embedded) + return; + + +#define REINITOBJ(name, classname) \ + if (pass == 1) { name = (classname*)0xC0C0C0C0; } \ + else if (pass == 2) { rsoPass2(#name); } \ + else if (pass == 3) { rsoPass3(#name, #classname, (void*)name); } + + +#define REINITOBJ2(name, classname) \ + if (pass == 1) { } \ + else if (pass == 2) { rsoPass2(#name); } \ + else if (pass == 3) { rsoPass3(#name, #classname, (void*)&name); } + + + REINITOBJ(wxNORMAL_FONT, wxFont); + REINITOBJ(wxSMALL_FONT, wxFont); + REINITOBJ(wxITALIC_FONT, wxFont); + REINITOBJ(wxSWISS_FONT, wxFont); + + REINITOBJ(wxRED_PEN, wxPen); + REINITOBJ(wxCYAN_PEN, wxPen); + REINITOBJ(wxGREEN_PEN, wxPen); + REINITOBJ(wxBLACK_PEN, wxPen); + REINITOBJ(wxWHITE_PEN, wxPen); + REINITOBJ(wxTRANSPARENT_PEN, wxPen); + REINITOBJ(wxBLACK_DASHED_PEN, wxPen); + REINITOBJ(wxGREY_PEN, wxPen); + REINITOBJ(wxMEDIUM_GREY_PEN, wxPen); + REINITOBJ(wxLIGHT_GREY_PEN, wxPen); + + REINITOBJ(wxBLUE_BRUSH, wxBrush); + REINITOBJ(wxGREEN_BRUSH, wxBrush); + REINITOBJ(wxWHITE_BRUSH, wxBrush); + REINITOBJ(wxBLACK_BRUSH, wxBrush); + REINITOBJ(wxTRANSPARENT_BRUSH, wxBrush); + REINITOBJ(wxCYAN_BRUSH, wxBrush); + REINITOBJ(wxRED_BRUSH, wxBrush); + REINITOBJ(wxGREY_BRUSH, wxBrush); + REINITOBJ(wxMEDIUM_GREY_BRUSH, wxBrush); + REINITOBJ(wxLIGHT_GREY_BRUSH, wxBrush); + + REINITOBJ(wxBLACK, wxColour); + REINITOBJ(wxWHITE, wxColour); + REINITOBJ(wxRED, wxColour); + REINITOBJ(wxBLUE, wxColour); + REINITOBJ(wxGREEN, wxColour); + REINITOBJ(wxCYAN, wxColour); + REINITOBJ(wxLIGHT_GREY, wxColour); + + REINITOBJ(wxSTANDARD_CURSOR, wxCursor); + REINITOBJ(wxHOURGLASS_CURSOR, wxCursor); + REINITOBJ(wxCROSS_CURSOR, wxCursor); + + REINITOBJ2(wxNullBitmap, wxBitmap); + REINITOBJ2(wxNullIcon, wxIcon); + REINITOBJ2(wxNullCursor, wxCursor); + REINITOBJ2(wxNullPen, wxPen); + REINITOBJ2(wxNullBrush, wxBrush); + REINITOBJ2(wxNullPalette, wxPalette); + REINITOBJ2(wxNullFont, wxFont); + REINITOBJ2(wxNullColour, wxColour); + + REINITOBJ(wxTheFontList, wxFontList); + REINITOBJ(wxThePenList, wxPenList); + REINITOBJ(wxTheBrushList, wxBrushList); + REINITOBJ(wxTheColourDatabase, wxColourDatabase); + + + REINITOBJ2(wxDefaultValidator, wxValidator); + REINITOBJ2(wxNullImage, wxImage); + REINITOBJ2(wxNullAcceleratorTable, wxAcceleratorTable); + +#undef REINITOBJ +#undef REINITOBJ2 } -PyObject* wxPyClassExistsUnicode(const wxString *className) { - if (!className->Len()) - return NULL; - char buff[64]; // should always be big enough... - char *nameBuf = new char[className->Len()+1]; - unicodeToChar(className, nameBuf); - sprintf(buff, "%sPtr", nameBuf); - PyObject* classobj = PyDict_GetItemString(wxPython_dict, buff); - delete [] nameBuf; - return classobj; // returns NULL if not found + +//--------------------------------------------------------------------------- + +// Check for existence of a wxApp, setting an exception if there isn't one. +// This doesn't need to aquire the GIL because it should only be called from +// an %exception before the lock is released. + +bool wxPyCheckForApp() { + if (wxTheApp != NULL) + return true; + else { + PyErr_SetString(wxPyNoAppError, "The wx.App object must be created first!"); + return false; + } } -#endif + +//--------------------------------------------------------------------------- + +void wxPyUserData_dtor(wxPyUserData* self) { + if (! wxPyDoingCleanup) { + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + Py_DECREF(self->m_obj); + self->m_obj = NULL; + wxPyEndBlockThreads(blocked); + } +} + + +void wxPyClientData_dtor(wxPyClientData* self) { + if (! wxPyDoingCleanup) { // Don't do it during cleanup as Python + // may have already garbage collected the object... + if (self->m_incRef) { + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + Py_DECREF(self->m_obj); + wxPyEndBlockThreads(blocked); + } + self->m_obj = NULL; + } +} + + + +// This is called when an OOR controled object is being destroyed. Although +// the C++ object is going away there is no way to force the Python object +// (and all references to it) to die too. This causes problems (crashes) in +// wxPython when a python shadow object attempts to call a C++ method using +// the now bogus pointer... So to try and prevent this we'll do a little black +// magic and change the class of the python instance to a class that will +// raise an exception for any attempt to call methods with it. See +// _wxPyDeadObject in _core_ex.py for the implementation of this class. +void wxPyOORClientData_dtor(wxPyOORClientData* self) { + + static PyObject* deadObjectClass = NULL; + + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + if (deadObjectClass == NULL) { + deadObjectClass = PyDict_GetItemString(wxPython_dict, "_wxPyDeadObject"); + // TODO: Can not wxASSERT here because inside a wxPyBeginBlock Threads, + // will lead to a deadlock when it tries to aquire the GIL again. + //wxASSERT_MSG(deadObjectClass != NULL, wxT("Can't get _wxPyDeadObject class!")); + Py_INCREF(deadObjectClass); + } + + + // Only if there is more than one reference to the object and we are + // holding the OOR reference: + if ( !wxPyDoingCleanup && self->m_obj->ob_refcnt > 1 && self->m_incRef) { + // bool isInstance = wxPyInstance_Check(self->m_obj); + // TODO same here + //wxASSERT_MSG(isInstance, wxT("m_obj not an instance!?!?!")); + + // Call __del__, if there is one. + PyObject* func = PyObject_GetAttrString(self->m_obj, "__del__"); + if (func) { + PyObject* rv = PyObject_CallMethod(self->m_obj, "__del__", NULL); + Py_XDECREF(rv); + Py_DECREF(func); + } + if (PyErr_Occurred()) + PyErr_Clear(); // just ignore it for now + + + PyObject* dict = PyObject_GetAttrString(self->m_obj, "__dict__"); + if (dict) { + // Clear the instance's dictionary + PyDict_Clear(dict); + + // put the name of the old class into the instance, and then reset the + // class to be the dead class. + PyObject* klass = PyObject_GetAttrString(self->m_obj, "__class__"); + PyObject* name = PyObject_GetAttrString(klass, "__name__"); + PyDict_SetItemString(dict, "_name", name); + PyObject_SetAttrString(self->m_obj, "__class__", deadObjectClass); + //Py_INCREF(deadObjectClass); + Py_DECREF(klass); + Py_DECREF(name); + } + } + + // m_obj is DECREF'd in the base class dtor... + wxPyEndBlockThreads(blocked); +} + + +//--------------------------------------------------------------------------- +// Stuff used by OOR to find the right wxPython class type to return and to +// build it. -PyObject* wxPyMake_wxObject(wxObject* source, bool checkEvtHandler) { +// The pointer type map is used when the "pointer" type name generated by SWIG +// is not the same as the shadow class name, for example wxPyTreeCtrl +// vs. wxTreeCtrl. It needs to be referenced in Python as well as from C++, +// so we'll just make it a Python dictionary in the wx module's namespace. +// (See __wxSetDictionary) +void wxPyPtrTypeMap_Add(const char* commonName, const char* ptrName) { + if (! wxPyPtrTypeMap) + wxPyPtrTypeMap = PyDict_New(); + PyDict_SetItemString(wxPyPtrTypeMap, + (char*)commonName, + PyString_FromString((char*)ptrName)); +} + + + + +PyObject* wxPyMake_wxObject(wxObject* source, bool setThisOwn, bool checkEvtHandler) { PyObject* target = NULL; - bool isEvtHandler = FALSE; + bool isEvtHandler = false; + bool isSizer = false; if (source) { // If it's derived from wxEvtHandler then there may // already be a pointer to a Python object that we can use // in the OOR data. if (checkEvtHandler && wxIsKindOf(source, wxEvtHandler)) { - isEvtHandler = TRUE; + isEvtHandler = true; wxEvtHandler* eh = (wxEvtHandler*)source; - wxPyClientData* data = (wxPyClientData*)eh->GetClientObject(); + wxPyOORClientData* data = (wxPyOORClientData*)eh->GetClientObject(); if (data) { target = data->m_obj; - Py_INCREF(target); + if (target) + Py_INCREF(target); } } - // TODO: unicode fix + // Also check for wxSizer + if (!target && wxIsKindOf(source, wxSizer)) { + isSizer = true; + wxSizer* sz = (wxSizer*)source; + wxPyOORClientData* data = (wxPyOORClientData*)sz->GetClientObject(); + if (data) { + target = data->m_obj; + if (target) + Py_INCREF(target); + } + } + if (! target) { - // Otherwise make it the old fashioned way by making a - // new shadow object and putting this pointer in it. - wxClassInfo* info = source->GetClassInfo(); - wxChar* name = (wxChar*)info->GetClassName(); - PyObject* klass = wxPyClassExists(name); - while (info && !klass) { - name = (wxChar*)info->GetBaseClassName1(); - info = wxClassInfo::FindClass(name); - klass = wxPyClassExists(name); + // Otherwise make it the old fashioned way by making a new shadow + // object and putting this pointer in it. Look up the class + // heirarchy until we find a class name that is located in the + // python module. + const wxClassInfo* info = source->GetClassInfo(); + wxString name = info->GetClassName(); + bool exists = wxPyCheckSwigType(name); + while (info && !exists) { + info = info->GetBaseClass1(); + name = info->GetClassName(); + exists = wxPyCheckSwigType(name); } if (info) { - target = wxPyConstructObject(source, name, klass, FALSE); + target = wxPyConstructObject((void*)source, name, setThisOwn); if (target && isEvtHandler) - ((wxEvtHandler*)source)->SetClientObject(new wxPyClientData(target)); + ((wxEvtHandler*)source)->SetClientObject(new wxPyOORClientData(target)); + if (target && isSizer) + ((wxSizer*)source)->SetClientObject(new wxPyOORClientData(target)); } else { - wxString msg("wxPython class not found for "); + wxString msg(wxT("wxPython class not found for ")); msg += source->GetClassInfo()->GetClassName(); - PyErr_SetString(PyExc_NameError, msg.c_str()); + PyErr_SetString(PyExc_NameError, msg.mbc_str()); target = NULL; } } @@ -442,126 +1063,51 @@ PyObject* wxPyMake_wxObject(wxObject* source, bool checkEvtHandler) { } -PyObject* wxPyMake_wxSizer(wxSizer* source) { - PyObject* target = NULL; - - if (source && wxIsKindOf(source, wxSizer)) { - // If it's derived from wxSizer then there may - // already be a pointer to a Python object that we can use - // in the OOR data. - wxSizer* sz = (wxSizer*)source; - wxPyClientData* data = (wxPyClientData*)sz->GetClientObject(); - if (data) { - target = data->m_obj; - Py_INCREF(target); - } - } - if (! target) { - target = wxPyMake_wxObject(source, FALSE); - if (target != Py_None) - ((wxSizer*)source)->SetClientObject(new wxPyClientData(target)); - } - return target; -} - - - -//--------------------------------------------------------------------------- - -PyObject* wxPyConstructObject(void* ptr, - const char* className, - PyObject* klass, - int setThisOwn) { - - PyObject* obj; - PyObject* arg; - PyObject* item; - char swigptr[64]; // should always be big enough... - char buff[64]; - - if ((item = PyDict_GetItemString(wxPyPtrTypeMap, (char*)className)) != NULL) { - className = PyString_AsString(item); - } - sprintf(buff, "_%s_p", className); - SWIG_MakePtr(swigptr, ptr, buff); - - arg = Py_BuildValue("(s)", swigptr); - obj = PyInstance_New(klass, arg, NULL); - Py_DECREF(arg); - - if (setThisOwn) { - PyObject* one = PyInt_FromLong(1); - PyObject_SetAttrString(obj, "thisown", one); - Py_DECREF(one); - } +PyObject* wxPyMake_wxSizer(wxSizer* source, bool setThisOwn) { - return obj; + return wxPyMake_wxObject(source, setThisOwn); } -PyObject* wxPyConstructObject(void* ptr, - const char* className, - int setThisOwn) { - PyObject* obj; - - if (!ptr) { - Py_INCREF(Py_None); - return Py_None; - } - - char buff[64]; // should always be big enough... - sprintf(buff, "%sPtr", className); - - wxASSERT_MSG(wxPython_dict, "wxPython_dict is not set yet!!"); - - PyObject* classobj = PyDict_GetItemString(wxPython_dict, buff); - if (! classobj) { - char temp[128]; - sprintf(temp, - "*** Unknown class name %s, tell Robin about it please ***", - buff); - obj = PyString_FromString(temp); - return obj; - } - - return wxPyConstructObject(ptr, className, classobj, setThisOwn); -} - //--------------------------------------------------------------------------- #ifdef WXP_WITH_THREAD +#if !wxPyUSE_GIL_STATE + inline unsigned long wxPyGetCurrentThreadId() { return wxThread::GetCurrentId(); } -static PyThreadState* gs_shutdownTState; +static wxPyThreadState gs_shutdownTState; + static -PyThreadState* wxPyGetThreadState() { +wxPyThreadState* wxPyGetThreadState() { if (wxPyTMutex == NULL) // Python is shutting down... - return gs_shutdownTState; + return &gs_shutdownTState; unsigned long ctid = wxPyGetCurrentThreadId(); - PyThreadState* tstate = NULL; + wxPyThreadState* tstate = NULL; wxPyTMutex->Lock(); for(size_t i=0; i < wxPyTStates->GetCount(); i++) { wxPyThreadState& info = wxPyTStates->Item(i); if (info.tid == ctid) { - tstate = info.tstate; + tstate = &info; break; } } wxPyTMutex->Unlock(); - wxASSERT_MSG(tstate, "PyThreadState should not be NULL!"); + wxASSERT_MSG(tstate, wxT("PyThreadState should not be NULL!")); return tstate; } + static void wxPySaveThreadState(PyThreadState* tstate) { if (wxPyTMutex == NULL) { // Python is shutting down, assume a single thread... - gs_shutdownTState = tstate; + gs_shutdownTState.tstate = tstate; return; } unsigned long ctid = wxPyGetCurrentThreadId(); @@ -569,7 +1115,16 @@ void wxPySaveThreadState(PyThreadState* tstate) { for(size_t i=0; i < wxPyTStates->GetCount(); i++) { wxPyThreadState& info = wxPyTStates->Item(i); if (info.tid == ctid) { - info.tstate = tstate; +#if 0 + if (info.tstate != tstate) + wxLogMessage("*** tstate mismatch!???"); +#endif + info.tstate = tstate; // allow for transient tstates + // Normally it will never change, but apparently COM callbacks + // (i.e. ActiveX controls) will (incorrectly IMHO) use a transient + // tstate which will then be garbage the next time we try to use + // it... + wxPyTMutex->Unlock(); return; } @@ -580,6 +1135,8 @@ void wxPySaveThreadState(PyThreadState* tstate) { } #endif +#endif + // Calls from Python to wxWindows code are wrapped in calls to these @@ -588,7 +1145,9 @@ void wxPySaveThreadState(PyThreadState* tstate) { PyThreadState* wxPyBeginAllowThreads() { #ifdef WXP_WITH_THREAD PyThreadState* saved = PyEval_SaveThread(); // Py_BEGIN_ALLOW_THREADS; +#if !wxPyUSE_GIL_STATE wxPySaveThreadState(saved); +#endif return saved; #else return NULL; @@ -606,18 +1165,49 @@ void wxPyEndAllowThreads(PyThreadState* saved) { // Calls from wxWindows back to Python code, or even any PyObject // manipulations, PyDECREF's and etc. are wrapped in calls to these functions: -void wxPyBeginBlockThreads() { +wxPyBlock_t wxPyBeginBlockThreads() { #ifdef WXP_WITH_THREAD - PyThreadState* tstate = wxPyGetThreadState(); - PyEval_RestoreThread(tstate); + if (! Py_IsInitialized()) { + return (wxPyBlock_t)0; + } +#if wxPyUSE_GIL_STATE + PyGILState_STATE state = PyGILState_Ensure(); + return state; +#else + PyThreadState *current = _PyThreadState_Current; + + // Only block if there wasn't already a tstate, or if the current one is + // not the one we are wanting to change to. This should prevent deadlock + // if there are nested calls to wxPyBeginBlockThreads + wxPyBlock_t blocked = false; + wxPyThreadState* tstate = wxPyGetThreadState(); + if (current != tstate->tstate) { + PyEval_RestoreThread(tstate->tstate); + blocked = true; + } + return blocked; +#endif +#else + return (wxPyBlock_t)0; #endif } -void wxPyEndBlockThreads() { +void wxPyEndBlockThreads(wxPyBlock_t blocked) { #ifdef WXP_WITH_THREAD - PyThreadState* tstate = PyEval_SaveThread(); - // Is there any need to save it again? + if (! Py_IsInitialized()) { + return; + } +#if wxPyUSE_GIL_STATE + PyGILState_Release(blocked); +#else + // Only unblock if we blocked in the last call to wxPyBeginBlockThreads. + // The value of blocked passed in needs to be the same as that returned + // from wxPyBeginBlockThreads at the same nesting level. + if ( blocked ) { + PyEval_SaveThread(); + } +#endif #endif } @@ -625,156 +1215,155 @@ void wxPyEndBlockThreads() { //--------------------------------------------------------------------------- // wxPyInputStream and wxPyCBInputStream methods -#include -WX_DEFINE_LIST(wxStringPtrList); - void wxPyInputStream::close() { - /* do nothing */ + /* do nothing for now */ } void wxPyInputStream::flush() { - /* do nothing */ + /* do nothing for now */ } bool wxPyInputStream::eof() { if (m_wxis) return m_wxis->Eof(); else - return TRUE; + return true; } wxPyInputStream::~wxPyInputStream() { - /* do nothing */ + if (m_wxis) + delete m_wxis; } -wxString* wxPyInputStream::read(int size) { - wxString* s = NULL; + + + +PyObject* wxPyInputStream::read(int size) { + PyObject* obj = NULL; + wxMemoryBuffer buf; const int BUFSIZE = 1024; // check if we have a real wxInputStream to work with if (!m_wxis) { + wxPyBlock_t blocked = wxPyBeginBlockThreads(); PyErr_SetString(PyExc_IOError, "no valid C-wxInputStream"); + wxPyEndBlockThreads(blocked); return NULL; } if (size < 0) { - // init buffers - char * buf = new char[BUFSIZE]; - if (!buf) { - PyErr_NoMemory(); - return NULL; - } - - s = new wxString(); - if (!s) { - delete buf; - PyErr_NoMemory(); - return NULL; - } - - // read until EOF - while (! m_wxis->Eof()) { - m_wxis->Read(buf, BUFSIZE); - s->Append(buf, m_wxis->LastRead()); - } - delete buf; - - // error check - if (m_wxis->LastError() == wxSTREAM_READ_ERROR) { - delete s; - PyErr_SetString(PyExc_IOError,"IOError in wxInputStream"); - return NULL; + // read while bytes are available on the stream + while ( m_wxis->CanRead() ) { + m_wxis->Read(buf.GetAppendBuf(BUFSIZE), BUFSIZE); + buf.UngetAppendBuf(m_wxis->LastRead()); } } else { // Read only size number of characters - s = new wxString; - if (!s) { - PyErr_NoMemory(); - return NULL; - } - - // read size bytes - m_wxis->Read(s->GetWriteBuf(size+1), size); - s->UngetWriteBuf(m_wxis->LastRead()); + m_wxis->Read(buf.GetWriteBuf(size), size); + buf.UngetWriteBuf(m_wxis->LastRead()); + } - // error check - if (m_wxis->LastError() == wxSTREAM_READ_ERROR) { - delete s; - PyErr_SetString(PyExc_IOError,"IOError in wxInputStream"); - return NULL; - } + // error check + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxStreamError err = m_wxis->GetLastError(); + if (err != wxSTREAM_NO_ERROR && err != wxSTREAM_EOF) { + PyErr_SetString(PyExc_IOError,"IOError in wxInputStream"); + } + else { + // We use only strings for the streams, not unicode + obj = PyString_FromStringAndSize(buf, buf.GetDataLen()); } - return s; + wxPyEndBlockThreads(blocked); + return obj; } -wxString* wxPyInputStream::readline (int size) { - // check if we have a real wxInputStream to work with - if (!m_wxis) { - PyErr_SetString(PyExc_IOError,"no valid C-wxInputStream"); - return NULL; - } - - // init buffer +PyObject* wxPyInputStream::readline(int size) { + PyObject* obj = NULL; + wxMemoryBuffer buf; int i; char ch; - wxString* s = new wxString; - if (!s) { - PyErr_NoMemory(); + + // check if we have a real wxInputStream to work with + if (!m_wxis) { + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + PyErr_SetString(PyExc_IOError,"no valid C-wxInputStream"); + wxPyEndBlockThreads(blocked); return NULL; } // read until \n or byte limit reached - for (i=ch=0; (ch != '\n') && (!m_wxis->Eof()) && ((size < 0) || (i < size)); i++) { - *s += ch = m_wxis->GetC(); + for (i=ch=0; (ch != '\n') && (m_wxis->CanRead()) && ((size < 0) || (i < size)); i++) { + ch = m_wxis->GetC(); + buf.AppendByte(ch); } // errorcheck - if (m_wxis->LastError() == wxSTREAM_READ_ERROR) { - delete s; + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxStreamError err = m_wxis->GetLastError(); + if (err != wxSTREAM_NO_ERROR && err != wxSTREAM_EOF) { PyErr_SetString(PyExc_IOError,"IOError in wxInputStream"); - return NULL; } - return s; + else { + // We use only strings for the streams, not unicode + obj = PyString_FromStringAndSize((char*)buf.GetData(), buf.GetDataLen()); + } + wxPyEndBlockThreads(blocked); + return obj; } -wxStringPtrList* wxPyInputStream::readlines (int sizehint) { +PyObject* wxPyInputStream::readlines(int sizehint) { + PyObject* pylist; + // check if we have a real wxInputStream to work with if (!m_wxis) { - PyErr_SetString(PyExc_IOError,"no valid C-wxInputStream below"); + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + PyErr_SetString(PyExc_IOError,"no valid C-wxInputStream"); + wxPyEndBlockThreads(blocked); return NULL; } // init list - wxStringPtrList* l = new wxStringPtrList(); - if (!l) { + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + pylist = PyList_New(0); + wxPyEndBlockThreads(blocked); + + if (!pylist) { + wxPyBlock_t blocked = wxPyBeginBlockThreads(); PyErr_NoMemory(); + wxPyEndBlockThreads(blocked); return NULL; } // read sizehint bytes or until EOF int i; - for (i=0; (!m_wxis->Eof()) && ((sizehint < 0) || (i < sizehint));) { - wxString* s = readline(); + for (i=0; (m_wxis->CanRead()) && ((sizehint < 0) || (i < sizehint));) { + PyObject* s = this->readline(); if (s == NULL) { - l->DeleteContents(TRUE); - l->Clear(); + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + Py_DECREF(pylist); + wxPyEndBlockThreads(blocked); return NULL; } - l->Append(s); - i = i + s->Length(); + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + PyList_Append(pylist, s); + i += PyString_Size(s); + wxPyEndBlockThreads(blocked); } // error check - if (m_wxis->LastError() == wxSTREAM_READ_ERROR) { - l->DeleteContents(TRUE); - l->Clear(); + wxStreamError err = m_wxis->GetLastError(); + if (err != wxSTREAM_NO_ERROR && err != wxSTREAM_EOF) { + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + Py_DECREF(pylist); PyErr_SetString(PyExc_IOError,"IOError in wxInputStream"); + wxPyEndBlockThreads(blocked); return NULL; } - return l; + + return pylist; } @@ -796,18 +1385,31 @@ wxPyCBInputStream::wxPyCBInputStream(PyObject *r, PyObject *s, PyObject *t, bool : wxInputStream(), m_read(r), m_seek(s), m_tell(t), m_block(block) {} +wxPyCBInputStream::wxPyCBInputStream(const wxPyCBInputStream& other) +{ + m_read = other.m_read; + m_seek = other.m_seek; + m_tell = other.m_tell; + m_block = other.m_block; + Py_INCREF(m_read); + Py_INCREF(m_seek); + Py_INCREF(m_tell); +} + wxPyCBInputStream::~wxPyCBInputStream() { - if (m_block) wxPyBeginBlockThreads(); + wxPyBlock_t blocked; + if (m_block) blocked = wxPyBeginBlockThreads(); Py_XDECREF(m_read); Py_XDECREF(m_seek); Py_XDECREF(m_tell); - if (m_block) wxPyEndBlockThreads(); + if (m_block) wxPyEndBlockThreads(blocked); } wxPyCBInputStream* wxPyCBInputStream::create(PyObject *py, bool block) { - if (block) wxPyBeginBlockThreads(); + wxPyBlock_t blocked; + if (block) blocked = wxPyBeginBlockThreads(); PyObject* read = getMethod(py, "read"); PyObject* seek = getMethod(py, "seek"); @@ -818,14 +1420,23 @@ wxPyCBInputStream* wxPyCBInputStream::create(PyObject *py, bool block) { Py_XDECREF(read); Py_XDECREF(seek); Py_XDECREF(tell); - if (block) wxPyEndBlockThreads(); + if (block) wxPyEndBlockThreads(blocked); return NULL; } - if (block) wxPyEndBlockThreads(); + if (block) wxPyEndBlockThreads(blocked); return new wxPyCBInputStream(read, seek, tell, block); } + +wxPyCBInputStream* wxPyCBInputStream_create(PyObject *py, bool block) { + return wxPyCBInputStream::create(py, block); +} + +wxPyCBInputStream* wxPyCBInputStream_copy(wxPyCBInputStream* other) { + return new wxPyCBInputStream(*other); +} + PyObject* wxPyCBInputStream::getMethod(PyObject* py, char* name) { if (!PyObject_HasAttrString(py, name)) return NULL; @@ -838,16 +1449,16 @@ PyObject* wxPyCBInputStream::getMethod(PyObject* py, char* name) { } -size_t wxPyCBInputStream::GetSize() const { +wxFileOffset wxPyCBInputStream::GetLength() const { wxPyCBInputStream* self = (wxPyCBInputStream*)this; // cast off const if (m_seek && m_tell) { - off_t temp = self->OnSysTell(); - off_t ret = self->OnSysSeek(0, wxFromEnd); + wxFileOffset temp = self->OnSysTell(); + wxFileOffset ret = self->OnSysSeek(0, wxFromEnd); self->OnSysSeek(temp, wxFromStart); return ret; } else - return 0; + return wxInvalidOffset; } @@ -855,26 +1466,25 @@ size_t wxPyCBInputStream::OnSysRead(void *buffer, size_t bufsize) { if (bufsize == 0) return 0; - wxPyBeginBlockThreads(); + wxPyBlock_t blocked = wxPyBeginBlockThreads(); PyObject* arglist = Py_BuildValue("(i)", bufsize); PyObject* result = PyEval_CallObject(m_read, arglist); Py_DECREF(arglist); size_t o = 0; - if ((result != NULL) && PyString_Check(result)) { // TODO: unicode? + if ((result != NULL) && PyString_Check(result)) { o = PyString_Size(result); if (o == 0) m_lasterror = wxSTREAM_EOF; if (o > bufsize) o = bufsize; - memcpy((char*)buffer, PyString_AsString(result), o); + memcpy((char*)buffer, PyString_AsString(result), o); // strings only, not unicode... Py_DECREF(result); } else m_lasterror = wxSTREAM_READ_ERROR; - wxPyEndBlockThreads(); - m_lastcount = o; + wxPyEndBlockThreads(blocked); return o; } @@ -883,27 +1493,42 @@ size_t wxPyCBInputStream::OnSysWrite(const void *buffer, size_t bufsize) { return 0; } -off_t wxPyCBInputStream::OnSysSeek(off_t off, wxSeekMode mode) { - wxPyBeginBlockThreads(); - PyObject* arglist = Py_BuildValue("(ii)", off, mode); + +wxFileOffset wxPyCBInputStream::OnSysSeek(wxFileOffset off, wxSeekMode mode) { + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + PyObject* arglist = PyTuple_New(2); + + if (sizeof(wxFileOffset) > sizeof(long)) + // wxFileOffset is a 64-bit value... + PyTuple_SET_ITEM(arglist, 0, PyLong_FromLongLong(off)); + else + PyTuple_SET_ITEM(arglist, 0, PyInt_FromLong(off)); + + PyTuple_SET_ITEM(arglist, 1, PyInt_FromLong(mode)); + + PyObject* result = PyEval_CallObject(m_seek, arglist); Py_DECREF(arglist); Py_XDECREF(result); - wxPyEndBlockThreads(); + wxPyEndBlockThreads(blocked); return OnSysTell(); } -off_t wxPyCBInputStream::OnSysTell() const { - wxPyBeginBlockThreads(); + +wxFileOffset wxPyCBInputStream::OnSysTell() const { + wxPyBlock_t blocked = wxPyBeginBlockThreads(); PyObject* arglist = Py_BuildValue("()"); PyObject* result = PyEval_CallObject(m_tell, arglist); Py_DECREF(arglist); - off_t o = 0; + wxFileOffset o = 0; if (result != NULL) { - o = PyInt_AsLong(result); + if (PyLong_Check(result)) + o = PyLong_AsLongLong(result); + else + o = PyInt_AsLong(result); Py_DECREF(result); }; - wxPyEndBlockThreads(); + wxPyEndBlockThreads(blocked); return o; } @@ -922,12 +1547,14 @@ wxPyCallback::wxPyCallback(const wxPyCallback& other) { } wxPyCallback::~wxPyCallback() { - wxPyBeginBlockThreads(); + wxPyBlock_t blocked = wxPyBeginBlockThreads(); Py_DECREF(m_func); - wxPyEndBlockThreads(); + wxPyEndBlockThreads(blocked); } +#define wxPy_PRECALLINIT "_preCallInit" +#define wxPy_POSTCALLCLEANUP "_postCallCleanup" // This function is used for all events destined for Python event handlers. void wxPyCallback::EventThunker(wxEvent& event) { @@ -936,39 +1563,84 @@ void wxPyCallback::EventThunker(wxEvent& event) { PyObject* result; PyObject* arg; PyObject* tuple; + bool checkSkip = false; - - wxPyBeginBlockThreads(); + wxPyBlock_t blocked = wxPyBeginBlockThreads(); wxString className = event.GetClassInfo()->GetClassName(); - if (className == "wxPyEvent") - arg = ((wxPyEvent*)&event)->GetSelf(); - else if (className == "wxPyCommandEvent") - arg = ((wxPyCommandEvent*)&event)->GetSelf(); + // If the event is one of these types then pass the original + // event object instead of the one passed to us. + if ( className == wxT("wxPyEvent") ) { + arg = ((wxPyEvent*)&event)->GetSelf(); + checkSkip = ((wxPyEvent*)&event)->GetCloned(); + } + else if ( className == wxT("wxPyCommandEvent") ) { + arg = ((wxPyCommandEvent*)&event)->GetSelf(); + checkSkip = ((wxPyCommandEvent*)&event)->GetCloned(); + } else { - -// TODO: get rid of this ifdef by changing wxPyConstructObject to take a wxString -#if wxUSE_UNICODE - char *classNameAsChrStr = new char[className.Len()+1]; - unicodeToChar(&className, classNameAsChrStr); - arg = wxPyConstructObject((void*)&event, classNameAsChrStr); - delete [] classNameAsChrStr; -#else - arg = wxPyConstructObject((void*)&event, className); -#endif + arg = wxPyConstructObject((void*)&event, className); } - tuple = PyTuple_New(1); - PyTuple_SET_ITEM(tuple, 0, arg); - result = PyEval_CallObject(func, tuple); - Py_DECREF(tuple); - if (result) { - Py_DECREF(result); - PyErr_Clear(); // Just in case... - } else { + if (!arg) { PyErr_Print(); + } else { + // "intern" the pre/post method names to speed up the HasAttr + static PyObject* s_preName = NULL; + static PyObject* s_postName = NULL; + if (s_preName == NULL) { + s_preName = PyString_FromString(wxPy_PRECALLINIT); + s_postName = PyString_FromString(wxPy_POSTCALLCLEANUP); + } + + // Check if the event object needs some preinitialization + if (PyObject_HasAttr(arg, s_preName)) { + result = PyObject_CallMethodObjArgs(arg, s_preName, arg, NULL); + if ( result ) { + Py_DECREF(result); // result is ignored, but we still need to decref it + PyErr_Clear(); // Just in case... + } else { + PyErr_Print(); + } + } + + // Call the event handler, passing the event object + tuple = PyTuple_New(1); + PyTuple_SET_ITEM(tuple, 0, arg); // steals ref to arg + result = PyEval_CallObject(func, tuple); + if ( result ) { + Py_DECREF(result); // result is ignored, but we still need to decref it + PyErr_Clear(); // Just in case... + } else { + PyErr_Print(); + } + + // Check if the event object needs some post cleanup + if (PyObject_HasAttr(arg, s_postName)) { + result = PyObject_CallMethodObjArgs(arg, s_postName, arg, NULL); + if ( result ) { + Py_DECREF(result); // result is ignored, but we still need to decref it + PyErr_Clear(); // Just in case... + } else { + PyErr_Print(); + } + } + + if ( checkSkip ) { + // if the event object was one of our special types and + // it had been cloned, then we need to extract the Skipped + // value from the original and set it in the clone. + result = PyObject_CallMethod(arg, "GetSkipped", ""); + if ( result ) { + event.Skip(PyInt_AsLong(result)); + Py_DECREF(result); + } else { + PyErr_Print(); + } + } + Py_DECREF(tuple); } - wxPyEndBlockThreads(); + wxPyEndBlockThreads(blocked); } @@ -1014,7 +1686,6 @@ PyObject* PyFindClassWithAttr(PyObject *klass, PyObject *name) if (PyType_Check(klass)) { // new style classes // This code is borrowed/adapted from _PyType_Lookup in typeobject.c - // (TODO: This part is not tested yet, so I'm not sure it is correct...) PyTypeObject* type = (PyTypeObject*)klass; PyObject *mro, *res, *base, *dict; /* Look in tp_dict of types in MRO */ @@ -1089,7 +1760,7 @@ bool wxPyCallbackHelper::findCallback(const char* name) const { // not from a base class... if (PyMethod_Check(method) && (klass = PyMethod_GetDefiningClass(method, (char*)name)) != NULL && - ((klass == m_class) || PyClass_IsSubclass(klass, m_class))) { + ((klass == m_class) || PyObject_IsSubclass(klass, m_class))) { // ...then we'll save a pointer to the method so callCallback can call it. self->m_lastFound = method; @@ -1104,7 +1775,7 @@ bool wxPyCallbackHelper::findCallback(const char* name) const { int wxPyCallbackHelper::callCallback(PyObject* argTuple) const { PyObject* result; - int retval = FALSE; + int retval = false; result = callCallbackObj(argTuple); if (result) { // Assumes an integer return type... @@ -1116,7 +1787,7 @@ int wxPyCallbackHelper::callCallback(PyObject* argTuple) const { } // Invoke the Python callable object, returning the raw PyObject return -// value. Caller should DECREF the return value and also call PyEval_SaveThread. +// value. Caller should DECREF the return value and also manage the GIL. PyObject* wxPyCallbackHelper::callCallbackObj(PyObject* argTuple) const { PyObject* result; @@ -1154,10 +1825,10 @@ PyObject* wxPyCBH_callCallbackObj(const wxPyCallbackHelper& cbh, PyObject* argTu void wxPyCBH_delete(wxPyCallbackHelper* cbh) { if (cbh->m_incRef) { - wxPyBeginBlockThreads(); + wxPyBlock_t blocked = wxPyBeginBlockThreads(); Py_XDECREF(cbh->m_self); Py_XDECREF(cbh->m_class); - wxPyEndBlockThreads(); + wxPyEndBlockThreads(blocked); } } @@ -1171,26 +1842,26 @@ void wxPyCBH_delete(wxPyCallbackHelper* cbh) { wxPyEvtSelfRef::wxPyEvtSelfRef() { //m_self = Py_None; // **** We don't do normal ref counting to prevent //Py_INCREF(m_self); // circular loops... - m_cloned = FALSE; + m_cloned = false; } wxPyEvtSelfRef::~wxPyEvtSelfRef() { - wxPyBeginBlockThreads(); + wxPyBlock_t blocked = wxPyBeginBlockThreads(); if (m_cloned) Py_DECREF(m_self); - wxPyEndBlockThreads(); + wxPyEndBlockThreads(blocked); } void wxPyEvtSelfRef::SetSelf(PyObject* self, bool clone) { - wxPyBeginBlockThreads(); + wxPyBlock_t blocked = wxPyBeginBlockThreads(); if (m_cloned) Py_DECREF(m_self); m_self = self; if (clone) { Py_INCREF(m_self); - m_cloned = TRUE; + m_cloned = true; } - wxPyEndBlockThreads(); + wxPyEndBlockThreads(blocked); } PyObject* wxPyEvtSelfRef::GetSelf() const { @@ -1203,15 +1874,15 @@ IMPLEMENT_ABSTRACT_CLASS(wxPyEvent, wxEvent); IMPLEMENT_ABSTRACT_CLASS(wxPyCommandEvent, wxCommandEvent); -wxPyEvent::wxPyEvent(int id) - : wxEvent(id) { +wxPyEvent::wxPyEvent(int winid, wxEventType commandType) + : wxEvent(winid, commandType) { } wxPyEvent::wxPyEvent(const wxPyEvent& evt) : wxEvent(evt) { - SetSelf(evt.m_self, TRUE); + SetSelf(evt.m_self, true); } @@ -1227,7 +1898,7 @@ wxPyCommandEvent::wxPyCommandEvent(wxEventType commandType, int id) wxPyCommandEvent::wxPyCommandEvent(const wxPyCommandEvent& evt) : wxCommandEvent(evt) { - SetSelf(evt.m_self, TRUE); + SetSelf(evt.m_self, true); } @@ -1237,84 +1908,47 @@ wxPyCommandEvent::~wxPyCommandEvent() { -//--------------------------------------------------------------------------- -//--------------------------------------------------------------------------- - - -wxPyTimer::wxPyTimer(PyObject* callback) { - func = callback; - Py_INCREF(func); -} - -wxPyTimer::~wxPyTimer() { - wxPyBeginBlockThreads(); - Py_DECREF(func); - wxPyEndBlockThreads(); -} - -void wxPyTimer::Notify() { - if (!func || func == Py_None) { - wxTimer::Notify(); - } - else { - wxPyBeginBlockThreads(); - - PyObject* result; - PyObject* args = Py_BuildValue("()"); - - result = PyEval_CallObject(func, args); - Py_DECREF(args); - if (result) { - Py_DECREF(result); - PyErr_Clear(); - } else { - PyErr_Print(); - } - - wxPyEndBlockThreads(); - } -} - - //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- -// Convert a wxList to a Python List +// Convert a wxList to a Python List, only works for lists of wxObjects -PyObject* wxPy_ConvertList(wxListBase* list, const char* className) { +PyObject* wxPy_ConvertList(wxListBase* listbase) { + wxList* list = (wxList*)listbase; // this is probably bad... PyObject* pyList; PyObject* pyObj; wxObject* wxObj; - wxNode* node = list->First(); + wxNode* node = list->GetFirst(); - wxPyBeginBlockThreads(); + wxPyBlock_t blocked = wxPyBeginBlockThreads(); pyList = PyList_New(0); while (node) { - wxObj = node->Data(); - pyObj = wxPyMake_wxObject(wxObj); //wxPyConstructObject(wxObj, className); + wxObj = node->GetData(); + pyObj = wxPyMake_wxObject(wxObj,false); PyList_Append(pyList, pyObj); - node = node->Next(); + node = node->GetNext(); } - wxPyEndBlockThreads(); + wxPyEndBlockThreads(blocked); return pyList; } //---------------------------------------------------------------------- long wxPyGetWinHandle(wxWindow* win) { + #ifdef __WXMSW__ return (long)win->GetHandle(); #endif - // Find and return the actual X-Window. -#ifdef __WXGTK__ - if (win->m_wxwindow) { - GdkWindowPrivate* bwin = (GdkWindowPrivate*)GTK_PIZZA(win->m_wxwindow)->bin_window; - if (bwin) { - return (long)bwin->xwindow; - } - } +#if defined(__WXGTK__) || defined(__WXX11) + return (long)GetXWindow(win); +#endif + +#ifdef __WXMAC__ + //return (long)MAC_WXHWND(win->MacGetTopLevelWindowRef()); + return (long)win->GetHandle(); #endif + return 0; } @@ -1322,49 +1956,120 @@ long wxPyGetWinHandle(wxWindow* win) { // Some helper functions for typemaps in my_typemaps.i, so they won't be // included in every file over and over again... -#if PYTHON_API_VERSION >= 1009 - static char* wxStringErrorMsg = "String or Unicode type required"; -#else - static char* wxStringErrorMsg = "String type required"; -#endif - - wxString* wxString_in_helper(PyObject* source) { - wxString* target; -#if PYTHON_API_VERSION >= 1009 // Have Python unicode API + wxString* target = NULL; + if (!PyString_Check(source) && !PyUnicode_Check(source)) { - PyErr_SetString(PyExc_TypeError, wxStringErrorMsg); + PyErr_SetString(PyExc_TypeError, "String or Unicode type required"); return NULL; } #if wxUSE_UNICODE - if (PyUnicode_Check(source)) { - target = new wxString(PyUnicode_AS_UNICODE(source)); - } else { - // It is a string, transform to unicode - PyObject *tempUniStr = PyObject_Unicode(source); - target = new wxString(PyUnicode_AS_UNICODE(tempUniStr)); - Py_DECREF(tempUniStr); + PyObject* uni = source; + if (PyString_Check(source)) { + uni = PyUnicode_FromEncodedObject(source, wxPyDefaultEncoding, "strict"); + if (PyErr_Occurred()) return NULL; } + target = new wxString(); + size_t len = PyUnicode_GET_SIZE(uni); + if (len) { + PyUnicode_AsWideChar((PyUnicodeObject*)uni, target->GetWriteBuf(len), len); + target->UngetWriteBuf(len); + } + + if (PyString_Check(source)) + Py_DECREF(uni); #else - char* tmpPtr; int tmpSize; - if (PyString_AsStringAndSize(source, &tmpPtr, &tmpSize) == -1) { - PyErr_SetString(PyExc_TypeError, "Unable to convert string"); - return NULL; + // Convert to a string object if it isn't already, then to wxString + PyObject* str = source; + if (PyUnicode_Check(source)) { + str = PyUnicode_AsEncodedString(source, wxPyDefaultEncoding, "strict"); + if (PyErr_Occurred()) return NULL; + } + else if (!PyString_Check(source)) { + str = PyObject_Str(source); + if (PyErr_Occurred()) return NULL; } + char* tmpPtr; int tmpSize; + PyString_AsStringAndSize(str, &tmpPtr, &tmpSize); target = new wxString(tmpPtr, tmpSize); + + if (!PyString_Check(source)) + Py_DECREF(str); #endif // wxUSE_UNICODE -#else // No Python unicode API (1.5.2) - if (!PyString_Check(source)) { - PyErr_SetString(PyExc_TypeError, wxStringErrorMsg); - return NULL; + return target; +} + + +// Similar to above except doesn't use "new" and doesn't set an exception +wxString Py2wxString(PyObject* source) +{ + wxString target; + +#if wxUSE_UNICODE + // Convert to a unicode object, if not already, then to a wxString + PyObject* uni = source; + if (!PyUnicode_Check(source)) { + uni = PyUnicode_FromEncodedObject(source, wxPyDefaultEncoding, "strict"); + if (PyErr_Occurred()) return wxEmptyString; // TODO: should we PyErr_Clear? } - target = new wxString(PyString_AS_STRING(source), PyString_GET_SIZE(source)); -#endif + size_t len = PyUnicode_GET_SIZE(uni); + if (len) { + PyUnicode_AsWideChar((PyUnicodeObject*)uni, target.GetWriteBuf(len), len); + target.UngetWriteBuf(); + } + + if (!PyUnicode_Check(source)) + Py_DECREF(uni); +#else + // Convert to a string object if it isn't already, then to wxString + PyObject* str = source; + if (PyUnicode_Check(source)) { + str = PyUnicode_AsEncodedString(source, wxPyDefaultEncoding, "strict"); + if (PyErr_Occurred()) return wxEmptyString; // TODO: should we PyErr_Clear? + } + else if (!PyString_Check(source)) { + str = PyObject_Str(source); + if (PyErr_Occurred()) return wxEmptyString; // TODO: should we PyErr_Clear? + } + char* tmpPtr; int tmpSize; + PyString_AsStringAndSize(str, &tmpPtr, &tmpSize); + target = wxString(tmpPtr, tmpSize); + + if (!PyString_Check(source)) + Py_DECREF(str); +#endif // wxUSE_UNICODE + return target; } +// Make either a Python String or Unicode object, depending on build mode +PyObject* wx2PyString(const wxString& src) +{ + PyObject* str; +#if wxUSE_UNICODE + str = PyUnicode_FromWideChar(src.c_str(), src.Len()); +#else + str = PyString_FromStringAndSize(src.c_str(), src.Len()); +#endif + return str; +} + + + +void wxSetDefaultPyEncoding(const char* encoding) +{ + strncpy(wxPyDefaultEncoding, encoding, DEFAULTENCODING_SIZE); +} + +const char* wxGetDefaultPyEncoding() +{ + return wxPyDefaultEncoding; +} + +//---------------------------------------------------------------------- + byte* byte_LIST_helper(PyObject* source) { if (!PyList_Check(source)) { @@ -1470,7 +2175,7 @@ static inline bool wxPointFromObjects(PyObject* o1, PyObject* o2, wxPoint* point point->y = (int)PyFloat_AS_DOUBLE(o2); return true; } - if (PyInstance_Check(o1) || PyInstance_Check(o2)) { + if (wxPySwigInstance_Check(o1) || wxPySwigInstance_Check(o2)) { // TODO: Why??? // Disallow instances because they can cause havok return false; } @@ -1528,9 +2233,9 @@ wxPoint* wxPoint_LIST_helper(PyObject* source, int *count) { goto error2; } } - else if (PyInstance_Check(o)) { + else if (wxPySwigInstance_Check(o)) { wxPoint* pt; - if (SWIG_GetPtrObj(o, (void **)&pt, "_wxPoint_p")) { + if (! wxPyConvertSwigPtr(o, (void **)&pt, wxT("wxPoint"))) { goto error2; } temp[x] = *pt; @@ -1560,7 +2265,7 @@ error2: if (!isFast) Py_DECREF(o); error1: - delete temp; + delete [] temp; error0: PyErr_SetString(PyExc_TypeError, "Expected a sequence of length-2 sequences or wxPoints."); return NULL; @@ -1582,10 +2287,10 @@ wxBitmap** wxBitmap_LIST_helper(PyObject* source) { } for (int x=0; x