<commit_before>/*++
Copyright (c) 2012 Microsoft Corporation

Module Name:

    distribute_forall_tactic.cpp

Abstract:

    <abstract>

Author:

    Leonardo de Moura (leonardo) 2012-02-18.

--*/
#include"tactical.h"
#include"rewriter_def.h"
#include"var_subst.h"

class distribute_forall_tactic : public tactic {

    struct rw_cfg : public default_rewriter_cfg {
        ast_manager & m;

        rw_cfg(ast_manager & _m):m(_m) {}
        bool reduce_quantifier(quantifier * old_q,
                               expr * new_body,
                               expr * const * new_patterns,
                               expr * const * new_no_patterns,
                               expr_ref & result,
                               proof_ref & result_pr) {

            if (m.is_not(new_body) && m.is_or(to_app(new_body)->get_arg(0))) {
                // (forall X (not (or F1 ... Fn)))
                // -->
                // (and (forall X (not F1))
                //      ...
                //      (forall X (not Fn)))
                app * or_e        = to_app(to_app(new_body)->get_arg(0));
                unsigned num_args = or_e->get_num_args();
                expr_ref_buffer new_args(m);
                for (unsigned i = 0; i < num_args; i++) {
                    expr * arg     = or_e->get_arg(i);
                    expr * not_arg = m.mk_not(arg);
                    quantifier_ref tmp_q(m);
                    tmp_q = m.update_quantifier(old_q, not_arg);
                    expr_ref new_q(m);
                    elim_unused_vars(m, tmp_q, new_q);
                    new_args.push_back(new_q);
                }
                result = m.mk_and(new_args.size(), new_args.c_ptr());
                return true;
            }

            if (m.is_and(new_body)) {
                // (forall X (and F1 ... Fn))
                // -->
                // (and (forall X F1)
                //      ...
                //      (forall X Fn)
                unsigned num_args = to_app(new_body)->get_num_args();
                expr_ref_buffer new_args(m);
                for (unsigned i = 0; i < num_args; i++) {
                    expr * arg     = to_app(new_body)->get_arg(i);
                    quantifier_ref tmp_q(m);
                    tmp_q = m.update_quantifier(old_q, arg);
                    expr_ref new_q(m);
                    elim_unused_vars(m, tmp_q, new_q);
                    new_args.push_back(new_q);
                }
                result = m.mk_and(new_args.size(), new_args.c_ptr());
                return true;
            }

            return false;
        }
    };

    struct rw : public rewriter_tpl<rw_cfg> {
        rw_cfg m_cfg;

        rw(ast_manager & m, bool proofs_enabled):
            rewriter_tpl<rw_cfg>(m, proofs_enabled, m_cfg),
            m_cfg(m) {
        }
    };

    rw * m_rw;

public:
    distribute_forall_tactic():m_rw(0) {}

    virtual tactic * translate(ast_manager & m) {
        return alloc(distribute_forall_tactic);
    }

    virtual void operator()(goal_ref const & g,
                            goal_ref_buffer & result,
                            model_converter_ref & mc,
                            proof_converter_ref & pc,
                            expr_dependency_ref & core) {
        SASSERT(g->is_well_sorted());
        ast_manager & m = g->m();
        bool produce_proofs = g->proofs_enabled();
        rw r(m, produce_proofs);
        #pragma omp critical (tactic_cancel)
        {
            m_rw = &r;
        }
        mc = 0; pc = 0; core = 0; result.reset();
        tactic_report report("distribute-forall", *g);

        expr_ref   new_curr(m);
        proof_ref  new_pr(m);
        unsigned size = g->size();
        for (unsigned idx = 0; idx < size; idx++) {
            if (g->inconsistent())
                break;
            expr * curr = g->form(idx);
            r(curr, new_curr, new_pr);
            if (g->proofs_enabled()) {
                proof * pr = g->pr(idx);
                new_pr     = m.mk_modus_ponens(pr, new_pr);
            }
            g->update(idx, new_curr, new_pr, g->dep(idx));
        }

        g->inc_depth();
        result.push_back(g.get());
        TRACE("distribute-forall", g->display(tout););
        SASSERT(g->is_well_sorted());
        #pragma omp critical (tactic_cancel)
        {
            m_rw = 0;
        }
    }

