/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the ActiveQt framework of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, 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 The Qt Company Ltd 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$
**
****************************************************************************/

#include "qaxwidget.h"
#include "../shared/qaxutils_p.h"

#include <ActiveQt/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 <windowsx.h>
#include <ocidl.h>
#include <olectl.h>
#include <docobj.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.h"

QT_BEGIN_NAMESPACE

/*  \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:
    Q_OBJECT_CHECK
    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:
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
    bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override;
#else
    bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;
#endif
    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;
};

/*  \class QAxClientSite
    \brief The QAxClientSite class implements the client site interfaces.

    \internal
*/
class QAxClientSite : public IDispatch,
                    public IOleClientSite,
                    public IOleControlSite,
#ifdef QAX_SUPPORT_WINDOWLESS
                    public IOleInPlaceSiteWindowless,
#else
                    public IOleInPlaceSite,
#endif
                    public IOleInPlaceFrame,
                    public IOleDocumentSite,
                    public 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;
    }

    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
    unsigned long WINAPI AddRef() override;
    unsigned long WINAPI Release() override;
    STDMETHOD(QueryInterface)(REFIID iid, void **iface);

    // 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)();
    STDMETHOD(GetMoniker)(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker **ppmk);
    STDMETHOD(GetContainer)(LPOLECONTAINER FAR* ppContainer);
    STDMETHOD(ShowObject)();
    STDMETHOD(OnShowWindow)(BOOL fShow);
    STDMETHOD(RequestNewObjectLayout)();

    // IOleControlSite
    STDMETHOD(OnControlInfoChanged)();
    STDMETHOD(LockInPlaceActive)(BOOL fLock);
    STDMETHOD(GetExtendedControl)(IDispatch** ppDisp);
    STDMETHOD(TransformCoords)(POINTL* pPtlHimetric, POINTF* pPtfContainer, DWORD dwFlags);
    STDMETHOD(TranslateAccelerator)(LPMSG lpMsg, DWORD grfModifiers);
    STDMETHOD(OnFocus)(BOOL fGotFocus);
    STDMETHOD(ShowPropertyFrame)();

    // IOleWindow
    STDMETHOD(GetWindow)(HWND *phwnd);
    STDMETHOD(ContextSensitiveHelp)(BOOL fEnterMode);

    // IOleInPlaceSite
    STDMETHOD(CanInPlaceActivate)();
    STDMETHOD(OnInPlaceActivate)();
    STDMETHOD(OnUIActivate)();
    STDMETHOD(GetWindowContext)(IOleInPlaceFrame **ppFrame, IOleInPlaceUIWindow **ppDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo);
    STDMETHOD(Scroll)(SIZE scrollExtant);
    STDMETHOD(OnUIDeactivate)(BOOL fUndoable);
    STDMETHOD(OnInPlaceDeactivate)();
    STDMETHOD(DiscardUndoState)();
    STDMETHOD(DeactivateAndUndo)();
    STDMETHOD(OnPosRectChange)(LPCRECT lprcPosRect);

#ifdef QAX_SUPPORT_WINDOWLESS
// IOleInPlaceSiteEx ###
    STDMETHOD(OnInPlaceActivateEx)(BOOL* /*pfNoRedraw*/, DWORD /*dwFlags*/)
    {
        return S_OK;
    }
    STDMETHOD(OnInPlaceDeactivateEx)(BOOL /*fNoRedraw*/)
    {
        return S_OK;
    }
    STDMETHOD(RequestUIActivate)()
    {
        return S_OK;
    }

