summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/phonon/phonon/path.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/phonon/phonon/path.cpp')
-rw-r--r--src/3rdparty/phonon/phonon/path.cpp472
1 files changed, 472 insertions, 0 deletions
diff --git a/src/3rdparty/phonon/phonon/path.cpp b/src/3rdparty/phonon/phonon/path.cpp
new file mode 100644
index 0000000000..b46d30afc9
--- /dev/null
+++ b/src/3rdparty/phonon/phonon/path.cpp
@@ -0,0 +1,472 @@
+/* This file is part of the KDE project
+ Copyright (C) 2007 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) version 3, or any
+ later version accepted by the membership of KDE e.V. (or its
+ successor approved by the membership of KDE e.V.), Trolltech ASA
+ (or its successors, if any) and the KDE Free Qt Foundation, which shall
+ act as a proxy defined in Section 6 of version 3 of the license.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "path.h"
+#include "path_p.h"
+
+#include "phononnamespace_p.h"
+#include "backendinterface.h"
+#include "factory_p.h"
+#include "medianode.h"
+#include "medianode_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace Phonon
+{
+
+class ConnectionTransaction
+{
+ public:
+ ConnectionTransaction(BackendInterface *b, const QSet<QObject*> &x) : backend(b), list(x)
+ {
+ success = backend->startConnectionChange(list);
+ }
+ ~ConnectionTransaction()
+ {
+ backend->endConnectionChange(list);
+ }
+ operator bool()
+ {
+ return success;
+ }
+ private:
+ bool success;
+ BackendInterface *const backend;
+ const QSet<QObject*> list;
+};
+
+PathPrivate::~PathPrivate()
+{
+#ifndef QT_NO_PHONON_EFFECT
+ foreach (Effect *e, effects) {
+ e->k_ptr->removeDestructionHandler(this);
+ }
+ delete effectsParent;
+#endif
+}
+
+Path::~Path()
+{
+}
+
+Path::Path()
+ : d(new PathPrivate)
+{
+}
+
+Path::Path(const Path &rhs)
+ : d(rhs.d)
+{
+}
+
+bool Path::isValid() const
+{
+ return d->sourceNode != 0 && d->sinkNode != 0;
+}
+
+#ifndef QT_NO_PHONON_EFFECT
+Effect *Path::insertEffect(const EffectDescription &desc, Effect *insertBefore)
+{
+ if (!d->effectsParent) {
+ d->effectsParent = new QObject;
+ }
+ Effect *e = new Effect(desc, d->effectsParent);
+ if (!e->isValid()) {
+ delete e;
+ return 0;
+ }
+ bool success = insertEffect(e, insertBefore);
+ if (!success) {
+ delete e;
+ return 0;
+ }
+ return e;
+}
+
+bool Path::insertEffect(Effect *newEffect, Effect *insertBefore)
+{
+ QObject *newEffectBackend = newEffect ? newEffect->k_ptr->backendObject() : 0;
+ if (!isValid() || !newEffectBackend || d->effects.contains(newEffect) ||
+ (insertBefore && (!d->effects.contains(insertBefore) || !insertBefore->k_ptr->backendObject()))) {
+ return false;
+ }
+ QObject *leftNode = 0;
+ QObject *rightNode = 0;
+ const int insertIndex = insertBefore ? d->effects.indexOf(insertBefore) : d->effects.size();
+ if (insertIndex == 0) {
+ //prepend
+ leftNode = d->sourceNode->k_ptr->backendObject();
+ } else {
+ leftNode = d->effects[insertIndex - 1]->k_ptr->backendObject();
+ }
+
+ if (insertIndex == d->effects.size()) {
+ //append
+ rightNode = d->sinkNode->k_ptr->backendObject();
+ } else {
+ Q_ASSERT(insertBefore);
+ rightNode = insertBefore->k_ptr->backendObject();
+ }
+
+ QList<QObjectPair> disconnections, connections;
+ disconnections << QObjectPair(leftNode, rightNode);
+ connections << QObjectPair(leftNode, newEffectBackend)
+ << QObjectPair(newEffectBackend, rightNode);
+
+ if (d->executeTransaction(disconnections, connections)) {
+ newEffect->k_ptr->addDestructionHandler(d.data());
+ d->effects.insert(insertIndex, newEffect);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool Path::removeEffect(Effect *effect)
+{
+ return d->removeEffect(effect);
+}
+
+QList<Effect *> Path::effects() const
+{
+ return d->effects;
+}
+#endif //QT_NO_PHONON_EFFECT
+
+bool Path::reconnect(MediaNode *source, MediaNode *sink)
+{
+ if (!source || !sink || !source->k_ptr->backendObject() || !sink->k_ptr->backendObject()) {
+ return false;
+ }
+
+ QList<QObjectPair> disconnections, connections;
+
+ //backend objects
+ QObject *bnewSource = source->k_ptr->backendObject();
+ QObject *bnewSink = sink->k_ptr->backendObject();
+ QObject *bcurrentSource = d->sourceNode ? d->sourceNode->k_ptr->backendObject() : 0;
+ QObject *bcurrentSink = d->sinkNode ? d->sinkNode->k_ptr->backendObject() : 0;
+
+ if (bnewSource != bcurrentSource) {
+ //we need to change the source
+#ifndef QT_NO_PHONON_EFFECT
+ MediaNode *next = d->effects.isEmpty() ? sink : d->effects.first();
+#else
+ MediaNode *next = sink;
+#endif //QT_NO_PHONON_EFFECT
+ QObject *bnext = next->k_ptr->backendObject();
+ if (bcurrentSource)
+ disconnections << QObjectPair(bcurrentSource, bnext);
+ connections << QObjectPair(bnewSource, bnext);
+ }
+
+ if (bnewSink != bcurrentSink) {
+#ifndef QT_NO_PHONON_EFFECT
+ MediaNode *previous = d->effects.isEmpty() ? source : d->effects.last();
+#else
+ MediaNode *previous = source;
+#endif //QT_NO_PHONON_EFFECT
+ QObject *bprevious = previous->k_ptr->backendObject();
+ if (bcurrentSink)
+ disconnections << QObjectPair(bprevious, bcurrentSink);
+ QObjectPair pair(bprevious, bnewSink);
+ if (!connections.contains(pair)) //avoid connecting twice
+ connections << pair;
+ }
+
+ if (d->executeTransaction(disconnections, connections)) {
+
+ //everything went well: let's update the path and the sink node
+ if (d->sinkNode != sink) {
+ if (d->sinkNode) {
+ d->sinkNode->k_ptr->removeInputPath(*this);
+ d->sinkNode->k_ptr->removeDestructionHandler(d.data());
+ }
+ sink->k_ptr->addInputPath(*this);
+ d->sinkNode = sink;
+ d->sinkNode->k_ptr->addDestructionHandler(d.data());
+ }
+
+ //everything went well: let's update the path and the source node
+ if (d->sourceNode != source) {
+ source->k_ptr->addOutputPath(*this);
+ if (d->sourceNode) {
+ d->sourceNode->k_ptr->removeOutputPath(*this);
+ d->sourceNode->k_ptr->removeDestructionHandler(d.data());
+ }
+ d->sourceNode = source;
+ d->sourceNode->k_ptr->addDestructionHandler(d.data());
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool Path::disconnect()
+{
+ if (!isValid()) {
+ return false;
+ }
+
+ QObjectList list;
+ if (d->sourceNode)
+ list << d->sourceNode->k_ptr->backendObject();
+#ifndef QT_NO_PHONON_EFFECT
+ foreach(Effect *e, d->effects) {
+ list << e->k_ptr->backendObject();
+ }
+#endif
+ if (d->sinkNode) {
+ list << d->sinkNode->k_ptr->backendObject();
+ }
+
+ //lets build the disconnection list
+ QList<QObjectPair> disco;
+ if (list.count() >=2 ) {
+ QObjectList::const_iterator it = list.begin();
+ for(;it+1 != list.end();++it) {
+ disco << QObjectPair(*it, *(it+1));
+ }
+ }
+
+ if (d->executeTransaction(disco, QList<QObjectPair>())) {
+ //everything went well, let's remove the reference
+ //to the paths from the source and sink
+ if (d->sourceNode) {
+ d->sourceNode->k_ptr->removeOutputPath(*this);
+ d->sourceNode->k_ptr->removeDestructionHandler(d.data());
+ }
+ d->sourceNode = 0;
+
+#ifndef QT_NO_PHONON_EFFECT
+ foreach(Effect *e, d->effects) {
+ e->k_ptr->removeDestructionHandler(d.data());
+ }
+ d->effects.clear();
+#endif
+
+ if (d->sinkNode) {
+ d->sinkNode->k_ptr->removeInputPath(*this);
+ d->sinkNode->k_ptr->removeDestructionHandler(d.data());
+ }
+ d->sinkNode = 0;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+MediaNode *Path::source() const
+{
+ return d->sourceNode;
+}
+
+MediaNode *Path::sink() const
+{
+ return d->sinkNode;
+}
+
+
+
+bool PathPrivate::executeTransaction( const QList<QObjectPair> &disconnections, const QList<QObjectPair> &connections)
+{
+ QSet<QObject*> nodesForTransaction;
+ foreach(const QObjectPair &pair, disconnections) {
+ nodesForTransaction << pair.first;
+ nodesForTransaction << pair.second;
+ }
+ foreach(const QObjectPair &pair, connections) {
+ nodesForTransaction << pair.first;
+ nodesForTransaction << pair.second;
+ }
+ BackendInterface *backend = qobject_cast<BackendInterface *>(Factory::backend());
+ if (!backend)
+ return false;
+
+ ConnectionTransaction transaction(backend, nodesForTransaction);
+ if (!transaction)
+ return false;
+
+ QList<QObjectPair>::const_iterator it = disconnections.begin();
+ for(;it != disconnections.end();++it) {
+ const QObjectPair &pair = *it;
+ if (!backend->disconnectNodes(pair.first, pair.second)) {
+
+ //Error: a disconnection failed
+ QList<QObjectPair>::const_iterator it2 = disconnections.begin();
+ for(; it2 != it; ++it2) {
+ const QObjectPair &pair = *it2;
+ bool success = backend->connectNodes(pair.first, pair.second);
+ Q_ASSERT(success); //a failure here means it is impossible to reestablish the connection
+ Q_UNUSED(success);
+ }
+ return false;
+ }
+ }
+
+ for(it = connections.begin(); it != connections.end();++it) {
+ const QObjectPair &pair = *it;
+ if (!backend->connectNodes(pair.first, pair.second)) {
+ //Error: a connection failed
+ QList<QObjectPair>::const_iterator it2 = connections.begin();
+ for(; it2 != it; ++it2) {
+ const QObjectPair &pair = *it2;
+ bool success = backend->disconnectNodes(pair.first, pair.second);
+ Q_ASSERT(success); //a failure here means it is impossible to reestablish the connection
+ Q_UNUSED(success);
+ }
+
+ //and now let's reconnect the nodes that were disconnected: rollback
+ foreach(const QObjectPair &pair, disconnections) {
+ bool success = backend->connectNodes(pair.first, pair.second);
+ Q_ASSERT(success); //a failure here means it is impossible to reestablish the connection
+ Q_UNUSED(success);
+ }
+
+ return false;
+
+ }
+ }
+ return true;
+}
+
+#ifndef QT_NO_PHONON_EFFECT
+bool PathPrivate::removeEffect(Effect *effect)
+{
+ if (!effects.contains(effect))
+ return false;
+
+ QObject *leftNode = 0;
+ QObject *rightNode = 0;
+ const int index = effects.indexOf(effect);
+ if (index == 0) {
+ leftNode = sourceNode->k_ptr->backendObject(); //append
+ } else {
+ leftNode = effects[index - 1]->k_ptr->backendObject();
+ }
+ if (index == effects.size()-1) {
+ rightNode = sinkNode->k_ptr->backendObject(); //prepend
+ } else {
+ rightNode = effects[index + 1]->k_ptr->backendObject();
+ }
+
+ QList<QObjectPair> disconnections, connections;
+ QObject *beffect = effect->k_ptr->backendObject();
+ disconnections << QObjectPair(leftNode, beffect) << QObjectPair(beffect, rightNode);
+ connections << QObjectPair(leftNode, rightNode);
+
+ if (executeTransaction(disconnections, connections)) {
+ effect->k_ptr->removeDestructionHandler(this);
+ effects.removeAt(index);
+ return true;
+ }
+ return false;
+}
+#endif //QT_NO_PHONON_EFFECT
+
+
+void PathPrivate::phononObjectDestroyed(MediaNodePrivate *mediaNodePrivate)
+{
+ Q_ASSERT(mediaNodePrivate);
+ if (mediaNodePrivate == sinkNode->k_ptr || mediaNodePrivate == sourceNode->k_ptr) {
+ //let's first disconnectq the path from its source and sink
+ QObject *bsink = sinkNode->k_ptr->backendObject();
+ QObject *bsource = sourceNode->k_ptr->backendObject();
+ QList<QObjectPair> disconnections;
+#ifndef QT_NO_PHONON_EFFECT
+ disconnections << QObjectPair(bsource, effects.isEmpty() ? bsink : effects.first()->k_ptr->backendObject());
+ if (!effects.isEmpty())
+ disconnections << QObjectPair(effects.last()->k_ptr->backendObject(), bsink);
+#else
+ disconnections << QObjectPair(bsource, bsink);
+#endif //QT_NO_PHONON_EFFECT
+
+ executeTransaction(disconnections, QList<QObjectPair>());
+
+ Path p; //temporary path
+ p.d = this;
+ if (mediaNodePrivate == sinkNode->k_ptr) {
+ sourceNode->k_ptr->removeOutputPath(p);
+ sourceNode->k_ptr->removeDestructionHandler(this);
+ } else {
+ sinkNode->k_ptr->removeInputPath(p);
+ sinkNode->k_ptr->removeDestructionHandler(this);
+ }
+ sourceNode = 0;
+ sinkNode = 0;
+ } else {
+#ifndef QT_NO_PHONON_EFFECT
+ foreach (Effect *e, effects) {
+ if (e->k_ptr == mediaNodePrivate) {
+ removeEffect(e);
+ }
+ }
+#endif //QT_NO_PHONON_EFFECT
+ }
+}
+
+Path createPath(MediaNode *source, MediaNode *sink)
+{
+ Path p;
+ if (!p.reconnect(source, sink)) {
+ const QObject *const src = source ? (source->k_ptr->qObject()
+#ifndef QT_NO_DYNAMIC_CAST
+ ? source->k_ptr->qObject() : dynamic_cast<QObject *>(source)
+#endif
+ ) : 0;
+ const QObject *const snk = sink ? (sink->k_ptr->qObject()
+#ifndef QT_NO_DYNAMIC_CAST
+ ? sink->k_ptr->qObject() : dynamic_cast<QObject *>(sink)
+#endif
+ ) : 0;
+ pWarning() << "Phonon::createPath: Cannot connect "
+ << (src ? src->metaObject()->className() : "")
+ << '(' << (src ? (src->objectName().isEmpty() ? "no objectName" : qPrintable(src->objectName())) : "null") << ") to "
+ << (snk ? snk->metaObject()->className() : "")
+ << '(' << (snk ? (snk->objectName().isEmpty() ? "no objectName" : qPrintable(snk->objectName())) : "null")
+ << ").";
+ }
+ return p;
+}
+
+
+Path & Path::operator=(const Path &other)
+{
+ d = other.d;
+ return *this;
+}
+
+bool Path::operator==(const Path &other) const
+{
+ return d == other.d;
+}
+
+bool Path::operator!=(const Path &other) const
+{
+ return !operator==(other);
+}
+
+} // namespace Phonon
+
+QT_END_NAMESPACE