    virtual void set_cancel(bool f) {
        if (m_rw)
            m_rw->set_cancel(f);
    }

    virtual void cleanup() {}
};

tactic * mk_distribute_forall_tactic(ast_manager & m, params_ref const & p) {
    return alloc(distribute_forall_tactic);
}
<commit_msg>fix distribute forall, fixes issue #138<commit_after>/*++
Copyright (c) 2012 Microsoft Corporation

Module Name:

    distribute_forall_tactic.cpp

Abstract:

    <abstract>

Author:

    Leonardo de Moura (leonardo) 2012-02-18.

--*/
#include"tactical.h"
#include"rewriter_def.h"
#include"var_subst.h"

class distribute_forall_tactic : public tactic {

    struct rw_cfg : public default_rewriter_cfg {
        ast_manager & m;

        rw_cfg(ast_manager & _m):m(_m) {}
        bool reduce_quantifier(quantifier * old_q,
                               expr * new_body,
                               expr * const * new_patterns,
                               expr * const * new_no_patterns,
                               expr_ref & result,
                               proof_ref & result_pr) {

            if (!old_q->is_forall()) {
                return false;
            }

            if (m.is_not(new_body) && m.is_or(to_app(new_body)->get_arg(0))) {
                // (forall X (not (or F1 ... Fn)))
                // -->
                // (and (forall X (not F1))
                //      ...
                //      (forall X (not Fn)))
                app * or_e        = to_app(to_app(new_body)->get_arg(0));
                unsigned num_args = or_e->get_num_args();
                expr_ref_buffer new_args(m);
                for (unsigned i = 0; i < num_args; i++) {
                    expr * arg     = or_e->get_arg(i);
                    expr * not_arg = m.mk_not(arg);
                    quantifier_ref tmp_q(m);
                    tmp_q = m.update_quantifier(old_q, not_arg);
                    expr_ref new_q(m);
                    elim_unused_vars(m, tmp_q, new_q);
                    new_args.push_back(new_q);
                }
                result = m.mk_and(new_args.size(), new_args.c_ptr());
                return true;
            }

            if (m.is_and(new_body)) {
                // (forall X (and F1 ... Fn))
                // -->
                // (and (forall X F1)
                //      ...
                //      (forall X Fn)
                unsigned num_args = to_app(new_body)->get_num_args();
                expr_ref_buffer new_args(m);
                for (unsigned i = 0; i < num_args; i++) {
                    expr * arg     = to_app(new_body)->get_arg(i);
                    quantifier_ref tmp_q(m);
                    tmp_q = m.update_quantifier(old_q, arg);
                    expr_ref new_q(m);
                    elim_unused_vars(m, tmp_q, new_q);
                    new_args.push_back(new_q);
                }
                result = m.mk_and(new_args.size(), new_args.c_ptr());
                return true;
            }

            return false;
        }
    };

    struct rw : public rewriter_tpl<rw_cfg> {
        rw_cfg m_cfg;

        rw(ast_manager & m, bool proofs_enabled):
            rewriter_tpl<rw_cfg>(m, proofs_enabled, m_cfg),
            m_cfg(m) {
        }
    };

    rw * m_rw;

public:
    distribute_forall_tactic():m_rw(0) {}

    virtual tactic * translate(ast_manager & m) {
        return alloc(distribute_forall_tactic);
    }

    virtual void operator()(goal_ref const & g,
                            goal_ref_buffer & result,
                            model_converter_ref & mc,
                            proof_converter_ref & pc,
                            expr_dependency_ref & core) {
        SASSERT(g->is_well_sorted());
        ast_manager & m = g->m();
        bool produce_proofs = g->proofs_enabled();
        rw r(m, produce_proofs);
        #pragma omp critical (tactic_cancel)
        {
            m_rw = &r;
        }
        mc = 0; pc = 0; core = 0; result.reset();
        tactic_report report("distribute-forall", *g);

        expr_ref   new_curr(m);
        proof_ref  new_pr(m);
        unsigned size = g->size();
        for (unsigned idx = 0; idx < size; idx++) {
            if (g->inconsistent())
                break;
            expr * curr = g->form(idx);
            r(curr, new_curr, new_pr);
            if (g->proofs_enabled()) {
                proof * pr = g->pr(idx);
                new_pr     = m.mk_modus_ponens(pr, new_pr);
            }
            g->update(idx, new_curr, new_pr, g->dep(idx));
        }

        g->inc_depth();
        result.push_back(g.get());
        TRACE("distribute-forall", g->display(tout););
        SASSERT(g->is_well_sorted());
        #pragma omp critical (tactic_cancel)
        {
            m_rw = 0;
        }
    }

