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 |
370 | |