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 \
35protected: \
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 \
54protected: \
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 \
72PHONON_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 \
79PHONON_CLASSNAME::PHONON_CLASSNAME(QObject *parent) \
80 : QObject(parent), \
81 MediaNode(*new PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private)()) \
82{ \
83} \
84void 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) \
96PHONON_CLASSNAME::PHONON_CLASSNAME(QObject *parent) \
97 : parentclass(*new PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private), parent) \
98{ \
99} \
100void 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) \
112QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar))
113#define BACKEND_GET1(returnType, returnVar, methodName, varType1, var1) \
114QMetaObject::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) \
116QMetaObject::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) \
118QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection)
119#define BACKEND_CALL1(methodName, varType1, var1) \
120QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1))
121#define BACKEND_CALL2(methodName, varType1, var1, varType2, var2) \
122QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1), Q_ARG(varType2, var2))
123
124#define pBACKEND_GET(returnType, returnVar, methodName) \
125QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar))
126#define pBACKEND_GET1(returnType, returnVar, methodName, varType1, var1) \
127QMetaObject::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) \
129QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar), Q_ARG(varType1, var1), Q_ARG(varType2, var2))
130#define pBACKEND_CALL(methodName) \
131QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection)
132#define pBACKEND_CALL1(methodName, varType1, var1) \
133QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1))
134#define pBACKEND_CALL2(methodName, varType1, var1, varType2, var2) \
135QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1), Q_ARG(varType2, var2))
136
137QT_BEGIN_NAMESPACE
138
139namespace 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
295QT_END_NAMESPACE
296
297#define INTERFACE_CALL(function) \
298Iface<PHONON_INTERFACENAME >::cast(d)->function
299
300#define pINTERFACE_CALL(function) \
301Iface<PHONON_INTERFACENAME >::cast(this)->function
302
303#define PHONON_GETTER(rettype, name, retdefault) \
304rettype 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) \
315rettype 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) \
324rettype 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) \
335rettype 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) \
344void 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) \
354void 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
366Q_DECLARE_METATYPE(QList<int>)
367#endif
368
369#endif // PHONONDEFS_P_H
370