/* This file is part of the KDE project Copyright (C) 2006-2007 Matthias Kretz This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PHONONDEFS_P_H #define PHONONDEFS_P_H #include #include "medianode_p.h" #define K_D(Class) Class##Private *const d = k_func() #define PHONON_CONCAT_HELPER_INTERNAL(x, y) x ## y #define PHONON_CONCAT_HELPER(x, y) PHONON_CONCAT_HELPER_INTERNAL(x, y) #define PHONON_PRIVATECLASS \ protected: \ virtual bool aboutToDeleteBackendObject(); \ virtual void createBackendObject(); \ /** * \internal * After construction of the Iface object this method is called * throughout the complete class hierarchy in order to set up the * properties that were already set on the public interface. * * An example implementation could look like this: * \code * ParentClassPrivate::setupBackendObject(); * m_iface->setPropertyA(d->propertyA); * m_iface->setPropertyB(d->propertyB); * \endcode */ \ void setupBackendObject(); #define PHONON_PRIVATEABSTRACTCLASS \ protected: \ virtual bool aboutToDeleteBackendObject(); \ /** * \internal * After construction of the Iface object this method is called * throughout the complete class hierarchy in order to set up the * properties that were already set on the public interface. * * An example implementation could look like this: * \code * ParentClassPrivate::setupBackendObject(); * m_iface->setPropertyA(d->propertyA); * m_iface->setPropertyB(d->propertyB); * \endcode */ \ void setupBackendObject(); #define PHONON_ABSTRACTBASE_IMPL \ PHONON_CLASSNAME::PHONON_CLASSNAME(PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) &dd, QObject *parent) \ : QObject(parent), \ MediaNode(dd) \ { \ } #define PHONON_OBJECT_IMPL \ PHONON_CLASSNAME::PHONON_CLASSNAME(QObject *parent) \ : QObject(parent), \ MediaNode(*new PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private)()) \ { \ } \ void PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private)::createBackendObject() \ { \ if (m_backendObject) \ return; \ Q_Q(PHONON_CLASSNAME); \ m_backendObject = Factory::PHONON_CONCAT_HELPER(create, PHONON_CLASSNAME)(q); \ if (m_backendObject) { \ setupBackendObject(); \ } \ } #define PHONON_HEIR_IMPL(parentclass) \ PHONON_CLASSNAME::PHONON_CLASSNAME(QObject *parent) \ : parentclass(*new PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private), parent) \ { \ } \ void PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private)::createBackendObject() \ { \ if (m_backendObject) \ return; \ Q_Q(PHONON_CLASSNAME); \ m_backendObject = Factory::PHONON_CONCAT_HELPER(create, PHONON_CLASSNAME)(q); \ if (m_backendObject) { \ setupBackendObject(); \ } \ } #define BACKEND_GET(returnType, returnVar, methodName) \ QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar)) #define BACKEND_GET1(returnType, returnVar, methodName, varType1, var1) \ QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar), Q_ARG(varType1, var1)) #define BACKEND_GET2(returnType, returnVar, methodName, varType1, var1, varType2, var2) \ QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar), Q_ARG(varType1, var1), Q_ARG(varType2, var2)) #define BACKEND_CALL(methodName) \ QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection) #define BACKEND_CALL1(methodName, varType1, var1) \ QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1)) #define BACKEND_CALL2(methodName, varType1, var1, varType2, var2) \ QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1), Q_ARG(varType2, var2)) #define pBACKEND_GET(returnType, returnVar, methodName) \ QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar)) #define pBACKEND_GET1(returnType, returnVar, methodName, varType1, var1) \ QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar), Q_ARG(varType1, var1)) #define pBACKEND_GET2(returnType, returnVar, methodName, varType1, var1, varType2, var2) \ QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar), Q_ARG(varType1, var1), Q_ARG(varType2, var2)) #define pBACKEND_CALL(methodName) \ QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection) #define pBACKEND_CALL1(methodName, varType1, var1) \ QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1)) #define pBACKEND_CALL2(methodName, varType1, var1, varType2, var2) \ QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1), Q_ARG(varType2, var2)) QT_BEGIN_NAMESPACE namespace Phonon { namespace { class NoIface; /// All template arguments are valid template struct IsValid { enum { Result = true }; }; /// except NoIface template<> struct IsValid { enum { Result = false }; }; template inline T my_cast(QObject *o) { return qobject_cast(o); } template inline T my_cast(const QObject *o) { return qobject_cast(o); } template<> inline NoIface *my_cast(QObject *) { return 0; } template<> inline NoIface *my_cast(const QObject *) { return 0; } } // anonymous namespace /** * \internal * * \brief Helper class to cast the backend object to the correct version of the interface. * * Additions to the backend interfaces cannot be done by adding virtual methods as that would * break the binary interface. So the old class is renamed and a new class with the old name * inheriting the old class is added, containing all the new virtual methods. * Example: * \code class FooInterface { public: virtual ~FooInterface() {} virtual oldMethod() = 0; }; Q_DECLARE_INTERFACE(FooInterface, "FooInterface0.phonon.kde.org") * \endcode * becomes * \code class FooInterface0 { public: virtual ~FooInterface0() {} virtual oldMethod() = 0; }; class FooInterface : public FooInterface0 { public: virtual newMethod() = 0; }; Q_DECLARE_INTERFACE(FooInterface0, "FooInterface0.phonon.kde.org") Q_DECLARE_INTERFACE(FooInterface, "FooInterface1.phonon.kde.org") * \endcode * * With this, backends compiled against the old header can be qobject_casted to FooInterface0, * but not to FooInterface. On the other hand backends compiled against the new header (they first * need to implement newMethod) can only be qobject_casted to FooInterface but not to * FooInterface0. (The qobject_cast relies on the string in Q_DECLARE_INTERFACE and not the * class name which is why it behaves that way.) * * Now, in order to call oldMethod, the code needs to try to cast to both FooInterface and * FooInterface0 (new backends will work with the former, old backends with the latter) and then * if one of them in non-zero call oldMethod on it. * * To call newMethod only a cast to FooInterface needs to be done. * * The Iface class does all this for you for up to three (for now) interface revisions. Just * create an object like this: * \code Iface iface0(d); if (iface0) { iface0->oldMethod(); } Iface iface(d); if (iface) { iface->newMethod(); } * \endcode * * This becomes a bit more convenient if you add macros like this: * \code #define IFACES1 FooInterface #define IFACES0 FooInterface0, IFACES1 * \endcode * which you can use like this: * \code Iface iface0(d); if (iface0) { iface0->oldMethod(); } Iface iface(d); if (iface) { iface->newMethod(); } * \endcode * With the next revision you can then change the macros to * \code #define IFACES2 FooInterface #define IFACES1 FooInterface1, IFACES2 #define IFACES0 FooInterface0, IFACES1 * \endcode * * \author Matthias Kretz */ template class Iface { public: static inline T0 *cast(MediaNodePrivate *const d) { if (IsValid::Result) { T0 *ret; if (IsValid::Result) { ret = reinterpret_cast(my_cast(d->m_backendObject)); if (ret) return ret; } ret = reinterpret_cast(my_cast(d->m_backendObject)); if (ret) return ret; } return qobject_cast(d->m_backendObject); } static inline const T0 *cast(const MediaNodePrivate *const d) { if (IsValid::Result) { const T0 *ret; if (IsValid::Result) { ret = reinterpret_cast(my_cast(d->m_backendObject)); if (ret) return ret; } ret = reinterpret_cast(my_cast(d->m_backendObject)); if (ret) return ret; } return qobject_cast(d->m_backendObject); } inline Iface(MediaNodePrivate *const d) : iface(cast(d)) {} inline operator T0 *() { return iface; } inline operator const T0 *() const { return iface; } inline T0 *operator->() { Q_ASSERT(iface); return iface; } inline const T0 *operator->() const { Q_ASSERT(iface); return iface; } private: T0 *const iface; }; template class ConstIface { public: inline ConstIface(const MediaNodePrivate *const d) : iface(Iface::cast(d)) {} inline operator const T0 *() const { return iface; } inline const T0 *operator->() const { Q_ASSERT(iface); return iface; } private: const T0 *const iface; }; } // namespace Phonon QT_END_NAMESPACE #define INTERFACE_CALL(function) \ Iface::cast(d)->function #define pINTERFACE_CALL(function) \ Iface::cast(this)->function #define PHONON_GETTER(rettype, name, retdefault) \ rettype PHONON_CLASSNAME::name() const \ { \ const PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \ if (!d->m_backendObject) \ return retdefault; \ rettype ret; \ BACKEND_GET(rettype, ret, #name); \ return ret; \ } #define PHONON_INTERFACE_GETTER(rettype, name, retdefault) \ rettype PHONON_CLASSNAME::name() const \ { \ const PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \ if (!d->m_backendObject) \ return retdefault; \ return Iface::cast(d)->name(); \ } #define PHONON_GETTER1(rettype, name, retdefault, argtype1, argvar1) \ rettype PHONON_CLASSNAME::name(argtype1 argvar1) const \ { \ const PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \ if (!d->m_backendObject) \ return retdefault; \ rettype ret; \ BACKEND_GET1(rettype, ret, #name, const QObject *, argvar1->k_ptr->backendObject()); \ return ret; \ } #define PHONON_INTERFACE_GETTER1(rettype, name, retdefault, argtype1, argvar1) \ rettype PHONON_CLASSNAME::name(argtype1 argvar1) const \ { \ const PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \ if (!d->m_backendObject) \ return retdefault; \ return Iface::cast(d)->name(argvar1->k_ptr->backendObject()); \ } #define PHONON_SETTER(functionname, privatevar, argtype1) \ void PHONON_CLASSNAME::functionname(argtype1 x) \ { \ PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \ d->privatevar = x; \ if (k_ptr->backendObject()) { \ BACKEND_CALL1(#functionname, argtype1, x); \ } \ } #define PHONON_INTERFACE_SETTER(functionname, privatevar, argtype1) \ void PHONON_CLASSNAME::functionname(argtype1 x) \ { \ PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \ d->privatevar = x; \ if (k_ptr->backendObject()) { \ Iface::cast(d)->functionname(x); \ } \ } #ifndef METATYPE_QLIST_INT_DEFINED #define METATYPE_QLIST_INT_DEFINED // Want this exactly once, see phonondefs_p.h kcm/outputdevicechoice.cpp Q_DECLARE_METATYPE(QList) #endif #endif // PHONONDEFS_P_H