Phonon

path.cpp
1/* This file is part of the KDE project
2 Copyright (C) 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#include "path.h"
24#include "path_p.h"
25
26#include "phononnamespace_p.h"
27#include "backendinterface.h"
28#include "factory_p.h"
29#include "medianode.h"
30#include "medianode_p.h"
31
32namespace Phonon
33{
34
35class ConnectionTransaction
36{
37 public:
38 ConnectionTransaction(BackendInterface *b, const QSet<QObject*> &x) : backend(b), list(x)
39 {
40 success = backend->startConnectionChange(list);
41 }
42 ~ConnectionTransaction()
43 {
44 backend->endConnectionChange(list);
45 }
46 operator bool()
47 {
48 return success;
49 }
50 private:
51 bool success;
52 BackendInterface *const backend;
53 const QSet<QObject*> list;
54};
55
56PathPrivate::~PathPrivate()
57{
58#ifndef QT_NO_PHONON_EFFECT
59 for (int i = 0; i < effects.count(); ++i) {
60 effects.at(i)->k_ptr->removeDestructionHandler(this);
61 }
62 delete effectsParent;
63#endif
64}
65
67{
68}
69
71 : d(new PathPrivate)
72{
73}
74
75Path::Path(const Path &rhs)
76 : d(rhs.d)
77{
78}
79
80bool Path::isValid() const
81{
82 return d->sourceNode != nullptr && d->sinkNode != nullptr;
83}
84
85#ifndef QT_NO_PHONON_EFFECT
87{
88 if (!d->effectsParent) {
89 d->effectsParent = new QObject;
90 }
91 Effect *e = new Effect(desc, d->effectsParent);
92 if (!e->isValid()) {
93 delete e;
94 return nullptr;
95 }
96 bool success = insertEffect(e, insertBefore);
97 if (!success) {
98 delete e;
99 return nullptr;
100 }
101 return e;
102}
103
104bool Path::insertEffect(Effect *newEffect, Effect *insertBefore)
105{
106 QObject *newEffectBackend = newEffect ? newEffect->k_ptr->backendObject() : nullptr;
107 if (!isValid() || !newEffectBackend || d->effects.contains(newEffect) ||
108 (insertBefore && (!d->effects.contains(insertBefore) || !insertBefore->k_ptr->backendObject()))) {
109 return false;
110 }
111 QObject *leftNode = nullptr;
112 QObject *rightNode = nullptr;
113 const int insertIndex = insertBefore ? d->effects.indexOf(insertBefore) : d->effects.size();
114 if (insertIndex == 0) {
115 //prepend
116 leftNode = d->sourceNode->k_ptr->backendObject();
117 } else {
118 leftNode = d->effects[insertIndex - 1]->k_ptr->backendObject();
119 }
120
121 if (insertIndex == d->effects.size()) {
122 //append
123 rightNode = d->sinkNode->k_ptr->backendObject();
124 } else {
125 Q_ASSERT(insertBefore);
126 rightNode = insertBefore->k_ptr->backendObject();
127 }
128
129 QList<QObjectPair> disconnections, connections;
130 disconnections << QObjectPair(leftNode, rightNode);
131 connections << QObjectPair(leftNode, newEffectBackend)
132 << QObjectPair(newEffectBackend, rightNode);
133
134 if (d->executeTransaction(disconnections, connections)) {
135 newEffect->k_ptr->addDestructionHandler(d.data());
136 d->effects.insert(insertIndex, newEffect);
137 return true;
138 } else {
139 return false;
140 }
141}
142
144{
145 return d->removeEffect(effect);
146}
147
149{
150 return d->effects;
151}
152#endif //QT_NO_PHONON_EFFECT
153
154bool Path::reconnect(MediaNode *source, MediaNode *sink)
155{
156 if (!source || !sink || !source->k_ptr->backendObject() || !sink->k_ptr->backendObject()) {
157 return false;
158 }
159
160 QList<QObjectPair> disconnections, connections;
161
162 //backend objects
163 QObject *bnewSource = source->k_ptr->backendObject();
164 QObject *bnewSink = sink->k_ptr->backendObject();
165 QObject *bcurrentSource = d->sourceNode ? d->sourceNode->k_ptr->backendObject() : nullptr;
166 QObject *bcurrentSink = d->sinkNode ? d->sinkNode->k_ptr->backendObject() : nullptr;
167
168 if (bnewSource != bcurrentSource) {
169 //we need to change the source
170#ifndef QT_NO_PHONON_EFFECT
171 MediaNode *next = d->effects.isEmpty() ? sink : d->effects.first();
172#else
173 MediaNode *next = sink;
174#endif //QT_NO_PHONON_EFFECT
175 QObject *bnext = next->k_ptr->backendObject();
176 if (bcurrentSource)
177 disconnections << QObjectPair(bcurrentSource, bnext);
178 connections << QObjectPair(bnewSource, bnext);
179 }
180
181 if (bnewSink != bcurrentSink) {
182#ifndef QT_NO_PHONON_EFFECT
183 MediaNode *previous = d->effects.isEmpty() ? source : d->effects.last();
184#else
185 MediaNode *previous = source;
186#endif //QT_NO_PHONON_EFFECT
187 QObject *bprevious = previous->k_ptr->backendObject();
188 if (bcurrentSink)
189 disconnections << QObjectPair(bprevious, bcurrentSink);
190 QObjectPair pair(bprevious, bnewSink);
191 if (!connections.contains(pair)) //avoid connecting twice
192 connections << pair;
193 }
194
195 if (d->executeTransaction(disconnections, connections)) {
196
197 //everything went well: let's update the path and the sink node
198 if (d->sinkNode != sink) {
199 if (d->sinkNode) {
200 d->sinkNode->k_ptr->removeInputPath(*this);
201 d->sinkNode->k_ptr->removeDestructionHandler(d.data());
202 }
203 sink->k_ptr->addInputPath(*this);
204 d->sinkNode = sink;
205 d->sinkNode->k_ptr->addDestructionHandler(d.data());
206 }
207
208 //everything went well: let's update the path and the source node
209 if (d->sourceNode != source) {
210 source->k_ptr->addOutputPath(*this);
211 if (d->sourceNode) {
212 d->sourceNode->k_ptr->removeOutputPath(*this);
213 d->sourceNode->k_ptr->removeDestructionHandler(d.data());
214 }
215 d->sourceNode = source;
216 d->sourceNode->k_ptr->addDestructionHandler(d.data());
217 }
218 return true;
219 } else {
220 return false;
221 }
222}
223
225{
226 if (!isValid()) {
227 return false;
228 }
229
230 QObjectList list;
231 if (d->sourceNode)
232 list << d->sourceNode->k_ptr->backendObject();
233#ifndef QT_NO_PHONON_EFFECT
234 for (int i = 0; i < d->effects.count(); ++i) {
235 list << d->effects.at(i)->k_ptr->backendObject();
236 }
237#endif
238 if (d->sinkNode) {
239 list << d->sinkNode->k_ptr->backendObject();
240 }
241
242 //lets build the disconnection list
243 QList<QObjectPair> disco;
244 if (list.count() >=2 ) {
245 QObjectList::const_iterator it = list.constBegin();
246 for(;it+1 != list.constEnd();++it) {
247 disco << QObjectPair(*it, *(it+1));
248 }
249 }
250
251 if (d->executeTransaction(disco, QList<QObjectPair>())) {
252 //everything went well, let's remove the reference
253 //to the paths from the source and sink
254 if (d->sourceNode) {
255 d->sourceNode->k_ptr->removeOutputPath(*this);
256 d->sourceNode->k_ptr->removeDestructionHandler(d.data());
257 }
258 d->sourceNode = nullptr;
259
260#ifndef QT_NO_PHONON_EFFECT
261 for (int i = 0; i < d->effects.count(); ++i) {
262 d->effects.at(i)->k_ptr->removeDestructionHandler(d.data());
263 }
264 d->effects.clear();
265#endif
266
267 if (d->sinkNode) {
268 d->sinkNode->k_ptr->removeInputPath(*this);
269 d->sinkNode->k_ptr->removeDestructionHandler(d.data());
270 }
271 d->sinkNode = nullptr;
272 return true;
273 } else {
274 return false;
275 }
276}
277
278MediaNode *Path::source() const
279{
280 return d->sourceNode;
281}
282
283MediaNode *Path::sink() const
284{
285 return d->sinkNode;
286}
287
288
289
290bool PathPrivate::executeTransaction( const QList<QObjectPair> &disconnections, const QList<QObjectPair> &connections)
291{
292 QSet<QObject*> nodesForTransaction;
293 for (int i = 0; i < disconnections.count(); ++i) {
294 const QObjectPair &pair = disconnections.at(i);
295 nodesForTransaction << pair.first;
296 nodesForTransaction << pair.second;
297 }
298 for (int i = 0; i < connections.count(); ++i) {
299 const QObjectPair &pair = connections.at(i);
300 nodesForTransaction << pair.first;
301 nodesForTransaction << pair.second;
302 }
303 BackendInterface *backend = qobject_cast<BackendInterface *>(Factory::backend());
304 if (!backend)
305 return false;
306
307 ConnectionTransaction transaction(backend, nodesForTransaction);
308 if (!transaction)
309 return false;
310
311 QList<QObjectPair>::const_iterator it = disconnections.begin();
312 for(;it != disconnections.end();++it) {
313 const QObjectPair &pair = *it;
314 if (!backend->disconnectNodes(pair.first, pair.second)) {
315
316 //Error: a disconnection failed
317 QList<QObjectPair>::const_iterator it2 = disconnections.begin();
318 for(; it2 != it; ++it2) {
319 const QObjectPair &pair = *it2;
320 bool success = backend->connectNodes(pair.first, pair.second);
321 Q_ASSERT(success); //a failure here means it is impossible to reestablish the connection
322 Q_UNUSED(success);
323 }
324 return false;
325 }
326 }
327
328 for(it = connections.begin(); it != connections.end();++it) {
329 const QObjectPair &pair = *it;
330 if (!backend->connectNodes(pair.first, pair.second)) {
331 //Error: a connection failed
332 QList<QObjectPair>::const_iterator it2 = connections.begin();
333 for(; it2 != it; ++it2) {
334 const QObjectPair &pair = *it2;
335 bool success = backend->disconnectNodes(pair.first, pair.second);
336 Q_ASSERT(success); //a failure here means it is impossible to reestablish the connection
337 Q_UNUSED(success);
338 }
339
340 //and now let's reconnect the nodes that were disconnected: rollback
341 for (int i = 0; i < disconnections.count(); ++i) {
342 const QObjectPair &pair = disconnections.at(i);
343 bool success = backend->connectNodes(pair.first, pair.second);
344 Q_ASSERT(success); //a failure here means it is impossible to reestablish the connection
345 Q_UNUSED(success);
346 }
347
348 return false;
349
350 }
351 }
352 return true;
353}
354
355#ifndef QT_NO_PHONON_EFFECT
356bool PathPrivate::removeEffect(Effect *effect)
357{
358 if (!effects.contains(effect))
359 return false;
360
361 QObject *leftNode = nullptr;
362 QObject *rightNode = nullptr;
363 const int index = effects.indexOf(effect);
364 if (index == 0) {
365 leftNode = sourceNode->k_ptr->backendObject(); //append
366 } else {
367 leftNode = effects[index - 1]->k_ptr->backendObject();
368 }
369 if (index == effects.size()-1) {
370 rightNode = sinkNode->k_ptr->backendObject(); //prepend
371 } else {
372 rightNode = effects[index + 1]->k_ptr->backendObject();
373 }
374
375 QList<QObjectPair> disconnections, connections;
376 QObject *beffect = effect->k_ptr->backendObject();
377 disconnections << QObjectPair(leftNode, beffect) << QObjectPair(beffect, rightNode);
378 connections << QObjectPair(leftNode, rightNode);
379
380 if (executeTransaction(disconnections, connections)) {
381 effect->k_ptr->removeDestructionHandler(this);
382 effects.removeAt(index);
383 return true;
384 }
385 return false;
386}
387#endif //QT_NO_PHONON_EFFECT
388
389
390void PathPrivate::phononObjectDestroyed(MediaNodePrivate *mediaNodePrivate)
391{
392 Q_ASSERT(mediaNodePrivate);
393 if (mediaNodePrivate == sinkNode->k_ptr || mediaNodePrivate == sourceNode->k_ptr) {
394 //let's first disconnectq the path from its source and sink
395 QObject *bsink = sinkNode->k_ptr->backendObject();
396 QObject *bsource = sourceNode->k_ptr->backendObject();
397 QList<QObjectPair> disconnections;
398#ifndef QT_NO_PHONON_EFFECT
399 disconnections << QObjectPair(bsource, effects.isEmpty() ? bsink : effects.first()->k_ptr->backendObject());
400 if (!effects.isEmpty())
401 disconnections << QObjectPair(effects.last()->k_ptr->backendObject(), bsink);
402#else
403 disconnections << QObjectPair(bsource, bsink);
404#endif //QT_NO_PHONON_EFFECT
405
406 executeTransaction(disconnections, QList<QObjectPair>());
407
408 Path p; //temporary path
409 p.d = this;
410 if (mediaNodePrivate == sinkNode->k_ptr) {
411 sourceNode->k_ptr->removeOutputPath(p);
412 sourceNode->k_ptr->removeDestructionHandler(this);
413 } else {
414 sinkNode->k_ptr->removeInputPath(p);
415 sinkNode->k_ptr->removeDestructionHandler(this);
416 }
417 sourceNode = nullptr;
418 sinkNode = nullptr;
419 } else {
420#ifndef QT_NO_PHONON_EFFECT
421 for (int i = 0; i < effects.count(); ++i) {
422 Effect *e = effects.at(i);
423 if (e->k_ptr == mediaNodePrivate) {
424 removeEffect(e);
425 }
426 }
427#endif //QT_NO_PHONON_EFFECT
428 }
429}
430
431Path createPath(MediaNode *source, MediaNode *sink)
432{
433 Path p;
434 if (!p.reconnect(source, sink)) {
435 const QObject *const src = source ? (source->k_ptr->qObject()
436#ifndef QT_NO_DYNAMIC_CAST
437 ? source->k_ptr->qObject() : dynamic_cast<QObject *>(source)
438#endif
439 ) : nullptr;
440 const QObject *const snk = sink ? (sink->k_ptr->qObject()
441#ifndef QT_NO_DYNAMIC_CAST
442 ? sink->k_ptr->qObject() : dynamic_cast<QObject *>(sink)
443#endif
444 ) : nullptr;
445 pWarning() << "Phonon::createPath: Cannot connect "
446 << (src ? src->metaObject()->className() : "")
447 << '(' << (src ? (src->objectName().isEmpty() ? "no objectName" : qPrintable(src->objectName())) : "null") << ") to "
448 << (snk ? snk->metaObject()->className() : "")
449 << '(' << (snk ? (snk->objectName().isEmpty() ? "no objectName" : qPrintable(snk->objectName())) : "null")
450 << ").";
451 }
452 return p;
453}
454
455
456Path & Path::operator=(const Path &other)
457{
458 d = other.d;
459 return *this;
460}
461
462bool Path::operator==(const Path &other) const
463{
464 return d == other.d;
465}
466
467bool Path::operator!=(const Path &other) const
468{
469 return !operator==(other);
470}
471
472} // namespace Phonon
virtual bool startConnectionChange(QSet< QObject * >)=0
When this function is called the nodes given in the parameter list should not lose any signal data wh...
virtual bool endConnectionChange(QSet< QObject * >)=0
When this function is called the nodes given in the parameter list may lose signal data when a port i...
Effects that can be inserted into a Path.
Definition effect.h:64
Provides a tuple of enduser visible name and description.
Connection object providing convenient effect insertion.
Definition path.h:62
bool operator!=(const Path &p) const
Returns true if this Path is not equal to p; otherwise returns false;.
Definition path.cpp:467
Path & operator=(const Path &p)
Assigns p to this Path and returns a reference to this Path.
Definition path.cpp:456
Effect * insertEffect(const EffectDescription &desc, Effect *insertBefore=nullptr)
Creates and inserts an effect into the path.
Definition path.cpp:86
bool reconnect(MediaNode *source, MediaNode *sink)
Tries to change the MediaNodes the path is connected to.
Definition path.cpp:154
bool removeEffect(Effect *effect)
Removes an effect from the path.
Definition path.cpp:143
Path()
Creates an invalid path.
Definition path.cpp:70
bool isValid() const
Returns whether the path object connects two MediaNodes or not.
Definition path.cpp:80
bool operator==(const Path &p) const
Returns true if this Path is equal to p; otherwise returns false;.
Definition path.cpp:462
~Path()
Destroys this reference to the Path.
Definition path.cpp:66
MediaNode * sink() const
Returns the sink MediaNode used by the path.
Definition path.cpp:283
QList< Effect * > effects() const
Returns a list of Effect objects that are currently used as effects.
Definition path.cpp:148
bool disconnect()
Disconnects the path from the MediaNodes it was connected to.
Definition path.cpp:224
MediaNode * source() const
Returns the source MediaNode used by the path.
Definition path.cpp:278
const_reference at(qsizetype i) const const
iterator begin()
const_iterator constBegin() const const
const_iterator constEnd() const const
bool contains(const AT &value) const const
qsizetype count() const const
iterator end()
const char * className() const const
virtual const QMetaObject * metaObject() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:20:24 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.