1
/*  This file is part of the KDE project
2
    Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org>
3
4
    This library is free software; you can redistribute it and/or
5
    modify it under the terms of the GNU Lesser General Public
6
    License as published by the Free Software Foundation; either
7
    version 2.1 of the License, or (at your option) version 3, or any
8
    later version accepted by the membership of KDE e.V. (or its
9
    successor approved by the membership of KDE e.V.), Nokia Corporation 
10
    (or its successors, if any) and the KDE Free Qt Foundation, which shall
11
    act as a proxy defined in Section 6 of version 3 of the license.
12
13
    This library is distributed in the hope that it will be useful,
14
    but WITHOUT ANY WARRANTY; without even the implied warranty of
15
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
    Lesser General Public License for more details.
17
18
    You should have received a copy of the GNU Lesser General Public 
19
    License along with this library.  If not, see <http://www.gnu.org/licenses/>.
20
21
*/
22
23
#ifndef PHONONDEFS_P_H
24
#define PHONONDEFS_P_H
25
26
#include <QtCore/QMetaType>
27
#include "medianode_p.h"
28
29
#define K_D(Class) Class##Private *const d = k_func()
30
31
#define PHONON_CONCAT_HELPER_INTERNAL(x, y) x ## y
32
#define PHONON_CONCAT_HELPER(x, y) PHONON_CONCAT_HELPER_INTERNAL(x, y)
33
34
#define PHONON_PRIVATECLASS \
35
protected: \
36
    virtual bool aboutToDeleteBackendObject(); \
37
    virtual void createBackendObject(); \
38
    /**
39
     * \internal
40
     * After construction of the Iface object this method is called
41
     * throughout the complete class hierarchy in order to set up the
42
     * properties that were already set on the public interface.
43
     *
44
     * An example implementation could look like this:
45
     * \code
46
     * ParentClassPrivate::setupBackendObject();
47
     * m_iface->setPropertyA(d->propertyA);
48
     * m_iface->setPropertyB(d->propertyB);
49
     * \endcode
50
     */ \
51
    void setupBackendObject();
52
53
#define PHONON_PRIVATEABSTRACTCLASS \
54
protected: \
55
    virtual bool aboutToDeleteBackendObject(); \
56
    /**
57
     * \internal
58
     * After construction of the Iface object this method is called
59
     * throughout the complete class hierarchy in order to set up the
60
     * properties that were already set on the public interface.
61
     *
62
     * An example implementation could look like this:
63
     * \code
64
     * ParentClassPrivate::setupBackendObject();
65
     * m_iface->setPropertyA(d->propertyA);
66
     * m_iface->setPropertyB(d->propertyB);
67
     * \endcode
68
     */ \
69
    void setupBackendObject();
70
71
#define PHONON_ABSTRACTBASE_IMPL \
72
PHONON_CLASSNAME::PHONON_CLASSNAME(PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) &dd, QObject *parent) \
73
    : QObject(parent), \
74
    MediaNode(dd) \
75
{ \
76
}
77
78
#define PHONON_OBJECT_IMPL \
79
PHONON_CLASSNAME::PHONON_CLASSNAME(QObject *parent) \
80
    : QObject(parent), \
81
    MediaNode(*new PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private)()) \
82
{ \
83
} \
84
void PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private)::createBackendObject() \
85
{ \
86
    if (m_backendObject) \
87
        return; \
88
    Q_Q(PHONON_CLASSNAME); \
89
    m_backendObject = Factory::PHONON_CONCAT_HELPER(create, PHONON_CLASSNAME)(q); \
90
    if (m_backendObject) { \
91
        setupBackendObject(); \
92
    } \
93
}
94
95
#define PHONON_HEIR_IMPL(parentclass) \
96
PHONON_CLASSNAME::PHONON_CLASSNAME(QObject *parent) \
97
    : parentclass(*new PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private), parent) \
