Revision control

++ // Copyright (C) 2015 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
++ // Qt-Security score:significant reason:default
++
++
++ #include "qaxwidget.h"
++ #include "qaxwidget_p.h"
++ #include "qaxbase_p.h"
++ #include <QtAxBase/private/qaxutils_p.h>
++ #include <QtAxBase/private/qaxtypefunctions_p.h>
++
++ #include <QtAxServer/qaxaggregated.h>
++
++ #include <qabstracteventdispatcher.h>
++ #include <qapplication.h>
++ #include <private/qapplication_p.h>
++ #include <qdockwidget.h>
++ #include <qevent.h>
++ #include <qlayout.h>
++ #include <qmainwindow.h>
++ #include <qmenu.h>
++ #include <qmenubar.h>
++ #include <qmetaobject.h>
++ #include <qpainter.h>
++ #include <qpointer.h>
++ #include <qregularexpression.h>
++ #include <quuid.h>
++ #include <qwhatsthis.h>
++ #include <qabstractnativeeventfilter.h>
++ #include <private/qpixmap_win_p.h>
++
++ #include <windowsx.h>
++ #include <ocidl.h>
++ #include <olectl.h>
++ #include <docobj.h>
++
++ #include <QtCore/private/qcomobject_p.h>
++
++ // #define QAX_DEBUG
++
++ #ifdef QAX_DEBUG
++ #define AX_DEBUG(x) qDebug(#x);
++ #else
++ #define AX_DEBUG(x);
++ #endif
++
++ // #define QAX_SUPPORT_WINDOWLESS
++ // #define QAX_SUPPORT_BORDERSPACE
++
++ // missing interface from win32api
++ #if defined(Q_CC_GNU) && !defined(__MINGW64_VERSION_MAJOR)
++ DECLARE_INTERFACE_(IOleInPlaceObjectWindowless,IOleInPlaceObject)
++ {
++ STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE;
++ STDMETHOD_(ULONG,AddRef)(THIS) PURE;
++ STDMETHOD_(ULONG,Release)(THIS) PURE;
++ STDMETHOD(GetWindow)(THIS_ HWND*) PURE;
++ STDMETHOD(ContextSensitiveHelp)(THIS_ BOOL) PURE;
++ STDMETHOD(InPlaceDeactivate)(THIS) PURE;
++ STDMETHOD(UIDeactivate)(THIS) PURE;
++ STDMETHOD(SetObjectRects)(THIS_ LPCRECT,LPCRECT) PURE;
++ STDMETHOD(ReactivateAndUndo)(THIS) PURE;
++ STDMETHOD(OnWindowMessage)(THIS_ UINT, WPARAM, LPARAM, LRESULT*) PURE;
++ STDMETHOD(GetDropTarget)(THIS_ IDropTarget**) PURE;
++ };
++ #endif
++
++ #include "../shared/qaxtypes_p.h"
++
++ QT_BEGIN_NAMESPACE
++
++ void QAxWidgetPrivate::emitException(int code, const QString &source, const QString &desc,
++ const QString &help)
++ {
++ Q_Q(QAxWidget);
++ emit q->exception(code, source, desc, help);
++ }
++
++ void QAxWidgetPrivate::emitPropertyChanged(const QString &name)
++ {
++ Q_Q(QAxWidget);
++ emit q->propertyChanged(name);
++ }
++
++ void QAxWidgetPrivate::emitSignal(const QString &name, int argc, void *argv)
++ {
++ Q_Q(QAxWidget);
++ emit q->signal(name, argc, argv);
++ }
++
++ /* \class QAxHostWidget
++ \brief The QAxHostWidget class is the actual container widget.
++
++ \internal
++ */
++ class QAxHostWidget : public QWidget
++ {
++ Q_DISABLE_COPY_MOVE(QAxHostWidget)
++ friend class QAxClientSite;
++ public:
++ QAxHostWidget(QWidget *parent, QAxClientSite *ax);
++ ~QAxHostWidget() override;
++
++ QSize sizeHint() const override;
++ QSize minimumSizeHint() const override;
++
++ int qt_metacall(QMetaObject::Call, int isignal, void **argv) override;
++ void *qt_metacast(const char *clname) override;
++
++ inline QAxClientSite *clientSite() const
++ {
++ return axhost;
++ }
++
++ QWindow *hostWindow() const;
++
++ protected:
++ bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override;
++ bool event(QEvent *e) override;
++ bool eventFilter(QObject *o, QEvent *e) override;
++ void resizeEvent(QResizeEvent *e) override;
++ void focusInEvent(QFocusEvent *e) override;
++ void focusOutEvent(QFocusEvent *e) override;
++ void paintEvent(QPaintEvent *e) override;
++ void showEvent(QShowEvent *e) override;
++ QPaintEngine *paintEngine() const override
++ {
++ return nullptr;
++ }
++
++ private:
++ void resizeObject();
++
++ int setFocusTimer;
++ bool hasFocus;
++ QAxClientSite *axhost;
++ };
++
++ namespace QtPrivate {
++
++ template <>
++ struct QComObjectTraits<IDispatch>
++ {
++ static constexpr bool isGuidOf(REFIID riid) noexcept
++ {
++ return QComObjectTraits<IDispatch, IUnknown>::isGuidOf(riid);
++ }
++ };
++
++ template <>
++ struct QComObjectTraits<IOleInPlaceSite>
++ {
++ static constexpr bool isGuidOf(REFIID riid) noexcept
++ {
++ return QComObjectTraits<IOleInPlaceSite, IOleWindow>::isGuidOf(riid);
++ }
++ };
++
++ template <>
++ struct QComObjectTraits<IOleInPlaceFrame>
++ {
++ static constexpr bool isGuidOf(REFIID riid) noexcept
++ {
++ return QComObjectTraits<IOleInPlaceFrame, IOleInPlaceUIWindow>::isGuidOf(riid);
++ }
++ };
++
++ #ifdef QAX_SUPPORT_WINDOWLESS
++ template <>
++ struct QComObjectTraits<IOleInPlaceSiteWindowless>
++ {
++ static constexpr bool isGuidOf(REFIID riid) noexcept
++ {
++ return QComObjectTraits<IOleInPlaceSiteWindowless, IOleInPlaceSiteEx>::isGuidOf(riid);
++ }
++ };
++ #endif
++
++ } // namespace QtPrivate
++
++ /* \class QAxClientSite
++ \brief The QAxClientSite class implements the client site interfaces.
++
++ \internal
++ */
++ class QAxClientSite : public QComObject<IDispatch, IOleClientSite, IOleControlSite,
++ #ifdef QAX_SUPPORT_WINDOWLESS
++ IOleInPlaceSiteWindowless,
++ #else
++ IOleInPlaceSite,
++ #endif
++ IOleInPlaceFrame, IOleDocumentSite, IAdviseSink>
++ {
++ Q_DISABLE_COPY_MOVE(QAxClientSite)
++ friend class QAxHostWidget;
++ public:
++ QAxClientSite(QAxWidget *c);
++ virtual ~QAxClientSite();
++
++ bool activateObject(bool initialized, const QByteArray &data);
++
++ void releaseAll();
++ void deactivate();
++ inline void reset(QWidget *p)
++ {
++ if (widget == p)
++ widget = nullptr;
++ else if (host == p)
++ host = nullptr;
++ }
++
++ inline IOleInPlaceActiveObject *inPlaceObject() const
++ {
++ return m_spInPlaceActiveObject.Get();
++ }
++
++ inline HRESULT doVerb(LONG index)
++ {
++ if (!m_spOleObject)
++ return E_NOTIMPL;
++ if (!host)
++ return OLE_E_NOT_INPLACEACTIVE;
++
++ RECT rcPos = qaxNativeWidgetRect(host);
++ return m_spOleObject->DoVerb(index, nullptr, this, 0,
++ reinterpret_cast<HWND>(host->winId()),
++ &rcPos);
++ }
++
++ // IUnknown
++ STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) override;
++
++ // IDispatch
++ HRESULT __stdcall GetTypeInfoCount(unsigned int *) override
++ { return E_NOTIMPL; }
++ HRESULT __stdcall GetTypeInfo(UINT, LCID, ITypeInfo **) override
++ { return E_NOTIMPL; }
++ HRESULT __stdcall GetIDsOfNames(const _GUID &, wchar_t **, unsigned int,
++ unsigned long, long *) override
++ { return E_NOTIMPL; }
++ HRESULT __stdcall Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
++ WORD wFlags, DISPPARAMS *pDispParams,
++ VARIANT *pVarResult, EXCEPINFO *pExcepInfo,
++ UINT *puArgErr) override;
++ void emitAmbientPropertyChange(DISPID dispid);
++
++ // IOleClientSite
++ STDMETHOD(SaveObject)() override;
++ STDMETHOD(GetMoniker)(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker **ppmk) override;
++ STDMETHOD(GetContainer)(LPOLECONTAINER FAR* ppContainer) override;
++ STDMETHOD(ShowObject)() override;
++ STDMETHOD(OnShowWindow)(BOOL fShow) override;
++ STDMETHOD(RequestNewObjectLayout)() override;
++
++ // IOleControlSite
++ STDMETHOD(OnControlInfoChanged)() override;
++ STDMETHOD(LockInPlaceActive)(BOOL fLock) override;
++ STDMETHOD(GetExtendedControl)(IDispatch** ppDisp) override;
++ STDMETHOD(TransformCoords)(POINTL* pPtlHimetric, POINTF* pPtfContainer, DWORD dwFlags) override;
++ STDMETHOD(TranslateAccelerator)(LPMSG lpMsg, DWORD grfModifiers) override;
++ STDMETHOD(OnFocus)(BOOL fGotFocus) override;
++ STDMETHOD(ShowPropertyFrame)() override;
++
++ // IOleWindow
++ STDMETHOD(GetWindow)(HWND *phwnd) override;
++ STDMETHOD(ContextSensitiveHelp)(BOOL fEnterMode) override;
++
++ // IOleInPlaceSite
++ STDMETHOD(CanInPlaceActivate)() override;
++ STDMETHOD(OnInPlaceActivate)() override;
++ STDMETHOD(OnUIActivate)() override;
++ STDMETHOD(GetWindowContext)(IOleInPlaceFrame **ppFrame, IOleInPlaceUIWindow **ppDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo) override;
++ STDMETHOD(Scroll)(SIZE scrollExtant) override;
++ STDMETHOD(OnUIDeactivate)(BOOL fUndoable) override;
++ STDMETHOD(OnInPlaceDeactivate)() override;
++ STDMETHOD(DiscardUndoState)() override;
++ STDMETHOD(DeactivateAndUndo)() override;
++ STDMETHOD(OnPosRectChange)(LPCRECT lprcPosRect) override;
++
++ #ifdef QAX_SUPPORT_WINDOWLESS
++ // IOleInPlaceSiteEx ###
++ STDMETHOD(OnInPlaceActivateEx)(BOOL* /*pfNoRedraw*/, DWORD /*dwFlags*/) override
++ {
++ return S_OK;
++ }
++ STDMETHOD(OnInPlaceDeactivateEx)(BOOL /*fNoRedraw*/) override
++ {
++ return S_OK;
++ }
++ STDMETHOD(RequestUIActivate)() override
++ {
++ return S_OK;
++ }
++
++ // IOleInPlaceSiteWindowless ###
++ STDMETHOD(CanWindowlessActivate)() override
++ {
++ return S_OK;
++ }
++ STDMETHOD(GetCapture)() override
++ {
++ return S_FALSE;
++ }
++ STDMETHOD(SetCapture)(BOOL /*fCapture*/) override
++ {
++ return S_FALSE;
++ }
++ STDMETHOD(GetFocus)() override
++ {
++ return S_FALSE;
++ }
++ STDMETHOD(SetFocus)(BOOL /*fCapture*/) override
++ {
++ return S_FALSE;
++ }
++ STDMETHOD(GetDC)(LPCRECT /*pRect*/, DWORD /*grfFlags*/, HDC *phDC) override
++ {
++ *phDC = 0;
++ return S_OK;
++ }
++ STDMETHOD(ReleaseDC)(HDC hDC) override
++ {
++ ::ReleaseDC((HWND)widget->winId(), hDC);
++ return S_OK;
++ }
++ STDMETHOD(InvalidateRect)(LPCRECT pRect, BOOL fErase) override
++ {
++ ::InvalidateRect((HWND)host->winId(), pRect, fErase);
++ return S_OK;
++ }
++ STDMETHOD(InvalidateRgn)(HRGN hRGN, BOOL fErase) override
++ {
++ ::InvalidateRgn((HWND)host->winId(), hRGN, fErase);
++ return S_OK;
++ }
++ STDMETHOD(ScrollRect)(int /*dx*/, int /*dy*/, LPCRECT /*pRectScroll*/, LPCRECT /*pRectClip*/) override
++ {
++ return S_OK;
++ }
++ STDMETHOD(AdjustRect)(LPRECT /*prc*/) override
++ {
++ return S_OK;
++ }
++ #ifdef Q_CC_GNU // signature incorrect in win32api
++ STDMETHOD(AdjustRect)(LPCRECT /*prc*/) override
++ {
++ RECT rect;
++ return AdjustRect(&rect);
++ }
++ #endif
++
++ STDMETHOD(OnDefWindowMessage)(UINT /*msg*/, WPARAM /*wPara*/, LPARAM /*lParam*/, LRESULT* /*plResult*/) override
++ {
++ return S_FALSE;
++ }
++ #endif
++
++ // IOleInPlaceFrame
++ STDMETHOD(InsertMenus(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths)) override;
++ STDMETHOD(SetMenu(HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject)) override;
++ STDMETHOD(RemoveMenus(HMENU hmenuShared)) override;
++ STDMETHOD(SetStatusText(LPCOLESTR pszStatusText)) override;
++ STDMETHOD(EnableModeless(BOOL fEnable)) override;
++ STDMETHOD(TranslateAccelerator(LPMSG lpMsg, WORD grfModifiers)) override;
++
++ // IOleInPlaceUIWindow
++ STDMETHOD(GetBorder(LPRECT lprectBorder)) override;
++ STDMETHOD(RequestBorderSpace(LPCBORDERWIDTHS pborderwidths)) override;
++ STDMETHOD(SetBorderSpace(LPCBORDERWIDTHS pborderwidths)) override;
++ STDMETHOD(SetActiveObject(IOleInPlaceActiveObject *pActiveObject, LPCOLESTR pszObjName)) override;
++
++ // IOleDocumentSite
++ STDMETHOD(ActivateMe(IOleDocumentView *pViewToActivate)) override;
++
++ // IAdviseSink
++ STDMETHOD_(void, OnDataChange)(FORMATETC* /*pFormatetc*/, STGMEDIUM* /*pStgmed*/) override
++ {
++ AX_DEBUG(QAxClientSite::OnDataChange);
++ }
++ STDMETHOD_(void, OnViewChange)(DWORD /*dwAspect*/, LONG /*lindex*/) override
++ {
++ AX_DEBUG(QAxClientSite::OnViewChange);
++ }
++ STDMETHOD_(void, OnRename)(IMoniker* /*pmk*/) override
++ {
++ }
++ STDMETHOD_(void, OnSave)() override
++ {
++ }
++ STDMETHOD_(void, OnClose)() override
++ {
++ }
++
++ QSize sizeHint() const { return sizehint; }
++ QSize minimumSizeHint() const;
++ inline void resize(QSize sz) { if (host) host->resize(sz); }
++
++ bool translateKeyEvent(int message, int keycode) const
++ {
++ if (!widget)
++ return false;
++ return widget->translateKeyEvent(message, keycode);
++ }
++
++ int qt_metacall(QMetaObject::Call, int isignal, void **argv);
++ void windowActivationChange();
++
++ bool eventTranslated : 1;
++
++ private:
++ struct OleMenuItem {
++ OleMenuItem(HMENU hm = nullptr, int ID = 0, QMenu *menu = nullptr)
++ : hMenu(hm), subMenu(menu), id(ID)
++ {}
++ HMENU hMenu;
++ QMenu *subMenu;
++ int id;
++ };
++ using MenuItemMap = QMap<QAction*, OleMenuItem>;
++
++ QMenu *generatePopup(HMENU subMenu, QWidget *parent);
++
++ ComPtr<IOleObject> m_spOleObject;
++ ComPtr<IOleControl> m_spOleControl;
++ ComPtr<IOleInPlaceObjectWindowless> m_spInPlaceObject;
++ ComPtr<IOleInPlaceActiveObject> m_spInPlaceActiveObject;
++ ComPtr<IOleDocumentView> m_spActiveView;
++
++ QAxAggregated *aggregatedObject = nullptr;
++
++ bool inPlaceObjectWindowless :1;
++ bool inPlaceModelessEnabled :1;
++ bool canHostDocument : 1;
++
++ DWORD m_dwOleObject = 0;
++ HWND m_menuOwner = nullptr;
++ CONTROLINFO control_info;
++
++ QSize sizehint;
++ QAxWidget *widget;
++ QAxHostWidget *host = nullptr;
++ QPointer<QMenuBar> menuBar;
++ MenuItemMap menuItemMap;
++ };
++
++ static const ushort mouseTbl[] = {
++ WM_MOUSEMOVE, QEvent::MouseMove, 0,
++ WM_LBUTTONDOWN, QEvent::MouseButtonPress, Qt::LeftButton,
++ WM_LBUTTONUP, QEvent::MouseButtonRelease, Qt::LeftButton,
++ WM_LBUTTONDBLCLK, QEvent::MouseButtonDblClick, Qt::LeftButton,
++ WM_RBUTTONDOWN, QEvent::MouseButtonPress, Qt::RightButton,
++ WM_RBUTTONUP, QEvent::MouseButtonRelease, Qt::RightButton,
++ WM_RBUTTONDBLCLK, QEvent::MouseButtonDblClick, Qt::RightButton,
++ WM_MBUTTONDOWN, QEvent::MouseButtonPress, Qt::MiddleButton,
++ WM_MBUTTONUP, QEvent::MouseButtonRelease, Qt::MiddleButton,
++ WM_MBUTTONDBLCLK, QEvent::MouseButtonDblClick, Qt::MiddleButton,
++ 0, 0, 0
++ };
++
++ static Qt::MouseButtons translateMouseButtonState(int s)
++ {
++ Qt::MouseButtons bst;
++ if (s & MK_LBUTTON)
++ bst |= Qt::LeftButton;
++ if (s & MK_MBUTTON)
++ bst |= Qt::MiddleButton;
++ if (s & MK_RBUTTON)
++ bst |= Qt::RightButton;
++
++ return bst;
++ }
++
++ static Qt::KeyboardModifiers translateModifierState(int s)
++ {
++ Qt::KeyboardModifiers bst;
++ if (s & MK_SHIFT)
++ bst |= Qt::ShiftModifier;
++ if (s & MK_CONTROL)
++ bst |= Qt::ControlModifier;
++ if (GetKeyState(VK_MENU) < 0)
++ bst |= Qt::AltModifier;
++
++ return bst;
++ }
++
++ static const wchar_t *qaxatom = L"QAxContainer4_Atom";
++
++ // The filter procedure listening to user interaction on the control
++ class QAxNativeEventFilter : public QAbstractNativeEventFilter
++ {
++ public:
++ bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *) override;
++ };
++ Q_GLOBAL_STATIC(QAxNativeEventFilter, s_nativeEventFilter)
++
++ bool QAxNativeEventFilter::nativeEventFilter(const QByteArray &, void *m, qintptr *)
++ {
++ MSG *msg = static_cast<MSG *>(m);
++ const uint message = msg->message;
++ if (message == WM_DISPLAYCHANGE)
++ qaxClearCachedSystemLogicalDpi();
++ if ((message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) || (message >= WM_KEYFIRST && message <= WM_KEYLAST)) {
++ HWND hwnd = msg->hwnd;
++ QAxWidget *ax = nullptr;
++ QAxHostWidget *host = nullptr;
++ while (!host && hwnd) {
++ // FIXME: 4.10.2011: Does this still work?
++ QWidget *widget = QWidget::find(reinterpret_cast<WId>(hwnd));
++ if (widget && widget->inherits("QAxHostWidget"))
++ host = qobject_cast<QAxHostWidget*>(widget);
++ hwnd = ::GetParent(hwnd);
++ }
++ if (host)
++ ax = qobject_cast<QAxWidget*>(host->parentWidget());
++ if (ax && msg->hwnd != reinterpret_cast<HWND>(host->winId())) {
++ if (message >= WM_KEYFIRST && message <= WM_KEYLAST) {
++ QAxClientSite *site = host->clientSite();
++ site->eventTranslated = true; // reset in QAxClientSite::TranslateAccelerator
++ HRESULT hres = S_FALSE;
++ if (site && site->inPlaceObject() && site->translateKeyEvent(msg->message, msg->wParam))
++ hres = site->inPlaceObject()->TranslateAccelerator(msg);
++ // if the object calls our TranslateAccelerator implementation, then continue with normal event processing
++ // otherwise the object has translated the accelerator, and the event should be stopped
++ if (site->eventTranslated && hres == S_OK)
++ return true;
++ } else {
++ int i;
++ for (i = 0; UINT(mouseTbl[i]) != message && mouseTbl[i]; i += 3)
++ ;
++
++ if (mouseTbl[i]) {
++ const QEvent::Type type = static_cast<QEvent::Type>(mouseTbl[++i]);
++ int button = mouseTbl[++i];
++ if (type != QEvent::MouseMove || ax->hasMouseTracking() || button) {
++ if (type == QEvent::MouseMove)
++ button = 0;
++
++ DWORD ol_pos = GetMessagePos();
++ const QPoint nativeGlobalPos(GET_X_LPARAM(ol_pos), GET_Y_LPARAM(ol_pos));
++ const QPoint gpos = qaxFromNativePosition(ax, nativeGlobalPos);
++ QPoint pos = ax->mapFromGlobal(gpos);
++
++ QMouseEvent e(type, pos, gpos, static_cast<Qt::MouseButton>(button),
++ translateMouseButtonState(int(msg->wParam)),
++ translateModifierState(int(msg->wParam)));
++ QCoreApplication::sendEvent(ax, &e);
++ }
++ }
++ }
++ }
++ }
++
++ return false;
++ }
++
++ QAxClientSite::QAxClientSite(QAxWidget *c)
++ : eventTranslated(true), widget(c)
++ {
++ aggregatedObject = widget->createAggregate();
++ if (aggregatedObject) {
++ aggregatedObject->controlling_unknown = static_cast<IUnknown *>(static_cast<IDispatch *>(this));
++ aggregatedObject->the_object = c;
++ }
++
++ inPlaceObjectWindowless = false;
++ inPlaceModelessEnabled = true;
++ canHostDocument = false;
++
++ memset(&control_info, 0, sizeof(control_info));
++ }
++
++ bool QAxClientSite::activateObject(bool initialized, const QByteArray &data)
++ {
++ if (!host)
++ host = new QAxHostWidget(widget, this);
++
++ bool showHost = false;
++ if (!m_spOleObject)
++ widget->queryInterface(IID_IOleObject, &m_spOleObject);
++ if (m_spOleObject) {
++ DWORD dwMiscStatus = 0;
++ m_spOleObject->GetMiscStatus(DVASPECT_CONTENT, &dwMiscStatus);
++
++ ComPtr<IOleDocument> document;
++ m_spOleObject->QueryInterface(IID_IOleDocument, &document);
++ if (document) {
++ ComPtr<IPersistStorage> persistStorage;
++ document->QueryInterface(IID_IPersistStorage, &persistStorage);
++ if (persistStorage) {
++ // try to activate as document server
++ ComPtr<IStorage> storage;
++ ComPtr<ILockBytes> bytes;
++ ::CreateILockBytesOnHGlobal(nullptr, TRUE, &bytes);
++ ::StgCreateDocfileOnILockBytes(bytes.Get(), STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, &storage);
++
++ persistStorage->InitNew(storage.Get());
++ canHostDocument = true;
++
++ m_spOleObject->SetClientSite(this);
++ OleRun(m_spOleObject.Get());
++ }
++ }
++
++ if (!canHostDocument) {
++ // activate as control
++ if(dwMiscStatus & OLEMISC_SETCLIENTSITEFIRST)
++ m_spOleObject->SetClientSite(this);
++
++ if (!initialized) {
++ ComPtr<IPersistStreamInit> spPSI;
++ m_spOleObject->QueryInterface(IID_IPersistStreamInit, &spPSI);
++ if (spPSI) {
++ if (data.length()) {
++ ComPtr<IStream> pStream;
++ HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, data.length());
++ if (hGlobal) {
++ if (auto pStByte = GlobalLock(hGlobal))
++ memcpy(pStByte, data.data(), data.length());
++ GlobalUnlock(hGlobal);
++ if (SUCCEEDED(CreateStreamOnHGlobal(hGlobal, TRUE, &pStream))) {
++ spPSI->Load(pStream.Get());
++ }
++ GlobalFree(hGlobal);
++ }
++ } else {
++ spPSI->InitNew();
++ }
++ } else if (data.length()) { //try initializing using a IPersistStorage
++ ComPtr<IPersistStorage> spPS;
++ m_spOleObject->QueryInterface( IID_IPersistStorage, &spPS);
++ if (spPS) {
++ HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, size_t(data.length()));
++ if (hGlobal) {
++ if (BYTE* pbData = static_cast<BYTE *>(GlobalLock(hGlobal)))
++ memcpy(pbData, data.data(), size_t(data.length()));
++ GlobalUnlock(hGlobal);
++ // open an IStorage on the data and pass it to Load
++ ComPtr<ILockBytes> pLockBytes;
++ if (SUCCEEDED(CreateILockBytesOnHGlobal(hGlobal, TRUE, &pLockBytes))) {
++ ComPtr<IStorage> pStorage;
++ if (SUCCEEDED(StgOpenStorageOnILockBytes(pLockBytes.Get(), nullptr,
++ STGM_READWRITE | STGM_SHARE_EXCLUSIVE, nullptr, 0, &pStorage))) {
++ spPS->Load(pStorage.Get());
++ }
++ }
++ GlobalFree(hGlobal);
++ }
++ }
++ }
++ }
++
++ if(!(dwMiscStatus & OLEMISC_SETCLIENTSITEFIRST))
++ m_spOleObject->SetClientSite(this);
++ }
++
++ ComPtr<IViewObject> spViewObject;
++ m_spOleObject->QueryInterface(IID_IViewObject, &spViewObject);
++
++ m_spOleObject->Advise(this, &m_dwOleObject);
++ ComPtr<IAdviseSink> spAdviseSink;
++ QueryInterface(IID_IAdviseSink, &spAdviseSink);
++ if (spAdviseSink && spViewObject) {
++ if (spViewObject)
++ spViewObject->SetAdvise(DVASPECT_CONTENT, 0, spAdviseSink.Get());
++ }
++
++ m_spOleObject->SetHostNames(OLESTR("AXWIN"), nullptr);
++
++ if (!(dwMiscStatus & OLEMISC_INVISIBLEATRUNTIME)) {
++ SIZEL hmSize = qaxMapPixToLogHiMetrics(QSize(250, 250), widget);
++
++ m_spOleObject->SetExtent(DVASPECT_CONTENT, &hmSize);
++ m_spOleObject->GetExtent(DVASPECT_CONTENT, &hmSize);
++
++ sizehint = qaxMapLogHiMetricsToPix(hmSize, widget);
++ showHost = true;
++ } else {
++ sizehint = QSize(0, 0);
++ host->hide();
++ }
++ if (!(dwMiscStatus & OLEMISC_NOUIACTIVATE)) {
++ host->setFocusPolicy(Qt::StrongFocus);
++ } else {
++ host->setFocusPolicy(Qt::NoFocus);
++ }
++
++ RECT rcPos = qaxQRect2Rect(QRect(qaxNativeWidgetPosition(host), qaxToNativeSize(host, sizehint)));
++
++ const HWND hostWnd = reinterpret_cast<HWND>(host->winId());
++ m_spOleObject->DoVerb(OLEIVERB_INPLACEACTIVATE, nullptr, static_cast<IOleClientSite *>(this), 0,
++ hostWnd,
++ &rcPos);
++
++ HWND controlWnd = {};
++ {
++ ComPtr<IOleWindow> oleWindow;
++ m_spOleObject->QueryInterface(IID_IOleWindow, &oleWindow);
++ if (oleWindow) {
++ oleWindow->GetWindow(&controlWnd);
++ }
++ }
++
++ if (controlWnd && !GetParent(controlWnd)) {
++ // re-parent control window
++ // this is necessary if the control is running in a "low integrity" process that isn't
++ // permitted to create a child window with hostWnd as parent
++ int winStyle = GetWindowLongPtr(controlWnd, GWL_STYLE);
++ winStyle &= ~WS_CAPTION; // remove title bar
++ winStyle |= WS_CHILD; // convert to child window
++ SetWindowLongPtr(controlWnd, GWL_STYLE, winStyle);
++ SetParent(controlWnd, hostWnd);
++ }
++
++ if (!m_spOleControl)
++ m_spOleObject->QueryInterface(IID_IOleControl, &m_spOleControl);
++ if (m_spOleControl) {
++ m_spOleControl->OnAmbientPropertyChange(DISPID_AMBIENT_BACKCOLOR);
++ m_spOleControl->OnAmbientPropertyChange(DISPID_AMBIENT_FORECOLOR);
++ m_spOleControl->OnAmbientPropertyChange(DISPID_AMBIENT_FONT);
++ m_spOleControl->OnAmbientPropertyChange(DISPID_AMBIENT_USERMODE);
++
++ control_info.cb = sizeof(control_info);
++ m_spOleControl->GetControlInfo(&control_info);
++ }
++
++ BSTR userType;
++ HRESULT result = m_spOleObject->GetUserType(USERCLASSTYPE_SHORT, &userType);
++ if (result == S_OK) {
++ widget->setWindowTitle(QString::fromWCharArray(userType));
++ CoTaskMemFree(userType);
++ }
++ } else {
++ ComPtr<IObjectWithSite> spSite;
++ widget->queryInterface(IID_IObjectWithSite, &spSite);
++ if (spSite) {
++ spSite->SetSite(static_cast<IUnknown *>(static_cast<IDispatch *>(this)));
++ }
++ }
++
++ host->resize(widget->size());
++ if (showHost)
++ host->show();
++
++ if (host->focusPolicy() != Qt::NoFocus) {
++ widget->setFocusProxy(host);
++ widget->setFocusPolicy(host->focusPolicy());
++ }
++
++ return true;
++ }
++
++ QAxClientSite::~QAxClientSite()
++ {
++ if (host) {
++ host->axhost = nullptr;
++ }
++
++ if (aggregatedObject)
++ aggregatedObject->the_object = nullptr;
++ delete aggregatedObject;
++ delete host;
++ }
++
++ void QAxClientSite::releaseAll()
++ {
++ m_spOleControl.Reset();
++ if (m_spOleObject) {
++ m_spOleObject->Unadvise(m_dwOleObject);
++ m_spOleObject->SetClientSite(nullptr);
++ }
++ m_spOleObject.Reset();
++ m_spInPlaceObject.Reset();
++ m_spInPlaceActiveObject.Reset();
++
++ inPlaceObjectWindowless = false;
++ }
++
++ void QAxClientSite::deactivate()
++ {
++ if (!m_spInPlaceObject)
++ return;
++
++ // InPlaceDeactivate should trigger an OnInPlaceDeactivate callback
++ HRESULT hr = m_spInPlaceObject->InPlaceDeactivate();
++
++ // call fails if an out-of-process control crashes
++ if (FAILED(hr)) {
++ // Call OnInPlaceDeactivate directly to clean up
++ OnInPlaceDeactivate();
++ // schedule release of QAxClientSite references that were held by the control
++ CoDisconnectObject(static_cast<IUnknown *>(static_cast<IDispatch *>(this)), 0);
++ }
++
++ Q_ASSERT(m_spInPlaceObject == nullptr);
++ }
++
++ //**** IUnknown
++ STDMETHODIMP QAxClientSite::QueryInterface(REFIID riid, void **ppvObject)
++ {
++ if (!ppvObject)
++ return E_POINTER;
++
++ *ppvObject = nullptr;
++
++ if (aggregatedObject) {
++ const HRESULT result = aggregatedObject->queryInterface(riid, ppvObject);
++ if (*ppvObject)
++ return result;
++ }
++
++ if (riid == IID_IOleDocumentSite && !canHostDocument)
++ return E_NOINTERFACE;
++
++ return QComObject::QueryInterface(riid, ppvObject);
++ }
++
++ bool qax_runsInDesignMode = false;
++
++ //**** IDispatch
++ HRESULT WINAPI QAxClientSite::Invoke(DISPID dispIdMember,
++ REFIID /*riid*/,
++ LCID /*lcid*/,
++ WORD /*wFlags*/,
++ DISPPARAMS * /*pDispParams*/,
++ VARIANT *pVarResult,
++ EXCEPINFO * /*pExcepInfo*/,
++ UINT * /*puArgErr*/)
++ {
++ if (!pVarResult)
++ return E_POINTER;
++ if (!widget || !host)
++ return E_UNEXPECTED;
++
++ switch(dispIdMember) {
++ case DISPID_AMBIENT_USERMODE:
++ pVarResult->vt = VT_BOOL;
++ pVarResult->boolVal = !qax_runsInDesignMode;
++ return S_OK;
++
++ case DISPID_AMBIENT_AUTOCLIP:
++ case DISPID_AMBIENT_SUPPORTSMNEMONICS:
++ pVarResult->vt = VT_BOOL;
++ pVarResult->boolVal = true;
++ return S_OK;
++
++ case DISPID_AMBIENT_SHOWHATCHING:
++ case DISPID_AMBIENT_SHOWGRABHANDLES:
++ case DISPID_AMBIENT_DISPLAYASDEFAULT:
++ case DISPID_AMBIENT_MESSAGEREFLECT:
++ pVarResult->vt = VT_BOOL;
++ pVarResult->boolVal = false;
++ return S_OK;
++
++ case DISPID_AMBIENT_DISPLAYNAME:
++ pVarResult->vt = VT_BSTR;
++ pVarResult->bstrVal = QStringToBSTR(widget->windowTitle());
++ return S_OK;
++
++ case DISPID_AMBIENT_FONT:
++ QVariantToVARIANT(widget->font(), *pVarResult);
++ return S_OK;
++
++ case DISPID_AMBIENT_BACKCOLOR:
++ pVarResult->vt = VT_UI4;
++ pVarResult->lVal = QColorToOLEColor(widget->palette().color(widget->backgroundRole()));
++ return S_OK;
++
++ case DISPID_AMBIENT_FORECOLOR:
++ pVarResult->vt = VT_UI4;
++ pVarResult->lVal = QColorToOLEColor(widget->palette().color(widget->foregroundRole()));
++ return S_OK;
++
++ case DISPID_AMBIENT_UIDEAD:
++ pVarResult->vt = VT_BOOL;
++ pVarResult->boolVal = !widget->isEnabled();
++ return S_OK;
++
++ default:
++ break;
++ }
++
++ return DISP_E_MEMBERNOTFOUND;
++ }
++
++ void QAxClientSite::emitAmbientPropertyChange(DISPID dispid)
++ {
++ if (m_spOleControl)
++ m_spOleControl->OnAmbientPropertyChange(dispid);
++ }
++
++ //**** IOleClientSite
++ HRESULT WINAPI QAxClientSite::SaveObject()
++ {
++ return E_NOTIMPL;
++ }
++
++ HRESULT WINAPI QAxClientSite::GetMoniker(DWORD, DWORD, IMoniker **ppmk)
++ {
++ if (!ppmk)
++ return E_POINTER;
++
++ *ppmk = nullptr;
++ return E_NOTIMPL;
++ }
++
++ HRESULT WINAPI QAxClientSite::GetContainer(LPOLECONTAINER *ppContainer)
++ {
++ if (!ppContainer)
++ return E_POINTER;
++
++ *ppContainer = nullptr;
++ return E_NOINTERFACE;
++ }
++
++ HRESULT WINAPI QAxClientSite::ShowObject()
++ {
++ return S_OK;
++ }
++
++ HRESULT WINAPI QAxClientSite::OnShowWindow(BOOL /*fShow*/)
++ {
++ return S_OK;
++ }
++
++ HRESULT WINAPI QAxClientSite::RequestNewObjectLayout()
++ {
++ return E_NOTIMPL;
++ }
++
++ //**** IOleControlSite
++ HRESULT WINAPI QAxClientSite::OnControlInfoChanged()
++ {
++ if (m_spOleControl)
++ m_spOleControl->GetControlInfo(&control_info);
++
++ return S_OK;
++ }
++
++ HRESULT WINAPI QAxClientSite::LockInPlaceActive(BOOL /*fLock*/)
++ {
++ AX_DEBUG(QAxClientSite::LockInPlaceActive);
++ return S_OK;
++ }
++
++ HRESULT WINAPI QAxClientSite::GetExtendedControl(IDispatch** ppDisp)
++ {
++ if (!ppDisp)
++ return E_POINTER;
++
++ *ppDisp = nullptr;
++ return E_NOTIMPL;
++ }
++
++ HRESULT WINAPI QAxClientSite::TransformCoords(POINTL* /*pPtlHimetric*/, POINTF* /*pPtfContainer*/, DWORD /*dwFlags*/)
++ {
++ return S_OK;
++ }
++
++ HRESULT WINAPI QAxClientSite::TranslateAccelerator(LPMSG lpMsg, DWORD /*grfModifiers*/)
++ {
++ if (lpMsg->message == WM_KEYDOWN && !lpMsg->wParam)
++ return S_OK;
++
++ bool ActiveQtDetected = false;
++ bool fromInProcServer = false;
++ #ifdef GWLP_USERDATA
++ LONG_PTR serverType = GetWindowLongPtr(lpMsg->hwnd, GWLP_USERDATA);
++ #else
++ LONG serverType = GetWindowLong(lpMsg->hwnd, GWL_USERDATA);
++ #endif
++ if (serverType == QAX_INPROC_SERVER) {
++ ActiveQtDetected = true;
++ fromInProcServer = true;
++ } else if (serverType == QAX_OUTPROC_SERVER) {
++ ActiveQtDetected = true;
++ fromInProcServer = false;
++ }
++
++ eventTranslated = false;
++ if (!ActiveQtDetected || !fromInProcServer) {
++ // if the request is coming from an out-of-proc server or a non ActiveQt server,
++ // we send the message to the host window. This will make sure this key event
++ // comes to Qt for processing.
++ SendMessage(reinterpret_cast<HWND>(host->winId()), lpMsg->message, lpMsg->wParam, lpMsg->lParam);
++ if (ActiveQtDetected && !fromInProcServer) {
++ // ActiveQt based servers will need further processing of the event
++ // (eg. <SPACE> key for a checkbox), so we return false.
++ return S_FALSE;
++ }
++ }
++ // ActiveQt based in-processes-servers will handle the event properly, so
++ // we don't need to send this key event to the host.
++ return S_OK;
++ }
++
++ HRESULT WINAPI QAxClientSite::OnFocus(BOOL bGotFocus)
++ {
++ AX_DEBUG(QAxClientSite::OnFocus);
++ if (host) {
++ host->hasFocus = bGotFocus;
++ qApp->removeEventFilter(host);
++ if (bGotFocus)
++ qApp->installEventFilter(host);
++ }
++ return S_OK;
++ }
++
++ HRESULT WINAPI QAxClientSite::ShowPropertyFrame()
++ {
++ return E_NOTIMPL;
++ }
++
++ //**** IOleWindow
++ HRESULT WINAPI QAxClientSite::GetWindow(HWND *phwnd)
++ {
++ if (!phwnd)
++ return E_POINTER;
++
++ *phwnd = reinterpret_cast<HWND>(host->winId());
++ return S_OK;
++ }
++
++ HRESULT WINAPI QAxClientSite::ContextSensitiveHelp(BOOL fEnterMode)
++ {
++ if (fEnterMode)
++ QWhatsThis::enterWhatsThisMode();
++ else
++ QWhatsThis::leaveWhatsThisMode();
++
++ return S_OK;
++ }
++
++ //**** IOleInPlaceSite
++ HRESULT WINAPI QAxClientSite::CanInPlaceActivate()
++ {
++ AX_DEBUG(QAxClientSite::CanInPlaceActivate);
++ return S_OK;
++ }
++
++ HRESULT WINAPI QAxClientSite::OnInPlaceActivate()
++ {
++ AX_DEBUG(QAxClientSite::OnInPlaceActivate);
++ OleLockRunning(m_spOleObject.Get(), true, false);
++ if (!m_spInPlaceObject) {
++ /* ### disabled for now
++ m_spOleObject->QueryInterface(IID_IOleInPlaceObjectWindowless, &m_spInPlaceObject);
++ */
++ if (m_spInPlaceObject) {
++ inPlaceObjectWindowless = true;
++ } else {
++ inPlaceObjectWindowless = false;
++ m_spOleObject->QueryInterface(IID_IOleInPlaceObject, &m_spInPlaceObject);
++ }
++ }
++
++ return S_OK;
++ }
++
++ HRESULT WINAPI QAxClientSite::OnUIActivate()
++ {
++ AX_DEBUG(QAxClientSite::OnUIActivate);
++ return S_OK;
++ }
++
++ HRESULT WINAPI QAxClientSite::GetWindowContext(IOleInPlaceFrame **ppFrame, IOleInPlaceUIWindow **ppDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo)
++ {
++ if (!ppFrame || !ppDoc || !lprcPosRect || !lprcClipRect || !lpFrameInfo)
++ return E_POINTER;
++
++ QueryInterface(IID_IOleInPlaceFrame, reinterpret_cast<void **>(ppFrame));
++ QueryInterface(IID_IOleInPlaceUIWindow, reinterpret_cast<void **>(ppDoc));
++
++ const HWND hwnd = reinterpret_cast<HWND>(host->winId());
++ ::GetClientRect(hwnd, lprcPosRect);
++ ::GetClientRect(hwnd, lprcClipRect);
++
++ lpFrameInfo->cb = sizeof(OLEINPLACEFRAMEINFO);
++ lpFrameInfo->fMDIApp = false;
++ lpFrameInfo->haccel = nullptr;
++ lpFrameInfo->cAccelEntries = 0;
++ // FIXME: 4.10.2011: Parent's HWND required, should work.
++ lpFrameInfo->hwndFrame = widget ? hwnd : nullptr;
++
++ return S_OK;
++ }
++
++ HRESULT WINAPI QAxClientSite::Scroll(SIZE /*scrollExtant*/)
++ {
++ return S_FALSE;
++ }
++
++ HRESULT WINAPI QAxClientSite::OnUIDeactivate(BOOL)
++ {
++ AX_DEBUG(QAxClientSite::OnUIDeactivate);
++ if (host && host->hasFocus) {
++ qApp->removeEventFilter(host);
++ host->hasFocus = false;
++ }
++ return S_OK;
++ }
++
++ HRESULT WINAPI QAxClientSite::OnInPlaceDeactivate()
++ {
++ AX_DEBUG(QAxClientSite::OnInPlaceDeactivate);
++ m_spInPlaceObject.Reset();
++ inPlaceObjectWindowless = false;
++ OleLockRunning(m_spOleObject.Get(), false, false);
++
++ return S_OK;
++ }
++
++ HRESULT WINAPI QAxClientSite::DiscardUndoState()
++ {
++ return S_OK;
++ }
++
++ HRESULT WINAPI QAxClientSite::DeactivateAndUndo()
++ {
++ if (m_spInPlaceObject)
++ m_spInPlaceObject->UIDeactivate();
++
++ return S_OK;
++ }
++
++ HRESULT WINAPI QAxClientSite::OnPosRectChange(LPCRECT /*lprcPosRect*/)
++ {
++ AX_DEBUG(QAxClientSite::OnPosRectChange);
++ // ###
++ return S_OK;
++ }
++
++ //**** IOleInPlaceFrame
++ HRESULT WINAPI QAxClientSite::InsertMenus(HMENU /*hmenuShared*/, LPOLEMENUGROUPWIDTHS lpMenuWidths)
++ {
++ AX_DEBUG(QAxClientSite::InsertMenus);
++ QMenuBar *mb = menuBar;
++ if (!mb)
++ mb = widget->window()->findChild<QMenuBar*>();
++ if (!mb)
++ return E_NOTIMPL;
++ menuBar = mb;
++
++ QMenu *fileMenu = nullptr;
++ QMenu *viewMenu = nullptr;
++ QMenu *windowMenu = nullptr;
++ const auto actions = menuBar->actions();
++ for (QAction *action : actions) {
++ QString text = action->text().remove(QLatin1Char('&'));
++ if (text == QLatin1String("File")) {
++ fileMenu = action->menu();
++ } else if (text == QLatin1String("View")) {
++ viewMenu = action->menu();
++ } else if (text == QLatin1String("Window")) {
++ windowMenu = action->menu();
++ }
++ }
++ if (fileMenu)
++ lpMenuWidths->width[0] = fileMenu->actions().size();
++ if (viewMenu)
++ lpMenuWidths->width[2] = viewMenu->actions().size();
++ if (windowMenu)
++ lpMenuWidths->width[4] = windowMenu->actions().size();
++
++ return S_OK;
++ }
++
++ static int menuItemEntry(HMENU menu, int index, MENUITEMINFO item, QString &text, QPixmap &/*icon*/)
++ {
++ if (item.fType == MFT_STRING && item.cch) {
++ wchar_t *titlebuf = new wchar_t[item.cch + 1];
++ item.dwTypeData = titlebuf;
++ item.cch++;
++ ::GetMenuItemInfo(menu, UINT(index), true, &item);
++ text = QString::fromWCharArray(titlebuf);
++ delete [] titlebuf;
++ return MFT_STRING;
++ }
++ #if 0
++ else if (item.fType == MFT_BITMAP) {
++ HBITMAP hbm = (HBITMAP)LOWORD(item.hbmpItem);
++ SIZE bmsize;
++ GetBitmapDimensionEx(hbm, &bmsize);
++ QPixmap pixmap(1,1);
++ QSize sz(MAP_LOGHIM_TO_PIX(bmsize.cx, pixmap.logicalDpiX()),
++ MAP_LOGHIM_TO_PIX(bmsize.cy, pixmap.logicalDpiY()));
++
++ pixmap.resize(bmsize.cx, bmsize.cy);
++ if (!pixmap.isNull()) {
++ HDC hdc = ::CreateCompatibleDC(pixmap.handle());
++ ::SelectObject(hdc, hbm);
++ BOOL res = ::BitBlt(pixmap.handle(), 0, 0, pixmap.width(), pixmap.height(), hdc, 0, 0, SRCCOPY);
++ ::DeleteObject(hdc);
++ }
++
++ icon = pixmap;
++ }
++ #endif
++ return -1;
++ }
++
++ QMenu *QAxClientSite::generatePopup(HMENU subMenu, QWidget *parent)
++ {
++ QMenu *popup = nullptr;
++ int count = GetMenuItemCount(subMenu);
++ if (count)
++ popup = new QMenu(parent);
++ for (int i = 0; i < count; ++i) {
++ MENUITEMINFO item;
++ memset(&item, 0, sizeof(MENUITEMINFO));
++ item.cbSize = sizeof(MENUITEMINFO);
++ item.fMask = MIIM_ID | MIIM_TYPE | MIIM_SUBMENU;
++ ::GetMenuItemInfo(subMenu, UINT(i), true, &item);
++
++ QAction *action = nullptr;
++ QMenu *popupMenu = nullptr;
++ if (item.fType == MFT_SEPARATOR) {
++ action = popup->addSeparator();
++ } else {
++ QString text;
++ QPixmap icon;
++ QKeySequence accel;
++ popupMenu = item.hSubMenu ? generatePopup(item.hSubMenu, popup) : nullptr;
++ int res = menuItemEntry(subMenu, i, item, text, icon);
++
++ int lastSep = text.lastIndexOf(QRegularExpression(QLatin1String("[\\s]")));
++ if (lastSep != -1) {
++ QString keyString = text.right(text.length() - lastSep);
++ accel = keyString;
++ if (!accel.isEmpty())
++ text.truncate(lastSep);
++ }
++
++ if (popupMenu)
++ popupMenu->setTitle(text);
++
++ switch (res) {
++ case MFT_STRING:
++ if (popupMenu)
++ action = popup->addMenu(popupMenu);
++ else
++ action = popup->addAction(text);
++ break;
++ case MFT_BITMAP:
++ if (popupMenu)
++ action = popup->addMenu(popupMenu);
++ else
++ action = popup->addAction(icon, text);
++ break;
++ }
++
++ if (action) {
++ if (!accel.isEmpty())
++ action->setShortcut(accel);
++ if (!icon.isNull())
++ action->setIcon(icon);
++ }
++ }
++
++ if (action) {
++ OleMenuItem oleItem(subMenu, int(item.wID), popupMenu);
++ menuItemMap.insert(action, oleItem);
++ }
++ }
++ return popup;
++ }
++
++ HRESULT WINAPI QAxClientSite::SetMenu(HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject)
++ {
++ AX_DEBUG(QAxClientSite::SetMenu);
++
++ if (hmenuShared) {
++ m_menuOwner = hwndActiveObject;
++ QMenuBar *mb = menuBar;
++ if (!mb)
++ mb = widget->window()->findChild<QMenuBar*>();
++ if (!mb)
++ return E_NOTIMPL;
++ menuBar = mb;
++
++ int count = GetMenuItemCount(hmenuShared);
++ for (int i = 0; i < count; ++i) {
++ MENUITEMINFO item;
++ memset(&item, 0, sizeof(MENUITEMINFO));
++ item.cbSize = sizeof(MENUITEMINFO);
++ item.fMask = MIIM_ID | MIIM_TYPE | MIIM_SUBMENU;
++ ::GetMenuItemInfo(hmenuShared, UINT(i), true, &item);
++
++ QAction *action = nullptr;
++ QMenu *popupMenu = nullptr;
++ if (item.fType == MFT_SEPARATOR) {
++ action = menuBar->addSeparator();
++ } else {
++ QString text;
++ QPixmap icon;
++ popupMenu = item.hSubMenu ? generatePopup(item.hSubMenu, menuBar) : nullptr;
++ int res = menuItemEntry(hmenuShared, i, item, text, icon);
++
++ if (popupMenu)
++ popupMenu->setTitle(text);
++
++ switch(res) {
++ case MFT_STRING:
++ if (popupMenu)
++ action = menuBar->addMenu(popupMenu);
++ else
++ action = menuBar->addAction(text);
++ break;
++ case MFT_BITMAP:
++ if (popupMenu)
++ action = menuBar->addMenu(popupMenu);
++ else
++ action = menuBar->addAction(text);
++ break;
++ default:
++ break;
++ }
++ if (action && !icon.isNull())
++ action->setIcon(icon);
++ }
++
++ if (action) {
++ OleMenuItem oleItem(hmenuShared, int(item.wID), popupMenu);
++ menuItemMap.insert(action, oleItem);
++ }
++ }
++ if (count) {
++ const QMetaObject *mbmo = menuBar->metaObject();
++ int index = mbmo->indexOfSignal("triggered(QAction*)");
++ Q_ASSERT(index != -1);
++ menuBar->disconnect(SIGNAL(triggered(QAction*)), host);
++ QMetaObject::connect(menuBar, index, host, index);
++ }
++ } else if (menuBar) {
++ m_menuOwner = nullptr;
++ for (auto it = menuItemMap.begin(), mend = menuItemMap.end(); it != mend; ++it)
++ delete it.key();
++ menuItemMap.clear();
++ }
++
++ OleSetMenuDescriptor(holemenu, widget ? hwndForWidget(widget) : nullptr, m_menuOwner, this, m_spInPlaceActiveObject.Get());
++ return S_OK;
++ }
++
++ int QAxClientSite::qt_metacall(QMetaObject::Call call, int isignal, void **argv)
++ {
++ if (!m_spOleObject || call != QMetaObject::InvokeMetaMethod || !menuBar)
++ return isignal;
++
++ if (isignal != menuBar->metaObject()->indexOfSignal("triggered(QAction*)"))
++ return isignal;
++
++ QAction *action = *reinterpret_cast<QAction **>(argv[1]);
++ // ###
++
++ OleMenuItem oleItem = menuItemMap.value(action);
++ if (oleItem.hMenu)
++ ::PostMessage(m_menuOwner, WM_COMMAND, WPARAM(oleItem.id), 0);
++ return -1;
++ }
++
++
++ HRESULT WINAPI QAxClientSite::RemoveMenus(HMENU /*hmenuShared*/)
++ {
++ AX_DEBUG(QAxClientSite::RemoveMenus);
++ for (auto it = menuItemMap.begin(), mend = menuItemMap.end(); it != mend; ++it) {
++ it.key()->setVisible(false);
++ delete it.key();
++ }
++ menuItemMap.clear();
++ return S_OK;
++ }
++
++ HRESULT WINAPI QAxClientSite::SetStatusText(LPCOLESTR pszStatusText)
++ {
++ QStatusTipEvent tip(QString::fromWCharArray(pszStatusText));
++ QCoreApplication::sendEvent(widget, &tip);
++ return S_OK;
++ }
++
++ extern Q_GUI_EXPORT bool qt_win_ignoreNextMouseReleaseEvent;
++
++ HRESULT WINAPI QAxClientSite::EnableModeless(BOOL fEnable)
++ {
++ EnableWindow(hwndForWidget(host), fEnable);
++
++ QWindow *hostWindow = host->hostWindow();
++ if (!hostWindow)
++ return S_FALSE;
++
++ if (!fEnable) {
++ if (!QApplicationPrivate::isBlockedByModal(host))
++ QGuiApplicationPrivate::showModalWindow(hostWindow);
++ } else {
++ if (QApplicationPrivate::isBlockedByModal(host))
++ QGuiApplicationPrivate::hideModalWindow(hostWindow);
++ }
++ // FIXME 4.10.2011: No longer exists in Lighthouse.
++ // qt_win_ignoreNextMouseReleaseEvent = false;
++ return S_OK;
++ }
++
++ HRESULT WINAPI QAxClientSite::TranslateAccelerator(LPMSG lpMsg, WORD grfModifiers)
++ {
++ return TranslateAccelerator(lpMsg, DWORD(grfModifiers));
++ }
++
++ //**** IOleInPlaceUIWindow
++ HRESULT WINAPI QAxClientSite::GetBorder(LPRECT lprectBorder)
++ {
++ #ifndef QAX_SUPPORT_BORDERSPACE
++ Q_UNUSED(lprectBorder);
++ return INPLACE_E_NOTOOLSPACE;
++ #else
++ AX_DEBUG(QAxClientSite::GetBorder);
++
++ QMainWindow *mw = qobject_cast<QMainWindow*>(widget->window());
++ if (!mw)
++ return INPLACE_E_NOTOOLSPACE;
++
++ RECT border = { 0,0, 300, 200 };
++ *lprectBorder = border;
++ return S_OK;
++ #endif
++ }
++
++ HRESULT WINAPI QAxClientSite::RequestBorderSpace(LPCBORDERWIDTHS /*pborderwidths*/)
++ {
++ #ifndef QAX_SUPPORT_BORDERSPACE
++ return INPLACE_E_NOTOOLSPACE;
++ #else
++ AX_DEBUG(QAxClientSite::RequestBorderSpace);
++
++ QMainWindow *mw = qobject_cast<QMainWindow*>(widget->window());
++ if (!mw)
++ return INPLACE_E_NOTOOLSPACE;
++
++ return S_OK;
++ #endif
++ }
++
++ HRESULT WINAPI QAxClientSite::SetBorderSpace(LPCBORDERWIDTHS pborderwidths)
++ {
++ #ifndef QAX_SUPPORT_BORDERSPACE
++ Q_UNUSED(pborderwidths);
++ return OLE_E_INVALIDRECT;
++ #else
++ AX_DEBUG(QAxClientSite::SetBorderSpace);
++
++ // object has no toolbars and wants container toolbars to remain
++ if (!pborderwidths)
++ return S_OK;
++
++ QMainWindow *mw = qobject_cast<QMainWindow*>(widget->window());
++ if (!mw)
++ return OLE_E_INVALIDRECT;
++
++ bool removeToolBars = !(pborderwidths->left || pborderwidths->top || pborderwidths->right || pborderwidths->bottom);
++
++ // object has toolbars, and wants container to remove toolbars
++ if (removeToolBars) {
++ if (mw) {
++ //### remove our toolbars
++ }
++ }
++
++ if (pborderwidths->left) {
++ QDockWidget *left = new QDockWidget(mw);
++ left->setFixedWidth(pborderwidths->left);
++ mw->addDockWidget(Qt::LeftDockWidgetArea, left);
++ left->show();
++ }
++ if (pborderwidths->top) {
++ QDockWidget *top = new QDockWidget(mw);
++ top->setFixedHeight(pborderwidths->top);
++ mw->addDockWidget(Qt::TopDockWidgetArea, top);
++ top->show();
++ }
++
++ return S_OK;
++ #endif
++ }
++
++ HRESULT WINAPI QAxClientSite::SetActiveObject(IOleInPlaceActiveObject *pActiveObject, LPCOLESTR pszObjName)
++ {
++ AX_DEBUG(QAxClientSite::SetActiveObject);
++
++ Q_UNUSED(pszObjName);
++ // we are ignoring the name of the object, as suggested by MSDN documentation
++ // for IOleInPlaceUIWindow::SetActiveObject().
++
++ if (m_spInPlaceActiveObject) {
++ if (!inPlaceModelessEnabled)
++ m_spInPlaceActiveObject->EnableModeless(true);
++ inPlaceModelessEnabled = true;
++ }
++
++ m_spInPlaceActiveObject = pActiveObject;
++
++ return S_OK;
++ }
++
++ //**** IOleDocumentSite
++ HRESULT WINAPI QAxClientSite::ActivateMe(IOleDocumentView *pViewToActivate)
++ {
++ AX_DEBUG(QAxClientSite::ActivateMe);
++
++ ComPtr<IOleDocumentView> createdView;
++
++ if (!pViewToActivate) {
++ ComPtr<IOleDocument> document;
++ m_spOleObject->QueryInterface(IID_IOleDocument, &document);
++ if (!document)
++ return E_FAIL;
++
++ document->CreateView(this, nullptr, 0, &createdView);
++ if (!pViewToActivate)
++ return E_OUTOFMEMORY;
++
++ pViewToActivate = createdView.Get();
++ } else {
++ pViewToActivate->SetInPlaceSite(this);
++ }
++
++ m_spActiveView = pViewToActivate;
++
++ m_spActiveView->UIActivate(TRUE);
++
++ RECT rect;
++ GetClientRect(HWND(widget->winId()), &rect);
++ m_spActiveView->SetRect(&rect);
++ m_spActiveView->Show(TRUE);
++
++ return S_OK;
++ }
++
++ QSize QAxClientSite::minimumSizeHint() const
++ {
++ if (!m_spOleObject)
++ return QSize();
++
++ SIZE sz = { 0, 0 };
++ m_spOleObject->SetExtent(DVASPECT_CONTENT, &sz);
++ return SUCCEEDED(m_spOleObject->GetExtent(DVASPECT_CONTENT, &sz))
++ ? qaxMapLogHiMetricsToPix(sz, widget) : QSize();
++ }
++
++ void QAxClientSite::windowActivationChange()
++ {
++ AX_DEBUG(QAxClientSite::windowActivationChange);
++
++ if (m_spInPlaceActiveObject && widget) {
++ QWidget *modal = QApplication::activeModalWidget();
++ if (modal && inPlaceModelessEnabled) {
++ m_spInPlaceActiveObject->EnableModeless(false);
++ inPlaceModelessEnabled = false;
++ } else if (!inPlaceModelessEnabled) {
++ m_spInPlaceActiveObject->EnableModeless(true);
++ inPlaceModelessEnabled = true;
++ }
++ m_spInPlaceActiveObject->OnFrameWindowActivate(widget->isActiveWindow());
++ }
++ }
++
++
++ //**** QWidget
++
++ QAxHostWidget::QAxHostWidget(QWidget *parent, QAxClientSite *ax)
++ : QWidget(parent), setFocusTimer(0), hasFocus(false), axhost(ax)
++ {
++ setAttribute(Qt::WA_OpaquePaintEvent);
++ setAttribute(Qt::WA_NoSystemBackground);
++ setAttribute(Qt::WA_OpaquePaintEvent);
++ setAttribute(Qt::WA_PaintOnScreen);
++
++ setObjectName(parent->objectName() + QLatin1String(" - QAxHostWidget"));
++ }
++
++ QAxHostWidget::~QAxHostWidget()
++ {
++ if (axhost)
++ axhost->reset(this);
++ }
++
++ int QAxHostWidget::qt_metacall(QMetaObject::Call call, int isignal, void **argv)
++ {
++ if (axhost)
++ return axhost->qt_metacall(call, isignal, argv);
++ return -1;
++ }
++
++ void* QAxHostWidget::qt_metacast(const char *clname)
++ {
++ if (!clname) return nullptr;
++ if (!qstrcmp(clname,"QAxHostWidget"))
++ return static_cast<void*>(const_cast< QAxHostWidget*>(this));
++ return QWidget::qt_metacast(clname);
++ }
++
++ QWindow *QAxHostWidget::hostWindow() const
++ {
++ if (QWindow *w = windowHandle())
++ return w;
++ if (QWidget *parent = nativeParentWidget())
++ return parent->windowHandle();
++ return nullptr;
++ }
++
++ QSize QAxHostWidget::sizeHint() const
++ {
++ return axhost ? axhost->sizeHint() : QWidget::sizeHint();
++ }
++
++ QSize QAxHostWidget::minimumSizeHint() const
++ {
++ QSize size;
++ if (axhost)
++ size = axhost->minimumSizeHint();
++ if (size.isValid())
++ return size;
++ return QWidget::minimumSizeHint();
++ }
++
++ void QAxHostWidget::resizeObject()
++ {
++ if (!axhost)
++ return;
++
++ // document server - talk to view?
++ if (axhost->m_spActiveView) {
++ RECT rect;
++ GetClientRect(reinterpret_cast<HWND>(winId()), &rect);
++ axhost->m_spActiveView->SetRect(&rect);
++
++ return;
++ }
++
++ SIZEL hmSize = qaxMapPixToLogHiMetrics(size(), this);
++ if (axhost->m_spOleObject)
++ axhost->m_spOleObject->SetExtent(DVASPECT_CONTENT, &hmSize);
++ if (axhost->m_spInPlaceObject) {
++ RECT rcPos = qaxNativeWidgetRect(this);
++ axhost->m_spInPlaceObject->SetObjectRects(&rcPos, &rcPos);
++ }
++ }
++
++ void QAxHostWidget::resizeEvent(QResizeEvent *)
++ {
++ resizeObject();
++ }
++
++ void QAxHostWidget::showEvent(QShowEvent *)
++ {
++ resizeObject();
++ }
++
++ bool QAxHostWidget::nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
++ {
++ if (axhost && axhost->inPlaceObjectWindowless
++ && eventType == QByteArrayLiteral("windows_generic_MSG")) {
++ Q_ASSERT(axhost->m_spInPlaceObject);
++ MSG *msg = static_cast<MSG *>(message);
++ LRESULT lres;
++ HRESULT hres = axhost->m_spInPlaceObject->OnWindowMessage(msg->message, msg->wParam, msg->lParam, &lres);
++ if (hres == S_OK)
++ return true;
++ }
++
++ QWidget::nativeEvent(eventType, message, result);
++ return false;
++ }
++
++ bool QAxHostWidget::event(QEvent *e)
++ {
++ switch (e->type()) {
++ case QEvent::Timer:
++ if (axhost && static_cast<const QTimerEvent *>(e)->timerId() == setFocusTimer) {
++ killTimer(setFocusTimer);
++ setFocusTimer = 0;
++ RECT rcPos = qaxNativeWidgetRect(this);
++ axhost->m_spOleObject->DoVerb(OLEIVERB_UIACTIVATE, nullptr, static_cast<IOleClientSite *>(axhost), 0,
++ reinterpret_cast<HWND>(winId()), &rcPos);
++ if (axhost->m_spActiveView)
++ axhost->m_spActiveView->UIActivate(TRUE);
++ }
++ break;
++ case QEvent::WindowBlocked:
++ if (IsWindowEnabled(reinterpret_cast<HWND>(winId()))) {
++ EnableWindow(reinterpret_cast<HWND>(winId()), false);
++ if (axhost && axhost->m_spInPlaceActiveObject) {
++ axhost->inPlaceModelessEnabled = false;
++ axhost->m_spInPlaceActiveObject->EnableModeless(false);
++ }
++ }
++ break;
++ case QEvent::WindowUnblocked:
++ if (!IsWindowEnabled(reinterpret_cast<HWND>(winId()))) {
++ EnableWindow(reinterpret_cast<HWND>(winId()), true);
++ if (axhost && axhost->m_spInPlaceActiveObject) {
++ axhost->inPlaceModelessEnabled = true;
++ axhost->m_spInPlaceActiveObject->EnableModeless(true);
++ }
++ }
++ break;
++ default:
++ break;
++ }
++
++ return QWidget::event(e);
++ }
++
++ bool QAxHostWidget::eventFilter(QObject *o, QEvent *e)
++ {
++ // focus goes to Qt while ActiveX still has it - deactivate
++ QWidget *newFocus = qobject_cast<QWidget*>(o);
++ if (e->type() == QEvent::FocusIn && hasFocus
++ && newFocus && newFocus->window() == window()) {
++ if (axhost && axhost->m_spInPlaceActiveObject && axhost->m_spInPlaceObject)
++ axhost->m_spInPlaceObject->UIDeactivate();
++ qApp->removeEventFilter(this);
++ }
++
++ return QWidget::eventFilter(o, e);
++ }
++
++ void QAxHostWidget::focusInEvent(QFocusEvent *e)
++ {
++ QWidget::focusInEvent(e);
++
++ if (!axhost || !axhost->m_spOleObject)
++ return;
++
++ // this is called by QWidget::setFocus which calls ::SetFocus on "this",
++ // so we have to UIActivate the control after all that had happend.
++ AX_DEBUG(Setting focus on in-place object);
++ setFocusTimer = startTimer(0);
++ }
++
++ void QAxHostWidget::focusOutEvent(QFocusEvent *e)
++ {
++ QWidget::focusOutEvent(e);
++ if (setFocusTimer) {
++ killTimer(setFocusTimer);
++ setFocusTimer = 0;
++ }
++ if (e->reason() == Qt::PopupFocusReason || e->reason() == Qt::MenuBarFocusReason)
++ return;
++
++ if (!axhost || !axhost->m_spInPlaceActiveObject || !axhost->m_spInPlaceObject)
++ return;
++
++ AX_DEBUG(Deactivating in-place object);
++ axhost->m_spInPlaceObject->UIDeactivate();
++ }
++
++ void QAxHostWidget::paintEvent(QPaintEvent*)
++ {
++ // QWidget having redirected paint device indicates non-regular paint, which implies
++ // somebody is grabbing the widget instead of painting it to screen.
++ QPoint dummyOffset(0, 0);
++ if (!redirected(&dummyOffset))
++ return;
++
++ ComPtr<IViewObject> view;
++ if (axhost)
++ axhost->widget->queryInterface(IID_IViewObject, &view);
++ if (!view)
++ return;
++
++ QPixmap pm(qaxNativeWidgetSize(this));
++ pm.fill();
++
++ HBITMAP hBmp = qt_pixmapToWinHBITMAP(pm);
++ const HDC displayDc = GetDC(nullptr);
++ HDC hBmp_hdc = CreateCompatibleDC(displayDc);
++ HGDIOBJ old_hBmp = SelectObject(hBmp_hdc, hBmp);
++
++ RECTL bounds;
++ bounds.left = 0;
++ bounds.right = pm.width();
++ bounds.top = 0;
++ bounds.bottom = pm.height();
++
++ view->Draw(DVASPECT_CONTENT, -1, nullptr, nullptr, nullptr, hBmp_hdc, &bounds, nullptr, nullptr /*fptr*/, 0);
++
++ QPainter painter(this);
++ QPixmap pixmap = qt_pixmapFromWinHBITMAP(hBmp);
++ pixmap.setDevicePixelRatio(devicePixelRatio());
++ painter.drawPixmap(0, 0, pixmap);
++
++ SelectObject(hBmp_hdc, old_hBmp);
++ DeleteObject(hBmp);
++ DeleteDC(hBmp_hdc);
++ ReleaseDC(nullptr, displayDc);
++ }
++
++ /*!
++ \class QAxBaseWidget
++ \brief QAxBaseWidget provides static properties and signals for QAxWidget.
++ \inmodule QAxContainer
++ \since 6.0
++ */
++
++ /*!
++ \property QAxBaseWidget::classContext
++ \brief the context the ActiveX control will run in (default CLSCTX_SERVER).
++
++ The property affects the "dwClsContext" argument when calling
++ CoCreateInstance. This can be used to control in-proc vs. out-of-proc
++ startup for controls supporting both alternatives. Also, it can be used to
++ modify/reduce control permissions when used with CLSCTX_ENABLE_CLOAKING
++ and an impersonation token.
++
++ Note that it must be set before setControl() to have any effect.
++ \sa control
++ */
++
++ /*!
++ \property QAxBaseWidget::control
++ \brief the name of the COM object wrapped by this QAxBaseWidget object.
++
++ Setting this property initializes the COM object. Any COM object
++ previously set is shut down.
++
++ The most efficient way to set this property is by using the
++ registered component's UUID, e.g.
++
++ \snippet src_activeqt_container_qaxbase.cpp 7
++
++ The second fastest way is to use the registered control's class
++ name (with or without version number), e.g.
++
++ \snippet src_activeqt_container_qaxbase.cpp 8
++
++ The slowest, but easiest way to use is to use the control's full
++ name, e.g.
++
++ \snippet src_activeqt_container_qaxbase.cpp 9
++
++ It is also possible to initialize the object from a file, e.g.
++
++ \snippet src_activeqt_container_qaxbase.cpp 10
++
++ If the component's UUID is used the following patterns can be used
++ to initialize the control on a remote machine, to initialize a
++ licensed control or to connect to a running object:
++ \list
++ \li To initialize the control on a different machine use the following
++ pattern:
++
++ \snippet src_activeqt_container_qaxbase.cpp 11
++
++ \li To initialize a licensed control use the following pattern:
++
++ \snippet src_activeqt_container_qaxbase.cpp 12
++
++ \li To connect to an already running object use the following pattern:
++
++ \snippet src_activeqt_container_qaxbase.cpp 13
++
++ \endlist
++ The first two patterns can be combined, e.g. to initialize a licensed
++ control on a remote machine:
++
++ \snippet src_activeqt_container_qaxbase.cpp 14
++
++ The control's read function always returns the control's UUID, if provided including the license
++ key, and the name of the server, but not including the username, the domain or the password.
++
++ \sa classContext
++ */
++
++ /*!
++ \fn void QAxBaseWidget::signal(const QString &name, int argc, void *argv)
++
++ This generic signal gets emitted when the COM object issues the
++ event \a name. \a argc is the number of parameters provided by the
++ event (DISPPARAMS.cArgs), and \a argv is the pointer to the
++ parameter values (DISPPARAMS.rgvarg). Note that the order of parameter
++ values is turned around, ie. the last element of the array is the first
++ parameter in the function.
++
++ \snippet src_activeqt_container_qaxbase.cpp 20
++
++ Use this signal if the event has parameters of unsupported data
++ types. Otherwise, connect directly to the signal \a name.
++
++ \sa QAxBaseObject::signal()
++ */
++
++ /*!
++ \fn void QAxBaseWidget::propertyChanged(const QString &name)
++
++ If the COM object supports property notification, this signal gets
++ emitted when the property called \a name is changed.
++
++ \sa QAxBaseObject::propertyChanged()
++ */
++
++ /*!
++ \fn void QAxBaseWidget::exception(int code, const QString &source, const QString &desc, const QString &help)
++
++ This signal is emitted when the COM object throws an exception while called using the OLE automation
++ interface IDispatch. \a code, \a source, \a desc and \a help provide information about the exception as
++ provided by the COM server and can be used to provide useful feedback to the end user. \a help includes
++ the help file, and the help context ID in brackets, e.g. "filename [id]".
++
++ \sa QAxBaseObject::exception()
++ */
++
++ QAxBaseWidget::QAxBaseWidget(QWidgetPrivate &d, QWidget *parent, Qt::WindowFlags f)
++ : QWidget(d, parent, f)
++ {
++ }
++
++ /*!
++ \class QAxWidget
++ \brief The QAxWidget class is a QWidget that wraps an ActiveX control.
++
++ \inmodule QAxContainer
++
++ A QAxWidget can be instantiated as an empty object, with the name
++ of the ActiveX control it should wrap, or with an existing
++ interface pointer to the ActiveX control. The ActiveX control's
++ properties, methods and events which only use QAxBase
++ supported data types, become available as Qt properties,
++ slots and signals. The base class QAxBase provides an API to
++ access the ActiveX directly through the \c IUnknown pointer.
++
++ QAxWidget is a QWidget and can mostly be used as such, e.g. it can be
++ organized in a widget hierarchy and layouts or act as an event filter.
++ Standard widget properties, e.g. \link QWidget::enabled
++ enabled \endlink are supported, but it depends on the ActiveX
++ control to implement support for ambient properties like e.g.
++ palette or font. QAxWidget tries to provide the necessary hints.
++
++ However, you cannot reimplement Qt-specific event handlers like
++ mousePressEvent or keyPressEvent and expect them to be called reliably.
++ The embedded control covers the QAxWidget completely, and usually
++ handles the user interface itself. Use control-specific APIs (i.e. listen
++ to the signals of the control), or use standard COM techniques like
++ window procedure subclassing.
++
++ QAxWidget also inherits most of its ActiveX-related functionality
++ from QAxBase, notably dynamicCall() and querySubObject().
++
++ \warning
++ You can subclass QAxWidget, but you cannot use the \c Q_OBJECT macro
++ in the subclass (the generated moc-file will not compile), so you
++ cannot add further signals, slots or properties. This limitation
++ is due to the metaobject information generated in runtime. To work
++ around this problem, aggregate the QAxWidget as a member of the
++ QObject subclass.
++
++ \sa QAxBase, QAxObject, QAxScript, {ActiveQt Framework}
++ */
++
++ /*!
++ Creates an empty QAxWidget widget and propagates \a parent
++ and \a f to the QWidget constructor. To initialize a control,
++ call setControl().
++ */
++ QAxWidget::QAxWidget(QWidget *parent, Qt::WindowFlags f)
++ : QAxBaseWidget(*new QAxWidgetPrivate, parent, f)
++ {
++ Q_D(QAxWidget);
++ axBaseInit(d);
++ }
++
++ /*!
++ Creates an QAxWidget widget and initializes the ActiveX control \a c.
++ \a parent and \a f are propagated to the QWidget contructor.
++
++ \sa setControl()
++ */
++ QAxWidget::QAxWidget(const QString &c, QWidget *parent, Qt::WindowFlags f)
++ : QAxBaseWidget(*new QAxWidgetPrivate, parent, f)
++ {
++ Q_D(QAxWidget);
++ axBaseInit(d);
++ setControl(c);
++ }
++
++ /*!
++ Creates a QAxWidget that wraps the COM object referenced by \a iface.
++ \a parent and \a f are propagated to the QWidget contructor.
++ */
++ QAxWidget::QAxWidget(IUnknown *iface, QWidget *parent, Qt::WindowFlags f)
++ : QAxBaseWidget(*new QAxWidgetPrivate, parent, f)
++ {
++ Q_D(QAxWidget);
++ axBaseInit(d, iface);
++ }
++
++ /*!
++ Shuts down the ActiveX control and destroys the QAxWidget widget,
++ cleaning up all allocated resources.
++
++ \sa clear()
++ */
++ QAxWidget::~QAxWidget()
++ {
++ Q_D(QAxWidget);
++ if (d->container)
++ d->container->reset(this);
++ d->clear();
++ }
++
++ /*!
++ \since 4.2
++
++ Calls QAxBase::initialize(\a ptr), and embeds the control in this
++ widget by calling createHostWindow(false) if successful.
++
++ To initialize the control before it is activated, reimplement this
++ function and add your initialization code before you call
++ createHostWindow(true).
++
++ Returns \c true on success, \c false otherwise.
++ */
++ bool QAxWidget::initialize(IUnknown **ptr)
++ {
++ if (!QAxBase::initialize(ptr))
++ return false;
++
++ return createHostWindow(false); // assume that control is not initialized
++ }
++
++ /*!
++ Creates the client site for the ActiveX control, and returns true if
++ the control could be embedded successfully, otherwise returns false.
++ If \a initialized is true the control has already been initialized.
++
++ This function is called by initialize(). If you reimplement initialize
++ to customize the actual control instantiation, call this function in your
++ reimplementation to have the control embedded by the default client side.
++ Creates the client site for the ActiveX control, and returns true if
++ the control could be embedded successfully, otherwise returns false.
++ */
++ bool QAxWidget::createHostWindow(bool initialized)
++ {
++ return createHostWindow(initialized, QByteArray());
++ }
++
++ /*!
++ \since 4.4
++
++ Creates the client site for the ActiveX control, and returns true if
++ the control could be embedded successfully, otherwise returns false.
++ If \a initialized is false the control will be initialized using the
++ \a data. The control will be initialized through either IPersistStreamInit
++ or IPersistStorage interface.
++
++ If the control needs to be initialized using custom data, call this function
++ in your reimplementation of initialize(). This function is not called by
++ the default implementation of initialize().
++ */
++ bool QAxWidget::createHostWindow(bool initialized, const QByteArray &data)
++ {
++ Q_D(QAxWidget);
++ if (!d->container) // Potentially called repeatedly from QAxBase::metaObject(), QAxWidget::initialize()
++ d->container = makeComObject<QAxClientSite>(this);
++
++ d->container->activateObject(initialized, data);
++
++ ATOM filter_ref = FindAtom(qaxatom);
++ if (!filter_ref)
++ QAbstractEventDispatcher::instance()->installNativeEventFilter(s_nativeEventFilter());
++ AddAtom(qaxatom);
++
++ if (parentWidget())
++ QApplication::postEvent(parentWidget(), new QEvent(QEvent::LayoutRequest));
++
++ return true;
++ }
++
++ /*!
++ Reimplement this function when you want to implement additional
++ COM interfaces for the client site of the ActiveX control, or when
++ you want to provide alternative implementations of COM interfaces.
++ Return a new object of a QAxAggregated subclass.
++
++ The default implementation returns the null pointer.
++ */
++ QAxAggregated *QAxWidget::createAggregate()
++ {
++ return nullptr;
++ }
++
++ ulong QAxWidget::classContext() const
++ {
++ return QAxBase::classContext();
++ }
++
++ void QAxWidget::setClassContext(ulong classContext)
++ {
++ QAxBase::setClassContext(classContext);
++ }
++
++ QString QAxWidget::control() const
++ {
++ return QAxBase::control();
++ }
++
++ bool QAxWidget::setControl(const QString &c)
++ {
++ return QAxBase::setControl(c);
++ }
++
++ /*!
++ Shuts down the ActiveX control.
++ \sa resetControl()
++ */
++ void QAxWidget::clear()
++ {
++ resetControl();
++ }
++
++ /*!
++ \reimp
++
++ Shuts down the ActiveX control.
++ */
++ void QAxWidget::resetControl()
++ {
++ Q_D(QAxWidget);
++ d->clear();
++ }
++
++ void QAxWidgetPrivate::clear()
++ {
++ Q_Q(QAxWidget);
++ if (q->isNull())
++ return;
++ if (!q->QAxBase::control().isEmpty()) {
++ ATOM filter_ref = FindAtom(qaxatom);
++ if (filter_ref)
++ DeleteAtom(filter_ref);
++ filter_ref = FindAtom(qaxatom);
++ if (!filter_ref)
++ QAbstractEventDispatcher::instance()->removeNativeEventFilter(s_nativeEventFilter());
++ }
++
++ if (container)
++ container->deactivate();
++
++ q->QAxBase::clear();
++ q->setFocusPolicy(Qt::NoFocus);
++
++ if (container) {
++ container->releaseAll();
++ }
++ container.Reset();
++ }
++
++ /*!
++ \since 4.1
++
++ Requests the ActiveX control to perform the action \a verb. The
++ possible verbs are returned by verbs().
++
++ The function returns true if the object could perform the action, otherwise returns false.
++ */
++ bool QAxWidget::doVerb(const QString &verb)
++ {
++ Q_D(QAxWidget);
++ if (!verbs().contains(verb))
++ return false;
++
++ return d->container->doVerb(indexOfVerb(verb)) == S_OK;
++ }
++
++ /*!
++ \fn QObject *QAxWidget::qObject() const
++ \internal
++ */
++
++ /*!
++ \internal
++ */
++ void QAxWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
++ {
++ QAxBasePrivate::qtStaticMetaCall(static_cast<QAxWidget *>(_o), _c, _id, _a);
++ }
++
++ const QMetaObject *QAxWidgetPrivate::fallbackMetaObject() const
++ {
++ return &QAxWidget::staticMetaObject;
++ }
++
++ /*!
++ \internal
++ */
++ const QMetaObject *QAxWidget::metaObject() const
++ {
++ return QAxBase::axBaseMetaObject();
++ }
++
++ const QMetaObject *QAxWidgetPrivate::parentMetaObject() const
++ {
++ return &QAxBaseWidget::staticMetaObject;
++ }
++
++ /*!
++ \internal
++ */
++ void *QAxWidget::qt_metacast(const char *cname)
++ {
++ if (!qstrcmp(cname, "QAxWidget")) return static_cast<void *>(this);
++ if (!qstrcmp(cname, "QAxBase")) return static_cast<QAxBase *>(this);
++ return QAxBaseWidget::qt_metacast(cname);
++ }
++
++ QObject* QAxWidgetPrivate::qObject() const
++ {
++ Q_Q(const QAxWidget);
++ return static_cast<QObject *>(const_cast<QAxWidget *>(q));
++ }
++
++ const char *QAxWidgetPrivate::className() const
++ {
++ return "QAxWidget";
++ }
++
++ /*!
++ \internal
++ */
++ int QAxWidget::qt_metacall(QMetaObject::Call call, int id, void **v)
++ {
++ Q_D(QAxWidget);
++ id = QAxBaseWidget::qt_metacall(call, id, v);
++ if (id < 0)
++ return id;
++ return d->qtMetaCall(call, id, v);
++ }
++
++ /*!
++ \reimp
++ */
++ QSize QAxWidget::sizeHint() const
++ {
++ Q_D(const QAxWidget);
++ if (d->container) {
++ QSize sh = d->container->sizeHint();
++ if (sh.isValid())
++ return sh;
++ }
++
++ return QWidget::sizeHint();
++ }
++
++ /*!
++ \reimp
++ */
++ QSize QAxWidget::minimumSizeHint() const
++ {
++ Q_D(const QAxWidget);
++ if (d->container) {
++ QSize sh = d->container->minimumSizeHint();
++ if (sh.isValid())
++ return sh;
++ }
++
++ return QWidget::minimumSizeHint();
++ }
++
++ /*!
++ \reimp
++ */
++ void QAxWidget::changeEvent(QEvent *e)
++ {
++ Q_D(QAxWidget);
++ if (isNull() || !d->container)
++ return;
++
++ switch (e->type()) {
++ case QEvent::EnabledChange:
++ d->container->emitAmbientPropertyChange(DISPID_AMBIENT_UIDEAD);
++ break;
++ case QEvent::FontChange:
++ d->container->emitAmbientPropertyChange(DISPID_AMBIENT_FONT);
++ break;
++ case QEvent::PaletteChange:
++ d->container->emitAmbientPropertyChange(DISPID_AMBIENT_BACKCOLOR);
++ d->container->emitAmbientPropertyChange(DISPID_AMBIENT_FORECOLOR);
++ break;
++ case QEvent::ActivationChange:
++ d->container->windowActivationChange();
++ break;
++ default:
++ break;
++ }
++ }
++
++ /*!
++ \reimp
++ */
++ void QAxWidget::resizeEvent(QResizeEvent *)
++ {
++ Q_D(QAxWidget);
++ if (d->container)
++ d->container->resize(size());
++ }
++
++ /*!
++ \reimp
++ */
++ void QAxWidget::connectNotify(const QMetaMethod &)
++ {
++ QAxBase::connectNotify();
++ }
++
++
++ /*!
++ Reimplement this function to pass certain key events to the
++ ActiveX control. \a message is the Window message identifier
++ specifying the message type (ie. WM_KEYDOWN), and \a keycode is
++ the virtual keycode (ie. VK_TAB).
++
++ If the function returns true the key event is passed on to the
++ ActiveX control, which then either processes the event or passes
++ the event on to Qt.
++
++ If the function returns false the processing of the key event is
++ ignored by ActiveQt, ie. the ActiveX control might handle it or
++ not.
++
++ The default implementation returns true for the following cases:
++
++ \table
++ \header
++ \li WM_SYSKEYDOWN
++ \li WM_SYSKEYUP
++ \li WM_KEYDOWN
++ \row
++ \li All keycodes
++ \li VK_MENU
++ \li VK_TAB, VK_DELETE and all non-arrow-keys in combination with VK_SHIFT,
++ VK_CONTROL or VK_MENU
++ \endtable
++
++ This table is the result of experimenting with popular ActiveX controls,
++ ie. Internet Explorer and Microsoft Office applications, but for some
++ controls it might require modification.
++ */
++ bool QAxWidget::translateKeyEvent(int message, int keycode) const
++ {
++ bool translate = false;
++
++ switch (message) {
++ case WM_SYSKEYDOWN:
++ translate = true;
++ break;
++ case WM_KEYDOWN:
++ translate = keycode == VK_TAB
++ || keycode == VK_DELETE;
++ if (!translate) {
++ int state = 0;
++ if (GetKeyState(VK_SHIFT) < 0)
++ state |= 0x01;
++ if (GetKeyState(VK_CONTROL) < 0)
++ state |= 0x02;
++ if (GetKeyState(VK_MENU) < 0)
++ state |= 0x04;
++ if (state) {
++ state = keycode < VK_LEFT || keycode > VK_DOWN;
++ }
++ translate = state;
++ }
++ break;
++ case WM_SYSKEYUP:
++ translate = keycode == VK_MENU;
++ break;
++ }
++
++ return translate;
++ }
++
++ #ifndef QT_NO_DATASTREAM
++ /*!
++ \internal
++ */
++ // friend
++ QDataStream &operator>>(QDataStream &s, QAxWidget &w)
++ {
++ return s >> static_cast<QAxBase &>(w);
++ }
++
++ /*!
++ \internal
++ */
++ // friend
++ QDataStream &operator<<(QDataStream &s, const QAxWidget &w)
++ {
++ return s << static_cast<const QAxBase &>(w);
++ }
++ #endif // QT_NO_DATASTREAM
++
++ QT_END_NAMESPACE