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