98
{ \
99
} \
100
void PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private)::createBackendObject() \
101
{ \
102
    if (m_backendObject) \
103
        return; \
104
    Q_Q(PHONON_CLASSNAME); \
105
    m_backendObject = Factory::PHONON_CONCAT_HELPER(create, PHONON_CLASSNAME)(q); \
106
    if (m_backendObject) { \
107
        setupBackendObject(); \
108
    } \
109
}
110
111
#define BACKEND_GET(returnType, returnVar, methodName) \
112
QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar))
113
#define BACKEND_GET1(returnType, returnVar, methodName, varType1, var1) \
114
QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar), Q_ARG(varType1, var1))
115
#define BACKEND_GET2(returnType, returnVar, methodName, varType1, var1, varType2, var2) \
116
QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar), Q_ARG(varType1, var1), Q_ARG(varType2, var2))
117
#define BACKEND_CALL(methodName) \
118
QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection)
119
#define BACKEND_CALL1(methodName, varType1, var1) \
120
QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1))
121
#define BACKEND_CALL2(methodName, varType1, var1, varType2, var2) \
122
QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1), Q_ARG(varType2, var2))
123
124
#define pBACKEND_GET(returnType, returnVar, methodName) \
125
QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar))
126
#define pBACKEND_GET1(returnType, returnVar, methodName, varType1, var1) \
127
QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar), Q_ARG(varType1, var1))
128
#define pBACKEND_GET2(returnType, returnVar, methodName, varType1, var1, varType2, var2) \
129
QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar), Q_ARG(varType1, var1), Q_ARG(varType2, var2))
130
#define pBACKEND_CALL(methodName) \
131
QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection)
132
#define pBACKEND_CALL1(methodName, varType1, var1) \
133
QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1))
134
#define pBACKEND_CALL2(methodName, varType1, var1, varType2, var2) \
135
QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1), Q_ARG(varType2, var2))
136
137
QT_BEGIN_NAMESPACE
138
139
namespace Phonon
140
{
141
    namespace
142
    {
143
        class NoIface;
144
145
        /// All template arguments are valid
146
        template<typename T> struct IsValid { enum { Result = true }; };
147
        /// except NoIface
148
        template<> struct IsValid<NoIface> { enum { Result = false }; };
149
150
        template<class T> inline T my_cast(QObject *o) { return qobject_cast<T>(o); }
151
        template<class T> inline T my_cast(const QObject *o) { return qobject_cast<T>(o); }
152
153
        template<> inline NoIface *my_cast<NoIface *>(QObject *) { return 0; }
154
        template<> inline NoIface *my_cast<NoIface *>(const QObject *) { return 0; }
155
    } // anonymous namespace
156
157
    /**
158
     * \internal
159
     *
160
     * \brief Helper class to cast the backend object to the correct version of the interface.
161
     *
162
     * Additions to the backend interfaces cannot be done by adding virtual methods as that would
163
     * break the binary interface. So the old class is renamed and a new class with the old name
164
     * inheriting the old class is added, containing all the new virtual methods.
165
     * Example:
166
     * \code
167
       class FooInterface
168
       {
169
       public:
170
           virtual ~FooInterface() {}
171
           virtual oldMethod() = 0;
172
       };
173
       Q_DECLARE_INTERFACE(FooInterface, "FooInterface0.phonon.kde.org")
174
     * \endcode
175
     * becomes
176
     * \code
177
       class FooInterface0
178
       {
179
       public:
180
           virtual ~FooInterface0() {}
181
           virtual oldMethod() = 0;
182
       };
183
       class FooInterface : public FooInterface0
184
       {
185
       public:
186
           virtual newMethod() = 0;
187
       };
188
       Q_DECLARE_INTERFACE(FooInterface0, "FooInterface0.phonon.kde.org")
189
       Q_DECLARE_INTERFACE(FooInterface,  "FooInterface1.phonon.kde.org")
190
     * \endcode
191
     *
192
     * With this, backends compiled against the old header can be qobject_casted to FooInterface0,
193
     * but not to FooInterface. On the other hand backends compiled against the new header (they first
194
     * need to implement newMethod) can only be qobject_casted to FooInterface but not to
195
     * FooInterface0. (The qobject_cast relies on the string in Q_DECLARE_INTERFACE and not the
196
     * class name which is why it behaves that way.)
197
     *
198
     * Now, in order to call oldMethod, the code needs to try to cast to both FooInterface and
199
     * FooInterface0 (new backends will work with the former, old backends with the latter) and then
200
     * if one of them in non-zero call oldMethod on it.
201
     *
202
     * To call newMethod only a cast to FooInterface needs to be done.
203
     *
204
     * The Iface class does all this for you for up to three (for now) interface revisions. Just
205
     * create an object like this:
206
     * \code
207
       Iface<FooInterface0, FooInterface> iface0(d);
208
       if (iface0) {
209
           iface0->oldMethod();
210
       }
211
       Iface<FooInterface> iface(d);
212
       if (iface) {
213
           iface->newMethod();
214
       }
215
     * \endcode
216
     *
217
     * This becomes a bit more convenient if you add macros like this:
218
     * \code
219
       #define IFACES1 FooInterface
220
       #define IFACES0 FooInterface0, IFACES1
221
     * \endcode
222
     * which you can use like this:
223
     * \code
224
       Iface<IFACES0> iface0(d);
225
       if (iface0) {
226
           iface0->oldMethod();
227
       }
228
       Iface<IFACES1> iface(d);
229
       if (iface) {
230
           iface->newMethod();
231
       }
232
     * \endcode
233
     * With the next revision you can then change the macros to
234
     * \code
235
       #define IFACES2 FooInterface
236
       #define IFACES1 FooInterface1, IFACES2
237
       #define IFACES0 FooInterface0, IFACES1
238
     * \endcode
239
     *
240
     * \author Matthias Kretz <kretz@kde.org>
241
     */
242
    template<class T0, class T1 = NoIface, class T2 = NoIface>
243
    class Iface
244
    {
245
    public:
246
        static inline T0 *cast(MediaNodePrivate *const d)
247
        {
248
            if (IsValid<T1>::Result) {
249
                T0 *ret;
250
                if (IsValid<T2>::Result) {
251
                    ret = reinterpret_cast<T0 *>(my_cast<T2 *>(d->m_backendObject));
252
                    if (ret) return ret;
253
                }
254
                ret = reinterpret_cast<T0 *>(my_cast<T1 *>(d->m_backendObject));
255
                if (ret) return ret;
256
            }
257
            return qobject_cast<T0 *>(d->m_backendObject);
258
        }
259
260
        static inline const T0 *cast(const MediaNodePrivate *const d)
261
        {
262
            if (IsValid<T1>::Result) {
263
                const T0 *ret;
264
                if (IsValid<T2>::Result) {
265
                    ret = reinterpret_cast<const T0 *>(my_cast<T2 *>(d->m_backendObject));
266
                    if (ret) return ret;
267
                }
268
                ret = reinterpret_cast<const T0 *>(my_cast<T1 *>(d->m_backendObject));
269
                if (ret) return ret;
270
            }
271
            return qobject_cast<T0 *>(d->m_backendObject);
272
        }
273
274
        inline Iface(MediaNodePrivate *const d) : iface(cast(d)) {}
275
        inline operator       T0 *()       { return iface; }
276
        inline operator const T0 *() const { return iface; }
277
        inline       T0 *operator->()       { Q_ASSERT(iface); return iface; }
278
        inline const T0 *operator->() const { Q_ASSERT(iface); return iface; }
279
    private:
280
        T0 *const iface;
281
    };
282
283
    template<class T0, class T1 = NoIface, class T2 = NoIface>
284
    class ConstIface
285
    {
286
    public:
287
        inline ConstIface(const MediaNodePrivate *const d) : iface(Iface<T0, T1, T2>::cast(d)) {}
288
        inline operator const T0 *() const { return iface; }
289
        inline const T0 *operator->() const { Q_ASSERT(iface); return iface; }
290
    private:
291
        const T0 *const iface;
292
    };
293
} // namespace Phonon
294
295
QT_END_NAMESPACE
296
297
#define INTERFACE_CALL(function) \
298
Iface<PHONON_INTERFACENAME >::cast(d)->function
299
300
#define pINTERFACE_CALL(function) \
301
Iface<PHONON_INTERFACENAME >::cast(this)->function
302
303
#define PHONON_GETTER(rettype, name, retdefault) \
304
rettype PHONON_CLASSNAME::name() const \
305
{ \
306
    const PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \
307
    if (!d->m_backendObject) \
308
        return retdefault; \
309
    rettype ret; \
310
    BACKEND_GET(rettype, ret, #name); \
311
    return ret; \
312
}
313
314
#define PHONON_INTERFACE_GETTER(rettype, name, retdefault) \
315
rettype PHONON_CLASSNAME::name() const \
316
{ \
317
    const PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \
318
    if (!d->m_backendObject) \
319
        return retdefault; \
320
    return Iface<PHONON_INTERFACENAME >::cast(d)->name(); \
321
}
322
323
#define PHONON_GETTER1(rettype, name, retdefault, argtype1, argvar1) \
324
rettype PHONON_CLASSNAME::name(argtype1 argvar1) const \
325
{ \
326
    const PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \
327
    if (!d->m_backendObject) \
328
        return retdefault; \
329
    rettype ret; \
330
    BACKEND_GET1(rettype, ret, #name, const QObject *, argvar1->k_ptr->backendObject()); \
331
    return ret; \
332
}
333
334
#define PHONON_INTERFACE_GETTER1(rettype, name, retdefault, argtype1, argvar1) \
335
rettype PHONON_CLASSNAME::name(argtype1 argvar1) const \
336
{ \
337
    const PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \
338
    if (!d->m_backendObject) \
339
        return retdefault; \
340
    return Iface<PHONON_INTERFACENAME >::cast(d)->name(argvar1->k_ptr->backendObject()); \
341
}
342
343
#define PHONON_SETTER(functionname, privatevar, argtype1) \
344
void PHONON_CLASSNAME::functionname(argtype1 x) \
345
{ \
346
    PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \
347
    d->privatevar = x; \
348
    if (k_ptr->backendObject()) { \
349
        BACKEND_CALL1(#functionname, argtype1, x); \
350
    } \
351
}
352
353
#define PHONON_INTERFACE_SETTER(functionname, privatevar, argtype1) \
354
void PHONON_CLASSNAME::functionname(argtype1 x) \
355
{ \
356
    PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \
357
    d->privatevar = x; \
358
    if (k_ptr->backendObject()) { \
359
        Iface<PHONON_INTERFACENAME >::cast(d)->functionname(x); \
360
    } \
361
}
362
363
#ifndef METATYPE_QLIST_INT_DEFINED
364
#define METATYPE_QLIST_INT_DEFINED
365
// Want this exactly once, see phonondefs_p.h kcm/outputdevicechoice.cpp
366
Q_DECLARE_METATYPE(QList<int>)
367
#endif
368
369
#endif // PHONONDEFS_P_H