diff options
author | Alexis Menard <alexis.menard@nokia.com> | 2009-04-17 14:06:06 (GMT) |
---|---|---|
committer | Alexis Menard <alexis.menard@nokia.com> | 2009-04-17 14:06:06 (GMT) |
commit | f15b8a83e2e51955776a3f07cb85ebfc342dd8ef (patch) | |
tree | c5dc684986051654898db11ce73e03b9fec8db99 /src/activeqt/container | |
download | Qt-f15b8a83e2e51955776a3f07cb85ebfc342dd8ef.zip Qt-f15b8a83e2e51955776a3f07cb85ebfc342dd8ef.tar.gz Qt-f15b8a83e2e51955776a3f07cb85ebfc342dd8ef.tar.bz2 |
Initial import of statemachine branch from the old kinetic repository
Diffstat (limited to 'src/activeqt/container')
-rw-r--r-- | src/activeqt/container/container.pro | 45 | ||||
-rw-r--r-- | src/activeqt/container/qaxbase.cpp | 4465 | ||||
-rw-r--r-- | src/activeqt/container/qaxbase.h | 226 | ||||
-rw-r--r-- | src/activeqt/container/qaxdump.cpp | 404 | ||||
-rw-r--r-- | src/activeqt/container/qaxobject.cpp | 213 | ||||
-rw-r--r-- | src/activeqt/container/qaxobject.h | 105 | ||||
-rw-r--r-- | src/activeqt/container/qaxscript.cpp | 1293 | ||||
-rw-r--r-- | src/activeqt/container/qaxscript.h | 248 | ||||
-rw-r--r-- | src/activeqt/container/qaxscriptwrapper.cpp | 64 | ||||
-rw-r--r-- | src/activeqt/container/qaxselect.cpp | 164 | ||||
-rw-r--r-- | src/activeqt/container/qaxselect.h | 75 | ||||
-rw-r--r-- | src/activeqt/container/qaxselect.ui | 173 | ||||
-rw-r--r-- | src/activeqt/container/qaxwidget.cpp | 2228 | ||||
-rw-r--r-- | src/activeqt/container/qaxwidget.h | 125 |
14 files changed, 9828 insertions, 0 deletions
diff --git a/src/activeqt/container/container.pro b/src/activeqt/container/container.pro new file mode 100644 index 0000000..ceedacf --- /dev/null +++ b/src/activeqt/container/container.pro @@ -0,0 +1,45 @@ +TEMPLATE = lib + +TARGET = ActiveQt +CONFIG += qt_install_headers +SYNCQT.HEADER_FILES = qaxbase.h qaxobject.h qaxscript.h qaxselect.h qaxwidget.h +SYNCQT.HEADER_CLASSES = ../../../include/ActiveQt/QAxBase ../../../include/ActiveQt/QAxObject ../../../include/ActiveQt/QAxScriptEngine ../../../include/ActiveQt/QAxScript ../../../include/ActiveQt/QAxScriptManager ../../../include/ActiveQt/QAxSelect ../../../include/ActiveQt/QAxWidget +include(../../qt_install.pri) + +TARGET = QAxContainer + +!debug_and_release|build_pass { + CONFIG(debug, debug|release) { + TARGET = $$member(TARGET, 0)d + } +} + +CONFIG += qt warn_on staticlib +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/lib + +LIBS += -lole32 -loleaut32 +!wince*:LIBS += -luser32 -lgdi32 -ladvapi32 +win32-g++:LIBS += -luuid + +contains(QT_EDITION, OpenSource|Console) { + message( "You are not licensed to use ActiveQt." ) +} else { + HEADERS = ../control/qaxaggregated.h \ + qaxbase.h \ + qaxwidget.h \ + qaxobject.h \ + qaxscript.h \ + qaxselect.h \ + ../shared/qaxtypes.h + + SOURCES = qaxbase.cpp \ + qaxdump.cpp \ + qaxwidget.cpp \ + qaxobject.cpp \ + qaxscript.cpp \ + qaxscriptwrapper.cpp \ + qaxselect.cpp \ + ../shared/qaxtypes.cpp + + FORMS = qaxselect.ui +} diff --git a/src/activeqt/container/qaxbase.cpp b/src/activeqt/container/qaxbase.cpp new file mode 100644 index 0000000..1ec704a --- /dev/null +++ b/src/activeqt/container/qaxbase.cpp @@ -0,0 +1,4465 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the ActiveQt framework of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QAX_NO_CLASSINFO + +#ifndef UNICODE +#define UNICODE +#endif + +#define QT_CHECK_STATE + +#include "qaxobject.h" + +#ifndef QT_NO_WIN_ACTIVEQT + +#include <qfile.h> +#include <qwidget.h> + +#include <quuid.h> +#include <qhash.h> +#include <qset.h> +#include <qpair.h> +#include <qmetaobject.h> +#include <qsettings.h> + +#ifndef QT_NO_THREAD +# include <qmutex.h> +#endif + +#include <qt_windows.h> +#include <ocidl.h> +#include <ctype.h> + +#include "../shared/qaxtypes.h" + +QT_BEGIN_NAMESPACE + +/* + \internal + \class QAxMetaObject + + \brief The QAxMetaObject class stores extended information +*/ +struct QAxMetaObject : public QMetaObject +{ + QAxMetaObject() + { + d.data = 0; + d.stringdata = 0; + } + ~QAxMetaObject() + { + delete [] (int*)d.data; + delete [] (char*)d.stringdata; + } + + int numParameter(const QByteArray &prototype); + QByteArray paramType(const QByteArray &signature, int index, bool *out = 0); + QByteArray propertyType(const QByteArray &propertyName); + void parsePrototype(const QByteArray &prototype); + DISPID dispIDofName(const QByteArray &name, IDispatch *disp); + +private: + friend class MetaObjectGenerator; + // save information about QAxEventSink connections, and connect when found in cache + QList<QUuid> connectionInterfaces; + // DISPID -> signal name + QMap< QUuid, QMap<DISPID, QByteArray> > sigs; + // DISPID -> property changed signal name + QMap< QUuid, QMap<DISPID, QByteArray> > propsigs; + // DISPID -> property name + QMap< QUuid, QMap<DISPID, QByteArray> > props; + + // Prototype -> member info + QHash<QByteArray, QList<QByteArray> > memberInfo; + QMap<QByteArray, QByteArray> realPrototype; + + // DISPID cache + QHash<QByteArray, DISPID> dispIDs; +}; + +void QAxMetaObject::parsePrototype(const QByteArray &prototype) +{ + QByteArray realProto = realPrototype.value(prototype, prototype); + QByteArray parameters = realProto.mid(realProto.indexOf('(') + 1); + parameters.truncate(parameters.length() - 1); + + if (parameters.isEmpty()) { + memberInfo.insert(prototype, QList<QByteArray>()); + } else { + QList<QByteArray> plist = parameters.split(','); + memberInfo.insert(prototype, plist); + } +} + +inline QByteArray QAxMetaObject::propertyType(const QByteArray &propertyName) +{ + return realPrototype.value(propertyName); +} + +int QAxMetaObject::numParameter(const QByteArray &prototype) +{ + if (!memberInfo.contains(prototype)) + parsePrototype(prototype); + + return memberInfo.value(prototype).count(); +} + +QByteArray QAxMetaObject::paramType(const QByteArray &prototype, int index, bool *out) +{ + if (!memberInfo.contains(prototype)) + parsePrototype(prototype); + + if (out) + *out = false; + + QList<QByteArray> plist = memberInfo.value(prototype); + if (index > plist.count() - 1) + return QByteArray(); + + QByteArray param(plist.at(index)); + if (param.isEmpty()) + return QByteArray(); + + bool byRef = param.endsWith('&') || param.endsWith("**"); + if (byRef) { + param.truncate(param.length() - 1); + if (out) + *out = true; + } + + return param; +} + +inline DISPID QAxMetaObject::dispIDofName(const QByteArray &name, IDispatch *disp) +{ + DISPID dispid = dispIDs.value(name, DISPID_UNKNOWN); + if (dispid == DISPID_UNKNOWN) { + // get the Dispatch ID from the object + QString unicodeName = QLatin1String(name); + OLECHAR *names = (TCHAR*)unicodeName.utf16(); + disp->GetIDsOfNames(IID_NULL, &names, 1, LOCALE_USER_DEFAULT, &dispid); + if (dispid != DISPID_UNKNOWN) + dispIDs.insert(name, dispid); + } + return dispid; +} + + +static QHash<QString, QAxMetaObject*> mo_cache; +static QHash<QUuid, QMap<QByteArray, QList<QPair<QByteArray, int> > > > enum_cache; +static int mo_cache_ref = 0; +static QMutex cache_mutex; + + +static const char *const type_conversion[][2] = +{ + { "float", "double"}, + { "short", "int"}, + { "char", "int"}, + { "QList<int>", "QVariantList" }, + { "QList<uint>", "QVariantList" }, + { "QList<double>", "QVariantList" }, + { "QList<bool>", "QVariantList" }, + { "QList<QDateTime>", "QVariantList" }, + { "QList<qlonglong>", "QVariantList" }, + { 0, 0 } +}; + +/* + \internal + \class QAxEventSink + + \brief The QAxEventSink class implements the event sink for all + IConnectionPoints implemented in the COM object. +*/ + +class QAxEventSink : public IDispatch, public IPropertyNotifySink +{ +public: + QAxEventSink(QAxBase *com) + : cpoint(0), ciid(IID_NULL), combase(com), ref(1) + {} + virtual ~QAxEventSink() + { + Q_ASSERT(!cpoint); + } + + QUuid connectionInterface() const + { + return ciid; + } + QMap<DISPID, QByteArray> signalMap() const + { + return sigs; + } + QMap<DISPID, QByteArray> propertyMap() const + { + return props; + } + QMap<DISPID, QByteArray> propSignalMap() const + { + return propsigs; + } + + // add a connection + void advise(IConnectionPoint *cp, IID iid) + { + cpoint = cp; + cpoint->AddRef(); + ciid = iid; + cpoint->Advise((IUnknown*)(IDispatch*)this, &cookie); + } + + // disconnect from all connection points + void unadvise() + { + combase = 0; + if (cpoint) { + cpoint->Unadvise(cookie); + cpoint->Release(); + cpoint = 0; + } + } + + void addSignal(DISPID memid, const char *name) + { + QByteArray signalname = name; + int pi = signalname.indexOf('('); + int i = 0; + while (type_conversion[i][0]) { + int ti = pi; + int len = int(strlen(type_conversion[i][0])); + while ((ti = signalname.indexOf(type_conversion[i][0], ti)) != -1) + signalname.replace(ti, len, type_conversion[i][1]); + ++i; + } + + sigs.insert(memid, signalname); + QMap<DISPID,QByteArray>::ConstIterator it; + DISPID id = -1; + for (it = propsigs.constBegin(); it!= propsigs.constEnd(); ++it) { + if (it.value() == signalname) { + id = it.key(); + break; + } + } + if (id != -1) + propsigs.remove(id); + } + void addProperty(DISPID propid, const char *name, const char *signal) + { + props.insert(propid, name); + propsigs.insert(propid, signal); + } + + // IUnknown + unsigned long __stdcall AddRef() + { + return ref++; + } + unsigned long __stdcall Release() + { + if (!--ref) { + delete this; + return 0; + } + return ref; + } + HRESULT __stdcall QueryInterface(REFIID riid, void **ppvObject) + { + *ppvObject = 0; + if (riid == IID_IUnknown) + *ppvObject = (IUnknown*)(IDispatch*)this; + else if (riid == IID_IDispatch) + *ppvObject = (IDispatch*)this; + else if (riid == IID_IPropertyNotifySink) + *ppvObject = (IPropertyNotifySink*)this; + else if (ciid == riid) + *ppvObject = (IDispatch*)this; + else + return E_NOINTERFACE; + + AddRef(); + return S_OK; + } + + // IDispatch + HRESULT __stdcall GetTypeInfoCount(unsigned int *) { return E_NOTIMPL; } + HRESULT __stdcall GetTypeInfo(UINT, LCID, ITypeInfo **) { return E_NOTIMPL; } + HRESULT __stdcall GetIDsOfNames(const _GUID &, wchar_t **, unsigned int, unsigned long, long *) { return E_NOTIMPL; } + + HRESULT __stdcall Invoke(DISPID dispIdMember, + REFIID riid, + LCID, + WORD wFlags, + DISPPARAMS *pDispParams, + VARIANT*, + EXCEPINFO*, + UINT*) + { + // verify input + if (riid != IID_NULL) + return DISP_E_UNKNOWNINTERFACE; + if (!(wFlags & DISPATCH_METHOD)) + return DISP_E_MEMBERNOTFOUND; + if (!combase) + return E_UNEXPECTED; + + QByteArray signame = sigs.value(dispIdMember); + if (signame.isEmpty()) + return DISP_E_MEMBERNOTFOUND; + + QObject *qobject = combase->qObject(); + if (qobject->signalsBlocked()) + return S_OK; + + QAxMetaObject *axmeta = combase->internalMetaObject(); + const QMetaObject *meta = combase->metaObject(); + + int index = -1; + // emit the generic signal "as is" + if (signalHasReceivers(qobject, "signal(QString,int,void*)")) { + index = meta->indexOfSignal("signal(QString,int,void*)"); + Q_ASSERT(index != -1); + + QString nameString = QLatin1String(signame); + void *argv[] = {0, &nameString, &pDispParams->cArgs, &pDispParams->rgvarg}; + combase->qt_metacall(QMetaObject::InvokeMetaMethod, index, argv); + } + + HRESULT hres = S_OK; + + // get the signal information from the metaobject + index = -1; + if (signalHasReceivers(qobject, signame)) { + index = meta->indexOfSignal(signame); + Q_ASSERT(index != -1); + const QMetaMethod signal = meta->method(index); + Q_ASSERT(signal.methodType() == QMetaMethod::Signal); + Q_ASSERT(signame == signal.signature()); + // verify parameter count + int pcount = axmeta->numParameter(signame); + int argcount = pDispParams->cArgs; + if (pcount > argcount) + return DISP_E_PARAMNOTOPTIONAL; + else if (pcount < argcount) + return DISP_E_BADPARAMCOUNT; + + // setup parameters (no return values in signals) + bool ok = true; + void *static_argv[QAX_NUM_PARAMS + 1]; + void *static_argv_pointer[QAX_NUM_PARAMS + 1]; + QVariant static_varp[QAX_NUM_PARAMS + 1]; + + void **argv = 0; + void **argv_pointer = 0; // in case we need an additional level of indirection + QVariant *varp = 0; + + if (pcount) { + if (pcount <= QAX_NUM_PARAMS) { + argv = static_argv; + argv_pointer = static_argv_pointer; + varp = static_varp; + } else { + argv = new void*[pcount + 1]; + argv_pointer = new void*[pcount + 1]; + varp = new QVariant[pcount + 1]; + } + + argv[0] = 0; + argv_pointer[0] = 0; + } + + int p; + for (p = 0; p < pcount && ok; ++p) { + // map the VARIANT to the void* + QByteArray ptype = axmeta->paramType(signame, p); + varp[p + 1] = VARIANTToQVariant(pDispParams->rgvarg[pcount - p - 1], ptype); + argv_pointer[p + 1] = 0; + if (varp[p + 1].isValid()) { + if (varp[p + 1].type() == QVariant::UserType) { + argv[p + 1] = varp[p + 1].data(); + } else if (ptype == "QVariant") { + argv[p + 1] = varp + p + 1; + } else { + argv[p + 1] = const_cast<void*>(varp[p + 1].constData()); + if (ptype.endsWith("*")) { + argv_pointer[p + 1] = argv[p + 1]; + argv[p + 1] = argv_pointer + p + 1; + } + } + } else if (ptype == "QVariant") { + argv[p + 1] = varp + p + 1; + } else { + ok = false; + } + } + + if (ok) { + // emit the generated signal if everything went well + combase->qt_metacall(QMetaObject::InvokeMetaMethod, index, argv); + // update the VARIANT for references and free memory + for (p = 0; p < pcount; ++p) { + bool out; + QByteArray ptype = axmeta->paramType(signame, p, &out); + if (out) { + if (!QVariantToVARIANT(varp[p + 1], pDispParams->rgvarg[pcount - p - 1], ptype, out)) + ok = false; + } + } + } + + if (argv != static_argv) { + delete [] argv; + delete [] argv_pointer; + delete [] varp; + } + hres = ok ? S_OK : (ok ? DISP_E_MEMBERNOTFOUND : DISP_E_TYPEMISMATCH); + } + + return hres; + } + + QByteArray findProperty(DISPID dispID); + + // IPropertyNotifySink + HRESULT __stdcall OnChanged(DISPID dispID) + { + // verify input + if (dispID == DISPID_UNKNOWN || !combase) + return S_OK; + + const QMetaObject *meta = combase->metaObject(); + if (!meta) + return S_OK; + + QByteArray propname(findProperty(dispID)); + if (propname.isEmpty()) + return S_OK; + + QObject *qobject = combase->qObject(); + if (qobject->signalsBlocked()) + return S_OK; + + // emit the generic signal + int index = meta->indexOfSignal("propertyChanged(QString)"); + if (index != -1) { + QString propnameString = QString::fromLatin1(propname); + void *argv[] = {0, &propnameString}; + combase->qt_metacall(QMetaObject::InvokeMetaMethod, index, argv); + } + + QByteArray signame = propsigs.value(dispID); + if (signame.isEmpty()) + return S_OK; + + index = meta->indexOfSignal(signame); + if (index == -1) // bindable but not marked as bindable in typelib + return S_OK; + + // get the signal information from the metaobject + if (signalHasReceivers(qobject, signame)) { + index = meta->indexOfSignal(signame); + Q_ASSERT(index != -1); + // setup parameters + QVariant var = qobject->property(propname); + if (!var.isValid()) + return S_OK; + + const QMetaProperty metaProp = meta->property(meta->indexOfProperty(propname)); + void *argv[] = {0, var.data()}; + if (metaProp.type() == QVariant::LastType) + argv[1] = &var; + + // emit the "changed" signal + combase->qt_metacall(QMetaObject::InvokeMetaMethod, index, argv); + } + return S_OK; + } + HRESULT __stdcall OnRequestEdit(DISPID dispID) + { + if (dispID == DISPID_UNKNOWN || !combase) + return S_OK; + + QByteArray propname(findProperty(dispID)); + if (propname.isEmpty()) + return S_OK; + + return combase->propertyWritable(propname) ? S_OK : S_FALSE; + } + + static bool signalHasReceivers(QObject *qobject, const char *signalName) + { + Q_ASSERT(qobject); + return ((QAxObject*)qobject)->receivers(QByteArray::number(QSIGNAL_CODE) + signalName); + } + + IConnectionPoint *cpoint; + IID ciid; + ULONG cookie; + + QMap<DISPID, QByteArray> sigs; + QMap<DISPID, QByteArray> propsigs; + QMap<DISPID, QByteArray> props; + + QAxBase *combase; + long ref; +}; + +/* + \internal + \class QAxBasePrivate +*/ + +class QAxBasePrivate +{ +public: + QAxBasePrivate() + : useEventSink(true), useMetaObject(true), useClassInfo(true), + cachedMetaObject(false), initialized(false), tryCache(false), + ptr(0), disp(0), metaobj(0) + { + // protect initialization + QMutexLocker locker(&cache_mutex); + mo_cache_ref++; + + qRegisterMetaType<IUnknown*>("IUnknown*", &ptr); + qRegisterMetaType<IDispatch*>("IDispatch*", &disp); + } + + ~QAxBasePrivate() + { + Q_ASSERT(!ptr); + Q_ASSERT(!disp); + + // protect cleanup + QMutexLocker locker(&cache_mutex); + if (!--mo_cache_ref) { + qDeleteAll(mo_cache); + mo_cache.clear(); + } + + CoFreeUnusedLibraries(); + } + + inline IDispatch *dispatch() const + { + if (disp) + return disp; + + if (ptr) + ptr->QueryInterface(IID_IDispatch, (void**)&disp); + return disp; + } + + QString ctrl; + + QHash<QUuid, QAxEventSink*> eventSink; + uint useEventSink :1; + uint useMetaObject :1; + uint useClassInfo :1; + uint cachedMetaObject :1; + uint initialized :1; + uint tryCache :1; + + IUnknown *ptr; + mutable IDispatch *disp; + + QMap<QByteArray, bool> propWritable; + + inline QAxMetaObject *metaObject() + { + if (!metaobj) + metaobj = new QAxMetaObject; + return metaobj; + } + + mutable QMap<QString, LONG> verbs; + + QAxMetaObject *metaobj; +}; + + +QByteArray QAxEventSink::findProperty(DISPID dispID) +{ + // look up in cache, and fall back to + // type info for precompiled metaobjects + QByteArray propname(props.value(dispID)); + + if (!propname.isEmpty()) + return propname; + + IDispatch *dispatch = combase->d->dispatch(); + ITypeInfo *typeinfo = 0; + if (dispatch) + dispatch->GetTypeInfo(0, LOCALE_USER_DEFAULT, &typeinfo); + if (!typeinfo) + return propname; + + BSTR names; + UINT cNames; + typeinfo->GetNames(dispID, &names, 1, &cNames); + if (cNames) { + propname = QString::fromUtf16((const ushort *)names).toLatin1(); + SysFreeString(names); + } + typeinfo->Release(); + + QByteArray propsignal(propname + "Changed("); + const QMetaObject *mo = combase->metaObject(); + int index = mo->indexOfProperty(propname); + const QMetaProperty prop = mo->property(index); + propsignal += prop.typeName(); + propsignal += ")"; + addProperty(dispID, propname, propsignal); + + return propname; +} + +/*! + \class QAxBase + \brief The QAxBase class is an abstract class that provides an API + to initialize and access a COM object. + + \inmodule QAxContainer + + QAxBase is an abstract class that cannot be used directly, and is + instantiated through the subclasses QAxObject and QAxWidget. This + class provides the API to access the COM object directly + through its IUnknown implementation. If the COM object implements + the IDispatch interface, the properties and methods of that object + become available as Qt properties and slots. + + \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 0 + + Properties exposed by the object's IDispatch implementation can + be read and written through the property system provided by the + Qt Object Model (both subclasses are QObjects, so you can use + QObject::setProperty() and QObject::property()). Properties with + multiple parameters are not supported. + + \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 1 + + Write-functions for properties and other methods exposed by the + object's IDispatch implementation can be called directly using + dynamicCall(), or indirectly as slots connected to a signal. + + \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 2 + + Outgoing events supported by the COM object are emitted as + standard Qt signals. + + \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 3 + + QAxBase transparently converts between COM data types and the + equivalent Qt data types. Some COM types have no equivalent Qt data structure. + + Supported COM datatypes are listed in the first column of following table. + The second column is the Qt type that can be used with the QObject property + functions. The third column is the Qt type that is used in the prototype of + generated signals and slots for in-parameters, and the last column is the Qt + type that is used in the prototype of signals and slots for out-parameters. + \table + \header + \i COM type + \i Qt property + \i in-parameter + \i out-parameter + \row + \i VARIANT_BOOL + \i bool + \i bool + \i bool& + \row + \i BSTR + \i QString + \i const QString& + \i QString& + \row + \i char, short, int, long + \i int + \i int + \i int& + \row + \i uchar, ushort, uint, ulong + \i uint + \i uint + \i uint& + \row + \i float, double + \i double + \i double + \i double& + \row + \i DATE + \i QDateTime + \i const QDateTime& + \i QDateTime& + \row + \i CY + \i qlonglong + \i qlonglong + \i qlonglong& + \row + \i OLE_COLOR + \i QColor + \i const QColor& + \i QColor& + \row + \i SAFEARRAY(VARIANT) + \i QList\<QVariant\> + \i const QList\<QVariant\>& + \i QList\<QVariant\>& + \row + \i SAFEARRAY(int), SAFEARRAY(double), SAFEARRAY(Date) + \i QList\<QVariant\> + \i const QList\<QVariant\>& + \i QList\<QVariant\>& + \row + \i SAFEARRAY(BYTE) + \i QByteArray + \i const QByteArray& + \i QByteArray& + \row + \i SAFEARRAY(BSTR) + \i QStringList + \i const QStringList& + \i QStringList& + \row + \i VARIANT + \i type-dependent + \i const QVariant& + \i QVariant& + \row + \i IFontDisp* + \i QFont + \i const QFont& + \i QFont& + \row + \i IPictureDisp* + \i QPixmap + \i const QPixmap& + \i QPixmap& + \row + \i IDispatch* + \i QAxObject* + \i \c QAxBase::asVariant() + \i QAxObject* (return value) + \row + \i IUnknown* + \i QAxObject* + \i \c QAxBase::asVariant() + \i QAxObject* (return value) + \row + \i SCODE, DECIMAL + \i \e unsupported + \i \e unsupported + \i \e unsupported + \row + \i VARIANT* (Since Qt 4.5) + \i \e unsupported + \i \e QVariant& + \i \e QVariant& + \endtable + + Supported are also enumerations, and typedefs to supported types. + + To call the methods of a COM interface described by the following IDL + + \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 4 + + use the QAxBase API like this: + + \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 5 + + Note that the QList the object should fill has to be provided as an + element in the parameter list of \l{QVariant}s. + + If you need to access properties or pass parameters of + unsupported datatypes you must access the COM object directly + through its \c IDispatch implementation or other interfaces. + Those interfaces can be retrieved through queryInterface(). + + \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 6 + + To get the definition of the COM interfaces you will have to use the header + files provided with the component you want to use. Some compilers can also + import type libraries using the #import compiler directive. See the component + documentation to find out which type libraries you have to import, and how to use + them. + + If you need to react to events that pass parameters of unsupported + datatypes you can use the generic signal that delivers the event + data as provided by the COM event. + + \sa QAxObject, QAxWidget, QAxScript, {ActiveQt Framework} +*/ + +/*! + \typedef QAxBase::PropertyBag + + A QMap<QString,QVariant> that can store properties as name:value pairs. +*/ + +/*! + Creates a QAxBase object that wraps the COM object \a iface. If \a + iface is 0 (the default), use setControl() to instantiate a COM + object. +*/ +QAxBase::QAxBase(IUnknown *iface) +{ + d = new QAxBasePrivate(); + d->ptr = iface; + if (d->ptr) { + d->ptr->AddRef(); + d->initialized = true; + } +#if defined(Q_OS_WINCE) + CoInitializeEx(0, COINIT_MULTITHREADED); +#endif +} + +/*! + Shuts down the COM object and destroys the QAxBase object. + + \sa clear() +*/ +QAxBase::~QAxBase() +{ +#if defined(Q_OS_WINCE) + CoUninitialize(); +#endif + + clear(); + + delete d; + d = 0; +} + +/*! + \internal + + Used by subclasses generated with dumpcpp to balance reference count. +*/ +void QAxBase::internalRelease() +{ + if (d->ptr) + d->ptr->Release(); +} + +/*! + \internal + + Used by subclasses generated with dumpcpp to implement cast-operators. +*/ +void QAxBase::initializeFrom(QAxBase *that) +{ + if (d->ptr) + return; + + d->ptr = that->d->ptr; + if (d->ptr) { + d->ptr->AddRef(); + d->initialized = true; + } +} + + +QAxMetaObject *QAxBase::internalMetaObject() const +{ + return d->metaObject(); +} + +/*! + \property QAxBase::control + \brief the name of the COM object wrapped by this QAxBase object. + + Setting this property initilializes 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 doc/src/snippets/code/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 doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 8 + + The slowest, but easiest way to use is to use the control's full + name, e.g. + + \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 9 + + It is also possible to initialize the object from a file, e.g. + + \snippet doc/src/snippets/code/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 + \i To initialize the control on a different machine use the following + pattern: + + \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 11 + + \i To initialize a licensed control use the following pattern: + + \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 12 + + \i To connect to an already running object use the following pattern: + + \snippet doc/src/snippets/code/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 doc/src/snippets/code/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. +*/ +bool QAxBase::setControl(const QString &c) +{ + if (c.toLower() == d->ctrl.toLower()) + return !d->ctrl.isEmpty(); + + QString search = c; + // don't waste time for DCOM requests + int dcomIDIndex = search.indexOf(QLatin1String("/{")); + if ((dcomIDIndex == -1 || dcomIDIndex != search.length()-39) && !search.endsWith(QLatin1String("}&"))) { + QUuid uuid(search); + if (uuid.isNull()) { + CLSID clsid; + HRESULT res = CLSIDFromProgID((WCHAR*)c.utf16(), &clsid); + if (res == S_OK) + search = QUuid(clsid).toString(); + else { + QSettings controls(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Classes\\"), QSettings::NativeFormat); + search = controls.value(c + QLatin1String("/CLSID/Default")).toString(); + if (search.isEmpty()) { + controls.beginGroup(QLatin1String("/CLSID")); + QStringList clsids = controls.childGroups(); + for (QStringList::Iterator it = clsids.begin(); it != clsids.end(); ++it) { + QString clsid = *it; + QString name = controls.value(clsid + QLatin1String("/Default")).toString(); + if (name == c) { + search = clsid; + break; + } + } + controls.endGroup(); + } + } + } + if (search.isEmpty()) + search = c; + } + + if (search.toLower() == d->ctrl.toLower()) + return !d->ctrl.isEmpty(); + + clear(); + d->ctrl = search; + + d->tryCache = true; + if (!initialize(&d->ptr)) + d->initialized = true; + if (isNull()) { + qWarning("QAxBase::setControl: requested control %s could not be instantiated", c.toLatin1().data()); + clear(); + return false; + } + return true; +} + +QString QAxBase::control() const +{ + return d->ctrl; +} + +/*! + Disables the event sink implementation for this ActiveX container. + If you don't intend to listen to the ActiveX control's events use + this function to speed up the meta object generation. + + Some ActiveX controls might be unstable when connected to an event + sink. To get OLE events you must use standard COM methods to + register your own event sink. Use queryInterface() to get access + to the raw COM object. + + Note that this function should be called immediately after + construction of the object. +*/ +void QAxBase::disableEventSink() +{ + d->useEventSink = false; +} + +/*! + Disables the meta object generation for this ActiveX container. + This also disables the event sink and class info generation. If + you don't intend to use the Qt meta object implementation call + this function to speed up instantiation of the control. You will + still be able to call the object through \l dynamicCall(), but + signals, slots and properties will not be available with QObject + APIs. + + Some ActiveX controls might be unstable when used with OLE + automation. Use standard COM methods to use those controls through + the COM interfaces provided by queryInterface(). + + Note that this function must be called immediately after + construction of the object. +*/ +void QAxBase::disableMetaObject() +{ + d->useMetaObject = false; + d->useEventSink = false; + d->useClassInfo = false; +} + +/*! + Disables the class info generation for this ActiveX container. If + you don't require any class information about the ActiveX control + use this function to speed up the meta object generation. + + Note that this function must be called immediately after + construction of the object +*/ +void QAxBase::disableClassInfo() +{ + d->useClassInfo = false; +} + +/*! + Disconnects and destroys the COM object. + + If you reimplement this function you must also reimplement the + destructor to call clear(), and call this implementation at the + end of your clear() function. +*/ +void QAxBase::clear() +{ + QHash<QUuid, QAxEventSink*>::Iterator it = d->eventSink.begin(); + while (it != d->eventSink.end()) { + QAxEventSink *eventSink = it.value(); + ++it; + if (eventSink) { + eventSink->unadvise(); + eventSink->Release(); + } + } + d->eventSink.clear(); + if (d->disp) { + d->disp->Release(); + d->disp = 0; + } + if (d->ptr) { + d->ptr->Release(); + d->ptr = 0; + d->initialized = false; + } + + d->ctrl.clear(); + + if (!d->cachedMetaObject) + delete d->metaobj; + d->metaobj = 0; +} + +/*! + \since 4.1 + + Returns the list of verbs that the COM object can execute. If + the object does not implement IOleObject, or does not support + any verbs, then this function returns an empty stringlist. + + Note that the OLE default verbs (OLEIVERB_SHOW etc) are not + included in the list. +*/ +QStringList QAxBase::verbs() const +{ + if (!d->ptr) + return QStringList(); + + if (d->verbs.isEmpty()) { + IOleObject *ole = 0; + d->ptr->QueryInterface(IID_IOleObject, (void**)&ole); + if (ole) { + IEnumOLEVERB *enumVerbs = 0; + ole->EnumVerbs(&enumVerbs); + if (enumVerbs) { + enumVerbs->Reset(); + ULONG c; + OLEVERB verb; + while (enumVerbs->Next(1, &verb, &c) == S_OK) { + if (!verb.lpszVerbName) + continue; + QString verbName = QString::fromUtf16((const ushort *)verb.lpszVerbName); + if (!verbName.isEmpty()) + d->verbs.insert(verbName, verb.lVerb); + } + enumVerbs->Release(); + } + ole->Release(); + } + } + + return d->verbs.keys(); +} + +/*! + \internal +*/ + +long QAxBase::indexOfVerb(const QString &verb) const +{ + return d->verbs.value(verb); +} + +/*! + This virtual function is called by setControl() and creates the + requested COM object. \a ptr is set to the object's IUnknown + implementation. The function returns true if the object + initialization succeeded; otherwise the function returns false. + + The default implementation interprets the string returned by + control(), and calls initializeRemote(), initializeLicensed() + or initializeActive() if the string matches the respective + patterns. If control() is the name of an existing file, + initializeFromFile() is called. If no pattern is matched, or + if remote or licensed initialization fails, CoCreateInstance + is used directly to create the object. + + See the \l control property documentation for details about + supported patterns. + + The interface returned in \a ptr must be referenced exactly once + when this function returns. The interface provided by e.g. + CoCreateInstance is already referenced, and there is no need to + reference it again. +*/ +bool QAxBase::initialize(IUnknown **ptr) +{ + if (*ptr || control().isEmpty()) + return false; + + *ptr = 0; + + bool res = false; + + const QString ctrl(d->ctrl); + if (ctrl.contains(QLatin1String("/{"))) // DCOM request + res = initializeRemote(ptr); + else if (ctrl.contains(QLatin1String("}:"))) // licensed control + res = initializeLicensed(ptr); + else if (ctrl.contains(QLatin1String("}&"))) // running object + res = initializeActive(ptr); + else if (QFile::exists(ctrl)) // existing file + res = initializeFromFile(ptr); + + if (!res) { // standard + HRESULT hres = CoCreateInstance(QUuid(ctrl), 0, CLSCTX_SERVER, IID_IUnknown, (void**)ptr); + res = S_OK == hres; +#ifndef QT_NO_DEBUG + if (!res) + qErrnoWarning(hres, "CoCreateInstance failure"); +#endif + } + + return *ptr != 0; +} + +/*! + Creates an instance of a licensed control, and returns the IUnknown interface + to the object in \a ptr. This functions returns true if successful, otherwise + returns false. + + This function is called by initialize() if the control string contains the + substring "}:". The license key needs to follow this substring. + + \sa initialize() +*/ +bool QAxBase::initializeLicensed(IUnknown** ptr) +{ + int at = control().lastIndexOf(QLatin1String("}:")); + + QString clsid(control().left(at)); + QString key(control().mid(at+2)); + + IClassFactory *factory = 0; + CoGetClassObject(QUuid(clsid), CLSCTX_SERVER, 0, IID_IClassFactory, (void**)&factory); + if (!factory) + return false; + initializeLicensedHelper(factory, key, ptr); + factory->Release(); + + return *ptr != 0; +} + +/* \internal + Called by initializeLicensed and initializedRemote to create an object + via IClassFactory2. +*/ +bool QAxBase::initializeLicensedHelper(void *f, const QString &key, IUnknown **ptr) +{ + IClassFactory *factory = (IClassFactory*)f; + IClassFactory2 *factory2 = 0; + factory->QueryInterface(IID_IClassFactory2, (void**)&factory2); + if (factory2) { + BSTR bkey = QStringToBSTR(key); + HRESULT hres = factory2->CreateInstanceLic(0, 0, IID_IUnknown, bkey, (void**)ptr); + SysFreeString(bkey); +#ifdef QT_DEBUG + LICINFO licinfo; + licinfo.cbLicInfo = sizeof(LICINFO); + factory2->GetLicInfo(&licinfo); + + if (hres != S_OK) { + SetLastError(hres); + qErrnoWarning("CreateInstanceLic failed"); + if (!licinfo.fLicVerified) { + qWarning("Wrong license key specified, and machine is not fully licensed."); + } else if (licinfo.fRuntimeKeyAvail) { + BSTR licenseKey; + factory2->RequestLicKey(0, &licenseKey); + QString qlicenseKey = QString::fromUtf16((const ushort *)licenseKey); + SysFreeString(licenseKey); + qWarning("Use license key is '%s' to create object on unlicensed machine.", + qlicenseKey.toLatin1().constData()); + } + } else if (licinfo.fLicVerified) { + qWarning("Machine is fully licensed for '%s'", control().toLatin1().constData()); + if (licinfo.fRuntimeKeyAvail) { + BSTR licenseKey; + factory2->RequestLicKey(0, &licenseKey); + QString qlicenseKey = QString::fromUtf16((const ushort *)licenseKey); + SysFreeString(licenseKey); + + if (qlicenseKey != key) + qWarning("Runtime license key is '%s'", qlicenseKey.toLatin1().constData()); + } + } +#else + Q_UNUSED(hres); +#endif + factory2->Release(); + } else { // give it a shot without license + factory->CreateInstance(0, IID_IUnknown, (void**)ptr); + } + return *ptr != 0; +} + + +/*! + Connects to an active instance running on the current machine, and returns the + IUnknown interface to the running object in \a ptr. This function returns true + if successful, otherwise returns false. + + This function is called by initialize() if the control string contains the + substring "}&". + + \sa initialize() +*/ +bool QAxBase::initializeActive(IUnknown** ptr) +{ +#if defined(Q_OS_WINCE) + Q_UNUSED(ptr); + return false; +#else + int at = control().lastIndexOf(QLatin1String("}&")); + QString clsid(control().left(at)); + + GetActiveObject(QUuid(clsid), 0, ptr); + + return *ptr != 0; +#endif +} + +#ifdef Q_CC_GNU +# ifndef OLEPENDER_NONE +# define OLERENDER_NONE 0 +# endif +#endif + +/*! + Creates the COM object handling the filename in the control property, and + returns the IUnknown interface to the object in \a ptr. This function returns + true if successful, otherwise returns false. + + This function is called by initialize() if the control string is the name of + an existing file. + + \sa initialize() +*/ +bool QAxBase::initializeFromFile(IUnknown** ptr) +{ +#if defined(Q_OS_WINCE) + Q_UNUSED(ptr); + return false; +#else + IStorage *storage = 0; + ILockBytes * bytes = 0; + HRESULT hres = ::CreateILockBytesOnHGlobal(0, TRUE, &bytes); + hres = ::StgCreateDocfileOnILockBytes(bytes, STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, &storage); + + hres = OleCreateFromFile(CLSID_NULL, reinterpret_cast<const wchar_t*>(control().utf16()), IID_IUnknown, OLERENDER_NONE, 0, 0, storage, (void**)ptr); + + storage->Release(); + bytes->Release(); + + return hres == S_OK; +#endif +} + + +// There seams to be a naming problem in mingw headers +#ifdef Q_CC_GNU +#ifndef COAUTHIDENTITY +#define COAUTHIDENTITY AUTH_IDENTITY +#endif +#endif + + +/*! + Creates the instance on a remote server, and returns the IUnknown interface + to the object in \a ptr. This function returns true if successful, otherwise + returns false. + + This function is called by initialize() if the control string contains the + substring "/{". The information about the remote machine needs to be provided + in front of the substring. + + \sa initialize() +*/ +bool QAxBase::initializeRemote(IUnknown** ptr) +{ + int at = control().lastIndexOf(QLatin1String("/{")); + + QString server(control().left(at)); + QString clsid(control().mid(at+1)); + + QString user; + QString domain; + QString passwd; + QString key; + + at = server.indexOf(QChar::fromLatin1('@')); + if (at != -1) { + user = server.left(at); + server = server.mid(at+1); + + at = user.indexOf(QChar::fromLatin1(':')); + if (at != -1) { + passwd = user.mid(at+1); + user = user.left(at); + } + at = user.indexOf(QChar::fromLatin1('/')); + if (at != -1) { + domain = user.left(at); + user = user.mid(at+1); + } + } + + at = clsid.lastIndexOf(QLatin1String("}:")); + if (at != -1) { + key = clsid.mid(at+2); + clsid = clsid.left(at); + } + + d->ctrl = server + QChar::fromLatin1('/') + clsid; + if (!key.isEmpty()) + d->ctrl = d->ctrl + QChar::fromLatin1(':') + key; + + COAUTHIDENTITY authIdentity; + authIdentity.UserLength = user.length(); + authIdentity.User = authIdentity.UserLength ? (ushort*)user.utf16() : 0; + authIdentity.DomainLength = domain.length(); + authIdentity.Domain = authIdentity.DomainLength ? (ushort*)domain.utf16() : 0; + authIdentity.PasswordLength = passwd.length(); + authIdentity.Password = authIdentity.PasswordLength ? (ushort*)passwd.utf16() : 0; + authIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; + + COAUTHINFO authInfo; + authInfo.dwAuthnSvc = RPC_C_AUTHN_WINNT; + authInfo.dwAuthzSvc = RPC_C_AUTHZ_NONE; + authInfo.pwszServerPrincName = 0; + authInfo.dwAuthnLevel = RPC_C_AUTHN_LEVEL_DEFAULT; + authInfo.dwImpersonationLevel = RPC_C_IMP_LEVEL_IMPERSONATE; + authInfo.pAuthIdentityData = &authIdentity; + authInfo.dwCapabilities = 0; + + COSERVERINFO serverInfo; + serverInfo.dwReserved1 = 0; + serverInfo.dwReserved2 = 0; + serverInfo.pAuthInfo = &authInfo; + serverInfo.pwszName = (WCHAR*)server.utf16(); + + IClassFactory *factory = 0; + HRESULT res = CoGetClassObject(QUuid(clsid), CLSCTX_REMOTE_SERVER, &serverInfo, IID_IClassFactory, (void**)&factory); + if (factory) { + if (!key.isEmpty()) + initializeLicensedHelper(factory, key, ptr); + else + res = factory->CreateInstance(0, IID_IUnknown, (void**)ptr); + factory->Release(); + } +#ifndef QT_NO_DEBUG + if (res != S_OK) + qErrnoWarning(res, "initializeRemote Failed"); +#endif + + return res == S_OK; +} + +/*! + Requests the interface \a uuid from the COM object and sets the + value of \a iface to the provided interface, or to 0 if the + requested interface could not be provided. + + Returns the result of the QueryInterface implementation of the COM object. + + \sa control +*/ +long QAxBase::queryInterface(const QUuid &uuid, void **iface) const +{ + *iface = 0; + if (!d->ptr) { + ((QAxBase*)this)->initialize(&d->ptr); + d->initialized = true; + } + + if (d->ptr && !uuid.isNull()) + return d->ptr->QueryInterface(uuid, iface); + + return E_NOTIMPL; +} + +class MetaObjectGenerator +{ +public: + MetaObjectGenerator(QAxBase *ax, QAxBasePrivate *dptr); + MetaObjectGenerator(ITypeLib *typelib, ITypeInfo *typeinfo); + ~MetaObjectGenerator(); + + QMetaObject *metaObject(const QMetaObject *parentObject, const QByteArray &className = QByteArray()); + + void readClassInfo(); + void readEnumInfo(); + void readInterfaceInfo(); + void readFuncsInfo(ITypeInfo *typeinfo, ushort nFuncs); + void readVarsInfo(ITypeInfo *typeinfo, ushort nVars); + void readEventInfo(); + void readEventInterface(ITypeInfo *eventinfo, IConnectionPoint *cpoint); + + inline void addClassInfo(const char *key, const char *value) + { + classinfo_list.insert(key, value); + } + +private: + void init(); + + + QMetaObject *tryCache(); + + QByteArray createPrototype(FUNCDESC *funcdesc, ITypeInfo *typeinfo, const QList<QByteArray> &names, + QByteArray &type, QList<QByteArray> ¶meters); + + QByteArray usertypeToString(const TYPEDESC &tdesc, ITypeInfo *info, const QByteArray &function); + QByteArray guessTypes(const TYPEDESC &tdesc, ITypeInfo *info, const QByteArray &function); + + // ### from qmetaobject.cpp + enum ProperyFlags { + Invalid = 0x00000000, + Readable = 0x00000001, + Writable = 0x00000002, + Resettable = 0x00000004, + EnumOrFlag = 0x00000008, + StdCppSet = 0x00000100, +// Override = 0x00000200, + Designable = 0x00001000, + ResolveDesignable = 0x00002000, + Scriptable = 0x00004000, + ResolveScriptable = 0x00008000, + Stored = 0x00010000, + ResolveStored = 0x00020000, + Editable = 0x00040000, + ResolveEditable = 0x00080000, + User = 0x00100000, + ResolveUser = 0x00200000, + // And our own - don't use the upper byte, as it's used for the property type + RequestingEdit = 0x00400000, + Bindable = 0x00800000 + }; + enum MemberFlags { + AccessPrivate = 0x00, + AccessProtected = 0x01, + AccessPublic = 0x02, + MemberMethod = 0x00, + MemberSignal = 0x04, + MemberSlot = 0x08, + MemberCompatibility = 0x10, + MemberCloned = 0x20, + MemberScriptable = 0x40, + }; + + inline QList<QByteArray> paramList(const QByteArray &proto) + { + QByteArray prototype(proto); + QByteArray parameters = prototype.mid(prototype.indexOf('(') + 1); + parameters.truncate(parameters.length() - 1); + + QList<QByteArray> plist = parameters.split(','); + return plist; + } + + inline QByteArray replaceType(const QByteArray &type) + { + int i = 0; + while (type_conversion[i][0]) { + int len = int(strlen(type_conversion[i][0])); + int ti; + if ((ti = type.indexOf(type_conversion[i][0])) != -1) { + QByteArray rtype(type); + rtype.replace(ti, len, type_conversion[i][1]); + return rtype; + } + ++i; + } + return type; + } + + QByteArray replacePrototype(const QByteArray &prototype) + { + QByteArray proto(prototype); + + QList<QByteArray> plist = paramList(prototype); + for (int p = 0; p < plist.count(); ++p) { + QByteArray param(plist.at(p)); + if (param != replaceType(param)) { + int type = 0; + while (type_conversion[type][0]) { + int paren = proto.indexOf('('); + while ((paren = proto.indexOf(type_conversion[type][0])) != -1) { + proto.replace(paren, qstrlen(type_conversion[type][0]), type_conversion[type][1]); + } + ++type; + } + break; + } + } + + return proto; + } + + QMap<QByteArray, QByteArray> classinfo_list; + + inline bool hasClassInfo(const char *key) + { + return classinfo_list.contains(key); + } + + struct Method { + Method() : flags(0) + {} + QByteArray type; + QByteArray parameters; + int flags; + QByteArray realPrototype; + }; + QMap<QByteArray, Method> signal_list; + inline void addSignal(const QByteArray &prototype, const QByteArray ¶meters) + { + QByteArray proto(replacePrototype(prototype)); + + Method &signal = signal_list[proto]; + signal.type = 0; + signal.parameters = parameters; + signal.flags = QMetaMethod::Public | MemberSignal; + if (proto != prototype) + signal.realPrototype = prototype; + } + + void addChangedSignal(const QByteArray &function, const QByteArray &type, int memid); + + inline bool hasSignal(const QByteArray &prototype) + { + return signal_list.contains(prototype); + } + + QMap<QByteArray, Method> slot_list; + inline void addSlot(const QByteArray &type, const QByteArray &prototype, const QByteArray ¶meters, int flags = QMetaMethod::Public) + { + QByteArray proto = replacePrototype(prototype); + + Method &slot = slot_list[proto]; + slot.type = replaceType(type); + slot.parameters = parameters; + slot.flags = flags | MemberSlot; + if (proto != prototype) + slot.realPrototype = prototype; + } + + void addSetterSlot(const QByteArray &property); + + inline bool hasSlot(const QByteArray &prototype) + { + return slot_list.contains(prototype); + } + + struct Property { + Property() : typeId(0) + {} + QByteArray type; + uint typeId; + QByteArray realType; + }; + QMap<QByteArray, Property> property_list; + void addProperty(const QByteArray &type, const QByteArray &name, uint flags) + { + Property &prop = property_list[name]; + if (!type.isEmpty() && type != "HRESULT") { + prop.type = replaceType(type); + if (prop.type != type) + prop.realType = type; + } + if (flags & Writable) + flags |= Stored; + prop.typeId |= flags; + QVariant::Type vartype = QVariant::nameToType(prop.type); + switch(vartype) { + case QVariant::Invalid: + if (prop.type == "QVariant") { + prop.typeId |= 0xff << 24; + break; + } + // fall through + case QVariant::UserType: + if (QMetaType::type(prop.type) == -1) + qWarning("QAxBase: Unsupported property type: %s", prop.type.data()); + break; + default: + prop.typeId |= vartype << 24; + break; + } + } + + inline bool hasProperty(const QByteArray &name) + { + return property_list.contains(name); + } + + inline QByteArray propertyType(const QByteArray &name) + { + return property_list.value(name).type; + } + + QMap<QByteArray, QList<QPair<QByteArray, int> > > enum_list; + inline void addEnumValue(const QByteArray &enumname, const QByteArray &key, int value) + { + enum_list[enumname].append(QPair<QByteArray, int>(key, value)); + } + + inline bool hasEnum(const QByteArray &enumname) + { + return enum_list.contains(enumname); + } + + QAxBase *that; + QAxBasePrivate *d; + + IDispatch *disp; + ITypeInfo *dispInfo; + ITypeInfo *classInfo; + ITypeLib *typelib; + QByteArray current_typelib; + + QSettings iidnames; + QString cacheKey; + QByteArray debugInfo; + + QUuid iid_propNotifySink; + + friend QMetaObject *qax_readClassInfo(ITypeLib *typeLib, ITypeInfo *classInfo, const QMetaObject *parentObject); +}; + +QMetaObject *qax_readEnumInfo(ITypeLib *typeLib, const QMetaObject *parentObject) +{ + MetaObjectGenerator generator(typeLib, 0); + + generator.readEnumInfo(); + return generator.metaObject(parentObject, "EnumInfo"); +} + +QMetaObject *qax_readInterfaceInfo(ITypeLib *typeLib, ITypeInfo *typeInfo, const QMetaObject *parentObject) +{ + MetaObjectGenerator generator(typeLib, typeInfo); + + QString className; + BSTR bstr; + if (S_OK != typeInfo->GetDocumentation(-1, &bstr, 0, 0, 0)) + return 0; + + className = QString::fromUtf16((const ushort *)bstr); + SysFreeString(bstr); + + generator.readEnumInfo(); + generator.readFuncsInfo(typeInfo, 0); + generator.readVarsInfo(typeInfo, 0); + + return generator.metaObject(parentObject, className.toLatin1()); +} + +QMetaObject *qax_readClassInfo(ITypeLib *typeLib, ITypeInfo *classInfo, const QMetaObject *parentObject) +{ + MetaObjectGenerator generator(typeLib, 0); + generator.addSignal("exception(int,QString,QString,QString)", "code,source,disc,help"); + generator.addSignal("propertyChanged(QString)", "name"); + + QString className; + BSTR bstr; + if (S_OK != classInfo->GetDocumentation(-1, &bstr, 0, 0, 0)) + return 0; + + className = QString::fromUtf16((const ushort *)bstr); + SysFreeString(bstr); + + generator.readEnumInfo(); + + TYPEATTR *typeattr; + classInfo->GetTypeAttr(&typeattr); + if (typeattr) { + int nInterfaces = typeattr->cImplTypes; + classInfo->ReleaseTypeAttr(typeattr); + + for (int index = 0; index < nInterfaces; ++index) { + HREFTYPE refType; + if (S_OK != classInfo->GetRefTypeOfImplType(index, &refType)) + continue; + + int flags = 0; + classInfo->GetImplTypeFlags(index, &flags); + if (flags & IMPLTYPEFLAG_FRESTRICTED) + continue; + + ITypeInfo *interfaceInfo = 0; + classInfo->GetRefTypeInfo(refType, &interfaceInfo); + if (!interfaceInfo) + continue; + + interfaceInfo->GetDocumentation(-1, &bstr, 0, 0, 0); + QString interfaceName = QString::fromUtf16((const ushort *)bstr); + SysFreeString(bstr); + QByteArray key; + + TYPEATTR *typeattr = 0; + interfaceInfo->GetTypeAttr(&typeattr); + + if (flags & IMPLTYPEFLAG_FSOURCE) { + if (typeattr && !(typeattr->wTypeFlags & TYPEFLAG_FHIDDEN)) + key = "Event Interface " + QByteArray::number(index); + generator.readEventInterface(interfaceInfo, 0); + } else { + if (typeattr && !(typeattr->wTypeFlags & TYPEFLAG_FHIDDEN)) + key = "Interface " + QByteArray::number(index); + generator.readFuncsInfo(interfaceInfo, 0); + generator.readVarsInfo(interfaceInfo, 0); + } + if (!key.isEmpty()) + generator.addClassInfo(key.data(), interfaceName.toLatin1()); + + if (typeattr) + interfaceInfo->ReleaseTypeAttr(typeattr); + interfaceInfo->Release(); + } + } + + return generator.metaObject(parentObject, className.toLatin1()); +} + +MetaObjectGenerator::MetaObjectGenerator(QAxBase *ax, QAxBasePrivate *dptr) +: that(ax), d(dptr), disp(0), dispInfo(0), classInfo(0), typelib(0), + iidnames(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Classes"), QSettings::NativeFormat) +{ + init(); +} + +MetaObjectGenerator::MetaObjectGenerator(ITypeLib *tlib, ITypeInfo *tinfo) +: that(0), d(0), disp(0), dispInfo(tinfo), classInfo(0), typelib(tlib), + iidnames(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Classes"), QSettings::NativeFormat) +{ + init(); + + if (dispInfo) + dispInfo->AddRef(); + if (typelib) { + typelib->AddRef(); + BSTR bstr; + typelib->GetDocumentation(-1, &bstr, 0, 0, 0); + current_typelib = QString::fromUtf16((const ushort *)bstr).toLatin1(); + SysFreeString(bstr); + } + readClassInfo(); +} + +void MetaObjectGenerator::init() +{ + if (d) + disp = d->dispatch(); + + iid_propNotifySink = IID_IPropertyNotifySink; + + addSignal("signal(QString,int,void*)", "name,argc,argv"); + addSignal("exception(int,QString,QString,QString)", "code,source,disc,help"); + addSignal("propertyChanged(QString)", "name"); + if (d || dispInfo) { + addProperty("QString", "control", Readable|Writable|Designable|Scriptable|Stored|Editable|StdCppSet); + } +} + +MetaObjectGenerator::~MetaObjectGenerator() +{ + if (dispInfo) dispInfo->Release(); + if (classInfo) classInfo->Release(); + if (typelib) typelib->Release(); +} + +bool qax_dispatchEqualsIDispatch = true; +QList<QByteArray> qax_qualified_usertypes; + +QByteArray MetaObjectGenerator::usertypeToString(const TYPEDESC &tdesc, ITypeInfo *info, const QByteArray &function) +{ + HREFTYPE usertype = tdesc.hreftype; + if (tdesc.vt != VT_USERDEFINED) + return 0; + + QByteArray typeName; + ITypeInfo *usertypeinfo = 0; + info->GetRefTypeInfo(usertype, &usertypeinfo); + if (usertypeinfo) { + ITypeLib *usertypelib = 0; + UINT index; + usertypeinfo->GetContainingTypeLib(&usertypelib, &index); + if (usertypelib) { + // get type library name + BSTR typelibname = 0; + usertypelib->GetDocumentation(-1, &typelibname, 0, 0, 0); + QByteArray typeLibName = QString::fromUtf16((const ushort *)typelibname).toLatin1(); + SysFreeString(typelibname); + + // get type name + BSTR usertypename = 0; + usertypelib->GetDocumentation(index, &usertypename, 0, 0, 0); + QByteArray userTypeName = QString::fromUtf16((const ushort *)usertypename).toLatin1(); + SysFreeString(usertypename); + + if (hasEnum(userTypeName)) // known enum? + typeName = userTypeName; + else if (userTypeName == "OLE_COLOR" || userTypeName == "VB_OLE_COLOR") + typeName = "QColor"; + else if (userTypeName == "IFontDisp" || userTypeName == "IFontDisp*" || userTypeName == "IFont" || userTypeName == "IFont*") + typeName = "QFont"; + else if (userTypeName == "Picture" || userTypeName == "Picture*") + typeName = "QPixmap"; + + if (typeName.isEmpty()) { + TYPEATTR *typeattr = 0; + usertypeinfo->GetTypeAttr(&typeattr); + if (typeattr) { + switch(typeattr->typekind) { + case TKIND_ALIAS: + userTypeName = guessTypes(typeattr->tdescAlias, usertypeinfo, function); + break; + case TKIND_DISPATCH: + case TKIND_COCLASS: + if (qax_dispatchEqualsIDispatch) { + userTypeName = "IDispatch"; + } else { + if (typeLibName != current_typelib) + userTypeName = typeLibName + "::" + userTypeName; + if (!qax_qualified_usertypes.contains(userTypeName)) + qax_qualified_usertypes << userTypeName; + } + break; + case TKIND_ENUM: + if (typeLibName != current_typelib) + userTypeName = typeLibName + "::" + userTypeName; + if (!qax_qualified_usertypes.contains("enum " + userTypeName)) + qax_qualified_usertypes << "enum " + userTypeName; + break; + case TKIND_INTERFACE: + if (typeLibName != current_typelib) + userTypeName = typeLibName + "::" + userTypeName; + if (!qax_qualified_usertypes.contains(userTypeName)) + qax_qualified_usertypes << userTypeName; + break; + case TKIND_RECORD: + if (!qax_qualified_usertypes.contains("struct " + userTypeName)) + qax_qualified_usertypes << "struct "+ userTypeName; + break; + default: + break; + } + } + + usertypeinfo->ReleaseTypeAttr(typeattr); + typeName = userTypeName; + } + usertypelib->Release(); + } + usertypeinfo->Release(); + } + + return typeName; +} + +#define VT_UNHANDLED(x) case VT_##x: qWarning("QAxBase: Unhandled type %s", #x); str = #x; break; + +QByteArray MetaObjectGenerator::guessTypes(const TYPEDESC &tdesc, ITypeInfo *info, const QByteArray &function) +{ + QByteArray str; + switch (tdesc.vt) { + case VT_EMPTY: + case VT_VOID: + break; + case VT_LPWSTR: + str = "wchar_t *"; + break; + case VT_BSTR: + str = "QString"; + break; + case VT_BOOL: + str = "bool"; + break; + case VT_I1: + str = "char"; + break; + case VT_I2: + str = "short"; + break; + case VT_I4: + case VT_INT: + str = "int"; + break; + case VT_I8: + str = "qlonglong"; + break; + case VT_UI1: + case VT_UI2: + case VT_UI4: + case VT_UINT: + str = "uint"; + break; + case VT_UI8: + str = "qulonglong"; + break; + case VT_CY: + str = "qlonglong"; + break; + case VT_R4: + str = "float"; + break; + case VT_R8: + str = "double"; + break; + case VT_DATE: + str = "QDateTime"; + break; + case VT_DISPATCH: + str = "IDispatch*"; + break; + case VT_VARIANT: + str = "QVariant"; + break; + case VT_UNKNOWN: + str = "IUnknown*"; + break; + case VT_HRESULT: + str = "HRESULT"; + break; + case VT_PTR: + str = guessTypes(*tdesc.lptdesc, info, function); + switch(tdesc.lptdesc->vt) { + case VT_VOID: + str = "void*"; + break; + case VT_VARIANT: + case VT_BSTR: + case VT_I1: + case VT_I2: + case VT_I4: + case VT_I8: + case VT_UI1: + case VT_UI2: + case VT_UI4: + case VT_UI8: + case VT_BOOL: + case VT_R4: + case VT_R8: + case VT_INT: + case VT_UINT: + case VT_CY: + str += "&"; + break; + case VT_PTR: + if (str == "QFont" || str == "QPixmap") { + str += "&"; + break; + } else if (str == "void*") { + str = "void **"; + break; + } + // FALLTHROUGH + default: + if (str == "QColor") + str += "&"; + else if (str == "QDateTime") + str += "&"; + else if (str == "QVariantList") + str += "&"; + else if (str == "QByteArray") + str += "&"; + else if (str == "QStringList") + str += "&"; + else if (!str.isEmpty() && hasEnum(str)) + str += "&"; + else if (!str.isEmpty() && str != "QFont" && str != "QPixmap" && str != "QVariant") + str += "*"; + } + break; + case VT_SAFEARRAY: + switch(tdesc.lpadesc->tdescElem.vt) { + // some shortcuts, and generic support for lists of QVariant-supported types + case VT_UI1: + str = "QByteArray"; + break; + case VT_BSTR: + str = "QStringList"; + break; + case VT_VARIANT: + str = "QVariantList"; + break; + default: + str = guessTypes(tdesc.lpadesc->tdescElem, info, function); + if (!str.isEmpty()) + str = "QList<" + str + ">"; + break; + } + break; + case VT_CARRAY: + str = guessTypes(tdesc.lpadesc->tdescElem, info, function); + if (!str.isEmpty()) { + for (int index = 0; index < tdesc.lpadesc->cDims; ++index) + str += "[" + QByteArray::number((int)tdesc.lpadesc->rgbounds[index].cElements) + "]"; + } + break; + case VT_USERDEFINED: + str = usertypeToString(tdesc, info, function); + break; + + VT_UNHANDLED(FILETIME); + VT_UNHANDLED(BLOB); + VT_UNHANDLED(ERROR); + VT_UNHANDLED(DECIMAL); + VT_UNHANDLED(LPSTR); + default: + break; + } + + if (tdesc.vt & VT_BYREF) + str += "&"; + + str.replace("&*", "**"); + return str; +} + +void MetaObjectGenerator::readClassInfo() +{ + // Read class information + IProvideClassInfo *provideClassInfo = 0; + if (d) + d->ptr->QueryInterface(IID_IProvideClassInfo, (void**)&provideClassInfo); + if (provideClassInfo) { + provideClassInfo->GetClassInfo(&classInfo); + TYPEATTR *typeattr = 0; + if (classInfo) + classInfo->GetTypeAttr(&typeattr); + + QString coClassID; + if (typeattr) { + QUuid clsid(typeattr->guid); + coClassID = clsid.toString().toUpper(); +#ifndef QAX_NO_CLASSINFO + // UUID + if (d->useClassInfo && !hasClassInfo("CoClass")) { + QString coClassIDstr = iidnames.value(QLatin1String("/CLSID/") + coClassID + QLatin1String("/Default"), coClassID).toString(); + addClassInfo("CoClass", coClassIDstr.isEmpty() ? coClassID.toLatin1() : coClassIDstr.toLatin1()); + QByteArray version = QByteArray::number(typeattr->wMajorVerNum) + "." + QByteArray::number(typeattr->wMinorVerNum); + if (version != "0.0") + addClassInfo("Version", version); + } +#endif + classInfo->ReleaseTypeAttr(typeattr); + } + provideClassInfo->Release(); + provideClassInfo = 0; + + if (d->tryCache && !coClassID.isEmpty()) + cacheKey = QString::fromLatin1("%1$%2$%3$%4").arg(coClassID) + .arg((int)d->useEventSink).arg((int)d->useClassInfo).arg((int)qax_dispatchEqualsIDispatch); + } + + UINT index = 0; + if (disp && !dispInfo) + disp->GetTypeInfo(index, LOCALE_USER_DEFAULT, &dispInfo); + + if (dispInfo && !typelib) + dispInfo->GetContainingTypeLib(&typelib, &index); + + if (!typelib) { + QSettings controls(QLatin1String("HKEY_LOCAL_MACHINE\\Software"), QSettings::NativeFormat); + QString tlid = controls.value(QLatin1String("/Classes/CLSID/") + that->control() + QLatin1String("/TypeLib/.")).toString(); + QString tlfile; + if (!tlid.isEmpty()) { + controls.beginGroup(QLatin1String("/Classes/TypeLib/") + tlid); + QStringList versions = controls.childGroups(); + QStringList::Iterator vit = versions.begin(); + while (tlfile.isEmpty() && vit != versions.end()) { + QString version = *vit; + ++vit; + tlfile = controls.value(QLatin1String("/") + version + QLatin1String("/0/win32/.")).toString(); + } + controls.endGroup(); + } else { + tlfile = controls.value(QLatin1String("/Classes/CLSID/") + that->control() + QLatin1String("/InprocServer32/.")).toString(); + if (tlfile.isEmpty()) + tlfile = controls.value(QLatin1String("/Classes/CLSID/") + that->control() + QLatin1String("/LocalServer32/.")).toString(); + } + if (!tlfile.isEmpty()) { + LoadTypeLib((OLECHAR*)tlfile.utf16(), &typelib); + if (!typelib) { + tlfile = tlfile.left(tlfile.lastIndexOf(QLatin1Char('.'))) + QLatin1String(".tlb"); + LoadTypeLib((OLECHAR*)tlfile.utf16(), &typelib); + } + if (!typelib) { + tlfile = tlfile.left(tlfile.lastIndexOf(QLatin1Char('.'))) + QLatin1String(".olb"); + LoadTypeLib((OLECHAR*)tlfile.utf16(), &typelib); + } + } + } + + if (!classInfo && typelib && that) + typelib->GetTypeInfoOfGuid(QUuid(that->control()), &classInfo); + + if (classInfo && !dispInfo) { + TYPEATTR *classAttr; + classInfo->GetTypeAttr(&classAttr); + if (classAttr) { + for (int i = 0; i < classAttr->cImplTypes; ++i) { + int typeFlags = 0; + classInfo->GetImplTypeFlags(i, &typeFlags); + if (typeFlags & IMPLTYPEFLAG_FSOURCE) + continue; + + HREFTYPE hrefType; + if (S_OK == classInfo->GetRefTypeOfImplType(i, &hrefType)) + classInfo->GetRefTypeInfo(hrefType, &dispInfo); + if (dispInfo) { + TYPEATTR *ifaceAttr; + dispInfo->GetTypeAttr(&ifaceAttr); + WORD typekind = ifaceAttr->typekind; + dispInfo->ReleaseTypeAttr(ifaceAttr); + + if (typekind & TKIND_DISPATCH) { + break; + } else { + dispInfo->Release(); + dispInfo = 0; + } + } + } + classInfo->ReleaseTypeAttr(classAttr); + } + } + + if (!d || !dispInfo || !cacheKey.isEmpty() || !d->tryCache) + return; + + TYPEATTR *typeattr = 0; + dispInfo->GetTypeAttr(&typeattr); + + QString interfaceID; + if (typeattr) { + QUuid iid(typeattr->guid); + interfaceID = iid.toString().toUpper(); + + dispInfo->ReleaseTypeAttr(typeattr); + // ### event interfaces!! + if (!interfaceID.isEmpty()) + cacheKey = QString::fromLatin1("%1$%2$%3$%4").arg(interfaceID) + .arg((int)d->useEventSink).arg((int)d->useClassInfo).arg((int)qax_dispatchEqualsIDispatch); + } +} + +void MetaObjectGenerator::readEnumInfo() +{ + if (!typelib) + return; + + QUuid libUuid; + + if (d && d->tryCache) { + TLIBATTR *libAttr = 0; + typelib->GetLibAttr(&libAttr); + if (libAttr) { + libUuid = QUuid(libAttr->guid); + typelib->ReleaseTLibAttr(libAttr); + enum_list = enum_cache.value(libUuid); + if (!enum_list.isEmpty()) + return; + } + } + + int valueindex = 0; + QSet<QString> clashCheck; + int clashIndex = 0; + + int enum_serial = 0; + UINT index = typelib->GetTypeInfoCount(); + for (UINT i = 0; i < index; ++i) { + TYPEKIND typekind; + typelib->GetTypeInfoType(i, &typekind); + if (typekind == TKIND_ENUM) { + // Get the type information for the enum + ITypeInfo *enuminfo = 0; + typelib->GetTypeInfo(i, &enuminfo); + if (!enuminfo) + continue; + + // Get the name of the enumeration + BSTR enumname; + QByteArray enumName; + if (typelib->GetDocumentation(i, &enumname, 0, 0, 0) == S_OK) { + enumName = QString::fromUtf16((const ushort *)enumname).toLatin1(); + SysFreeString(enumname); + } else { + enumName = "enum" + QByteArray::number(++enum_serial); + } + + // Get the attributes of the enum type + TYPEATTR *typeattr = 0; + enuminfo->GetTypeAttr(&typeattr); + if (typeattr) { + // Get all values of the enumeration + for (UINT vd = 0; vd < (UINT)typeattr->cVars; ++vd) { + VARDESC *vardesc = 0; + enuminfo->GetVarDesc(vd, &vardesc); + if (vardesc && vardesc->varkind == VAR_CONST) { + int value = vardesc->lpvarValue->lVal; + int memid = vardesc->memid; + // Get the name of the value + BSTR valuename; + QByteArray valueName; + UINT maxNamesOut; + enuminfo->GetNames(memid, &valuename, 1, &maxNamesOut); + if (maxNamesOut) { + valueName = QString::fromUtf16((const ushort *)valuename).toLatin1(); + SysFreeString(valuename); + } else { + valueName = "value" + QByteArray::number(valueindex++); + } + + if (clashCheck.contains(QString::fromLatin1(valueName))) + valueName += QByteArray::number(++clashIndex); + + clashCheck.insert(QString::fromLatin1(valueName)); + addEnumValue(enumName, valueName, value); + } + enuminfo->ReleaseVarDesc(vardesc); + } + } + enuminfo->ReleaseTypeAttr(typeattr); + enuminfo->Release(); + } + } + + if (!libUuid.isNull()) + enum_cache.insert(libUuid, enum_list); +} + +void MetaObjectGenerator::addChangedSignal(const QByteArray &function, const QByteArray &type, int memid) +{ + QAxEventSink *eventSink = 0; + if (d) { + eventSink = d->eventSink.value(iid_propNotifySink); + if (!eventSink && d->useEventSink) { + eventSink = new QAxEventSink(that); + d->eventSink.insert(iid_propNotifySink, eventSink); + } + } + // generate changed signal + QByteArray signalName(function); + signalName += "Changed"; + QByteArray signalProto = signalName + "(" + replaceType(type) + ")"; + if (!hasSignal(signalProto)) + addSignal(signalProto, function); + if (eventSink) + eventSink->addProperty(memid, function, signalProto); +} + +void MetaObjectGenerator::addSetterSlot(const QByteArray &property) +{ + QByteArray set; + QByteArray prototype(property); + if (isupper(prototype.at(0))) { + set = "Set"; + } else { + set = "set"; + prototype[0] = toupper(prototype[0]); + } + prototype = set + prototype + "(" + propertyType(property) + ")"; + if (!hasSlot(prototype)) + addSlot(0, prototype, property); +} + +QByteArray MetaObjectGenerator::createPrototype(FUNCDESC *funcdesc, ITypeInfo *typeinfo, const QList<QByteArray> &names, + QByteArray &type, QList<QByteArray> ¶meters) +{ + QByteArray prototype; + QByteArray function(names.at(0)); + const QByteArray hresult("HRESULT"); + // get function prototype + type = guessTypes(funcdesc->elemdescFunc.tdesc, typeinfo, function); + if ((type.isEmpty() || type == hresult) && funcdesc->invkind == INVOKE_PROPERTYPUT && funcdesc->lprgelemdescParam) { + type = guessTypes(funcdesc->lprgelemdescParam->tdesc, typeinfo, function); + } + + prototype = function + "("; + if (funcdesc->invkind == INVOKE_FUNC && type == hresult) + type = 0; + + int p; + for (p = 1; p < names.count(); ++p) { + // parameter + QByteArray paramName = names.at(p); + bool optional = p > (funcdesc->cParams - funcdesc->cParamsOpt); + TYPEDESC tdesc = funcdesc->lprgelemdescParam[p-1].tdesc; + PARAMDESC pdesc = funcdesc->lprgelemdescParam[p-1].paramdesc; + + QByteArray ptype = guessTypes(tdesc, typeinfo, function); + if (pdesc.wParamFlags & PARAMFLAG_FRETVAL) { + if (ptype.endsWith("&")) { + ptype.truncate(ptype.length() - 1); + } else if (ptype.endsWith("**")) { + ptype.truncate(ptype.length() - 1); + } + type = ptype; + } else { + prototype += ptype; + if (pdesc.wParamFlags & PARAMFLAG_FOUT && !ptype.endsWith("&") && !ptype.endsWith("**")) + prototype += "&"; + if (optional || pdesc.wParamFlags & PARAMFLAG_FOPT) + paramName += "=0"; + else if (pdesc.wParamFlags & PARAMFLAG_FHASDEFAULT) { + // ### get the value from pdesc.pparamdescex + paramName += "=0"; + } + parameters << paramName; + } + if (p < funcdesc->cParams && !(pdesc.wParamFlags & PARAMFLAG_FRETVAL)) + prototype += ","; + } + + if (!prototype.isEmpty()) { + if (prototype.right(1) == ",") { + if (funcdesc->invkind == INVOKE_PROPERTYPUT && p == funcdesc->cParams) { + TYPEDESC tdesc = funcdesc->lprgelemdescParam[p-1].tdesc; + QByteArray ptype = guessTypes(tdesc, typeinfo, function); + prototype += ptype; + prototype += ")"; + parameters << "rhs"; + } else { + prototype[prototype.length()-1] = ')'; + } + } else { + prototype += ")"; + } + } + + return prototype; +} + +void MetaObjectGenerator::readFuncsInfo(ITypeInfo *typeinfo, ushort nFuncs) +{ + if (!nFuncs) { + TYPEATTR *typeattr = 0; + typeinfo->GetTypeAttr(&typeattr); + if (typeattr) { + nFuncs = typeattr->cFuncs; + typeinfo->ReleaseTypeAttr(typeattr); + } + } + + // get information about all functions + for (ushort fd = 0; fd < nFuncs ; ++fd) { + FUNCDESC *funcdesc = 0; + typeinfo->GetFuncDesc(fd, &funcdesc); + if (!funcdesc) + break; + + QByteArray function; + QByteArray type; + QByteArray prototype; + QList<QByteArray> parameters; + + // parse function description + BSTR bstrNames[256]; + UINT maxNames = 255; + UINT maxNamesOut; + typeinfo->GetNames(funcdesc->memid, (BSTR*)&bstrNames, maxNames, &maxNamesOut); + QList<QByteArray> names; + int p; + for (p = 0; p < (int)maxNamesOut; ++p) { + names << QString::fromUtf16((const ushort *)bstrNames[p]).toLatin1(); + SysFreeString(bstrNames[p]); + } + + // function name + function = names.at(0); + if ((maxNamesOut == 3 && function == "QueryInterface") || + (maxNamesOut == 1 && function == "AddRef") || + (maxNamesOut == 1 && function == "Release") || + (maxNamesOut == 9 && function == "Invoke") || + (maxNamesOut == 6 && function == "GetIDsOfNames") || + (maxNamesOut == 2 && function == "GetTypeInfoCount") || + (maxNamesOut == 4 && function == "GetTypeInfo")) { + typeinfo->ReleaseFuncDesc(funcdesc); + continue; + } + + prototype = createPrototype(/*in*/ funcdesc, typeinfo, names, /*out*/type, parameters); + + // get type of function + switch(funcdesc->invkind) { + case INVOKE_PROPERTYGET: // property + case INVOKE_PROPERTYPUT: + if (funcdesc->cParams - funcdesc->cParamsOpt <= 1) { + bool dontBreak = false; + // getter with non-default-parameters -> fall through to function handling + if (funcdesc->invkind == INVOKE_PROPERTYGET && parameters.count() && funcdesc->cParams - funcdesc->cParamsOpt) { + dontBreak = true; + } else { + uint flags = Readable; + if (funcdesc->invkind != INVOKE_PROPERTYGET) + flags |= Writable; + if (!(funcdesc->wFuncFlags & (FUNCFLAG_FNONBROWSABLE | FUNCFLAG_FHIDDEN))) + flags |= Designable; + if (!(funcdesc->wFuncFlags & FUNCFLAG_FRESTRICTED)) + flags |= Scriptable; + if (funcdesc->wFuncFlags & FUNCFLAG_FREQUESTEDIT) + flags |= RequestingEdit; + if (hasEnum(type)) + flags |= EnumOrFlag; + + if (funcdesc->wFuncFlags & FUNCFLAG_FBINDABLE && funcdesc->invkind == INVOKE_PROPERTYGET) { + addChangedSignal(function, type, funcdesc->memid); + flags |= Bindable; + } + // Don't generate code for properties without type + if (type.isEmpty()) + break; + addProperty(type, function, flags); + + // more parameters -> function handling + if (funcdesc->invkind == INVOKE_PROPERTYGET && funcdesc->cParams) + dontBreak = true; + } + + if (!funcdesc->cParams) { + // don't generate slots for incomplete properties + if (type.isEmpty()) + break; + + // Done for getters + if (funcdesc->invkind == INVOKE_PROPERTYGET) + break; + + // generate setter slot + if (funcdesc->invkind == INVOKE_PROPERTYPUT && hasProperty(function)) { + addSetterSlot(function); + break; + } + } else if (funcdesc->invkind == INVOKE_PROPERTYPUT && hasProperty(function)) { + addSetterSlot(function); + // more parameters -> function handling + if (funcdesc->cParams > 1) + dontBreak = true; + } + if (!dontBreak) + break; + } + if (funcdesc->invkind == INVOKE_PROPERTYPUT) { + QByteArray set; + if (isupper(prototype.at(0))) { + set = "Set"; + } else { + set = "set"; + prototype[0] = toupper(prototype[0]); + } + + prototype = set + prototype; + } + // FALL THROUGH to support multi-variat properties + case INVOKE_FUNC: // method + { + bool cloned = false; + bool defargs; + do { + QByteArray pnames; + for (p = 0; p < parameters.count(); ++p) { + pnames += parameters.at(p); + if (p < parameters.count() - 1) + pnames += ','; + } + defargs = pnames.contains("=0"); + int flags = QMetaMethod::Public; + if (cloned) + flags |= QMetaMethod::Cloned << 4; + cloned |= defargs; + addSlot(type, prototype, pnames.replace("=0", ""), flags); + + if (defargs) { + parameters.takeLast(); + int lastParam = prototype.lastIndexOf(","); + if (lastParam == -1) + lastParam = prototype.indexOf("(") + 1; + prototype.truncate(lastParam); + prototype += ")"; + } + } while (defargs); + } + break; + + default: + break; + } +#if 0 // documentation in metaobject would be cool? + // get function documentation + BSTR bstrDocu; + info->GetDocumentation(funcdesc->memid, 0, &bstrDocu, 0, 0); + QString strDocu = QString::fromUtf16((const ushort*)bstrDocu); + SysFreeString(bstrDocu); + if (!!strDocu) + desc += "[" + strDocu + "]"; + desc += "\n"; +#endif + typeinfo->ReleaseFuncDesc(funcdesc); + } +} + +void MetaObjectGenerator::readVarsInfo(ITypeInfo *typeinfo, ushort nVars) +{ + if (!nVars) { + TYPEATTR *typeattr = 0; + typeinfo->GetTypeAttr(&typeattr); + if (typeattr) { + nVars = typeattr->cVars; + typeinfo->ReleaseTypeAttr(typeattr); + } + } + + // get information about all variables + for (ushort vd = 0; vd < nVars; ++vd) { + VARDESC *vardesc; + typeinfo->GetVarDesc(vd, &vardesc); + if (!vardesc) + break; + + // no use if it's not a dispatched variable + if (vardesc->varkind != VAR_DISPATCH) { + typeinfo->ReleaseVarDesc(vardesc); + continue; + } + + // get variable name + BSTR bstrName; + UINT maxNames = 1; + UINT maxNamesOut; + typeinfo->GetNames(vardesc->memid, &bstrName, maxNames, &maxNamesOut); + if (maxNamesOut != 1 || !bstrName) { + typeinfo->ReleaseVarDesc(vardesc); + continue; + } + QByteArray variableType; + QByteArray variableName; + uint flags = 0; + + variableName = QString::fromUtf16((const ushort *)bstrName).toLatin1(); + SysFreeString(bstrName); + + // get variable type + TYPEDESC typedesc = vardesc->elemdescVar.tdesc; + variableType = guessTypes(typedesc, typeinfo, variableName); + + // generate meta property + if (!hasProperty(variableName)) { + flags = Readable; + if (!(vardesc->wVarFlags & VARFLAG_FREADONLY)) + flags |= Writable; + if (!(vardesc->wVarFlags & (VARFLAG_FNONBROWSABLE | VARFLAG_FHIDDEN))) + flags |= Designable; + if (!(vardesc->wVarFlags & VARFLAG_FRESTRICTED)) + flags |= Scriptable; + if (vardesc->wVarFlags & VARFLAG_FREQUESTEDIT) + flags |= RequestingEdit; + if (hasEnum(variableType)) + flags |= EnumOrFlag; + + if (vardesc->wVarFlags & VARFLAG_FBINDABLE) { + addChangedSignal(variableName, variableType, vardesc->memid); + flags |= Bindable; + } + addProperty(variableType, variableName, flags); + } + + // generate a set slot + if (!(vardesc->wVarFlags & VARFLAG_FREADONLY)) + addSetterSlot(variableName); + +#if 0 // documentation in metaobject would be cool? + // get function documentation + BSTR bstrDocu; + info->GetDocumentation(vardesc->memid, 0, &bstrDocu, 0, 0); + QString strDocu = QString::fromUtf16((const ushort*)bstrDocu); + SysFreeString(bstrDocu); + if (!!strDocu) + desc += "[" + strDocu + "]"; + desc += "\n"; +#endif + typeinfo->ReleaseVarDesc(vardesc); + } +} + +void MetaObjectGenerator::readInterfaceInfo() +{ + ITypeInfo *typeinfo = dispInfo; + if (!typeinfo) + return; + typeinfo->AddRef(); + int interface_serial = 0; + while (typeinfo) { + ushort nFuncs = 0; + ushort nVars = 0; + ushort nImpl = 0; + // get information about type + TYPEATTR *typeattr; + typeinfo->GetTypeAttr(&typeattr); + bool interesting = true; + if (typeattr) { + // get number of functions, variables, and implemented interfaces + nFuncs = typeattr->cFuncs; + nVars = typeattr->cVars; + nImpl = typeattr->cImplTypes; + + if ((typeattr->typekind == TKIND_DISPATCH || typeattr->typekind == TKIND_INTERFACE) && + (typeattr->guid != IID_IDispatch && typeattr->guid != IID_IUnknown)) { +#ifndef QAX_NO_CLASSINFO + if (d && d->useClassInfo) { + // UUID + QUuid uuid(typeattr->guid); + QString uuidstr = uuid.toString().toUpper(); + uuidstr = iidnames.value(QLatin1String("/Interface/") + uuidstr + QLatin1String("/Default"), uuidstr).toString(); + addClassInfo("Interface " + QByteArray::number(++interface_serial), uuidstr.toLatin1()); + } +#endif + typeinfo->ReleaseTypeAttr(typeattr); + } else { + interesting = false; + typeinfo->ReleaseTypeAttr(typeattr); + } + } + + if (interesting) { + readFuncsInfo(typeinfo, nFuncs); + readVarsInfo(typeinfo, nVars); + } + + if (!nImpl) { + typeinfo->Release(); + typeinfo = 0; + break; + } + + // go up one base class + HREFTYPE pRefType; + typeinfo->GetRefTypeOfImplType(0, &pRefType); + ITypeInfo *baseInfo = 0; + typeinfo->GetRefTypeInfo(pRefType, &baseInfo); + typeinfo->Release(); + if (typeinfo == baseInfo) { // IUnknown inherits IUnknown ??? + baseInfo->Release(); + typeinfo = 0; + break; + } + typeinfo = baseInfo; + } +} + +void MetaObjectGenerator::readEventInterface(ITypeInfo *eventinfo, IConnectionPoint *cpoint) +{ + TYPEATTR *eventattr; + eventinfo->GetTypeAttr(&eventattr); + if (!eventattr) + return; + if (eventattr->typekind != TKIND_DISPATCH) { + eventinfo->ReleaseTypeAttr(eventattr); + return; + } + + QAxEventSink *eventSink = 0; + if (d) { + IID conniid; + cpoint->GetConnectionInterface(&conniid); + eventSink = d->eventSink.value(QUuid(conniid)); + if (!eventSink) { + eventSink = new QAxEventSink(that); + d->eventSink.insert(QUuid(conniid), eventSink); + eventSink->advise(cpoint, conniid); + } + } + + // get information about all event functions + for (UINT fd = 0; fd < (UINT)eventattr->cFuncs; ++fd) { + FUNCDESC *funcdesc; + eventinfo->GetFuncDesc(fd, &funcdesc); + if (!funcdesc) + break; + if (funcdesc->invkind != INVOKE_FUNC || + funcdesc->funckind != FUNC_DISPATCH) { + eventinfo->ReleaseTypeAttr(eventattr); + eventinfo->ReleaseFuncDesc(funcdesc); + continue; + } + + QByteArray function; + QByteArray prototype; + QList<QByteArray> parameters; + + // parse event function description + BSTR bstrNames[256]; + UINT maxNames = 255; + UINT maxNamesOut; + eventinfo->GetNames(funcdesc->memid, (BSTR*)&bstrNames, maxNames, &maxNamesOut); + QList<QByteArray> names; + int p; + for (p = 0; p < (int)maxNamesOut; ++p) { + names << QString::fromUtf16((const ushort *)bstrNames[p]).toLatin1(); + SysFreeString(bstrNames[p]); + } + + // get event function prototype + function = names.at(0); + QByteArray type; // dummy - we don't care about return values for signals + prototype = createPrototype(/*in*/ funcdesc, eventinfo, names, /*out*/type, parameters); + if (!hasSignal(prototype)) { + QByteArray pnames; + for (p = 0; p < parameters.count(); ++p) { + pnames += parameters.at(p); + if (p < parameters.count() - 1) + pnames += ','; + } + addSignal(prototype, pnames); + } + if (eventSink) + eventSink->addSignal(funcdesc->memid, prototype); + +#if 0 // documentation in metaobject would be cool? + // get function documentation + BSTR bstrDocu; + eventinfo->GetDocumentation(funcdesc->memid, 0, &bstrDocu, 0, 0); + QString strDocu = QString::fromUtf16((const ushort*)bstrDocu); + SysFreeString(bstrDocu); + if (!!strDocu) + desc += "[" + strDocu + "]"; + desc += "\n"; +#endif + eventinfo->ReleaseFuncDesc(funcdesc); + } + eventinfo->ReleaseTypeAttr(eventattr); +} + +void MetaObjectGenerator::readEventInfo() +{ + int event_serial = 0; + IConnectionPointContainer *cpoints = 0; + if (d && d->useEventSink) + d->ptr->QueryInterface(IID_IConnectionPointContainer, (void**)&cpoints); + if (cpoints) { + // Get connection point enumerator + IEnumConnectionPoints *epoints = 0; + cpoints->EnumConnectionPoints(&epoints); + if (epoints) { + ULONG c = 1; + IConnectionPoint *cpoint = 0; + epoints->Reset(); + QList<QUuid> cpointlist; + do { + if (cpoint) cpoint->Release(); + cpoint = 0; + HRESULT hr = epoints->Next(c, &cpoint, &c); + if (!c || hr != S_OK) + break; + + IID conniid; + cpoint->GetConnectionInterface(&conniid); + // workaround for typelibrary bug of Word.Application + QUuid connuuid(conniid); + if (cpointlist.contains(connuuid)) + break; + +#ifndef QAX_NO_CLASSINFO + if (d->useClassInfo) { + QString uuidstr = connuuid.toString().toUpper(); + uuidstr = iidnames.value(QLatin1String("/Interface/") + uuidstr + QLatin1String("/Default"), uuidstr).toString(); + addClassInfo("Event Interface " + QByteArray::number(++event_serial), uuidstr.toLatin1()); + } +#endif + + // get information about type + if (conniid == IID_IPropertyNotifySink) { + // test whether property notify sink has been created already, and advise on it + QAxEventSink *eventSink = d->eventSink.value(iid_propNotifySink); + if (eventSink) + eventSink->advise(cpoint, conniid); + continue; + } + + ITypeInfo *eventinfo = 0; + if (typelib) + typelib->GetTypeInfoOfGuid(conniid, &eventinfo); + + if (eventinfo) { + // avoid recursion (see workaround above) + cpointlist.append(connuuid); + + readEventInterface(eventinfo, cpoint); + eventinfo->Release(); + } + } while (c); + if (cpoint) cpoint->Release(); + epoints->Release(); + } else if (classInfo) { // no enumeration - search source interfaces and ask for those + TYPEATTR *typeattr = 0; + classInfo->GetTypeAttr(&typeattr); + if (typeattr) { + for (int i = 0; i < typeattr->cImplTypes; ++i) { + int flags = 0; + classInfo->GetImplTypeFlags(i, &flags); + if (!(flags & IMPLTYPEFLAG_FSOURCE)) + continue; + HREFTYPE reference; + if (S_OK != classInfo->GetRefTypeOfImplType(i, &reference)) + continue; + ITypeInfo *eventInfo = 0; + classInfo->GetRefTypeInfo(reference, &eventInfo); + if (!eventInfo) + continue; + TYPEATTR *eventattr = 0; + eventInfo->GetTypeAttr(&eventattr); + if (eventattr) { + IConnectionPoint *cpoint = 0; + cpoints->FindConnectionPoint(eventattr->guid, &cpoint); + if (cpoint) { + if (eventattr->guid == IID_IPropertyNotifySink) { + // test whether property notify sink has been created already, and advise on it + QAxEventSink *eventSink = d->eventSink.value(iid_propNotifySink); + if (eventSink) + eventSink->advise(cpoint, eventattr->guid); + continue; + } + + readEventInterface(eventInfo, cpoint); + cpoint->Release(); + } + eventInfo->ReleaseTypeAttr(eventattr); + } + eventInfo->Release(); + } + classInfo->ReleaseTypeAttr(typeattr); + } + } + cpoints->Release(); + } +} + +QMetaObject *MetaObjectGenerator::tryCache() +{ + if (!cacheKey.isEmpty()) { + d->metaobj = mo_cache.value(cacheKey); + if (d->metaobj) { + d->cachedMetaObject = true; + + IConnectionPointContainer *cpoints = 0; + d->ptr->QueryInterface(IID_IConnectionPointContainer, (void**)&cpoints); + if (cpoints) { + QList<QUuid>::ConstIterator it = d->metaobj->connectionInterfaces.begin(); + while (it != d->metaobj->connectionInterfaces.end()) { + QUuid iid = *it; + ++it; + + IConnectionPoint *cpoint = 0; + cpoints->FindConnectionPoint(iid, &cpoint); + if (cpoint) { + QAxEventSink *sink = new QAxEventSink(that); + sink->advise(cpoint, iid); + d->eventSink.insert(iid, sink); + sink->sigs = d->metaobj->sigs.value(iid); + sink->props = d->metaobj->props.value(iid); + sink->propsigs = d->metaobj->propsigs.value(iid); + cpoint->Release(); + } + } + cpoints->Release(); + } + + return d->metaobj; + } + } + return 0; +} + +QMetaObject *MetaObjectGenerator::metaObject(const QMetaObject *parentObject, const QByteArray &className) +{ + if (that) { + readClassInfo(); + if (typelib) { + BSTR bstr; + typelib->GetDocumentation(-1, &bstr, 0, 0, 0); + current_typelib = QString::fromUtf16((const ushort *)bstr).toLatin1(); + SysFreeString(bstr); + } + if (d->tryCache && tryCache()) + return d->metaobj; + readEnumInfo(); + readInterfaceInfo(); + readEventInfo(); + } + + current_typelib = QByteArray(); + +#ifndef QAX_NO_CLASSINFO + if (!debugInfo.isEmpty() && d->useClassInfo) + addClassInfo("debugInfo", debugInfo); +#endif + + QAxMetaObject *metaobj = new QAxMetaObject; + + // revision + classname + table + zero terminator + uint int_data_size = 1+1+2+2+2+2+1; + + int_data_size += classinfo_list.count() * 2; + int_data_size += signal_list.count() * 5; + int_data_size += slot_list.count() * 5; + int_data_size += property_list.count() * 3; + int_data_size += enum_list.count() * 4; + for (QMap<QByteArray, QList<QPair<QByteArray, int> > >::ConstIterator it = enum_list.begin(); + it != enum_list.end(); ++it) { + int_data_size += (*it).count() * 2; + } + + uint *int_data = new uint[int_data_size]; + int_data[0] = 1; // revision number + int_data[1] = 0; // classname index + int_data[2] = classinfo_list.count(); // num_classinfo + int_data[3] = 10; // idx_classinfo + int_data[4] = signal_list.count() + slot_list.count(); // num_methods + int_data[5] = int_data[3] + int_data[2] * 2; // idx_signals + int_data[6] = property_list.count(); // num_properties + int_data[7] = int_data[5] + int_data[4] * 5; // idx_properties + int_data[8] = enum_list.count(); // num_enums + int_data[9] = int_data[7] + int_data[6] * 3; // idx_enums + int_data[int_data_size - 1] = 0; // eod; + + char null('\0'); + // data + zero-terminator + QByteArray stringdata = that ? QByteArray(that->className()) : className; + stringdata += null; + stringdata.reserve(8192); + + uint offset = int_data[3]; //idx_classinfo + + // each class info in form key\0value\0 + for (QMap<QByteArray, QByteArray>::ConstIterator it = classinfo_list.begin(); it != classinfo_list.end(); ++it) { + QByteArray key(it.key()); + QByteArray value(it.value()); + int_data[offset++] = stringdata.length(); + stringdata += key; + stringdata += null; + int_data[offset++] = stringdata.length(); + stringdata += value; + stringdata += null; + } + Q_ASSERT(offset == int_data[5]); + + // each signal in form prototype\0parameters\0type\0tag\0 + for (QMap<QByteArray, Method>::ConstIterator it = signal_list.begin(); it != signal_list.end(); ++it) { + QByteArray prototype(QMetaObject::normalizedSignature(it.key())); + QByteArray type(it.value().type); + QByteArray parameters(it.value().parameters); + if (!it.value().realPrototype.isEmpty()) + metaobj->realPrototype.insert(prototype, it.value().realPrototype); + QByteArray tag; + int flags = it.value().flags; + + int_data[offset++] = stringdata.length(); + stringdata += prototype; + stringdata += null; + int_data[offset++] = stringdata.length(); + stringdata += parameters; + stringdata += null; + int_data[offset++] = stringdata.length(); + stringdata += type; + stringdata += null; + int_data[offset++] = stringdata.length(); + stringdata += tag; + stringdata += null; + int_data[offset++] = flags; + } + + // each slot in form prototype\0parameters\0type\0tag\0 + for (QMap<QByteArray, Method>::ConstIterator it = slot_list.begin(); it != slot_list.end(); ++it) { + QByteArray prototype(QMetaObject::normalizedSignature(it.key())); + QByteArray type(it.value().type); + QByteArray parameters(it.value().parameters); + if (!it.value().realPrototype.isEmpty()) + metaobj->realPrototype.insert(prototype, it.value().realPrototype); + QByteArray tag; + int flags = it.value().flags; + + int_data[offset++] = stringdata.length(); + stringdata += prototype; + stringdata += null; + int_data[offset++] = stringdata.length(); + stringdata += parameters; + stringdata += null; + int_data[offset++] = stringdata.length(); + stringdata += type; + stringdata += null; + int_data[offset++] = stringdata.length(); + stringdata += tag; + stringdata += null; + int_data[offset++] = flags; + } + Q_ASSERT(offset == int_data[7]); + + // each property in form name\0type\0 + for (QMap<QByteArray, Property>::ConstIterator it = property_list.begin(); it != property_list.end(); ++it) { + QByteArray name(it.key()); + QByteArray type(it.value().type); + QByteArray realType(it.value().realType); + if (!realType.isEmpty() && realType != type) + metaobj->realPrototype.insert(name, realType); + uint flags = it.value().typeId; + + int_data[offset++] = stringdata.length(); + stringdata += name; + stringdata += null; + int_data[offset++] = stringdata.length(); + stringdata += type; + stringdata += null; + int_data[offset++] = flags; + } + Q_ASSERT(offset == int_data[9]); + + int value_offset = offset + enum_list.count() * 4; + // each enum in form name\0 + for (QMap<QByteArray, QList<QPair<QByteArray, int> > >::ConstIterator it = enum_list.begin(); it != enum_list.end(); ++it) { + QByteArray name(it.key()); + int flags = 0x0; // 0x1 for flag? + int count = it.value().count(); + + int_data[offset++] = stringdata.length(); + stringdata += name; + stringdata += null; + int_data[offset++] = flags; + int_data[offset++] = count; + int_data[offset++] = value_offset; + value_offset += count * 2; + } + Q_ASSERT(offset == int_data[9] + enum_list.count() * 4); + + // each enum value in form key\0 + for (QMap<QByteArray, QList<QPair<QByteArray, int> > >::ConstIterator it = enum_list.begin(); it != enum_list.end(); ++it) { + for (QList<QPair<QByteArray,int> >::ConstIterator it2 = it.value().begin(); it2 != it.value().end(); ++it2) { + QByteArray key((*it2).first); + int value = (*it2).second; + int_data[offset++] = stringdata.length(); + stringdata += key; + stringdata += null; + int_data[offset++] = value; + } + } + Q_ASSERT(offset == int_data_size-1); + + char *string_data = new char[stringdata.length()]; + memset(string_data, 0, sizeof(string_data)); + memcpy(string_data, stringdata, stringdata.length()); + + // put the metaobject together + metaobj->d.data = int_data; + metaobj->d.extradata = 0; + metaobj->d.stringdata = string_data; + metaobj->d.superdata = parentObject; + + if (d) + d->metaobj = metaobj; + + if (!cacheKey.isEmpty()) { + mo_cache.insert(cacheKey, d->metaobj); + d->cachedMetaObject = true; + for (QHash<QUuid, QAxEventSink*>::Iterator it = d->eventSink.begin(); it != d->eventSink.end(); ++it) { + QAxEventSink *sink = it.value(); + if (sink) { + QUuid ciid = sink->connectionInterface(); + + d->metaobj->connectionInterfaces.append(ciid); + d->metaobj->sigs.insert(ciid, sink->signalMap()); + d->metaobj->props.insert(ciid, sink->propertyMap()); + d->metaobj->propsigs.insert(ciid, sink->propSignalMap()); + } + } + } + + return metaobj; +} + +static const uint qt_meta_data_QAxBase[] = { + + // content: + 1, // revision + 0, // classname + 0, 0, // classinfo + 3, 10, // methods + 1, 25, // properties + 0, 0, // enums/sets + + // signals: signature, parameters, type, tag, flags + 24, 9, 8, 8, 0x05, + 55, 50, 8, 8, 0x05, + 102, 80, 8, 8, 0x05, + + // properties: name, type, flags + 149, 141, 0x0a095103, + + 0 // eod +}; + +static const char qt_meta_stringdata_QAxBase[] = { + "QAxBase\0\0name,argc,argv\0signal(QString,int,void*)\0name\0" + "propertyChanged(QString)\0code,source,desc,help\0" + "exception(int,QString,QString,QString)\0QString\0control\0" +}; + +static QMetaObject qaxobject_staticMetaObject = { + &QObject::staticMetaObject, qt_meta_stringdata_QAxBase, + qt_meta_data_QAxBase, 0 +}; +static QMetaObject qaxwidget_staticMetaObject = { + &QWidget::staticMetaObject, qt_meta_stringdata_QAxBase, + qt_meta_data_QAxBase, 0 +}; + +/*! + \internal + + The metaobject is generated on the fly from the information + provided by the IDispatch and ITypeInfo interface implementations + in the COM object. +*/ +const QMetaObject *QAxBase::metaObject() const +{ + if (d->metaobj) + return d->metaobj; + const QMetaObject* parentObject = parentMetaObject(); + + if (!d->ptr && !d->initialized) { + ((QAxBase*)this)->initialize(&d->ptr); + d->initialized = true; + } + +#ifndef QT_NO_THREAD + // only one thread at a time can generate meta objects + QMutexLocker locker(&cache_mutex); +#endif + + // return the default meta object if not yet initialized + if (!d->ptr || !d->useMetaObject) { + if (qObject()->isWidgetType()) + return &qaxwidget_staticMetaObject; + return &qaxobject_staticMetaObject; + } + MetaObjectGenerator generator((QAxBase*)this, d); + return generator.metaObject(parentObject); +} + +/*! + \internal + + Connects to all event interfaces of the object. + + Called by the subclasses' connectNotify() reimplementations, so + at this point the connection as actually been created already. +*/ +void QAxBase::connectNotify() +{ + if (d->eventSink.count()) // already listening + return; + + IEnumConnectionPoints *epoints = 0; + if (d->ptr && d->useEventSink) { + IConnectionPointContainer *cpoints = 0; + d->ptr->QueryInterface(IID_IConnectionPointContainer, (void**)&cpoints); + if (!cpoints) + return; + + cpoints->EnumConnectionPoints(&epoints); + cpoints->Release(); + } + + if (!epoints) + return; + + UINT index; + IDispatch *disp = d->dispatch(); + ITypeInfo *typeinfo = 0; + ITypeLib *typelib = 0; + if (disp) + disp->GetTypeInfo(0, LOCALE_USER_DEFAULT, &typeinfo); + if (typeinfo) + typeinfo->GetContainingTypeLib(&typelib, &index); + + if (!typelib) { + epoints->Release(); + return; + } + + MetaObjectGenerator generator(this, d); + bool haveEnumInfo = false; + + ULONG c = 1; + IConnectionPoint *cpoint = 0; + epoints->Reset(); + do { + if (cpoint) cpoint->Release(); + cpoint = 0; + epoints->Next(c, &cpoint, &c); + if (!c || !cpoint) + break; + + IID conniid; + cpoint->GetConnectionInterface(&conniid); + // workaround for typelibrary bug of Word.Application + QString connuuid(QUuid(conniid).toString()); + if (d->eventSink.contains(connuuid)) + break; + + // Get ITypeInfo for source-interface, and skip if not supporting IDispatch + ITypeInfo *eventinfo = 0; + typelib->GetTypeInfoOfGuid(conniid, &eventinfo); + if (eventinfo) { + TYPEATTR *eventAttr; + eventinfo->GetTypeAttr(&eventAttr); + if (!eventAttr) { + eventinfo->Release(); + break; + } + + TYPEKIND eventKind = eventAttr->typekind; + eventinfo->ReleaseTypeAttr(eventAttr); + if (eventKind != TKIND_DISPATCH) { + eventinfo->Release(); + break; + } + } + + // always into the cache to avoid recoursion + QAxEventSink *eventSink = eventinfo ? new QAxEventSink(this) : 0; + d->eventSink.insert(connuuid, eventSink); + + if (!eventinfo) + continue; + + // have to get type info to support signals with enum parameters + if (!haveEnumInfo) { + bool wasTryCache = d->tryCache; + d->tryCache = true; + generator.readClassInfo(); + generator.readEnumInfo(); + d->tryCache = wasTryCache; + haveEnumInfo = true; + } + generator.readEventInterface(eventinfo, cpoint); + eventSink->advise(cpoint, conniid); + + eventinfo->Release(); + } while (c); + if (cpoint) cpoint->Release(); + epoints->Release(); + + typelib->Release(); + + // make sure we don't try again + if (!d->eventSink.count()) + d->eventSink.insert(QString(), 0); +} + +/*! + \fn QString QAxBase::generateDocumentation() + + Returns a rich text string with documentation for the + wrapped COM object. Dump the string to an HTML-file, + or use it in e.g. a QTextBrowser widget. +*/ + +static bool checkHRESULT(HRESULT hres, EXCEPINFO *exc, QAxBase *that, const QString &name, uint argerr) +{ + switch(hres) { + case S_OK: + return true; + case DISP_E_BADPARAMCOUNT: + qWarning("QAxBase: Error calling IDispatch member %s: Bad parameter count", name.toLatin1().data()); + return false; + case DISP_E_BADVARTYPE: + qWarning("QAxBase: Error calling IDispatch member %s: Bad variant type", name.toLatin1().data()); + return false; + case DISP_E_EXCEPTION: + { + bool printWarning = true; + unsigned short code = -1; + QString source, desc, help; + const QMetaObject *mo = that->metaObject(); + int exceptionSignal = mo->indexOfSignal("exception(int,QString,QString,QString)"); + if (exceptionSignal >= 0) { + if (exc->pfnDeferredFillIn) + exc->pfnDeferredFillIn(exc); + + code = exc->wCode ? exc->wCode : exc->scode; + source = QString::fromUtf16((const ushort *)exc->bstrSource); + desc = QString::fromUtf16((const ushort *)exc->bstrDescription); + help = QString::fromUtf16((const ushort *)exc->bstrHelpFile); + uint helpContext = exc->dwHelpContext; + + if (helpContext && !help.isEmpty()) + help += QString::fromLatin1(" [%1]").arg(helpContext); + + if (QAxEventSink::signalHasReceivers(that->qObject(), "exception(int,QString,QString,QString)")) { + void *argv[] = {0, &code, &source, &desc, &help}; + that->qt_metacall(QMetaObject::InvokeMetaMethod, exceptionSignal, argv); + printWarning = false; + } + } + if (printWarning) { + qWarning("QAxBase: Error calling IDispatch member %s: Exception thrown by server", name.toLatin1().data()); + qWarning(" Code : %d", code); + qWarning(" Source : %s", source.toLatin1().data()); + qWarning(" Description: %s", desc.toLatin1().data()); + qWarning(" Help : %s", help.toLatin1().data()); + qWarning(" Connect to the exception(int,QString,QString,QString) signal to catch this exception"); + } + } + return false; + case DISP_E_MEMBERNOTFOUND: + qWarning("QAxBase: Error calling IDispatch member %s: Member not found", name.toLatin1().data()); + return false; + case DISP_E_NONAMEDARGS: + qWarning("QAxBase: Error calling IDispatch member %s: No named arguments", name.toLatin1().data()); + return false; + case DISP_E_OVERFLOW: + qWarning("QAxBase: Error calling IDispatch member %s: Overflow", name.toLatin1().data()); + return false; + case DISP_E_PARAMNOTFOUND: + qWarning("QAxBase: Error calling IDispatch member %s: Parameter %d not found", name.toLatin1().data(), argerr); + return false; + case DISP_E_TYPEMISMATCH: + qWarning("QAxBase: Error calling IDispatch member %s: Type mismatch in parameter %d", name.toLatin1().data(), argerr); + return false; + case DISP_E_UNKNOWNINTERFACE: + qWarning("QAxBase: Error calling IDispatch member %s: Unknown interface", name.toLatin1().data()); + return false; + case DISP_E_UNKNOWNLCID: + qWarning("QAxBase: Error calling IDispatch member %s: Unknown locale ID", name.toLatin1().data()); + return false; + case DISP_E_PARAMNOTOPTIONAL: + qWarning("QAxBase: Error calling IDispatch member %s: Non-optional parameter missing", name.toLatin1().data()); + return false; + default: + qWarning("QAxBase: Error calling IDispatch member %s: Unknown error", name.toLatin1().data()); + return false; + } +} + +/*! + \internal +*/ +int QAxBase::internalProperty(QMetaObject::Call call, int index, void **v) +{ + const QMetaObject *mo = metaObject(); + const QMetaProperty prop = mo->property(index + mo->propertyOffset()); + QByteArray propname = prop.name(); + + // hardcoded control property + if (propname == "control") { + switch(call) { + case QMetaObject::ReadProperty: + *(QString*)*v = control(); + break; + case QMetaObject::WriteProperty: + setControl(*(QString*)*v); + break; + case QMetaObject::ResetProperty: + clear(); + break; + default: + break; + } + return index - mo->propertyCount(); + } + + // get the IDispatch + if (!d->ptr || !prop.isValid()) + return index; + IDispatch *disp = d->dispatch(); + if (!disp) + return index; + + DISPID dispid = d->metaObject()->dispIDofName(propname, disp); + if (dispid == DISPID_UNKNOWN) + return index; + + Q_ASSERT(d->metaobj); + // property found, so everthing that goes wrong now should not bother the caller + index -= mo->propertyCount(); + + VARIANTARG arg; + VariantInit(&arg); + DISPPARAMS params; + EXCEPINFO excepinfo; + memset(&excepinfo, 0, sizeof(excepinfo)); + UINT argerr = 0; + HRESULT hres = E_FAIL; + + QByteArray proptype(prop.typeName()); + switch (call) { + case QMetaObject::ReadProperty: + { + params.cArgs = 0; + params.cNamedArgs = 0; + params.rgdispidNamedArgs = 0; + params.rgvarg = 0; + + hres = disp->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &arg, &excepinfo, 0); + + // map result VARIANTARG to void* + uint type = QVariant::Int; + if (!prop.isEnumType()) + type = prop.type(); + QVariantToVoidStar(VARIANTToQVariant(arg, proptype, type), *v, proptype, type); + if ((arg.vt != VT_DISPATCH && arg.vt != VT_UNKNOWN) || type == QVariant::Pixmap || type == QVariant::Font) + clearVARIANT(&arg); + } + break; + + case QMetaObject::WriteProperty: + { + QVariant::Type t = (QVariant::Type)prop.type(); + + DISPID dispidNamed = DISPID_PROPERTYPUT; + params.cArgs = 1; + params.cNamedArgs = 1; + params.rgdispidNamedArgs = &dispidNamed; + params.rgvarg = &arg; + + arg.vt = VT_ERROR; + arg.scode = DISP_E_TYPEMISMATCH; + + // map void* to VARIANTARG via QVariant + QVariant qvar; + if (prop.isEnumType()) { + qvar = *(int*)v[0]; + proptype = 0; + } else { + if (t == QVariant::LastType) { + qvar = *(QVariant*)v[0]; + proptype = 0; + } else if (t == QVariant::UserType) { + qvar = QVariant(qRegisterMetaType<void*>(prop.typeName()), (void**)v[0]); +// qVariantSetValue(qvar, *(void**)v[0], prop.typeName()); + } else { + proptype = d->metaObject()->propertyType(propname); + qvar = QVariant(t, v[0]); + } + } + + QVariantToVARIANT(qvar, arg, proptype); + if (arg.vt == VT_EMPTY || arg.vt == VT_ERROR) { + qWarning("QAxBase::setProperty: Unhandled property type %s", prop.typeName()); + break; + } + } + hres = disp->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, ¶ms, 0, &excepinfo, &argerr); + clearVARIANT(&arg); + break; + + default: + break; + } + + checkHRESULT(hres, &excepinfo, this, QLatin1String(propname), argerr); + return index; +} + +int QAxBase::internalInvoke(QMetaObject::Call call, int index, void **v) +{ + Q_ASSERT(call == QMetaObject::InvokeMetaMethod); + Q_UNUSED(call); + + // get the IDispatch + IDispatch *disp = d->dispatch(); + if (!disp) + return index; + + const QMetaObject *mo = metaObject(); + // get the slot information + const QMetaMethod slot = mo->method(index + mo->methodOffset()); + Q_ASSERT(slot.methodType() == QMetaMethod::Slot); + + QByteArray signature(slot.signature()); + QByteArray slotname(signature); + slotname.truncate(slotname.indexOf('(')); + + // Get the Dispatch ID of the method to be called + bool isProperty = false; + DISPID dispid = d->metaObject()->dispIDofName(slotname, disp); + + Q_ASSERT(d->metaobj); + + if (dispid == DISPID_UNKNOWN && slotname.toLower().startsWith("set")) { + // see if we are calling a property set function as a slot + slotname = slotname.right(slotname.length() - 3); + dispid = d->metaobj->dispIDofName(slotname, disp); + isProperty = true; + } + if (dispid == DISPID_UNKNOWN) + return index; + + // slot found, so everthing that goes wrong now should not bother the caller + index -= mo->methodCount(); + + // setup the parameters + DISPPARAMS params; + DISPID dispidNamed = DISPID_PROPERTYPUT; + params.cArgs = d->metaobj->numParameter(signature); + params.cNamedArgs = isProperty ? 1 : 0; + params.rgdispidNamedArgs = isProperty ? &dispidNamed : 0; + params.rgvarg = 0; + VARIANTARG static_rgvarg[QAX_NUM_PARAMS]; + if (params.cArgs) { + if (params.cArgs <= QAX_NUM_PARAMS) + params.rgvarg = static_rgvarg; + else + params.rgvarg = new VARIANTARG[params.cArgs]; + } + + int p; + for (p = 0; p < (int)params.cArgs; ++p) { + bool out; + QByteArray type = d->metaobj->paramType(signature, p, &out); + QVariant::Type vt = QVariant::nameToType(type); + QVariant qvar; + if (vt != QVariant::UserType) + qvar = QVariant(vt, v[p + 1]); + + if (!qvar.isValid()) { + if (type == "IDispatch*") + qVariantSetValue(qvar, *(IDispatch**)v[p+1]); + else if (type == "IUnknown*") + qVariantSetValue(qvar, *(IUnknown**)v[p+1]); + else if (type == "QVariant") + qvar = *(QVariant*)v[p + 1]; + else if (mo->indexOfEnumerator(type) != -1) + qvar = *(int*)v[p + 1]; + else + qvar = QVariant(QMetaType::type(type), v[p + 1]); + } + + QVariantToVARIANT(qvar, params.rgvarg[params.cArgs - p - 1], type, out); + } + + // call the method + VARIANT ret; + VariantInit(&ret); + UINT argerr = 0; + HRESULT hres = E_FAIL; + EXCEPINFO excepinfo; + memset(&excepinfo, 0, sizeof(excepinfo)); + + WORD wFlags = isProperty ? DISPATCH_PROPERTYPUT : DISPATCH_METHOD | DISPATCH_PROPERTYGET; + hres = disp->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, wFlags, ¶ms, &ret, &excepinfo, &argerr); + + // get return value + if (hres == S_OK && ret.vt != VT_EMPTY) + QVariantToVoidStar(VARIANTToQVariant(ret, slot.typeName()), v[0], slot.typeName()); + + // update out parameters + for (p = 0; p < (int)params.cArgs; ++p) { + bool out; + QByteArray ptype = d->metaobj->paramType(signature, p, &out); + if (out) + QVariantToVoidStar(VARIANTToQVariant(params.rgvarg[params.cArgs - p - 1], ptype), v[p+1], ptype); + } + // clean up + for (p = 0; p < (int)params.cArgs; ++p) + clearVARIANT(params.rgvarg+p); + if (params.rgvarg != static_rgvarg) + delete [] params.rgvarg; + + checkHRESULT(hres, &excepinfo, this, QString::fromLatin1(slotname), params.cArgs-argerr-1); + return index; +} + +/*! + \internal +*/ +int QAxBase::qt_metacall(QMetaObject::Call call, int id, void **v) +{ + const QMetaObject *mo = metaObject(); + if (isNull() && mo->property(id + mo->propertyOffset()).name() != QByteArray("control")) { + qWarning("QAxBase::qt_metacall: Object is not initialized, or initialization failed"); + return id; + } + + switch(call) { + case QMetaObject::InvokeMetaMethod: + switch (mo->method(id + mo->methodOffset()).methodType()) { + case QMetaMethod::Signal: + QMetaObject::activate(qObject(), mo, id, v); + id -= mo->methodCount(); + break; + case QMetaMethod::Method: + case QMetaMethod::Slot: + id = internalInvoke(call, id, v); + break; + } + break; + case QMetaObject::ReadProperty: + case QMetaObject::WriteProperty: + case QMetaObject::ResetProperty: + id = internalProperty(call, id, v); + break; + case QMetaObject::QueryPropertyScriptable: + case QMetaObject::QueryPropertyDesignable: + case QMetaObject::QueryPropertyStored: + case QMetaObject::QueryPropertyEditable: + case QMetaObject::QueryPropertyUser: + id -= mo->propertyCount(); + break; + } + Q_ASSERT(id < 0); + return id; +} + +#ifdef QT_CHECK_STATE +static void qax_noSuchFunction(int disptype, const QByteArray &name, const QByteArray &function, const QAxBase *that) +{ + const QMetaObject *metaObject = that->metaObject(); + const char *coclass = metaObject->classInfo(metaObject->indexOfClassInfo("CoClass")).value(); + + if (disptype == DISPATCH_METHOD) { + qWarning("QAxBase::dynamicCallHelper: %s: No such method in %s [%s]", name.data(), that->control().toLatin1().data(), coclass ? coclass: "unknown"); + qWarning("\tCandidates are:"); + for (int i = 0; i < metaObject->methodCount(); ++i) { + const QMetaMethod slot(metaObject->method(i)); + if (slot.methodType() != QMetaMethod::Slot) + continue; + QByteArray signature = slot.signature(); + if (signature.toLower().startsWith(function.toLower())) + qWarning("\t\t%s", signature.data()); + } + } else { + qWarning("QAxBase::dynamicCallHelper: %s: No such property in %s [%s]", name.data(), that->control().toLatin1().data(), coclass ? coclass: "unknown"); + if (!function.isEmpty()) { + qWarning("\tCandidates are:"); + char f0 = function.toLower().at(0); + for (int i = metaObject->propertyOffset(); i < metaObject->propertyCount(); ++i) { + QByteArray signature(metaObject->property(i).name()); + if (!signature.isEmpty() && signature.toLower().at(0) == f0) + qWarning("\t\t%s", signature.data()); + } + } + } +} +#endif + +/*! + \internal + + \a name is already normalized? +*/ +bool QAxBase::dynamicCallHelper(const char *name, void *inout, QList<QVariant> &vars, QByteArray &type) +{ + if (isNull()) { + qWarning("QAxBase::dynamicCallHelper: Object is not initialized, or initialization failed"); + return false; + } + + IDispatch *disp = d->dispatch(); + if (!disp) { + qWarning("QAxBase::dynamicCallHelper: Object does not support automation"); + return false; + } + + const QMetaObject *mo = metaObject(); + d->metaObject(); + Q_ASSERT(d->metaobj); + + int varc = vars.count(); + + QByteArray normFunction = QMetaObject::normalizedSignature(name); + QByteArray function(normFunction); + VARIANT staticarg[QAX_NUM_PARAMS]; + VARIANT *arg = 0; + VARIANTARG *res = (VARIANTARG*)inout; + + unsigned short disptype; + + int id = -1; + bool parse = false; + + if (function.contains('(')) { + disptype = DISPATCH_METHOD | DISPATCH_PROPERTYGET; + if (d->useMetaObject) + id = mo->indexOfSlot(function); + if (id >= 0) { + const QMetaMethod slot = mo->method(id); + Q_ASSERT(slot.methodType() == QMetaMethod::Slot); + function = slot.signature(); + type = slot.typeName(); + } + function.truncate(function.indexOf('(')); + parse = !varc && normFunction.length() > function.length() + 2; + if (parse) { + QString args = QLatin1String(normFunction); + args = args.mid(function.length() + 1); + // parse argument string int list of arguments + QString curArg; + const QChar *c = args.unicode(); + int index = 0; + bool inString = false; + bool inEscape = false; + while (index < (int)args.length()) { + QChar cc = *c; + ++c; + ++index; + switch(cc.toLatin1()) { + case 'n': + if (inEscape) + cc = QLatin1Char('\n'); + break; + case 'r': + if (inEscape) + cc = QLatin1Char('\r'); + break; + case 't': + if (inEscape) + cc = QLatin1Char('\t'); + break; + case '\\': + if (!inEscape && inString) { + inEscape = true; + continue; + } + break; + case '"': + if (!inEscape) { + inString = !inString; + curArg += cc; + continue; + } + break; + case ' ': + if (!inString && curArg.isEmpty()) + continue; + break; + case ',': + case ')': + if (inString) + break; + curArg = curArg.trimmed(); + if (curArg.at(0) == QLatin1Char('\"') && curArg.at(curArg.length()-1) == QLatin1Char('\"')) { + vars << curArg.mid(1, curArg.length() - 2); + } else { + bool isNumber = false; + bool isDouble = false; + int number = curArg.toInt(&isNumber); + double dbl = curArg.toDouble(&isDouble); + if (isNumber) { + vars << number; + } else if (isDouble) { + vars << dbl; + } else { + bool isEnum = false; + for (int enumIndex = 0; enumIndex < mo->enumeratorCount(); ++enumIndex) { + QMetaEnum metaEnum =mo->enumerator(enumIndex); + int value = metaEnum.keyToValue(curArg.toLatin1()); + if (value != -1 && !QByteArray(metaEnum.valueToKey(value)).isEmpty()) { + vars << value; + isEnum = true; + break; + } + } + if (!isEnum) + vars << curArg; + } + } + curArg.clear(); + continue; + default: + break; + } + inEscape = false; + curArg += cc; + } + + varc = vars.count(); + } + } else { + if (d->useMetaObject) + id = mo->indexOfProperty(normFunction); + + if (id >= 0) { + const QMetaProperty prop =mo->property(id); + type = prop.typeName(); + } + if (varc == 1) { + res = 0; + disptype = DISPATCH_PROPERTYPUT; + } else { + disptype = DISPATCH_PROPERTYGET; + } + } + if (varc) { + varc = qMin(varc, d->metaobj->numParameter(normFunction)); + arg = varc <= QAX_NUM_PARAMS ? staticarg : new VARIANT[varc]; + for (int i = 0; i < varc; ++i) { + QVariant var(vars.at(i)); + VariantInit(arg + (varc - i - 1)); + bool out = false; + QByteArray paramType; + if (disptype == DISPATCH_PROPERTYPUT) + paramType = type; + else if (parse || disptype == DISPATCH_PROPERTYGET) + paramType = 0; + else + paramType = d->metaobj->paramType(normFunction, i, &out); + + if (!parse && d->useMetaObject && var.type() == QVariant::String || var.type() == QVariant::ByteArray) { + int enumIndex =mo->indexOfEnumerator(paramType); + if (enumIndex != -1) { + QMetaEnum metaEnum =mo->enumerator(enumIndex); + QVariantToVARIANT(metaEnum.keyToValue(var.toByteArray()), arg[varc - i - 1], "int", out); + } + } + + if (arg[varc - i - 1].vt == VT_EMPTY) + QVariantToVARIANT(var, arg[varc - i - 1], paramType, out); + } + } + + DISPID dispid = d->metaobj->dispIDofName(function, disp); + if (dispid == DISPID_UNKNOWN && function.toLower().startsWith("set")) { + function = function.mid(3); + dispid = d->metaobj->dispIDofName(function, disp); + disptype = DISPATCH_PROPERTYPUT; + } + + if (dispid == DISPID_UNKNOWN) { +#ifdef QT_CHECK_STATE + qax_noSuchFunction(disptype, normFunction, function, this); +#endif + return false; + } + + DISPPARAMS params; + DISPID dispidNamed = DISPID_PROPERTYPUT; + + params.cArgs = varc; + params.cNamedArgs = (disptype == DISPATCH_PROPERTYPUT) ? 1 : 0; + params.rgdispidNamedArgs = (disptype == DISPATCH_PROPERTYPUT) ? &dispidNamed : 0; + params.rgvarg = arg; + EXCEPINFO excepinfo; + memset(&excepinfo, 0, sizeof(excepinfo)); + UINT argerr = 0; + + HRESULT hres = disp->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, disptype, ¶ms, res, &excepinfo, &argerr); + + if (disptype == (DISPATCH_METHOD|DISPATCH_PROPERTYGET) && hres == S_OK && varc) { + for (int i = 0; i < varc; ++i) + if (arg[varc-i-1].vt & VT_BYREF) // update out-parameters + vars[i] = VARIANTToQVariant(arg[varc-i-1], vars.at(i).typeName()); + } + + // clean up + for (int i = 0; i < varc; ++i) + clearVARIANT(params.rgvarg+i); + if (arg && arg != staticarg) + delete[] arg; + + return checkHRESULT(hres, &excepinfo, this, QLatin1String(function), varc-argerr-1); +} + + +/*! + Calls the COM object's method \a function, passing the + parameters \a var1, \a var1, \a var2, \a var3, \a var4, \a var5, + \a var6, \a var7 and \a var8, and returns the value returned by + the method, or an invalid QVariant if the method does not return + a value or when the function call failed. + + If \a function is a method of the object the string must be provided + as the full prototype, for example as it would be written in a + QObject::connect() call. + + \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 15 + + Alternatively a function can be called passing the parameters embedded + in the string, e.g. above function can also be invoked using + + \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 16 + + All parameters are passed as strings; it depends on the control whether + they are interpreted correctly, and is slower than using the prototype + with correctly typed parameters. + + If \a function is a property the string has to be the name of the + property. The property setter is called when \a var1 is a valid QVariant, + otherwise the getter is called. + + \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 17 + + Note that it is faster to get and set properties using + QObject::property() and QObject::setProperty(). + + dynamicCall() can also be used to call objects with a + \l{QAxBase::disableMetaObject()}{disabled metaobject} wrapper, + which can improve performance significantely, esp. when calling many + different objects of different types during an automation process. + ActiveQt will then however not validate parameters. + + It is only possible to call functions through dynamicCall() that + have parameters or return values of datatypes supported by + QVariant. See the QAxBase class documentation for a list of + supported and unsupported datatypes. If you want to call functions + that have unsupported datatypes in the parameter list, use + queryInterface() to retrieve the appropriate COM interface, and + use the function directly. + + \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 18 + + This is also more efficient. +*/ +QVariant QAxBase::dynamicCall(const char *function, + const QVariant &var1, + const QVariant &var2, + const QVariant &var3, + const QVariant &var4, + const QVariant &var5, + const QVariant &var6, + const QVariant &var7, + const QVariant &var8) +{ + QList<QVariant> vars; + QVariant var = var1; + int argc = 1; + while(var.isValid()) { + vars << var; + switch(++argc) { + case 2: var = var2; break; + case 3: var = var3; break; + case 4: var = var4; break; + case 5: var = var5; break; + case 6: var = var6; break; + case 7: var = var7; break; + case 8: var = var8; break; + default:var = QVariant(); break; + } + } + + return dynamicCall(function, vars); +} + +/*! + \overload + + Calls the COM object's method \a function, passing the + parameters in \a vars, and returns the value returned by + the method. If the method does not return a value or when + the function call failed this function returns an invalid + QVariant object. + + The QVariant objects in \a vars are updated when the method has + out-parameters. +*/ +QVariant QAxBase::dynamicCall(const char *function, QList<QVariant> &vars) +{ + VARIANTARG res; + VariantInit(&res); + + QByteArray rettype; + if (!dynamicCallHelper(function, &res, vars, rettype)) + return QVariant(); + + QVariant qvar = VARIANTToQVariant(res, rettype); + if ((res.vt != VT_DISPATCH && res.vt != VT_UNKNOWN) || qvar.type() == QVariant::Pixmap || qvar.type() == QVariant::Font) + clearVARIANT(&res); + + return qvar; +} + +/*! + Returns a pointer to a QAxObject wrapping the COM object provided + by the method or property \a name, passing passing the parameters + \a var1, \a var1, \a var2, \a var3, \a var4, \a var5, \a var6, + \a var7 and \a var8. + + If \a name is provided by a method the string must include the + full function prototype. + + If \a name is a property the string must be the name of the property, + and \a var1, ... \a var8 are ignored. + + The returned QAxObject is a child of this object (which is either of + type QAxObject or QAxWidget), and is deleted when this object is + deleted. It is however safe to delete the returned object yourself, + and you should do so when you iterate over lists of subobjects. + + COM enabled applications usually have an object model publishing + certain elements of the application as dispatch interfaces. Use + this method to navigate the hierarchy of the object model, e.g. + + \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 19 +*/ +QAxObject *QAxBase::querySubObject(const char *name, + const QVariant &var1, + const QVariant &var2, + const QVariant &var3, + const QVariant &var4, + const QVariant &var5, + const QVariant &var6, + const QVariant &var7, + const QVariant &var8) +{ + QList<QVariant> vars; + QVariant var = var1; + int argc = 1; + while(var.isValid()) { + vars << var; + switch(++argc) { + case 2: var = var2; break; + case 3: var = var3; break; + case 4: var = var4; break; + case 5: var = var5; break; + case 6: var = var6; break; + case 7: var = var7; break; + case 8: var = var8; break; + default:var = QVariant(); break; + } + } + + return querySubObject(name, vars); +} + +/*! + \overload + + The QVariant objects in \a vars are updated when the method has + out-parameters. +*/ +QAxObject *QAxBase::querySubObject(const char *name, QList<QVariant> &vars) +{ + QAxObject *object = 0; + VARIANTARG res; + VariantInit(&res); + + QByteArray rettype; + if (!dynamicCallHelper(name, &res, vars, rettype)) + return 0; + + switch (res.vt) { + case VT_DISPATCH: + if (res.pdispVal) { + if (rettype.isEmpty() || rettype == "IDispatch*" || rettype == "QVariant") { + object = new QAxObject(res.pdispVal, qObject()); + } else if (QMetaType::type(rettype)) { + QVariant qvar = VARIANTToQVariant(res, rettype, 0); + object = *(QAxObject**)qvar.constData(); +// qVariantGet(qvar, object, rettype); + res.pdispVal->AddRef(); + } + if (object) + ((QAxBase*)object)->d->tryCache = true; + } + break; + case VT_UNKNOWN: + if (res.punkVal) { + if (rettype.isEmpty() || rettype == "IUnknown*") { + object = new QAxObject(res.punkVal, qObject()); + } else if (QMetaType::type(rettype)) { + QVariant qvar = VARIANTToQVariant(res, rettype, 0); + object = *(QAxObject**)qvar.constData(); +// qVariantGet(qvar, object, rettype); + res.punkVal->AddRef(); + } + if (object) + ((QAxBase*)object)->d->tryCache = true; + } + break; + case VT_EMPTY: +#ifdef QT_CHECK_STATE + { + const char *coclass = metaObject()->classInfo(metaObject()->indexOfClassInfo("CoClass")).value(); + qWarning("QAxBase::querySubObject: %s: Error calling function or property in %s (%s)" + , name, control().toLatin1().data(), coclass ? coclass: "unknown"); + } +#endif + break; + default: +#ifdef QT_CHECK_STATE + { + const char *coclass = metaObject()->classInfo(metaObject()->indexOfClassInfo("CoClass")).value(); + qWarning("QAxBase::querySubObject: %s: Method or property is not of interface type in %s (%s)" + , name, control().toLatin1().data(), coclass ? coclass: "unknown"); + } +#endif + break; + } + + clearVARIANT(&res); + return object; +} + +class QtPropertyBag : public IPropertyBag +{ +public: + QtPropertyBag() :ref(0) {} + virtual ~QtPropertyBag() {} + + HRESULT __stdcall QueryInterface(REFIID iid, LPVOID *iface) + { + *iface = 0; + if (iid == IID_IUnknown) + *iface = this; + else if (iid == IID_IPropertyBag) + *iface = this; + else + return E_NOINTERFACE; + + AddRef(); + return S_OK; + } + unsigned long __stdcall AddRef() { return ++ref; } + unsigned long __stdcall Release() + { + if (!--ref) { + delete this; + return 0; + } + return ref; + } + + HRESULT __stdcall Read(LPCOLESTR name, VARIANT *var, IErrorLog *) + { + if (!var) + return E_POINTER; + + QString property = QString::fromUtf16((const ushort *)name); + QVariant qvar = map.value(property); + QVariantToVARIANT(qvar, *var); + return S_OK; + } + HRESULT __stdcall Write(LPCOLESTR name, VARIANT *var) + { + if (!var) + return E_POINTER; + QString property = QString::fromUtf16((const ushort *)name); + QVariant qvar = VARIANTToQVariant(*var, 0); + map[property] = qvar; + + return S_OK; + } + + QAxBase::PropertyBag map; + +private: + unsigned long ref; +}; + +/*! + Returns a name:value map of all the properties exposed by the COM + object. + + This is more efficient than getting multiple properties + individually if the COM object supports property bags. + + \warning It is not guaranteed that the property bag implementation + of the COM object returns all properties, or that the properties + returned are the same as those available through the IDispatch + interface. +*/ +QAxBase::PropertyBag QAxBase::propertyBag() const +{ + PropertyBag result; + + if (!d->ptr && !d->initialized) { + ((QAxBase*)this)->initialize(&d->ptr); + d->initialized = true; + } + + if (isNull()) + return result; + IPersistPropertyBag *persist = 0; + d->ptr->QueryInterface(IID_IPersistPropertyBag, (void**)&persist); + if (persist) { + QtPropertyBag *pbag = new QtPropertyBag(); + pbag->AddRef(); + persist->Save(pbag, false, true); + result = pbag->map; + pbag->Release(); + persist->Release(); + return result; + } else { + const QMetaObject *mo = metaObject(); + for (int p = mo->propertyOffset(); p < mo->propertyCount(); ++p) { + const QMetaProperty property = mo->property(p); + QVariant var = qObject()->property(property.name()); + result.insert(QLatin1String(property.name()), var); + } + } + return result; +} + +/*! + Sets the properties of the COM object to the corresponding values + in \a bag. + + \warning + You should only set property bags that have been returned by the + propertyBag function, as it cannot be guaranteed that the property + bag implementation of the COM object supports the same properties + that are available through the IDispatch interface. + + \sa propertyBag() +*/ +void QAxBase::setPropertyBag(const PropertyBag &bag) +{ + if (!d->ptr && !d->initialized) { + initialize(&d->ptr); + d->initialized = true; + } + + if (isNull()) + return; + IPersistPropertyBag *persist = 0; + d->ptr->QueryInterface(IID_IPersistPropertyBag, (void**)&persist); + if (persist) { + QtPropertyBag *pbag = new QtPropertyBag(); + pbag->map = bag; + pbag->AddRef(); + persist->Load(pbag, 0); + pbag->Release(); + persist->Release(); + } else { + const QMetaObject *mo = metaObject(); + for (int p = mo->propertyOffset(); p < mo->propertyCount(); ++p) { + const QMetaProperty property = mo->property(p); + QVariant var = bag.value(QLatin1String(property.name())); + qObject()->setProperty(property.name(), var); + } + } +} + +/*! + Returns true if the property \a prop is writable; otherwise + returns false. By default, all properties are writable. + + \warning + Depending on the control implementation this setting might be + ignored for some properties. + + \sa setPropertyWritable(), propertyChanged() +*/ +bool QAxBase::propertyWritable(const char *prop) const +{ + return d->propWritable.value(prop, true); +} + +/*! + Sets the property \a prop to writable if \a ok is true, otherwise + sets \a prop to be read-only. By default, all properties are + writable. + + \warning + Depending on the control implementation this setting might be + ignored for some properties. + + \sa propertyWritable(), propertyChanged() +*/ +void QAxBase::setPropertyWritable(const char *prop, bool ok) +{ + d->propWritable[prop] = ok; +} + +/*! + Returns true if there is no COM object loaded by this wrapper; + otherwise return false. + + \sa control +*/ +bool QAxBase::isNull() const +{ + return !d->ptr; +} + +/*! + Returns a QVariant that wraps the COM object. The variant can + then be used as a parameter in e.g. dynamicCall(). +*/ +QVariant QAxBase::asVariant() const +{ + if (!d->ptr && !d->initialized) { + ((QAxBase*)this)->initialize(&d->ptr); + d->initialized = true; + } + + QVariant qvar; + QByteArray cn(className()); + if (cn == "QAxObject" || cn == "QAxWidget" || cn == "QAxBase") { + if (d->dispatch()) + qVariantSetValue(qvar, d->dispatch()); + else if (d->ptr) + qVariantSetValue(qvar, d->ptr); + } else { + cn = cn.mid(cn.lastIndexOf(':') + 1); + QObject *object = qObject(); + if (QMetaType::type(cn)) + qvar = QVariant(qRegisterMetaType<QObject*>(cn + "*"), &object); +// qVariantSetValue(qvar, qObject(), cn + "*"); + } + + return qvar; +} + +// internal function that creates a QAxObject from an iface +// used by type-conversion code (types.cpp) +void *qax_createObjectWrapper(int metaType, IUnknown *iface) +{ + if (!iface) + return 0; + + QAxObject *object = (QAxObject*)QMetaType::construct(metaType, 0); + QAxBasePrivate *d = object->d; + + d->ptr = iface; + d->initialized = true; + + // no release, since no addref + + return object; +} + +/*! + \fn void QAxBase::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 doc/src/snippets/code/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. +*/ + +/*! + \fn void QAxBase::propertyChanged(const QString &name) + + If the COM object supports property notification, this signal gets + emitted when the property called \a name is changed. +*/ + +/*! + \fn void QAxBase::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]". +*/ + +/*! + \fn QObject *QAxBase::qObject() const + \internal +*/ + +/*! + \fn const char *QAxBase::className() const + \internal +*/ + +QT_END_NAMESPACE +#endif //QT_NO_WIN_ACTIVEQT diff --git a/src/activeqt/container/qaxbase.h b/src/activeqt/container/qaxbase.h new file mode 100644 index 0000000..7e128e5 --- /dev/null +++ b/src/activeqt/container/qaxbase.h @@ -0,0 +1,226 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the ActiveQt framework of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QAXBASE_H +#define QAXBASE_H + +#include <QtCore/qdatastream.h> +#include <QtCore/qmap.h> +#include <QtCore/qobject.h> +#include <QtCore/qvariant.h> + +struct IUnknown; +struct IDispatch; + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(ActiveQt) + +#ifndef QT_NO_WIN_ACTIVEQT + +struct QUuid; +class QAxEventSink; +class QAxObject; +class QAxBasePrivate; +struct QAxMetaObject; + +class QAxBase +{ + QDOC_PROPERTY(QString control READ control WRITE setControl) + +public: + typedef QMap<QString, QVariant> PropertyBag; + + QAxBase(IUnknown *iface = 0); + virtual ~QAxBase(); + + QString control() const; + + long queryInterface(const QUuid &, void**) const; + + QVariant dynamicCall(const char *name, const QVariant &v1 = QVariant(), + const QVariant &v2 = QVariant(), + const QVariant &v3 = QVariant(), + const QVariant &v4 = QVariant(), + const QVariant &v5 = QVariant(), + const QVariant &v6 = QVariant(), + const QVariant &v7 = QVariant(), + const QVariant &v8 = QVariant()); + QVariant dynamicCall(const char *name, QList<QVariant> &vars); + QAxObject *querySubObject(const char *name, const QVariant &v1 = QVariant(), + const QVariant &v2 = QVariant(), + const QVariant &v3 = QVariant(), + const QVariant &v4 = QVariant(), + const QVariant &v5 = QVariant(), + const QVariant &v6 = QVariant(), + const QVariant &v7 = QVariant(), + const QVariant &v8 = QVariant()); + QAxObject* querySubObject(const char *name, QList<QVariant> &vars); + + virtual const QMetaObject *metaObject() const; + virtual int qt_metacall(QMetaObject::Call, int, void **); + + virtual QObject *qObject() const = 0; + virtual const char *className() const = 0; + + PropertyBag propertyBag() const; + void setPropertyBag(const PropertyBag&); + + QString generateDocumentation(); + + virtual bool propertyWritable(const char*) const; + virtual void setPropertyWritable(const char*, bool); + + bool isNull() const; + + QStringList verbs() const; + + QVariant asVariant() const; + +#ifdef qdoc +Q_SIGNALS: + void signal(const QString&,int,void*); + void propertyChanged(const QString&); + void exception(int,const QString&,const QString&,const QString&); +#endif + +public: + virtual void clear(); + bool setControl(const QString&); + + void disableMetaObject(); + void disableClassInfo(); + void disableEventSink(); + +protected: + virtual bool initialize(IUnknown** ptr); + bool initializeRemote(IUnknown** ptr); + bool initializeLicensed(IUnknown** ptr); + bool initializeActive(IUnknown** ptr); + bool initializeFromFile(IUnknown** ptr); + + void internalRelease(); + void initializeFrom(QAxBase *that); + void connectNotify(); + long indexOfVerb(const QString &verb) const; + +private: + friend class QAxEventSink; + friend void *qax_createObjectWrapper(int, IUnknown*); + bool initializeLicensedHelper(void *factory, const QString &key, IUnknown **ptr); + QAxBasePrivate *d; + QAxMetaObject *internalMetaObject() const; + + virtual const QMetaObject *parentMetaObject() const = 0; + int internalProperty(QMetaObject::Call, int index, void **v); + int internalInvoke(QMetaObject::Call, int index, void **v); + bool dynamicCallHelper(const char *name, void *out, QList<QVariant> &var, QByteArray &type); + + static QMetaObject staticMetaObject; +}; + +#if defined Q_CC_MSVC && _MSC_VER < 1300 +template <> inline QAxBase *qobject_cast_helper<QAxBase*>(const QObject *o, QAxBase *) +#else +template <> inline QAxBase *qobject_cast<QAxBase*>(const QObject *o) +#endif +{ + void *result = o ? const_cast<QObject *>(o)->qt_metacast("QAxBase") : 0; + return (QAxBase*)(result); +} + +#if defined Q_CC_MSVC && _MSC_VER < 1300 +template <> inline QAxBase *qobject_cast_helper<QAxBase*>(QObject *o, QAxBase *) +#else +template <> inline QAxBase *qobject_cast<QAxBase*>(QObject *o) +#endif +{ + void *result = o ? o->qt_metacast("QAxBase") : 0; + return (QAxBase*)(result); +} + +extern QString qax_generateDocumentation(QAxBase *); + +inline QString QAxBase::generateDocumentation() +{ + return qax_generateDocumentation(this); +} + +#ifndef QT_NO_DATASTREAM +inline QDataStream &operator >>(QDataStream &s, QAxBase &c) +{ + QAxBase::PropertyBag bag; + c.qObject()->blockSignals(true); + QString control; + s >> control; + c.setControl(control); + s >> bag; + c.setPropertyBag(bag); + c.qObject()->blockSignals(false); + + return s; +} + +inline QDataStream &operator <<(QDataStream &s, const QAxBase &c) +{ + QAxBase::PropertyBag bag = c.propertyBag(); + s << c.control(); + s << bag; + + return s; +} +#endif // QT_NO_DATASTREAM + +QT_END_NAMESPACE + +#ifndef Q_COM_METATYPE_DECLARED +#define Q_COM_METATYPE_DECLARED + +Q_DECLARE_METATYPE(IUnknown*) +Q_DECLARE_METATYPE(IDispatch*) + +#endif + +#endif // QT_NO_WIN_ACTIVEQT + +QT_END_HEADER + +#endif // QAXBASE_H diff --git a/src/activeqt/container/qaxdump.cpp b/src/activeqt/container/qaxdump.cpp new file mode 100644 index 0000000..62ef0a4 --- /dev/null +++ b/src/activeqt/container/qaxdump.cpp @@ -0,0 +1,404 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the ActiveQt framework of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaxbase.h" + +#ifndef QT_NO_WIN_ACTIVEQT + +#include <qmetaobject.h> +#include <quuid.h> +#include <qt_windows.h> +#include <qtextstream.h> + +#include <ctype.h> + +#include "../shared/qaxtypes.h" + +QT_BEGIN_NAMESPACE + +QString qax_docuFromName(ITypeInfo *typeInfo, const QString &name) +{ + QString docu; + if (!typeInfo) + return docu; + + MEMBERID memId; + BSTR names = QStringToBSTR(name); + typeInfo->GetIDsOfNames((BSTR*)&names, 1, &memId); + SysFreeString(names); + if (memId != DISPID_UNKNOWN) { + BSTR docStringBstr, helpFileBstr; + ulong helpContext; + HRESULT hres = typeInfo->GetDocumentation(memId, 0, &docStringBstr, &helpContext, &helpFileBstr); + QString docString = QString::fromUtf16((const ushort *)docStringBstr); + QString helpFile = QString::fromUtf16((const ushort *)helpFileBstr); + SysFreeString(docStringBstr); + SysFreeString(helpFileBstr); + if (hres == S_OK) { + if (!docString.isEmpty()) + docu += docString + QLatin1String("\n"); + if (!helpFile.isEmpty()) + docu += QString::fromLatin1("For more information, see help context %1 in %2.").arg((uint)helpContext).arg(helpFile); + } + } + + return docu; +} + +static inline QString docuFromName(ITypeInfo *typeInfo, const QString &name) +{ + return QLatin1String("<p>") + qax_docuFromName(typeInfo, name) + QLatin1String("\n"); +} + +static QByteArray namedPrototype(const QList<QByteArray> ¶meterTypes, const QList<QByteArray> ¶meterNames, int numDefArgs = 0) +{ + QByteArray prototype("("); + for (int p = 0; p < parameterTypes.count(); ++p) { + QByteArray type(parameterTypes.at(p)); + prototype += type; + + if (p < parameterNames.count()) + prototype += " " + parameterNames.at(p); + + if (numDefArgs >= parameterTypes.count() - p) + prototype += " = 0"; + if (p < parameterTypes.count() - 1) + prototype += ", "; + } + prototype += ")"; + + return prototype; +} + +static QByteArray toType(const QByteArray &t) +{ + QByteArray type = t; + int vartype = QVariant::nameToType(type); + if (vartype == QVariant::Invalid) + type = "int"; + + if (type.at(0) == 'Q') + type = type.mid(1); + type[0] = toupper(type.at(0)); + if (type == "VariantList") + type = "List"; + else if (type == "Map<QVariant,QVariant>") + type = "Map"; + else if (type == "Uint") + type = "UInt"; + + return "to" + type + "()"; +} + +QString qax_generateDocumentation(QAxBase *that) +{ + that->metaObject(); + + if (that->isNull()) + return QString(); + + ITypeInfo *typeInfo = 0; + IDispatch *dispatch = 0; + that->queryInterface(IID_IDispatch, (void**)&dispatch); + if (dispatch) + dispatch->GetTypeInfo(0, LOCALE_SYSTEM_DEFAULT, &typeInfo); + + QString docu; + QTextStream stream(&docu, QIODevice::WriteOnly); + + const QMetaObject *mo = that->metaObject(); + QString coClass = QLatin1String(mo->classInfo(mo->indexOfClassInfo("CoClass")).value()); + + stream << "<h1 align=center>" << coClass << " Reference</h1>" << endl; + stream << "<p>The " << coClass << " COM object is a " << that->qObject()->metaObject()->className(); + stream << " with the CLSID " << that->control() << ".</p>" << endl; + + stream << "<h3>Interfaces</h3>" << endl; + stream << "<ul>" << endl; + const char *inter = 0; + int interCount = 1; + while ((inter = mo->classInfo(mo->indexOfClassInfo("Interface " + QByteArray::number(interCount))).value())) { + stream << "<li>" << inter << endl; + interCount++; + } + stream << "</ul>" << endl; + + stream << "<h3>Event Interfaces</h3>" << endl; + stream << "<ul>" << endl; + interCount = 1; + while ((inter = mo->classInfo(mo->indexOfClassInfo("Event Interface " + QByteArray::number(interCount))).value())) { + stream << "<li>" << inter << endl; + interCount++; + } + stream << "</ul>" << endl; + + QList<QString> methodDetails, propDetails; + + const int slotCount = mo->methodCount(); + if (slotCount) { + stream << "<h2>Public Slots:</h2>" << endl; + stream << "<ul>" << endl; + + int defArgCount = 0; + for (int islot = mo->methodOffset(); islot < slotCount; ++islot) { + const QMetaMethod slot = mo->method(islot); + if (slot.methodType() != QMetaMethod::Slot) + continue; + + if (slot.attributes() & QMetaMethod::Cloned) { + ++defArgCount; + continue; + } + + QByteArray returntype(slot.typeName()); + if (returntype.isEmpty()) + returntype = "void"; + QByteArray prototype = namedPrototype(slot.parameterTypes(), slot.parameterNames(), defArgCount); + QByteArray signature = slot.signature(); + QByteArray name = signature.left(signature.indexOf('(')); + stream << "<li>" << returntype << " <a href=\"#" << name << "\"><b>" << name << "</b></a>" << prototype << ";</li>" << endl; + + prototype = namedPrototype(slot.parameterTypes(), slot.parameterNames()); + QString detail = QString::fromLatin1("<h3><a name=") + QString::fromLatin1(name.constData()) + QLatin1String("></a>") + + QString::fromLatin1(returntype.constData()) + QLatin1String(" ") + + QString::fromLatin1(name.constData()) + QLatin1String(" ") + + QString::fromLatin1(prototype.constData()) + QLatin1String("<tt> [slot]</tt></h3>\n"); + prototype = namedPrototype(slot.parameterTypes(), QList<QByteArray>()); + detail += docuFromName(typeInfo, QString::fromLatin1(name.constData())); + detail += QLatin1String("<p>Connect a signal to this slot:<pre>\n"); + detail += QString::fromLatin1("\tQObject::connect(sender, SIGNAL(someSignal") + QString::fromLatin1(prototype.constData()) + + QLatin1String("), object, SLOT(") + QString::fromLatin1(name.constData()) + + QString::fromLatin1(prototype.constData()) + QLatin1String("));"); + detail += QLatin1String("</pre>\n"); + + if (1) { + detail += QLatin1String("<p>Or call the function directly:<pre>\n"); + + bool hasParams = slot.parameterTypes().count() != 0; + if (hasParams) + detail += QLatin1String("\tQVariantList params = ...\n"); + detail += QLatin1String("\t"); + QByteArray functionToCall = "dynamicCall"; + if (returntype == "IDispatch*" || returntype == "IUnknown*") { + functionToCall = "querySubObject"; + returntype = "QAxObject *"; + } + if (returntype != "void") + detail += QString::fromLatin1(returntype.constData()) + QLatin1String(" result = "); + detail += QLatin1String("object->") + QString::fromLatin1(functionToCall.constData()) + + QLatin1String("(\"" + name + prototype + "\""); + if (hasParams) + detail += QLatin1String(", params"); + detail += QLatin1String(")"); + if (returntype != "void" && returntype != "QAxObject *" && returntype != "QVariant") + detail += QLatin1String(".") + QString::fromLatin1(toType(returntype)); + detail += QLatin1String(";</pre>\n"); + } else { + detail += QLatin1String("<p>This function has parameters of unsupported types and cannot be called directly."); + } + + methodDetails << detail; + defArgCount = 0; + } + + stream << "</ul>" << endl; + } + int signalCount = mo->methodCount(); + if (signalCount) { + ITypeLib *typeLib = 0; + if (typeInfo) { + UINT index = 0; + typeInfo->GetContainingTypeLib(&typeLib, &index); + typeInfo->Release(); + } + typeInfo = 0; + + stream << "<h2>Signals:</h2>" << endl; + stream << "<ul>" << endl; + + for (int isignal = mo->methodOffset(); isignal < signalCount; ++isignal) { + const QMetaMethod signal(mo->method(isignal)); + if (signal.methodType() != QMetaMethod::Signal) + continue; + + QByteArray prototype = namedPrototype(signal.parameterTypes(), signal.parameterNames()); + QByteArray signature = signal.signature(); + QByteArray name = signature.left(signature.indexOf('(')); + stream << "<li>void <a href=\"#" << name << "\"><b>" << name << "</b></a>" << prototype << ";</li>" << endl; + + QString detail = QLatin1String("<h3><a name=") + QString::fromLatin1(name.constData()) + QLatin1String("></a>void ") + + QString::fromLatin1(name.constData()) + QLatin1String(" ") + + QString::fromLatin1(prototype.constData()) + QLatin1String("<tt> [signal]</tt></h3>\n"); + if (typeLib) { + interCount = 0; + do { + if (typeInfo) + typeInfo->Release(); + typeInfo = 0; + typeLib->GetTypeInfo(++interCount, &typeInfo); + QString typeLibDocu = docuFromName(typeInfo, QString::fromLatin1(name.constData())); + if (!typeLibDocu.isEmpty()) { + detail += typeLibDocu; + break; + } + } while (typeInfo); + } + prototype = namedPrototype(signal.parameterTypes(), QList<QByteArray>()); + detail += QLatin1String("<p>Connect a slot to this signal:<pre>\n"); + detail += QLatin1String("\tQObject::connect(object, SIGNAL(") + QString::fromLatin1(name.constData()) + + QString::fromLatin1(prototype.constData()) + + QLatin1String("), receiver, SLOT(someSlot") + QString::fromLatin1(prototype.constData()) + QLatin1String("));"); + detail += QLatin1String("</pre>\n"); + + methodDetails << detail; + if (typeInfo) + typeInfo->Release(); + typeInfo = 0; + } + stream << "</ul>" << endl; + + if (typeLib) + typeLib->Release(); + } + + const int propCount = mo->propertyCount(); + if (propCount) { + if (dispatch) + dispatch->GetTypeInfo(0, LOCALE_SYSTEM_DEFAULT, &typeInfo); + stream << "<h2>Properties:</h2>" << endl; + stream << "<ul>" << endl; + + for (int iprop = 0; iprop < propCount; ++iprop) { + const QMetaProperty prop = mo->property(iprop); + QByteArray name(prop.name()); + QByteArray type(prop.typeName()); + + stream << "<li>" << type << " <a href=\"#" << name << "\"><b>" << name << "</b></a>;</li>" << endl; + QString detail = QLatin1String("<h3><a name=") + QString::fromLatin1(name.constData()) + QLatin1String("></a>") + + QString::fromLatin1(type.constData()) + + QLatin1String(" ") + QString::fromLatin1(name.constData()) + QLatin1String("</h3>\n"); + detail += docuFromName(typeInfo, QString::fromLatin1(name)); + QVariant::Type vartype = QVariant::nameToType(type); + if (!prop.isReadable()) + continue; + + if (prop.isEnumType()) + vartype = QVariant::Int; + + if (vartype != QVariant::Invalid) { + detail += QLatin1String("<p>Read this property's value using QObject::property:<pre>\n"); + if (prop.isEnumType()) + detail += QLatin1String("\tint val = "); + else + detail += QLatin1String("\t") + QString::fromLatin1(type.constData()) + QLatin1String(" val = "); + detail += QLatin1String("object->property(\"") + QString::fromLatin1(name.constData()) + + QLatin1String("\").") + QString::fromLatin1(toType(type).constData()) + QLatin1String(";\n"); + detail += QLatin1String("</pre>\n"); + } else if (type == "IDispatch*" || type == "IUnknown*") { + detail += QLatin1String("<p>Get the subobject using querySubObject:<pre>\n"); + detail += QLatin1String("\tQAxObject *") + QString::fromLatin1(name.constData()) + + QLatin1String(" = object->querySubObject(\"") + QString::fromLatin1(name.constData()) + QLatin1String("\");\n"); + detail += QLatin1String("</pre>\n"); + } else { + detail += QLatin1String("<p>This property is of an unsupported type.\n"); + } + if (prop.isWritable()) { + detail += QLatin1String("Set this property' value using QObject::setProperty:<pre>\n"); + if (prop.isEnumType()) { + detail += QLatin1String("\tint newValue = ... // string representation of values also supported\n"); + } else { + detail += QLatin1String("\t") + QString::fromLatin1(type.constData()) + QLatin1String(" newValue = ...\n"); + } + detail += QLatin1String("\tobject->setProperty(\"") + QString::fromLatin1(name) + QLatin1String("\", newValue);\n"); + detail += QLatin1String("</pre>\n"); + detail += QLatin1String("Or using the "); + QByteArray setterSlot; + if (isupper(name.at(0))) { + setterSlot = "Set" + name; + } else { + QByteArray nameUp = name; + nameUp[0] = toupper(nameUp.at(0)); + setterSlot = "set" + nameUp; + } + detail += QLatin1String("<a href=\"#") + QString::fromLatin1(setterSlot) + QLatin1String("\">") + + QString::fromLatin1(setterSlot.constData()) + QLatin1String("</a> slot.\n"); + } + if (prop.isEnumType()) { + detail += QLatin1String("<p>See also <a href=\"#") + QString::fromLatin1(type) + + QLatin1String("\">") + QString::fromLatin1(type) + QLatin1String("</a>.\n"); + } + + propDetails << detail; + } + stream << "</ul>" << endl; + } + + const int enumCount = mo->enumeratorCount(); + if (enumCount) { + stream << "<hr><h2>Member Type Documentation</h2>" << endl; + for (int i = 0; i < enumCount; ++i) { + const QMetaEnum enumdata = mo->enumerator(i); + stream << "<h3><a name=" << enumdata.name() << "></a>" << enumdata.name() << "</h3>" << endl; + stream << "<ul>" << endl; + for (int e = 0; e < enumdata.keyCount(); ++e) { + stream << "<li>" << enumdata.key(e) << "\t=" << enumdata.value(e) << "</li>" << endl; + } + stream << "</ul>" << endl; + } + } + if (methodDetails.count()) { + stream << "<hr><h2>Member Function Documentation</h2>" << endl; + for (int i = 0; i < methodDetails.count(); ++i) + stream << methodDetails.at(i) << endl; + } + if (propDetails.count()) { + stream << "<hr><h2>Property Documentation</h2>" << endl; + for (int i = 0; i < propDetails.count(); ++i) + stream << propDetails.at(i) << endl; + } + + if (typeInfo) + typeInfo->Release(); + if (dispatch) + dispatch->Release(); + return docu; +} + +QT_END_NAMESPACE +#endif // QT_NO_WIN_ACTIVEQT diff --git a/src/activeqt/container/qaxobject.cpp b/src/activeqt/container/qaxobject.cpp new file mode 100644 index 0000000..bd71dfa --- /dev/null +++ b/src/activeqt/container/qaxobject.cpp @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the ActiveQt framework of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef UNICODE +#define UNICODE +#endif + +#include "qaxobject.h" + +#ifndef QT_NO_WIN_ACTIVEQT + +#include <quuid.h> +#include <qmetaobject.h> +#include <qstringlist.h> + +#include <windows.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QAxObject + \brief The QAxObject class provides a QObject that wraps a COM object. + + \inmodule QAxContainer + + A QAxObject can be instantiated as an empty object, with the name + of the COM object it should wrap, or with a pointer to the + IUnknown that represents an existing COM object. If the COM object + implements the \c IDispatch interface, the properties, methods and + events of that object become available as Qt properties, slots and + signals. The base class, QAxBase, provides an API to access the + COM object directly through the IUnknown pointer. + + QAxObject is a QObject and can be used as such, e.g. it can be + organized in an object hierarchy, receive events and connect to + signals and slots. + + QAxObject also inherits most of its ActiveX-related functionality + from QAxBase, notably dynamicCall() and querySubObject(). + + \warning + You can subclass QAxObject, but you cannot use the 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 QAxObject as a member of + the QObject subclass. + + \sa QAxBase, QAxWidget, QAxScript, {ActiveQt Framework} +*/ + +/*! + Creates an empty COM object and propagates \a parent to the + QObject constructor. To initialize the object, call \link + QAxBase::setControl() setControl \endlink. +*/ +QAxObject::QAxObject(QObject *parent) +: QObject(parent) +{ +} + +/*! + Creates a QAxObject that wraps the COM object \a c. \a parent is + propagated to the QObject constructor. + + \sa setControl() +*/ +QAxObject::QAxObject(const QString &c, QObject *parent) +: QObject(parent) +{ + setControl(c); +} + +/*! + Creates a QAxObject that wraps the COM object referenced by \a + iface. \a parent is propagated to the QObject constructor. +*/ +QAxObject::QAxObject(IUnknown *iface, QObject *parent) +: QObject(parent), QAxBase(iface) +{ +} + +/*! + Releases the COM object and destroys the QAxObject, + cleaning up all allocated resources. +*/ +QAxObject::~QAxObject() +{ + clear(); +} + +/*! + \reimp +*/ +const QMetaObject *QAxObject::metaObject() const +{ + return QAxBase::metaObject(); +} + +/*! + \reimp +*/ +const QMetaObject *QAxObject::parentMetaObject() const +{ + return &QObject::staticMetaObject; +} + +/*! + \internal +*/ +void *QAxObject::qt_metacast(const char *cname) +{ + if (!qstrcmp(cname, "QAxObject")) return (void*)this; + if (!qstrcmp(cname, "QAxBase")) return (QAxBase*)this; + return QObject::qt_metacast(cname); +} + +/*! + \reimp +*/ +const char *QAxObject::className() const +{ + return "QAxObject"; +} + +/*! + \reimp +*/ +int QAxObject::qt_metacall(QMetaObject::Call call, int id, void **v) +{ + id = QObject::qt_metacall(call, id, v); + if (id < 0) + return id; + return QAxBase::qt_metacall(call, id, v); +} + +/*! + \fn QObject *QAxObject::qObject() const + \internal +*/ + +/*! + \reimp +*/ +void QAxObject::connectNotify(const char *) +{ + QAxBase::connectNotify(); +} + +/*! + \since 4.1 + + Requests the COM object 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 QAxObject::doVerb(const QString &verb) +{ + if (!verbs().contains(verb)) + return false; + IOleObject *ole = 0; + queryInterface(IID_IOleObject, (void**)&ole); + if (!ole) + return false; + + LONG index = indexOfVerb(verb); + + HRESULT hres = ole->DoVerb(index, 0, 0, 0, 0, 0); + + ole->Release(); + + return hres == S_OK; +} + +QT_END_NAMESPACE +#endif // QT_NO_WIN_ACTIVEQT diff --git a/src/activeqt/container/qaxobject.h b/src/activeqt/container/qaxobject.h new file mode 100644 index 0000000..ffa2087 --- /dev/null +++ b/src/activeqt/container/qaxobject.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the ActiveQt framework of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QAXOBJECT_H +#define QAXOBJECT_H + +#include <ActiveQt/qaxbase.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(ActiveQt) + +#ifndef QT_NO_WIN_ACTIVEQT + +class QAxObject : public QObject, public QAxBase +{ + friend class QAxEventSink; +public: + const QMetaObject *metaObject() const; + void* qt_metacast(const char*); + int qt_metacall(QMetaObject::Call, int, void **); + QObject* qObject() const { return (QObject*)this; } + const char *className() const; + + QAxObject(QObject *parent = 0); + QAxObject(const QString &c, QObject *parent = 0); + QAxObject(IUnknown *iface, QObject *parent = 0); + ~QAxObject(); + + bool doVerb(const QString &verb); + +protected: + void connectNotify(const char *signal); + +private: + const QMetaObject *parentMetaObject() const; + static QMetaObject staticMetaObject; +}; + +#if defined Q_CC_MSVC && _MSC_VER < 1300 +template <> inline QAxObject *qobject_cast_helper<QAxObject*>(const QObject *o, QAxObject *) +#else +template <> inline QAxObject *qobject_cast<QAxObject*>(const QObject *o) +#endif +{ + void *result = o ? const_cast<QObject *>(o)->qt_metacast("QAxObject") : 0; + return (QAxObject*)(result); +} + +#if defined Q_CC_MSVC && _MSC_VER < 1300 +template <> inline QAxObject *qobject_cast_helper<QAxObject*>(QObject *o, QAxObject *) +#else +template <> inline QAxObject *qobject_cast<QAxObject*>(QObject *o) +#endif +{ + void *result = o ? o->qt_metacast("QAxObject") : 0; + return (QAxObject*)(result); +} + +QT_END_NAMESPACE +Q_DECLARE_METATYPE(QAxObject*) + +#endif // QT_NO_WIN_ACTIVEQT + +QT_END_HEADER + +#endif // QAXOBJECT_H diff --git a/src/activeqt/container/qaxscript.cpp b/src/activeqt/container/qaxscript.cpp new file mode 100644 index 0000000..c69fea0 --- /dev/null +++ b/src/activeqt/container/qaxscript.cpp @@ -0,0 +1,1293 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the ActiveQt framework of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef UNICODE +#define UNICODE +#endif + +#include "qaxscript.h" + +#ifndef QT_NO_WIN_ACTIVEQT + +#if defined(Q_CC_GNU) +# define QT_NO_QAXSCRIPT +#elif defined(Q_CC_BOR) && __BORLANDC__ < 0x560 +# define QT_NO_QAXSCRIPT +#endif + +#include <qapplication.h> +#include <qfile.h> +#include <qhash.h> +#include <qmetaobject.h> +#include <quuid.h> +#include <qwidget.h> + +#include <qt_windows.h> +#ifndef QT_NO_QAXSCRIPT +#include <initguid.h> +#include <activscp.h> +#endif + +#include "../shared/qaxtypes.h" + +QT_BEGIN_NAMESPACE + +struct QAxEngineDescriptor { QString name, extension, code; }; +static QList<QAxEngineDescriptor> engines; + +class QAxScriptManagerPrivate +{ +public: + QHash<QString, QAxScript*> scriptDict; + QHash<QString, QAxBase*> objectDict; +}; + +/* + \class QAxScriptSite + \brief The QAxScriptSite class implements a Windows Scripting Host + \internal + + The QAxScriptSite is used internally to communicate callbacks from the script + engine to the script manager. +*/ + +#ifndef QT_NO_QAXSCRIPT + +class QAxScriptSite : public IActiveScriptSite, public IActiveScriptSiteWindow +{ +public: + QAxScriptSite(QAxScript *script); + + ULONG WINAPI AddRef(); + ULONG WINAPI Release(); + HRESULT WINAPI QueryInterface(REFIID iid, void **ppvObject); + + HRESULT WINAPI GetLCID(LCID *plcid); + HRESULT WINAPI GetItemInfo(LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown **ppiunkItem, ITypeInfo **ppti); + HRESULT WINAPI GetDocVersionString(BSTR *pbstrVersion); + + HRESULT WINAPI OnScriptTerminate(const VARIANT *pvarResult, const EXCEPINFO *pexcepinfo); + HRESULT WINAPI OnStateChange(SCRIPTSTATE ssScriptState); + HRESULT WINAPI OnScriptError(IActiveScriptError *pscripterror); + HRESULT WINAPI OnEnterScript(); + HRESULT WINAPI OnLeaveScript(); + + HRESULT WINAPI GetWindow(HWND *phwnd); + HRESULT WINAPI EnableModeless(BOOL fEnable); + +protected: + QWidget *window() const; + +private: + QAxScript *script; + unsigned long ref; +}; + +/* + Constructs the site for the \a s. +*/ +QAxScriptSite::QAxScriptSite(QAxScript *s) +: script(s), ref(1) +{ +} + +/* + Implements IUnknown::AddRef +*/ +ULONG WINAPI QAxScriptSite::AddRef() +{ + return ++ref; +} + +/* + Implements IUnknown::Release +*/ +ULONG WINAPI QAxScriptSite::Release() +{ + if (!--ref) { + delete this; + return 0; + } + return ref; +} + +/* + Implements IUnknown::QueryInterface +*/ +HRESULT WINAPI QAxScriptSite::QueryInterface(REFIID iid, void **ppvObject) +{ + *ppvObject = 0; + if (iid == IID_IUnknown) + *ppvObject = (IUnknown*)(IActiveScriptSite*)this; + else if (iid == IID_IActiveScriptSite) + *ppvObject = (IActiveScriptSite*)this; + else if (iid == IID_IActiveScriptSiteWindow) + *ppvObject = (IActiveScriptSiteWindow*)this; + else + return E_NOINTERFACE; + + AddRef(); + return S_OK; +} + +/* + Implements IActiveScriptSite::GetLCID + + This method is not implemented. Use the system-defined locale. +*/ +HRESULT WINAPI QAxScriptSite::GetLCID(LCID * /*plcid*/) +{ + return E_NOTIMPL; +} + +/* + Implements IActiveScriptSite::GetItemInfo + + Tries to find the QAxBase for \a pstrName and returns the + relevant interfaces in \a item and \a type as requested through \a mask. +*/ +HRESULT WINAPI QAxScriptSite::GetItemInfo(LPCOLESTR pstrName, DWORD mask, IUnknown **item, ITypeInfo **type) +{ + if (item) + *item = 0; + else if (mask & SCRIPTINFO_IUNKNOWN) + return E_POINTER; + + if (type) + *type = 0; + else if (mask & SCRIPTINFO_ITYPEINFO) + return E_POINTER; + + QAxBase *object = script->findObject(QString::fromUtf16((const ushort*)pstrName)); + if (!object) + return TYPE_E_ELEMENTNOTFOUND; + + if (mask & SCRIPTINFO_IUNKNOWN) + object->queryInterface(IID_IUnknown, (void**)item); + if (mask & SCRIPTINFO_ITYPEINFO) { + IProvideClassInfo *classInfo = 0; + object->queryInterface(IID_IProvideClassInfo, (void**)&classInfo); + if (classInfo) { + classInfo->GetClassInfo(type); + classInfo->Release(); + } + } + return S_OK; +} + +/* + Implements IActiveScriptSite::GetDocVersionString + + This method is not implemented. The scripting engine should assume + that the script is in sync with the document. +*/ +HRESULT WINAPI QAxScriptSite::GetDocVersionString(BSTR * /*version*/) +{ + return E_NOTIMPL; +} + +/* + Implements IActiveScriptSite::OnScriptTerminate + + This method is usually not called, but if it is it fires + QAxScript::finished(). +*/ +HRESULT WINAPI QAxScriptSite::OnScriptTerminate(const VARIANT *result, const EXCEPINFO *exception) +{ + emit script->finished(); + + if (result && result->vt != VT_EMPTY) + emit script->finished(VARIANTToQVariant(*result, 0)); + if (exception) + emit script->finished(exception->wCode, + QString::fromUtf16((const ushort*)exception->bstrSource), + QString::fromUtf16((const ushort*)exception->bstrDescription), + QString::fromUtf16((const ushort*)exception->bstrHelpFile) + ); + return S_OK; +} + +/* + Implements IActiveScriptSite::OnEnterScript + + Fires QAxScript::entered() to inform the host that the + scripting engine has begun executing the script code. +*/ +HRESULT WINAPI QAxScriptSite::OnEnterScript() +{ + emit script->entered(); + return S_OK; +} + +/* + Implements IActiveScriptSite::OnLeaveScript + + Fires QAxScript::finished() to inform the host that the + scripting engine has returned from executing the script code. +*/ +HRESULT WINAPI QAxScriptSite::OnLeaveScript() +{ + emit script->finished(); + return S_OK; +} + +/* + Implements IActiveScriptSite::OnScriptError + + Fires QAxScript::error() to inform the host that an + that an execution error occurred while the engine was running the script. +*/ +HRESULT WINAPI QAxScriptSite::OnScriptError(IActiveScriptError *error) +{ + EXCEPINFO exception; + memset(&exception, 0, sizeof(exception)); + DWORD context; + ULONG lineNumber; + LONG charPos; + BSTR bstrLineText; + QString lineText; + + error->GetExceptionInfo(&exception); + error->GetSourcePosition(&context, &lineNumber, &charPos); + HRESULT hres = error->GetSourceLineText(&bstrLineText); + if (hres == S_OK) { + lineText = QString::fromUtf16((const ushort*)bstrLineText); + SysFreeString(bstrLineText); + } + SysFreeString(exception.bstrSource); + SysFreeString(exception.bstrDescription); + SysFreeString(exception.bstrHelpFile); + + emit script->error(exception.wCode, QString::fromUtf16((const ushort*)exception.bstrDescription), lineNumber, lineText); + + return S_OK; +} + +/* + Implements IActiveScriptSite::OnStateChange + + Fires QAxScript::stateChanged() to inform the + the host that the scripting engine has changed states. +*/ +HRESULT WINAPI QAxScriptSite::OnStateChange(SCRIPTSTATE ssScriptState) +{ + emit script->stateChanged(ssScriptState); + return S_OK; +} + +/* + \internal + Returns the toplevel widget parent of this script, or + the application' active window if there is no widget parent. +*/ +QWidget *QAxScriptSite::window() const +{ + QWidget *w = 0; + QObject *p = script->parent(); + while (!w && p) { + w = qobject_cast<QWidget*>(p); + p = p->parent(); + } + + if (w) + w = w->window(); + if (!w && qApp) + w = qApp->activeWindow(); + + return w; +} + +/* + Implements IActiveScriptSiteWindow::GetWindow + + Retrieves the handle to a window that can act as the owner of a + pop-up window that the scripting engine must display. +*/ +HRESULT WINAPI QAxScriptSite::GetWindow(HWND *phwnd) +{ + if (!phwnd) + return E_POINTER; + + *phwnd = 0; + QWidget *w = window(); + if (!w) + return E_FAIL; + + *phwnd = w->winId(); + return S_OK; +} + +/* + Implements IActiveScriptSiteWindow::EnableModeless + + Causes the host to enable or disable its main window + as well as any modeless dialog boxes. +*/ +HRESULT WINAPI QAxScriptSite::EnableModeless(BOOL fEnable) +{ + QWidget *w = window(); + if (!w) + return E_FAIL; + + EnableWindow(w->winId(), fEnable); + return S_OK; +} + +#endif //QT_NO_QAXSCRIPT + + +/*! + \class QAxScriptEngine + \brief The QAxScriptEngine class provides a wrapper around a script engine. + \inmodule QAxContainer + + Every instance of the QAxScriptEngine class represents an interpreter + for script code in a particular scripting language. The class is usually + not used directly. The QAxScript and QAxScriptManager classes provide + convenient functions to handle and call script code. + + Direct access to the script engine is provided through + queryInterface(). + + \warning This class is not available with the bcc5.5 and MingW + compilers. + + \sa QAxScript, QAxScriptManager, QAxBase, {ActiveQt Framework} +*/ + +/*! + \enum QAxScriptEngine::State + + The State enumeration defines the different states a script + engine can be in. + + \value Uninitialized The script has been created, but not yet initialized + \value Initialized The script has been initialized, but is not running + \value Started The script can execute code, but does not yet handle events + \value Connected The script can execute code and is connected so + that it can handle events + \value Disconnected The script is loaded, but is not connected to + event sources + \value Closed The script has been closed. +*/ + +/*! + Constructs a QAxScriptEngine object interpreting script code in \a language + provided by the code in \a script. This is usually done by the QAxScript + class when \link QAxScript::load() loading a script\endlink. + + Instances of QAxScriptEngine should always have both a language and a + script. +*/ +QAxScriptEngine::QAxScriptEngine(const QString &language, QAxScript *script) +: QAxObject(script), script_code(script), engine(0), script_language(language) +{ +#ifdef QT_CHECK_STATE + if (language.isEmpty()) + qWarning("QAxScriptEngine: created without language"); + + if (!script_code) + qWarning("QAxScriptEngine: created without script"); +#endif + setObjectName(QLatin1String("QAxScriptEngine_") + language); + disableClassInfo(); + disableEventSink(); +} + +/*! + Destroys the QAxScriptEngine object, releasing all allocated + resources. +*/ +QAxScriptEngine::~QAxScriptEngine() +{ +#ifndef QT_NO_QAXSCRIPT + if (engine) { + engine->SetScriptState(SCRIPTSTATE_DISCONNECTED); + engine->Close(); + engine->Release(); + } +#endif +} + +/*! + \fn QString QAxScriptEngine::scriptLanguage() const + Returns the scripting language, for example "VBScript", + or "JScript". +*/ + +/*! + \reimp +*/ +bool QAxScriptEngine::initialize(IUnknown **ptr) +{ + *ptr = 0; + +#ifndef QT_NO_QAXSCRIPT + if (!script_code || script_language.isEmpty()) + return false; + + CLSID clsid; + HRESULT hres = CLSIDFromProgID((WCHAR*)script_language.utf16(), &clsid); + if(FAILED(hres)) + return false; + + CoCreateInstance(clsid, 0, CLSCTX_INPROC_SERVER, IID_IActiveScript, (void**)&engine); + if (!engine) + return false; + + IActiveScriptParse *parser = 0; + engine->QueryInterface(IID_IActiveScriptParse, (void**)&parser); + if (!parser) { + engine->Release(); + engine = 0; + return false; + } + + if (engine->SetScriptSite(script_code->script_site) != S_OK) { + engine->Release(); + engine = 0; + return false; + } + if (parser->InitNew() != S_OK) { + parser->Release(); + engine->Release(); + engine = 0; + return false; + } + + BSTR bstrCode = QStringToBSTR(script_code->scriptCode()); +#ifdef Q_OS_WIN64 + hres = parser->ParseScriptText(bstrCode, 0, 0, 0, DWORDLONG(this), 0, SCRIPTTEXT_ISVISIBLE, 0, 0); +#else + hres = parser->ParseScriptText(bstrCode, 0, 0, 0, DWORD(this), 0, SCRIPTTEXT_ISVISIBLE, 0, 0); +#endif + SysFreeString(bstrCode); + + parser->Release(); + parser = 0; + + script_code->updateObjects(); + + if (engine->SetScriptState(SCRIPTSTATE_CONNECTED) != S_OK) { + engine = 0; + return false; + } + + IDispatch *scriptDispatch = 0; + engine->GetScriptDispatch(0, &scriptDispatch); + if (scriptDispatch) { + scriptDispatch->QueryInterface(IID_IUnknown, (void**)ptr); + scriptDispatch->Release(); + } +#endif + + return *ptr != 0; +} + +/*! + \fn bool QAxScriptEngine::isValid() const + + Returns true if the script engine has been initialized + correctly; otherwise returns false. +*/ + +/*! + Returns true if the script engine supports introspection; + otherwise returns false. +*/ +bool QAxScriptEngine::hasIntrospection() const +{ + if (!isValid()) + return false; + + IDispatch *scriptDispatch = 0; + QAxBase::queryInterface(IID_IDispatch, (void**)&scriptDispatch); + if (!scriptDispatch) + return false; + + UINT tic = 0; + HRESULT hres = scriptDispatch->GetTypeInfoCount(&tic); + scriptDispatch->Release(); + return hres == S_OK && tic > 0; +} + +/*! + Requests the interface \a uuid from the script engine object and + sets the value of \a iface to the provided interface, or to 0 if + the requested interface could not be provided. + + Returns the result of the QueryInterface implementation of the COM + object. +*/ +long QAxScriptEngine::queryInterface(const QUuid &uuid, void **iface) const +{ + *iface = 0; + if (!engine) + return E_NOTIMPL; + +#ifndef QT_NO_QAXSCRIPT + return engine->QueryInterface(uuid, iface); +#else + return E_NOTIMPL; +#endif +} + +/*! + Returns the state of the script engine. +*/ +QAxScriptEngine::State QAxScriptEngine::state() const +{ + if (!engine) + return Uninitialized; + +#ifndef QT_NO_QAXSCRIPT + SCRIPTSTATE state; + engine->GetScriptState(&state); + return (State)state; +#else + return Uninitialized; +#endif +} + +/*! + Sets the state of the script engine to \a st. + Calling this function is usually not necessary. +*/ +void QAxScriptEngine::setState(State st) +{ +#ifndef QT_NO_QAXSCRIPT + if (!engine) + return; + + engine->SetScriptState((SCRIPTSTATE)st); +#endif +} + +/*! + Registers an item with the script engine. Script code can + refer to this item using \a name. +*/ +void QAxScriptEngine::addItem(const QString &name) +{ +#ifndef QT_NO_QAXSCRIPT + if (!engine) + return; + + engine->AddNamedItem((WCHAR*)name.utf16(), SCRIPTITEM_ISSOURCE|SCRIPTITEM_ISVISIBLE); +#endif +} + +/*! + \class QAxScript + \brief The QAxScript class provides a wrapper around script code. + \inmodule QAxContainer + + Every instance of the QAxScript class represents a piece of + scripting code in a particular scripting language. The code is + loaded into the script engine using load(). Functions declared + in the code can be called using call(). + + The script provides scriptEngine() provides feedback to the + application through signals. The most important signal is the + error() signal. Direct access to the QAxScriptEngine is provided + through the scriptEngine() function. + + \warning This class is not available with the bcc5.5 and MingW + compilers. + + \sa QAxScriptEngine, QAxScriptManager, QAxBase, {ActiveQt Framework} +*/ + +/*! + \enum QAxScript::FunctionFlags + + This FunctionFlags enum describes formatting for function introspection. + + \value FunctionNames Only function names are returned. + \value FunctionSignatures Returns the functions with signatures. +*/ + +/*! + Constructs a QAxScript object called \a name and registers + it with the QAxScriptManager \a manager. This is usually done by the + QAxScriptManager class when \link QAxScriptManager::load() loading a + script\endlink. + + A script should always have a name. A manager is necessary to allow + the script code to reference objects in the application. The \a manager + takes ownership of the object. +*/ +QAxScript::QAxScript(const QString &name, QAxScriptManager *manager) +: QObject(manager), script_name(name), script_manager(manager), +script_engine(0) +{ + if (manager) { + manager->d->scriptDict.insert(name, this); + connect(this, SIGNAL(error(int,QString,int,QString)), + manager, SLOT(scriptError(int,QString,int,QString))); + } + +#ifndef QT_NO_QAXSCRIPT + script_site = new QAxScriptSite(this); +#else + script_site = 0; +#endif +} + +/*! + Destroys the object, releasing all allocated resources. +*/ +QAxScript::~QAxScript() +{ + delete script_engine; + script_engine = 0; + +#ifndef QT_NO_QAXSCRIPT + script_site->Release(); +#endif +} + +/*! + Loads the script source \a code written in language \a language + into the script engine. Returns true if \a code was successfully + entered into the script engine; otherwise returns false. + + If \a language is empty (the default) it will be determined + heuristically. If \a code contains the string \c {End Sub} it will + be interpreted as VBScript, otherwise as JScript. Additional + scripting languages can be registered using + QAxScript::registerEngine(). + + This function can only be called once for each QAxScript object, + which is done automatically when using QAxScriptManager::load(). +*/ +bool QAxScript::load(const QString &code, const QString &language) +{ + if (script_engine || code.isEmpty()) + return false; + + script_code = code; + QString lang = language; + if (lang.isEmpty()) { + if (code.contains(QLatin1String("End Sub"), Qt::CaseInsensitive)) + lang = QLatin1String("VBScript"); + + QList<QAxEngineDescriptor>::ConstIterator it; + for (it = engines.begin(); it != engines.end(); ++it) { + QAxEngineDescriptor engine = *it; + if (engine.code.isEmpty()) + continue; + + if (code.contains(engine.code)) { + lang = engine.name; + break; + } + } + } + if (lang.isEmpty()) + lang = QLatin1String("JScript"); + + script_engine = new QAxScriptEngine(lang, this); + // trigger call to initialize + script_engine->metaObject(); + + return script_engine->isValid(); +} + +/*! + Returns a list of all the functions in this script if the respective + script engine supports introspection; otherwise returns an empty list. + The functions are either provided with full prototypes or only as + names, depending on the value of \a flags. + + \sa QAxScriptEngine::hasIntrospection() +*/ +QStringList QAxScript::functions(FunctionFlags flags) const +{ + QStringList functions; + + const QMetaObject *mo = script_engine->metaObject(); + for (int i = mo->methodOffset(); i < mo->methodCount(); ++i) { + const QMetaMethod slot(mo->method(i)); + if (slot.methodType() != QMetaMethod::Slot || slot.access() != QMetaMethod::Public) + continue; + QString slotname = QString::fromLatin1(slot.signature()); + if (slotname.contains(QLatin1Char('_'))) + continue; + + if (flags == FunctionSignatures) + functions << slotname; + else + functions << slotname.left(slotname.indexOf(QLatin1Char('('))); + } + + return functions; +} + +/*! + Calls \a function, passing the parameters \a var1, \a var1, + \a var2, \a var3, \a var4, \a var5, \a var6, \a var7 and \a var8 + as arguments and returns the value returned by the function, or an + invalid QVariant if the function does not return a value or when + the function call failed. + + See QAxScriptManager::call() for more information about how to call + script functions. +*/ +QVariant QAxScript::call(const QString &function, const QVariant &var1, + const QVariant &var2, + const QVariant &var3, + const QVariant &var4, + const QVariant &var5, + const QVariant &var6, + const QVariant &var7, + const QVariant &var8) +{ + if (!script_engine) + return QVariant(); + + return script_engine->dynamicCall(function.toLatin1(), var1, var2, var3, var4, var5, var6, var7, var8); +} + +/*! + \overload + + Calls \a function passing \a arguments as parameters, and returns + the result. Returns when the script's execution has finished. + + See QAxScriptManager::call() for more information about how to call + script functions. +*/ +QVariant QAxScript::call(const QString &function, QList<QVariant> &arguments) +{ + if (!script_engine) + return QVariant(); + + return script_engine->dynamicCall(function.toLatin1(), arguments); +} + +/*! \internal + Registers all objects in the manager with the script engine. +*/ +void QAxScript::updateObjects() +{ + if (!script_manager) + return; + + script_manager->updateScript(this); +} + +/*! \internal + Returns the object \a name registered with the manager. +*/ +QAxBase *QAxScript::findObject(const QString &name) +{ + if (!script_manager) + return 0; + + return script_manager->d->objectDict.value(name); +} + +/*! \fn QString QAxScript::scriptName() const + Returns the name of the script. +*/ + +/*! \fn QString QAxScript::scriptCode() const + Returns the script's code, or the null-string if no + code has been loaded yet. + + \sa load() +*/ + +/*! \fn QAxScriptEngine* QAxScript::scriptEngine() const + Returns a pointer to the script engine. + + You can use the object returned to connect signals to the + script functions, or to access the script engine directly. +*/ + +/*! \fn void QAxScript::entered() + + This signal is emitted when a script engine has started executing code. +*/ + +/*! \fn void QAxScript::finished() + + This signal is emitted when a script engine has finished executing code. +*/ + +/*! + \fn void QAxScript::finished(const QVariant &result) + \overload + + \a result contains the script's result. This will be an invalid + QVariant if the script has no return value. +*/ + +/*! \fn void QAxScript::finished(int code, const QString &source, + const QString &description, const QString &help) + \overload + + \a code, \a source, \a description and \a help contain exception information + when the script terminated. +*/ + +/*! \fn void QAxScript::stateChanged(int state); + + This signal is emitted when a script engine changes state. + \a state can be any value in the QAxScriptEngineState enumeration. +*/ + +/*! + \fn void QAxScript::error(int code, const QString &description, + int sourcePosition, const QString &sourceText) + + This signal is emitted when an execution error occurred while + running a script. + + \a code, \a description, \a sourcePosition and \a sourceText + contain information about the execution error. +*/ + + + +/*! + \class QAxScriptManager + \brief The QAxScriptManager class provides a bridge between application objects + and script code. + \inmodule QAxContainer + + The QAxScriptManager acts as a bridge between the COM objects embedded + in the Qt application through QAxObject or QAxWidget, and the scripting + languages available through the Windows Script technologies, usually JScript + and VBScript. + + Create one QAxScriptManager for each separate document in your + application, and add the COM objects the scripts need to access + using addObject(). Then load() the script sources and invoke the + functions using call(). + + \warning This class is not available with the bcc5.5 and MingW + compilers. + + \sa QAxScript, QAxScriptEngine, QAxBase, {ActiveQt Framework} +*/ + +/*! + Creates a QAxScriptManager object. \a parent is passed on to the + QObject constructor. + + It is usual to create one QAxScriptManager for each document in an + application. +*/ +QAxScriptManager::QAxScriptManager(QObject *parent) +: QObject(parent) +{ + d = new QAxScriptManagerPrivate; +} + +/*! + Destroys the objects, releasing all allocated resources. +*/ +QAxScriptManager::~QAxScriptManager() +{ + delete d; +} + +/*! + Returns a list with all the functions that are available. + Functions provided by script engines that don't support + introspection are not included in the list. + The functions are either provided with full prototypes or + only as names, depending on the value of \a flags. +*/ +QStringList QAxScriptManager::functions(QAxScript::FunctionFlags flags) const +{ + QStringList functions; + + QHash<QString, QAxScript*>::ConstIterator scriptIt; + for (scriptIt = d->scriptDict.begin(); scriptIt != d->scriptDict.end(); ++scriptIt) { + QAxScript *script = scriptIt.value(); + functions += script->functions(flags); + } + + return functions; +} + +/*! + Returns a list with the names of all the scripts. +*/ +QStringList QAxScriptManager::scriptNames() const +{ + QStringList scripts; + + QHash<QString, QAxScript*>::ConstIterator scriptIt; + for (scriptIt = d->scriptDict.begin(); scriptIt != d->scriptDict.end(); ++scriptIt) { + scripts << scriptIt.key(); + } + + return scripts; +} + +/*! + Returns the script called \a name. + + You can use the returned pointer to call functions directly + through QAxScript::call(), to access the script engine directly, or + to delete and thus unload the script. +*/ +QAxScript *QAxScriptManager::script(const QString &name) const +{ + return d->scriptDict.value(name); +} + +/*! + Adds \a object to the manager. Scripts handled by this manager + can access the object in the code using the object's + \l{QObject::objectName}{objectName} property. + + You must add all the necessary objects before loading any scripts. +*/ +void QAxScriptManager::addObject(QAxBase *object) +{ + QObject *obj = object->qObject(); + QString name = obj->objectName(); + if (d->objectDict.contains(name)) + return; + + d->objectDict.insert(name, object); + connect(obj, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed(QObject*))); +} + +/*! \fn void QAxScriptManager::addObject(QObject *object) + \overload + + Adds a generic COM wrapper for \a object to the manager. \a object + must be exposed as a COM object using the functionality provided + by the QAxServer module. Applications + using this function you must link against the qaxserver library. +*/ + +/*! + Loads the script source \a code using the script engine for \a + language. The script can later be referred to using its \a name + which should not be empty. + + The function returns a pointer to the script for the given + \a code if the \a code was loaded successfully; otherwise it + returns 0. + + If \a language is empty it will be determined heuristically. If \a + code contains the string "End Sub" it will be interpreted as + VBScript, otherwise as JScript. Additional script engines can be + registered using registerEngine(). + + You must add all the objects necessary (using addObject()) \e + before loading any scripts. If \a code declares a function that is + already available (no matter in which language) the first function + is overloaded and can no longer be called via call(); but it will + still be available by calling its \link script() script \endlink + directly. + + \sa addObject(), scriptNames(), functions() +*/ +QAxScript *QAxScriptManager::load(const QString &code, const QString &name, const QString &language) +{ + QAxScript *script = new QAxScript(name, this); + if (script->load(code, language)) + return script; + + delete script; + return 0; +} + +/*! + \overload + + Loads the source code from the \a file. The script can later be + referred to using its \a name which should not be empty. + + The function returns a pointer to the script engine for the code + in \a file if \a file was loaded successfully; otherwise it + returns 0. + + The script engine used is determined from the file's extension. By + default ".js" files are interpreted as JScript files, and ".vbs" + and ".dsm" files are interpreted as VBScript. Additional script + engines can be registered using registerEngine(). +*/ +QAxScript *QAxScriptManager::load(const QString &file, const QString &name) +{ + QFile f(file); + if (!f.open(QIODevice::ReadOnly)) + return 0; + QByteArray data = f.readAll(); + QString contents = QString::fromLocal8Bit(data, data.size()); + f.close(); + + if (contents.isEmpty()) + return 0; + + QString language; + if (file.endsWith(QLatin1String(".js"))) { + language = QLatin1String("JScript"); + } else { + QList<QAxEngineDescriptor>::ConstIterator it; + for (it = engines.begin(); it != engines.end(); ++it) { + QAxEngineDescriptor engine = *it; + if (engine.extension.isEmpty()) + continue; + + if (file.endsWith(engine.extension)) { + language = engine.name; + break; + } + } + } + + if (language.isEmpty()) + language = QLatin1String("VBScript"); + + QAxScript *script = new QAxScript(name, this); + if (script->load(contents, language)) + return script; + + delete script; + return 0; +} + +/*! + Calls \a function, passing the parameters \a var1, \a var1, + \a var2, \a var3, \a var4, \a var5, \a var6, \a var7 and \a var8 + as arguments and returns the value returned by the function, or an + invalid QVariant if the function does not return a value or when + the function call failed. The call returns when the script's + execution has finished. + + In most script engines the only supported parameter type is "const + QVariant&", for example, to call a JavaScript function + \snippet doc/src/snippets/code/src_activeqt_container_qaxscript.cpp 0 + use + \snippet doc/src/snippets/code/src_activeqt_container_qaxscript.cpp 1 + As with \link QAxBase::dynamicCall() dynamicCall \endlink the + parameters can directly be embedded in the function string. + \snippet doc/src/snippets/code/src_activeqt_container_qaxscript.cpp 2 + However, this is slower. + + Functions provided by script engines that don't support + introspection are not available and must be called directly + using QAxScript::call() on the respective \link script() + script \endlink object. + + Note that calling this function can be significantely slower than + using call() on the respective QAxScript directly. +*/ +QVariant QAxScriptManager::call(const QString &function, const QVariant &var1, + const QVariant &var2, + const QVariant &var3, + const QVariant &var4, + const QVariant &var5, + const QVariant &var6, + const QVariant &var7, + const QVariant &var8) +{ + QAxScript *s = script(function); + if (!s) { +#ifdef QT_CHECK_STATE + qWarning("QAxScriptManager::call: No script provides function %s, or this function\n" + "\tis provided through an engine that does not support introspection", function.latin1()); +#endif + return QVariant(); + } + + return s->call(function, var1, var2, var3, var4, var5, var6, var7, var8); +} + +/*! \overload + + Calls \a function passing \a arguments as parameters, and returns + the result. Returns when the script's execution has finished. +*/ +QVariant QAxScriptManager::call(const QString &function, QList<QVariant> &arguments) +{ + QAxScript *s = script(function); + if (!s) { +#ifdef QT_CHECK_STATE + qWarning("QAxScriptManager::call: No script provides function %s, or this function\n" + "\tis provided through an engine that does not support introspection", function.latin1()); +#endif + return QVariant(); + } + + QList<QVariant> args(arguments); + return s->call(function, args); +} + +/*! + Registers the script engine called \a name and returns true if the + engine was found; otherwise does nothing and returns false. + + The script engine will be used when loading files with the given + \a extension, or when loading source code that contains the string + \a code. +*/ +bool QAxScriptManager::registerEngine(const QString &name, const QString &extension, const QString &code) +{ + if (name.isEmpty()) + return false; + + CLSID clsid; + HRESULT hres = CLSIDFromProgID((WCHAR*)name.utf16(), &clsid); + if (hres != S_OK) + return false; + + QAxEngineDescriptor engine; + engine.name = name; + engine.extension = extension; + engine.code = code; + + engines.prepend(engine); + return true; +} + +/*! + Returns a file filter listing all the supported script languages. + This filter string is convenient for use with QFileDialog. +*/ +QString QAxScriptManager::scriptFileFilter() +{ + QString allFiles = QLatin1String("Script Files (*.js *.vbs *.dsm"); + QString specialFiles = QLatin1String(";;VBScript Files (*.vbs *.dsm)" + ";;JavaScript Files (*.js)"); + + QList<QAxEngineDescriptor>::ConstIterator it; + for (it = engines.begin(); it != engines.end(); ++it) { + QAxEngineDescriptor engine = *it; + if (engine.extension.isEmpty()) + continue; + + allFiles += QLatin1String(" *") + engine.extension; + specialFiles += QLatin1String(";;") + engine.name + QLatin1String(" Files (*") + engine.extension + QLatin1String(")"); + } + allFiles += QLatin1String(")"); + + return allFiles + specialFiles + QLatin1String(";;All Files (*.*)"); +} + +/*! + \fn void QAxScriptManager::error(QAxScript *script, int code, const QString &description, + int sourcePosition, const QString &sourceText) + + This signal is emitted when an execution error occurred while + running \a script. + + \a code, \a description, \a sourcePosition and \a sourceText + contain information about the execution error. + + \warning Do not delete \a script in a slot connected to this signal. Use deleteLater() + instead. +*/ + +/*! + \internal + + Returns a pointer to the first QAxScript that knows + about \a function, or 0 if this function is unknown. +*/ +QAxScript *QAxScriptManager::scriptForFunction(const QString &function) const +{ + // check full prototypes if included + if (function.contains(QLatin1Char('('))) { + QHash<QString, QAxScript*>::ConstIterator scriptIt; + for (scriptIt = d->scriptDict.begin(); scriptIt != d->scriptDict.end(); ++scriptIt) { + QAxScript *script = scriptIt.value(); + + if (script->functions(QAxScript::FunctionSignatures).contains(function)) + return script; + } + } + + QString funcName = function; + funcName = funcName.left(funcName.indexOf(QLatin1Char('('))); + // second try, checking only names, not prototypes + QHash<QString, QAxScript*>::ConstIterator scriptIt; + for (scriptIt = d->scriptDict.begin(); scriptIt != d->scriptDict.end(); ++scriptIt) { + QAxScript *script = scriptIt.value(); + + if (script->functions(QAxScript::FunctionNames).contains(funcName)) + return script; + } + + return 0; +} + +/*! + \internal +*/ +void QAxScriptManager::updateScript(QAxScript *script) +{ + QHash<QString, QAxBase*>::ConstIterator objectIt; + for (objectIt = d->objectDict.constBegin(); objectIt != d->objectDict.constEnd(); ++objectIt) { + QString name = objectIt.key(); + + QAxScriptEngine *engine = script->scriptEngine(); + if (engine) + engine->addItem(name); + } +} + +/*! + \internal +*/ +void QAxScriptManager::objectDestroyed(QObject *o) +{ + d->objectDict.take(o->objectName()); +} + +/*! + \internal +*/ +void QAxScriptManager::scriptError(int code, const QString &desc, int spos, const QString &stext) +{ + QAxScript *source = qobject_cast<QAxScript*>(sender()); + emit error(source, code, desc, spos, stext); +} + +QT_END_NAMESPACE +#endif // QT_NO_WIN_ACTIVEQT diff --git a/src/activeqt/container/qaxscript.h b/src/activeqt/container/qaxscript.h new file mode 100644 index 0000000..3332715 --- /dev/null +++ b/src/activeqt/container/qaxscript.h @@ -0,0 +1,248 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the ActiveQt framework of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QAXSCRIPT_H +#define QAXSCRIPT_H + +#include <ActiveQt/qaxobject.h> + +struct IActiveScript; + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(ActiveQt) + +#ifndef QT_NO_WIN_ACTIVEQT + +class QAxBase; +class QAxScript; +class QAxScriptSite; +class QAxScriptEngine; +class QAxScriptManager; +class QAxScriptManagerPrivate; + +class QAxScriptEngine : public QAxObject +{ +public: + enum State { + Uninitialized = 0, + Initialized = 5, + Started = 1, + Connected = 2, + Disconnected = 3, + Closed = 4 + }; + + QAxScriptEngine(const QString &language, QAxScript *script); + ~QAxScriptEngine(); + + bool isValid() const; + bool hasIntrospection() const; + + QString scriptLanguage() const; + + State state() const; + void setState(State st); + + void addItem(const QString &name); + + long queryInterface(const QUuid &, void**) const; + +protected: + bool initialize(IUnknown** ptr); + +private: + QAxScript *script_code; + IActiveScript *engine; + + QString script_language; +}; + +class QAxScript : public QObject +{ + Q_OBJECT + +public: + enum FunctionFlags { + FunctionNames = 0, + FunctionSignatures + }; + + QAxScript(const QString &name, QAxScriptManager *manager); + ~QAxScript(); + + bool load(const QString &code, const QString &language = QString()); + + QStringList functions(FunctionFlags = FunctionNames) const; + + QString scriptCode() const; + QString scriptName() const; + QAxScriptEngine *scriptEngine() const; + + QVariant call(const QString &function, const QVariant &v1 = QVariant(), + const QVariant &v2 = QVariant(), + const QVariant &v3 = QVariant(), + const QVariant &v4 = QVariant(), + const QVariant &v5 = QVariant(), + const QVariant &v6 = QVariant(), + const QVariant &v7 = QVariant(), + const QVariant &v8 = QVariant()); + QVariant call(const QString &function, QList<QVariant> &arguments); + +Q_SIGNALS: + void entered(); + void finished(); + void finished(const QVariant &result); + void finished(int code, const QString &source,const QString &description, const QString &help); + void stateChanged(int state); + void error(int code, const QString &description, int sourcePosition, const QString &sourceText); + +private: + friend class QAxScriptSite; + friend class QAxScriptEngine; + + void updateObjects(); + QAxBase *findObject(const QString &name); + + QString script_name; + QString script_code; + QAxScriptManager *script_manager; + QAxScriptEngine *script_engine; + QAxScriptSite *script_site; +}; + +class QAxScriptManager : public QObject +{ + Q_OBJECT + +public: + QAxScriptManager(QObject *parent = 0); + ~QAxScriptManager(); + + void addObject(QAxBase *object); + void addObject(QObject *object); + + QStringList functions(QAxScript::FunctionFlags = QAxScript::FunctionNames) const; + QStringList scriptNames() const; + QAxScript *script(const QString &name) const; + + QAxScript* load(const QString &code, const QString &name, const QString &language); + QAxScript* load(const QString &file, const QString &name); + + QVariant call(const QString &function, const QVariant &v1 = QVariant(), + const QVariant &v2 = QVariant(), + const QVariant &v3 = QVariant(), + const QVariant &v4 = QVariant(), + const QVariant &v5 = QVariant(), + const QVariant &v6 = QVariant(), + const QVariant &v7 = QVariant(), + const QVariant &v8 = QVariant()); + QVariant call(const QString &function, QList<QVariant> &arguments); + + static bool registerEngine(const QString &name, const QString &extension, const QString &code = QString()); + static QString scriptFileFilter(); + +Q_SIGNALS: + void error(QAxScript *script, int code, const QString &description, int sourcePosition, const QString &sourceText); + +private Q_SLOTS: + void objectDestroyed(QObject *o); + void scriptError(int code, const QString &description, int sourcePosition, const QString &sourceText); + +private: + friend class QAxScript; + QAxScriptManagerPrivate *d; + + void updateScript(QAxScript*); + QAxScript *scriptForFunction(const QString &function) const; +}; + + +// QAxScript inlines + +inline QString QAxScript::scriptCode() const +{ + return script_code; +} + +inline QString QAxScript::scriptName() const +{ + return script_name; +} + +inline QAxScriptEngine *QAxScript::scriptEngine() const +{ + return script_engine; +} + +// QAxScriptEngine inlines + +inline bool QAxScriptEngine::isValid() const +{ + return engine != 0; +} + +inline QString QAxScriptEngine::scriptLanguage() const +{ + return script_language; +} + +// QAxScriptManager inlines + +extern QAxBase *qax_create_object_wrapper(QObject*); + +inline void QAxScriptManager::addObject(QObject *object) +{ + QAxBase *wrapper = qax_create_object_wrapper(object); + if (!wrapper) { + qWarning("QAxScriptMananger::addObject: Class %s not exposed through the QAxFactory", + object->metaObject()->className()); + Q_ASSERT(wrapper); + } + addObject(wrapper); +} + +QT_END_NAMESPACE +#endif // QT_NO_WIN_ACTIVEQT + +QT_END_HEADER + +#endif // QAXSCRIPT_H diff --git a/src/activeqt/container/qaxscriptwrapper.cpp b/src/activeqt/container/qaxscriptwrapper.cpp new file mode 100644 index 0000000..7515975 --- /dev/null +++ b/src/activeqt/container/qaxscriptwrapper.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the ActiveQt framework of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaxobject.h" + +#ifndef QT_NO_WIN_ACTIVEQT + +#include <ActiveQt/qaxfactory.h> + +#include <qt_windows.h> + +QT_BEGIN_NAMESPACE + +QAxBase *qax_create_object_wrapper(QObject *object) +{ + IDispatch *dispatch = 0; + QAxObject *wrapper = 0; + qAxFactory()->createObjectWrapper(object, &dispatch); + if (dispatch) { + wrapper = new QAxObject(dispatch, object); + wrapper->setObjectName(object->objectName()); + dispatch->Release(); + } + return wrapper; +} + +QT_END_NAMESPACE +#endif // QT_NO_WIN_ACTIVEQT diff --git a/src/activeqt/container/qaxselect.cpp b/src/activeqt/container/qaxselect.cpp new file mode 100644 index 0000000..7a47004 --- /dev/null +++ b/src/activeqt/container/qaxselect.cpp @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the ActiveQt framework of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaxselect.h" + +#ifndef QT_NO_WIN_ACTIVEQT + +#include <qt_windows.h> + +QT_BEGIN_NAMESPACE + +class ControlList : public QAbstractListModel +{ +public: + ControlList(QObject *parent=0) + : QAbstractListModel(parent) + { + HKEY classes_key; + QT_WA_INLINE( + RegOpenKeyExW(HKEY_CLASSES_ROOT, L"CLSID", 0, KEY_READ, &classes_key), + RegOpenKeyExA(HKEY_CLASSES_ROOT, "CLSID", 0, KEY_READ, &classes_key)); + if (!classes_key) + return; + + DWORD index = 0; + LONG result = 0; + TCHAR buffer[256]; + DWORD szBuffer = sizeof(buffer); + FILETIME ft; + do { + result = QT_WA_INLINE( + RegEnumKeyExW(classes_key, index, (wchar_t*)&buffer, &szBuffer, 0, 0, 0, &ft), + RegEnumKeyExA(classes_key, index, (char*)&buffer, &szBuffer, 0, 0, 0, &ft)); + szBuffer = sizeof(buffer); + if (result == ERROR_SUCCESS) { + HKEY sub_key; + QString clsid = QT_WA_INLINE(QString::fromUtf16((ushort*)buffer), QString::fromLocal8Bit((char*)buffer)); + result = QT_WA_INLINE( + RegOpenKeyExW(classes_key, reinterpret_cast<const wchar_t *>(QString(clsid + "\\Control").utf16()), 0, KEY_READ, &sub_key), + RegOpenKeyA(classes_key, QString(clsid + QLatin1String("\\Control")).toLocal8Bit(), &sub_key)); + if (result == ERROR_SUCCESS) { + RegCloseKey(sub_key); + QT_WA_INLINE( + RegistryQueryValueW(classes_key, buffer, (LPBYTE)buffer, &szBuffer), + RegQueryValueA(classes_key, (char*)buffer, (char*)buffer, (LONG*)&szBuffer)); + QString name = QT_WA_INLINE(QString::fromUtf16((ushort*)buffer, szBuffer / sizeof(TCHAR)) , QString::fromLocal8Bit((char*)buffer, szBuffer)); + + controls << name; + clsids.insert(name, clsid); + } + result = ERROR_SUCCESS; + } + szBuffer = sizeof(buffer); + ++index; + } while (result == ERROR_SUCCESS); + RegCloseKey(classes_key); + controls.sort(); + } + + LONG RegistryQueryValueW(HKEY hKey, LPCWSTR lpSubKey, LPBYTE lpData, LPDWORD lpcbData) + { + LONG ret = ERROR_FILE_NOT_FOUND; + HKEY hSubKey = NULL; + RegOpenKeyExW(hKey, lpSubKey, 0, KEY_READ, &hSubKey); + if (hSubKey) { + ret = RegQueryValueExW(hSubKey, 0, 0, 0, lpData, lpcbData); + RegCloseKey(hSubKey); + } + return ret; + } + + int rowCount(const QModelIndex & = QModelIndex()) const { return controls.count(); } + QVariant data(const QModelIndex &index, int role) const; + +private: + QStringList controls; + QMap<QString, QString> clsids; +}; + +QVariant ControlList::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (role == Qt::DisplayRole) + return controls.at(index.row()); + if (role == Qt::UserRole) + return clsids.value(controls.at(index.row())); + + return QVariant(); +} + +QAxSelect::QAxSelect(QWidget *parent, Qt::WindowFlags f) +: QDialog(parent, f) +{ +#ifndef QT_NO_CURSOR + QApplication::setOverrideCursor(Qt::WaitCursor); +#endif + + setupUi(this); + ActiveXList->setModel(new ControlList(this)); + connect(ActiveXList->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), + this, SLOT(on_ActiveXList_clicked(QModelIndex))); +#ifndef QT_NO_CURSOR + QApplication::restoreOverrideCursor(); +#endif + ActiveXList->setFocus(); + + connect(buttonOk, SIGNAL(clicked()), this, SLOT(accept())); + connect(buttonCancel, SIGNAL(clicked()), this, SLOT(reject())); +} + +void QAxSelect::on_ActiveXList_clicked(const QModelIndex &index) +{ + QVariant clsid = ActiveXList->model()->data(index, Qt::UserRole); + ActiveX->setText(clsid.toString()); +} + +void QAxSelect::on_ActiveXList_doubleClicked(const QModelIndex &index) +{ + QVariant clsid = ActiveXList->model()->data(index, Qt::UserRole); + ActiveX->setText(clsid.toString()); + + accept(); +} + +QT_END_NAMESPACE +#endif // QT_NO_WIN_ACTIVEQT diff --git a/src/activeqt/container/qaxselect.h b/src/activeqt/container/qaxselect.h new file mode 100644 index 0000000..7e41b3f --- /dev/null +++ b/src/activeqt/container/qaxselect.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the ActiveQt framework of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QAXSELECT_H +#define QAXSELECT_H + +#include <QtGui/qdialog.h> + +#ifndef QT_NO_WIN_ACTIVEQT +#include "ui_qaxselect.h" +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(ActiveQt) + +#ifndef QT_NO_WIN_ACTIVEQT + +class QAxSelect : public QDialog, private Ui::QAxSelect +{ + Q_OBJECT +public: + QAxSelect(QWidget *parent = 0, Qt::WindowFlags f = 0); + + QString clsid() const { return ActiveX->text(); } + +private Q_SLOTS: + void on_ActiveXList_clicked(const QModelIndex &index); + void on_ActiveXList_doubleClicked(const QModelIndex &index); +}; + +QT_END_NAMESPACE +#endif // QT_NO_WIN_ACTIVEQT + +QT_END_HEADER + +#endif // QAXSELECT_H diff --git a/src/activeqt/container/qaxselect.ui b/src/activeqt/container/qaxselect.ui new file mode 100644 index 0000000..f9c8d2c --- /dev/null +++ b/src/activeqt/container/qaxselect.ui @@ -0,0 +1,173 @@ +<ui version="4.0" stdsetdef="1" > + <class>QAxSelect</class> + <comment>********************************************************************* +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the ActiveQt framework of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +*********************************************************************</comment> + <widget class="QDialog" name="QAxSelect" > + <property name="objectName" > + <string notr="true" >QAxSelect</string> + </property> + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>439</width> + <height>326</height> + </rect> + </property> + <property name="windowTitle" > + <string>Select ActiveX Control</string> + </property> + <property name="sizeGripEnabled" > + <bool>true</bool> + </property> + <layout class="QGridLayout" > + <property name="objectName" > + <string notr="true" >unnamed</string> + </property> + <property name="margin" > + <number>11</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item rowspan="2" row="0" column="1" colspan="1" > + <layout class="QVBoxLayout" > + <property name="objectName" > + <string notr="true" >unnamed</string> + </property> + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QPushButton" name="buttonOk" > + <property name="objectName" > + <string notr="true" >buttonOk</string> + </property> + <property name="text" > + <string>OK</string> + </property> + <property name="autoDefault" > + <bool>true</bool> + </property> + <property name="default" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonCancel" > + <property name="objectName" > + <string notr="true" >buttonCancel</string> + </property> + <property name="text" > + <string>&Cancel</string> + </property> + <property name="autoDefault" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer name="Spacer2" > + <property name="sizeHint" > + <size> + <width>20</width> + <height>0</height> + </size> + </property> + <property name="sizeType" > + <enum>Expanding</enum> + </property> + <property name="orientation" > + <enum>Vertical</enum> + </property> + </spacer> + </item> + </layout> + </item> + <item row="0" column="0" > + <widget class="QListView" name="ActiveXList" > + <property name="objectName" > + <string notr="true" >ActiveXList</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <layout class="QHBoxLayout" > + <property name="objectName" > + <string notr="true" >unnamed</string> + </property> + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLabel" name="TextLabel1" > + <property name="objectName" > + <string notr="true" >TextLabel1</string> + </property> + <property name="text" > + <string>COM &Object:</string> + </property> + <property name="buddy" stdset="0" > + <cstring>ActiveX</cstring> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="ActiveX" > + <property name="objectName" > + <string notr="true" >ActiveX</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11" /> + <includes> + <include location="local" >qaxwidget.h</include> + </includes> +</ui> diff --git a/src/activeqt/container/qaxwidget.cpp b/src/activeqt/container/qaxwidget.cpp new file mode 100644 index 0000000..4e8473f --- /dev/null +++ b/src/activeqt/container/qaxwidget.cpp @@ -0,0 +1,2228 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the ActiveQt framework of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef UNICODE +#define UNICODE +#endif + + +#include "qaxwidget.h" + +#ifndef QT_NO_WIN_ACTIVEQT + +#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 <qregexp.h> +#include <quuid.h> +#include <qwhatsthis.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) +# if !defined(IOleInPlaceObjectWindowless) +# undef INTERFACE +# define INTERFACE IOleInPlaceObjectWindowless + 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 +#endif + +#include "../shared/qaxtypes.h" + +QT_BEGIN_NAMESPACE + +/* \class QAxHostWidget + \brief The QAxHostWidget class is the actual container widget. + + \internal +*/ +class QAxHostWidget : public QWidget +{ + friend class QAxClientSite; +public: + Q_OBJECT_CHECK + QAxHostWidget(QWidget *parent, QAxClientSite *ax); + ~QAxHostWidget(); + + QSize sizeHint() const; + QSize minimumSizeHint() const; + + int qt_metacall(QMetaObject::Call, int isignal, void **argv); + inline QAxClientSite *clientSite() const + { + return axhost; + } + +protected: + bool winEvent(MSG *msg, long *result); + bool event(QEvent *e); + bool eventFilter(QObject *o, QEvent *e); + void resizeEvent(QResizeEvent *e); + void focusInEvent(QFocusEvent *e); + void focusOutEvent(QFocusEvent *e); + void paintEvent(QPaintEvent *e); + void showEvent(QShowEvent *e); + QPaintEngine* paintEngine() const + { + return 0; + } + +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 +{ + 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 = 0; + else if (host == p) + host = 0; + } + + 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 = { host->x(), host->y(), host->x()+host->width(), host->y()+host->height() }; + return m_spOleObject->DoVerb(index, 0, this, 0, host->winId(), &rcPos); + } + + // IUnknown + unsigned long WINAPI AddRef(); + unsigned long WINAPI Release(); + STDMETHOD(QueryInterface)(REFIID iid, void **iface); + + // IDispatch + HRESULT __stdcall GetTypeInfoCount(unsigned int *) { return E_NOTIMPL; } + HRESULT __stdcall GetTypeInfo(UINT, LCID, ITypeInfo **) { return E_NOTIMPL; } + HRESULT __stdcall GetIDsOfNames(const _GUID &, wchar_t **, unsigned int, unsigned long, long *) { return E_NOTIMPL; } + HRESULT __stdcall Invoke(DISPID dispIdMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS *pDispParams, + VARIANT *pVarResult, + EXCEPINFO *pExcepInfo, + UINT *puArgErr); + 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(widget->winId(), hDC); + return S_OK; + } + STDMETHOD(InvalidateRect)(LPCRECT pRect, BOOL fErase) + { + ::InvalidateRect(host->winId(), pRect, fErase); + return S_OK; + } + STDMETHOD(InvalidateRgn)(HRGN hRGN, BOOL fErase) + { + ::InvalidateRgn(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: +#if !defined(Q_OS_WINCE) + struct OleMenuItem { + OleMenuItem(HMENU hm = 0, int ID = 0, QMenu *menu = 0) + : hMenu(hm), id(ID), subMenu(menu) + {} + HMENU hMenu; + int id; + QMenu *subMenu; + }; + QMenu *generatePopup(HMENU subMenu, QWidget *parent); +#endif + + IOleObject *m_spOleObject; + IOleControl *m_spOleControl; + IOleInPlaceObjectWindowless *m_spInPlaceObject; + IOleInPlaceActiveObject *m_spInPlaceActiveObject; + IOleDocumentView *m_spActiveView; + + QAxAggregated *aggregatedObject; + + bool inPlaceObjectWindowless :1; + bool inPlaceModelessEnabled :1; + bool canHostDocument : 1; + + DWORD m_dwOleObject; +#if !defined(Q_OS_WINCE) + HWND m_menuOwner; +#endif + CONTROLINFO control_info; + + QSize sizehint; + unsigned long ref; + QAxWidget *widget; + QAxHostWidget *host; +#if !defined(Q_OS_WINCE) + QPointer<QMenuBar> menuBar; + QMap<QAction*,OleMenuItem> menuItemMap; +#endif +}; + +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 = 0; + 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 = 0; + 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 QAbstractEventDispatcher::EventFilter previous_filter = 0; +#if QT_VERSION >= 0x050000 +#error "Fix QAbstractEventDispatcher::setEventFilter" +#endif +#if defined(Q_OS_WINCE) +static int filter_ref = 0; +#else +static const char *qaxatom = "QAxContainer4_Atom"; +#endif + +// The filter procedure listening to user interaction on the control +bool axc_FilterProc(void *m) +{ + MSG *msg = (MSG*)m; + const uint message = msg->message; + if ((message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) || (message >= WM_KEYFIRST && message <= WM_KEYLAST)) { + HWND hwnd = msg->hwnd; + QAxWidget *ax = 0; + QAxHostWidget *host = 0; + while (!host && hwnd) { + host = qobject_cast<QAxHostWidget*>(QWidget::find(hwnd)); + hwnd = ::GetParent(hwnd); + } + if (host) + ax = qobject_cast<QAxWidget*>(host->parentWidget()); + if (ax && msg->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]) { + QEvent::Type type = (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(); + QPoint gpos(GET_X_LPARAM(ol_pos), GET_Y_LPARAM(ol_pos)); + QPoint pos = ax->mapFromGlobal(gpos); + + QMouseEvent e(type, pos, gpos, (Qt::MouseButton)button, + translateMouseButtonState(msg->wParam), + translateModifierState(msg->wParam)); + QApplication::sendEvent(ax, &e); + } + } + } + } + } + + if (previous_filter) + return previous_filter(m); + + return false; +} + +QAxClientSite::QAxClientSite(QAxWidget *c) +: ref(1), widget(c), host(0), eventTranslated(true) +{ + aggregatedObject = widget->createAggregate(); + if (aggregatedObject) { + aggregatedObject->controlling_unknown = (IUnknown*)(IDispatch*)this; + aggregatedObject->the_object = c; + } + + m_spOleObject = 0; + m_spOleControl = 0; + m_spInPlaceObject = 0; + m_spInPlaceActiveObject = 0; + m_spActiveView = 0; + + inPlaceObjectWindowless = false; + inPlaceModelessEnabled = true; + canHostDocument = false; + + m_dwOleObject = 0; +#if !defined(Q_OS_WINCE) + m_menuOwner = 0; + menuBar = 0; +#endif + 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; + HRESULT hr = S_OK; + if (!m_spOleObject) + widget->queryInterface(IID_IOleObject, (void**)&m_spOleObject); + if (m_spOleObject) { + DWORD dwMiscStatus = 0; + m_spOleObject->GetMiscStatus(DVASPECT_CONTENT, &dwMiscStatus); + +#if !defined(Q_OS_WINCE) + IOleDocument *document = 0; + m_spOleObject->QueryInterface(IID_IOleDocument, (void**)&document); + if (document) { + IPersistStorage *persistStorage = 0; + document->QueryInterface(IID_IPersistStorage, (void**)&persistStorage); + if (persistStorage) { + // try to activate as document server + IStorage *storage = 0; + ILockBytes * bytes = 0; + HRESULT hres = ::CreateILockBytesOnHGlobal(0, TRUE, &bytes); + hres = ::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(); + } +#endif + + if (!canHostDocument) { + // activate as control + if(dwMiscStatus & OLEMISC_SETCLIENTSITEFIRST) + m_spOleObject->SetClientSite(this); + + if (!initialized) { + IPersistStreamInit *spPSI = 0; + m_spOleObject->QueryInterface(IID_IPersistStreamInit, (void**)&spPSI); + if (spPSI) { + if (data.length()) { + IStream *pStream = 0; + HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, data.length()); + if (hGlobal) { + BYTE *pStByte = (BYTE *)GlobalLock(hGlobal); + if (pStByte) + 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 = 0; + m_spOleObject->QueryInterface( IID_IPersistStorage, (void**)&spPS ); + if (spPS) { + HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, data.length()); + if (hGlobal) { +#if !defined(Q_OS_WINCE) + BYTE* pbData = (BYTE*)GlobalLock(hGlobal); + if (pbData) + memcpy(pbData, data.data(), data.length()); + GlobalUnlock(hGlobal); + // open an IStorage on the data and pass it to Load + LPLOCKBYTES pLockBytes = 0; + if (SUCCEEDED(CreateILockBytesOnHGlobal(hGlobal, TRUE, &pLockBytes))) { + LPSTORAGE pStorage = 0; + if (SUCCEEDED(StgOpenStorageOnILockBytes(pLockBytes, 0, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStorage))) { + spPS->Load(pStorage); + pStorage->Release(); + } + pLockBytes->Release(); + } + GlobalFree(hGlobal); +#endif + } + spPS->Release(); + } + } + } + + if(!(dwMiscStatus & OLEMISC_SETCLIENTSITEFIRST)) + m_spOleObject->SetClientSite(this); + } + + IViewObject *spViewObject = 0; + m_spOleObject->QueryInterface(IID_IViewObject, (void**) &spViewObject); + + m_spOleObject->Advise(this, &m_dwOleObject); + IAdviseSink *spAdviseSink = 0; + QueryInterface(IID_IAdviseSink, (void**)&spAdviseSink); + if (spAdviseSink && spViewObject) { + if (spViewObject) + spViewObject->SetAdvise(DVASPECT_CONTENT, 0, spAdviseSink); + spAdviseSink->Release(); + } + if (spViewObject) + spViewObject->Release(); + + m_spOleObject->SetHostNames(OLESTR("AXWIN"), 0); + + if (!(dwMiscStatus & OLEMISC_INVISIBLEATRUNTIME)) { + SIZEL hmSize; + hmSize.cx = MAP_PIX_TO_LOGHIM(250, widget->logicalDpiX()); + hmSize.cy = MAP_PIX_TO_LOGHIM(250, widget->logicalDpiY()); + + m_spOleObject->SetExtent(DVASPECT_CONTENT, &hmSize); + m_spOleObject->GetExtent(DVASPECT_CONTENT, &hmSize); + + sizehint.setWidth(MAP_LOGHIM_TO_PIX(hmSize.cx, widget->logicalDpiX())); + sizehint.setHeight(MAP_LOGHIM_TO_PIX(hmSize.cy, widget->logicalDpiY())); + showHost = true; + } else { + sizehint = QSize(0, 0); + host->hide(); + } + if (!(dwMiscStatus & OLEMISC_NOUIACTIVATE)) { + host->setFocusPolicy(Qt::StrongFocus); + } else { + host->setFocusPolicy(Qt::NoFocus); + } + + RECT rcPos = { host->x(), host->y(), host->x()+sizehint.width(), host->y()+sizehint.height() }; + + hr = m_spOleObject->DoVerb(OLEIVERB_INPLACEACTIVATE, 0, (IOleClientSite*)this, 0, host->winId(), &rcPos); + + if (!m_spOleControl) + m_spOleObject->QueryInterface(IID_IOleControl, (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::fromUtf16((const ushort *)userType)); + CoTaskMemFree(userType); + } + } else { + IObjectWithSite *spSite = 0; + widget->queryInterface(IID_IObjectWithSite, (void**)&spSite); + if (spSite) { + spSite->SetSite((IUnknown*)(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 = 0; + } + + if (aggregatedObject) + aggregatedObject->the_object = 0; + delete aggregatedObject; + delete host; +} + +void QAxClientSite::releaseAll() +{ + if (m_spOleObject) { + m_spOleObject->SetClientSite(0); + m_spOleObject->Unadvise(m_dwOleObject); + m_spOleObject->Release(); + } + m_spOleObject = 0; + if (m_spOleControl) m_spOleControl->Release(); + m_spOleControl = 0; + if (m_spInPlaceObject) m_spInPlaceObject->Release(); + m_spInPlaceObject = 0; + if (m_spInPlaceActiveObject) m_spInPlaceActiveObject->Release(); + m_spInPlaceActiveObject = 0; + + inPlaceObjectWindowless = false; +} + +void QAxClientSite::deactivate() +{ + if (m_spInPlaceObject) m_spInPlaceObject->InPlaceDeactivate(); + // if this assertion fails the control didn't call OnInPlaceDeactivate + Q_ASSERT(m_spInPlaceObject == 0); +} + +//**** IUnknown +unsigned long WINAPI QAxClientSite::AddRef() +{ + return ++ref; +} + +unsigned long WINAPI QAxClientSite::Release() +{ + if (!--ref) { + delete this; + return 0; + } + return ref; +} + +HRESULT WINAPI QAxClientSite::QueryInterface(REFIID iid, void **iface) +{ + *iface = 0; + + if (iid == IID_IUnknown) { + *iface = (IUnknown*)(IDispatch*)this; + } else { + HRESULT res = S_OK; + if (aggregatedObject) + res = aggregatedObject->queryInterface(iid, iface); + if (*iface) + return res; + } + + if (!(*iface)) { + if (iid == IID_IDispatch) + *iface = (IDispatch*)this; + else if (iid == IID_IOleClientSite) + *iface = (IOleClientSite*)this; + else if (iid == IID_IOleControlSite) + *iface = (IOleControlSite*)this; + else if (iid == IID_IOleWindow) + *iface = (IOleWindow*)(IOleInPlaceSite*)this; + else if (iid == IID_IOleInPlaceSite) + *iface = (IOleInPlaceSite*)this; +#ifdef QAX_SUPPORT_WINDOWLESS + else if (iid == IID_IOleInPlaceSiteEx) + *iface = (IOleInPlaceSiteEx*)this; + else if (iid == IID_IOleInPlaceSiteWindowless) + *iface = (IOleInPlaceSiteWindowless*)this; +#endif + else if (iid == IID_IOleInPlaceFrame) + *iface = (IOleInPlaceFrame*)this; + else if (iid == IID_IOleInPlaceUIWindow) + *iface = (IOleInPlaceUIWindow*)this; + else if (iid == IID_IOleDocumentSite && canHostDocument) + *iface = (IOleDocumentSite*)this; + else if (iid == IID_IAdviseSink) + *iface = (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 = 0; + return E_NOTIMPL; +} + +HRESULT WINAPI QAxClientSite::GetContainer(LPOLECONTAINER *ppContainer) +{ + if (!ppContainer) + return E_POINTER; + + *ppContainer = 0; + 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 = 0; + return E_NOTIMPL; +} + +HRESULT WINAPI QAxClientSite::TransformCoords(POINTL* /*pPtlHimetric*/, POINTF* /*pPtfContainer*/, DWORD /*dwFlags*/) +{ + return S_OK; +} + +HRESULT WINAPI QAxClientSite::TranslateAccelerator(LPMSG lpMsg, DWORD /*grfModifiers*/) +{ + eventTranslated = false; + if (lpMsg->message == WM_KEYDOWN && !lpMsg->wParam) + return S_OK; + QT_WA_INLINE( + SendMessage(host->winId(), lpMsg->message, lpMsg->wParam, lpMsg->lParam), + SendMessageA(host->winId(), lpMsg->message, lpMsg->wParam, lpMsg->lParam) + ); + 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 = 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); +#if !defined(Q_OS_WINCE) + OleLockRunning(m_spOleObject, true, false); +#endif + 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, (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, (void**)ppFrame); + QueryInterface(IID_IOleInPlaceUIWindow, (void**)ppDoc); + + ::GetClientRect(host->winId(), lprcPosRect); + ::GetClientRect(host->winId(), lprcClipRect); + + lpFrameInfo->cb = sizeof(OLEINPLACEFRAMEINFO); + lpFrameInfo->fMDIApp = false; + lpFrameInfo->haccel = 0; + lpFrameInfo->cAccelEntries = 0; + lpFrameInfo->hwndFrame = widget ? widget->window()->winId() : 0; + + 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 = 0; + inPlaceObjectWindowless = false; +#if !defined(Q_OS_WINCE) + OleLockRunning(m_spOleObject, false, false); +#endif + + 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 +#if defined(Q_OS_WINCE) +HRESULT WINAPI QAxClientSite::InsertMenus(HMENU /*hmenuShared*/, LPOLEMENUGROUPWIDTHS /*lpMenuWidths*/) +{ + return E_NOTIMPL; +#else +HRESULT WINAPI QAxClientSite::InsertMenus(HMENU /*hmenuShared*/, LPOLEMENUGROUPWIDTHS lpMenuWidths) +{ + AX_DEBUG(QAxClientSite::InsertMenus); + QMenuBar *mb = menuBar; + if (!mb) + mb = qFindChild<QMenuBar*>(widget->window()); + if (!mb) + return E_NOTIMPL; + menuBar = mb; + + QMenu *fileMenu = 0; + QMenu *viewMenu = 0; + QMenu *windowMenu = 0; + 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; +#endif +} + +static int menuItemEntry(HMENU menu, int index, MENUITEMINFOA item, QString &text, QPixmap &/*icon*/) +{ + if (item.fType == MFT_STRING && item.cch) { + char *titlebuf = new char[item.cch+1]; + item.dwTypeData = titlebuf; + item.cch++; + ::GetMenuItemInfoA(menu, index, true, &item); + text = QString::fromLocal8Bit(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; +} + +#if !defined(Q_OS_WINCE) +QMenu *QAxClientSite::generatePopup(HMENU subMenu, QWidget *parent) +{ + QMenu *popup = 0; + int count = GetMenuItemCount(subMenu); + if (count) + popup = new QMenu(parent); + for (int i = 0; i < count; ++i) { + MENUITEMINFOA item; + memset(&item, 0, sizeof(MENUITEMINFOA)); + item.cbSize = sizeof(MENUITEMINFOA); + item.fMask = MIIM_ID | MIIM_TYPE | MIIM_SUBMENU; + ::GetMenuItemInfoA(subMenu, i, true, &item); + + QAction *action = 0; + QMenu *popupMenu = 0; + if (item.fType == MFT_SEPARATOR) { + action = popup->addSeparator(); + } else { + QString text; + QPixmap icon; + QKeySequence accel; + popupMenu = item.hSubMenu ? generatePopup(item.hSubMenu, popup) : 0; + int res = menuItemEntry(subMenu, i, item, text, icon); + + int lastSep = text.lastIndexOf(QRegExp(QLatin1String("[\\s]"))); + if (lastSep != -1) { + QString keyString = text.right(text.length() - lastSep); + accel = keyString; + if ((int)accel) + text = text.left(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 (int(accel)) + action->setShortcut(accel); + if (!icon.isNull()) + action->setIcon(icon); + } + } + + if (action) { + OleMenuItem oleItem(subMenu, item.wID, popupMenu); + menuItemMap.insert(action, oleItem); + } + } + return popup; +} +#endif + +#if defined(Q_OS_WINCE) +HRESULT WINAPI QAxClientSite::SetMenu(HMENU /*hmenuShared*/, HOLEMENU /*holemenu*/, HWND /*hwndActiveObject*/) +{ + return E_NOTIMPL; +#else +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 = qFindChild<QMenuBar*>(widget->window()); + if (!mb) + return E_NOTIMPL; + menuBar = mb; + + int count = GetMenuItemCount(hmenuShared); + for (int i = 0; i < count; ++i) { + MENUITEMINFOA item; + memset(&item, 0, sizeof(MENUITEMINFOA)); + item.cbSize = sizeof(MENUITEMINFOA); + item.fMask = MIIM_ID | MIIM_TYPE | MIIM_SUBMENU; + ::GetMenuItemInfoA(hmenuShared, i, true, &item); + + QAction *action = 0; + QMenu *popupMenu = 0; + if (item.fType == MFT_SEPARATOR) { + action = menuBar->addSeparator(); + } else { + QString text; + QPixmap icon; + popupMenu = item.hSubMenu ? generatePopup(item.hSubMenu, menuBar) : 0; + 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, 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 = 0; + QMap<QAction*, OleMenuItem>::Iterator it; + for (it = menuItemMap.begin(); it != menuItemMap.end(); ++it) { + QAction *action = it.key(); + delete action; + } + menuItemMap.clear(); + } + + OleSetMenuDescriptor(holemenu, widget ? widget->window()->winId() : 0, m_menuOwner, this, m_spInPlaceActiveObject); + return S_OK; +#endif +} + +#if defined(Q_OS_WINCE) +int QAxClientSite::qt_metacall(QMetaObject::Call /*call*/, int isignal, void ** /*argv*/) +{ + return isignal; +#else +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 = *(QAction**)argv[1]; + // ### + + OleMenuItem oleItem = menuItemMap.value(action); + if (oleItem.hMenu) + ::PostMessageA(m_menuOwner, WM_COMMAND, oleItem.id, 0); + return -1; +#endif +} + + +HRESULT WINAPI QAxClientSite::RemoveMenus(HMENU /*hmenuShared*/) +{ +#if defined(Q_OS_WINCE) + return E_NOTIMPL; +#else + AX_DEBUG(QAxClientSite::RemoveMenus); + QMap<QAction*, OleMenuItem>::Iterator it; + for (it = menuItemMap.begin(); it != menuItemMap.end(); ++it) { + QAction *action = it.key(); + action->setVisible(false); + delete action; + } + menuItemMap.clear(); + return S_OK; +#endif +} + +HRESULT WINAPI QAxClientSite::SetStatusText(LPCOLESTR pszStatusText) +{ + QStatusTipEvent tip(QString::fromUtf16((const ushort *)(BSTR)pszStatusText)); + QApplication::sendEvent(widget, &tip); + return S_OK; +} + +extern Q_GUI_EXPORT bool qt_win_ignoreNextMouseReleaseEvent; + +HRESULT WINAPI QAxClientSite::EnableModeless(BOOL fEnable) +{ + EnableWindow(host->window()->winId(), fEnable); + + if (!fEnable) { + if (!QApplicationPrivate::isBlockedByModal(host)) + QApplicationPrivate::enterModal(host); + } else { + if (QApplicationPrivate::isBlockedByModal(host)) + QApplicationPrivate::leaveModal(host); + } + 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); + + if (pszObjName && widget) + widget->setWindowTitle(QString::fromUtf16((const ushort *)(BSTR)pszObjName)); + + 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 = 0; + + if (!pViewToActivate) { + IOleDocument *document = 0; + m_spOleObject->QueryInterface(IID_IOleDocument, (void**)&document); + if (!document) + return E_FAIL; + + document->CreateView(this, 0, 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(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); + HRESULT res = m_spOleObject->GetExtent(DVASPECT_CONTENT, &sz); + if (SUCCEEDED(res)) { + return QSize(MAP_LOGHIM_TO_PIX(sz.cx, widget->logicalDpiX()), + MAP_LOGHIM_TO_PIX(sz.cy, widget->logicalDpiY())); + } + return 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_NoBackground); + 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; +} + +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(winId(), &rect); + axhost->m_spActiveView->SetRect(&rect); + + return; + } + + SIZEL hmSize; + hmSize.cx = MAP_PIX_TO_LOGHIM(width(), logicalDpiX()); + hmSize.cy = MAP_PIX_TO_LOGHIM(height(), logicalDpiY()); + + if (axhost->m_spOleObject) + axhost->m_spOleObject->SetExtent(DVASPECT_CONTENT, &hmSize); + if (axhost->m_spInPlaceObject) { + RECT rcPos = { x(), y(), x()+width(), y()+height() }; + axhost->m_spInPlaceObject->SetObjectRects(&rcPos, &rcPos); + } +} + +void QAxHostWidget::resizeEvent(QResizeEvent *) +{ + resizeObject(); +} + +void QAxHostWidget::showEvent(QShowEvent *) +{ + resizeObject(); +} + +bool QAxHostWidget::winEvent(MSG *msg, long *result) +{ + if (axhost && axhost->inPlaceObjectWindowless) { + Q_ASSERT(axhost->m_spInPlaceObject); + IOleInPlaceObjectWindowless *windowless = (IOleInPlaceObjectWindowless*)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; + } + return QWidget::winEvent(msg, result); +} + +bool QAxHostWidget::event(QEvent *e) +{ + switch (e->type()) { + case QEvent::Timer: + if (axhost && ((QTimerEvent*)e)->timerId() == setFocusTimer) { + killTimer(setFocusTimer); + setFocusTimer = 0; + RECT rcPos = { x(), y(), x()+size().width(), y()+size().height() }; + axhost->m_spOleObject->DoVerb(OLEIVERB_UIACTIVATE, 0, (IOleClientSite*)axhost, 0, winId(), &rcPos); + if (axhost->m_spActiveView) + axhost->m_spActiveView->UIActivate(TRUE); + } + break; + case QEvent::WindowBlocked: + if (IsWindowEnabled(winId())) { + EnableWindow(winId(), false); + if (axhost && axhost->m_spInPlaceActiveObject) { + axhost->inPlaceModelessEnabled = false; + axhost->m_spInPlaceActiveObject->EnableModeless(false); + } + } + break; + case QEvent::WindowUnblocked: + if (!IsWindowEnabled(winId())) { + EnableWindow(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*) +{ + if (!QPainter::redirected(this)) + return; + + IViewObject *view = 0; + if (axhost) + axhost->widget->queryInterface(IID_IViewObject, (void**)&view); + if (!view) + return; + + // somebody tries to grab us! + QPixmap pm(size()); + pm.fill(); + + HBITMAP hBmp = pm.toWinHBITMAP(); + HDC hBmp_hdc = CreateCompatibleDC(qt_win_display_dc()); + 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, 0, 0, 0, hBmp_hdc, &bounds, 0, 0 /*fptr*/, 0); + view->Release(); + + QPainter painter(this); + painter.drawPixmap(0, 0, QPixmap::fromWinHBITMAP(hBmp)); + + SelectObject(hBmp_hdc, old_hBmp); + DeleteObject(hBmp); + DeleteDC(hBmp_hdc); +} + +/*! + \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) +: QWidget(parent, f), container(0) +{ +} + +/*! + 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), container(0) +{ + 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), container(0) +{ +} + +/*! + 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). +*/ +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) +{ +#ifdef QT3_SUPPORT + QApplication::sendPostedEvents(0, QEvent::ChildInserted); +#endif + + container = new QAxClientSite(this); + container->activateObject(initialized, data); + +#if !defined(Q_OS_WINCE) + ATOM filter_ref = FindAtomA(qaxatom); +#endif + if (!filter_ref) + previous_filter = QAbstractEventDispatcher::instance()->setEventFilter(axc_FilterProc); +#if !defined(Q_OS_WINCE) + AddAtomA(qaxatom); +#else + ++filter_ref; +#endif + + 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 0; +} + +/*! + \reimp + + Shuts down the ActiveX control. +*/ +void QAxWidget::clear() +{ + if (isNull()) + return; + if (!control().isEmpty()) { +#if !defined(Q_OS_WINCE) + ATOM filter_ref = FindAtomA(qaxatom); + if (filter_ref) + DeleteAtom(filter_ref); + filter_ref = FindAtomA(qaxatom); + if (!filter_ref) { +#else + if (!filter_ref && !--filter_ref) { +#endif + QAbstractEventDispatcher::instance()->setEventFilter(previous_filter); + previous_filter = 0; + } + } + + if (container) + container->deactivate(); + + QAxBase::clear(); + setFocusPolicy(Qt::NoFocus); + + if (container) { + container->releaseAll(); + container->Release(); + } + container = 0; +} + +/*! + \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 +*/ + +/*! + \reimp +*/ +const QMetaObject *QAxWidget::metaObject() const +{ + return QAxBase::metaObject(); +} + +/*! + \reimp +*/ +const QMetaObject *QAxWidget::parentMetaObject() const +{ + return &QWidget::staticMetaObject; +} + +/*! + \internal +*/ +void *QAxWidget::qt_metacast(const char *cname) +{ + if (!qstrcmp(cname, "QAxWidget")) return (void*)this; + if (!qstrcmp(cname, "QAxBase")) return (QAxBase*)this; + return QWidget::qt_metacast(cname); +} + +/*! + \reimp +*/ +const char *QAxWidget::className() const +{ + return "QAxWidget"; +} + +/*! + \reimp +*/ +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()) + 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 char *) +{ + 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 + \i WM_SYSKEYDOWN + \i WM_SYSKEYUP + \i WM_KEYDOWN + \row + \i All keycodes + \i VK_MENU + \i 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 +#endif // QT_NO_WIN_ACTIVEQT diff --git a/src/activeqt/container/qaxwidget.h b/src/activeqt/container/qaxwidget.h new file mode 100644 index 0000000..e07caad --- /dev/null +++ b/src/activeqt/container/qaxwidget.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the ActiveQt framework of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QAXWIDGET_H +#define QAXWIDGET_H + +#include <ActiveQt/qaxbase.h> +#include <QtGui/qwidget.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(ActiveQt) + +#ifndef QT_NO_WIN_ACTIVEQT + +class QAxHostWindow; +class QAxAggregated; + +class QAxClientSite; + +class QAxWidget : public QWidget, public QAxBase +{ +public: + const QMetaObject *metaObject() const; + void* qt_metacast(const char*); + int qt_metacall(QMetaObject::Call, int, void **); + QObject* qObject() const { return (QWidget*)this; } + const char *className() const; + + QAxWidget(QWidget* parent = 0, Qt::WindowFlags f = 0); + QAxWidget(const QString &c, QWidget *parent = 0, Qt::WindowFlags f = 0); + QAxWidget(IUnknown *iface, QWidget *parent = 0, Qt::WindowFlags f = 0); + ~QAxWidget(); + + void clear(); + bool doVerb(const QString &verb); + + QSize sizeHint() const; + QSize minimumSizeHint() const; + + virtual QAxAggregated *createAggregate(); + +protected: + bool initialize(IUnknown**); + virtual bool createHostWindow(bool); + bool createHostWindow(bool, const QByteArray&); + + void changeEvent(QEvent *e); + void resizeEvent(QResizeEvent *); + + virtual bool translateKeyEvent(int message, int keycode) const; + + void connectNotify(const char *signal); +private: + friend class QAxClientSite; + QAxClientSite *container; + + const QMetaObject *parentMetaObject() const; + static QMetaObject staticMetaObject; +}; + +#if defined Q_CC_MSVC && _MSC_VER < 1300 +template <> inline QAxWidget *qobject_cast_helper<QAxWidget*>(const QObject *o, QAxWidget *) +#else +template <> inline QAxWidget *qobject_cast<QAxWidget*>(const QObject *o) +#endif +{ + void *result = o ? const_cast<QObject *>(o)->qt_metacast("QAxWidget") : 0; + return (QAxWidget*)(result); +} + +#if defined Q_CC_MSVC && _MSC_VER < 1300 +template <> inline QAxWidget *qobject_cast_helper<QAxWidget*>(QObject *o, QAxWidget *) +#else +template <> inline QAxWidget *qobject_cast<QAxWidget*>(QObject *o) +#endif +{ + void *result = o ? o->qt_metacast("QAxWidget") : 0; + return (QAxWidget*)(result); +} + +QT_END_NAMESPACE +#endif // QT_NO_WIN_ACTIVEQT + +QT_END_HEADER + +#endif // QAXWIDGET_H |