X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/a7fa65b96e0076675dbc6d34a7849a20e67946a0..9745cf4d6ca2370d71763b8e5f6e446ca2881571:/wxPython/src/helpers.cpp diff --git a/wxPython/src/helpers.cpp b/wxPython/src/helpers.cpp index 9d12d8962a..dfafce7dc5 100644 --- a/wxPython/src/helpers.cpp +++ b/wxPython/src/helpers.cpp @@ -4,40 +4,98 @@ // // 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 "wx/wxPython/wxPython_int.h" +#include "wx/wxPython/pyistream.h" +#include "wx/wxPython/swigver.h" +#include "wx/wxPython/twoitem.h" #ifdef __WXMSW__ #include -#undef FindWindow -#undef GetCharWidth -#undef LoadAccelerators -#undef GetClassInfo -#undef GetClassName +#include +#include #endif #ifdef __WXGTK__ +#include +#include #include #include +#ifdef __WXGTK20__ #include -//#include -//#include -//#include +#else +#include +#endif +#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 < 1009 && wxUSE_UNICODE +#error Python must support Unicode to use wxWindows Unicode +#endif + +//---------------------------------------------------------------------- + +wxPyApp* wxPythonApp = NULL; // Global instance of application object +bool wxPyDoCleanup = false; +bool wxPyDoingCleanup = false; -//extern GtkWidget *wxRootWindow; +#ifdef WXP_WITH_THREAD +#if !wxPyUSE_GIL_STATE +struct wxPyThreadState { + unsigned long tid; + PyThreadState* tstate; + + wxPyThreadState(unsigned long _tid=0, PyThreadState* _tstate=NULL) + : tid(_tid), tstate(_tstate) {} +}; + +#include +WX_DECLARE_OBJARRAY(wxPyThreadState, wxPyThreadStateArray); +#include +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... @@ -45,706 +103,1954 @@ // 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 //---------------------------------------------------------------------- -// Class for implementing the wxp main application shell. +// Classes for implementing the wxp main application shell. //---------------------------------------------------------------------- -wxPyApp *wxPythonApp = NULL; // Global instance of application object +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() -bool wxPyApp::OnInit(void) { - return FALSE; +// This one isn't acutally called... We fake it with _BootstrapApp +bool wxPyApp::OnInit() { + return false; } -int wxPyApp::MainLoop(void) { + +int wxPyApp::MainLoop() { int retval = 0; DeletePendingObjects(); -#ifdef __WXGTK__ - m_initialized = wxTopLevelWindows.GetCount() != 0; -#endif + bool initialized = wxTopLevelWindows.GetCount() != 0; + if (initialized) { + if ( m_exitOnFrameDelete == Later ) { + m_exitOnFrameDelete = Yes; + } - if (Initialized()) { retval = wxApp::MainLoop(); - wxPythonApp->OnExit(); + OnExit(); } return retval; } -//--------------------------------------------------------------------- -//---------------------------------------------------------------------- +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; +} -#ifdef __WXMSW__ -#include "wx/msw/msvcrt.h" -#endif + +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; +} -int WXDLLEXPORT wxEntryStart( int argc, char** argv ); -int WXDLLEXPORT wxEntryInitGui(); -void WXDLLEXPORT wxEntryCleanup(); +void wxPyApp::ExitMainLoop() { + bool found; + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + if ((found = wxPyCBH_findCallback(m_myInst, "ExitMainLoop"))) + wxPyCBH_callCallback(m_myInst, Py_BuildValue("()")); + wxPyEndBlockThreads(blocked); + if (! found) + wxApp::ExitMainLoop(); +} -#ifdef WXP_WITH_THREAD -PyThreadState* wxPyEventThreadState = NULL; + +#ifdef __WXDEBUG__ +void wxPyApp::OnAssertFailure(const wxChar *file, + int line, + const wxChar *func, + 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 ( func && *func ) + buf << wxT(" in ") << func << wxT("()"); + if (msg != NULL) + buf << wxT(": ") << 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 at %s(%d)"), cond, file, line); + if ( func && *func ) + buf << wxT(" in ") << func << wxT("()"); + if (msg != NULL) + buf << wxT(": ") << 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 ( func && *func ) + buf << wxT(" in ") << func << wxT("()"); + if (msg != NULL) + buf << wxT(": ") << msg; + wxLogDebug(buf); + } + + // do the normal wx assert dialog? + if (m_assertMode & wxPYAPP_ASSERT_DIALOG) + wxApp::OnAssertFailure(file, line, func, cond, msg); + } +} #endif + // For catching Apple Events +void wxPyApp::MacOpenFile(const wxString &fileName) +{ + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + if (wxPyCBH_findCallback(m_myInst, "MacOpenFile")) { + PyObject* s = wx2PyString(fileName); + wxPyCBH_callCallback(m_myInst, Py_BuildValue("(O)", s)); + Py_DECREF(s); + } + wxPyEndBlockThreads(blocked); +} + +void wxPyApp::MacPrintFile(const wxString &fileName) +{ + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + if (wxPyCBH_findCallback(m_myInst, "MacPrintFile")) { + PyObject* s = wx2PyString(fileName); + wxPyCBH_callCallback(m_myInst, Py_BuildValue("(O)", s)); + Py_DECREF(s); + } + wxPyEndBlockThreads(blocked); +} + +void wxPyApp::MacNewFile() +{ + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + if (wxPyCBH_findCallback(m_myInst, "MacNewFile")) + wxPyCBH_callCallback(m_myInst, Py_BuildValue("()")); + wxPyEndBlockThreads(blocked); +} -// This is where we pick up the first part of the wxEntry functionality... -// The rest is in __wxStart and __wxCleanup. This function is called when -// wxcmodule is imported. (Before there is a wxApp object.) -void __wxPreStart() +void wxPyApp::MacReopenApp() { + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + if (wxPyCBH_findCallback(m_myInst, "MacReopenApp")) + wxPyCBH_callCallback(m_myInst, Py_BuildValue("()")); + wxPyEndBlockThreads(blocked); +} -#ifdef __WXMSW__ -// wxCrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF); + +/*static*/ +bool wxPyApp::GetMacSupportPCMenuShortcuts() { + return 0; +} + +/*static*/ +long wxPyApp::GetMacAboutMenuItemId() { +#ifdef __WXMAC__ + return s_macAboutMenuItemId; +#else + return 0; #endif +} -#ifdef WXP_WITH_THREAD -#if 0 // OLD THREAD STUFF - PyEval_InitThreads(); - wxPyEventThreadState = PyThreadState_Get(); +/*static*/ +long wxPyApp::GetMacPreferencesMenuItemId() { +#ifdef __WXMAC__ + return s_macPreferencesMenuItemId; #else - PyEval_InitThreads(); - wxPyEventThreadState = PyThreadState_New(PyThreadState_Get()->interp); + return 0; +#endif +} + +/*static*/ +long wxPyApp::GetMacExitMenuItemId() { +#ifdef __WXMAC__ + return s_macExitMenuItemId; +#else + return 0; #endif +} + +/*static*/ +wxString wxPyApp::GetMacHelpMenuTitleName() { +#ifdef __WXMAC__ + return s_macHelpMenuTitleName; +#else + return wxEmptyString; #endif +} - // Bail out if there is already windows created. This means that the - // toolkit has already been initialized, as in embedding wxPython in - // a C++ wxWindows app. - if (wxTopLevelWindows.Number() > 0) - return; +/*static*/ +void wxPyApp::SetMacSupportPCMenuShortcuts(bool) { +} +/*static*/ +void wxPyApp::SetMacAboutMenuItemId(long val) { +#ifdef __WXMAC__ + s_macAboutMenuItemId = val; +#endif +} - PyObject* sysargv = PySys_GetObject("argv"); - int argc = PyList_Size(sysargv); - char** argv = new char*[argc+1]; - int x; - for(x=0; x 0) { - PyErr_SetString(PyExc_TypeError, "Only 1 wxApp per process!"); - return NULL; - } + blocked = wxPyBeginBlockThreads(); + if (! result) { + PyErr_SetString(PyExc_SystemError, + "wxEntryStart failed, unable to initialize wxWidgets!" +#ifdef __WXGTK__ + " (Is DISPLAY set properly?)" #endif + ); + goto error; + } - // This is the next part of the wxEntry functionality... - PyObject* sysargv = PySys_GetObject("argv"); - int argc = PyList_Size(sysargv); - char** argv = new char*[argc+1]; - int x; - for(x=0; xargc = argc; - wxPythonApp->argv = argv; - - wxEntryInitGui(); + // 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 - // Call the Python App's OnInit function - arglist = PyTuple_New(0); - result = PyEval_CallObject(onInitFunc, arglist); - if (!result) { // an exception was raised. - return NULL; +// wxSystemOptions::SetOption(wxT("mac.textcontrol-use-mlte"), 1); + + wxPyEndBlockThreads(blocked); + haveInitialized = true; + } + else { + this->argc = 0; + this->argv = NULL; } + + + // It's now ok to generate exceptions for assertion errors. + wxPythonApp->SetStartupComplete(true); + + + // Call the Python wxApp's OnPreInit and OnInit functions + blocked = wxPyBeginBlockThreads(); + if (wxPyCBH_findCallback(m_myInst, "OnPreInit")) { + PyObject* method = m_myInst.GetLastFound(); + PyObject* argTuple = PyTuple_New(0); + retval = PyEval_CallObject(method, argTuple); + m_myInst.clearRecursionGuard(method); + Py_DECREF(argTuple); + Py_DECREF(method); + if (retval == NULL) + goto error; + } + if (wxPyCBH_findCallback(m_myInst, "OnInit")) { + + PyObject* method = m_myInst.GetLastFound(); + PyObject* argTuple = PyTuple_New(0); + retval = PyEval_CallObject(method, argTuple); + m_myInst.clearRecursionGuard(method); + Py_DECREF(argTuple); + Py_DECREF(method); + if (retval == NULL) + // Don't PyErr_Print here, let the exception in this case go back + // up to the wx.PyApp.__init__ scope. + goto error; - if (! PyInt_Check(result)) { - PyErr_SetString(PyExc_TypeError, "OnInit should return a boolean value"); - return NULL; + 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 + - Py_INCREF(Py_None); - return Py_None; +inline const char* dropwx(const char* name) { + if (name[0] == 'w' && name[1] == 'x') + return name+2; + else + return name; } -void __wxCleanup() { - wxEntryCleanup(); +//---------------------------------------------------------------------- + +// 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 + + // Ensure that the build options in the DLL (or whatever) match this build + wxApp::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE, "wxPython"); + + wxInitAllImageHandlers(); } -PyObject* wxPython_dict; -PyObject* __wxSetDictionary(PyObject* /* self */, PyObject* args) +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 +} + + +// 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; } + + if (! wxPyPtrTypeMap) + 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 __WXQT__ -#define wxPlatform "__WXQT__" +#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 - PyDict_SetItemString(wxPython_dict, "wxPlatform", PyString_FromString(wxPlatform)); +#ifdef __WXDEBUG__ + int wxdebug = 1; +#else + int wxdebug = 0; +#endif - Py_INCREF(Py_None); - return Py_None; -} + // 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; -//--------------------------------------------------------------------------- +#define _AddInfoString(st) \ + obj = PyString_FromString(st); \ + PyList_Append(PlatInfo, obj); \ + Py_DECREF(obj) -PyObject* wxPyConstructObject(void* ptr, - const char* className, - int setThisOwn) { - PyObject* obj; - PyObject* arg; + _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 + _AddInfoString(wxPy_SWIG_VERSION); +#ifdef __WXMAC__ + #if wxMAC_USE_CORE_GRAPHICS + _AddInfoString("mac-cg"); + #else + _AddInfoString("mac-qd"); + #endif + #if wxMAC_USE_NATIVE_TOOLBAR + _AddInfoString("mac-native-tb"); + #else + _AddInfoString("mac-no-native-tb"); + #endif +#endif + +#undef _AddInfoString - if (!ptr) { - Py_INCREF(Py_None); - return Py_None; - } + PyObject* PlatInfoTuple = PyList_AsTuple(PlatInfo); + Py_DECREF(PlatInfo); + PyDict_SetItemString(wxPython_dict, "PlatformInfo", PlatInfoTuple); - char buff[64]; // should always be big enough... - char swigptr[64]; + RETURN_NONE(); +} - sprintf(buff, "_%s_p", className); - SWIG_MakePtr(swigptr, ptr, buff); - sprintf(buff, "%sPtr", className); - PyObject* classobj = PyDict_GetItemString(wxPython_dict, buff); - if (! classobj) { - //Py_INCREF(Py_None); - //return Py_None; - char temp[128]; - sprintf(temp, - "*** Unknown class name %s, tell Robin about it please ***", - buff); - obj = PyString_FromString(temp); - return obj; - } - arg = Py_BuildValue("(s)", swigptr); - obj = PyInstance_New(classobj, arg, NULL); - Py_DECREF(arg); +//--------------------------------------------------------------------------- - if (setThisOwn) { - PyObject* one = PyInt_FromLong(1); - PyObject_SetAttrString(obj, "thisown", one); - Py_DECREF(one); - } +// 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. - return obj; +bool wxPyCheckForApp() { + if (wxTheApp != NULL) + return true; + else { + PyErr_SetString(wxPyNoAppError, "The wx.App object must be created first!"); + return false; + } } //--------------------------------------------------------------------------- -static PyThreadState* myPyThreadState_Get() { - PyThreadState* current; - current = PyThreadState_Swap(NULL); - PyThreadState_Swap(current); - return current; +void wxPyUserData_dtor(wxPyUserData* self) { + if (! wxPyDoingCleanup) { + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + Py_DECREF(self->m_obj); + self->m_obj = NULL; + wxPyEndBlockThreads(blocked); + } } -bool wxPyRestoreThread() { - // NOTE: The Python API docs state that if a thread already has the - // interpreter lock and calls PyEval_RestoreThread again a deadlock - // occurs, so I put in this code as a guard condition since there are - // many possibilites for nested events and callbacks in wxPython. If - // The current thread is our thread, then we can assume that we - // already have the lock. (I hope!) - // -#ifdef WXP_WITH_THREAD -#if 0 // OLD THREAD STUFF - if (wxPyEventThreadState != myPyThreadState_Get()) { - PyEval_RestoreThread(wxPyEventThreadState); - return TRUE; - } - else -#else - if (wxPyEventThreadState != myPyThreadState_Get()) { - PyEval_AcquireThread(wxPyEventThreadState); - return TRUE; +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; } - else -#endif -#endif - return FALSE; } -void wxPySaveThread(bool doSave) { -#ifdef WXP_WITH_THREAD -#if 0 // OLD THREAD STUFF - if (doSave) { - wxPyEventThreadState = PyEval_SaveThread(); - } -#else - if (doSave) { - PyEval_ReleaseThread(wxPyEventThreadState); + +// 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); } -#endif -#endif -} -//--------------------------------------------------------------------------- + // 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!?!?!")); -IMPLEMENT_ABSTRACT_CLASS(wxPyCallback, wxObject); + // 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); + } + } -wxPyCallback::wxPyCallback(PyObject* func) { - m_func = func; - Py_INCREF(m_func); + // m_obj is DECREF'd in the base class dtor... + wxPyEndBlockThreads(blocked); } -wxPyCallback::wxPyCallback(const wxPyCallback& other) { - m_func = other.m_func; - Py_INCREF(m_func); -} -wxPyCallback::~wxPyCallback() { - bool doSave = wxPyRestoreThread(); - Py_DECREF(m_func); - wxPySaveThread(doSave); +//--------------------------------------------------------------------------- +// Stuff used by OOR to find the right wxPython class type to return and to +// build it. + + +// 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)); } -// This function is used for all events destined for Python event handlers. -void wxPyCallback::EventThunker(wxEvent& event) { - wxPyCallback* cb = (wxPyCallback*)event.m_callbackUserData; - PyObject* func = cb->m_func; - PyObject* result; - PyObject* arg; - PyObject* tuple; - - bool doSave = wxPyRestoreThread(); - wxString className = event.GetClassInfo()->GetClassName(); +PyObject* wxPyMake_wxObject(wxObject* source, bool setThisOwn, bool checkEvtHandler) { + PyObject* target = NULL; + bool isEvtHandler = false; + bool isSizer = false; - if (className == "wxPyEvent") - arg = ((wxPyEvent*)&event)->GetSelf(); - else if (className == "wxPyCommandEvent") - arg = ((wxPyCommandEvent*)&event)->GetSelf(); - else - arg = wxPyConstructObject((void*)&event, className); + 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; + wxEvtHandler* eh = (wxEvtHandler*)source; + wxPyOORClientData* data = (wxPyOORClientData*)eh->GetClientObject(); + if (data) { + target = data->m_obj; + if (target) + Py_INCREF(target); + } + } - 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(); - } else { - PyErr_Print(); - } - wxPySaveThread(doSave); + // 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. 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((void*)source, name, setThisOwn); + if (target && isEvtHandler) + ((wxEvtHandler*)source)->SetClientObject(new wxPyOORClientData(target)); + if (target && isSizer) + ((wxSizer*)source)->SetClientObject(new wxPyOORClientData(target)); + } else { + wxString msg(wxT("wxPython class not found for ")); + msg += source->GetClassInfo()->GetClassName(); + PyErr_SetString(PyExc_NameError, msg.mbc_str()); + target = NULL; + } + } + } else { // source was NULL so return None. + Py_INCREF(Py_None); target = Py_None; + } + return target; } -//---------------------------------------------------------------------- +PyObject* wxPyMake_wxSizer(wxSizer* source, bool setThisOwn) { -wxPyCallbackHelper::wxPyCallbackHelper(const wxPyCallbackHelper& other) { - m_lastFound = NULL; - m_self = other.m_self; - m_class = other.m_class; - if (m_self) { - Py_INCREF(m_self); - Py_INCREF(m_class); - } + return wxPyMake_wxObject(source, setThisOwn); } -void wxPyCallbackHelper::setSelf(PyObject* self, PyObject* klass, int incref) { - m_self = self; - m_class = klass; - m_incRef = incref; - if (incref) { - Py_INCREF(m_self); - Py_INCREF(m_class); - } +//--------------------------------------------------------------------------- + + +#ifdef WXP_WITH_THREAD +#if !wxPyUSE_GIL_STATE + +inline +unsigned long wxPyGetCurrentThreadId() { + return wxThread::GetCurrentId(); } +static wxPyThreadState gs_shutdownTState; -// If the object (m_self) has an attibute of the given name, and if that -// attribute is a method, and if that method's class is not from a base class, -// then we'll save a pointer to the method so callCallback can call it. -bool wxPyCallbackHelper::findCallback(const char* name) const { - wxPyCallbackHelper* self = (wxPyCallbackHelper*)this; // cast away const - self->m_lastFound = NULL; - if (m_self && PyObject_HasAttrString(m_self, (char*)name)) { - PyObject* method; - method = PyObject_GetAttrString(m_self, (char*)name); +static +wxPyThreadState* wxPyGetThreadState() { + if (wxPyTMutex == NULL) // Python is shutting down... + return &gs_shutdownTState; - if (PyMethod_Check(method) && - ((PyMethod_GET_CLASS(method) == m_class) || - PyClass_IsSubclass(PyMethod_GET_CLASS(method), m_class))) { + unsigned long ctid = wxPyGetCurrentThreadId(); + wxPyThreadState* tstate = NULL; - self->m_lastFound = method; - } - else { - Py_DECREF(method); + wxPyTMutex->Lock(); + for(size_t i=0; i < wxPyTStates->GetCount(); i++) { + wxPyThreadState& info = wxPyTStates->Item(i); + if (info.tid == ctid) { + tstate = &info; + break; } } - return m_lastFound != NULL; + wxPyTMutex->Unlock(); + wxASSERT_MSG(tstate, wxT("PyThreadState should not be NULL!")); + return tstate; } -int wxPyCallbackHelper::callCallback(PyObject* argTuple) const { - PyObject* result; - int retval = FALSE; - - result = callCallbackObj(argTuple); - if (result) { // Assumes an integer return type... - retval = PyInt_AsLong(result); - Py_DECREF(result); - PyErr_Clear(); // forget about it if it's not... +static +void wxPySaveThreadState(PyThreadState* tstate) { + if (wxPyTMutex == NULL) { // Python is shutting down, assume a single thread... + gs_shutdownTState.tstate = tstate; + return; } - return retval; + unsigned long ctid = wxPyGetCurrentThreadId(); + wxPyTMutex->Lock(); + for(size_t i=0; i < wxPyTStates->GetCount(); i++) { + wxPyThreadState& info = wxPyTStates->Item(i); + if (info.tid == ctid) { +#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; + } + } + // not found, so add it... + wxPyTStates->Add(new wxPyThreadState(ctid, tstate)); + wxPyTMutex->Unlock(); } -// Invoke the Python callable object, returning the raw PyObject return -// value. Caller should DECREF the return value and also call PyEval_SaveThread. -PyObject* wxPyCallbackHelper::callCallbackObj(PyObject* argTuple) const { - PyObject* result; +#endif +#endif - // Save a copy of the pointer in case the callback generates another - // callback. In that case m_lastFound will have a different value when - // it gets back here... - PyObject* method = m_lastFound; - result = PyEval_CallObject(method, argTuple); - Py_DECREF(argTuple); - Py_DECREF(method); - if (!result) { - PyErr_Print(); - } - return result; -} +// Calls from Python to wxWindows code are wrapped in calls to these +// functions: -void wxPyCBH_setSelf(wxPyCallbackHelper& cbh, PyObject* self, PyObject* klass, int incref) { - cbh.setSelf(self, klass, incref); +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; +#endif } -bool wxPyCBH_findCallback(const wxPyCallbackHelper& cbh, const char* name) { - return cbh.findCallback(name); +void wxPyEndAllowThreads(PyThreadState* saved) { +#ifdef WXP_WITH_THREAD + PyEval_RestoreThread(saved); // Py_END_ALLOW_THREADS; +#endif } -int wxPyCBH_callCallback(const wxPyCallbackHelper& cbh, PyObject* argTuple) { - return cbh.callCallback(argTuple); -} -PyObject* wxPyCBH_callCallbackObj(const wxPyCallbackHelper& cbh, PyObject* argTuple) { - return cbh.callCallbackObj(argTuple); + +// Calls from wxWindows back to Python code, or even any PyObject +// manipulations, PyDECREF's and etc. are wrapped in calls to these functions: + +wxPyBlock_t wxPyBeginBlockThreads() { +#ifdef WXP_WITH_THREAD + 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 wxPyCBH_delete(wxPyCallbackHelper* cbh) { - bool doSave = wxPyRestoreThread(); - if (cbh->m_incRef) { - Py_XDECREF(cbh->m_self); - Py_XDECREF(cbh->m_class); +void wxPyEndBlockThreads(wxPyBlock_t blocked) { +#ifdef WXP_WITH_THREAD + 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(); } - wxPySaveThread(doSave); +#endif +#endif } + //--------------------------------------------------------------------------- -//--------------------------------------------------------------------------- -// These classes can be derived from in Python and passed through the event -// system without losing anything. They do this by keeping a reference to -// themselves and some special case handling in wxPyCallback::EventThunker. +// wxPyInputStream and wxPyCBInputStream methods -wxPyEvtSelfRef::wxPyEvtSelfRef() { - //m_self = Py_None; // **** We don't do normal ref counting to prevent - //Py_INCREF(m_self); // circular loops... - m_cloned = FALSE; +void wxPyInputStream::close() { + /* do nothing for now */ } -wxPyEvtSelfRef::~wxPyEvtSelfRef() { - bool doSave = wxPyRestoreThread(); - if (m_cloned) - Py_DECREF(m_self); - wxPySaveThread(doSave); +void wxPyInputStream::flush() { + /* do nothing for now */ } -void wxPyEvtSelfRef::SetSelf(PyObject* self, bool clone) { - bool doSave = wxPyRestoreThread(); - if (m_cloned) - Py_DECREF(m_self); - m_self = self; - if (clone) { - Py_INCREF(m_self); - m_cloned = TRUE; - } - wxPySaveThread(doSave); +bool wxPyInputStream::eof() { + if (m_wxis) + return m_wxis->Eof(); + else + return true; } -PyObject* wxPyEvtSelfRef::GetSelf() const { - Py_INCREF(m_self); - return m_self; +wxPyInputStream::~wxPyInputStream() { + if (m_wxis) + delete m_wxis; } -wxPyEvent::wxPyEvent(int id) - : wxEvent(id) { -} - -wxPyEvent::~wxPyEvent() { -} -// This one is so the event object can be Cloned... -void wxPyEvent::CopyObject(wxObject& dest) const { - wxEvent::CopyObject(dest); - ((wxPyEvent*)&dest)->SetSelf(m_self, TRUE); -} +PyObject* wxPyInputStream::read(int size) { + PyObject* obj = NULL; + wxMemoryBuffer buf; + const int BUFSIZE = 1024; -IMPLEMENT_DYNAMIC_CLASS(wxPyEvent, wxEvent); + // 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) { + // read while bytes are available on the stream + while ( m_wxis->CanRead() ) { + m_wxis->Read(buf.GetAppendBuf(BUFSIZE), BUFSIZE); + buf.UngetAppendBuf(m_wxis->LastRead()); + } -wxPyCommandEvent::wxPyCommandEvent(wxEventType commandType, int id) - : wxCommandEvent(commandType, id) { -} + } else { // Read only size number of characters + m_wxis->Read(buf.GetWriteBuf(size), size); + buf.UngetWriteBuf(m_wxis->LastRead()); + } -wxPyCommandEvent::~wxPyCommandEvent() { + // 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()); + } + wxPyEndBlockThreads(blocked); + return obj; } -void wxPyCommandEvent::CopyObject(wxObject& dest) const { - wxCommandEvent::CopyObject(dest); - ((wxPyCommandEvent*)&dest)->SetSelf(m_self, TRUE); -} +PyObject* wxPyInputStream::readline(int size) { + PyObject* obj = NULL; + wxMemoryBuffer buf; + int i; + char ch; -IMPLEMENT_DYNAMIC_CLASS(wxPyCommandEvent, wxCommandEvent); + // 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->CanRead()) && ((size < 0) || (i < size)); i++) { + ch = m_wxis->GetC(); + buf.AppendByte(ch); + } + // errorcheck + 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((char*)buf.GetData(), buf.GetDataLen()); + } + wxPyEndBlockThreads(blocked); + return obj; +} -//--------------------------------------------------------------------------- -//--------------------------------------------------------------------------- +PyObject* wxPyInputStream::readlines(int sizehint) { + PyObject* pylist; -wxPyTimer::wxPyTimer(PyObject* callback) { - func = callback; - Py_INCREF(func); -} + // 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; + } -wxPyTimer::~wxPyTimer() { - bool doSave = wxPyRestoreThread(); - Py_DECREF(func); - wxPySaveThread(doSave); -} + // init list + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + pylist = PyList_New(0); + wxPyEndBlockThreads(blocked); -void wxPyTimer::Notify() { - if (!func || func == Py_None) { - wxTimer::Notify(); + if (!pylist) { + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + PyErr_NoMemory(); + wxPyEndBlockThreads(blocked); + return NULL; } - else { - bool doSave = wxPyRestoreThread(); - PyObject* result; - PyObject* args = Py_BuildValue("()"); - - result = PyEval_CallObject(func, args); - Py_DECREF(args); - if (result) { - Py_DECREF(result); - PyErr_Clear(); - } else { - PyErr_Print(); + // read sizehint bytes or until EOF + int i; + for (i=0; (m_wxis->CanRead()) && ((sizehint < 0) || (i < sizehint));) { + PyObject* s = this->readline(); + if (s == NULL) { + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + Py_DECREF(pylist); + wxPyEndBlockThreads(blocked); + return NULL; } + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + PyList_Append(pylist, s); + i += PyString_Size(s); + wxPyEndBlockThreads(blocked); + } - wxPySaveThread(doSave); + // error check + 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 pylist; } +void wxPyInputStream::seek(int offset, int whence) { + if (m_wxis) + m_wxis->SeekI(offset, wxSeekMode(whence)); +} -//--------------------------------------------------------------------------- -//--------------------------------------------------------------------------- -// Convert a wxList to a Python List +int wxPyInputStream::tell(){ + if (m_wxis) + return m_wxis->TellI(); + else return 0; +} -PyObject* wxPy_ConvertList(wxListBase* list, const char* className) { - PyObject* pyList; - PyObject* pyObj; - wxObject* wxObj; - wxNode* node = list->First(); - bool doSave = wxPyRestoreThread(); - pyList = PyList_New(0); - while (node) { - wxObj = node->Data(); - pyObj = wxPyConstructObject(wxObj, className); - PyList_Append(pyList, pyObj); - node = node->Next(); - } - wxPySaveThread(doSave); - return pyList; -} -//---------------------------------------------------------------------- -long wxPyGetWinHandle(wxWindow* win) { -#ifdef __WXMSW__ - return (long)win->GetHandle(); -#endif +wxPyCBInputStream::wxPyCBInputStream(PyObject *r, PyObject *s, PyObject *t, bool block) + : wxInputStream(), m_read(r), m_seek(s), m_tell(t), m_block(block) +{} - // 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; - } - } -#endif - return 0; +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); } -//---------------------------------------------------------------------- -// Some helper functions for typemaps in my_typemaps.i, so they won't be -// included in every file... +wxPyCBInputStream::~wxPyCBInputStream() { + wxPyBlock_t blocked; + if (m_block) blocked = wxPyBeginBlockThreads(); + Py_XDECREF(m_read); + Py_XDECREF(m_seek); + Py_XDECREF(m_tell); + if (m_block) wxPyEndBlockThreads(blocked); +} -byte* byte_LIST_helper(PyObject* source) { - if (!PyList_Check(source)) { - PyErr_SetString(PyExc_TypeError, "Expected a list object."); - return NULL; - } - int count = PyList_Size(source); - byte* temp = new byte[count]; - if (! temp) { - PyErr_SetString(PyExc_MemoryError, "Unable to allocate temporary array"); + +wxPyCBInputStream* wxPyCBInputStream::create(PyObject *py, bool block) { + wxPyBlock_t blocked; + if (block) blocked = wxPyBeginBlockThreads(); + + PyObject* read = getMethod(py, "read"); + PyObject* seek = getMethod(py, "seek"); + PyObject* tell = getMethod(py, "tell"); + + if (!read) { + PyErr_SetString(PyExc_TypeError, "Not a file-like object"); + Py_XDECREF(read); + Py_XDECREF(seek); + Py_XDECREF(tell); + if (block) wxPyEndBlockThreads(blocked); return NULL; } - for (int x=0; xOnSysTell(); + wxFileOffset ret = self->OnSysSeek(0, wxFromEnd); + self->OnSysSeek(temp, wxFromStart); + return ret; } - int count = PyList_Size(source); - char** temp = new char*[count]; - if (! temp) { - PyErr_SetString(PyExc_MemoryError, "Unable to allocate temporary array"); - return NULL; + else + return wxInvalidOffset; +} + + +size_t wxPyCBInputStream::OnSysRead(void *buffer, size_t bufsize) { + if (bufsize == 0) + return 0; + + 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)) { + o = PyString_Size(result); + if (o == 0) + m_lasterror = wxSTREAM_EOF; + if (o > bufsize) + o = bufsize; + memcpy((char*)buffer, PyString_AsString(result), o); // strings only, not unicode... + Py_DECREF(result); + + } + else + m_lasterror = wxSTREAM_READ_ERROR; + wxPyEndBlockThreads(blocked); + return o; +} + +size_t wxPyCBInputStream::OnSysWrite(const void *buffer, size_t bufsize) { + m_lasterror = wxSTREAM_WRITE_ERROR; + return 0; +} + + +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(blocked); + return OnSysTell(); +} + + +wxFileOffset wxPyCBInputStream::OnSysTell() const { + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + PyObject* arglist = Py_BuildValue("()"); + PyObject* result = PyEval_CallObject(m_tell, arglist); + Py_DECREF(arglist); + wxFileOffset o = 0; + if (result != NULL) { + if (PyLong_Check(result)) + o = PyLong_AsLongLong(result); + else + o = PyInt_AsLong(result); + Py_DECREF(result); + }; + wxPyEndBlockThreads(blocked); + return o; +} + +//---------------------------------------------------------------------- + +IMPLEMENT_ABSTRACT_CLASS(wxPyCallback, wxObject); + +wxPyCallback::wxPyCallback(PyObject* func) { + m_func = func; + Py_INCREF(m_func); +} + +wxPyCallback::wxPyCallback(const wxPyCallback& other) { + m_func = other.m_func; + Py_INCREF(m_func); +} + +wxPyCallback::~wxPyCallback() { + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + Py_DECREF(m_func); + 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) { + wxPyCallback* cb = (wxPyCallback*)event.m_callbackUserData; + PyObject* func = cb->m_func; + PyObject* result; + PyObject* arg; + PyObject* tuple; + bool checkSkip = false; + + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxString className = event.GetClassInfo()->GetClassName(); + + // 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 { + arg = wxPyConstructObject((void*)&event, className); + } + + 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(blocked); +} + + +//---------------------------------------------------------------------- + +wxPyCallbackHelper::wxPyCallbackHelper(const wxPyCallbackHelper& other) { + m_lastFound = NULL; + m_self = other.m_self; + m_class = other.m_class; + if (m_self) { + Py_INCREF(m_self); + Py_INCREF(m_class); + } +} + + +void wxPyCallbackHelper::setSelf(PyObject* self, PyObject* klass, int incref) { + m_self = self; + m_class = klass; + m_incRef = incref; + if (incref) { + Py_INCREF(m_self); + Py_INCREF(m_class); + } +} + + +#if PYTHON_API_VERSION >= 1011 + +// Prior to Python 2.2 PyMethod_GetClass returned the class object +// in which the method was defined. Starting with 2.2 it returns +// "class that asked for the method" which seems totally bogus to me +// but apprently it fixes some obscure problem waiting to happen in +// Python. Since the API was not documented Guido and the gang felt +// safe in changing it. Needless to say that totally screwed up the +// logic below in wxPyCallbackHelper::findCallback, hence this icky +// code to find the class where the method is actually defined... + +static +PyObject* PyFindClassWithAttr(PyObject *klass, PyObject *name) +{ + int i, n; + + if (PyType_Check(klass)) { // new style classes + // This code is borrowed/adapted from _PyType_Lookup in typeobject.c + PyTypeObject* type = (PyTypeObject*)klass; + PyObject *mro, *res, *base, *dict; + /* Look in tp_dict of types in MRO */ + mro = type->tp_mro; + assert(PyTuple_Check(mro)); + n = PyTuple_GET_SIZE(mro); + for (i = 0; i < n; i++) { + base = PyTuple_GET_ITEM(mro, i); + if (PyClass_Check(base)) + dict = ((PyClassObject *)base)->cl_dict; + else { + assert(PyType_Check(base)); + dict = ((PyTypeObject *)base)->tp_dict; + } + assert(dict && PyDict_Check(dict)); + res = PyDict_GetItem(dict, name); + if (res != NULL) + return base; + } + return NULL; + } + + else if (PyClass_Check(klass)) { // old style classes + // This code is borrowed/adapted from class_lookup in classobject.c + PyClassObject* cp = (PyClassObject*)klass; + PyObject *value = PyDict_GetItem(cp->cl_dict, name); + if (value != NULL) { + return (PyObject*)cp; + } + n = PyTuple_Size(cp->cl_bases); + for (i = 0; i < n; i++) { + PyObject* base = PyTuple_GetItem(cp->cl_bases, i); + PyObject *v = PyFindClassWithAttr(base, name); + if (v != NULL) + return v; + } + return NULL; + } + return NULL; +} +#endif + + +static +PyObject* PyMethod_GetDefiningClass(PyObject* method, PyObject* nameo) +{ + PyObject* mgc = PyMethod_GET_CLASS(method); + +#if PYTHON_API_VERSION <= 1010 // prior to Python 2.2, the easy way + return mgc; +#else // 2.2 and after, the hard way... + return PyFindClassWithAttr(mgc, nameo); +#endif +} + + + +// To avoid recursion when an overridden virtual method wants to call the base +// class version, temporarily set an attribute in the instance with the same +// name as the method. Then the PyObject_GetAttr in the next findCallback +// will return this attribute and the PyMethod_Check will fail. + +void wxPyCallbackHelper::setRecursionGuard(PyObject* method) const +{ + PyFunctionObject* func = (PyFunctionObject*)PyMethod_Function(method); + PyObject_SetAttr(m_self, func->func_name, Py_None); +} + +void wxPyCallbackHelper::clearRecursionGuard(PyObject* method) const +{ + PyFunctionObject* func = (PyFunctionObject*)PyMethod_Function(method); + if (PyObject_HasAttr(m_self, func->func_name)) { + PyObject_DelAttr(m_self, func->func_name); + } +} + +// bool wxPyCallbackHelper::hasRecursionGuard(PyObject* method) const +// { +// PyFunctionObject* func = (PyFunctionObject*)PyMethod_Function(method); +// if (PyObject_HasAttr(m_self, func->func_name)) { +// PyObject* attr = PyObject_GetAttr(m_self, func->func_name); +// bool retval = (attr == Py_None); +// Py_DECREF(attr); +// return retval; +// } +// return false; +// } + + +bool wxPyCallbackHelper::findCallback(const char* name, bool setGuard) const { + wxPyCallbackHelper* self = (wxPyCallbackHelper*)this; // cast away const + PyObject *method, *klass; + PyObject* nameo = PyString_FromString(name); + self->m_lastFound = NULL; + + // If the object (m_self) has an attibute of the given name... + if (m_self && PyObject_HasAttr(m_self, nameo)) { + method = PyObject_GetAttr(m_self, nameo); + + // ...and if that attribute is a method, and if that method's class is + // not from the registered class or a base class... + if (PyMethod_Check(method) && + (klass = PyMethod_GetDefiningClass(method, nameo)) != NULL && + (klass != m_class) && + PyObject_IsSubclass(klass, m_class)) { + + // ...then we'll save a pointer to the method so callCallback can + // call it. But first, set a recursion guard in case the + // overridden method wants to call the base class version. + if (setGuard) + setRecursionGuard(method); + self->m_lastFound = method; + } + else { + Py_DECREF(method); + } + } + + Py_DECREF(nameo); + return m_lastFound != NULL; +} + + +int wxPyCallbackHelper::callCallback(PyObject* argTuple) const { + PyObject* result; + int retval = false; + + result = callCallbackObj(argTuple); + if (result) { // Assumes an integer return type... + retval = PyInt_AsLong(result); + Py_DECREF(result); + PyErr_Clear(); // forget about it if it's not... + } + return retval; +} + +// Invoke the Python callable object, returning the raw PyObject return +// value. Caller should DECREF the return value and also manage the GIL. +PyObject* wxPyCallbackHelper::callCallbackObj(PyObject* argTuple) const { + PyObject* result; + + // Save a copy of the pointer in case the callback generates another + // callback. In that case m_lastFound will have a different value when + // it gets back here... + PyObject* method = m_lastFound; + + result = PyEval_CallObject(method, argTuple); + clearRecursionGuard(method); + + Py_DECREF(argTuple); + Py_DECREF(method); + if (!result) { + PyErr_Print(); + } + return result; +} + + +void wxPyCBH_setCallbackInfo(wxPyCallbackHelper& cbh, PyObject* self, PyObject* klass, int incref) { + cbh.setSelf(self, klass, incref); +} + +bool wxPyCBH_findCallback(const wxPyCallbackHelper& cbh, const char* name, bool setGuard) { + return cbh.findCallback(name, setGuard); +} + +int wxPyCBH_callCallback(const wxPyCallbackHelper& cbh, PyObject* argTuple) { + return cbh.callCallback(argTuple); +} + +PyObject* wxPyCBH_callCallbackObj(const wxPyCallbackHelper& cbh, PyObject* argTuple) { + return cbh.callCallbackObj(argTuple); +} + + +void wxPyCBH_delete(wxPyCallbackHelper* cbh) { + if (cbh->m_incRef && Py_IsInitialized()) { + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + Py_XDECREF(cbh->m_self); + Py_XDECREF(cbh->m_class); + wxPyEndBlockThreads(blocked); + } +} + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +// These event classes can be derived from in Python and passed through the event +// system without losing anything. They do this by keeping a reference to +// themselves and some special case handling in wxPyCallback::EventThunker. + + +wxPyEvtSelfRef::wxPyEvtSelfRef() { + //m_self = Py_None; // **** We don't do normal ref counting to prevent + //Py_INCREF(m_self); // circular loops... + m_cloned = false; +} + +wxPyEvtSelfRef::~wxPyEvtSelfRef() { + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + if (m_cloned) + Py_DECREF(m_self); + wxPyEndBlockThreads(blocked); +} + +void wxPyEvtSelfRef::SetSelf(PyObject* self, bool clone) { + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + if (m_cloned) + Py_DECREF(m_self); + m_self = self; + if (clone) { + Py_INCREF(m_self); + m_cloned = true; + } + wxPyEndBlockThreads(blocked); +} + +PyObject* wxPyEvtSelfRef::GetSelf() const { + Py_INCREF(m_self); + return m_self; +} + + +IMPLEMENT_ABSTRACT_CLASS(wxPyEvent, wxEvent); +IMPLEMENT_ABSTRACT_CLASS(wxPyCommandEvent, wxCommandEvent); + + +wxPyEvent::wxPyEvent(int winid, wxEventType commandType) + : wxEvent(winid, commandType) { +} + + +wxPyEvent::wxPyEvent(const wxPyEvent& evt) + : wxEvent(evt) +{ + SetSelf(evt.m_self, true); +} + + +wxPyEvent::~wxPyEvent() { +} + + +wxPyCommandEvent::wxPyCommandEvent(wxEventType commandType, int id) + : wxCommandEvent(commandType, id) { +} + + +wxPyCommandEvent::wxPyCommandEvent(const wxPyCommandEvent& evt) + : wxCommandEvent(evt) +{ + SetSelf(evt.m_self, true); +} + + +wxPyCommandEvent::~wxPyCommandEvent() { +} + + + + + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +// Convert a wxList to a Python List, only works for lists of wxObjects + +PyObject* wxPy_ConvertList(wxListBase* listbase) { + wxList* list = (wxList*)listbase; // this is probably bad... + PyObject* pyList; + PyObject* pyObj; + wxObject* wxObj; + wxNode* node = list->GetFirst(); + + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + pyList = PyList_New(0); + while (node) { + wxObj = node->GetData(); + pyObj = wxPyMake_wxObject(wxObj,false); + PyList_Append(pyList, pyObj); + Py_DECREF(pyObj); // the Append also does an INCREF, that's one more than we need. + node = node->GetNext(); + } + wxPyEndBlockThreads(blocked); + return pyList; +} + +//---------------------------------------------------------------------- + +long wxPyGetWinHandle(wxWindow* win) { + +#ifdef __WXMSW__ + return (long)win->GetHandle(); +#endif + +#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; +} + +//---------------------------------------------------------------------- +// Some helper functions for typemaps in my_typemaps.i, so they won't be +// included in every file over and over again... + +wxString* wxString_in_helper(PyObject* source) { + wxString* target = NULL; + + if (!PyString_Check(source) && !PyUnicode_Check(source)) { + PyErr_SetString(PyExc_TypeError, "String or Unicode type required"); + return NULL; + } +#if wxUSE_UNICODE + 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 + // 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; Py_ssize_t tmpSize; + PyString_AsStringAndSize(str, &tmpPtr, &tmpSize); + target = new wxString(tmpPtr, tmpSize); + + if (!PyString_Check(source)) + Py_DECREF(str); +#endif // wxUSE_UNICODE + + 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? + } + 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; Py_ssize_t 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)) { + PyErr_SetString(PyExc_TypeError, "Expected a list object."); + return NULL; + } + int count = PyList_Size(source); + byte* temp = new byte[count]; + if (! temp) { + PyErr_SetString(PyExc_MemoryError, "Unable to allocate temporary array"); + return NULL; + } + for (int x=0; xx = PyInt_AS_LONG(o1); - point->y = PyInt_AS_LONG(o2); - return true; - } - if (PyFloat_Check(o1) && PyFloat_Check(o2)) { +//--------------------------------------------------------------------------- +// NOTE: The following functions could really use some refactoring using +// templates to make things somewhat simpler... + +inline bool wxPointFromObjects(PyObject* o1, PyObject* o2, wxPoint* point) +{ + // get the x value + if (PyInt_Check(o1)) + point->x = (int)PyInt_AS_LONG(o1); + else if (PyFloat_Check(o1)) point->x = (int)PyFloat_AS_DOUBLE(o1); + else if (PyNumber_Check(o1)) + point->x = (int)PyInt_AsLong(o1); + else + return false; + + // get the y value + if (PyInt_Check(o2)) + point->y = (int)PyInt_AS_LONG(o2); + else if (PyFloat_Check(o2)) point->y = (int)PyFloat_AS_DOUBLE(o2); - return true; - } - if (PyInstance_Check(o1) || PyInstance_Check(o2)) { - // Disallow instances because they can cause havok + else if (PyNumber_Check(o2)) + point->y = (int)PyInt_AsLong(o2); + else return false; - } - if (PyNumber_Check(o1) && PyNumber_Check(o2)) { - // I believe this excludes instances, so this should be safe without INCREFFing o1 and o2 - point->x = PyInt_AsLong(o1); - point->y = PyInt_AsLong(o2); - return true; - } - return false; + + return true; + +// NOTE: This function used to have this code in it, but I don't know why it +// is a problem nor what havok it will cause, so removing for now... +// if (wxPySwigInstance_Check(o1) || wxPySwigInstance_Check(o2)) { +// // Disallow instances because they can cause havok +// return false; +// } } -#if PYTHON_API_VERSION < 1009 -#define PySequence_Fast_GET_ITEM(o, i)\ - (PyList_Check(o) ? PyList_GET_ITEM(o, i) : PyTuple_GET_ITEM(o, i)) -#endif +inline bool wxPoint2DFromObjects(PyObject* o1, PyObject* o2, wxPoint2D* point) +{ + // get the x value + if (PyInt_Check(o1)) + point->m_x = (double)PyInt_AS_LONG(o1); + else if (PyFloat_Check(o1)) + point->m_x = (double)PyFloat_AS_DOUBLE(o1); + else if (PyNumber_Check(o1)) + point->m_x = (double)PyFloat_AsDouble(o1); + else + return false; + + // get the y value + if (PyInt_Check(o2)) + point->m_y = (double)PyInt_AS_LONG(o2); + else if (PyFloat_Check(o2)) + point->m_y = (double)PyFloat_AS_DOUBLE(o2); + else if (PyNumber_Check(o2)) + point->m_y = (double)PyFloat_AsDouble(o2); + else + return false; + + return true; +} + -wxPoint* wxPoint_LIST_helper(PyObject* source, int *count) { - // Putting all of the declarations here allows - // us to put the error handling all in one place. - int x; + +wxPoint* wxPoint_LIST_helper(PyObject* source, int *count) +{ + int idx; wxPoint* temp; PyObject *o, *o1, *o2; - int isFast = PyList_Check(source) || PyTuple_Check(source); + bool isFast = PyList_Check(source) || PyTuple_Check(source); - // The length of the sequence is returned in count. if (!PySequence_Check(source)) { goto error0; } + + // The length of the sequence is returned in count. *count = PySequence_Length(source); if (*count < 0) { goto error0; @@ -811,13 +2149,13 @@ wxPoint* wxPoint_LIST_helper(PyObject* source, int *count) { PyErr_SetString(PyExc_MemoryError, "Unable to allocate temporary array"); return NULL; } - for (x=0; x<*count; x++) { + for (idx=0; idx<*count; idx++) { // Get an item: try fast way first. if (isFast) { - o = PySequence_Fast_GET_ITEM(source, x); + o = PySequence_Fast_GET_ITEM(source, idx); } else { - o = PySequence_GetItem(source, x); + o = PySequence_GetItem(source, idx); if (o == NULL) { goto error1; } @@ -828,21 +2166,21 @@ wxPoint* wxPoint_LIST_helper(PyObject* source, int *count) { (PyList_Check(o) && PyList_GET_SIZE(o) == 2)) { o1 = PySequence_Fast_GET_ITEM(o, 0); o2 = PySequence_Fast_GET_ITEM(o, 1); - if (!wxPointFromObjects(o1, o2, &temp[x])) { + if (!wxPointFromObjects(o1, o2, &temp[idx])) { 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; + temp[idx] = *pt; } else if (PySequence_Check(o) && PySequence_Length(o) == 2) { o1 = PySequence_GetItem(o, 0); o2 = PySequence_GetItem(o, 1); - if (!wxPointFromObjects(o1, o2, &temp[x])) { + if (!wxPointFromObjects(o1, o2, &temp[idx])) { goto error3; } Py_DECREF(o1); @@ -864,13 +2202,96 @@ 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; } -// end of patch -//------------------------------ + + + +wxPoint2D* wxPoint2D_LIST_helper(PyObject* source, size_t *count) +{ + size_t idx; + wxPoint2D* temp; + PyObject *o, *o1, *o2; + bool isFast = PyList_Check(source) || PyTuple_Check(source); + + if (!PySequence_Check(source)) { + goto error0; + } + + // The length of the sequence is returned in count. + *count = PySequence_Length(source); + if (*count < 0) { + goto error0; + } + + temp = new wxPoint2D[*count]; + if (!temp) { + PyErr_SetString(PyExc_MemoryError, "Unable to allocate temporary array"); + return NULL; + } + for (idx=0; idx<*count; idx++) { + // Get an item: try fast way first. + if (isFast) { + o = PySequence_Fast_GET_ITEM(source, idx); + } + else { + o = PySequence_GetItem(source, idx); + if (o == NULL) { + goto error1; + } + } + + // Convert o to wxPoint. + if ((PyTuple_Check(o) && PyTuple_GET_SIZE(o) == 2) || + (PyList_Check(o) && PyList_GET_SIZE(o) == 2)) { + o1 = PySequence_Fast_GET_ITEM(o, 0); + o2 = PySequence_Fast_GET_ITEM(o, 1); + if (!wxPoint2DFromObjects(o1, o2, &temp[idx])) { + goto error2; + } + } + else if (wxPySwigInstance_Check(o)) { + wxPoint2D* pt; + if (! wxPyConvertSwigPtr(o, (void **)&pt, wxT("wxPoint2D"))) { + goto error2; + } + temp[idx] = *pt; + } + else if (PySequence_Check(o) && PySequence_Length(o) == 2) { + o1 = PySequence_GetItem(o, 0); + o2 = PySequence_GetItem(o, 1); + if (!wxPoint2DFromObjects(o1, o2, &temp[idx])) { + goto error3; + } + Py_DECREF(o1); + Py_DECREF(o2); + } + else { + goto error2; + } + // Clean up. + if (!isFast) + Py_DECREF(o); + } + return temp; + +error3: + Py_DECREF(o1); + Py_DECREF(o2); +error2: + if (!isFast) + Py_DECREF(o); +error1: + delete [] temp; +error0: + PyErr_SetString(PyExc_TypeError, "Expected a sequence of length-2 sequences or wxPoint2Ds."); + return NULL; +} + +//--------------------------------------------------------------------------- wxBitmap** wxBitmap_LIST_helper(PyObject* source) { @@ -886,10 +2307,10 @@ wxBitmap** wxBitmap_LIST_helper(PyObject* source) { } for (int x=0; x= 1009 + if (! PyString_Check(o) && ! PyUnicode_Check(o)) { + PyErr_SetString(PyExc_TypeError, "Expected a list of string or unicode objects."); + return NULL; + } +#else if (! PyString_Check(o)) { PyErr_SetString(PyExc_TypeError, "Expected a list of strings."); return NULL; } - temp[x] = PyString_AsString(o); +#endif + + wxString* pStr = wxString_in_helper(o); + temp[x] = *pStr; + delete pStr; } return temp; } @@ -940,25 +2371,54 @@ wxAcceleratorEntry* wxAcceleratorEntry_LIST_helper(PyObject* source) { } for (int x=0; x