]> git.saurik.com Git - wxWidgets.git/blobdiff - wxPython/contrib/activex/activex.i
Lindsay Mathieson's newest wxActiveX class has been wrapped into a new
[wxWidgets.git] / wxPython / contrib / activex / activex.i
diff --git a/wxPython/contrib/activex/activex.i b/wxPython/contrib/activex/activex.i
new file mode 100644 (file)
index 0000000..d428843
--- /dev/null
@@ -0,0 +1,1091 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        activex.i
+// Purpose:     ActiveX controls (such as Internet Explorer) in a wxWindow
+//
+// Author:      Robin Dunn
+//
+// Created:     18-Mar-2004
+// RCS-ID:      $Id$
+// Copyright:   (c) 2004 by Total Control Software
+// Licence:     wxWindows license
+/////////////////////////////////////////////////////////////////////////////
+
+
+%module activex
+
+%{
+#include "wx/wxPython/wxPython.h"
+#include "wx/wxPython/pyclasses.h"
+#include "wx/wxPython/pyistream.h"
+
+#include "wxactivex.h"
+%}
+
+//---------------------------------------------------------------------------
+
+%import core.i
+%pythoncode { wx = core }
+
+MAKE_CONST_WXSTRING_NOSWIG(PanelNameStr);
+
+%include _activex_rename.i
+
+
+//---------------------------------------------------------------------------
+
+typedef unsigned short  USHORT;
+typedef long            DISPID;
+typedef long            MEMBERID;
+typedef unsigned short  VARTYPE;
+
+
+%{
+// Since SWIG doesn't support nested classes, we need to fool it a bit
+// and make them look like global classes.  These defines make the C++ code
+// know what we are doing.
+#define wxParamX        wxActiveX::ParamX
+#define wxFuncX         wxActiveX::FuncX
+#define wxPropX         wxActiveX::PropX
+#define wxParamXArray   wxActiveX::ParamXArray
+#define wxFuncXArray    wxActiveX::FuncXArray
+#define wxPropXArray    wxActiveX::PropXArray
+%}
+
+
+%{
+// Some conversion helpers
+static wxVariant _PyObj2Variant(PyObject* value);
+static PyObject* _Variant2PyObj(wxVariant& value, bool useNone=False);
+static wxString  _VARTYPEname(VARTYPE vt);
+
+// Check if an exception has been raised  (blocking threads)
+inline bool wxPyErr_Occurred()
+{
+    bool rval;
+    wxPyBeginBlockThreads();
+    rval = PyErr_Occurred() != NULL;
+    wxPyEndBlockThreads();
+    return rval;
+}
+
+%}
+
+//---------------------------------------------------------------------------
+%newgroup
+
+DocStr(CLSID,
+"This class wraps the Windows CLSID structure and is used to
+specify the class of the ActiveX object that is to be created.  A
+CLSID can be constructed from either a ProgID string, (such as
+'WordPad.Document.1') or a classID string, (such as
+'{CA8A9783-280D-11CF-A24D-444553540000}').");
+
+class CLSID {
+public:
+    %extend {
+        CLSID(const wxString& id)
+        {
+            int result;
+            CLSID* self = new CLSID;
+            memset(self, 0, sizeof(CLSID));
+
+            if (id[0] == _T('{')) {
+                // Looks like a classID string
+                result =
+                    CLSIDFromString(
+                        (LPOLESTR)(const wchar_t *)id.wc_str(wxConvUTF8),
+                        self);
+            } else {
+                // Try a progID
+                result =
+                    CLSIDFromProgID(
+                        (LPOLESTR)(const wchar_t *)id.wc_str(wxConvUTF8),
+                        self);
+            }
+            if (result != NOERROR) {
+                wxPyErr_SetString(PyExc_ValueError, "Not a recognized classID or progID");
+                delete self;
+                return NULL;
+            }
+            return self;
+        }
+
+        ~CLSID() { delete self; }
+
+        wxString GetCLSIDString()
+        {
+            LPOLESTR s;
+            wxString str;
+            if (StringFromCLSID(*self, &s) == S_OK) {
+                str = s;
+                CoTaskMemFree(s);
+            }
+            else {
+                str = _T("Error!");  // TODO: raise exception?
+            }
+            return str;
+        }
+        wxString GetProgIDString()
+        {
+            LPOLESTR s;
+            wxString str;
+            if (ProgIDFromCLSID(*self, &s) == S_OK) {
+                str = s;
+                CoTaskMemFree(s);
+            }
+            else {
+                str = _T("Error!"); // TODO: raise exception?
+            }
+            return str;
+        }
+    }
+    %pythoncode { def __str__(self):   return self.GetCLSIDString() }
+};
+
+
+//---------------------------------------------------------------------------
+%newgroup
+
+%define MAKE_ARRAY_WRAPPER(basetype, arrayname)
+class arrayname
+{
+public:
+    %extend {
+        bool __nonzero__()          { return self->size() > 0; }
+        int  __len__()              { return self->size(); }
+
+        const basetype& __getitem__(int idx) {
+            if ( idx >= 0 && idx < self->size() )
+                return (*self)[idx];
+            else {
+                static basetype BadVal;
+                wxPyErr_SetString(PyExc_IndexError, "Index out of range");
+                return BadVal;
+            }
+        }
+        // TODO  __iter__??
+    }
+};
+%enddef
+
+//---------------------------------------------------------------------------
+
+%immutable;
+
+class wxParamX
+{
+public:
+    USHORT      flags;
+    bool        isPtr;
+    bool        isSafeArray;
+    bool        isOptional;
+    VARTYPE     vt;
+    wxString    name;
+    
+    %feature("shadow") vt_type_get "vt_type = property(_activex.ParamX_vt_type_get)";
+    %extend { wxString vt_type_get() { return _VARTYPEname(self->vt); } }
+
+    %feature("shadow") IsIn     "isIn = property(_activex.ParamX_IsIn)";
+    %feature("shadow") IsOut    "isOut = property(_activex.ParamX_IsOut)";
+    %feature("shadow") IsRetVal "isRetVal = property(_activex.ParamX_IsRetVal)";
+    bool IsIn() const;
+    bool IsOut() const;
+    bool IsRetVal() const;
+
+};
+
+
+class wxFuncX
+{
+public:
+    wxString      name;
+    MEMBERID      memid;
+    bool          hasOut;
+
+    wxParamX      retType;
+    wxParamXArray params;
+};
+
+
+class wxPropX
+{
+public:
+    wxString    name;
+    MEMBERID    memid;
+    wxParamX    type;
+    wxParamX    arg;
+    bool        putByRef;
+
+    %feature("shadow") CanGet "canGet = property(_activex.PropX_CanGet)";
+    %feature("shadow") CanSet "canSet = property(_activex.PropX_CanSet)";
+    bool CanGet() const;
+    bool CanSet() const;
+};
+%mutable;
+
+
+MAKE_ARRAY_WRAPPER(wxParamX, wxParamXArray);
+MAKE_ARRAY_WRAPPER(wxFuncX, wxFuncXArray);
+MAKE_ARRAY_WRAPPER(wxPropX, wxPropXArray);
+
+
+//---------------------------------------------------------------------------
+%newgroup
+
+%{
+// C++ version of a Python-aware wxActiveX    
+class wxActiveXWindow : public wxActiveX
+{
+private:
+    CLSID       m_CLSID;
+public:
+    wxActiveXWindow( wxWindow* parent, const CLSID& clsId, wxWindowID id = -1,
+                     const wxPoint& pos = wxDefaultPosition,
+                     const wxSize& size = wxDefaultSize,
+                     long style = 0,
+                     const wxString& name = wxPyPanelNameStr)
+        : wxActiveX(parent, clsId, id, pos, size, style, name)
+    {
+        m_CLSID = clsId;
+    }
+
+    const CLSID& GetCLSID() const { return m_CLSID; }
+    
+    
+    // Renamed versions of some base class methods that delegate
+    // to the base where appropriate, and raise Python exceptions
+    // when needed.
+    int GetAXEventCount() const  { return wxActiveX::GetEventCount(); }
+    int GetAXPropCount() const   { return wxActiveX::GetPropCount(); }
+    int GetAXMethodCount() const { return wxActiveX::GetMethodCount(); }
+
+    const wxFuncX& GetAXEventDesc(int idx) const
+    {
+        static wxFuncX BadVal;
+        if (idx < 0 || idx >= GetAXEventCount()) {
+            wxPyErr_SetString(PyExc_IndexError, "Index out of range");
+            return BadVal;
+        }
+        return m_events[idx];
+    }
+    const wxFuncX& GetAXMethodDesc(int idx) const
+    {
+        static wxFuncX BadVal;
+        if (idx < 0 || idx >= GetAXMethodCount()) {
+            wxPyErr_SetString(PyExc_IndexError, "Index out of range");
+            return BadVal;
+        }
+        return m_methods[idx];
+    }
+    const wxPropX& GetAXPropDesc(int idx) const
+    {
+        static wxPropX BadVal;
+        if (idx < 0 || idx >= GetAXPropCount()) {
+            wxPyErr_SetString(PyExc_IndexError, "Index out of range");
+            return BadVal;
+        }
+        return m_props[idx];
+    }
+
+    const wxFuncX& GetAXMethodDesc(const wxString& name) const
+    {
+        NameMap::const_iterator it = m_methodNames.find(name);
+        if (it == m_methodNames.end())     {
+            wxString msg;
+            msg << "method <" << name << "> not found";
+            wxPyErr_SetString(PyExc_KeyError, msg.mb_str());
+            static wxFuncX BadVal;
+            return BadVal;
+        };
+        return GetAXMethodDesc(it->second);
+    }
+    const wxPropX& GetAXPropDesc(const wxString& name) const
+    {
+        NameMap::const_iterator it = m_propNames.find(name);
+        if (it == m_propNames.end())     {
+            wxString msg;
+            msg << "property <" << name << "> not found";
+            wxPyErr_SetString(PyExc_KeyError, msg.mb_str());
+            static wxPropX BadVal;
+            return BadVal;
+        };
+        return GetAXPropDesc(it->second);
+    }
+
+    // Accessors for the internal vectors of events, methods and
+    // proprties.  Can be used as sequence like objects from
+    // Python.
+    const wxFuncXArray& GetAXEvents()      { return m_events; }
+    const wxFuncXArray& GetAXMethods()     { return m_methods; }
+    const wxPropXArray& GetAXProperties()  { return m_props; }
+
+
+    // Set a property from a Python object
+    void SetAXProp(const wxString& name, PyObject* value)
+    {        
+        const wxPropX& prop = GetAXPropDesc(name);
+        wxPyBeginBlockThreads();
+        if (! PyErr_Occurred() ) {
+            if (! prop.CanSet()) {
+                wxString msg;
+                msg << "property <" << name << "> is readonly";
+                PyErr_SetString(PyExc_TypeError, msg.mb_str());
+                goto done;
+            } else {
+                wxVariant wxV = _PyObj2Variant(value);
+                if (PyErr_Occurred())
+                    goto done;
+                VARIANT v = {prop.arg.vt};
+                if (!VariantToMSWVariant(wxV, v) || PyErr_Occurred()) {
+                    wxString msg;
+                    msg << "Unable to convert value to expected type: ("
+                        << _VARTYPEname(prop.arg.vt) << ") for property <"
+                        << name << ">";
+                    PyErr_SetString(PyExc_TypeError, msg.mb_str());
+                    goto done;
+                }
+                PyThreadState* tstate = wxPyBeginAllowThreads();
+                SetProp(prop.memid, v);
+                VariantClear(&v);
+                wxPyEndAllowThreads(tstate);
+            }
+        }
+    done:
+        wxPyEndBlockThreads();
+    }
+
+    
+    // Get a property and convert it to a Python object
+    PyObject* GetAXProp(const wxString& name)
+    {        
+        PyObject* rval = NULL;
+        const wxPropX& prop = GetAXPropDesc(name);
+        wxPyBeginBlockThreads();
+        if (! PyErr_Occurred() ) {
+            if (! prop.CanGet()) {
+                wxString msg;
+                msg << "property <" << name << "> is writeonly";
+                PyErr_SetString(PyExc_TypeError, msg.mb_str());
+                goto done;
+            } else {
+                PyThreadState* tstate = wxPyBeginAllowThreads();
+                VARIANT v = GetPropAsVariant(prop.memid);
+                wxPyEndAllowThreads(tstate);
+                wxVariant wv;
+                if (!MSWVariantToVariant(v, wv) || PyErr_Occurred()) {
+                    wxString msg;
+                    msg << "Unable to convert value to expected type: ("
+                        << _VARTYPEname(prop.arg.vt) << ") for property <"
+                        << name << ">";
+                    PyErr_SetString(PyExc_TypeError, msg.mb_str());
+                    goto done;
+                }
+                rval = _Variant2PyObj(wv);
+                VariantClear(&v);
+            }
+        }
+    done:
+        wxPyEndBlockThreads();
+        return rval;
+    }
+
+
+    // If both IsIn and isOut are false, assume it is actually an
+    // input param
+    bool paramIsIn(const wxParamX& p)
+    {
+        return p.IsIn() || (!p.IsIn() && !p.IsOut());
+    }
+    
+
+    // Call a method of the ActiveX object
+    PyObject* _CallAXMethod(const wxString& name, PyObject* args)
+    {
+        VARIANTARG *vargs = NULL;
+        int nargs = 0;
+        PyObject* rval = NULL;
+        const wxFuncX& func = GetAXMethodDesc(name);
+        
+        wxPyBeginBlockThreads();
+        if (! PyErr_Occurred() ) {
+            nargs = func.params.size();
+            if (nargs > 0)
+                vargs = new VARIANTARG[nargs];
+
+            if (vargs) {
+                // init type of vargs, in reverse order
+                int i;
+                for (i = 0; i < nargs; i++)
+                    vargs[nargs - i - 1].vt = func.params[i].vt;
+
+                // Map the args coming from Python to the input parameters in vargs
+                int pi = 0;
+                i = 0;
+                while ( i<nargs && pi<PyTuple_Size(args) ) {
+                    // Move to the next input param. 
+                    if (! paramIsIn(func.params[i])) {
+                        i += 1;
+                        continue;
+                    }
+                    // convert the python object
+                    PyObject* obj = PyTuple_GetItem(args, pi);
+                    if (obj == Py_None) // special-case None?
+                        vargs[nargs - i - 1].vt = VT_EMPTY;
+                    else {
+                        wxVariant wxV = _PyObj2Variant(obj);
+                        if (PyErr_Occurred())
+                            goto done;
+                        if (!VariantToMSWVariant(wxV, vargs[nargs - i - 1]) || PyErr_Occurred()) {
+                            wxString msg;
+                            msg << "Unable to convert value to expected type: ("
+                                << _VARTYPEname(vargs[nargs - i - 1].vt)
+                                << ") for parameter " << i;
+                            PyErr_SetString(PyExc_TypeError, msg.mb_str());
+                            goto done;
+                        }
+                    }
+                    i += 1;
+                    pi += 1;                        
+                }
+            }
+
+            // call the method
+            PyThreadState* tstate = wxPyBeginAllowThreads();
+            VARIANT rv = CallMethod(func.memid, vargs, nargs);
+            wxPyEndAllowThreads(tstate);
+
+            // Convert the return value and any out-params, ignoring
+            // conversion errors for now
+            wxVariant wv;
+            MSWVariantToVariant(rv, wv);
+            rval = _Variant2PyObj(wv, True);
+            VariantClear(&rv);
+
+            if (func.hasOut) {
+                // make a list and put the rval in it if it is not None
+                PyObject* lst = PyList_New(0);
+                if (rval != Py_None)
+                    PyList_Append(lst, rval);
+                else
+                    Py_DECREF(rval);
+
+                // find the out params and convert them
+                for (int i = 0; i < nargs; i++) {
+                    VARIANTARG& va = vargs[nargs - i - 1];
+                    const wxParamX &px = func.params[i];
+                    if (px.IsOut()) {
+                        MSWVariantToVariant(va, wv);
+                        PyObject* obj = _Variant2PyObj(wv, True);
+                        PyList_Append(lst, obj);
+                    }
+                }
+                rval = PyList_AsTuple(lst);
+                Py_DECREF(lst);
+            }
+            if (PyErr_Occurred())
+                PyErr_Clear();
+        }
+    done:
+        wxPyEndBlockThreads();
+        if (vargs) {
+            for (int i = 0; i < nargs; i++)
+                VariantClear(&vargs[i]);
+            delete [] vargs;
+        }
+        return rval;
+    }
+};
+%}
+
+
+
+// Now tell SWIG about this new class that is implemented above.
+
+DocStr(wxActiveXWindow,
+"ActiveXWindow derives from wxWindow and the constructor accepts a
+CLSID for the ActiveX Control that should be created.  The
+ActiveXWindow class simply adds methods that allow you to query
+some of the TypeInfo exposed by the ActiveX object, and also to
+get/set properties or call methods by name.  The Python
+implementation automatically handles converting parameters and
+return values to/from the types expected by the ActiveX code as
+specified by the TypeInfo.
+");
+
+
+class wxActiveXWindow : public wxWindow
+{
+public:
+    %pythonAppend wxActiveXWindow      "self._setOORInfo(self)"
+
+    DocCtorStr(
+        wxActiveXWindow( wxWindow* parent, const CLSID& clsId, wxWindowID id = -1,
+                         const wxPoint& pos = wxDefaultPosition,
+                         const wxSize& size = wxDefaultSize,
+                         long style = 0,
+                         const wxString& name = wxPyPanelNameStr),
+        "Creates an ActiveX control from the clsID given and makes it act
+as much like a regular wx.Window as possible.");
+
+    DocDeclStr(
+        const CLSID& , GetCLSID() const,
+        "Return the CLSID used to construct this ActiveX window");
+    
+
+    DocDeclStr(
+        int , GetAXEventCount() const,
+        "Number of events defined for this control");
+
+    DocDeclStr(
+        const wxFuncX& , GetAXEventDesc(int idx) const,
+        "Returns event description by index");
+
+
+    DocDeclStr(
+        int , GetAXPropCount() const,
+        "Number of properties defined for this control");
+
+    %nokwargs GetAXPropDesc;
+    DocStr(GetPropDesc, "Returns property description by index or by name");
+    const wxPropX& GetAXPropDesc(int idx) const;
+    const wxPropX& GetAXPropDesc(const wxString& name) const;
+
+
+
+    DocDeclStr(
+        int , GetAXMethodCount() const,
+        "Number of methods defined for this control");
+
+    %nokwargs GetAXMethodDesc;
+    DocStr(GetMethodDesc, "Returns method description by index or name");
+    const wxFuncX& GetAXMethodDesc(int idx) const;
+    const wxFuncX& GetAXMethodDesc(const wxString& name) const;
+
+
+    DocDeclStr(
+        const wxFuncXArray& , GetAXEvents(),
+        "Returns a sequence of FuncX objects describing the events
+available for this ActiveX object.");
+
+    DocDeclStr(
+        const wxFuncXArray& , GetAXMethods(),
+        "Returns a sequence of FuncX objects describing the methods
+available for this ActiveX object.");
+
+    DocDeclStr(
+        const wxPropXArray& , GetAXProperties(),
+        "Returns a sequence of PropX objects describing the properties
+available for this ActiveX object.");
+
+
+
+    DocDeclStr(
+        void , SetAXProp(const wxString& name, PyObject* value),
+        "Set a property of the ActiveX object by name.");
+
+
+    DocDeclStr(
+        PyObject* , GetAXProp(const wxString& name),
+        "Get the value of an ActiveX property by name.");
+
+    
+    %nokwargs _CallAXMethod;
+    DocDeclStr(
+        PyObject* , _CallAXMethod(const wxString& name, PyObject* args),
+        "The implementation for CallMethod.  Calls an ActiveX method, by
+name passing the parameters given in args.");
+    %pythoncode {
+        def CallAXMethod(self, name, *args):
+            """
+            Front-end for _CallMethod.  Simply passes all positional args
+            after the name as a single tuple to _CallMethod.
+            """
+            return self._CallAXMethod(name, args)
+    }
+};
+
+//---------------------------------------------------------------------------
+%newgroup
+
+DocDeclStr(
+    wxEventType , RegisterActiveXEvent(const wxString& eventName),
+    "Creates a standard wx event ID for the given eventName.");
+
+
+
+DocStr(wxActiveXEvent,
+"An instance of ActiveXEvent is sent to the handler for all bound
+ActiveX events.  Any event parameters from the ActiveX cntrol are
+turned into attributes of the Python proxy for this event object.
+Additionally, there is a property called eventName that will
+return (suprizingly <wink>) the name of the ActiveX event.");
+
+class wxActiveXEvent : public wxCommandEvent
+{
+public:
+    %feature("shadow") EventName "eventName = property(_activex.ActiveXEvent_EventName)";
+    wxString EventName();
+
+    %extend {
+        DocStr(_preInit,
+"This is called by the EventThunker before calling the handler.
+We'll convert and load the ActiveX event parameters into
+attributes of the Python event object.");
+        void _preInit(PyObject* pyself) {
+            wxPyBeginBlockThreads();
+            PyObject* pList = PyList_New(0);
+            PyObject_SetAttrString(pyself, "paramList", pList);
+            Py_DECREF(pList);
+            for (int i=0; i<self->ParamCount(); i+=1) {
+                PyObject* name = PyString_FromString((char*)self->ParamName(i).mb_str());
+                PyObject* val = _Variant2PyObj((*self)[i], True);
+                PyObject_SetAttr(pyself, name, val);
+                PyList_Append(pList, name);
+                Py_DECREF(val);
+                Py_DECREF(name);
+            }
+            wxPyEndBlockThreads();
+        }
+    }
+};    
+
+//---------------------------------------------------------------------------
+
+%{
+
+// Caller should already have the GIL!
+wxVariant _PyObj2Variant(PyObject* value)
+{
+    wxVariant rval;
+
+    if (value == Py_None)
+        return rval;
+    
+    else if (PyBool_Check(value))
+        rval = (value == Py_True) ? true : false;
+
+    else if (PyInt_Check(value))
+        rval = PyInt_AS_LONG(value);
+
+    else if (PyFloat_Check(value))
+        rval = PyFloat_AS_DOUBLE(value);
+
+    else if (PyString_Check(value) || PyUnicode_Check(value))
+        rval = Py2wxString(value);
+
+    // TODO:    PyList of strings --> wxArrayString
+    //          wxDateTime
+    //          list of objects
+
+    else {
+        PyErr_SetString(PyExc_TypeError, "Unsupported object type in _PyObj2Variant");
+        rval = (long)0;
+    }
+
+    return rval;
+}
+
+// Caller should already have the GIL!
+PyObject* _Variant2PyObj(wxVariant& value, bool useNone)
+{
+    PyObject* rval = NULL;
+
+    if (value.IsNull()) {
+        rval = Py_None;
+        Py_INCREF(rval);
+    }
+    
+    // should "char" be treated as an int or as a string?
+    else if (value.IsType(_T("char")) || value.IsType(_T("long")))
+        rval = PyInt_FromLong(value);
+    
+    else if (value.IsType(_T("double")))
+        rval = PyFloat_FromDouble(value);
+
+    else if (value.IsType(_T("bool"))) 
+        rval = PyBool_FromLong((bool)value);
+    
+    else if (value.IsType(_T("string")))
+        rval = wx2PyString(value);
+
+    else {
+        if (useNone) {
+            rval = Py_None;
+            Py_INCREF(rval);
+        }
+        else {
+            PyErr_SetString(PyExc_TypeError, "Unsupported object type in _Variant2PyObj");
+        }
+    }
+    return rval;
+}
+
+
+wxString _VARTYPEname(VARTYPE vt)
+{
+    if (vt & VT_BYREF)
+        vt &= ~(VT_BYREF);
+
+    switch(vt) {
+    case VT_VARIANT:
+        return _T("VT_VARIANT");
+        
+    // 1 byte chars
+    case VT_I1:
+    case VT_UI1:
+    // 2 byte shorts
+    case VT_I2:
+    case VT_UI2:
+    // 4 bytes longs
+    case VT_I4:
+    case VT_UI4:
+    case VT_INT:
+    case VT_UINT:
+    case VT_ERROR:
+        return _T("int");
+
+    // 4 byte floats
+    case VT_R4:
+    // 8 byte doubles
+    case VT_R8:
+    // decimals are converted from doubles too
+    case VT_DECIMAL:
+        return _T("double");
+        
+    case VT_BOOL:
+        return _T("bool");
+        
+    case VT_DATE:
+        return _T("wx.DateTime");
+        
+    case VT_BSTR:
+        return _T("string");
+
+    case VT_UNKNOWN:
+        return _T("VT_UNKNOWN");
+        
+    case VT_DISPATCH: 
+        return _T("VT_DISPATCH");
+
+    case VT_EMPTY:
+        return _T("VT_EMPTY");
+        
+    case VT_NULL:
+        return _T("VT_NULL");
+        
+    case VT_VOID:
+        return _T("VT_VOID");
+        
+    default:
+        wxString msg;
+        msg << _T("unsupported type ") << vt;
+        return msg;
+    }
+}
+
+%}
+
+//---------------------------------------------------------------------------
+//---------------------------------------------------------------------------
+%newgroup
+
+
+%{
+// A class derived from out wxActiveXWindow for the IE WebBrowser
+// control that will serve as a base class for a Python
+// implementation.  This is done so we can "eat our own dog food"
+// and use a class at least mostly generated by genaxmodule, but
+// also get some of the extra stuff like loading a document from
+// a string or a stream, getting text contents, etc. that
+// Lindsay's version gives us.
+//
+
+#include <wx/mstream.h>
+#include <oleidl.h>
+#include <winerror.h>
+#include <exdispid.h>
+#include <exdisp.h>
+#include <olectl.h>
+#include <Mshtml.h>
+#include <sstream>
+
+#include "IEHtmlStream.h"
+
+class wxIEHtmlWindowBase : public wxActiveXWindow {
+private:
+    wxAutoOleInterface<IWebBrowser2>  m_webBrowser;
+
+public:
+
+    wxIEHtmlWindowBase ( wxWindow* parent, const CLSID& clsId, wxWindowID id = -1,
+                         const wxPoint& pos = wxDefaultPosition,
+                         const wxSize& size = wxDefaultSize,
+                         long style = 0,
+                         const wxString& name = wxPyPanelNameStr)
+        : wxActiveXWindow(parent, clsId, id, pos, size, style, name)
+    {
+        HRESULT hret;
+
+        // Get IWebBrowser2 Interface
+        hret = m_webBrowser.QueryInterface(IID_IWebBrowser2, m_ActiveX);
+        wxASSERT(SUCCEEDED(hret));        
+    }
+
+
+    void SetCharset(const wxString& charset)
+    {
+        HRESULT hret;
+        
+        // HTML Document ?
+        IDispatch *pDisp = NULL;
+        hret = m_webBrowser->get_Document(&pDisp);
+        wxAutoOleInterface<IDispatch> disp(pDisp);
+
+        if (disp.Ok())
+        {
+            wxAutoOleInterface<IHTMLDocument2> doc(IID_IHTMLDocument2, disp);
+            if (doc.Ok())
+                doc->put_charset((BSTR) (const wchar_t *) charset.wc_str(wxConvUTF8));
+            //doc->put_charset((BSTR) wxConvUTF8.cMB2WC(charset).data());
+        }
+    }
+
+
+    bool LoadString(const wxString& html)
+    {
+        char *data = NULL;
+        size_t len = html.length();
+        len *= sizeof(wxChar);
+        data = (char *) malloc(len);
+        memcpy(data, html.c_str(), len);
+        return LoadStream(new wxOwnedMemInputStream(data, len));
+    }
+
+    
+    bool LoadStream(IStreamAdaptorBase *pstrm)
+    {
+        // need to prepend this as poxy MSHTML will not recognise a HTML comment
+        // as starting a html document and treats it as plain text
+        // Does nayone know how to force it to html mode ?
+        pstrm->prepend = "<html>";
+
+        // strip leading whitespace as it can confuse MSHTML
+        wxAutoOleInterface<IStream> strm(pstrm);
+
+        // Document Interface
+        IDispatch *pDisp = NULL;
+        HRESULT hret = m_webBrowser->get_Document(&pDisp);
+        if (! pDisp)
+            return false;
+        wxAutoOleInterface<IDispatch> disp(pDisp);
+
+
+        // get IPersistStreamInit
+        wxAutoOleInterface<IPersistStreamInit>
+            pPersistStreamInit(IID_IPersistStreamInit, disp);
+
+        if (pPersistStreamInit.Ok())
+        {
+            HRESULT hr = pPersistStreamInit->InitNew();
+            if (SUCCEEDED(hr))
+                hr = pPersistStreamInit->Load(strm);
+
+            return SUCCEEDED(hr);
+        }
+        else
+            return false;
+    }
+
+    bool LoadStream(wxInputStream *is)
+    {
+        // wrap reference around stream
+        IwxStreamAdaptor *pstrm = new IwxStreamAdaptor(is);
+        pstrm->AddRef();
+
+        return LoadStream(pstrm);
+    }
+
+    
+    wxString GetStringSelection(bool asHTML)
+    {
+        wxAutoOleInterface<IHTMLTxtRange> tr(wxieGetSelRange(m_oleObject));
+        if (! tr)
+            return wxEmptyString;
+
+        BSTR text = NULL;
+        HRESULT hr = E_FAIL;
+    
+        if (asHTML)
+            hr = tr->get_htmlText(&text);
+        else
+            hr = tr->get_text(&text);
+        if (hr != S_OK)
+            return wxEmptyString;
+
+        wxString s = text;
+        SysFreeString(text);
+
+        return s;
+    };
+
+    wxString GetText(bool asHTML)
+    {
+        if (! m_webBrowser.Ok())
+            return wxEmptyString;
+
+        // get document dispatch interface
+        IDispatch *iDisp = NULL;
+        HRESULT hr = m_webBrowser->get_Document(&iDisp);
+        if (hr != S_OK)
+            return wxEmptyString;
+
+        // Query for Document Interface
+        wxAutoOleInterface<IHTMLDocument2> hd(IID_IHTMLDocument2, iDisp);
+        iDisp->Release();
+
+        if (! hd.Ok())
+            return wxEmptyString;
+
+        // get body element
+        IHTMLElement *_body = NULL;
+        hd->get_body(&_body);
+        if (! _body)
+            return wxEmptyString;
+        wxAutoOleInterface<IHTMLElement> body(_body);
+
+        // get inner text
+        BSTR text = NULL;
+        hr = E_FAIL;
+    
+        if (asHTML)
+            hr = body->get_innerHTML(&text);
+        else
+            hr = body->get_innerText(&text);
+        if (hr != S_OK)
+            return wxEmptyString;
+
+        wxString s = text;
+        SysFreeString(text);
+
+        return s;   
+    }
+
+
+//     void wxIEHtmlWin::SetEditMode(bool seton)
+//     {
+//         m_bAmbientUserMode = ! seton;
+//         AmbientPropertyChanged(DISPID_AMBIENT_USERMODE);
+//     };
+
+//     bool wxIEHtmlWin::GetEditMode()
+//     {
+//         return ! m_bAmbientUserMode;
+//     };
+};
+%}
+
+
+// we'll document it in the derived Python class
+%feature("noautodoc") wxIEHtmlWindowBase;
+%feature("noautodoc") wxIEHtmlWindowBase::SetCharset;
+%feature("noautodoc") wxIEHtmlWindowBase::LoadString;
+%feature("noautodoc") wxIEHtmlWindowBase::LoadStream;
+%feature("noautodoc") wxIEHtmlWindowBase::GetStringSelection;
+%feature("noautodoc") wxIEHtmlWindowBase::GetText;
+
+
+class wxIEHtmlWindowBase : public wxActiveXWindow {
+public:
+
+    wxIEHtmlWindowBase ( wxWindow* parent, const CLSID& clsId, wxWindowID id = -1,
+                         const wxPoint& pos = wxDefaultPosition,
+                         const wxSize& size = wxDefaultSize,
+                         long style = 0,
+                         const wxString& name = wxPyPanelNameStr);
+
+    void SetCharset(const wxString& charset);
+    bool LoadString(const wxString& html);
+    bool LoadStream(wxInputStream *is);
+    wxString GetStringSelection(bool asHTML);
+    wxString GetText(bool asHTML);
+};
+
+
+
+
+
+
+
+
+
+
+
+#if 0
+enum wxIEHtmlRefreshLevel
+{
+    wxIEHTML_REFRESH_NORMAL = 0,
+    wxIEHTML_REFRESH_IFEXPIRED = 1,
+    wxIEHTML_REFRESH_CONTINUE = 2,
+    wxIEHTML_REFRESH_COMPLETELY = 3
+};
+
+DocStr(wxIEHtmlWin,
+"");
+class wxIEHtmlWin : public wxWindow 
+{
+public:
+    %pythonAppend wxIEHtmlWin      "self._setOORInfo(self)"
+
+    wxIEHtmlWin(wxWindow * parent, wxWindowID id = -1,
+                const wxPoint& pos = wxDefaultPosition,
+                const wxSize& size = wxDefaultSize,
+                long style = 0,
+                const wxString& name = wxPyPanelNameStr);
+
+    void LoadUrl(const wxString& url);
+    bool LoadString(wxString html);
+    bool LoadStream(wxInputStream *is);
+
+    %pythoncode { Navigate = LoadUrl }
+
+    void SetCharset(wxString charset);
+    void SetEditMode(bool seton);
+    bool GetEditMode();
+    wxString GetStringSelection(bool asHTML = false);
+    wxString GetText(bool asHTML = false);
+
+    bool GoBack();
+    bool GoForward();
+    bool GoHome();
+    bool GoSearch();
+    %name(RefreshPage)bool Refresh(wxIEHtmlRefreshLevel level);
+    bool Stop();
+};
+
+
+
+%pythoncode {
+wxEVT_COMMAND_MSHTML_BEFORENAVIGATE2  = RegisterActiveXEvent('BeforeNavigate2')
+wxEVT_COMMAND_MSHTML_NEWWINDOW2       = RegisterActiveXEvent('NewWindow2')
+wxEVT_COMMAND_MSHTML_DOCUMENTCOMPLETE = RegisterActiveXEvent('DocumentComplete')
+wxEVT_COMMAND_MSHTML_PROGRESSCHANGE   = RegisterActiveXEvent('ProgressChange')
+wxEVT_COMMAND_MSHTML_STATUSTEXTCHANGE = RegisterActiveXEvent('StatusTextChange')
+wxEVT_COMMAND_MSHTML_TITLECHANGE      = RegisterActiveXEvent('TitleChange')
+
+EVT_MSHTML_BEFORENAVIGATE2      = wx.PyEventBinder(wxEVT_COMMAND_MSHTML_BEFORENAVIGATE2, 1)
+EVT_MSHTML_NEWWINDOW2           = wx.PyEventBinder(wxEVT_COMMAND_MSHTML_NEWWINDOW2, 1)
+EVT_MSHTML_DOCUMENTCOMPLETE     = wx.PyEventBinder(wxEVT_COMMAND_MSHTML_DOCUMENTCOMPLETE, 1)
+EVT_MSHTML_PROGRESSCHANGE       = wx.PyEventBinder(wxEVT_COMMAND_MSHTML_PROGRESSCHANGE, 1)
+EVT_MSHTML_STATUSTEXTCHANGE     = wx.PyEventBinder(wxEVT_COMMAND_MSHTML_STATUSTEXTCHANGE, 1)
+EVT_MSHTML_TITLECHANGE          = wx.PyEventBinder(wxEVT_COMMAND_MSHTML_TITLECHANGE, 1)
+}
+
+#endif
+
+//---------------------------------------------------------------------------
+// Include some extra Python code into the proxy module
+
+%pythoncode "_activex_ex.py"
+
+//---------------------------------------------------------------------------