File indexing completed on 2024-04-21 04:43:18

0001 /*  This file is part of the KDE project
0002     Copyright (C) 2007 Matthias Kretz <kretz@kde.org>
0003 
0004     This library is free software; you can redistribute it and/or
0005     modify it under the terms of the GNU Lesser General Public
0006     License as published by the Free Software Foundation; either
0007     version 2.1 of the License, or (at your option) version 3, or any
0008     later version accepted by the membership of KDE e.V. (or its
0009     successor approved by the membership of KDE e.V.), Nokia Corporation
0010     (or its successors, if any) and the KDE Free Qt Foundation, which shall
0011     act as a proxy defined in Section 6 of version 3 of the license.
0012 
0013     This library is distributed in the hope that it will be useful,
0014     but WITHOUT ANY WARRANTY; without even the implied warranty of
0015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0016     Lesser General Public License for more details.
0017 
0018     You should have received a copy of the GNU Lesser General Public 
0019     License along with this library.  If not, see <http://www.gnu.org/licenses/>.
0020 
0021 */
0022 
0023 #include "path.h"
0024 #include "path_p.h"
0025 
0026 #include "phononnamespace_p.h"
0027 #include "backendinterface.h"
0028 #include "factory_p.h"
0029 #include "medianode.h"
0030 #include "medianode_p.h"
0031 
0032 namespace Phonon
0033 {
0034 
0035 class ConnectionTransaction
0036 {
0037     public:
0038         ConnectionTransaction(BackendInterface *b, const QSet<QObject*> &x) : backend(b), list(x)
0039         {
0040             success = backend->startConnectionChange(list);
0041         }
0042         ~ConnectionTransaction()
0043         {
0044             backend->endConnectionChange(list);
0045         }
0046         operator bool()
0047         {
0048             return success;
0049         }
0050     private:
0051         bool success;
0052         BackendInterface *const backend;
0053         const QSet<QObject*> list;
0054 };
0055 
0056 PathPrivate::~PathPrivate()
0057 {
0058 #ifndef QT_NO_PHONON_EFFECT
0059     for (int i = 0; i < effects.count(); ++i) {
0060         effects.at(i)->k_ptr->removeDestructionHandler(this);
0061     }
0062     delete effectsParent;
0063 #endif
0064 }
0065 
0066 Path::~Path()
0067 {
0068 }
0069 
0070 Path::Path()
0071     : d(new PathPrivate)
0072 {
0073 }
0074 
0075 Path::Path(const Path &rhs)
0076     : d(rhs.d)
0077 {
0078 }
0079 
0080 bool Path::isValid() const
0081 {
0082     return d->sourceNode != nullptr && d->sinkNode != nullptr;
0083 }
0084 
0085 #ifndef QT_NO_PHONON_EFFECT
0086 Effect *Path::insertEffect(const EffectDescription &desc, Effect *insertBefore)
0087 {
0088     if (!d->effectsParent) {
0089         d->effectsParent = new QObject;
0090     }
0091     Effect *e = new Effect(desc, d->effectsParent);
0092     if (!e->isValid()) {
0093         delete e;
0094         return nullptr;
0095     }
0096     bool success = insertEffect(e, insertBefore);
0097     if (!success) {
0098         delete e;
0099         return nullptr;
0100     }
0101     return e;
0102 }
0103 
0104 bool Path::insertEffect(Effect *newEffect, Effect *insertBefore)
0105 {
0106     QObject *newEffectBackend = newEffect ? newEffect->k_ptr->backendObject() : nullptr;
0107     if (!isValid() || !newEffectBackend || d->effects.contains(newEffect) ||
0108             (insertBefore && (!d->effects.contains(insertBefore) || !insertBefore->k_ptr->backendObject()))) {
0109         return false;
0110     }
0111     QObject *leftNode = nullptr;
0112     QObject *rightNode = nullptr;
0113     const int insertIndex = insertBefore ? d->effects.indexOf(insertBefore) : d->effects.size();
0114     if (insertIndex == 0) {
0115         //prepend
0116         leftNode = d->sourceNode->k_ptr->backendObject();
0117     } else {
0118         leftNode = d->effects[insertIndex - 1]->k_ptr->backendObject();
0119     }
0120 
0121     if (insertIndex == d->effects.size()) {
0122         //append
0123         rightNode = d->sinkNode->k_ptr->backendObject();
0124     } else {
0125         Q_ASSERT(insertBefore);
0126         rightNode = insertBefore->k_ptr->backendObject();
0127     }
0128 
0129     QList<QObjectPair> disconnections, connections;
0130     disconnections << QObjectPair(leftNode, rightNode);
0131     connections << QObjectPair(leftNode, newEffectBackend)
0132         << QObjectPair(newEffectBackend, rightNode);
0133 
0134     if (d->executeTransaction(disconnections, connections)) {
0135         newEffect->k_ptr->addDestructionHandler(d.data());
0136         d->effects.insert(insertIndex, newEffect);
0137         return true;
0138     } else {
0139         return false;
0140     }
0141 }
0142 
0143 bool Path::removeEffect(Effect *effect)
0144 {
0145     return d->removeEffect(effect);
0146 }
0147 
0148 QList<Effect *> Path::effects() const
0149 {
0150     return d->effects;
0151 }
0152 #endif //QT_NO_PHONON_EFFECT
0153 
0154 bool Path::reconnect(MediaNode *source, MediaNode *sink)
0155 {
0156     if (!source || !sink || !source->k_ptr->backendObject() || !sink->k_ptr->backendObject()) {
0157         return false;
0158     }
0159 
0160     QList<QObjectPair> disconnections, connections;
0161 
0162     //backend objects
0163     QObject *bnewSource = source->k_ptr->backendObject();
0164     QObject *bnewSink = sink->k_ptr->backendObject();
0165     QObject *bcurrentSource = d->sourceNode ? d->sourceNode->k_ptr->backendObject() : nullptr;
0166     QObject *bcurrentSink = d->sinkNode ? d->sinkNode->k_ptr->backendObject() : nullptr;
0167 
0168     if (bnewSource != bcurrentSource) {
0169         //we need to change the source
0170 #ifndef QT_NO_PHONON_EFFECT
0171         MediaNode *next = d->effects.isEmpty() ? sink : d->effects.first();
0172 #else
0173         MediaNode *next = sink;
0174 #endif //QT_NO_PHONON_EFFECT
0175         QObject *bnext = next->k_ptr->backendObject();
0176         if (bcurrentSource)
0177             disconnections << QObjectPair(bcurrentSource, bnext);
0178         connections << QObjectPair(bnewSource, bnext);
0179     }
0180 
0181     if (bnewSink != bcurrentSink) {
0182 #ifndef QT_NO_PHONON_EFFECT
0183         MediaNode *previous = d->effects.isEmpty() ? source : d->effects.last();
0184 #else
0185         MediaNode *previous = source;
0186 #endif //QT_NO_PHONON_EFFECT
0187         QObject *bprevious = previous->k_ptr->backendObject();
0188         if (bcurrentSink)
0189             disconnections << QObjectPair(bprevious, bcurrentSink);
0190         QObjectPair pair(bprevious, bnewSink);
0191         if (!connections.contains(pair)) //avoid connecting twice
0192             connections << pair;
0193     }
0194 
0195     if (d->executeTransaction(disconnections, connections)) {
0196 
0197         //everything went well: let's update the path and the sink node
0198         if (d->sinkNode != sink) {
0199             if (d->sinkNode) {
0200                 d->sinkNode->k_ptr->removeInputPath(*this);
0201                 d->sinkNode->k_ptr->removeDestructionHandler(d.data());
0202             }
0203             sink->k_ptr->addInputPath(*this);
0204             d->sinkNode = sink;
0205             d->sinkNode->k_ptr->addDestructionHandler(d.data());
0206         }
0207 
0208         //everything went well: let's update the path and the source node
0209         if (d->sourceNode != source) {
0210             source->k_ptr->addOutputPath(*this);
0211             if (d->sourceNode) {
0212                 d->sourceNode->k_ptr->removeOutputPath(*this);
0213                 d->sourceNode->k_ptr->removeDestructionHandler(d.data());
0214             }
0215             d->sourceNode = source;
0216             d->sourceNode->k_ptr->addDestructionHandler(d.data());
0217         }
0218         return true;
0219     } else {
0220         return false;
0221     }
0222 }
0223 
0224 bool Path::disconnect()
0225 {
0226     if (!isValid()) {
0227         return false;
0228     }
0229 
0230     QObjectList list;
0231     if (d->sourceNode)
0232         list << d->sourceNode->k_ptr->backendObject();
0233 #ifndef QT_NO_PHONON_EFFECT
0234     for (int i = 0; i < d->effects.count(); ++i) {
0235         list << d->effects.at(i)->k_ptr->backendObject();
0236     }
0237 #endif
0238     if (d->sinkNode) {
0239         list << d->sinkNode->k_ptr->backendObject();
0240     }
0241 
0242     //lets build the disconnection list
0243     QList<QObjectPair> disco;
0244     if (list.count() >=2 ) {
0245         QObjectList::const_iterator it = list.constBegin();
0246         for(;it+1 != list.constEnd();++it) {
0247             disco << QObjectPair(*it, *(it+1));
0248         }
0249     }
0250 
0251     if (d->executeTransaction(disco, QList<QObjectPair>())) {
0252         //everything went well, let's remove the reference
0253         //to the paths from the source and sink
0254         if (d->sourceNode) {
0255             d->sourceNode->k_ptr->removeOutputPath(*this);
0256             d->sourceNode->k_ptr->removeDestructionHandler(d.data());
0257         }
0258         d->sourceNode = nullptr;
0259 
0260 #ifndef QT_NO_PHONON_EFFECT
0261         for (int i = 0; i < d->effects.count(); ++i) {
0262             d->effects.at(i)->k_ptr->removeDestructionHandler(d.data());
0263         }
0264         d->effects.clear();
0265 #endif 
0266 
0267         if (d->sinkNode) {
0268             d->sinkNode->k_ptr->removeInputPath(*this);
0269             d->sinkNode->k_ptr->removeDestructionHandler(d.data());
0270         }
0271         d->sinkNode = nullptr;
0272         return true;
0273     } else {
0274         return false;
0275     }
0276 }
0277 
0278 MediaNode *Path::source() const
0279 {
0280     return d->sourceNode;
0281 }
0282 
0283 MediaNode *Path::sink() const
0284 {
0285     return d->sinkNode;
0286 }
0287 
0288 
0289 
0290 bool PathPrivate::executeTransaction( const QList<QObjectPair> &disconnections, const QList<QObjectPair> &connections)
0291 {
0292     QSet<QObject*> nodesForTransaction;
0293     for (int i = 0; i < disconnections.count(); ++i) {
0294         const QObjectPair &pair = disconnections.at(i);
0295         nodesForTransaction << pair.first;
0296         nodesForTransaction << pair.second;
0297     }
0298     for (int i = 0; i < connections.count(); ++i) {
0299         const QObjectPair &pair = connections.at(i);
0300         nodesForTransaction << pair.first;
0301         nodesForTransaction << pair.second;
0302     }
0303     BackendInterface *backend = qobject_cast<BackendInterface *>(Factory::backend());
0304     if (!backend)
0305         return false;
0306 
0307     ConnectionTransaction transaction(backend, nodesForTransaction);
0308     if (!transaction)
0309         return false;
0310 
0311     QList<QObjectPair>::const_iterator it = disconnections.begin();
0312     for(;it != disconnections.end();++it) {
0313         const QObjectPair &pair = *it;
0314         if (!backend->disconnectNodes(pair.first, pair.second)) {
0315 
0316             //Error: a disconnection failed
0317             QList<QObjectPair>::const_iterator it2 = disconnections.begin();
0318             for(; it2 != it; ++it2) {
0319                 const QObjectPair &pair = *it2;
0320                 bool success = backend->connectNodes(pair.first, pair.second);
0321                 Q_ASSERT(success); //a failure here means it is impossible to reestablish the connection
0322                 Q_UNUSED(success);
0323             }
0324             return false;
0325         }
0326     }
0327 
0328     for(it = connections.begin(); it != connections.end();++it) {
0329         const QObjectPair &pair = *it;
0330         if (!backend->connectNodes(pair.first, pair.second)) {
0331             //Error: a connection failed
0332             QList<QObjectPair>::const_iterator it2 = connections.begin();
0333             for(; it2 != it; ++it2) {
0334                 const QObjectPair &pair = *it2;
0335                 bool success = backend->disconnectNodes(pair.first, pair.second);
0336                 Q_ASSERT(success); //a failure here means it is impossible to reestablish the connection
0337                 Q_UNUSED(success);
0338             }
0339 
0340             //and now let's reconnect the nodes that were disconnected: rollback
0341             for (int i = 0; i < disconnections.count(); ++i) {
0342                 const QObjectPair &pair = disconnections.at(i);
0343                 bool success = backend->connectNodes(pair.first, pair.second);
0344                 Q_ASSERT(success); //a failure here means it is impossible to reestablish the connection
0345                 Q_UNUSED(success);
0346             }
0347 
0348             return false;
0349 
0350         }
0351     }
0352     return true;
0353 }
0354 
0355 #ifndef QT_NO_PHONON_EFFECT
0356 bool PathPrivate::removeEffect(Effect *effect)
0357 {
0358     if (!effects.contains(effect))
0359         return false;
0360 
0361     QObject *leftNode = nullptr;
0362     QObject *rightNode = nullptr;
0363     const int index = effects.indexOf(effect);
0364     if (index == 0) {
0365         leftNode = sourceNode->k_ptr->backendObject(); //append
0366     } else {
0367         leftNode = effects[index - 1]->k_ptr->backendObject();
0368     }
0369     if (index == effects.size()-1) {
0370         rightNode = sinkNode->k_ptr->backendObject(); //prepend
0371     } else {
0372         rightNode = effects[index + 1]->k_ptr->backendObject();
0373     }
0374 
0375     QList<QObjectPair> disconnections, connections;
0376     QObject *beffect = effect->k_ptr->backendObject();
0377     disconnections << QObjectPair(leftNode, beffect) << QObjectPair(beffect, rightNode);
0378     connections << QObjectPair(leftNode, rightNode);
0379 
0380     if (executeTransaction(disconnections, connections)) {
0381         effect->k_ptr->removeDestructionHandler(this);
0382         effects.removeAt(index);
0383         return true;
0384     }
0385     return false;
0386 }
0387 #endif //QT_NO_PHONON_EFFECT
0388 
0389 
0390 void PathPrivate::phononObjectDestroyed(MediaNodePrivate *mediaNodePrivate)
0391 {
0392     Q_ASSERT(mediaNodePrivate);
0393     if (mediaNodePrivate == sinkNode->k_ptr || mediaNodePrivate == sourceNode->k_ptr) {
0394         //let's first disconnectq the path from its source and sink
0395         QObject *bsink = sinkNode->k_ptr->backendObject();
0396         QObject *bsource = sourceNode->k_ptr->backendObject();
0397         QList<QObjectPair> disconnections;
0398 #ifndef QT_NO_PHONON_EFFECT
0399         disconnections << QObjectPair(bsource, effects.isEmpty() ? bsink : effects.first()->k_ptr->backendObject());
0400         if (!effects.isEmpty())
0401             disconnections << QObjectPair(effects.last()->k_ptr->backendObject(), bsink);
0402 #else
0403         disconnections << QObjectPair(bsource, bsink);
0404 #endif //QT_NO_PHONON_EFFECT
0405 
0406         executeTransaction(disconnections, QList<QObjectPair>());
0407 
0408         Path p; //temporary path
0409         p.d = this;
0410         if (mediaNodePrivate == sinkNode->k_ptr) {
0411             sourceNode->k_ptr->removeOutputPath(p);
0412             sourceNode->k_ptr->removeDestructionHandler(this);
0413         } else {
0414             sinkNode->k_ptr->removeInputPath(p);
0415             sinkNode->k_ptr->removeDestructionHandler(this);
0416         }
0417         sourceNode = nullptr;
0418         sinkNode = nullptr;
0419     } else {
0420 #ifndef QT_NO_PHONON_EFFECT
0421         for (int i = 0; i < effects.count(); ++i) {
0422             Effect *e = effects.at(i);
0423             if (e->k_ptr == mediaNodePrivate) {
0424                 removeEffect(e);
0425             }
0426         }
0427 #endif //QT_NO_PHONON_EFFECT
0428     }
0429 }
0430 
0431 Path createPath(MediaNode *source, MediaNode *sink)
0432 {
0433     Path p;
0434     if (!p.reconnect(source, sink)) {
0435         const QObject *const src = source ? (source->k_ptr->qObject()
0436 #ifndef QT_NO_DYNAMIC_CAST
0437                 ? source->k_ptr->qObject() : dynamic_cast<QObject *>(source)
0438 #endif
0439                 ) : nullptr;
0440         const QObject *const snk = sink ? (sink->k_ptr->qObject()
0441 #ifndef QT_NO_DYNAMIC_CAST
0442                 ? sink->k_ptr->qObject() : dynamic_cast<QObject *>(sink)
0443 #endif
0444                 ) : nullptr;
0445         pWarning() << "Phonon::createPath: Cannot connect "
0446             << (src ? src->metaObject()->className() : "")
0447             << '(' << (src ? (src->objectName().isEmpty() ? "no objectName" : qPrintable(src->objectName())) : "null") << ") to "
0448             << (snk ? snk->metaObject()->className() : "")
0449             << '(' << (snk ? (snk->objectName().isEmpty() ? "no objectName" : qPrintable(snk->objectName())) : "null")
0450             << ").";
0451     }
0452     return p;
0453 }
0454 
0455 
0456 Path & Path::operator=(const Path &other)
0457 {
0458     d = other.d;
0459     return *this;
0460 }
0461 
0462 bool Path::operator==(const Path &other) const
0463 {
0464     return d == other.d;
0465 }
0466 
0467 bool Path::operator!=(const Path &other) const
0468 {
0469     return !operator==(other);
0470 }
0471 
0472 } // namespace Phonon