diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:18:55 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:18:55 (GMT) |
commit | e5fcad302d86d316390c6b0f62759a067313e8a9 (patch) | |
tree | c2afbf6f1066b6ce261f14341cf6d310e5595bc1 /src/activeqt/control/qaxserverbase.cpp | |
download | Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2 |
Long live Qt 4.5!
Diffstat (limited to 'src/activeqt/control/qaxserverbase.cpp')
-rw-r--r-- | src/activeqt/control/qaxserverbase.cpp | 4490 |
1 files changed, 4490 insertions, 0 deletions
diff --git a/src/activeqt/control/qaxserverbase.cpp b/src/activeqt/control/qaxserverbase.cpp new file mode 100644 index 0000000..f3e1dff --- /dev/null +++ b/src/activeqt/control/qaxserverbase.cpp @@ -0,0 +1,4490 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the ActiveQt framework of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#define QT_NO_CAST_TO_ASCII + +#ifndef QT_NO_WIN_ACTIVEQT + +#include <qabstracteventdispatcher.h> +#include <qapplication.h> +#include <qbuffer.h> +#include <qdatastream.h> +#include <qdebug.h> +#include <qevent.h> +#include <qeventloop.h> +#include <qfile.h> +#include <qpointer.h> +#include <qhash.h> +#include <qmap.h> +#include <qmenubar.h> +#include <qmenu.h> +#include <qmetaobject.h> +#include <qpixmap.h> +#include <qstatusbar.h> +#include <qwhatsthis.h> +#include <ocidl.h> +#include <olectl.h> +#include <private/qcoreapplication_p.h> + +#include "qaxfactory.h" +#include "qaxbindable.h" +#include "qaxaggregated.h" + +#include "../shared/qaxtypes.h" + +#if defined Q_CC_GNU +# include <w32api.h> +#endif + +#ifndef Q_OS_WIN64 +#define ULONG_PTR DWORD +#endif + +QT_BEGIN_NAMESPACE + +extern HHOOK qax_hhook; + +// in qaxserver.cpp +extern ITypeLib *qAxTypeLibrary; +extern QAxFactory *qAxFactory(); +extern unsigned long qAxLock(); +extern unsigned long qAxUnlock(); +extern HANDLE qAxInstance; +extern bool qAxOutProcServer; + +static int invokeCount = 0; + +#ifdef QT_DEBUG +unsigned long qaxserverbase_instance_count = 0; +#endif + +// in qaxserverdll.cpp +extern bool qax_ownQApp; + +struct QAxExceptInfo +{ + QAxExceptInfo(int c, const QString &s, const QString &d, const QString &x) + : code(c), src(s), desc(d), context(x) + { + } + int code; + QString src; + QString desc; + QString context; +}; + + +bool qt_sendSpontaneousEvent(QObject*, QEvent*); + +/* + \class QAxServerBase + \brief The QAxServerBase class is an ActiveX control hosting a QWidget. + + \internal +*/ +class QAxServerBase : + public QObject, + public IAxServerBase, + public IDispatch, + public IOleObject, + public IOleControl, +#if defined Q_CC_GNU +# if (__W32API_MAJOR_VERSION < 2 || (__W32API_MAJOR_VERSION == 2 && __W32API_MINOR_VERSION < 5)) + public IViewObject, // this should not be needed as IViewObject2 is meant to inherit from this, + // untill the mingw headers are fixed this will need to stay. +# endif +#endif + public IViewObject2, + public IOleInPlaceObject, + public IOleInPlaceActiveObject, + public IProvideClassInfo2, + public IConnectionPointContainer, + public IPersistStream, + public IPersistStreamInit, + public IPersistStorage, + public IPersistPropertyBag, + public IPersistFile, + public IDataObject +{ +public: + typedef QMap<QUuid,IConnectionPoint*> ConnectionPoints; + typedef QMap<QUuid,IConnectionPoint*>::Iterator ConnectionPointsIterator; + + QAxServerBase(const QString &classname, IUnknown *outerUnknown); + QAxServerBase(QObject *o); + + void init(); + + ~QAxServerBase(); + +// Window creation + HWND create(HWND hWndParent, RECT& rcPos); + HMENU createPopup(QMenu *popup, HMENU oldMenu = 0); + void createMenu(QMenuBar *menuBar); + void removeMenu(); + + static LRESULT CALLBACK ActiveXProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +// Object registration with OLE + void registerActiveObject(IUnknown *object); + void revokeActiveObject(); + +// IUnknown + unsigned long WINAPI AddRef() + { + if (m_outerUnknown) + return m_outerUnknown->AddRef(); + + EnterCriticalSection(&refCountSection); + unsigned long r = ++ref; + LeaveCriticalSection(&refCountSection); + + return r; + } + unsigned long WINAPI Release() + { + if (m_outerUnknown) + return m_outerUnknown->Release(); + + EnterCriticalSection(&refCountSection); + unsigned long r = --ref; + LeaveCriticalSection(&refCountSection); + + if (!r) { + delete this; + return 0; + } + return r; + } + HRESULT WINAPI QueryInterface(REFIID iid, void **iface); + HRESULT InternalQueryInterface(REFIID iid, void **iface); + +// IAxServerBase + IUnknown *clientSite() const + { + return m_spClientSite; + } + + void emitPropertyChanged(const char*); + bool emitRequestPropertyChange(const char*); + QObject *qObject() const + { + return theObject; + } + void ensureMetaData(); + bool isPropertyExposed(int index); + + void reportError(int code, const QString &src, const QString &desc, const QString &context) + { + if (exception) + delete exception; + exception = new QAxExceptInfo(code, src, desc, context); + } + +// IDispatch + STDMETHOD(GetTypeInfoCount)(UINT* pctinfo); + STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo** pptinfo); + STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid); + STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid, + LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, + EXCEPINFO* pexcepinfo, UINT* puArgErr); + +// IProvideClassInfo + STDMETHOD(GetClassInfo)(ITypeInfo** pptinfo); + +// IProvideClassInfo2 + STDMETHOD(GetGUID)(DWORD dwGuidKind, GUID* pGUID); + +// IOleObject + STDMETHOD(Advise)(IAdviseSink* pAdvSink, DWORD* pdwConnection); + STDMETHOD(Close)(DWORD dwSaveOption); + STDMETHOD(DoVerb)(LONG iVerb, LPMSG lpmsg, IOleClientSite* pActiveSite, LONG lindex, HWND hwndParent, LPCRECT lprcPosRect); + STDMETHOD(EnumAdvise)(IEnumSTATDATA** ppenumAdvise); + STDMETHOD(EnumVerbs)(IEnumOLEVERB** ppEnumOleVerb); + STDMETHOD(GetClientSite)(IOleClientSite** ppClientSite); + STDMETHOD(GetClipboardData)(DWORD dwReserved, IDataObject** ppDataObject); + STDMETHOD(GetExtent)(DWORD dwDrawAspect, SIZEL* psizel); + STDMETHOD(GetMiscStatus)(DWORD dwAspect, DWORD *pdwStatus); + STDMETHOD(GetMoniker)(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker** ppmk); + STDMETHOD(GetUserClassID)(CLSID* pClsid); + STDMETHOD(GetUserType)(DWORD dwFormOfType, LPOLESTR *pszUserType); + STDMETHOD(InitFromData)(IDataObject* pDataObject, BOOL fCreation, DWORD dwReserved); + STDMETHOD(IsUpToDate)(); + STDMETHOD(SetClientSite)(IOleClientSite* pClientSite); + STDMETHOD(SetColorScheme)(LOGPALETTE* pLogPal); + STDMETHOD(SetExtent)(DWORD dwDrawAspect, SIZEL* psizel); + STDMETHOD(SetHostNames)(LPCOLESTR szContainerApp, LPCOLESTR szContainerObj); + STDMETHOD(SetMoniker)(DWORD dwWhichMoniker, IMoniker* ppmk); + STDMETHOD(Unadvise)(DWORD dwConnection); + STDMETHOD(Update)(); + +// IViewObject + STDMETHOD(Draw)(DWORD dwAspect, LONG lIndex, void *pvAspect, DVTARGETDEVICE *ptd, + HDC hicTargetDevice, HDC hdcDraw, LPCRECTL lprcBounds, LPCRECTL lprcWBounds, + BOOL(__stdcall*pfnContinue)(ULONG_PTR), ULONG_PTR dwContinue); + STDMETHOD(GetColorSet)(DWORD dwDrawAspect, LONG lindex, void *pvAspect, DVTARGETDEVICE *ptd, + HDC hicTargetDev, LOGPALETTE **ppColorSet); + STDMETHOD(Freeze)(DWORD dwAspect, LONG lindex, void *pvAspect, DWORD *pdwFreeze); + STDMETHOD(Unfreeze)(DWORD dwFreeze); + STDMETHOD(SetAdvise)(DWORD aspects, DWORD advf, IAdviseSink *pAdvSink); + STDMETHOD(GetAdvise)(DWORD *aspects, DWORD *advf, IAdviseSink **pAdvSink); + +// IViewObject2 + STDMETHOD(GetExtent)(DWORD dwAspect, LONG lindex, DVTARGETDEVICE *ptd, LPSIZEL lpsizel); + +// IOleControl + STDMETHOD(FreezeEvents)(BOOL); + STDMETHOD(GetControlInfo)(LPCONTROLINFO); + STDMETHOD(OnAmbientPropertyChange)(DISPID); + STDMETHOD(OnMnemonic)(LPMSG); + +// IOleWindow + STDMETHOD(GetWindow)(HWND *pHwnd); + STDMETHOD(ContextSensitiveHelp)(BOOL fEnterMode); + +// IOleInPlaceObject + STDMETHOD(InPlaceDeactivate)(); + STDMETHOD(UIDeactivate)(); + STDMETHOD(SetObjectRects)(LPCRECT lprcPosRect, LPCRECT lprcClipRect); + STDMETHOD(ReactivateAndUndo)(); + +// IOleInPlaceActiveObject + STDMETHOD(TranslateAcceleratorW)(MSG *pMsg); + STDMETHOD(TranslateAcceleratorA)(MSG *pMsg); + STDMETHOD(OnFrameWindowActivate)(BOOL); + STDMETHOD(OnDocWindowActivate)(BOOL fActivate); + STDMETHOD(ResizeBorder)(LPCRECT prcBorder, IOleInPlaceUIWindow *pUIWindow, BOOL fFrameWindow); + STDMETHOD(EnableModeless)(BOOL); + +// IConnectionPointContainer + STDMETHOD(EnumConnectionPoints)(IEnumConnectionPoints**); + STDMETHOD(FindConnectionPoint)(REFIID, IConnectionPoint**); + +// IPersist + STDMETHOD(GetClassID)(GUID*clsid) + { + *clsid = qAxFactory()->classID(class_name); + return S_OK; + } + +// IPersistStreamInit + STDMETHOD(InitNew)(VOID); + STDMETHOD(IsDirty)(); + STDMETHOD(Load)(IStream *pStm); + STDMETHOD(Save)(IStream *pStm, BOOL fClearDirty); + STDMETHOD(GetSizeMax)(ULARGE_INTEGER *pcbSize); + +// IPersistPropertyBag + STDMETHOD(Load)(IPropertyBag *, IErrorLog *); + STDMETHOD(Save)(IPropertyBag *, BOOL, BOOL); + +// IPersistStorage + STDMETHOD(InitNew)(IStorage *pStg); + STDMETHOD(Load)(IStorage *pStg); + STDMETHOD(Save)(IStorage *pStg, BOOL fSameAsLoad); + STDMETHOD(SaveCompleted)(IStorage *pStgNew); + STDMETHOD(HandsOffStorage)(); + +// IPersistFile + STDMETHOD(SaveCompleted)(LPCOLESTR fileName); + STDMETHOD(GetCurFile)(LPOLESTR *currentFile); + STDMETHOD(Load)(LPCOLESTR fileName, DWORD mode); + STDMETHOD(Save)(LPCOLESTR fileName, BOOL fRemember); + +// IDataObject + STDMETHOD(GetData)(FORMATETC *pformatetcIn, STGMEDIUM *pmedium); + STDMETHOD(GetDataHere)(FORMATETC* /* pformatetc */, STGMEDIUM* /* pmedium */); + STDMETHOD(QueryGetData)(FORMATETC* /* pformatetc */); + STDMETHOD(GetCanonicalFormatEtc)(FORMATETC* /* pformatectIn */,FORMATETC* /* pformatetcOut */); + STDMETHOD(SetData)(FORMATETC* /* pformatetc */, STGMEDIUM* /* pmedium */, BOOL /* fRelease */); + STDMETHOD(EnumFormatEtc)(DWORD /* dwDirection */, IEnumFORMATETC** /* ppenumFormatEtc */); + STDMETHOD(DAdvise)(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection); + STDMETHOD(DUnadvise)(DWORD dwConnection); + STDMETHOD(EnumDAdvise)(IEnumSTATDATA **ppenumAdvise); + +// QObject + int qt_metacall(QMetaObject::Call, int index, void **argv); + + bool eventFilter(QObject *o, QEvent *e); +private: + void update(); + void resize(const QSize &newSize); + void updateGeometry(); + void updateMask(); + bool internalCreate(); + void internalBind(); + void internalConnect(); + HRESULT internalActivate(); + + friend class QAxBindable; + friend class QAxPropertyPage; + + QAxAggregated *aggregatedObject; + ConnectionPoints points; + + union { + QWidget *widget; + QObject *object; + } qt; + QPointer<QObject> theObject; + unsigned isWidget :1; + unsigned ownObject :1; + unsigned initNewCalled :1; + unsigned dirtyflag :1; + unsigned hasStockEvents :1; + unsigned stayTopLevel :1; + unsigned isInPlaceActive :1; + unsigned isUIActive :1; + unsigned wasUIActive :1; + unsigned inDesignMode :1; + unsigned canTakeFocus :1; + short freezeEvents; + + HWND m_hWnd; + + HMENU hmenuShared; + HOLEMENU holemenu; + HWND hwndMenuOwner; + QMap<HMENU, QMenu*> menuMap; + QMap<UINT, QAction*> actionMap; + QPointer<QMenuBar> menuBar; + QPointer<QStatusBar> statusBar; + QPointer<QMenu> currentPopup; + QAxExceptInfo *exception; + + CRITICAL_SECTION refCountSection; + CRITICAL_SECTION createWindowSection; + + unsigned long ref; + unsigned long ole_ref; + + QString class_name; + QString currentFileName; + + QHash<long, int> indexCache; + QHash<int,DISPID> signalCache; + + IUnknown *m_outerUnknown; + IAdviseSink *m_spAdviseSink; + QList<STATDATA> adviseSinks; + IOleClientSite *m_spClientSite; + IOleInPlaceSiteWindowless *m_spInPlaceSite; + IOleInPlaceFrame *m_spInPlaceFrame; + ITypeInfo *m_spTypeInfo; + IStorage *m_spStorage; + QSize m_currentExtent; +}; + +class QAxServerAggregate : public IUnknown +{ +public: + QAxServerAggregate(const QString &className, IUnknown *outerUnknown) + : m_outerUnknown(outerUnknown), ref(0) + { + object = new QAxServerBase(className, outerUnknown); + object->registerActiveObject(this); + + InitializeCriticalSection(&refCountSection); + InitializeCriticalSection(&createWindowSection); + } + ~QAxServerAggregate() + { + DeleteCriticalSection(&refCountSection); + DeleteCriticalSection(&createWindowSection); + + delete object; + } + +// IUnknown + unsigned long WINAPI AddRef() + { + EnterCriticalSection(&refCountSection); + unsigned long r = ++ref; + LeaveCriticalSection(&refCountSection); + + return r; + } + unsigned long WINAPI Release() + { + EnterCriticalSection(&refCountSection); + unsigned long r = --ref; + LeaveCriticalSection(&refCountSection); + + if (!r) { + delete this; + return 0; + } + return r; + } + HRESULT WINAPI QueryInterface(REFIID iid, void **iface) + { + *iface = 0; + + HRESULT res = E_NOINTERFACE; + if (iid == IID_IUnknown) { + *iface = (IUnknown*)this; + AddRef(); + return S_OK; + } + return object->InternalQueryInterface(iid, iface); + } + +private: + QAxServerBase *object; + IUnknown *m_outerUnknown; + unsigned long ref; + + CRITICAL_SECTION refCountSection; + CRITICAL_SECTION createWindowSection; +}; + +bool QAxFactory::createObjectWrapper(QObject *object, IDispatch **wrapper) +{ + *wrapper = 0; + QAxServerBase *obj = new QAxServerBase(object); + obj->QueryInterface(IID_IDispatch, (void**)wrapper); + if (*wrapper) + return true; + + delete obj; + return false; +} + + +/* + Helper class to enumerate all supported event interfaces. +*/ +class QAxSignalVec : public IEnumConnectionPoints +{ +public: + QAxSignalVec(const QAxServerBase::ConnectionPoints &points) + : cpoints(points), ref(0) + { + InitializeCriticalSection(&refCountSection); + for (QAxServerBase::ConnectionPointsIterator i = cpoints.begin(); i != cpoints.end(); ++i) + (*i)->AddRef(); + } + QAxSignalVec(const QAxSignalVec &old) + { + InitializeCriticalSection(&refCountSection); + ref = 0; + cpoints = old.cpoints; + for (QAxServerBase::ConnectionPointsIterator i = cpoints.begin(); i != cpoints.end(); ++i) + (*i)->AddRef(); + it = old.it; + } + ~QAxSignalVec() + { + for (QAxServerBase::ConnectionPointsIterator i = cpoints.begin(); i != cpoints.end(); ++i) + (*i)->Release(); + + DeleteCriticalSection(&refCountSection); + } + + unsigned long __stdcall AddRef() + { + EnterCriticalSection(&refCountSection); + unsigned long r = ++ref; + LeaveCriticalSection(&refCountSection); + return ++r; + } + unsigned long __stdcall Release() + { + EnterCriticalSection(&refCountSection); + unsigned long r = --ref; + LeaveCriticalSection(&refCountSection); + + if (!r) { + delete this; + return 0; + } + return r; + } + STDMETHOD(QueryInterface)(REFIID iid, void **iface) + { + *iface = 0; + if (iid == IID_IUnknown) + *iface = this; + else if (iid == IID_IEnumConnectionPoints) + *iface = this; + else + return E_NOINTERFACE; + + AddRef(); + return S_OK; + } + STDMETHOD(Next)(ULONG cConnections, IConnectionPoint **cpoint, ULONG *pcFetched) + { + unsigned long i; + for (i = 0; i < cConnections; i++) { + if (it == cpoints.end()) + break; + IConnectionPoint *cp = *it; + cp->AddRef(); + cpoint[i] = cp; + ++it; + } + *pcFetched = i; + return i == cConnections ? S_OK : S_FALSE; + } + STDMETHOD(Skip)(ULONG cConnections) + { + while (cConnections) { + ++it; + --cConnections; + if (it == cpoints.end()) + return S_FALSE; + } + return S_OK; + } + STDMETHOD(Reset)() + { + it = cpoints.begin(); + + return S_OK; + } + STDMETHOD(Clone)(IEnumConnectionPoints **ppEnum) + { + *ppEnum = new QAxSignalVec(*this); + (*ppEnum)->AddRef(); + + return S_OK; + } + + QAxServerBase::ConnectionPoints cpoints; + QAxServerBase::ConnectionPointsIterator it; + +private: + CRITICAL_SECTION refCountSection; + + unsigned long ref; +}; + +/* + Helper class to store and enumerate all connected event listeners. +*/ +class QAxConnection : public IConnectionPoint, + public IEnumConnections +{ +public: + typedef QList<CONNECTDATA> Connections; + typedef QList<CONNECTDATA>::Iterator Iterator; + + QAxConnection(QAxServerBase *parent, const QUuid &uuid) + : that(parent), iid(uuid), ref(1) + { + InitializeCriticalSection(&refCountSection); + } + QAxConnection(const QAxConnection &old) + { + InitializeCriticalSection(&refCountSection); + ref = 0; + connections = old.connections; + it = old.it; + that = old.that; + iid = old.iid; + QList<CONNECTDATA>::Iterator it = connections.begin(); + while (it != connections.end()) { + CONNECTDATA connection = *it; + ++it; + connection.pUnk->AddRef(); + } + } + ~QAxConnection() + { + DeleteCriticalSection(&refCountSection); + } + + unsigned long __stdcall AddRef() + { + EnterCriticalSection(&refCountSection); + unsigned long r = ++ref; + LeaveCriticalSection(&refCountSection); + return r; + } + unsigned long __stdcall Release() + { + EnterCriticalSection(&refCountSection); + unsigned long r = --ref; + LeaveCriticalSection(&refCountSection); + + if (!r) { + delete this; + return 0; + } + return r; + } + STDMETHOD(QueryInterface)(REFIID iid, void **iface) + { + *iface = 0; + if (iid == IID_IUnknown) + *iface = (IConnectionPoint*)this; + else if (iid == IID_IConnectionPoint) + *iface = this; + else if (iid == IID_IEnumConnections) + *iface = this; + else + return E_NOINTERFACE; + + AddRef(); + return S_OK; + } + STDMETHOD(GetConnectionInterface)(IID *pIID) + { + *pIID = iid; + return S_OK; + } + STDMETHOD(GetConnectionPointContainer)(IConnectionPointContainer **ppCPC) + { + return that->QueryInterface(IID_IConnectionPointContainer, (void**)ppCPC); + } + STDMETHOD(Advise)(IUnknown*pUnk, DWORD *pdwCookie) + { + { + IDispatch *checkImpl = 0; + pUnk->QueryInterface(iid, (void**)&checkImpl); + if (!checkImpl) + return CONNECT_E_CANNOTCONNECT; + checkImpl->Release(); + } + + CONNECTDATA cd; + cd.dwCookie = connections.count()+1; + cd.pUnk = pUnk; + cd.pUnk->AddRef(); + connections.append(cd); + + *pdwCookie = cd.dwCookie; + return S_OK; + } + STDMETHOD(Unadvise)(DWORD dwCookie) + { + QList<CONNECTDATA>::Iterator it = connections.begin(); + while (it != connections.end()) { + CONNECTDATA cd = *it; + if (cd.dwCookie == dwCookie) { + cd.pUnk->Release(); + connections.erase(it); + return S_OK; + } + ++it; + } + return CONNECT_E_NOCONNECTION; + } + STDMETHOD(EnumConnections)(IEnumConnections **ppEnum) + { + *ppEnum = this; + AddRef(); + + return S_OK; + } + STDMETHOD(Next)(ULONG cConnections, CONNECTDATA *cd, ULONG *pcFetched) + { + unsigned long i; + for (i = 0; i < cConnections; i++) { + if (it == connections.end()) + break; + cd[i] = *it; + cd[i].pUnk->AddRef(); + ++it; + } + if (pcFetched) + *pcFetched = i; + return i == cConnections ? S_OK : S_FALSE; + } + STDMETHOD(Skip)(ULONG cConnections) + { + while (cConnections) { + ++it; + --cConnections; + if (it == connections.end()) + return S_FALSE; + } + return S_OK; + } + STDMETHOD(Reset)() + { + it = connections.begin(); + + return S_OK; + } + STDMETHOD(Clone)(IEnumConnections **ppEnum) + { + *ppEnum = new QAxConnection(*this); + (*ppEnum)->AddRef(); + + return S_OK; + } + +private: + QAxServerBase *that; + QUuid iid; + Connections connections; + Iterator it; + + CRITICAL_SECTION refCountSection; + unsigned long ref; +}; + +// callback for DLL server to hook into non-Qt eventloop +LRESULT CALLBACK axs_FilterProc(int nCode, WPARAM wParam, LPARAM lParam) +{ + if (qApp && !invokeCount) + qApp->sendPostedEvents(); + + return CallNextHookEx(qax_hhook, nCode, wParam, lParam); +} + +// filter for executable case to hook into Qt eventloop +// for DLLs the client calls TranslateAccelerator +bool qax_winEventFilter(void *message) +{ + MSG *pMsg = (MSG*)message; + if (pMsg->message < WM_KEYFIRST || pMsg->message > WM_KEYLAST) + return false; + + bool ret = false; + QWidget *aqt = QWidget::find(pMsg->hwnd); + if (!aqt) + return ret; + + HWND baseHwnd = ::GetParent(aqt->winId()); + QAxServerBase *axbase = 0; + while (!axbase && baseHwnd) { +#ifdef GWLP_USERDATA + QT_WA({ + axbase = (QAxServerBase*)GetWindowLongPtrW(baseHwnd, GWLP_USERDATA); + }, { + axbase = (QAxServerBase*)GetWindowLongPtrA(baseHwnd, GWLP_USERDATA); + }); +#else + QT_WA({ + axbase = (QAxServerBase*)GetWindowLongW(baseHwnd, GWL_USERDATA); + }, { + axbase = (QAxServerBase*)GetWindowLongA(baseHwnd, GWL_USERDATA); + }); +#endif + + baseHwnd = ::GetParent(baseHwnd); + } + if (!axbase) + return ret; + + HRESULT hres = axbase->TranslateAcceleratorW(pMsg); + return hres == S_OK; +} + +extern void qWinMsgHandler(QtMsgType t, const char* str); + +// COM Factory class, mapping COM requests to ActiveQt requests. +// One instance of this class for each ActiveX the server can provide. +class QClassFactory : public IClassFactory2 +{ +public: + QClassFactory(CLSID clsid) + : ref(0), licensed(false) + { + InitializeCriticalSection(&refCountSection); + + // COM only knows the CLSID, but QAxFactory is class name based... + QStringList keys = qAxFactory()->featureList(); + for (QStringList::Iterator key = keys.begin(); key != keys.end(); ++key) { + if (qAxFactory()->classID(*key) == clsid) { + className = *key; + break; + } + } + + const QMetaObject *mo = qAxFactory()->metaObject(className); + if (mo) { + classKey = QLatin1String(mo->classInfo(mo->indexOfClassInfo("LicenseKey")).value()); + licensed = !classKey.isEmpty(); + } + } + + ~QClassFactory() + { + DeleteCriticalSection(&refCountSection); + } + + // IUnknown + unsigned long WINAPI AddRef() + { + EnterCriticalSection(&refCountSection); + unsigned long r = ++ref; + LeaveCriticalSection(&refCountSection); + return ++r; + } + unsigned long WINAPI Release() + { + EnterCriticalSection(&refCountSection); + unsigned long r = --ref; + LeaveCriticalSection(&refCountSection); + + if (!r) { + delete this; + return 0; + } + return r; + } + HRESULT WINAPI QueryInterface(REFIID iid, LPVOID *iface) + { + *iface = 0; + if (iid == IID_IUnknown) + *iface = (IUnknown*)this; + else if (iid == IID_IClassFactory) + *iface = (IClassFactory*)this; + else if (iid == IID_IClassFactory2 && licensed) + *iface = (IClassFactory2*)this; + else + return E_NOINTERFACE; + + AddRef(); + return S_OK; + } + + HRESULT WINAPI CreateInstanceHelper(IUnknown *pUnkOuter, REFIID iid, void **ppObject) + { + if (pUnkOuter) { + if (iid != IID_IUnknown) + return CLASS_E_NOAGGREGATION; + const QMetaObject *mo = qAxFactory()->metaObject(className); + if (mo && !qstricmp(mo->classInfo(mo->indexOfClassInfo("Aggregatable")).value(), "no")) + return CLASS_E_NOAGGREGATION; + } + + // Make sure a QApplication instance is present (inprocess case) + if (!qApp) { + qInstallMsgHandler(qWinMsgHandler); + qax_ownQApp = true; + int argc = 0; + QApplication *app = new QApplication(argc, 0); + } + qApp->setQuitOnLastWindowClosed(false); + + if (qAxOutProcServer) + QAbstractEventDispatcher::instance()->setEventFilter(qax_winEventFilter); + else + QApplication::instance()->d_func()->in_exec = true; + + // hook into eventloop; this allows a server to create his own QApplication object + if (!qax_hhook && qax_ownQApp) { + QT_WA({ + qax_hhook = SetWindowsHookExW(WH_GETMESSAGE, axs_FilterProc, 0, GetCurrentThreadId()); + }, { + qax_hhook = SetWindowsHookExA(WH_GETMESSAGE, axs_FilterProc, 0, GetCurrentThreadId()); + }); + } + + HRESULT res; + // Create the ActiveX wrapper - aggregate if requested + if (pUnkOuter) { + QAxServerAggregate *aggregate = new QAxServerAggregate(className, pUnkOuter); + res = aggregate->QueryInterface(iid, ppObject); + if (FAILED(res)) + delete aggregate; + } else { + QAxServerBase *activeqt = new QAxServerBase(className, pUnkOuter); + res = activeqt->QueryInterface(iid, ppObject); + if (FAILED(res)) + delete activeqt; + else + activeqt->registerActiveObject((IUnknown*)(IDispatch*)activeqt); + } + return res; + } + + // IClassFactory + HRESULT WINAPI CreateInstance(IUnknown *pUnkOuter, REFIID iid, void **ppObject) + { + // class is licensed + if (licensed && !qAxFactory()->validateLicenseKey(className, QString())) + return CLASS_E_NOTLICENSED; + + return CreateInstanceHelper(pUnkOuter, iid, ppObject); + } + HRESULT WINAPI LockServer(BOOL fLock) + { + if (fLock) + qAxLock(); + else + qAxUnlock(); + + return S_OK; + } + + // IClassFactory2 + HRESULT WINAPI RequestLicKey(DWORD, BSTR *pKey) + { + if (!pKey) + return E_POINTER; + *pKey = 0; + + // This of course works only on fully licensed machines + if (!qAxFactory()->validateLicenseKey(className, QString())) + return CLASS_E_NOTLICENSED; + + *pKey = QStringToBSTR(classKey); + return S_OK; + } + + HRESULT WINAPI GetLicInfo(LICINFO *pLicInfo) + { + if (!pLicInfo) + return E_POINTER; + pLicInfo->cbLicInfo = sizeof(LICINFO); + + // class specific license key? + const QMetaObject *mo = qAxFactory()->metaObject(className); + const char *key = mo->classInfo(mo->indexOfClassInfo("LicenseKey")).value(); + pLicInfo->fRuntimeKeyAvail = key && key[0]; + + // machine fully licensed? + pLicInfo->fLicVerified = qAxFactory()->validateLicenseKey(className, QString()); + + return S_OK; + } + + HRESULT WINAPI CreateInstanceLic(IUnknown *pUnkOuter, IUnknown *pUnkReserved, REFIID iid, BSTR bKey, PVOID *ppObject) + { + QString licenseKey = QString::fromUtf16((const ushort *)bKey); + if (!qAxFactory()->validateLicenseKey(className, licenseKey)) + return CLASS_E_NOTLICENSED; + return CreateInstanceHelper(pUnkOuter, iid, ppObject); + } + + QString className; + +protected: + CRITICAL_SECTION refCountSection; + unsigned long ref; + bool licensed; + QString classKey; +}; + +// Create a QClassFactory object for class \a iid +HRESULT GetClassObject(REFIID clsid, REFIID iid, void **ppUnk) +{ + QClassFactory *factory = new QClassFactory(clsid); + if (!factory) + return E_OUTOFMEMORY; + if (factory->className.isEmpty()) { + delete factory; + return E_NOINTERFACE; + } + HRESULT res = factory->QueryInterface(iid, ppUnk); + if (res != S_OK) + delete factory; + return res; +} + + +/*! + Constructs a QAxServerBase object wrapping the QWidget \a + classname into an ActiveX control. + + The constructor is called by the QClassFactory object provided by + the COM server for the respective CLSID. +*/ +QAxServerBase::QAxServerBase(const QString &classname, IUnknown *outerUnknown) +: aggregatedObject(0), ref(0), ole_ref(0), class_name(classname), + m_hWnd(0), hmenuShared(0), hwndMenuOwner(0), + m_outerUnknown(outerUnknown) +{ + init(); + + internalCreate(); +} + +/*! + Constructs a QAxServerBase object wrapping \a o. +*/ +QAxServerBase::QAxServerBase(QObject *o) +: aggregatedObject(0), ref(0), ole_ref(0), + m_hWnd(0), hmenuShared(0), hwndMenuOwner(0), + m_outerUnknown(0) +{ + init(); + + qt.object = o; + if (o) { + theObject = o; + isWidget = false; + class_name = QLatin1String(o->metaObject()->className()); + } + internalBind(); + internalConnect(); +} + +/*! + Initializes data members. +*/ +void QAxServerBase::init() +{ + qt.object = 0; + isWidget = false; + ownObject = false; + initNewCalled = false; + dirtyflag = false; + hasStockEvents = false; + stayTopLevel = false; + isInPlaceActive = false; + isUIActive = false; + wasUIActive = false; + inDesignMode = false; + canTakeFocus = false; + freezeEvents = 0; + exception = 0; + + m_spAdviseSink = 0; + m_spClientSite = 0; + m_spInPlaceSite = 0; + m_spInPlaceFrame = 0; + m_spTypeInfo = 0; + m_spStorage = 0; + + InitializeCriticalSection(&refCountSection); + InitializeCriticalSection(&createWindowSection); + +#ifdef QT_DEBUG + EnterCriticalSection(&refCountSection); + ++qaxserverbase_instance_count; + LeaveCriticalSection(&refCountSection); +#endif + + qAxLock(); + + points[IID_IPropertyNotifySink] = new QAxConnection(this, IID_IPropertyNotifySink); +} + +/*! + Destroys the QAxServerBase object, releasing all allocated + resources and interfaces. +*/ +QAxServerBase::~QAxServerBase() +{ +#ifdef QT_DEBUG + EnterCriticalSection(&refCountSection); + --qaxserverbase_instance_count; + LeaveCriticalSection(&refCountSection); +#endif + + revokeActiveObject(); + + for (QAxServerBase::ConnectionPointsIterator it = points.begin(); it != points.end(); ++it) { + if (it.value()) + (*it)->Release(); + } + delete aggregatedObject; + aggregatedObject = 0; + if (theObject) { + qt.object->disconnect(this); + QObject *aqt = qt.object; + qt.object = 0; + if (ownObject) + delete aqt; + } + + if (m_spAdviseSink) m_spAdviseSink->Release(); + m_spAdviseSink = 0; + for (int i = 0; i < adviseSinks.count(); ++i) { + adviseSinks.at(i).pAdvSink->Release(); + } + if (m_spClientSite) m_spClientSite->Release(); + m_spClientSite = 0; + if (m_spInPlaceFrame) m_spInPlaceFrame->Release(); + m_spInPlaceFrame = 0; + if (m_spInPlaceSite) m_spInPlaceSite->Release(); + m_spInPlaceSite = 0; + if (m_spTypeInfo) m_spTypeInfo->Release(); + m_spTypeInfo = 0; + if (m_spStorage) m_spStorage->Release(); + m_spStorage = 0; + + DeleteCriticalSection(&refCountSection); + DeleteCriticalSection(&createWindowSection); + + qAxUnlock(); +} + +/* + Registering with OLE +*/ +void QAxServerBase::registerActiveObject(IUnknown *object) +{ + if (ole_ref || !qt.object || !qAxOutProcServer) + return; + + const QMetaObject *mo = qt.object->metaObject(); + if (!qstricmp(mo->classInfo(mo->indexOfClassInfo("RegisterObject")).value(), "yes")) + RegisterActiveObject(object, qAxFactory()->classID(class_name), ACTIVEOBJECT_WEAK, &ole_ref); +} + +void QAxServerBase::revokeActiveObject() +{ + if (!ole_ref) + return; + + RevokeActiveObject(ole_ref, 0); + ole_ref = 0; +} + +/* + QueryInterface implementation. +*/ +HRESULT WINAPI QAxServerBase::QueryInterface(REFIID iid, void **iface) +{ + if (m_outerUnknown) + return m_outerUnknown->QueryInterface(iid, iface); + + return InternalQueryInterface(iid, iface); +} + +HRESULT QAxServerBase::InternalQueryInterface(REFIID iid, void **iface) +{ + *iface = 0; + + if (iid == IID_IUnknown) { + *iface = (IUnknown*)(IDispatch*)this; + } else { + HRESULT res = S_OK; + if (aggregatedObject) + res = aggregatedObject->queryInterface(iid, iface); + if (*iface) + return res; + } + + if (!(*iface)) { + if (iid == qAxFactory()->interfaceID(class_name)) + *iface = (IDispatch*)this; + if (iid == IID_IDispatch) + *iface = (IDispatch*)this; + else if (iid == IID_IAxServerBase) + *iface = (IAxServerBase*)this; + else if (iid == IID_IOleObject) + *iface = (IOleObject*)this; + else if (iid == IID_IConnectionPointContainer) + *iface = (IConnectionPointContainer*)this; + else if (iid == IID_IProvideClassInfo) + *iface = (IProvideClassInfo*)this; + else if (iid == IID_IProvideClassInfo2) + *iface = (IProvideClassInfo2*)this; + else if (iid == IID_IPersist) + *iface = (IPersist*)(IPersistStream*)this; + else if (iid == IID_IPersistStream) + *iface = (IPersistStream*)this; + else if (iid == IID_IPersistStreamInit) + *iface = (IPersistStreamInit*)this; + else if (iid == IID_IPersistStorage) + *iface = (IPersistStorage*)this; + else if (iid == IID_IPersistPropertyBag) + *iface = (IPersistPropertyBag*)this; + else if (iid == IID_IPersistFile && + qAxFactory()->metaObject(class_name)->indexOfClassInfo("MIME") != -1) + *iface = (IPersistFile*)this; + else if (iid == IID_IViewObject) + *iface = (IViewObject*)this; + else if (iid == IID_IViewObject2) + *iface = (IViewObject2*)this; + else if (isWidget) { + if (iid == IID_IOleControl) + *iface = (IOleControl*)this; + else if (iid == IID_IOleWindow) + *iface = (IOleWindow*)(IOleInPlaceObject*)this; + else if (iid == IID_IOleInPlaceObject) + *iface = (IOleInPlaceObject*)this; + else if (iid == IID_IOleInPlaceActiveObject) + *iface = (IOleInPlaceActiveObject*)this; + else if (iid == IID_IDataObject) + *iface = (IDataObject*)this; + } + } + if (!*iface) + return E_NOINTERFACE; + + AddRef(); + return S_OK; +} + +/*! + Detects and initilaizes implementation of QAxBindable in objects. +*/ +void QAxServerBase::internalBind() +{ + QAxBindable *axb = (QAxBindable*)qt.object->qt_metacast("QAxBindable"); + if (axb) { + // no addref; this is aggregated + axb->activex = this; + if (!aggregatedObject) + aggregatedObject = axb->createAggregate(); + if (aggregatedObject) { + aggregatedObject->controlling_unknown = (IUnknown*)(IDispatch*)this; + aggregatedObject->the_object = qt.object; + } + } +} + +/*! + Connects object signals to event dispatcher. +*/ +void QAxServerBase::internalConnect() +{ + QUuid eventsID = qAxFactory()->eventsID(class_name); + if (!eventsID.isNull()) { + if (!points[eventsID]) + points[eventsID] = new QAxConnection(this, eventsID); + + // connect the generic slot to all signals of qt.object + const QMetaObject *mo = qt.object->metaObject(); + for (int isignal = mo->methodCount()-1; isignal >= 0; --isignal) { + if (mo->method(isignal).methodType() == QMetaMethod::Signal) + QMetaObject::connect(qt.object, isignal, this, isignal); + } + } +} + +/*! + Creates the QWidget for the classname passed to the c'tor. + + All signals of the widget class are connected to the internal event mapper. + If the widget implements QAxBindable, stock events are also connected. +*/ +bool QAxServerBase::internalCreate() +{ + if (qt.object) + return true; + + qt.object = qAxFactory()->createObject(class_name); + Q_ASSERT(qt.object); + if (!qt.object) + return false; + + theObject = qt.object; + ownObject = true; + isWidget = qt.object->isWidgetType(); + hasStockEvents = qAxFactory()->hasStockEvents(class_name); + stayTopLevel = qAxFactory()->stayTopLevel(class_name); + + internalBind(); + if (isWidget) { + if (!stayTopLevel) { + QEvent e(QEvent::EmbeddingControl); + QApplication::sendEvent(qt.widget, &e); + QT_WA({ + ::SetWindowLongW(qt.widget->winId(), GWL_STYLE, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); + }, { + ::SetWindowLongA(qt.widget->winId(), GWL_STYLE, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); + }); + } + qt.widget->setAttribute(Qt::WA_QuitOnClose, false); + qt.widget->move(0, 0); + + // initialize to sizeHint, but don't set resized flag so that container has a chance to override + bool wasResized = qt.widget->testAttribute(Qt::WA_Resized); + updateGeometry(); + if (!wasResized && qt.widget->testAttribute(Qt::WA_Resized) + && qt.widget->sizePolicy() != QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)) { + qt.widget->setAttribute(Qt::WA_Resized, false); + } + } + + internalConnect(); + // install an event filter for stock events + if (isWidget) { + qt.object->installEventFilter(this); + const QList<QWidget*> children = qFindChildren<QWidget*>(qt.object); + QList<QWidget*>::ConstIterator it = children.constBegin(); + while (it != children.constEnd()) { + (*it)->installEventFilter(this); + ++it; + } + } + return true; +} + +/* +class HackMenuData : public QMenuData +{ + friend class QAxServerBase; +}; +*/ + +class HackWidget : public QWidget +{ + friend class QAxServerBase; +}; +/* + Message handler. \a hWnd is always the ActiveX widget hosting the Qt widget. + \a uMsg is handled as follows + \list + \i WM_CREATE The ActiveX control is created + \i WM_DESTROY The QWidget is destroyed + \i WM_SHOWWINDOW The QWidget is parented into the ActiveX window + \i WM_PAINT The QWidget is updated + \i WM_SIZE The QWidget is resized to the new size + \i WM_SETFOCUS and + \i WM_KILLFOCUS The client site is notified about the focus transfer + \i WM_MOUSEACTIVATE The ActiveX is activated + \endlist + + The semantics of \a wParam and \a lParam depend on the value of \a uMsg. +*/ +LRESULT CALLBACK QAxServerBase::ActiveXProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (uMsg == WM_CREATE) { + QAxServerBase *that; + QT_WA({ + CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam; + that = (QAxServerBase*)cs->lpCreateParams; + }, { + CREATESTRUCTA *cs = (CREATESTRUCTA*)lParam; + that = (QAxServerBase*)cs->lpCreateParams; + }); + +#ifdef GWLP_USERDATA + QT_WA({ + SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)that); + }, { + SetWindowLongPtrA(hWnd, GWLP_USERDATA, (LONG_PTR)that); + }); +#else + QT_WA({ + SetWindowLongW(hWnd, GWL_USERDATA, (LONG)that); + }, { + SetWindowLongA(hWnd, GWL_USERDATA, (LONG)that); + }); +#endif + + that->m_hWnd = hWnd; + + QT_WA({ + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); + }, { + return ::DefWindowProcA(hWnd, uMsg, wParam, lParam); + }); + } + + QAxServerBase *that = 0; + +#ifdef GWLP_USERDATA + QT_WA({ + that = (QAxServerBase*)GetWindowLongPtrW(hWnd, GWLP_USERDATA); + }, { + that = (QAxServerBase*)GetWindowLongPtrA(hWnd, GWLP_USERDATA); + }); +#else + QT_WA({ + that = (QAxServerBase*)GetWindowLongW(hWnd, GWL_USERDATA); + }, { + that = (QAxServerBase*)GetWindowLongA(hWnd, GWL_USERDATA); + }); +#endif + + if (that) { + int width = that->qt.widget ? that->qt.widget->width() : 0; + int height = that->qt.widget ? that->qt.widget->height() : 0; + RECT rcPos = {0, 0, width + 1, height + 1}; + + switch (uMsg) { + case WM_NCDESTROY: + that->m_hWnd = 0; + break; + + case WM_QUERYENDSESSION: + case WM_DESTROY: + // save the window handle + if (that->qt.widget) { + that->qt.widget->hide(); + ::SetParent(that->qt.widget->winId(), 0); + } + break; + + case WM_SHOWWINDOW: + if(wParam) { + that->internalCreate(); + if (!that->stayTopLevel) { + ::SetParent(that->qt.widget->winId(), that->m_hWnd); + that->qt.widget->raise(); + that->qt.widget->move(0, 0); + } + that->qt.widget->show(); + } else if (that->qt.widget) { + that->qt.widget->hide(); + } + break; + + case WM_ERASEBKGND: + that->updateMask(); + break; + + case WM_SIZE: + that->resize(QSize(LOWORD(lParam), HIWORD(lParam))); + break; + + case WM_SETFOCUS: + if (that->isInPlaceActive && that->m_spClientSite && !that->inDesignMode && that->canTakeFocus) { + that->DoVerb(OLEIVERB_UIACTIVATE, NULL, that->m_spClientSite, 0, that->m_hWnd, &rcPos); + if (that->isUIActive) { + IOleControlSite *spSite = 0; + that->m_spClientSite->QueryInterface(IID_IOleControlSite, (void**)&spSite); + if (spSite) { + spSite->OnFocus(true); + spSite->Release(); + } + QWidget *candidate = that->qt.widget; + while (!(candidate->focusPolicy() & Qt::TabFocus)) { + candidate = candidate->nextInFocusChain(); + if (candidate == that->qt.widget) { + candidate = 0; + break; + } + } + if (candidate) { + candidate->setFocus(); + HackWidget *widget = (HackWidget*)that->qt.widget; + if (::GetKeyState(VK_SHIFT) < 0) + widget->focusNextPrevChild(false); + } + } + } + break; + + case WM_KILLFOCUS: + if (that->isInPlaceActive && that->isUIActive && that->m_spClientSite) { + IOleControlSite *spSite = 0; + that->m_spClientSite->QueryInterface(IID_IOleControlSite, (void**)&spSite); + if (spSite) { + if (!::IsChild(that->m_hWnd, ::GetFocus())) + spSite->OnFocus(false); + spSite->Release(); + } + } + break; + + case WM_MOUSEACTIVATE: + that->DoVerb(OLEIVERB_UIACTIVATE, NULL, that->m_spClientSite, 0, that->m_hWnd, &rcPos); + break; + + case WM_INITMENUPOPUP: + if (that->qt.widget) { + that->currentPopup = that->menuMap[(HMENU)wParam]; + if (!that->currentPopup) + break; + const QMetaObject *mo = that->currentPopup->metaObject(); + int index = mo->indexOfSignal("aboutToShow()"); + if (index < 0) + break; + + that->currentPopup->qt_metacall(QMetaObject::InvokeMetaMethod, index, 0); + that->createPopup(that->currentPopup, (HMENU)wParam); + return 0; + } + break; + + case WM_MENUSELECT: + case WM_COMMAND: + if (that->qt.widget) { + QMenuBar *menuBar = that->menuBar; + if (!menuBar) + break; + + QObject *menuObject = 0; + bool menuClosed = false; + + if (uMsg == WM_COMMAND) { + menuObject = that->actionMap.value(wParam); + } else if (!lParam) { + menuClosed = true; + menuObject = that->currentPopup; + } else { + menuObject = that->actionMap.value(LOWORD(wParam)); + } + + if (menuObject) { + const QMetaObject *mo = menuObject->metaObject(); + int index = -1; + + if (uMsg == WM_COMMAND) + index = mo->indexOfSignal("activated()"); + else if (menuClosed) + index = mo->indexOfSignal("aboutToHide()"); + else + index = mo->indexOfSignal("hovered()"); + + if (index < 0) + break; + + menuObject->qt_metacall(QMetaObject::InvokeMetaMethod, index, 0); + if (menuClosed || uMsg == WM_COMMAND) + that->currentPopup = 0; + return 0; + } + } + break; + + default: + break; + } + } + + QT_WA({ + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); + }, { + return ::DefWindowProcA(hWnd, uMsg, wParam, lParam); + }); +} + +/*! + Creates the window hosting the QWidget. +*/ +HWND QAxServerBase::create(HWND hWndParent, RECT& rcPos) +{ + Q_ASSERT(isWidget && qt.widget); + + static ATOM atom = 0; + HINSTANCE hInst = (HINSTANCE)qAxInstance; + EnterCriticalSection(&createWindowSection); + QString cn(QLatin1String("QAxControl")); + cn += QString::number((int)ActiveXProc); + if (!atom) { + QT_WA({ + WNDCLASSW wcTemp; + wcTemp.style = CS_DBLCLKS; + wcTemp.cbClsExtra = 0; + wcTemp.cbWndExtra = 0; + wcTemp.hbrBackground = 0; + wcTemp.hCursor = 0; + wcTemp.hIcon = 0; + wcTemp.hInstance = hInst; + wcTemp.lpszClassName = (wchar_t*)cn.utf16(); + wcTemp.lpszMenuName = 0; + wcTemp.lpfnWndProc = ActiveXProc; + + atom = RegisterClassW(&wcTemp); + }, { + QByteArray cna = cn.toLatin1(); + WNDCLASSA wcTemp; + wcTemp.style = CS_DBLCLKS; + wcTemp.cbClsExtra = 0; + wcTemp.cbWndExtra = 0; + wcTemp.hbrBackground = 0; + wcTemp.hCursor = 0; + wcTemp.hIcon = 0; + wcTemp.hInstance = hInst; + wcTemp.lpszClassName = cna.data(); + wcTemp.lpszMenuName = 0; + wcTemp.lpfnWndProc = ActiveXProc; + + atom = RegisterClassA(&wcTemp); + }); + } + LeaveCriticalSection(&createWindowSection); + if (!atom && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) + return 0; + + Q_ASSERT(!m_hWnd); + HWND hWnd = 0; + QT_WA({ + hWnd = ::CreateWindowW((wchar_t*)cn.utf16(), 0, + WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, + rcPos.left, rcPos.top, rcPos.right - rcPos.left, + rcPos.bottom - rcPos.top, hWndParent, 0, hInst, this); + }, { + hWnd = ::CreateWindowA(cn.toLatin1().data(), 0, + WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, + rcPos.left, rcPos.top, rcPos.right - rcPos.left, + rcPos.bottom - rcPos.top, hWndParent, 0, hInst, this); + }); + + Q_ASSERT(m_hWnd == hWnd); + + updateMask(); + EnableWindow(m_hWnd, qt.widget->isEnabled()); + + return hWnd; +} + +/* + Recoursively creates Win32 submenus. +*/ +HMENU QAxServerBase::createPopup(QMenu *popup, HMENU oldMenu) +{ + HMENU popupMenu = oldMenu ? oldMenu : CreatePopupMenu(); + menuMap.insert(popupMenu, popup); + + if (oldMenu) while (GetMenuItemCount(oldMenu)) { + DeleteMenu(oldMenu, 0, MF_BYPOSITION); + } + + const QList<QAction*> actions = popup->actions(); + for (int i = 0; i < actions.count(); ++i) { + QAction *action = actions.at(i); + + uint flags = action->isEnabled() ? MF_ENABLED : MF_GRAYED; + if (action->isSeparator()) + flags |= MF_SEPARATOR; + else if (action->menu()) + flags |= MF_POPUP; + else + flags |= MF_STRING; + if (action->isChecked()) + flags |= MF_CHECKED; + + ushort itemId; + if (flags & MF_POPUP) { + itemId = static_cast<ushort>( + reinterpret_cast<ulong>(createPopup(action->menu())) + ); + } else { + itemId = static_cast<ushort>(reinterpret_cast<ulong>(action)); + actionMap.remove(itemId); + actionMap.insert(itemId, action); + } + QT_WA({ + AppendMenuW(popupMenu, flags, itemId, (TCHAR*)action->text().utf16()); + }, { + AppendMenuA(popupMenu, flags, itemId, action->text().toLocal8Bit()); + }); + } + if (oldMenu) + DrawMenuBar(hwndMenuOwner); + return popupMenu; +} + +/*! + Creates a Win32 menubar. +*/ +void QAxServerBase::createMenu(QMenuBar *menuBar) +{ + hmenuShared = ::CreateMenu(); + + int edit = 0; + int object = 0; + int help = 0; + + const QList<QAction*> actions = menuBar->actions(); + for (int i = 0; i < actions.count(); ++i) { + QAction *action = actions.at(i); + + uint flags = action->isEnabled() ? MF_ENABLED : MF_GRAYED; + if (action->isSeparator()) + flags |= MF_SEPARATOR; + else if (action->menu()) + flags |= MF_POPUP; + else + flags |= MF_STRING; + + if (action->text() == QCoreApplication::translate(qt.widget->metaObject()->className(), "&Edit")) + edit++; + else if (action->text() == QCoreApplication::translate(qt.widget->metaObject()->className(), "&Help")) + help++; + else + object++; + + ushort itemId; + if (flags & MF_POPUP) { + itemId = static_cast<ushort>( + reinterpret_cast<ulong>(createPopup(action->menu())) + ); + } else { + itemId = static_cast<ushort>(reinterpret_cast<ulong>(action)); + actionMap.insert(itemId, action); + } + QT_WA({ + AppendMenuW(hmenuShared, flags, itemId, (TCHAR*)action->text().utf16()); + } , { + AppendMenuA(hmenuShared, flags, itemId, action->text().toLocal8Bit()); + }); + } + + OLEMENUGROUPWIDTHS menuWidths = {0,edit,0,object,0,help}; + HRESULT hres = m_spInPlaceFrame->InsertMenus(hmenuShared, &menuWidths); + if (FAILED(hres)) { + ::DestroyMenu(hmenuShared); + hmenuShared = 0; + return; + } + + m_spInPlaceFrame->GetWindow(&hwndMenuOwner); + + holemenu = OleCreateMenuDescriptor(hmenuShared, &menuWidths); + hres = m_spInPlaceFrame->SetMenu(hmenuShared, holemenu, m_hWnd); + if (FAILED(hres)) { + ::DestroyMenu(hmenuShared); + hmenuShared = 0; + OleDestroyMenuDescriptor(holemenu); + } +} + +/*! + Remove the Win32 menubar. +*/ +void QAxServerBase::removeMenu() +{ + if (hmenuShared) + m_spInPlaceFrame->RemoveMenus(hmenuShared); + holemenu = 0; + m_spInPlaceFrame->SetMenu(0, 0, m_hWnd); + if (hmenuShared) { + DestroyMenu(hmenuShared); + hmenuShared = 0; + menuMap.clear(); + } + hwndMenuOwner = 0; +} + +extern bool ignoreSlots(const char *test); +extern bool ignoreProps(const char *test); + +/*! + Makes sure the type info is loaded +*/ +void QAxServerBase::ensureMetaData() +{ + if (!m_spTypeInfo) { + qAxTypeLibrary->GetTypeInfoOfGuid(qAxFactory()->interfaceID(class_name), &m_spTypeInfo); + m_spTypeInfo->AddRef(); + } +} + +/*! + \internal + Returns true if the property \a index is exposed to COM and should + be saved/loaded. +*/ +bool QAxServerBase::isPropertyExposed(int index) +{ + if (!theObject) + return false; + + bool result = false; + const QMetaObject *mo = theObject->metaObject(); + + int qtProps = 0; + if (theObject->isWidgetType()) + qtProps = QWidget::staticMetaObject.propertyCount(); + QMetaProperty property = mo->property(index); + if (index <= qtProps && ignoreProps(property.name())) + return result; + + BSTR bstrNames = QStringToBSTR(QLatin1String(property.name())); + DISPID dispId; + GetIDsOfNames(IID_NULL, (BSTR*)&bstrNames, 1, LOCALE_USER_DEFAULT, &dispId); + result = dispId != DISPID_UNKNOWN; + SysFreeString(bstrNames); + + return result; +} + + +/*! + \internal + Updates the view, or asks the client site to do so. +*/ +void QAxServerBase::update() +{ + if (isInPlaceActive) { + if (m_hWnd) + ::InvalidateRect(m_hWnd, 0, true); + else if (m_spInPlaceSite) + m_spInPlaceSite->InvalidateRect(NULL, true); + } else if (m_spAdviseSink) { + m_spAdviseSink->OnViewChange(DVASPECT_CONTENT, -1); + for (int i = 0; i < adviseSinks.count(); ++i) { + adviseSinks.at(i).pAdvSink->OnViewChange(DVASPECT_CONTENT, -1); + } + } +} + +/*! + Resizes the control, faking a QResizeEvent if required +*/ +void QAxServerBase::resize(const QSize &size) +{ + if (!isWidget || !qt.widget || !size.isValid() || size == QSize(0, 0)) + return; + + QSize oldSize = qt.widget->size(); + qt.widget->resize(size); + QSize newSize = qt.widget->size(); + // make sure we get a resize event even if not embedded as a control + if (!m_hWnd && !qt.widget->isVisible() && newSize != oldSize) { + QResizeEvent resizeEvent(newSize, oldSize); +#ifndef QT_DLL // import from static library + extern bool qt_sendSpontaneousEvent(QObject*,QEvent*); +#endif + qt_sendSpontaneousEvent(qt.widget, &resizeEvent); + } + m_currentExtent = qt.widget->size(); +} + +/*! + \internal + + Updates the internal size values. +*/ +void QAxServerBase::updateGeometry() +{ + if (!isWidget || !qt.widget) + return; + + const QSize sizeHint = qt.widget->sizeHint(); + const QSize size = qt.widget->size(); + if (sizeHint.isValid()) { // if provided, adjust to sizeHint + QSize newSize = size; + if (!qt.widget->testAttribute(Qt::WA_Resized)) { + newSize = sizeHint; + } else { // according to sizePolicy rules if already resized + QSizePolicy sizePolicy = qt.widget->sizePolicy(); + if (sizeHint.width() > size.width() && !(sizePolicy.horizontalPolicy() & QSizePolicy::ShrinkFlag)) + newSize.setWidth(sizeHint.width()); + if (sizeHint.width() < size.width() && !(sizePolicy.horizontalPolicy() & QSizePolicy::GrowFlag)) + newSize.setWidth(sizeHint.width()); + if (sizeHint.height() > size.height() && !(sizePolicy.verticalPolicy() & QSizePolicy::ShrinkFlag)) + newSize.setHeight(sizeHint.height()); + if (sizeHint.height() < size.height() && !(sizePolicy.verticalPolicy() & QSizePolicy::GrowFlag)) + newSize.setHeight(sizeHint.height()); + } + resize(newSize); + + // set an initial size suitable for embedded controls + } else if (!qt.widget->testAttribute(Qt::WA_Resized)) { + resize(QSize(100, 100)); + qt.widget->setAttribute(Qt::WA_Resized, false); + } +} + +/*! + \internal + + Updates the mask of the widget parent. +*/ +void QAxServerBase::updateMask() +{ + if (!isWidget || !qt.widget || qt.widget->mask().isEmpty()) + return; + + QRegion rgn = qt.widget->mask(); + HRGN hrgn = rgn.handle(); + + // Since SetWindowRegion takes ownership + HRGN wr = CreateRectRgn(0,0,0,0); + CombineRgn(wr, hrgn, 0, RGN_COPY); + SetWindowRgn(m_hWnd, wr, true); +} + +static bool checkHRESULT(HRESULT hres) +{ + const char *name = 0; + switch(hres) { + case S_OK: + return true; + case DISP_E_BADPARAMCOUNT: +#if defined(QT_CHECK_STATE) + qWarning("QAxBase: Error calling IDispatch member %s: Bad parameter count", name); +#endif + return false; + case DISP_E_BADVARTYPE: +#if defined(QT_CHECK_STATE) + qWarning("QAxBase: Error calling IDispatch member %s: Bad variant type", name); +#endif + return false; + case DISP_E_EXCEPTION: +#if defined(QT_CHECK_STATE) + qWarning("QAxBase: Error calling IDispatch member %s: Exception thrown by server", name); +#endif + return false; + case DISP_E_MEMBERNOTFOUND: +#if defined(QT_CHECK_STATE) + qWarning("QAxBase: Error calling IDispatch member %s: Member not found", name); +#endif + return false; + case DISP_E_NONAMEDARGS: +#if defined(QT_CHECK_STATE) + qWarning("QAxBase: Error calling IDispatch member %s: No named arguments", name); +#endif + return false; + case DISP_E_OVERFLOW: +#if defined(QT_CHECK_STATE) + qWarning("QAxBase: Error calling IDispatch member %s: Overflow", name); +#endif + return false; + case DISP_E_PARAMNOTFOUND: +#if defined(QT_CHECK_STATE) + qWarning("QAxBase: Error calling IDispatch member %s: Parameter not found", name); +#endif + return false; + case DISP_E_TYPEMISMATCH: +#if defined(QT_CHECK_STATE) + qWarning("QAxBase: Error calling IDispatch member %s: Type mismatch", name); +#endif + return false; + case DISP_E_UNKNOWNINTERFACE: +#if defined(QT_CHECK_STATE) + qWarning("QAxBase: Error calling IDispatch member %s: Unknown interface", name); +#endif + return false; + case DISP_E_UNKNOWNLCID: +#if defined(QT_CHECK_STATE) + qWarning("QAxBase: Error calling IDispatch member %s: Unknown locale ID", name); +#endif + return false; + case DISP_E_PARAMNOTOPTIONAL: +#if defined(QT_CHECK_STATE) + qWarning("QAxBase: Error calling IDispatch member %s: Non-optional parameter missing", name); +#endif + return false; + default: +#if defined(QT_CHECK_STATE) + qWarning("QAxBase: Error calling IDispatch member %s: Unknown error", name); +#endif + return false; + } +} + +static inline QByteArray paramType(const QByteArray &ptype, bool *out) +{ + *out = ptype.endsWith('&') || ptype.endsWith("**"); + if (*out) { + QByteArray res(ptype); + res.truncate(res.length() - 1); + return res; + } + + return ptype; +} + +/*! + Catches all signals emitted by the Qt widget and fires the respective COM event. + + \a isignal is the Qt Meta Object index of the received signal, and \a _o the + signal parameters. +*/ +int QAxServerBase::qt_metacall(QMetaObject::Call call, int index, void **argv) +{ + Q_ASSERT(call == QMetaObject::InvokeMetaMethod); + + if (index == -1) { + if (sender() && m_spInPlaceFrame) { + if (qobject_cast<QStatusBar*>(sender()) != statusBar) + return true; + + if (statusBar->isHidden()) { + QString message = *(QString*)argv[1]; + m_spInPlaceFrame->SetStatusText(QStringToBSTR(message)); + } + } + return true; + } + + if (freezeEvents || inDesignMode) + return true; + + ensureMetaData(); + + // get the signal information. + const QMetaObject *mo = qt.object->metaObject(); + QMetaMethod signal; + DISPID eventId = index; + int pcount = 0; + QByteArray type; + QList<QByteArray> ptypes; + + switch(index) { + case DISPID_KEYDOWN: + case DISPID_KEYUP: + pcount = 2; + ptypes << "int&" << "int"; + break; + case DISPID_KEYPRESS: + pcount = 1; + ptypes << "int&"; + break; + case DISPID_MOUSEDOWN: + case DISPID_MOUSEMOVE: + case DISPID_MOUSEUP: + pcount = 4; + ptypes << "int" << "int" << "int" << "int"; + break; + case DISPID_CLICK: + pcount = 0; + break; + case DISPID_DBLCLICK: + pcount = 0; + break; + default: + { + signal = mo->method(index); + Q_ASSERT(signal.methodType() == QMetaMethod::Signal); + type = signal.typeName(); + QByteArray signature(signal.signature()); + QByteArray name(signature); + name.truncate(name.indexOf('(')); + + eventId = signalCache.value(index, -1); + if (eventId == -1) { + ITypeInfo *eventInfo = 0; + qAxTypeLibrary->GetTypeInfoOfGuid(qAxFactory()->eventsID(class_name), &eventInfo); + if (eventInfo) { + QString uni_name = QLatin1String(name); + const OLECHAR *olename = reinterpret_cast<const OLECHAR *>(uni_name.utf16()); + eventInfo->GetIDsOfNames((OLECHAR**)&olename, 1, &eventId); + eventInfo->Release(); + } + } + + signature = signature.mid(name.length() + 1); + signature.truncate(signature.length() - 1); + + if (!signature.isEmpty()) + ptypes = signature.split(','); + + pcount = ptypes.count(); + } + break; + } + if (pcount && !argv) { + qWarning("QAxServerBase::qt_metacall: Missing %d arguments", pcount); + return false; + } + if (eventId == -1) + return false; + + // For all connected event sinks... + IConnectionPoint *cpoint = 0; + GUID IID_QAxEvents = qAxFactory()->eventsID(class_name); + FindConnectionPoint(IID_QAxEvents, &cpoint); + if (cpoint) { + IEnumConnections *clist = 0; + cpoint->EnumConnections(&clist); + if (clist) { + clist->Reset(); + ULONG cc = 1; + CONNECTDATA c[1]; + clist->Next(cc, (CONNECTDATA*)&c, &cc); + if (cc) { + // setup parameters + unsigned int argErr = 0; + DISPPARAMS dispParams; + dispParams.cArgs = pcount; + dispParams.cNamedArgs = 0; + dispParams.rgdispidNamedArgs = 0; + dispParams.rgvarg = 0; + + if (pcount) // Use malloc/free for eval package compatibility + dispParams.rgvarg = (VARIANTARG*)malloc(pcount * sizeof(VARIANTARG)); + int p = 0; + for (p = 0; p < pcount; ++p) { + VARIANT *arg = dispParams.rgvarg + (pcount - p - 1); + VariantInit(arg); + + bool out; + QByteArray ptype = paramType(ptypes.at(p), &out); + QVariant variant; + if (mo->indexOfEnumerator(ptype) != -1) { + // convert enum values to int + variant = QVariant(*reinterpret_cast<int *>(argv[p+1])); + } else { + QVariant::Type vt = QVariant::nameToType(ptype); + if (vt == QVariant::UserType) { + if (ptype.endsWith('*')) { + variant = QVariant(QMetaType::type(ptype), (void**)argv[p+1]); + // qVariantSetValue(variant, *(void**)(argv[p + 1]), ptype); + } else { + variant = QVariant(QMetaType::type(ptype), argv[p+1]); + // qVariantSetValue(variant, argv[p + 1], ptype); + } + } else { + variant = QVariant(vt, argv[p + 1]); + } + } + + QVariantToVARIANT(variant, *arg, type, out); + } + + VARIANT retval; + VariantInit(&retval); + VARIANT *pretval = 0; + if (!type.isEmpty()) + pretval = &retval; + + // call listeners (through IDispatch) + while (cc) { + if (c->pUnk) { + IDispatch *disp = 0; + c->pUnk->QueryInterface(IID_QAxEvents, (void**)&disp); + if (disp) { + disp->Invoke(eventId, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispParams, pretval, 0, &argErr); + + // update out-parameters and return value + if (index > 0) { + for (p = 0; p < pcount; ++p) { + bool out; + QByteArray ptype = paramType(ptypes.at(p), &out); + if (out) + QVariantToVoidStar(VARIANTToQVariant(dispParams.rgvarg[pcount - p - 1], ptype), argv[p+1], ptype); + } + if (pretval) + QVariantToVoidStar(VARIANTToQVariant(retval, type), argv[0], type); + } + disp->Release(); + } + c->pUnk->Release(); // AddRef'ed by clist->Next implementation + } + clist->Next(cc, (CONNECTDATA*)&c, &cc); + } + + // clean up + for (p = 0; p < pcount; ++p) + clearVARIANT(dispParams.rgvarg+p); + free(dispParams.rgvarg); + } + clist->Release(); + } + cpoint->Release(); + } + + return true; +} + +/*! + Call IPropertyNotifySink of connected clients. + \a dispId specifies the ID of the property that changed. +*/ +bool QAxServerBase::emitRequestPropertyChange(const char *property) +{ + long dispId = -1; + + IConnectionPoint *cpoint = 0; + FindConnectionPoint(IID_IPropertyNotifySink, &cpoint); + if (cpoint) { + IEnumConnections *clist = 0; + cpoint->EnumConnections(&clist); + if (clist) { + clist->Reset(); + ULONG cc = 1; + CONNECTDATA c[1]; + clist->Next(cc, (CONNECTDATA*)&c, &cc); + if (cc) { + if (dispId == -1) { + BSTR bstr = QStringToBSTR(QLatin1String(property)); + GetIDsOfNames(IID_NULL, &bstr, 1, LOCALE_USER_DEFAULT, &dispId); + SysFreeString(bstr); + } + if (dispId != -1) while (cc) { + if (c->pUnk) { + IPropertyNotifySink *sink = 0; + c->pUnk->QueryInterface(IID_IPropertyNotifySink, (void**)&sink); + bool disallows = sink && sink->OnRequestEdit(dispId) == S_FALSE; + sink->Release(); + c->pUnk->Release(); + if (disallows) { // a client disallows the property to change + clist->Release(); + cpoint->Release(); + return false; + } + } + clist->Next(cc, (CONNECTDATA*)&c, &cc); + } + } + clist->Release(); + } + cpoint->Release(); + } + dirtyflag = true; + return true; +} + +/*! + Call IPropertyNotifySink of connected clients. + \a dispId specifies the ID of the property that changed. +*/ +void QAxServerBase::emitPropertyChanged(const char *property) +{ + long dispId = -1; + + IConnectionPoint *cpoint = 0; + FindConnectionPoint(IID_IPropertyNotifySink, &cpoint); + if (cpoint) { + IEnumConnections *clist = 0; + cpoint->EnumConnections(&clist); + if (clist) { + clist->Reset(); + ULONG cc = 1; + CONNECTDATA c[1]; + clist->Next(cc, (CONNECTDATA*)&c, &cc); + if (cc) { + if (dispId == -1) { + BSTR bstr = QStringToBSTR(QLatin1String(property)); + GetIDsOfNames(IID_NULL, &bstr, 1, LOCALE_USER_DEFAULT, &dispId); + SysFreeString(bstr); + } + if (dispId != -1) while (cc) { + if (c->pUnk) { + IPropertyNotifySink *sink = 0; + c->pUnk->QueryInterface(IID_IPropertyNotifySink, (void**)&sink); + if (sink) { + sink->OnChanged(dispId); + sink->Release(); + } + c->pUnk->Release(); + } + clist->Next(cc, (CONNECTDATA*)&c, &cc); + } + } + clist->Release(); + } + cpoint->Release(); + } + dirtyflag = true; +} + +//**** IProvideClassInfo +/* + Provide the ITypeInfo implementation for the COM class. +*/ +HRESULT WINAPI QAxServerBase::GetClassInfo(ITypeInfo** pptinfo) +{ + if (!pptinfo) + return E_POINTER; + + *pptinfo = 0; + if (!qAxTypeLibrary) + return DISP_E_BADINDEX; + + return qAxTypeLibrary->GetTypeInfoOfGuid(qAxFactory()->classID(class_name), pptinfo); +} + +//**** IProvideClassInfo2 +/* + Provide the ID of the event interface. +*/ +HRESULT WINAPI QAxServerBase::GetGUID(DWORD dwGuidKind, GUID* pGUID) +{ + if (!pGUID) + return E_POINTER; + + if (dwGuidKind == GUIDKIND_DEFAULT_SOURCE_DISP_IID) { + *pGUID = qAxFactory()->eventsID(class_name); + return S_OK; + } + *pGUID = GUID_NULL; + return E_FAIL; +} + +//**** IDispatch +/* + Returns the number of class infos for this IDispatch. +*/ +HRESULT WINAPI QAxServerBase::GetTypeInfoCount(UINT* pctinfo) +{ + if (!pctinfo) + return E_POINTER; + + *pctinfo = qAxTypeLibrary ? 1 : 0; + return S_OK; +} + +/* + Provides the ITypeInfo for this IDispatch implementation. +*/ +HRESULT WINAPI QAxServerBase::GetTypeInfo(UINT itinfo, LCID /*lcid*/, ITypeInfo** pptinfo) +{ + if (!pptinfo) + return E_POINTER; + + if (!qAxTypeLibrary) + return DISP_E_BADINDEX; + + ensureMetaData(); + + *pptinfo = m_spTypeInfo; + (*pptinfo)->AddRef(); + + return S_OK; +} + +/* + Provides the names of the methods implemented in this IDispatch implementation. +*/ +HRESULT WINAPI QAxServerBase::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, + LCID /*lcid*/, DISPID* rgdispid) +{ + if (!rgszNames || !rgdispid) + return E_POINTER; + + if (!qAxTypeLibrary) + return DISP_E_UNKNOWNNAME; + + ensureMetaData(); + if (!m_spTypeInfo) + return DISP_E_UNKNOWNNAME; + + return m_spTypeInfo->GetIDsOfNames(rgszNames, cNames, rgdispid); +} + +/* + Map the COM call to the Qt slot/property for \a dispidMember. +*/ +HRESULT WINAPI QAxServerBase::Invoke(DISPID dispidMember, REFIID riid, + LCID /*lcid*/, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pvarResult, + EXCEPINFO* pexcepinfo, UINT* puArgErr) +{ + if (riid != IID_NULL) + return DISP_E_UNKNOWNINTERFACE; + if (!theObject) + return E_UNEXPECTED; + + HRESULT res = DISP_E_MEMBERNOTFOUND; + + bool uniqueIndex = wFlags == DISPATCH_PROPERTYGET || wFlags == DISPATCH_PROPERTYPUT || wFlags == DISPATCH_METHOD; + + int index = uniqueIndex ? indexCache.value(dispidMember, -1) : -1; + QByteArray name; + if (index == -1) { + ensureMetaData(); + + // This property or method is invoked when an ActiveX client specifies + // the object name without a property or method. We only support property. + if (dispidMember == DISPID_VALUE && (wFlags == DISPATCH_PROPERTYGET || wFlags == DISPATCH_PROPERTYPUT)) { + const QMetaObject *mo = qt.object->metaObject(); + index = mo->indexOfClassInfo("DefaultProperty"); + if (index != -1) { + name = mo->classInfo(index).value(); + index = mo->indexOfProperty(name); + } + } else { + BSTR bname; + UINT cname = 0; + if (m_spTypeInfo) + m_spTypeInfo->GetNames(dispidMember, &bname, 1, &cname); + if (!cname) + return res; + + name = QString::fromUtf16((const ushort *)bname).toLatin1(); + SysFreeString(bname); + } + } + + const QMetaObject *mo = qt.object->metaObject(); + QSize oldSizeHint; + if (isWidget) + oldSizeHint = qt.widget->sizeHint(); + + switch (wFlags) { + case DISPATCH_PROPERTYGET|DISPATCH_METHOD: + case DISPATCH_PROPERTYGET: + { + if (index == -1) { + index = mo->indexOfProperty(name); + if (index == -1 && wFlags == DISPATCH_PROPERTYGET) + return res; + } + + QMetaProperty property; + if (index < mo->propertyCount()) + property = mo->property(index); + + if (property.isReadable()) { + if (!pvarResult) + return DISP_E_PARAMNOTOPTIONAL; + if (pDispParams->cArgs || + pDispParams->cNamedArgs) + return DISP_E_BADPARAMCOUNT; + + QVariant var = qt.object->property(property.name()); + if (!var.isValid()) + res = DISP_E_MEMBERNOTFOUND; + else if (!QVariantToVARIANT(var, *pvarResult)) + res = DISP_E_TYPEMISMATCH; + else + res = S_OK; + break; + } else if (wFlags == DISPATCH_PROPERTYGET) { + break; + } + } + // FALLTHROUGH if wFlags == DISPATCH_PROPERTYGET|DISPATCH_METHOD AND not a property. + case DISPATCH_METHOD: + { + int nameLength = 0; + if (index == -1) { + nameLength = name.length(); + name += "("; + // no parameter - shortcut + if (!pDispParams->cArgs) + index = mo->indexOfSlot((name + ")")); + // search + if (index == -1) { + for (int i = 0; i < mo->methodCount(); ++i) { + const QMetaMethod slot(mo->method(i)); + if (slot.methodType() == QMetaMethod::Slot && QByteArray(slot.signature()).startsWith(name)) { + index = i; + break; + } + } + // resolve overloads + if (index == -1) { + QRegExp regexp(QLatin1String("_([0-9])\\(")); + if (regexp.lastIndexIn(QString::fromLatin1(name.constData())) != -1) { + name = name.left(name.length() - regexp.cap(0).length()) + "("; + int overload = regexp.cap(1).toInt() + 1; + + for (int s = 0; s < qt.object->metaObject()->methodCount(); ++s) { + QMetaMethod slot = qt.object->metaObject()->method(s); + if (slot.methodType() == QMetaMethod::Slot && QByteArray(slot.signature()).startsWith(name)) { + if (!--overload) { + index = s; + break; + } + } + } + } + } + if (index == -1) + return res; + } + } + + int lookupIndex = index; + + // get slot info + QMetaMethod slot(mo->method(index)); + Q_ASSERT(slot.methodType() == QMetaMethod::Slot); + QByteArray type = slot.typeName(); + name = slot.signature(); + nameLength = name.indexOf('('); + QByteArray prototype = name.mid(nameLength + 1); + prototype.truncate(prototype.length() - 1); + QList<QByteArray> ptypes; + if (!prototype.isEmpty()) + ptypes = prototype.split(','); + int pcount = ptypes.count(); + + // verify parameter count + if (pcount > pDispParams->cArgs) { + // count cloned slots immediately following the real thing + int defArgs = 0; + while (index < mo->methodCount()) { + ++index; + slot = mo->method(index); + if (!(slot.attributes() & QMetaMethod::Cloned)) + break; + --pcount; + // found a matching overload. ptypes still valid + if (pcount <= pDispParams->cArgs) + break; + } + // still wrong :( + if (pcount > pDispParams->cArgs) + return DISP_E_PARAMNOTOPTIONAL; + } else if (pcount < pDispParams->cArgs) { + return DISP_E_BADPARAMCOUNT; + } + + // setup parameters (pcount + return) + bool ok = true; + void *static_argv[QAX_NUM_PARAMS + 1]; + QVariant static_varp[QAX_NUM_PARAMS + 1]; + void *static_argv_pointer[QAX_NUM_PARAMS + 1]; + + int totalParam = pcount; + if (!type.isEmpty()) + ++totalParam; + + void **argv = 0; // the actual array passed into qt_metacall + void **argv_pointer = 0; // in case we need an additional level of indirection + QVariant *varp = 0; // QVariants to hold the temporary Qt data object for us + + if (totalParam) { + if (totalParam <= QAX_NUM_PARAMS) { + argv = static_argv; + argv_pointer = static_argv_pointer; + varp = static_varp; + } else { + argv = new void*[pcount + 1]; + argv_pointer = new void*[pcount + 1]; + varp = new QVariant[pcount + 1]; + } + + argv_pointer[0] = 0; + } + + for (int p = 0; p < pcount; ++p) { + // map the VARIANT to the void* + bool out; + QByteArray ptype = paramType(ptypes.at(p), &out); + varp[p + 1] = VARIANTToQVariant(pDispParams->rgvarg[pcount - p - 1], ptype); + argv_pointer[p + 1] = 0; + if (varp[p + 1].isValid()) { + if (varp[p + 1].type() == QVariant::UserType) { + argv[p + 1] = varp[p + 1].data(); + } else if (ptype == "QVariant") { + argv[p + 1] = varp + p + 1; + } else { + argv[p + 1] = const_cast<void*>(varp[p + 1].constData()); + if (ptype.endsWith("*")) { + argv_pointer[p + 1] = argv[p + 1]; + argv[p + 1] = argv_pointer + p + 1; + } + } + } else if (ptype == "QVariant") { + argv[p + 1] = varp + p + 1; + } else { + if (puArgErr) + *puArgErr = pcount-p-1; + ok = false; + } + } + + // return value + if (!type.isEmpty()) { + QVariant::Type vt = QVariant::nameToType(type); + if (vt == QVariant::UserType) + vt = QVariant::Invalid; + varp[0] = QVariant(vt); + if (varp[0].type() == QVariant::Invalid && mo->indexOfEnumerator(slot.typeName()) != -1) + varp[0] = QVariant(QVariant::Int); + + if (varp[0].type() == QVariant::Invalid) { + if (type == "QVariant") + argv[0] = varp; + else + argv[0] = 0; + } else { + argv[0] = const_cast<void*>(varp[0].constData()); + } + if (type.endsWith("*")) { + argv_pointer[0] = argv[0]; + argv[0] = argv_pointer; + } + } + + // call the slot if everthing went fine. + if (ok) { + ++invokeCount; + qt.object->qt_metacall(QMetaObject::InvokeMetaMethod, index, argv); + if (--invokeCount < 0) + invokeCount = 0; + + // update reference parameters and return value + for (int p = 0; p < pcount; ++p) { + bool out; + QByteArray ptype = paramType(ptypes.at(p), &out); + if (out) { + if (!QVariantToVARIANT(varp[p + 1], pDispParams->rgvarg[pcount - p - 1], ptype, out)) + ok = false; + } + } + if (!type.isEmpty() && pvarResult) { + if (!varp[0].isValid() && type != "QVariant") + varp[0] = QVariant(QMetaType::type(type), argv_pointer); +// qVariantSetValue(varp[0], argv_pointer[0], type); + ok = QVariantToVARIANT(varp[0], *pvarResult, type); + } + } + if (argv && argv != static_argv) { + delete []argv; + delete []argv_pointer; + delete []varp; + } + + res = ok ? S_OK : DISP_E_TYPEMISMATCH; + + // reset in case index changed for default-arg handling + index = lookupIndex; + } + break; + case DISPATCH_PROPERTYPUT: + case DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF: + { + if (index == -1) { + index = mo->indexOfProperty(name); + if (index == -1) + return res; + } + + QMetaProperty property; + if (index < mo->propertyCount()) + property = mo->property(index); + if (!property.isWritable()) + return DISP_E_MEMBERNOTFOUND; + if (!pDispParams->cArgs) + return DISP_E_PARAMNOTOPTIONAL; + if (pDispParams->cArgs != 1 || + pDispParams->cNamedArgs != 1 || + *pDispParams->rgdispidNamedArgs != DISPID_PROPERTYPUT) + return DISP_E_BADPARAMCOUNT; + + QVariant var = VARIANTToQVariant(*pDispParams->rgvarg, property.typeName(), property.type()); + if (!var.isValid()) { + if (puArgErr) + *puArgErr = 0; + return DISP_E_BADVARTYPE; + } + if (!qt.object->setProperty(property.name(), var)) { + if (puArgErr) + *puArgErr = 0; + return DISP_E_TYPEMISMATCH; + } + + res = S_OK; + } + break; + + default: + break; + } + + // maybe calling a setter? Notify client about changes + switch(wFlags) { + case DISPATCH_METHOD: + case DISPATCH_PROPERTYPUT: + case DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF: + if (m_spAdviseSink || adviseSinks.count()) { + FORMATETC fmt; + fmt.cfFormat = 0; + fmt.ptd = 0; + fmt.dwAspect = DVASPECT_CONTENT; + fmt.lindex = -1; + fmt.tymed = TYMED_NULL; + + STGMEDIUM stg; + stg.tymed = TYMED_NULL; + stg.pUnkForRelease = 0; + stg.hBitmap = 0; // initializes the whole union + + if (m_spAdviseSink) { + m_spAdviseSink->OnViewChange(DVASPECT_CONTENT, -1); + m_spAdviseSink->OnDataChange(&fmt, &stg); + } + for (int i = 0; i < adviseSinks.count(); ++i) { + adviseSinks.at(i).pAdvSink->OnDataChange(&fmt, &stg); + } + } + + dirtyflag = true; + break; + default: + break; + } + + if (index != -1 && uniqueIndex) + indexCache.insert(dispidMember, index); + + if (exception) { + if (pexcepinfo) { + memset(pexcepinfo, 0, sizeof(EXCEPINFO)); + + pexcepinfo->wCode = exception->code; + if (!exception->src.isNull()) + pexcepinfo->bstrSource = QStringToBSTR(exception->src); + if (!exception->desc.isNull()) + pexcepinfo->bstrDescription = QStringToBSTR(exception->desc); + if (!exception->context.isNull()) { + QString context = exception->context; + int contextID = 0; + int br = context.indexOf(QLatin1Char('[')); + if (br != -1) { + context = context.mid(br+1); + context = context.left(context.length() - 1); + contextID = context.toInt(); + + context = exception->context; + context = context.left(br-1); + } + pexcepinfo->bstrHelpFile = QStringToBSTR(context); + pexcepinfo->dwHelpContext = contextID; + } + } + delete exception; + exception = 0; + return DISP_E_EXCEPTION; + } else if (isWidget) { + QSize sizeHint = qt.widget->sizeHint(); + if (oldSizeHint != sizeHint) { + updateGeometry(); + if (m_spInPlaceSite) { + RECT rect = {0, 0, sizeHint.width(), sizeHint.height()}; + m_spInPlaceSite->OnPosRectChange(&rect); + } + } + updateMask(); + } + + return res; +} + +//**** IConnectionPointContainer +/* + Provide the IEnumConnectionPoints implemented in the QAxSignalVec class. +*/ +HRESULT WINAPI QAxServerBase::EnumConnectionPoints(IEnumConnectionPoints **epoints) +{ + if (!epoints) + return E_POINTER; + *epoints = new QAxSignalVec(points); + (*epoints)->AddRef(); + return S_OK; +} + +/* + Provide the IConnectionPoint implemented in the QAxConnection for \a iid. +*/ +HRESULT WINAPI QAxServerBase::FindConnectionPoint(REFIID iid, IConnectionPoint **cpoint) +{ + if (!cpoint) + return E_POINTER; + + IConnectionPoint *cp = points[iid]; + *cpoint = cp; + if (cp) { + cp->AddRef(); + return S_OK; + } + return CONNECT_E_NOCONNECTION; +} + +//**** IPersistStream +/* + \reimp + + See documentation of IPersistStorage::IsDirty. +*/ +HRESULT WINAPI QAxServerBase::IsDirty() +{ + return dirtyflag ? S_OK : S_FALSE; +} + +HRESULT WINAPI QAxServerBase::Load(IStream *pStm) +{ + STATSTG stat; + HRESULT hres = pStm->Stat(&stat, STATFLAG_DEFAULT); + bool openAsText = false; + QByteArray qtarray; + if (hres == S_OK) { + QString streamName = QString::fromUtf16((const ushort *)stat.pwcsName); + CoTaskMemFree(stat.pwcsName); + openAsText = streamName == QLatin1String("SomeStreamName"); + if (stat.cbSize.HighPart) // more than 4GB - too large! + return S_FALSE; + + qtarray.resize(stat.cbSize.LowPart); + ULONG read; + pStm->Read(qtarray.data(), stat.cbSize.LowPart, &read); + } + const QMetaObject *mo = qt.object->metaObject(); + + QBuffer qtbuffer(&qtarray); + QByteArray mimeType = mo->classInfo(mo->indexOfClassInfo("MIME")).value(); + if (!mimeType.isEmpty()) { + mimeType = mimeType.left(mimeType.indexOf(':')); // first type + QAxBindable *axb = (QAxBindable*)qt.object->qt_metacast("QAxBindable"); + if (axb && axb->readData(&qtbuffer, QString::fromLatin1(mimeType))) + return S_OK; + } + + qtbuffer.close(); // resets + qtbuffer.open(openAsText ? (QIODevice::ReadOnly | QIODevice::Text) : QIODevice::ReadOnly); + + QDataStream qtstream(&qtbuffer); + int version; + qtstream >> version; + qtstream.setVersion(version); + int more = 0; + qtstream >> more; + + while (!qtbuffer.atEnd() && more) { + QString propname; + QVariant value; + qtstream >> propname; + if (propname.isEmpty()) + break; + qtstream >> value; + qtstream >> more; + + int idx = mo->indexOfProperty(propname.toLatin1()); + QMetaProperty property = mo->property(idx); + if (property.isWritable()) + qt.object->setProperty(propname.toLatin1(), value); + } + return S_OK; +} + +HRESULT WINAPI QAxServerBase::Save(IStream *pStm, BOOL clearDirty) +{ + const QMetaObject *mo = qt.object->metaObject(); + + QBuffer qtbuffer; + bool saved = false; + QByteArray mimeType = mo->classInfo(mo->indexOfClassInfo("MIME")).value(); + if (!mimeType.isEmpty()) { + QAxBindable *axb = (QAxBindable*)qt.object->qt_metacast("QAxBindable"); + saved = axb && axb->writeData(&qtbuffer); + qtbuffer.close(); + } + + if (!saved) { + qtbuffer.open(QIODevice::WriteOnly); + QDataStream qtstream(&qtbuffer); + qtstream << qtstream.version(); + + for (int prop = 0; prop < mo->propertyCount(); ++prop) { + if (!isPropertyExposed(prop)) + continue; + QMetaProperty metaprop = mo->property(prop); + if (QByteArray(metaprop.typeName()).endsWith('*')) + continue; + QString property = QLatin1String(metaprop.name()); + QVariant qvar = qt.object->property(metaprop.name()); + if (qvar.isValid()) { + qtstream << int(1); + qtstream << property; + qtstream << qvar; + } + } + + qtstream << int(0); + qtbuffer.close(); + } + + QByteArray qtarray = qtbuffer.buffer(); + ULONG written = 0; + const char *data = qtarray.constData(); + ULARGE_INTEGER newsize; + newsize.HighPart = 0; + newsize.LowPart = qtarray.size(); + pStm->SetSize(newsize); + pStm->Write(data, qtarray.size(), &written); + pStm->Commit(STGC_ONLYIFCURRENT); + + if (clearDirty) + dirtyflag = false; + return S_OK; +} + +HRESULT WINAPI QAxServerBase::GetSizeMax(ULARGE_INTEGER *pcbSize) +{ + const QMetaObject *mo = qt.object->metaObject(); + + int np = mo->propertyCount(); + pcbSize->HighPart = 0; + pcbSize->LowPart = np * 50; + + return S_OK; +} + +//**** IPersistStorage + +HRESULT WINAPI QAxServerBase::InitNew(IStorage *pStg) +{ + if (initNewCalled) + return CO_E_ALREADYINITIALIZED; + + dirtyflag = false; + initNewCalled = true; + + m_spStorage = pStg; + if (m_spStorage) + m_spStorage->AddRef(); + return S_OK; +} + +HRESULT WINAPI QAxServerBase::Load(IStorage *pStg) +{ + if (InitNew(pStg) != S_OK) + return CO_E_ALREADYINITIALIZED; + + IStream *spStream = 0; + QString streamName = QLatin1String(qt.object->metaObject()->className()); + streamName.replace(QLatin1Char(':'), QLatin1Char('.')); + /* Also invalid, but not relevant + streamName.replace(QLatin1Char('/'), QLatin1Char('_')); + streamName.replace(QLatin1Char('\\'), QLatin1Char('_')); + */ + streamName += QLatin1String("_Stream4.2"); + + pStg->OpenStream((const WCHAR *)streamName.utf16(), 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &spStream); + if (!spStream) // support for streams saved with 4.1 and earlier + pStg->OpenStream(L"SomeStreamName", 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &spStream); + if (!spStream) + return E_FAIL; + + Load(spStream); + spStream->Release(); + + return S_OK; +} + +HRESULT WINAPI QAxServerBase::Save(IStorage *pStg, BOOL fSameAsLoad) +{ + IStream *spStream = 0; + QString streamName = QLatin1String(qt.object->metaObject()->className()); + streamName.replace(QLatin1Char(':'), QLatin1Char('.')); + /* Also invalid, but not relevant + streamName.replace(QLatin1Char('/'), QLatin1Char('_')); + streamName.replace(QLatin1Char('\\'), QLatin1Char('_')); + */ + streamName += QLatin1String("_Stream4.2"); + + pStg->CreateStream((const WCHAR *)streamName.utf16(), STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &spStream); + if (!spStream) + return E_FAIL; + + Save(spStream, true); + + spStream->Release(); + return S_OK; +} + +HRESULT WINAPI QAxServerBase::SaveCompleted(IStorage *pStgNew) +{ + if (pStgNew) { + if (m_spStorage) + m_spStorage->Release(); + m_spStorage = pStgNew; + m_spStorage->AddRef(); + } + return S_OK; +} + +HRESULT WINAPI QAxServerBase::HandsOffStorage() +{ + if (m_spStorage) m_spStorage->Release(); + m_spStorage = 0; + + return S_OK; +} + +//**** IPersistPropertyBag +/* + Initialize the properties of the Qt widget. +*/ +HRESULT WINAPI QAxServerBase::InitNew() +{ + if (initNewCalled) + return CO_E_ALREADYINITIALIZED; + + dirtyflag = false; + initNewCalled = true; + return S_OK; +} + +/* + Set the properties of the Qt widget to the values provided in the \a bag. +*/ +HRESULT WINAPI QAxServerBase::Load(IPropertyBag *bag, IErrorLog * /*log*/) +{ + if (!bag) + return E_POINTER; + + if (InitNew() != S_OK) + return E_UNEXPECTED; + + bool error = false; + const QMetaObject *mo = qt.object->metaObject(); + for (int prop = 0; prop < mo->propertyCount(); ++prop) { + if (!isPropertyExposed(prop)) + continue; + QMetaProperty property = mo->property(prop); + const char* pname = property.name(); + BSTR bstr = QStringToBSTR(QLatin1String(pname)); + VARIANT var; + var.vt = VT_EMPTY; + HRESULT res = bag->Read(bstr, &var, 0); + if (property.isWritable() && var.vt != VT_EMPTY) { + if (res != S_OK || !qt.object->setProperty(pname, VARIANTToQVariant(var, property.typeName(), property.type()))) + error = true; + } + SysFreeString(bstr); + } + + updateGeometry(); + + return /*error ? E_FAIL :*/ S_OK; +} + +/* + Save the properties of the Qt widget into the \a bag. +*/ +HRESULT WINAPI QAxServerBase::Save(IPropertyBag *bag, BOOL clearDirty, BOOL /*saveAll*/) +{ + if (!bag) + return E_POINTER; + + if (clearDirty) + dirtyflag = false; + bool error = false; + const QMetaObject *mo = qt.object->metaObject(); + for (int prop = 0; prop < mo->propertyCount(); ++prop) { + if (!isPropertyExposed(prop)) + continue; + QMetaProperty property = mo->property(prop); + if (QByteArray(property.typeName()).endsWith('*')) + continue; + + BSTR bstr = QStringToBSTR(QLatin1String(property.name())); + QVariant qvar = qt.object->property(property.name()); + if (!qvar.isValid()) + error = true; + VARIANT var; + QVariantToVARIANT(qvar, var); + bag->Write(bstr, &var); + SysFreeString(bstr); + } + return /*error ? E_FAIL :*/ S_OK; +} + +//**** IPersistFile +/* +*/ +HRESULT WINAPI QAxServerBase::SaveCompleted(LPCOLESTR fileName) +{ + if (qt.object->metaObject()->indexOfClassInfo("MIME") == -1) + return E_NOTIMPL; + + currentFileName = QString::fromUtf16(reinterpret_cast<const ushort *>(fileName)); + return S_OK; +} + +HRESULT WINAPI QAxServerBase::GetCurFile(LPOLESTR *currentFile) +{ + if (qt.object->metaObject()->indexOfClassInfo("MIME") == -1) + return E_NOTIMPL; + + if (currentFileName.isEmpty()) { + *currentFile = 0; + return S_FALSE; + } + IMalloc *malloc = 0; + CoGetMalloc(1, &malloc); + if (!malloc) + return E_OUTOFMEMORY; + + *currentFile = static_cast<WCHAR *>(malloc->Alloc(currentFileName.length() * 2)); + malloc->Release(); + memcpy(*currentFile, currentFileName.unicode(), currentFileName.length() * 2); + + return S_OK; +} + +HRESULT WINAPI QAxServerBase::Load(LPCOLESTR fileName, DWORD mode) +{ + const QMetaObject *mo = qt.object->metaObject(); + int mimeIndex = mo->indexOfClassInfo("MIME"); + if (mimeIndex == -1) + return E_NOTIMPL; + + QAxBindable *axb = (QAxBindable*)qt.object->qt_metacast("QAxBindable"); + if (!axb) { + qWarning() << class_name << ": No QAxBindable implementation for mime-type handling"; + return E_NOTIMPL; + } + + QString loadFileName = QString::fromUtf16(reinterpret_cast<const ushort *>(fileName)); + QString fileExtension = loadFileName.mid(loadFileName.lastIndexOf(QLatin1Char('.')) + 1); + QFile file(loadFileName); + + QString mimeType = QLatin1String(mo->classInfo(mimeIndex).value()); + QStringList mimeTypes = mimeType.split(QLatin1Char(';')); + for (int m = 0; m < mimeTypes.count(); ++m) { + QString mime = mimeTypes.at(m); + if (mime.count(QLatin1Char(':')) != 2) { + qWarning() << class_name << ": Invalid syntax in Q_CLASSINFO for MIME"; + continue; + } + + mimeType = mime.left(mimeType.indexOf(QLatin1Char(':'))); // first type + if (mimeType.isEmpty()) { + qWarning() << class_name << ": Invalid syntax in Q_CLASSINFO for MIME"; + continue; + } + QString mimeExtension = mime.mid(mimeType.length() + 1); + mimeExtension = mimeExtension.left(mimeExtension.indexOf(QLatin1Char(':'))); + if (mimeExtension != fileExtension) + continue; + + if (axb->readData(&file, mimeType)) { + currentFileName = loadFileName; + return S_OK; + } + } + + return E_FAIL; +} + +HRESULT WINAPI QAxServerBase::Save(LPCOLESTR fileName, BOOL fRemember) +{ + const QMetaObject *mo = qt.object->metaObject(); + int mimeIndex = mo->indexOfClassInfo("MIME"); + if (mimeIndex == -1) + return E_NOTIMPL; + + QAxBindable *axb = (QAxBindable*)qt.object->qt_metacast("QAxBindable"); + if (!axb) { + qWarning() << class_name << ": No QAxBindable implementation for mime-type handling"; + return E_NOTIMPL; + } + + QString saveFileName = QString::fromUtf16(reinterpret_cast<const ushort *>(fileName)); + QString fileExtension = saveFileName.mid(saveFileName.lastIndexOf(QLatin1Char('.')) + 1); + QFile file(saveFileName); + + QString mimeType = QLatin1String(mo->classInfo(mimeIndex).value()); + QStringList mimeTypes = mimeType.split(QLatin1Char(';')); + for (int m = 0; m < mimeTypes.count(); ++m) { + QString mime = mimeTypes.at(m); + if (mime.count(QLatin1Char(':')) != 2) { + qWarning() << class_name << ": Invalid syntax in Q_CLASSINFO for MIME"; + continue; + } + mimeType = mime.left(mimeType.indexOf(QLatin1Char(':'))); // first type + if (mimeType.isEmpty()) { + qWarning() << class_name << ": Invalid syntax in Q_CLASSINFO for MIME"; + continue; + } + QString mimeExtension = mime.mid(mimeType.length() + 1); + mimeExtension = mimeExtension.left(mimeExtension.indexOf(QLatin1Char(':'))); + if (mimeExtension != fileExtension) + continue; + if (axb->writeData(&file)) { + if (fRemember) + currentFileName = saveFileName; + return S_OK; + } + } + return E_FAIL; +} + +//**** IViewObject +/* + Draws the widget into the provided device context. +*/ +HRESULT WINAPI QAxServerBase::Draw(DWORD dwAspect, LONG lindex, void *pvAspect, DVTARGETDEVICE *ptd, + HDC hicTargetDev, HDC hdcDraw, LPCRECTL lprcBounds, LPCRECTL /*lprcWBounds*/, + BOOL(__stdcall* /*pfnContinue*/)(ULONG_PTR), ULONG_PTR /*dwContinue*/) +{ + if (!lprcBounds) + return E_INVALIDARG; + + internalCreate(); + if (!isWidget || !qt.widget) + return OLE_E_BLANK; + + switch (dwAspect) { + case DVASPECT_CONTENT: + case DVASPECT_OPAQUE: + case DVASPECT_TRANSPARENT: + break; + default: + return DV_E_DVASPECT; + } + if (!ptd) + hicTargetDev = 0; + + bool bDeleteDC = false; + if (!hicTargetDev) { + hicTargetDev = ::CreateDCA("DISPLAY", NULL, NULL, NULL); + bDeleteDC = (hicTargetDev != hdcDraw); + } + + RECTL rc = *lprcBounds; + bool bMetaFile = GetDeviceCaps(hdcDraw, TECHNOLOGY) == DT_METAFILE; + if (!bMetaFile) + ::LPtoDP(hicTargetDev, (LPPOINT)&rc, 2); + + QPixmap pm = QPixmap::grabWidget(qt.widget); + HBITMAP hbm = pm.toWinHBITMAP(); + HDC hdc = CreateCompatibleDC(0); + SelectObject(hdc, hbm); + ::StretchBlt(hdcDraw, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, hdc, 0, 0,pm.width(), pm.height(), SRCCOPY); + DeleteDC(hdc); + DeleteObject(hbm); + + if (bDeleteDC) + DeleteDC(hicTargetDev); + + return S_OK; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::GetColorSet(DWORD dwDrawAspect, LONG lindex, void *pvAspect, DVTARGETDEVICE *ptd, + HDC hicTargetDev, LOGPALETTE **ppColorSet) +{ + return E_NOTIMPL; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::Freeze(DWORD dwAspect, LONG lindex, void *pvAspect, DWORD *pdwFreeze) +{ + return E_NOTIMPL; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::Unfreeze(DWORD dwFreeze) +{ + return E_NOTIMPL; +} + +/* + Stores the provided advise sink. +*/ +HRESULT WINAPI QAxServerBase::SetAdvise(DWORD /*aspects*/, DWORD /*advf*/, IAdviseSink *pAdvSink) +{ + if (m_spAdviseSink) m_spAdviseSink->Release(); + + m_spAdviseSink = pAdvSink; + if (m_spAdviseSink) m_spAdviseSink->AddRef(); + return S_OK; +} + +/* + Returns the advise sink. +*/ +HRESULT WINAPI QAxServerBase::GetAdvise(DWORD* /*aspects*/, DWORD* /*advf*/, IAdviseSink **ppAdvSink) +{ + if (!ppAdvSink) + return E_POINTER; + + *ppAdvSink = m_spAdviseSink; + if (*ppAdvSink) + (*ppAdvSink)->AddRef(); + return S_OK; +} + +//**** IViewObject2 +/* + Returns the current size ONLY if the widget has already been sized. +*/ +HRESULT WINAPI QAxServerBase::GetExtent(DWORD dwAspect, LONG /*lindex*/, DVTARGETDEVICE* /*ptd*/, LPSIZEL lpsizel) +{ + if (!isWidget || !qt.widget || !qt.widget->testAttribute(Qt::WA_Resized)) + return OLE_E_BLANK; + + return GetExtent(dwAspect, lpsizel); +} + +//**** IOleControl +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::GetControlInfo(LPCONTROLINFO) +{ + return E_NOTIMPL; +} + +/* + Turns event firing on and off. +*/ +HRESULT WINAPI QAxServerBase::FreezeEvents(BOOL bFreeze) +{ + // member of CComControl + if (bFreeze) + freezeEvents++; + else + freezeEvents--; + + return S_OK; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::OnMnemonic(LPMSG) +{ + return E_NOTIMPL; +} + +/* + Update the ambient properties of the Qt widget. +*/ +HRESULT WINAPI QAxServerBase::OnAmbientPropertyChange(DISPID dispID) +{ + if (!m_spClientSite || !theObject) + return S_OK; + + IDispatch *disp = 0; + m_spClientSite->QueryInterface(IID_IDispatch, (void**)&disp); + if (!disp) + return S_OK; + + VARIANT var; + VariantInit(&var); + DISPPARAMS params = { 0, 0, 0, 0 }; + disp->Invoke(dispID, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &var, 0, 0); + disp->Release(); + disp = 0; + + switch(dispID) { + case DISPID_AMBIENT_APPEARANCE: + break; + case DISPID_AMBIENT_AUTOCLIP: + break; + case DISPID_AMBIENT_BACKCOLOR: + case DISPID_AMBIENT_FORECOLOR: + if (isWidget) { + long rgb; + if (var.vt == VT_UI4) + rgb = var.ulVal; + else if (var.vt == VT_I4) + rgb = var.lVal; + else + break; + QPalette pal = qt.widget->palette(); + pal.setColor(dispID == DISPID_AMBIENT_BACKCOLOR ? QPalette::Window : QPalette::WindowText, + OLEColorToQColor(rgb)); + qt.widget->setPalette(pal); + } + break; + case DISPID_AMBIENT_DISPLAYASDEFAULT: + break; + case DISPID_AMBIENT_DISPLAYNAME: + if (var.vt != VT_BSTR || !isWidget) + break; + qt.widget->setWindowTitle(QString::fromUtf16((const ushort *)var.bstrVal)); + break; + case DISPID_AMBIENT_FONT: + if (var.vt != VT_DISPATCH || !isWidget) + break; + { + QVariant qvar = VARIANTToQVariant(var, "QFont", QVariant::Font); + QFont qfont = qVariantValue<QFont>(qvar); + qt.widget->setFont(qfont); + } + break; + case DISPID_AMBIENT_LOCALEID: + break; + case DISPID_AMBIENT_MESSAGEREFLECT: + if (var.vt != VT_BOOL) + break; + if (var.boolVal) + qt.widget->installEventFilter(this); + else + qt.widget->removeEventFilter(this); + break; + case DISPID_AMBIENT_PALETTE: + break; + case DISPID_AMBIENT_SCALEUNITS: + break; + case DISPID_AMBIENT_SHOWGRABHANDLES: + break; + case DISPID_AMBIENT_SHOWHATCHING: + break; + case DISPID_AMBIENT_SUPPORTSMNEMONICS: + break; + case DISPID_AMBIENT_TEXTALIGN: + break; + case DISPID_AMBIENT_UIDEAD: + if (var.vt != VT_BOOL || !isWidget) + break; + qt.widget->setEnabled(!var.boolVal); + break; + case DISPID_AMBIENT_USERMODE: + if (var.vt != VT_BOOL) + break; + inDesignMode = !var.boolVal; + break; + case DISPID_AMBIENT_RIGHTTOLEFT: + if (var.vt != VT_BOOL) + break; + qApp->setLayoutDirection(var.boolVal?Qt::RightToLeft:Qt::LeftToRight); + break; + } + + return S_OK; +} + +//**** IOleWindow +/* + Returns the HWND of the control. +*/ +HRESULT WINAPI QAxServerBase::GetWindow(HWND *pHwnd) +{ + if (!pHwnd) + return E_POINTER; + *pHwnd = m_hWnd; + return S_OK; +} + +/* + Enters What's This mode. +*/ +HRESULT WINAPI QAxServerBase::ContextSensitiveHelp(BOOL fEnterMode) +{ + if (fEnterMode) + QWhatsThis::enterWhatsThisMode(); + else + QWhatsThis::leaveWhatsThisMode(); + return S_OK; +} + +//**** IOleInPlaceObject +/* + Deactivates the control in place. +*/ +HRESULT WINAPI QAxServerBase::InPlaceDeactivate() +{ + if (!isInPlaceActive) + return S_OK; + UIDeactivate(); + + isInPlaceActive = false; + + // if we have a window, tell it to go away. + if (m_hWnd) { + if (::IsWindow(m_hWnd)) + ::DestroyWindow(m_hWnd); + m_hWnd = 0; + } + + if (m_spInPlaceSite) + m_spInPlaceSite->OnInPlaceDeactivate(); + + return S_OK; +} + +/* + Deactivates the control's user interface. +*/ +HRESULT WINAPI QAxServerBase::UIDeactivate() +{ + // if we're not UIActive, not much to do. + if (!isUIActive || !m_spInPlaceSite) + return S_OK; + + isUIActive = false; + + // notify frame windows, if appropriate, that we're no longer ui-active. + HWND hwndParent; + if (m_spInPlaceSite->GetWindow(&hwndParent) == S_OK) { + if (m_spInPlaceFrame) m_spInPlaceFrame->Release(); + m_spInPlaceFrame = 0; + IOleInPlaceUIWindow *spInPlaceUIWindow = 0; + RECT rcPos, rcClip; + OLEINPLACEFRAMEINFO frameInfo; + frameInfo.cb = sizeof(OLEINPLACEFRAMEINFO); + + m_spInPlaceSite->GetWindowContext(&m_spInPlaceFrame, &spInPlaceUIWindow, &rcPos, &rcClip, &frameInfo); + if (spInPlaceUIWindow) { + spInPlaceUIWindow->SetActiveObject(0, 0); + spInPlaceUIWindow->Release(); + } + if (m_spInPlaceFrame) { + removeMenu(); + if (menuBar) { + menuBar->removeEventFilter(this); + menuBar = 0; + } + if (statusBar) { + statusBar->removeEventFilter(this); + const int index = statusBar->metaObject()->indexOfSignal("messageChanged(QString)"); + QMetaObject::disconnect(statusBar, index, this, -1); + statusBar = 0; + } + m_spInPlaceFrame->SetActiveObject(0, 0); + m_spInPlaceFrame->Release(); + m_spInPlaceFrame = 0; + } + } + // we don't need to explicitly release the focus here since somebody + // else grabbing the focus is usually why we are getting called at all + m_spInPlaceSite->OnUIDeactivate(false); + + return S_OK; +} + +/* + Positions the control, and applies requested clipping. +*/ +HRESULT WINAPI QAxServerBase::SetObjectRects(LPCRECT prcPos, LPCRECT prcClip) +{ + if (prcPos == 0 || prcClip == 0) + return E_POINTER; + + if (m_hWnd) { + // the container wants us to clip, so figure out if we really need to + RECT rcIXect; + BOOL b = IntersectRect(&rcIXect, prcPos, prcClip); + HRGN tempRgn = 0; + if (b && !EqualRect(&rcIXect, prcPos)) { + OffsetRect(&rcIXect, -(prcPos->left), -(prcPos->top)); + tempRgn = CreateRectRgnIndirect(&rcIXect); + } + + ::SetWindowRgn(m_hWnd, tempRgn, true); + ::SetWindowPos(m_hWnd, 0, prcPos->left, prcPos->top, + prcPos->right - prcPos->left, prcPos->bottom - prcPos->top, + SWP_NOZORDER | SWP_NOACTIVATE); + } + + //Save the new extent. + m_currentExtent.rwidth() = qBound(qt.widget->minimumWidth(), int(prcPos->right - prcPos->left), qt.widget->maximumWidth()); + m_currentExtent.rheight() = qBound(qt.widget->minimumHeight(), int(prcPos->bottom - prcPos->top), qt.widget->maximumHeight()); + + return S_OK; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::ReactivateAndUndo() +{ + return E_NOTIMPL; +} + +//**** IOleInPlaceActiveObject + +Q_GUI_EXPORT int qt_translateKeyCode(int); + +HRESULT WINAPI QAxServerBase::TranslateAcceleratorW(MSG *pMsg) +{ + if (pMsg->message != WM_KEYDOWN || !isWidget) + return S_FALSE; + + DWORD dwKeyMod = 0; + if (::GetKeyState(VK_SHIFT) < 0) + dwKeyMod |= 1; // KEYMOD_SHIFT + if (::GetKeyState(VK_CONTROL) < 0) + dwKeyMod |= 2; // KEYMOD_CONTROL + if (::GetKeyState(VK_MENU) < 0) + dwKeyMod |= 4; // KEYMOD_ALT + + switch (LOWORD(pMsg->wParam)) { + case VK_TAB: + if (isUIActive) { + bool shift = ::GetKeyState(VK_SHIFT) < 0; + bool giveUp = true; + QWidget *curFocus = qt.widget->focusWidget(); + if (curFocus) { + if (shift) { + if (!curFocus->isWindow()) { + QWidget *nextFocus = curFocus->nextInFocusChain(); + QWidget *prevFocus = 0; + QWidget *topLevel = 0; + while (nextFocus != curFocus) { + if (nextFocus->focusPolicy() & Qt::TabFocus) { + prevFocus = nextFocus; + topLevel = 0; + } else if (nextFocus->isWindow()) { + topLevel = nextFocus; + } + nextFocus = nextFocus->nextInFocusChain(); + } + + if (!topLevel) { + giveUp = false; + ((HackWidget*)curFocus)->focusNextPrevChild(false); + } + } + } else { + QWidget *nextFocus = curFocus; + while (1) { + nextFocus = nextFocus->nextInFocusChain(); + if (nextFocus->isWindow()) + break; + if (nextFocus->focusPolicy() & Qt::TabFocus) { + giveUp = false; + ((HackWidget*)curFocus)->focusNextPrevChild(true); + break; + } + } + } + } + if (giveUp) { + HWND hwnd = ::GetParent(m_hWnd); + ::SetFocus(hwnd); + } else { + return S_OK; + } + + } + break; + + case VK_LEFT: + case VK_RIGHT: + case VK_UP: + case VK_DOWN: + if (isUIActive) + return S_FALSE; + break; + + default: + if (isUIActive && qt.widget->focusWidget()) { + int state = Qt::NoButton; + if (dwKeyMod & 1) + state |= Qt::ShiftModifier; + if (dwKeyMod & 2) + state |= Qt::ControlModifier; + if (dwKeyMod & 4) + state |= Qt::AltModifier; + + int key = pMsg->wParam; + if (!(key >= 'A' && key <= 'Z') && !(key >= '0' && key <= '9')) + key = qt_translateKeyCode(pMsg->wParam); + + QKeyEvent override(QEvent::ShortcutOverride, key, (Qt::KeyboardModifiers)state); + override.ignore(); + QApplication::sendEvent(qt.widget->focusWidget(), &override); + if (override.isAccepted()) + return S_FALSE; + } + break; + } + + if (!m_spClientSite) + return S_FALSE; + + IOleControlSite *controlSite = 0; + m_spClientSite->QueryInterface(IID_IOleControlSite, (void**)&controlSite); + if (!controlSite) + return S_FALSE; + + HRESULT hres = controlSite->TranslateAcceleratorW(pMsg, dwKeyMod); + + controlSite->Release(); + + return hres; +} + +HRESULT WINAPI QAxServerBase::TranslateAcceleratorA(MSG *pMsg) +{ + return TranslateAcceleratorW(pMsg); +} + +HRESULT WINAPI QAxServerBase::OnFrameWindowActivate(BOOL fActivate) +{ + if (fActivate) { + if (wasUIActive) + ::SetFocus(m_hWnd); + } else { + wasUIActive = isUIActive; + } + return S_OK; +} + +HRESULT WINAPI QAxServerBase::OnDocWindowActivate(BOOL fActivate) +{ + return S_OK; +} + +HRESULT WINAPI QAxServerBase::ResizeBorder(LPCRECT prcBorder, IOleInPlaceUIWindow *pUIWindow, BOOL fFrameWindow) +{ + return S_OK; +} + +HRESULT WINAPI QAxServerBase::EnableModeless(BOOL fEnable) +{ + if (!isWidget) + return S_OK; + + EnableWindow(qt.widget->winId(), fEnable); + return S_OK; +} + +//**** IOleObject + +static inline LPOLESTR QStringToOLESTR(const QString &qstring) +{ + LPOLESTR olestr = (wchar_t*)CoTaskMemAlloc(qstring.length()*2+2); + memcpy(olestr, (ushort*)qstring.unicode(), qstring.length()*2); + olestr[qstring.length()] = 0; + return olestr; +} + +/* + \reimp + + See documentation of IOleObject::GetUserType. +*/ +HRESULT WINAPI QAxServerBase::GetUserType(DWORD dwFormOfType, LPOLESTR *pszUserType) +{ + if (!pszUserType) + return E_POINTER; + + switch (dwFormOfType) { + case USERCLASSTYPE_FULL: + *pszUserType = QStringToOLESTR(class_name); + break; + case USERCLASSTYPE_SHORT: + if (!qt.widget || !isWidget || qt.widget->windowTitle().isEmpty()) + *pszUserType = QStringToOLESTR(class_name); + else + *pszUserType = QStringToOLESTR(qt.widget->windowTitle()); + break; + case USERCLASSTYPE_APPNAME: + *pszUserType = QStringToOLESTR(qApp->objectName()); + break; + } + + return S_OK; +} + +/* + Returns the status flags registered for this control. +*/ +HRESULT WINAPI QAxServerBase::GetMiscStatus(DWORD dwAspect, DWORD *pdwStatus) +{ + return OleRegGetMiscStatus(qAxFactory()->classID(class_name), dwAspect, pdwStatus); +} + +/* + Stores the provided advise sink. +*/ +HRESULT WINAPI QAxServerBase::Advise(IAdviseSink* pAdvSink, DWORD* pdwConnection) +{ + *pdwConnection = adviseSinks.count() + 1; + STATDATA data = { {0, 0, DVASPECT_CONTENT, -1, TYMED_NULL} , 0, pAdvSink, *pdwConnection }; + adviseSinks.append(data); + pAdvSink->AddRef(); + return S_OK; +} + +/* + Closes the control. +*/ +HRESULT WINAPI QAxServerBase::Close(DWORD dwSaveOption) +{ + if (dwSaveOption != OLECLOSE_NOSAVE && m_spClientSite) + m_spClientSite->SaveObject(); + if (isInPlaceActive) { + HRESULT hr = InPlaceDeactivate(); + if (FAILED(hr)) + return hr; + } + if (m_hWnd) { + if (IsWindow(m_hWnd)) + DestroyWindow(m_hWnd); + m_hWnd = 0; + if (m_spClientSite) + m_spClientSite->OnShowWindow(false); + } + + if (m_spInPlaceSite) m_spInPlaceSite->Release(); + m_spInPlaceSite = 0; + + if (m_spAdviseSink) + m_spAdviseSink->OnClose(); + for (int i = 0; i < adviseSinks.count(); ++i) { + adviseSinks.at(i).pAdvSink->OnClose(); + } + + return S_OK; +} + +bool qax_disable_inplaceframe = true; + +/* + Executes the steps to activate the control. +*/ +HRESULT QAxServerBase::internalActivate() +{ + if (!m_spClientSite) + return S_OK; + if (!m_spInPlaceSite) + m_spClientSite->QueryInterface(IID_IOleInPlaceSite, (void**)&m_spInPlaceSite); + if (!m_spInPlaceSite) + return E_FAIL; + + HRESULT hr = E_FAIL; + if (!isInPlaceActive) { + BOOL bNoRedraw = false; + hr = m_spInPlaceSite->CanInPlaceActivate(); + if (FAILED(hr)) + return hr; + if (hr != S_OK) + return E_FAIL; + m_spInPlaceSite->OnInPlaceActivate(); + } + + isInPlaceActive = true; + OnAmbientPropertyChange(DISPID_AMBIENT_USERMODE); + + if (isWidget) { + IOleInPlaceUIWindow *spInPlaceUIWindow = 0; + HWND hwndParent; + if (m_spInPlaceSite->GetWindow(&hwndParent) == S_OK) { + // get location in the parent window, as well as some information about the parent + if (m_spInPlaceFrame) m_spInPlaceFrame->Release(); + m_spInPlaceFrame = 0; + RECT rcPos, rcClip; + OLEINPLACEFRAMEINFO frameInfo; + frameInfo.cb = sizeof(OLEINPLACEFRAMEINFO); + m_spInPlaceSite->GetWindowContext(&m_spInPlaceFrame, &spInPlaceUIWindow, &rcPos, &rcClip, &frameInfo); + if (m_hWnd) { + ::ShowWindow(m_hWnd, SW_SHOW); + if (!::IsChild(m_hWnd, ::GetFocus()) && qt.widget->focusPolicy() != Qt::NoFocus) + ::SetFocus(m_hWnd); + } else { + create(hwndParent, rcPos); + } + } + + // Gone active by now, take care of UIACTIVATE + canTakeFocus = qt.widget->focusPolicy() != Qt::NoFocus && !inDesignMode; + if (!canTakeFocus && !inDesignMode) { + QList<QWidget*> widgets = qFindChildren<QWidget*>(qt.widget); + for (int w = 0; w < widgets.count(); ++w) { + QWidget *widget = widgets[w]; + canTakeFocus = widget->focusPolicy() != Qt::NoFocus; + if (canTakeFocus) + break; + } + } + if (!isUIActive && canTakeFocus) { + isUIActive = true; + hr = m_spInPlaceSite->OnUIActivate(); + if (FAILED(hr)) { + if (m_spInPlaceFrame) m_spInPlaceFrame->Release(); + m_spInPlaceFrame = 0; + if (spInPlaceUIWindow) spInPlaceUIWindow->Release(); + return hr; + } + + if (isInPlaceActive) { + if (!::IsChild(m_hWnd, ::GetFocus())) + ::SetFocus(m_hWnd); + } + + if (m_spInPlaceFrame) { + hr = m_spInPlaceFrame->SetActiveObject(this, QStringToBSTR(class_name)); + if (!FAILED(hr)) { + menuBar = (qt.widget && !qax_disable_inplaceframe) ? qFindChild<QMenuBar*>(qt.widget) : 0; + if (menuBar && !menuBar->isVisible()) { + createMenu(menuBar); + menuBar->hide(); + menuBar->installEventFilter(this); + } + statusBar = qt.widget ? qFindChild<QStatusBar*>(qt.widget) : 0; + if (statusBar && !statusBar->isVisible()) { + const int index = statusBar->metaObject()->indexOfSignal("messageChanged(QString)"); + QMetaObject::connect(statusBar, index, this, -1); + statusBar->hide(); + statusBar->installEventFilter(this); + } + } + } + if (spInPlaceUIWindow) { + spInPlaceUIWindow->SetActiveObject(this, QStringToBSTR(class_name)); + spInPlaceUIWindow->SetBorderSpace(0); + } + } + if (spInPlaceUIWindow) spInPlaceUIWindow->Release(); + ShowWindow(m_hWnd, SW_NORMAL); + } + + m_spClientSite->ShowObject(); + + return S_OK; +} + +/* + Executes the "verb" \a iVerb. +*/ +HRESULT WINAPI QAxServerBase::DoVerb(LONG iVerb, LPMSG /*lpmsg*/, IOleClientSite* /*pActiveSite*/, LONG /*lindex*/, + HWND /*hwndParent*/, LPCRECT /*prcPosRect*/) +{ + HRESULT hr = E_NOTIMPL; + switch (iVerb) + { + case OLEIVERB_SHOW: + hr = internalActivate(); + if (SUCCEEDED(hr)) + hr = S_OK; + break; + + case OLEIVERB_PRIMARY: + case OLEIVERB_INPLACEACTIVATE: + hr = internalActivate(); + if (SUCCEEDED(hr)) { + hr = S_OK; + update(); + } + break; + + case OLEIVERB_UIACTIVATE: + if (!isUIActive) { + hr = internalActivate(); + if (SUCCEEDED(hr)) + hr = S_OK; + } + break; + + case OLEIVERB_HIDE: + UIDeactivate(); + if (m_hWnd) + ::ShowWindow(m_hWnd, SW_HIDE); + hr = S_OK; + return hr; + + default: + break; + } + return hr; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::EnumAdvise(IEnumSTATDATA** /*ppenumAdvise*/) +{ + return E_NOTIMPL; +} + +/* + Returns an enumerator for the verbs registered for this class. +*/ +HRESULT WINAPI QAxServerBase::EnumVerbs(IEnumOLEVERB** ppEnumOleVerb) +{ + if (!ppEnumOleVerb) + return E_POINTER; + return OleRegEnumVerbs(qAxFactory()->classID(class_name), ppEnumOleVerb); +} + +/* + Returns the current client site.. +*/ +HRESULT WINAPI QAxServerBase::GetClientSite(IOleClientSite** ppClientSite) +{ + if (!ppClientSite) + return E_POINTER; + *ppClientSite = m_spClientSite; + if (*ppClientSite) + (*ppClientSite)->AddRef(); + return S_OK; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::GetClipboardData(DWORD, IDataObject**) +{ + return E_NOTIMPL; +} + +/* + Returns the current extent. +*/ +HRESULT WINAPI QAxServerBase::GetExtent(DWORD dwDrawAspect, SIZEL* psizel) +{ + if (dwDrawAspect != DVASPECT_CONTENT || !isWidget || !qt.widget) + return E_FAIL; + if (!psizel) + return E_POINTER; + + psizel->cx = MAP_PIX_TO_LOGHIM(m_currentExtent.width(), qt.widget->logicalDpiX()); + psizel->cy = MAP_PIX_TO_LOGHIM(m_currentExtent.height(), qt.widget->logicalDpiY()); + return S_OK; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::GetMoniker(DWORD, DWORD, IMoniker** ) +{ + return E_NOTIMPL; +} + +/* + Returns the CLSID of this class. +*/ +HRESULT WINAPI QAxServerBase::GetUserClassID(CLSID* pClsid) +{ + if (!pClsid) + return E_POINTER; + *pClsid = qAxFactory()->classID(class_name); + return S_OK; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::InitFromData(IDataObject*, BOOL, DWORD) +{ + return E_NOTIMPL; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::IsUpToDate() +{ + return S_OK; +} + +/* + Stores the client site. +*/ +HRESULT WINAPI QAxServerBase::SetClientSite(IOleClientSite* pClientSite) +{ + // release all client site interfaces + if (m_spClientSite) m_spClientSite->Release(); + if (m_spInPlaceSite) m_spInPlaceSite->Release(); + m_spInPlaceSite = 0; + if (m_spInPlaceFrame) m_spInPlaceFrame->Release(); + m_spInPlaceFrame = 0; + + m_spClientSite = pClientSite; + if (m_spClientSite) { + m_spClientSite->AddRef(); + m_spClientSite->QueryInterface(IID_IOleInPlaceSite, (void **)&m_spInPlaceSite); + } + + return S_OK; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::SetColorScheme(LOGPALETTE*) +{ + return E_NOTIMPL; +} + + +#ifdef QT_DLL // avoid conflict with symbol in static lib +bool qt_sendSpontaneousEvent(QObject *o, QEvent *e) +{ + return QCoreApplication::sendSpontaneousEvent(o, e); +} +#endif + +/* + Tries to set the size of the control. +*/ +HRESULT WINAPI QAxServerBase::SetExtent(DWORD dwDrawAspect, SIZEL* psizel) +{ + if (dwDrawAspect != DVASPECT_CONTENT) + return DV_E_DVASPECT; + if (!psizel) + return E_POINTER; + + if (!isWidget || !qt.widget) // nothing to do + return S_OK; + + QSize proposedSize(MAP_LOGHIM_TO_PIX(psizel->cx, qt.widget->logicalDpiX()), + MAP_LOGHIM_TO_PIX(psizel->cy, qt.widget->logicalDpiY())); + + // can the widget be resized at all? + if (qt.widget->minimumSize() == qt.widget->maximumSize() && qt.widget->minimumSize() != proposedSize) + return E_FAIL; + //Save the extent, bound to the widget restrictions. + m_currentExtent.rwidth() = qBound(qt.widget->minimumWidth(), proposedSize.width(), qt.widget->maximumWidth()); + m_currentExtent.rheight() = qBound(qt.widget->minimumHeight(), proposedSize.height(), qt.widget->maximumHeight()); + + resize(proposedSize); + return S_OK; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::SetHostNames(LPCOLESTR szContainerApp, LPCOLESTR szContainerObj) +{ + return S_OK; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::SetMoniker(DWORD, IMoniker*) +{ + return E_NOTIMPL; +} + +/* + Disconnects an advise sink. +*/ +HRESULT WINAPI QAxServerBase::Unadvise(DWORD dwConnection) +{ + for (int i = 0; i < adviseSinks.count(); ++i) { + STATDATA entry = adviseSinks.at(i); + if (entry.dwConnection == dwConnection) { + entry.pAdvSink->Release(); + adviseSinks.removeAt(i); + return S_OK; + } + } + return OLE_E_NOCONNECTION; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::Update() +{ + return S_OK; +} + +//**** IDataObject +/* + Calls IViewObject::Draw after setting up the parameters. +*/ +HRESULT WINAPI QAxServerBase::GetData(FORMATETC *pformatetcIn, STGMEDIUM *pmedium) +{ + if (!pmedium) + return E_POINTER; + if ((pformatetcIn->tymed & TYMED_MFPICT) == 0) + return DATA_E_FORMATETC; + + internalCreate(); + if (!isWidget || !qt.widget) + return E_UNEXPECTED; + + // Container wants to draw, but the size is not defined yet - ask container + if (m_spInPlaceSite && !qt.widget->testAttribute(Qt::WA_Resized)) { + IOleInPlaceUIWindow *spInPlaceUIWindow = 0; + RECT rcPos, rcClip; + OLEINPLACEFRAMEINFO frameInfo; + frameInfo.cb = sizeof(OLEINPLACEFRAMEINFO); + + HRESULT hres = m_spInPlaceSite->GetWindowContext(&m_spInPlaceFrame, &spInPlaceUIWindow, &rcPos, &rcClip, &frameInfo); + if (hres == S_OK) { + QSize size(rcPos.right - rcPos.left, rcPos.bottom - rcPos.top); + resize(size); + } else { + qt.widget->adjustSize(); + } + if (spInPlaceUIWindow) spInPlaceUIWindow->Release(); // no need for it + } + + int width = qt.widget->width(); + int height = qt.widget->height(); + RECTL rectl = {0, 0, width, height}; + + HDC hdc = CreateMetaFile(0); + SaveDC(hdc); + SetWindowOrgEx(hdc, 0, 0, 0); + SetWindowExtEx(hdc, rectl.right, rectl.bottom, 0); + + Draw(pformatetcIn->dwAspect, pformatetcIn->lindex, 0, pformatetcIn->ptd, 0, hdc, &rectl, &rectl, 0, 0); + + RestoreDC(hdc, -1); + HMETAFILE hMF = CloseMetaFile(hdc); + if (!hMF) + return E_UNEXPECTED; + + HGLOBAL hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, sizeof(METAFILEPICT)); + if (!hMem) { + DeleteMetaFile(hMF); + return ResultFromScode(STG_E_MEDIUMFULL); + } + + LPMETAFILEPICT pMF = (LPMETAFILEPICT)GlobalLock(hMem); + pMF->hMF = hMF; + pMF->mm = MM_ANISOTROPIC; + pMF->xExt = MAP_PIX_TO_LOGHIM(width, qt.widget->logicalDpiX()); + pMF->yExt = MAP_PIX_TO_LOGHIM(height, qt.widget->logicalDpiY()); + GlobalUnlock(hMem); + + memset(pmedium, 0, sizeof(STGMEDIUM)); + pmedium->tymed = TYMED_MFPICT; + pmedium->hGlobal = hMem; + pmedium->pUnkForRelease = 0; + + return S_OK; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::DAdvise(FORMATETC *pformatetc, DWORD advf, + IAdviseSink *pAdvSink, DWORD *pdwConnection) +{ + if (pformatetc->dwAspect != DVASPECT_CONTENT) + return E_FAIL; + + *pdwConnection = adviseSinks.count() + 1; + STATDATA data = { + {pformatetc->cfFormat,pformatetc->ptd,pformatetc->dwAspect,pformatetc->lindex,pformatetc->tymed}, + advf, pAdvSink, *pdwConnection + }; + adviseSinks.append(data); + pAdvSink->AddRef(); + return S_OK; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::DUnadvise(DWORD dwConnection) +{ + return Unadvise(dwConnection); +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::EnumDAdvise(IEnumSTATDATA ** /*ppenumAdvise*/) +{ + return E_NOTIMPL; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::GetDataHere(FORMATETC* /* pformatetc */, STGMEDIUM* /* pmedium */) +{ + return E_NOTIMPL; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::QueryGetData(FORMATETC* /* pformatetc */) +{ + return E_NOTIMPL; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::GetCanonicalFormatEtc(FORMATETC* /* pformatectIn */,FORMATETC* /* pformatetcOut */) +{ + return E_NOTIMPL; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::SetData(FORMATETC* /* pformatetc */, STGMEDIUM* /* pmedium */, BOOL /* fRelease */) +{ + return E_NOTIMPL; +} + +/* + Not implemented. +*/ +HRESULT WINAPI QAxServerBase::EnumFormatEtc(DWORD /* dwDirection */, IEnumFORMATETC** /* ppenumFormatEtc */) +{ + return E_NOTIMPL; +} + + + +static int mapModifiers(int state) +{ + int ole = 0; + if (state & Qt::ShiftModifier) + ole |= 1; + if (state & Qt::ControlModifier) + ole |= 2; + if (state & Qt::AltModifier) + ole |= 4; + + return ole; +} + +/* + \reimp +*/ +bool QAxServerBase::eventFilter(QObject *o, QEvent *e) +{ + if (!theObject) + return QObject::eventFilter(o, e); + + if ((e->type() == QEvent::Show || e->type() == QEvent::Hide) && (o == statusBar || o == menuBar)) { + if (o == menuBar) { + if (e->type() == QEvent::Hide) { + createMenu(menuBar); + } else if (e->type() == QEvent::Show) { + removeMenu(); + } + } else if (statusBar) { + statusBar->setSizeGripEnabled(false); + } + updateGeometry(); + if (m_spInPlaceSite && qt.widget->sizeHint().isValid()) { + RECT rect = {0, 0, qt.widget->sizeHint().width(), qt.widget->sizeHint().height()}; + m_spInPlaceSite->OnPosRectChange(&rect); + } + } + switch (e->type()) { + case QEvent::ChildAdded: + static_cast<QChildEvent*>(e)->child()->installEventFilter(this); + break; + case QEvent::ChildRemoved: + static_cast<QChildEvent*>(e)->child()->removeEventFilter(this); + break; + case QEvent::KeyPress: + if (o == qt.object && hasStockEvents) { + QKeyEvent *ke = (QKeyEvent*)e; + int key = ke->key(); + int state = ke->modifiers(); + void *argv[] = { + 0, + &key, + &state + }; + qt_metacall(QMetaObject::InvokeMetaMethod, DISPID_KEYDOWN, argv); + if (!ke->text().isEmpty()) + qt_metacall(QMetaObject::InvokeMetaMethod, DISPID_KEYPRESS, argv); + } + break; + case QEvent::KeyRelease: + if (o == qt.object && hasStockEvents) { + QKeyEvent *ke = (QKeyEvent*)e; + int key = ke->key(); + int state = ke->modifiers(); + void *argv[] = { + 0, + &key, + &state + }; + qt_metacall(QMetaObject::InvokeMetaMethod, DISPID_KEYUP, argv); + } + break; + case QEvent::MouseMove: + if (o == qt.object && hasStockEvents) { + QMouseEvent *me = (QMouseEvent*)e; + int button = me->buttons() & Qt::MouseButtonMask; + int state = mapModifiers(me->modifiers()); + int x = me->x(); + int y = me->y(); + void *argv[] = { + 0, + &button, + &state, + &x, + &y + }; + qt_metacall(QMetaObject::InvokeMetaMethod, DISPID_MOUSEMOVE, argv); + } + break; + case QEvent::MouseButtonRelease: + if (o == qt.object && hasStockEvents) { + QMouseEvent *me = (QMouseEvent*)e; + int button = me->button(); + int state = mapModifiers(me->modifiers()); + int x = me->x(); + int y = me->y(); + void *argv[] = { + 0, + &button, + &state, + &x, + &y + }; + qt_metacall(QMetaObject::InvokeMetaMethod, DISPID_MOUSEUP, argv); + qt_metacall(QMetaObject::InvokeMetaMethod, DISPID_CLICK, 0); + } + break; + case QEvent::MouseButtonDblClick: + if (o == qt.object && hasStockEvents) { + qt_metacall(QMetaObject::InvokeMetaMethod, DISPID_DBLCLICK, 0); + } + break; + case QEvent::MouseButtonPress: + if (m_spInPlaceSite && !isUIActive) { + internalActivate(); + } + if (o == qt.widget && hasStockEvents) { + QMouseEvent *me = (QMouseEvent*)e; + int button = me->button(); + int state = mapModifiers(me->modifiers()); + int x = me->x(); + int y = me->y(); + void *argv[] = { + 0, + &button, + &state, + &x, + &y + }; + qt_metacall(QMetaObject::InvokeMetaMethod, DISPID_MOUSEDOWN, argv); + } + break; + case QEvent::Show: + if (m_hWnd && o == qt.widget) + ShowWindow(m_hWnd, SW_SHOW); + updateMask(); + break; + case QEvent::Hide: + if (m_hWnd && o == qt.widget) + ShowWindow(m_hWnd, SW_HIDE); + break; + + case QEvent::EnabledChange: + if (m_hWnd && o == qt.widget) + EnableWindow(m_hWnd, qt.widget->isEnabled()); + // Fall Through + case QEvent::FontChange: + case QEvent::ActivationChange: + case QEvent::StyleChange: + case QEvent::IconTextChange: + case QEvent::ModifiedChange: + case QEvent::Resize: + updateMask(); + break; + case QEvent::WindowBlocked: { + if (!m_spInPlaceFrame) + break; + m_spInPlaceFrame->EnableModeless(FALSE); + MSG msg; + // Visual Basic 6.0 posts the message WM_USER+3078 from the EnableModeless(). + // While handling this message, VB will disable all current top-levels. After + // this we have to re-enable the Qt modal widget to receive input events. + if (PeekMessage(&msg, 0, WM_USER+3078, WM_USER+3078, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + QWidget *modalWidget = QApplication::activeModalWidget(); + if (modalWidget && modalWidget->isVisible() && modalWidget->isEnabled() + && !IsWindowEnabled(modalWidget->effectiveWinId())) + EnableWindow(modalWidget->effectiveWinId(), TRUE); + } + break; + } + case QEvent::WindowUnblocked: + if (!m_spInPlaceFrame) + break; + m_spInPlaceFrame->EnableModeless(TRUE); + break; + default: + break; + } + return QObject::eventFilter(o, e); +} + +QT_END_NAMESPACE +#endif // QT_NO_WIN_ACTIVEQT |