// IOleInPlaceSiteWindowless ###
    STDMETHOD(CanWindowlessActivate)()
    {
        return S_OK;
    }
    STDMETHOD(GetCapture)()
    {
        return S_FALSE;
    }
    STDMETHOD(SetCapture)(BOOL /*fCapture*/)
    {
        return S_FALSE;
    }
    STDMETHOD(GetFocus)()
    {
        return S_FALSE;
    }
    STDMETHOD(SetFocus)(BOOL /*fCapture*/)
    {
        return S_FALSE;
    }
    STDMETHOD(GetDC)(LPCRECT /*pRect*/, DWORD /*grfFlags*/, HDC *phDC)
    {
        *phDC = 0;
        return S_OK;
    }
    STDMETHOD(ReleaseDC)(HDC hDC)
    {
        ::ReleaseDC((HWND)widget->winId(), hDC);
        return S_OK;
    }
    STDMETHOD(InvalidateRect)(LPCRECT pRect, BOOL fErase)
    {
        ::InvalidateRect((HWND)host->winId(), pRect, fErase);
        return S_OK;
    }
    STDMETHOD(InvalidateRgn)(HRGN hRGN, BOOL fErase)
    {
        ::InvalidateRgn((HWND)host->winId(), hRGN, fErase);
        return S_OK;
    }
    STDMETHOD(ScrollRect)(int /*dx*/, int /*dy*/, LPCRECT /*pRectScroll*/, LPCRECT /*pRectClip*/)
    {
        return S_OK;
    }
    STDMETHOD(AdjustRect)(LPRECT /*prc*/)
    {
        return S_OK;
    }
#ifdef Q_CC_GNU // signature incorrect in win32api
    STDMETHOD(AdjustRect)(LPCRECT /*prc*/)
    {
        RECT rect;
        return AdjustRect(&rect);
    }
#endif

    STDMETHOD(OnDefWindowMessage)(UINT /*msg*/, WPARAM /*wPara*/, LPARAM /*lParam*/, LRESULT* /*plResult*/)
    {
        return S_FALSE;
    }
#endif

    // IOleInPlaceFrame
    STDMETHOD(InsertMenus(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths));
    STDMETHOD(SetMenu(HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject));
    STDMETHOD(RemoveMenus(HMENU hmenuShared));
    STDMETHOD(SetStatusText(LPCOLESTR pszStatusText));
    STDMETHOD(EnableModeless(BOOL fEnable));
    STDMETHOD(TranslateAccelerator(LPMSG lpMsg, WORD grfModifiers));

    // IOleInPlaceUIWindow
    STDMETHOD(GetBorder(LPRECT lprectBorder));
    STDMETHOD(RequestBorderSpace(LPCBORDERWIDTHS pborderwidths));
    STDMETHOD(SetBorderSpace(LPCBORDERWIDTHS pborderwidths));
    STDMETHOD(SetActiveObject(IOleInPlaceActiveObject *pActiveObject, LPCOLESTR pszObjName));

    // IOleDocumentSite
    STDMETHOD(ActivateMe(IOleDocumentView *pViewToActivate));

    // IAdviseSink
    STDMETHOD_(void, OnDataChange)(FORMATETC* /*pFormatetc*/, STGMEDIUM* /*pStgmed*/)
    {
        AX_DEBUG(QAxClientSite::OnDataChange);
    }
    STDMETHOD_(void, OnViewChange)(DWORD /*dwAspect*/, LONG /*lindex*/)
    {
        AX_DEBUG(QAxClientSite::OnViewChange);
    }
    STDMETHOD_(void, OnRename)(IMoniker* /*pmk*/)
    {
    }
    STDMETHOD_(void, OnSave)()
    {
    }
    STDMETHOD_(void, OnClose)()
    {
    }

    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);

    IOleObject *m_spOleObject = nullptr;
    IOleControl *m_spOleControl = nullptr;
    IOleInPlaceObjectWindowless *m_spInPlaceObject = nullptr;
    IOleInPlaceActiveObject *m_spInPlaceActiveObject = nullptr;
    IOleDocumentView *m_spActiveView = nullptr;

    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;
    LONG ref = 1;
    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::MidButton,
    WM_MBUTTONUP,       QEvent::MouseButtonRelease,     Qt::MidButton,
    WM_MBUTTONDBLCLK,   QEvent::MouseButtonDblClick,    Qt::MidButton,
    0,                  0,                              0
};