    virtual void set_cancel(bool f) {
        if (m_rw)
            m_rw->set_cancel(f);
    }

    virtual void cleanup() {}
};

tactic * mk_distribute_forall_tactic(ast_manager & m, params_ref const & p) {
    return alloc(distribute_forall_tactic);
}
<|endoftext|>


<commit_before>/*  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() ? source->k_ptr->qObject() : dynamic_cast<QObject *>(source)) : 0;
        const QObject *const snk = sink ? (sink->k_ptr->qObject() ? sink->k_ptr->qObject() : dynamic_cast<QObject *>(sink)) : 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
<commit_msg>when compiling with -fno-rtti/QT_NO_DYNAMIC_CAST we don't try as hard to get a useful warning message<commit_after>/*  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
<|endoftext|>


<commit_before>/*  Sirikata Input Plugin -- plugins/input
 *  InputDevice.cpp
 *
 *  Copyright (c) 2009, Patrick Reiter Horn
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are
 *  met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  * Neither the name of Sirikata nor the names of its contributors may
 *    be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sirikata/core/util/Standard.hh>
#include "../task/Event.hpp"
#include "../task/EventManager.hpp"
#include "InputEvents.hpp"
#include "InputDevice.hpp"

namespace Sirikata {
namespace Input {

using Task::EventPtr;
using Task::GenEventManager;

bool InputDevice::changeButton(unsigned int button, bool newState, Modifier &modifiers) {
    bool changed;
    if (newState == true) {
        ButtonSet::iterator iter = buttonState.find(button);
        if (iter != buttonState.end()) {
            if ((*iter).second != modifiers) {
                modifiers = (*iter).second;
                changed = true;
            } else {
                changed = false;
            }
        } else {
            changed = true;
        }
        if (changed) {
            // may have different set of modifiers.
            buttonState.insert(ButtonSet::value_type(button,modifiers));
        }
    } else {
        ButtonSet::iterator iter = buttonState.find(button);
        if (iter != buttonState.end()) {
            modifiers = (*iter).second;
            buttonState.erase(iter);
            changed = true;
        } else {
            changed = false;
        }
    }
    return changed;
}
bool InputDevice::fireButton(const InputDevicePtr &thisptr,
                             GenEventManager *em,
                             unsigned int button, bool newStateIsPressed, Modifier modifiers) {
    Modifier oldmodifiers = modifiers;
    bool changed = changeButton(button, newStateIsPressed, oldmodifiers);
    if (changed) {
        if (newStateIsPressed) {
            if (oldmodifiers != modifiers) {
                // If modifiers change, release old
                em->fire(EventPtr(new ButtonReleased(thisptr, button, oldmodifiers)));
            }
            em->fire(EventPtr(new ButtonPressed(thisptr, button, modifiers)));
        } else {
            em->fire(EventPtr(new ButtonReleased(thisptr, button, oldmodifiers)));
        }
    } else {
        if (newStateIsPressed) {
            if (oldmodifiers != modifiers) {
                // If modifiers change, release old and press new
                em->fire(EventPtr(new ButtonReleased(thisptr, button, oldmodifiers)));
                em->fire(EventPtr(new ButtonPressed(thisptr, button, modifiers)));
            }
            else {
                // Otherwise, we're really in repeat mode
                em->fire(EventPtr(new ButtonRepeated(thisptr, button, modifiers)));
            }
        }
        // Otherwise, we're getting repeats when the key is up....
    }
    return changed;
}

bool InputDevice::changeAxis(unsigned int axis, AxisValue newState) {
    newState.clip();
    while (axisState.size() <= axis) {
        axisState.push_back(AxisValue::null());
    }
    bool changed = (axisState[axis] != newState);
    axisState[axis] = newState;
    return changed;
}

bool InputDevice::fireAxis(const InputDevicePtr &thisptr,
                           GenEventManager *em,
                           unsigned int axis, AxisValue newState) {
    newState.clip();
    bool changed = changeAxis(axis, newState);
    changed = changed || (newState != AxisValue::null());
    if (changed) {
        em->fire(EventPtr(new AxisEvent(thisptr, axis, newState)));
    }
    return changed;
}

void PointerDevice::firePointerClick(
        const PointerDevicePtr &thisptr,
        GenEventManager *em,
        float xPixel,
        float yPixel,
        int cursor,
        int button,
        bool state) {
    DragMap::iterator iter = mDragInfo.begin();
    while (iter != mDragInfo.end()) {
        if ((*iter).mButton == button) {
            break;
        }
        ++iter;
    }
    if (iter == mDragInfo.end()) {
        if (!state) return;
        DragInfo di;
        di.mButton = button;
        di.mIsDragging = false;
        di.mDragStartX = xPixel;
        di.mDragStartY = yPixel;
        di.mDragX = xPixel;
        di.mDragY = yPixel;
        di.mOffsetX = 0;
        di.mOffsetY = 0;
        mDragInfo.insert(mDragInfo.begin(), di);
        em->fire(EventPtr(
                new MousePressedEvent(
                    thisptr,
                    xPixel,
                    yPixel,
                    cursor, button)));
    } else {
        if (state) return;
        if ((*iter).mIsDragging) {
            if (mRelativeMode) {
                xPixel = (*iter).mDragX;
                yPixel = (*iter).mDragY;
            }
            em->fire(EventPtr(
                    new MouseDragEvent(
                        thisptr, DRAG_END,
                        (*iter).mDragStartX,
                        (*iter).mDragStartY,
                        xPixel+(*iter).mOffsetX,
                        yPixel+(*iter).mOffsetY,
                        (*iter).mDragX+(*iter).mOffsetX,
                        (*iter).mDragY+(*iter).mOffsetY,
                        cursor, button, 0, 0, 0)));
        } else {
            em->fire(EventPtr(
                    new MouseClickEvent(
                        thisptr,
                        (*iter).mDragStartX,
                        (*iter).mDragStartY,
                        cursor, button)));
            em->fire(EventPtr(
                    new MouseReleasedEvent(
                        thisptr,
                        xPixel,
                        yPixel,
                        cursor, button)));
        }
        mDragInfo.erase(iter);
    }
}

void PointerDevice::firePointerMotion(
        const PointerDevicePtr &thisptr,
        GenEventManager *em,
        float xPixelArg,
        float yPixelArg,
        int cursorType,
        int pressure, int pressmin, int pressmax) {
    if (mDragInfo.empty() && !mRelativeMode) {
        em->fire(EventPtr(new MouseHoverEvent(thisptr, xPixelArg, yPixelArg, cursorType)));
    } else {
        bool first = true;
        for (DragMap::iterator iter = mDragInfo.begin();
             iter != mDragInfo.end(); ++iter) {
            DragInfo &di = (*iter);
            float xPixel = xPixelArg, yPixel = yPixelArg;
            if (mRelativeMode) {
                xPixel = di.mDragX + xPixelArg;
                yPixel = di.mDragY + yPixelArg;
            }
            if (first) {
                MouseDragType dragType = DRAG_DEADBAND;
                if (!di.mIsDragging) {
                    float xdiff = (xPixel - di.mDragStartX);
                    float ydiff = (yPixel - di.mDragStartY);
                    if (xdiff*xdiff + ydiff*ydiff >= mDeadband*mDeadband) {
                        di.mIsDragging = true;
                        dragType = DRAG_START;
                    }
                } else {
                    dragType = DRAG_DRAG;
                }
                em->fire(EventPtr(new MouseDragEvent(
                                      thisptr, dragType,
                                      di.mDragStartX, di.mDragStartY,
                                      xPixel+di.mOffsetX, yPixel+di.mOffsetY,
                                      di.mDragX+di.mOffsetX, di.mDragY+di.mOffsetY,
                                      cursorType, di.mButton,
                                      pressure, pressmin, pressmax)));
                first = false;
                if (mRelativeMode) {
                    di.mOffsetX += xPixelArg;
                    di.mOffsetY += yPixelArg;
                }
            } else if (!mRelativeMode) {
                di.mOffsetX += di.mDragX - xPixel;
                di.mOffsetY += di.mDragY - yPixel;
            }
            di.mDragX = xPixel;
            di.mDragY = yPixel;
        }
    }
}

}
}
<commit_msg>Fix MouseReleasedEvents in Ogre so they fire at the end of drags. MousePressed and MouseReleased should always be fired, only Drags and Clicks are mutually exclusive.<commit_after>/*  Sirikata Input Plugin -- plugins/input
 *  InputDevice.cpp
 *
 *  Copyright (c) 2009, Patrick Reiter Horn
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are
 *  met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  * Neither the name of Sirikata nor the names of its contributors may
 *    be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sirikata/core/util/Standard.hh>
#include "../task/Event.hpp"
#include "../task/EventManager.hpp"
#include "InputEvents.hpp"
#include "InputDevice.hpp"

namespace Sirikata {
namespace Input {

using Task::EventPtr;
using Task::GenEventManager;

bool InputDevice::changeButton(unsigned int button, bool newState, Modifier &modifiers) {
    bool changed;
    if (newState == true) {
        ButtonSet::iterator iter = buttonState.find(button);
        if (iter != buttonState.end()) {
            if ((*iter).second != modifiers) {
                modifiers = (*iter).second;
                changed = true;
            } else {
                changed = false;
            }
        } else {
            changed = true;
        }
        if (changed) {
            // may have different set of modifiers.
            buttonState.insert(ButtonSet::value_type(button,modifiers));
        }
    } else {
        ButtonSet::iterator iter = buttonState.find(button);
        if (iter != buttonState.end()) {
            modifiers = (*iter).second;
            buttonState.erase(iter);
            changed = true;
        } else {
            changed = false;
        }
    }
    return changed;
}
bool InputDevice::fireButton(const InputDevicePtr &thisptr,
                             GenEventManager *em,
                             unsigned int button, bool newStateIsPressed, Modifier modifiers) {
    Modifier oldmodifiers = modifiers;
    bool changed = changeButton(button, newStateIsPressed, oldmodifiers);
    if (changed) {
        if (newStateIsPressed) {
            if (oldmodifiers != modifiers) {
                // If modifiers change, release old
                em->fire(EventPtr(new ButtonReleased(thisptr, button, oldmodifiers)));
            }
            em->fire(EventPtr(new ButtonPressed(thisptr, button, modifiers)));
        } else {
            em->fire(EventPtr(new ButtonReleased(thisptr, button, oldmodifiers)));
        }
    } else {
        if (newStateIsPressed) {
            if (oldmodifiers != modifiers) {
                // If modifiers change, release old and press new
                em->fire(EventPtr(new ButtonReleased(thisptr, button, oldmodifiers)));
                em->fire(EventPtr(new ButtonPressed(thisptr, button, modifiers)));
            }
            else {
                // Otherwise, we're really in repeat mode
                em->fire(EventPtr(new ButtonRepeated(thisptr, button, modifiers)));
            }
        }
        // Otherwise, we're getting repeats when the key is up....
    }
    return changed;
}

bool InputDevice::changeAxis(unsigned int axis, AxisValue newState) {
    newState.clip();
    while (axisState.size() <= axis) {
        axisState.push_back(AxisValue::null());
    }
    bool changed = (axisState[axis] != newState);
    axisState[axis] = newState;
    return changed;
}

bool InputDevice::fireAxis(const InputDevicePtr &thisptr,
                           GenEventManager *em,
                           unsigned int axis, AxisValue newState) {
    newState.clip();
    bool changed = changeAxis(axis, newState);
    changed = changed || (newState != AxisValue::null());
    if (changed) {
        em->fire(EventPtr(new AxisEvent(thisptr, axis, newState)));
    }
    return changed;
}

void PointerDevice::firePointerClick(
        const PointerDevicePtr &thisptr,
        GenEventManager *em,
        float xPixel,
        float yPixel,
        int cursor,
        int button,
        bool state) {
    DragMap::iterator iter = mDragInfo.begin();
    while (iter != mDragInfo.end()) {
        if ((*iter).mButton == button) {
            break;
        }
        ++iter;
    }
    if (iter == mDragInfo.end()) {
        if (!state) return;
        DragInfo di;
        di.mButton = button;
        di.mIsDragging = false;
        di.mDragStartX = xPixel;
        di.mDragStartY = yPixel;
        di.mDragX = xPixel;
        di.mDragY = yPixel;
        di.mOffsetX = 0;
        di.mOffsetY = 0;
        mDragInfo.insert(mDragInfo.begin(), di);
        em->fire(EventPtr(
                new MousePressedEvent(
                    thisptr,
                    xPixel,
                    yPixel,
                    cursor, button)));
    } else {
        if (state) return;
        if ((*iter).mIsDragging) {
            if (mRelativeMode) {
                xPixel = (*iter).mDragX;
                yPixel = (*iter).mDragY;
            }
            em->fire(EventPtr(
                    new MouseDragEvent(
                        thisptr, DRAG_END,
                        (*iter).mDragStartX,
                        (*iter).mDragStartY,
                        xPixel+(*iter).mOffsetX,
                        yPixel+(*iter).mOffsetY,
                        (*iter).mDragX+(*iter).mOffsetX,
                        (*iter).mDragY+(*iter).mOffsetY,
                        cursor, button, 0, 0, 0)));
        } else {
            em->fire(EventPtr(
                    new MouseClickEvent(
                        thisptr,
                        (*iter).mDragStartX,
                        (*iter).mDragStartY,
                        cursor, button)));
        }
        em->fire(EventPtr(
                new MouseReleasedEvent(
                    thisptr,
                    xPixel,
                    yPixel,
                    cursor, button)));
        mDragInfo.erase(iter);
    }
}

void PointerDevice::firePointerMotion(
        const PointerDevicePtr &thisptr,
        GenEventManager *em,
        float xPixelArg,
        float yPixelArg,
        int cursorType,
        int pressure, int pressmin, int pressmax) {
    if (mDragInfo.empty() && !mRelativeMode) {
        em->fire(EventPtr(new MouseHoverEvent(thisptr, xPixelArg, yPixelArg, cursorType)));
    } else {
        bool first = true;
        for (DragMap::iterator iter = mDragInfo.begin();
             iter != mDragInfo.end(); ++iter) {
            DragInfo &di = (*iter);
            float xPixel = xPixelArg, yPixel = yPixelArg;
            if (mRelativeMode) {
                xPixel = di.mDragX + xPixelArg;
                yPixel = di.mDragY + yPixelArg;
            }
            if (first) {
                MouseDragType dragType = DRAG_DEADBAND;
                if (!di.mIsDragging) {
                    float xdiff = (xPixel - di.mDragStartX);
                    float ydiff = (yPixel - di.mDragStartY);
                    if (xdiff*xdiff + ydiff*ydiff >= mDeadband*mDeadband) {
                        di.mIsDragging = true;
                        dragType = DRAG_START;
                    }
                } else {
                    dragType = DRAG_DRAG;
                }
                em->fire(EventPtr(new MouseDragEvent(
                                      thisptr, dragType,
                                      di.mDragStartX, di.mDragStartY,
                                      xPixel+di.mOffsetX, yPixel+di.mOffsetY,
                                      di.mDragX+di.mOffsetX, di.mDragY+di.mOffsetY,
                                      cursorType, di.mButton,
                                      pressure, pressmin, pressmax)));
                first = false;
                if (mRelativeMode) {
                    di.mOffsetX += xPixelArg;
                    di.mOffsetY += yPixelArg;
                }
            } else if (!mRelativeMode) {
                di.mOffsetX += di.mDragX - xPixel;
                di.mOffsetY += di.mDragY - yPixel;
            }
            di.mDragX = xPixel;
            di.mDragY = yPixel;
        }
    }
}

}
}
<|endoftext|>


<commit_before>/*  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.), Nokia Corporation
    (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
<commit_msg>fix compilation with -DQT_STRICT_ITERATORS<commit_after>/*  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.), Nokia Corporation
    (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.constBegin();
        for(;it+1 != list.constEnd();++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
<|endoftext|>