static Qt::MouseButtons translateMouseButtonState(int s)
{
    Qt::MouseButtons bst = nullptr;
    if (s & MK_LBUTTON)
        bst |= Qt::LeftButton;
    if (s & MK_MBUTTON)
        bst |= Qt::MidButton;
    if (s & MK_RBUTTON)
        bst |= Qt::RightButton;

    return bst;
}

static Qt::KeyboardModifiers translateModifierState(int s)
{
    Qt::KeyboardModifiers bst = nullptr;
    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:
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
    bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *) override;
#else
    bool nativeEventFilter(const QByteArray &eventType, void *message, long *) override;
#endif
};
Q_GLOBAL_STATIC(QAxNativeEventFilter, s_nativeEventFilter)

#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
bool QAxNativeEventFilter::nativeEventFilter(const QByteArray &, void *m, qintptr *)
#else
bool QAxNativeEventFilter::nativeEventFilter(const QByteArray &, void *m, long *)
#endif
{
    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, reinterpret_cast<void**>(&m_spOleObject));
    if (m_spOleObject) {
        DWORD dwMiscStatus = 0;
        m_spOleObject->GetMiscStatus(DVASPECT_CONTENT, &dwMiscStatus);

        IOleDocument *document = nullptr;
        m_spOleObject->QueryInterface(IID_IOleDocument, reinterpret_cast<void**>(&document));
        if (document) {
            IPersistStorage *persistStorage = nullptr;
            document->QueryInterface(IID_IPersistStorage, reinterpret_cast<void**>(&persistStorage));
            if (persistStorage) {
            // try to activate as document server
                IStorage *storage = nullptr;
                ILockBytes * bytes = nullptr;
                ::CreateILockBytesOnHGlobal(nullptr, TRUE, &bytes);
                ::StgCreateDocfileOnILockBytes(bytes, STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, &storage);

                persistStorage->InitNew(storage);
                persistStorage->Release();
                canHostDocument = true;
                storage->Release();
                bytes->Release();

                m_spOleObject->SetClientSite(this);
                OleRun(m_spOleObject);
            }
            document->Release();
        }

        if (!canHostDocument) {
            // activate as control
            if(dwMiscStatus & OLEMISC_SETCLIENTSITEFIRST)
                m_spOleObject->SetClientSite(this);

            if (!initialized) {
                IPersistStreamInit *spPSI = nullptr;
                m_spOleObject->QueryInterface(IID_IPersistStreamInit, reinterpret_cast<void**>(&spPSI));
                if (spPSI) {
                    if (data.length()) {
                        IStream *pStream = nullptr;
                        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);
                                pStream->Release();
                            }
                            GlobalFree(hGlobal);
                        }
                    } else {
                        spPSI->InitNew();
                    }
                    spPSI->Release();
                } else if (data.length()) { //try initializing using a IPersistStorage
                    IPersistStorage *spPS = nullptr;
                    m_spOleObject->QueryInterface( IID_IPersistStorage, reinterpret_cast<void **>(&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
                            LPLOCKBYTES pLockBytes = nullptr;
                            if (SUCCEEDED(CreateILockBytesOnHGlobal(hGlobal, TRUE, &pLockBytes))) {
                                LPSTORAGE pStorage = nullptr;
                                if (SUCCEEDED(StgOpenStorageOnILockBytes(pLockBytes, nullptr,
                                              STGM_READWRITE | STGM_SHARE_EXCLUSIVE, nullptr, 0, &pStorage))) {
                                    spPS->Load(pStorage);
                                    pStorage->Release();
                                }
                                pLockBytes->Release();
                            }
                            GlobalFree(hGlobal);
                        }
                        spPS->Release();
                    }
                }
            }

            if(!(dwMiscStatus & OLEMISC_SETCLIENTSITEFIRST))
                m_spOleObject->SetClientSite(this);
        }

        IViewObject *spViewObject = nullptr;
        m_spOleObject->QueryInterface(IID_IViewObject, reinterpret_cast<void **>(&spViewObject));

        m_spOleObject->Advise(this, &m_dwOleObject);
        IAdviseSink *spAdviseSink = nullptr;
        QueryInterface(IID_IAdviseSink, reinterpret_cast<void **>(&spAdviseSink));
        if (spAdviseSink && spViewObject) {
            if (spViewObject)
                spViewObject->SetAdvise(DVASPECT_CONTENT, 0, spAdviseSink);
        }
        if (spAdviseSink)
            spAdviseSink->Release();
        if (spViewObject)
            spViewObject->Release();

        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 = {};
        {
            IOleWindow *oleWindow = nullptr;
            m_spOleObject->QueryInterface(IID_IOleWindow, reinterpret_cast<void **>(&oleWindow));
            if (oleWindow) {
                oleWindow->GetWindow(&controlWnd);
                oleWindow->Release();
            }
        }

        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, reinterpret_cast<void **>(&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 {
        IObjectWithSite *spSite = nullptr;
        widget->queryInterface(IID_IObjectWithSite, reinterpret_cast<void **>(&spSite));
        if (spSite) {
            spSite->SetSite(static_cast<IUnknown *>(static_cast<IDispatch *>(this)));
            spSite->Release();
        }
    }

    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()
{
    if (m_spOleControl)
        m_spOleControl->Release();
    m_spOleControl = nullptr;
    if (m_spOleObject) {
        m_spOleObject->Unadvise(m_dwOleObject);
        m_spOleObject->SetClientSite(nullptr);
        m_spOleObject->Release();
    }
    m_spOleObject = nullptr;
    if (m_spInPlaceObject) m_spInPlaceObject->Release();
    m_spInPlaceObject = nullptr;
    if (m_spInPlaceActiveObject) m_spInPlaceActiveObject->Release();
    m_spInPlaceActiveObject = nullptr;

    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
unsigned long WINAPI QAxClientSite::AddRef()
{
    return InterlockedIncrement(&ref);
}

unsigned long WINAPI QAxClientSite::Release()
{
    LONG refCount = InterlockedDecrement(&ref);
    if (!refCount)
        delete this;

    return refCount;
}

HRESULT WINAPI QAxClientSite::QueryInterface(REFIID iid, void **iface)
{
    *iface = nullptr;

    if (iid == IID_IUnknown) {
        *iface = static_cast<IUnknown *>(static_cast<IDispatch *>(this));
    } else {
        HRESULT res = S_OK;
        if (aggregatedObject)
            res = aggregatedObject->queryInterface(iid, iface);
        if (*iface)
            return res;
    }

    if (!(*iface)) {
        if (iid == IID_IDispatch)
            *iface = static_cast<IDispatch *>(this);
        else if (iid == IID_IOleClientSite)
            *iface = static_cast<IOleClientSite *>(this);
        else if (iid == IID_IOleControlSite)
            *iface = static_cast<IOleControlSite *>(this);
        else if (iid == IID_IOleWindow)
            *iface = static_cast<IOleWindow *>(static_cast<IOleInPlaceSite *>(this));
        else if (iid == IID_IOleInPlaceSite)
            *iface = static_cast<IOleInPlaceSite *>(this);
#ifdef QAX_SUPPORT_WINDOWLESS
        else if (iid == IID_IOleInPlaceSiteEx)
            *iface = static_cast<IOleInPlaceSiteEx *>(this);
        else if (iid == IID_IOleInPlaceSiteWindowless)
            *iface = static_cast<IOleInPlaceSiteWindowless *>(this);
#endif
        else if (iid == IID_IOleInPlaceFrame)
            *iface = static_cast<IOleInPlaceFrame *>(this);
        else if (iid == IID_IOleInPlaceUIWindow)
            *iface = static_cast<IOleInPlaceUIWindow *>(this);
        else if (iid == IID_IOleDocumentSite && canHostDocument)
            *iface = static_cast<IOleDocumentSite *>(this);
        else if (iid == IID_IAdviseSink)
            *iface = static_cast<IAdviseSink *>(this);
    }
    if (!*iface)
        return E_NOINTERFACE;

    AddRef();
    return S_OK;
}

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, true, false);
    if (!m_spInPlaceObject) {
/* ### disabled for now
        m_spOleObject->QueryInterface(IID_IOleInPlaceObjectWindowless, (void**) &m_spInPlaceObject);
*/
        if (m_spInPlaceObject) {
            inPlaceObjectWindowless = true;
        } else {
            inPlaceObjectWindowless = false;
            m_spOleObject->QueryInterface(IID_IOleInPlaceObject, reinterpret_cast<void **>(&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);
    if (m_spInPlaceObject)
        m_spInPlaceObject->Release();
    m_spInPlaceObject = nullptr;
    inPlaceObjectWindowless = false;
    OleLockRunning(m_spOleObject, 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;
    QList<QAction*> actions = menuBar->actions();
    for (int i = 0; i < actions.count(); ++i) {
        QAction *action = actions.at(i);
        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().count();
    if (viewMenu)
        lpMenuWidths->width[2] = viewMenu->actions().count();
    if (windowMenu)
        lpMenuWidths->width[4] = windowMenu->actions().count();

    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;
        const MenuItemMap::Iterator mend = menuItemMap.end();
        for (MenuItemMap::Iterator it = menuItemMap.begin(); it != mend; ++it)
            delete it.key();
        menuItemMap.clear();
    }

    OleSetMenuDescriptor(holemenu, widget ? hwndForWidget(widget) : nullptr, m_menuOwner, this, m_spInPlaceActiveObject);
    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);
    const MenuItemMap::Iterator mend = menuItemMap.end();
    for (MenuItemMap::Iterator it = menuItemMap.begin(); 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->Release();
    }

    m_spInPlaceActiveObject = pActiveObject;
    if (m_spInPlaceActiveObject)
        m_spInPlaceActiveObject->AddRef();

    return S_OK;
}

//**** IOleDocumentSite
HRESULT WINAPI QAxClientSite::ActivateMe(IOleDocumentView *pViewToActivate)
{
    AX_DEBUG(QAxClientSite::ActivateMe);

    if (m_spActiveView)
        m_spActiveView->Release();
    m_spActiveView = nullptr;

    if (!pViewToActivate) {
        IOleDocument *document = nullptr;
        m_spOleObject->QueryInterface(IID_IOleDocument, reinterpret_cast<void **>(&document));
        if (!document)
            return E_FAIL;

        document->CreateView(this, nullptr, 0, &pViewToActivate);

        document->Release();
        if (!pViewToActivate)
            return E_OUTOFMEMORY;
    } else {
        pViewToActivate->SetInPlaceSite(this);
    }

    m_spActiveView = pViewToActivate;
    m_spActiveView->AddRef();

    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();
}

#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
bool QAxHostWidget::nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
#else
bool QAxHostWidget::nativeEvent(const QByteArray &eventType, void *message, long *result)
#endif
{
    if (axhost && axhost->inPlaceObjectWindowless
        && eventType == QByteArrayLiteral("windows_generic_MSG")) {
        Q_ASSERT(axhost->m_spInPlaceObject);
        MSG *msg = static_cast<MSG *>(message);
        IOleInPlaceObjectWindowless *windowless = axhost->m_spInPlaceObject;
        Q_ASSERT(windowless);
        LRESULT lres;
        HRESULT hres = windowless->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();
}

Q_GUI_EXPORT HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &p, int hbitmapFormat = 0);
Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0);

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;

    IViewObject *view = nullptr;
    if (axhost)
        axhost->widget->queryInterface(IID_IViewObject, reinterpret_cast<void **>(&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);
    view->Release();

    QPainter painter(this);
    QPixmap pixmap = qt_pixmapFromWinHBITMAP(hBmp);
    pixmap.setDevicePixelRatio(devicePixelRatioF());
    painter.drawPixmap(0, 0, pixmap);

    SelectObject(hBmp_hdc, old_hBmp);
    DeleteObject(hBmp);
    DeleteDC(hBmp_hdc);
    ReleaseDC(nullptr, displayDc);
}

/*!
    \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}
*/

const QMetaObject QAxWidget::staticMetaObject = {
    { &QWidget::staticMetaObject, qt_meta_stringdata_QAxBase.data,
      qt_meta_data_QAxBase, qt_static_metacall, nullptr, nullptr }
};

/*!
    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)
: QWidget(parent, f)
{
}

/*!
    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)
: QWidget(parent, f)
{
    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)
: QWidget(parent, f), QAxBase(iface)
{
}

/*!
    Shuts down the ActiveX control and destroys the QAxWidget widget,
    cleaning up all allocated resources.

    \sa clear()
*/
QAxWidget::~QAxWidget()
{
    if (container)
        container->reset(this);
    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)
{
    if (!container) // Potentially called repeatedly from QAxBase::metaObject(), QAxWidget::initialize()
        container = new QAxClientSite(this);

    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;
}

/*!
    \reimp

    Shuts down the ActiveX control.
*/
void QAxWidget::clear()
{
    if (isNull())
        return;
    if (!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();

    QAxBase::clear();
    setFocusPolicy(Qt::NoFocus);

    if (container) {
        container->releaseAll();
        container->Release();
    }
    container = nullptr;
}

/*!
    \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)
{
    if (!verbs().contains(verb))
        return false;

    HRESULT hres = container->doVerb(indexOfVerb(verb));

    return hres == S_OK;
}

 /*!
    \fn QObject *QAxWidget::qObject() const
    \internal
*/

/*!
    \internal
*/
void QAxWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    QAxBase::qt_static_metacall(qobject_cast<QAxWidget*>(_o), _c, _id, _a);
}

/*!
    \internal
*/
const QMetaObject *QAxWidget::fallbackMetaObject() const
{
    return &staticMetaObject;
}

/*!
    \internal
*/
const QMetaObject *QAxWidget::metaObject() const
{
    return QAxBase::metaObject();
}

/*!
    \internal
*/
const QMetaObject *QAxWidget::parentMetaObject() const
{
    return &QWidget::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 QWidget::qt_metacast(cname);
}

/*!
    \internal
*/
const char *QAxWidget::className() const
{
    return "QAxWidget";
}

/*!
    \internal
*/
int QAxWidget::qt_metacall(QMetaObject::Call call, int id, void **v)
{
    id = QWidget::qt_metacall(call, id, v);
    if (id < 0)
        return id;
    return QAxBase::qt_metacall(call, id, v);
}

/*!
    \reimp
*/
QSize QAxWidget::sizeHint() const
{
    if (container) {
        QSize sh = container->sizeHint();
        if (sh.isValid())
            return sh;
    }

    return QWidget::sizeHint();
}

/*!
    \reimp
*/
QSize QAxWidget::minimumSizeHint() const
{
    if (container) {
        QSize sh = container->minimumSizeHint();
        if (sh.isValid())
            return sh;
    }

    return QWidget::minimumSizeHint();
}

/*!
    \reimp
*/
void QAxWidget::changeEvent(QEvent *e)
{
    if (isNull() || !container)
        return;

    switch (e->type()) {
    case QEvent::EnabledChange:
        container->emitAmbientPropertyChange(DISPID_AMBIENT_UIDEAD);
        break;
    case QEvent::FontChange:
        container->emitAmbientPropertyChange(DISPID_AMBIENT_FONT);
        break;
    case QEvent::PaletteChange:
        container->emitAmbientPropertyChange(DISPID_AMBIENT_BACKCOLOR);
        container->emitAmbientPropertyChange(DISPID_AMBIENT_FORECOLOR);
        break;
    case QEvent::ActivationChange:
        container->windowActivationChange();
        break;
    default:
        break;
    }
}

/*!
    \reimp
*/
void QAxWidget::resizeEvent(QResizeEvent *)
{
    if (container)
        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;
}

QT_END_NAMESPACE
