<commit_before>#include "qnodeeditorsocketmodel.h"

#include "graphicsnode.hpp"
#include "graphicsnodescene.hpp"

#include "qreactiveproxymodel.h"

#include <QtCore/QDebug>

#define REACTIVE_MODEL qobject_cast<QReactiveProxyModel*>(d_ptr->q_ptr->sourceModel())

struct NodeWrapper
{
    GraphicsNode* m_pNode;

    // Keep aligned with the source row
    QVector<GraphicsNodeSocket*> m_lSourcesToSrc; //TODO would a QHash make sense?
    QVector<GraphicsNodeSocket*> m_lSinksToSrc; //TODO would a QHash make sense?

    // Keep aligned with the node row
    QVector<GraphicsNodeSocket*> m_lSources;
    QVector<GraphicsNodeSocket*> m_lSinks;
};

class QNodeEditorSocketModelPrivate final : public QObject
{
public:
    explicit QNodeEditorSocketModelPrivate(QObject* p) : QObject(p) {}

    QNodeEditorEdgeModel m_EdgeModel {this};
    QVector<NodeWrapper*> m_lWrappers;
    GraphicsNodeScene* m_pScene;

    // helper
    GraphicsNode* insertNode(int idx, const QString& title);
    NodeWrapper* getNode(const QModelIndex& idx, bool r = false) const;

    void insertSockets(const QModelIndex& parent, int first, int last);
    void updateSockets(const QModelIndex& parent, int first, int last);
    void removeSockets(const QModelIndex& parent, int first, int last);

    QNodeEditorSocketModel* q_ptr;
public Q_SLOTS:
    void slotRowsInserted(const QModelIndex& parent, int first, int last);
};

QNodeEditorSocketModel::QNodeEditorSocketModel( QReactiveProxyModel* rmodel, GraphicsNodeScene* scene ) :
    QIdentityProxyModel(rmodel), d_ptr(new QNodeEditorSocketModelPrivate(this))
{
    Q_ASSERT(rmodel);

    d_ptr->q_ptr    = this;
    d_ptr->m_pScene = scene;
    setSourceModel(rmodel);

    d_ptr->m_EdgeModel.setSourceModel(rmodel->connectionsModel());

    connect(this, &QAbstractItemModel::rowsInserted,
        d_ptr, &QNodeEditorSocketModelPrivate::slotRowsInserted);
}

QNodeEditorSocketModel::~QNodeEditorSocketModel()
{

}

void QNodeEditorSocketModel::setSourceModel(QAbstractItemModel *sm)
{
    // This models can only work with a QReactiveProxyModel (no proxies)
    Q_ASSERT(qobject_cast<QReactiveProxyModel*>(sm));

    QIdentityProxyModel::setSourceModel(sm);

    //TODO clear (this can wait, it wont happen anyway)

    d_ptr->slotRowsInserted({}, 0, sourceModel()->rowCount() -1 );
}

int QNodeEditorSocketModel::sourceSocketCount(const QModelIndex& idx) const
{
    auto nodew = d_ptr->getNode(idx);

    return nodew ? nodew->m_lSources.size() : 0;
}

int QNodeEditorSocketModel::sinkSocketCount(const QModelIndex& idx) const
{
    auto nodew = d_ptr->getNode(idx);

    return nodew ? nodew->m_lSinks.size() : 0;
}

GraphicsNode* QNodeEditorSocketModel::getNode(const QModelIndex& idx, bool recursive)
{
    if ((!idx.isValid()) || idx.model() != this)
        return Q_NULLPTR;

    auto i = (recursive && idx.parent().isValid()) ? idx.parent() : idx;

    auto nodew = d_ptr->getNode(i);

    return nodew ? nodew->m_pNode : Q_NULLPTR;
}

QVector<GraphicsNodeSocket*> QNodeEditorSocketModel::getSourceSockets(const QModelIndex& idx) const
{
    auto nodew = d_ptr->getNode(idx);

    return nodew ? nodew->m_lSources : QVector<GraphicsNodeSocket*>();
}

QVector<GraphicsNodeSocket*> QNodeEditorSocketModel::getSinkSockets(const QModelIndex& idx) const
{
    auto nodew = d_ptr->getNode(idx);

    return nodew ? nodew->m_lSinks : QVector<GraphicsNodeSocket*>();
}

GraphicsNodeSocket* QNodeEditorSocketModel::getSourceSocket(const QModelIndex& idx)
{
    if (!idx.parent().isValid())
        return Q_NULLPTR;

    const auto nodew = d_ptr->getNode(idx, true);

    if (!nodew)
        return Q_NULLPTR;

    return nodew->m_lSourcesToSrc.size() > idx.row() ?
        nodew->m_lSourcesToSrc[idx.row()] : Q_NULLPTR;
}

GraphicsNodeSocket* QNodeEditorSocketModel::getSinkSocket(const QModelIndex& idx)
{
    if (!idx.parent().isValid())
        return Q_NULLPTR;

    const auto nodew = d_ptr->getNode(idx, true);

    if (!nodew)
        return Q_NULLPTR;

    return nodew->m_lSinksToSrc.size() > idx.row() ?
        nodew->m_lSinksToSrc[idx.row()] : Q_NULLPTR;
}

QNodeEditorEdgeModel* QNodeEditorSocketModel::edgeModel() const
{
    return &d_ptr->m_EdgeModel;
}

void QNodeEditorSocketModelPrivate::slotRowsInserted(const QModelIndex& parent, int first, int last)
{
    if (last < first) return;

    if (!parent.isValid()) {
        // create new nodes
        for (int i = first; i <= last; i++) {
            const auto idx = q_ptr->index(i, 0);
            if (idx.isValid()) {
                insertNode(idx.row(), idx.data().toString());
                slotRowsInserted(idx, 0, q_ptr->rowCount(idx));
            }
        }
    }
    else if (!parent.parent().isValid())
        insertSockets(parent, first, last);
}

GraphicsNode* QNodeEditorSocketModelPrivate::insertNode(int idx, const QString& title)
{
    qDebug() << "\n\n\nIN INSERT NODE!!!" << idx;
    auto idx2 = q_ptr->index(idx, 0);

    Q_ASSERT(idx2.isValid());

    if (idx == 0 && m_lWrappers.size())
        Q_ASSERT(false);

    auto n = new GraphicsNode(q_ptr, idx2);
    n->setTitle(title);

    auto nw = new NodeWrapper{
        n, {}, {}, {}, {}
    };

    m_lWrappers.insert(idx, nw);

    m_pScene->addItem(n->graphicsItem());

    return n;
}

NodeWrapper* QNodeEditorSocketModelPrivate::getNode(const QModelIndex& idx, bool r) const
{
    // for convenience
    auto i = idx.model() == q_ptr->sourceModel() ? q_ptr->mapFromSource(idx) : idx;

    if ((!i.isValid()) || i.model() != q_ptr)
        return Q_NULLPTR;

    if (i.parent().isValid() && r)
        i = i.parent();

    if (i.parent().isValid())
        return Q_NULLPTR;

    // This should have been taken care of already. If it isn't, either the
    // source model is buggy (it will cause crashes later anyway) or this
    // code is (and in that case, the state is already corrupted, ignoring that
    // will cause garbage data to be shown/serialized).
    Q_ASSERT(m_lWrappers.size() > idx.row());

    return m_lWrappers[idx.row()];
}

void QNodeEditorSocketModelPrivate::insertSockets(const QModelIndex& parent, int first, int last)
{
    qDebug() << "IN QNodeEditorSocketModelPrivate::insertSockets" << first << last;

    auto nodew = getNode(parent);
    Q_ASSERT(nodew);

    Q_ASSERT(parent.isValid() && (!parent.parent().isValid()) && parent.model() == q_ptr);

    for (int i = 0; i < q_ptr->rowCount(parent); i++) {
        const auto idx = q_ptr->index(i, 0, parent);

        // It doesn't attempt to insert the socket at the correct index as
        // many items will be rejected

        // SOURCES
        if (idx.flags() & Qt::ItemIsDragEnabled) {
            auto s = new GraphicsNodeSocket(
                idx,
                GraphicsNodeSocket::SocketType::SOURCE,
                idx.data().toString(),
                nodew->m_pNode,
                (QObject*) q_ptr->sourceModel(),
                i
            );
            nodew->m_lSourcesToSrc.resize(
                std::max(i+1,nodew->m_lSourcesToSrc.size())
            );
            nodew->m_lSourcesToSrc.insert(i, s);
            nodew->m_lSources << s;

        }

        // SINKS
        if (idx.flags() & (Qt::ItemIsDropEnabled | Qt::ItemIsEditable)) {
            auto s = new GraphicsNodeSocket(
                idx,
                GraphicsNodeSocket::SocketType::SINK,
                idx.data().toString(),
                nodew->m_pNode,
                (QObject*) q_ptr->sourceModel(),
                i
            );
            nodew->m_lSinksToSrc.resize(
                std::max(i+1,nodew->m_lSinksToSrc.size())
            );
            nodew->m_lSinksToSrc.insert(i, s);
            nodew->m_lSinks << s;
        }

        nodew->m_pNode->update();
    }
}

void QNodeEditorSocketModelPrivate::updateSockets(const QModelIndex& parent, int first, int last)
{
    Q_UNUSED(parent)
    Q_UNUSED(first)
    Q_UNUSED(last)
    //TODO
}

void QNodeEditorSocketModelPrivate::removeSockets(const QModelIndex& parent, int first, int last)
{
    Q_UNUSED(parent)
    Q_UNUSED(first)
    Q_UNUSED(last)
    //TODO
}

QNodeEditorEdgeModel::QNodeEditorEdgeModel(QNodeEditorSocketModelPrivate* parent)
    : QIdentityProxyModel(parent), d_ptr(parent)
{

}

QNodeEditorEdgeModel::~QNodeEditorEdgeModel()
{

}

bool QNodeEditorEdgeModel::canConnect(const QModelIndex& idx1, const QModelIndex& idx2) const
{
    return true; //TODO
}

bool QNodeEditorEdgeModel::connectSocket(const QModelIndex& idx1, const QModelIndex& idx2)
{
    if (idx1.model() != d_ptr->q_ptr || idx2.model() != d_ptr->q_ptr)
        return false;

    auto m = REACTIVE_MODEL;

    m->connectIndices(
        d_ptr->q_ptr->mapToSource(idx1),
        d_ptr->q_ptr->mapToSource(idx2)
    );
}

QNodeEditorSocketModel* QNodeEditorEdgeModel::socketModel() const
{
    return d_ptr->q_ptr;
}
<commit_msg>socketproxy: Force the QObjectModel value role to be synched.<commit_after>#include "qnodeeditorsocketmodel.h"

#include "graphicsnode.hpp"
#include "graphicsnodescene.hpp"

#include "qreactiveproxymodel.h"

#include <QtCore/QDebug>

#include "qobjectmodel.h" //TODO remove

#define REACTIVE_MODEL qobject_cast<QReactiveProxyModel*>(d_ptr->q_ptr->sourceModel())

struct NodeWrapper
{
    GraphicsNode* m_pNode;

    // Keep aligned with the source row
    QVector<GraphicsNodeSocket*> m_lSourcesToSrc; //TODO would a QHash make sense?
    QVector<GraphicsNodeSocket*> m_lSinksToSrc; //TODO would a QHash make sense?

    // Keep aligned with the node row
    QVector<GraphicsNodeSocket*> m_lSources;
    QVector<GraphicsNodeSocket*> m_lSinks;
};

class QNodeEditorSocketModelPrivate final : public QObject
{
public:
    explicit QNodeEditorSocketModelPrivate(QObject* p) : QObject(p) {}

    QNodeEditorEdgeModel m_EdgeModel {this};
    QVector<NodeWrapper*> m_lWrappers;
    GraphicsNodeScene* m_pScene;

    // helper
    GraphicsNode* insertNode(int idx, const QString& title);
    NodeWrapper* getNode(const QModelIndex& idx, bool r = false) const;

    void insertSockets(const QModelIndex& parent, int first, int last);
    void updateSockets(const QModelIndex& parent, int first, int last);
    void removeSockets(const QModelIndex& parent, int first, int last);

    QNodeEditorSocketModel* q_ptr;
public Q_SLOTS:
    void slotRowsInserted(const QModelIndex& parent, int first, int last);
};

QNodeEditorSocketModel::QNodeEditorSocketModel( QReactiveProxyModel* rmodel, GraphicsNodeScene* scene ) :
    QIdentityProxyModel(rmodel), d_ptr(new QNodeEditorSocketModelPrivate(this))
{
    Q_ASSERT(rmodel);

    rmodel->addConnectedRole(QObjectModel::Role::ValueRole);

    d_ptr->q_ptr    = this;
    d_ptr->m_pScene = scene;
    setSourceModel(rmodel);

    d_ptr->m_EdgeModel.setSourceModel(rmodel->connectionsModel());

    connect(this, &QAbstractItemModel::rowsInserted,
        d_ptr, &QNodeEditorSocketModelPrivate::slotRowsInserted);
}

QNodeEditorSocketModel::~QNodeEditorSocketModel()
{

}

void QNodeEditorSocketModel::setSourceModel(QAbstractItemModel *sm)
{
    // This models can only work with a QReactiveProxyModel (no proxies)
    Q_ASSERT(qobject_cast<QReactiveProxyModel*>(sm));

    QIdentityProxyModel::setSourceModel(sm);

    //TODO clear (this can wait, it wont happen anyway)

    d_ptr->slotRowsInserted({}, 0, sourceModel()->rowCount() -1 );
}

int QNodeEditorSocketModel::sourceSocketCount(const QModelIndex& idx) const
{
    auto nodew = d_ptr->getNode(idx);

    return nodew ? nodew->m_lSources.size() : 0;
}

int QNodeEditorSocketModel::sinkSocketCount(const QModelIndex& idx) const
{
    auto nodew = d_ptr->getNode(idx);

    return nodew ? nodew->m_lSinks.size() : 0;
}

GraphicsNode* QNodeEditorSocketModel::getNode(const QModelIndex& idx, bool recursive)
{
    if ((!idx.isValid()) || idx.model() != this)
        return Q_NULLPTR;

    auto i = (recursive && idx.parent().isValid()) ? idx.parent() : idx;

    auto nodew = d_ptr->getNode(i);

    return nodew ? nodew->m_pNode : Q_NULLPTR;
}

QVector<GraphicsNodeSocket*> QNodeEditorSocketModel::getSourceSockets(const QModelIndex& idx) const
{
    auto nodew = d_ptr->getNode(idx);

    return nodew ? nodew->m_lSources : QVector<GraphicsNodeSocket*>();
}

QVector<GraphicsNodeSocket*> QNodeEditorSocketModel::getSinkSockets(const QModelIndex& idx) const
{
    auto nodew = d_ptr->getNode(idx);

    return nodew ? nodew->m_lSinks : QVector<GraphicsNodeSocket*>();
}

GraphicsNodeSocket* QNodeEditorSocketModel::getSourceSocket(const QModelIndex& idx)
{
    if (!idx.parent().isValid())
        return Q_NULLPTR;

    const auto nodew = d_ptr->getNode(idx, true);

    if (!nodew)
        return Q_NULLPTR;

    return nodew->m_lSourcesToSrc.size() > idx.row() ?
        nodew->m_lSourcesToSrc[idx.row()] : Q_NULLPTR;
}

GraphicsNodeSocket* QNodeEditorSocketModel::getSinkSocket(const QModelIndex& idx)
{
    if (!idx.parent().isValid())
        return Q_NULLPTR;

    const auto nodew = d_ptr->getNode(idx, true);

    if (!nodew)
        return Q_NULLPTR;

    return nodew->m_lSinksToSrc.size() > idx.row() ?
        nodew->m_lSinksToSrc[idx.row()] : Q_NULLPTR;
}

QNodeEditorEdgeModel* QNodeEditorSocketModel::edgeModel() const
{
    return &d_ptr->m_EdgeModel;
}

void QNodeEditorSocketModelPrivate::slotRowsInserted(const QModelIndex& parent, int first, int last)
{
    if (last < first) return;

    if (!parent.isValid()) {
        // create new nodes
        for (int i = first; i <= last; i++) {
            const auto idx = q_ptr->index(i, 0);
            if (idx.isValid()) {
                insertNode(idx.row(), idx.data().toString());
                slotRowsInserted(idx, 0, q_ptr->rowCount(idx));
            }
        }
    }
    else if (!parent.parent().isValid())
        insertSockets(parent, first, last);
}

GraphicsNode* QNodeEditorSocketModelPrivate::insertNode(int idx, const QString& title)
{
    qDebug() << "\n\n\nIN INSERT NODE!!!" << idx;
    auto idx2 = q_ptr->index(idx, 0);

    Q_ASSERT(idx2.isValid());

    if (idx == 0 && m_lWrappers.size())
        Q_ASSERT(false);

    auto n = new GraphicsNode(q_ptr, idx2);
    n->setTitle(title);

    auto nw = new NodeWrapper{
        n, {}, {}, {}, {}
    };

    m_lWrappers.insert(idx, nw);

    m_pScene->addItem(n->graphicsItem());

    return n;
}

NodeWrapper* QNodeEditorSocketModelPrivate::getNode(const QModelIndex& idx, bool r) const
{
    // for convenience
    auto i = idx.model() == q_ptr->sourceModel() ? q_ptr->mapFromSource(idx) : idx;

    if ((!i.isValid()) || i.model() != q_ptr)
        return Q_NULLPTR;

    if (i.parent().isValid() && r)
        i = i.parent();

    if (i.parent().isValid())
        return Q_NULLPTR;

    // This should have been taken care of already. If it isn't, either the
    // source model is buggy (it will cause crashes later anyway) or this
    // code is (and in that case, the state is already corrupted, ignoring that
    // will cause garbage data to be shown/serialized).
    Q_ASSERT(m_lWrappers.size() > idx.row());

    return m_lWrappers[idx.row()];
}

void QNodeEditorSocketModelPrivate::insertSockets(const QModelIndex& parent, int first, int last)
{
    qDebug() << "IN QNodeEditorSocketModelPrivate::insertSockets" << first << last;

    auto nodew = getNode(parent);
    Q_ASSERT(nodew);

    Q_ASSERT(parent.isValid() && (!parent.parent().isValid()) && parent.model() == q_ptr);

    for (int i = 0; i < q_ptr->rowCount(parent); i++) {
        const auto idx = q_ptr->index(i, 0, parent);

        // It doesn't attempt to insert the socket at the correct index as
        // many items will be rejected

        // SOURCES
        if (idx.flags() & Qt::ItemIsDragEnabled) {
            auto s = new GraphicsNodeSocket(
                idx,
                GraphicsNodeSocket::SocketType::SOURCE,
                idx.data().toString(),
                nodew->m_pNode,
                (QObject*) q_ptr->sourceModel(),
                i
            );
            nodew->m_lSourcesToSrc.resize(
                std::max(i+1,nodew->m_lSourcesToSrc.size())
            );
            nodew->m_lSourcesToSrc.insert(i, s);
            nodew->m_lSources << s;

        }

        // SINKS
        if (idx.flags() & (Qt::ItemIsDropEnabled | Qt::ItemIsEditable)) {
            auto s = new GraphicsNodeSocket(
                idx,
                GraphicsNodeSocket::SocketType::SINK,
                idx.data().toString(),
                nodew->m_pNode,
                (QObject*) q_ptr->sourceModel(),
                i
            );
            nodew->m_lSinksToSrc.resize(
                std::max(i+1,nodew->m_lSinksToSrc.size())
            );
            nodew->m_lSinksToSrc.insert(i, s);
            nodew->m_lSinks << s;
        }

        nodew->m_pNode->update();
    }
}

void QNodeEditorSocketModelPrivate::updateSockets(const QModelIndex& parent, int first, int last)
{
    Q_UNUSED(parent)
    Q_UNUSED(first)
    Q_UNUSED(last)
    //TODO
}

void QNodeEditorSocketModelPrivate::removeSockets(const QModelIndex& parent, int first, int last)
{
    Q_UNUSED(parent)
    Q_UNUSED(first)
    Q_UNUSED(last)
    //TODO
}

QNodeEditorEdgeModel::QNodeEditorEdgeModel(QNodeEditorSocketModelPrivate* parent)
    : QIdentityProxyModel(parent), d_ptr(parent)
{

}

QNodeEditorEdgeModel::~QNodeEditorEdgeModel()
{

}

bool QNodeEditorEdgeModel::canConnect(const QModelIndex& idx1, const QModelIndex& idx2) const
{
    return true; //TODO
}

bool QNodeEditorEdgeModel::connectSocket(const QModelIndex& idx1, const QModelIndex& idx2)
{
    if (idx1.model() != d_ptr->q_ptr || idx2.model() != d_ptr->q_ptr)
        return false;

    auto m = REACTIVE_MODEL;

    m->connectIndices(
        d_ptr->q_ptr->mapToSource(idx1),
        d_ptr->q_ptr->mapToSource(idx2)
    );
}

QNodeEditorSocketModel* QNodeEditorEdgeModel::socketModel() const
{
    return d_ptr->q_ptr;
}
<|endoftext|>


<commit_before>// -*- Mode:C++ -*-

/**************************************************************************************************/
/*                                                                                */
/* Copyright (C) 2014 University of Hull                                                          */
/*                                                                                */
/**************************************************************************************************/
/*                                                                                */
/*  module     :  field/connection/manager.hpp                                                    */
/*  project    :                                                                                */
/*  description:                                                                                */
/*                                                                                */
/**************************************************************************************************/

#if !defined(UKACHULLDCS_08961_FIELD_CONNECTION_MANAGER_HPP)

#define UKACHULLDCS_08961_FIELD_CONNECTION_MANAGER_HPP

// includes, system

#include <boost/bimap/bimap.hpp>                 // boost::bimaps::*
#include <boost/bimap/unordered_multiset_of.hpp> // boost::bimaps::unordered_multiset_of<>
#include <boost/utility/mutexed_singleton.hpp>   // boost::mutexed_singleton<>

// includes, project

#include <field/connection/update.hpp>
#include <support/printable.hpp>

namespace field {

  class base;

  namespace connection {

    class base;

    // types, exported (class, enum, struct, union, typedef)

    class manager : public support::printable,
                    public boost::mutexed_singleton<manager> {

      BOOST_SINGLETON_PLACEMENT_DECLARATION;

    public:

      typedef std::function<void ()> update_function_type;

      bool connect   (::field::base* const /* src */, ::field::base* const /* dst */,
                      update_function_type);
      bool disconnect(::field::base* const /* src/dst */);
      bool update    (::field::base* const /* src */);

      std::string status() const;

      virtual void print_on(std::ostream&) const;

    private:

      template <class, int, typename> friend class boost::mutexed_singleton;

      struct dst {};
      struct src {};
      struct upd {};

      typedef boost::bimaps::unordered_multiset_of<boost::bimaps::tagged<::field::base*, src>>
        connection_src_type;
      typedef boost::bimaps::unordered_multiset_of<boost::bimaps::tagged<::field::base*, dst>>
        connection_dst_type;
      typedef boost::bimaps::with_info<boost::bimaps::tagged<update_function_type, upd>>
        connection_upd_type;
      typedef boost::bimaps::unordered_multiset_of_relation<> connection_rel_type;
      typedef boost::bimaps::bimap<connection_src_type,
                                   connection_dst_type,
                                   connection_rel_type,
                                   connection_upd_type>
        connection_map_type;

      connection_map_type connection_map_;

      explicit manager(boost::restricted);

      template <typename, typename>
      void print_helper(::field::base*, std::ostream&) const;

    };

    // variables, exported (extern)

    // functions, inlined (inline)

    // functions, exported (extern)

  } // namespace connection {

  // functions, inlined (inline)

  template <typename T1, typename T2 = T1>
  bool connect   (T1* const /* src */, T2* const /* dst */,
                  std::function<void (T1* const, T2* const)> /* upd */ =
                  connection::update::assign<T1,T2>);

  template <typename T>
  bool disconnect(T* const /* src/dst */);

} // namespace field {

#include <field/connection/manager.inl>

#endif // #if !defined(UKACHULLDCS_08961_FIELD_CONNECTION_MANAGER_HPP)
<commit_msg>added: some documentation<commit_after>// -*- Mode:C++ -*-

/**************************************************************************************************/
/*                                                                                */
/* Copyright (C) 2014 University of Hull                                                          */
/*                                                                                */
/**************************************************************************************************/
/*                                                                                */
/*  module     :  field/connection/manager.hpp                                                    */
/*  project    :                                                                                */
/*  description:                                                                                */
/*                                                                                */
/**************************************************************************************************/

#if !defined(UKACHULLDCS_08961_FIELD_CONNECTION_MANAGER_HPP)

#define UKACHULLDCS_08961_FIELD_CONNECTION_MANAGER_HPP

// includes, system

#include <boost/bimap/bimap.hpp>                 // boost::bimaps::*
#include <boost/bimap/unordered_multiset_of.hpp> // boost::bimaps::unordered_multiset_of<>
#include <boost/utility/mutexed_singleton.hpp>   // boost::mutexed_singleton<>

// includes, project

#include <field/connection/update.hpp>
#include <support/printable.hpp>

namespace field {

  class base;

  namespace connection {

    class base;

    // types, exported (class, enum, struct, union, typedef)

    class manager : public support::printable,
                    public boost::mutexed_singleton<manager> {

      BOOST_SINGLETON_PLACEMENT_DECLARATION;

    public:

      typedef std::function<void ()> update_function_type;

      bool connect   (::field::base* const /* src */, ::field::base* const /* dst */,
                      update_function_type);
      bool disconnect(::field::base* const /* src/dst */);
      bool update    (::field::base* const /* src */);

      std::string status() const;

      virtual void print_on(std::ostream&) const;

    private:

      template <class, int, typename> friend class boost::mutexed_singleton;

      struct dst {};
      struct src {};
      struct upd {};

      typedef boost::bimaps::unordered_multiset_of<boost::bimaps::tagged<::field::base*, src>>
        connection_src_type;
      typedef boost::bimaps::unordered_multiset_of<boost::bimaps::tagged<::field::base*, dst>>
        connection_dst_type;
      typedef boost::bimaps::with_info<boost::bimaps::tagged<update_function_type, upd>>
        connection_upd_type;
      typedef boost::bimaps::unordered_multiset_of_relation<> connection_rel_type;
      typedef boost::bimaps::bimap<connection_src_type,
                                   connection_dst_type,
                                   connection_rel_type,
                                   connection_upd_type>
        connection_map_type;

      connection_map_type connection_map_;

      explicit manager(boost::restricted);

      template <typename, typename>
      void print_helper(::field::base*, std::ostream&) const;

    };

    // variables, exported (extern)

    // functions, inlined (inline)

    // functions, exported (extern)

  } // namespace connection {

  // functions, inlined (inline)

  /**
   * \brief establishes a connection between a source and destination field
   *
   * ...
   *
   * \param  source field
   * \param  destination field
   * \param  update operator (dflt: assign)
   * \return true if connection established
   *
   * \thow   nothing
   */
  template <typename T1, typename T2 = T1>
  bool connect   (T1* const /* src */, T2* const /* dst */,
                  std::function<void (T1* const, T2* const)> /* upd */ =
                  connection::update::assign<T1,T2>);

  /**
   * \brief breaks the connection of either a source or a destination field
   *
   * ...
   *
   * \param  source or destination field
   * \return true if disconnected
   *
   * \thow   nothing
   */
  template <typename T>
  bool disconnect(T* const /* src/dst */);

} // namespace field {

#include <field/connection/manager.inl>

#endif // #if !defined(UKACHULLDCS_08961_FIELD_CONNECTION_MANAGER_HPP)
<|endoftext|>


<commit_before>// Copyright (c) 2011-2014 The Bitcoin developers
// Copyright (c) 2014-2015 The Dash developers
// Copyright (c) 2015-2018 The PIVX developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "askpassphrasedialog.h"
#include "ui_askpassphrasedialog.h"
#include <QGraphicsDropShadowEffect>

#include "guiconstants.h"
#include "guiutil.h"
#include "walletmodel.h"
#include "qt/pivx/qtutils.h"
#include "qt/pivx/loadingdialog.h"
#include "qt/pivx/PIVXGUI.h"
#include <QDebug>

#include <QKeyEvent>
#include <QMessageBox>
#include <QPushButton>
#include <QWidget>

AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget* parent, WalletModel* model, Context context) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint),
                                                                                ui(new Ui::AskPassphraseDialog),
                                                                                mode(mode),
                                                                                model(model),
                                                                                context(context),
                                                                                btnWatch(new QCheckBox()),
                                                                                fCapsLock(false)
{
    ui->setupUi(this);
    this->setStyleSheet(GUIUtil::loadStyleSheet());

    ui->left->setProperty("cssClass", "container-dialog");

    ui->labelTitle->setText("Change passphrase");
    ui->labelTitle->setProperty("cssClass", "text-title-screen");

    ui->warningLabel->setProperty("cssClass", "text-subtitle");

    ui->btnEsc->setText("");
    ui->btnEsc->setProperty("cssClass", "ic-close");

    ui->pushButtonOk->setText("OK");
    ui->pushButtonOk->setProperty("cssClass", "btn-primary");

    initCssEditLine(ui->passEdit1);
    initCssEditLine(ui->passEdit2);
    initCssEditLine(ui->passEdit3);

    ui->passLabel1->setText("Current passphrase");
    ui->passLabel1->setProperty("cssClass", "text-title");

    ui->passLabel2->setText("New passphrase");
    ui->passLabel2->setProperty("cssClass", "text-title");

    ui->passLabel3->setText("Repeat passphrase");
    ui->passLabel3->setProperty("cssClass", "text-title");

    ui->capsLabel->setVisible(false);

    ui->passEdit1->setMinimumSize(ui->passEdit1->sizeHint());
    ui->passEdit2->setMinimumSize(ui->passEdit2->sizeHint());
    ui->passEdit3->setMinimumSize(ui->passEdit3->sizeHint());

    ui->passEdit1->setMaxLength(MAX_PASSPHRASE_SIZE);
    ui->passEdit2->setMaxLength(MAX_PASSPHRASE_SIZE);
    ui->passEdit3->setMaxLength(MAX_PASSPHRASE_SIZE);

    setShadow(ui->layoutEdit);
    setShadow(ui->layoutEdit2);

    // Setup Caps Lock detection.
    ui->passEdit1->installEventFilter(this);
    ui->passEdit2->installEventFilter(this);
    ui->passEdit3->installEventFilter(this);

    this->model = model;

    QString title;
    switch (mode) {
    case Mode::Encrypt: // Ask passphrase x2
        ui->warningLabel->setText(tr("Enter the new passphrase to the wallet.
Please use a passphrase of ten or more random characters, or eight or more words."));
        ui->passLabel1->hide();
        ui->passEdit1->hide();
        ui->layoutEdit->hide();
        title = tr("Encrypt wallet");
        initWatch(ui->layoutEdit2);
        break;
    case Mode::UnlockAnonymize:
        ui->warningLabel->setText(tr("This operation needs your wallet passphrase to unlock the wallet."));
        ui->passLabel2->hide();
        ui->passEdit2->hide();
        ui->layoutEdit2->hide();
        ui->passLabel3->hide();
        ui->passEdit3->hide();
        title = tr("Unlock wallet\nfor staking");
        initWatch(ui->layoutEdit);
        break;
    case Mode::Unlock: // Ask passphrase
        ui->warningLabel->setText(tr("This operation needs your wallet passphrase to unlock the wallet."));
        ui->passLabel2->hide();
        ui->passEdit2->hide();
        ui->layoutEdit2->hide();
        ui->passLabel3->hide();
        ui->passEdit3->hide();
        title = tr("Unlock wallet");
        initWatch(ui->layoutEdit);
        break;
    case Mode::Decrypt: // Ask passphrase
        ui->warningLabel->setText(tr("This operation needs your wallet passphrase to decrypt the wallet."));
        ui->passLabel2->hide();
        ui->passEdit2->hide();
        ui->layoutEdit2->hide();
        ui->passLabel3->hide();
        ui->passEdit3->hide();
        title = tr("Decrypt wallet");
        initWatch(ui->layoutEdit);
        break;
    case Mode::ChangePass: // Ask old passphrase + new passphrase x2
        title = tr("Change passphrase");
        ui->warningLabel->setText(tr("Enter the old and new passphrase to the wallet."));
        initWatch(ui->layoutEdit);
        break;
    }

    ui->labelTitle->setText(title);

    textChanged();
    connect(btnWatch, SIGNAL(clicked()), this, SLOT(onWatchClicked()));
    connect(ui->passEdit1, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
    connect(ui->passEdit2, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
    connect(ui->passEdit3, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
    connect(ui->pushButtonOk, SIGNAL(clicked()), this, SLOT(accept()));
    connect(ui->btnEsc, SIGNAL(clicked()), this, SLOT(close()));
}

void AskPassphraseDialog::onWatchClicked(){
    int state = btnWatch->checkState();
    ui->passEdit3->setEchoMode(state == Qt::Checked ? QLineEdit::Normal : QLineEdit::Password );
    ui->passEdit2->setEchoMode(state== Qt::Checked ? QLineEdit::Normal : QLineEdit::Password );
    ui->passEdit1->setEchoMode(state == Qt::Checked ? QLineEdit::Normal : QLineEdit::Password );
}

AskPassphraseDialog::~AskPassphraseDialog()
{
    // Attempt to overwrite text so that they do not linger around in memory
    ui->passEdit1->setText(QString(" ").repeated(ui->passEdit1->text().size()));
    ui->passEdit2->setText(QString(" ").repeated(ui->passEdit2->text().size()));
    ui->passEdit3->setText(QString(" ").repeated(ui->passEdit3->text().size()));
    delete ui;
}

void AskPassphraseDialog::accept()
{
    SecureString oldpass, newpass1, newpass2;
    if (!model)
        return;
    oldpass.reserve(MAX_PASSPHRASE_SIZE);
    newpass1.reserve(MAX_PASSPHRASE_SIZE);
    newpass2.reserve(MAX_PASSPHRASE_SIZE);
    // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
    // Alternately, find a way to make this input mlock()'d to begin with.
    oldpass.assign(ui->passEdit1->text().toStdString().c_str());
    newpass1.assign(ui->passEdit2->text().toStdString().c_str());
    newpass2.assign(ui->passEdit3->text().toStdString().c_str());

    switch (mode) {
    case Mode::Encrypt: {
        if (newpass1.empty() || newpass2.empty()) {
            // Cannot encrypt with empty passphrase
            break;
        }
        QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm wallet encryption"),
            tr("Warning: If you encrypt your wallet and lose your passphrase, you will LOSE ALL OF YOUR PIV!") + "

" + tr("Are you sure you wish to encrypt your wallet?"),
            QMessageBox::Yes | QMessageBox::Cancel,
            QMessageBox::Cancel);
        if (retval == QMessageBox::Yes) {
            if (newpass1 == newpass2) {
                newpassCache = newpass1;
                PIVXGUI* window = static_cast<PIVXGUI*>(parentWidget());
                LoadingDialog *dialog = new LoadingDialog(window);
                dialog->execute(this, 1);
                openDialogWithOpaqueBackgroundFullScreen(dialog, window);
            } else {
                QMessageBox::critical(this, tr("Wallet encryption failed"),
                    tr("The supplied passphrases do not match."));
            }
        } else {
            QDialog::reject(); // Cancelled
        }
    } break;
    case Mode::UnlockAnonymize:
        if (!model->setWalletLocked(false, oldpass, true)) {
            QMessageBox::critical(this, tr("Wallet unlock failed"),
                                  tr("The passphrase entered for the wallet decryption was incorrect."));
        } else {
            QDialog::accept(); // Success
        }
        break;
    case Mode::Unlock:
        if (!model->setWalletLocked(false, oldpass, false)) {
            QMessageBox::critical(this, tr("Wallet unlock failed"),
                tr("The passphrase entered for the wallet decryption was incorrect."));
        } else {
            QDialog::accept(); // Success
        }
        break;
    case Mode::Decrypt:
        if (!model->setWalletEncrypted(false, oldpass)) {
            QMessageBox::critical(this, tr("Wallet decryption failed"),
                tr("The passphrase entered for the wallet decryption was incorrect."));
        } else {
            QDialog::accept(); // Success
        }
        break;
    case Mode::ChangePass:
        if (newpass1 == newpass2) {
            if (model->changePassphrase(oldpass, newpass1)) {
                QMessageBox::information(this, tr("Wallet encrypted"),
                    tr("Wallet passphrase was successfully changed."));
                QDialog::accept(); // Success
            } else {
                QMessageBox::critical(this, tr("Wallet encryption failed"),
                    tr("The passphrase entered for the wallet decryption was incorrect."));
            }
        } else {
            QMessageBox::critical(this, tr("Wallet encryption failed"),
                tr("The supplied passphrases do not match."));
        }
        break;
    }
}

void AskPassphraseDialog::textChanged()
{
    // Validate input, set Ok button to enabled when acceptable
    bool acceptable = false;
    switch (mode) {
    case Mode::Encrypt: // New passphrase x2
        acceptable = !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty();
        break;
    case Mode::UnlockAnonymize: // Old passphrase x1
    case Mode::Unlock:          // Old passphrase x1
    case Mode::Decrypt:
        acceptable = !ui->passEdit1->text().isEmpty();
        break;
    case Mode::ChangePass: // Old passphrase x1, new passphrase x2
        acceptable = !ui->passEdit1->text().isEmpty() && !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty();
        break;
    }
    ui->pushButtonOk->setEnabled(acceptable);
}

bool AskPassphraseDialog::event(QEvent* event)
{
    // Detect Caps Lock key press.
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent* ke = static_cast<QKeyEvent*>(event);
        if (ke->key() == Qt::Key_CapsLock) {
            fCapsLock = !fCapsLock;
        }
        if (fCapsLock) {
            ui->capsLabel->setText(tr("Warning: The Caps Lock key is on!"));
            ui->capsLabel->setVisible(true);
        } else {
            ui->capsLabel->clear();
            ui->capsLabel->setVisible(false);
        }
    }
    return QWidget::event(event);
}

bool AskPassphraseDialog::eventFilter(QObject* object, QEvent* event)
{
    /* Detect Caps Lock.
     * There is no good OS-independent way to check a key state in Qt, but we
     * can detect Caps Lock by checking for the following condition:
     * Shift key is down and the result is a lower case character, or
     * Shift key is not down and the result is an upper case character.
     */
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent* ke = static_cast<QKeyEvent*>(event);
        QString str = ke->text();
        if (str.length() != 0) {
            const QChar* psz = str.unicode();
            bool fShift = (ke->modifiers() & Qt::ShiftModifier) != 0;
            if ((fShift && *psz >= 'a' && *psz <= 'z') || (!fShift && *psz >= 'A' && *psz <= 'Z')) {
                fCapsLock = true;
                ui->capsLabel->setText(tr("Warning: The Caps Lock key is on!"));
                ui->capsLabel->setVisible(true);
            } else if (psz->isLetter()) {
                fCapsLock = false;
                ui->capsLabel->clear();
                ui->capsLabel->setVisible(false);
            }
        }
    }
    return QDialog::eventFilter(object, event);
}

void AskPassphraseDialog::warningMessage() {
    QMessageBox::warning(this, tr("Wallet encrypted"),
                         "<qt>" +
                         tr("PIVX will close now to finish the encryption process. "
                            "Remember that encrypting your wallet cannot fully protect "
                            "your PIVs from being stolen by malware infecting your computer.") +
                         "

" +
                         tr("IMPORTANT: Any previous backups you have made of your wallet file "
                            "should be replaced with the newly generated, encrypted wallet file. "
                            "For security reasons, previous backups of the unencrypted wallet file "
                            "will become useless as soon as you start using the new, encrypted wallet.") +
                         "</qt>");
    QApplication::quit();
}

void AskPassphraseDialog::errorEncryptingWallet() {
    QMessageBox::critical(this, tr("Wallet encryption failed"),
                          tr("Wallet encryption failed due to an internal error. Your wallet was not encrypted."));
}

void AskPassphraseDialog::run(int type){
    if (type == 1) {
        if (!newpassCache.empty()) {
            if (model->setWalletEncrypted(true, newpassCache)) {
                QMetaObject::invokeMethod(this, "warningMessage", Qt::QueuedConnection);
            } else {
                QMetaObject::invokeMethod(this, "errorEncryptingWallet", Qt::QueuedConnection);
            }
            newpassCache.clear();
            QDialog::accept(); // Success
        }
    }
}
void AskPassphraseDialog::onError(int type, QString error){
    newpassCache = "";
}

void AskPassphraseDialog::initWatch(QWidget *parent) {
    btnWatch = new QCheckBox(parent);
    btnWatch->setProperty("cssClass", "btn-watch-password");
    btnWatch->setChecked(false);
    QSize BUTTON_CONTACT_SIZE = QSize(24, 24);
    btnWatch->setMinimumSize(BUTTON_CONTACT_SIZE);
    btnWatch->setMaximumSize(BUTTON_CONTACT_SIZE);
    btnWatch->show();
    btnWatch->raise();

    int posXX = ui->layoutEdit->width() - 30;
    int posYY = 8;
    btnWatch->move(450, posYY);
}<commit_msg>[GUI] hide dialog on loading action.<commit_after>// Copyright (c) 2011-2014 The Bitcoin developers
// Copyright (c) 2014-2015 The Dash developers
// Copyright (c) 2015-2018 The PIVX developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "askpassphrasedialog.h"
#include "ui_askpassphrasedialog.h"
#include <QGraphicsDropShadowEffect>

#include "guiconstants.h"
#include "guiutil.h"
#include "walletmodel.h"
#include "qt/pivx/qtutils.h"
#include "qt/pivx/loadingdialog.h"
#include "qt/pivx/PIVXGUI.h"
#include <QDebug>

#include <QKeyEvent>
#include <QMessageBox>
#include <QPushButton>
#include <QWidget>

AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget* parent, WalletModel* model, Context context) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint),
                                                                                ui(new Ui::AskPassphraseDialog),
                                                                                mode(mode),
                                                                                model(model),
                                                                                context(context),
                                                                                btnWatch(new QCheckBox()),
                                                                                fCapsLock(false)
{
    ui->setupUi(this);
    this->setStyleSheet(GUIUtil::loadStyleSheet());

    ui->left->setProperty("cssClass", "container-dialog");

    ui->labelTitle->setText("Change passphrase");
    ui->labelTitle->setProperty("cssClass", "text-title-screen");

    ui->warningLabel->setProperty("cssClass", "text-subtitle");

    ui->btnEsc->setText("");
    ui->btnEsc->setProperty("cssClass", "ic-close");

    ui->pushButtonOk->setText("OK");
    ui->pushButtonOk->setProperty("cssClass", "btn-primary");

    initCssEditLine(ui->passEdit1);
    initCssEditLine(ui->passEdit2);
    initCssEditLine(ui->passEdit3);

    ui->passLabel1->setText("Current passphrase");
    ui->passLabel1->setProperty("cssClass", "text-title");

    ui->passLabel2->setText("New passphrase");
    ui->passLabel2->setProperty("cssClass", "text-title");

    ui->passLabel3->setText("Repeat passphrase");
    ui->passLabel3->setProperty("cssClass", "text-title");

    ui->capsLabel->setVisible(false);

    ui->passEdit1->setMinimumSize(ui->passEdit1->sizeHint());
    ui->passEdit2->setMinimumSize(ui->passEdit2->sizeHint());
    ui->passEdit3->setMinimumSize(ui->passEdit3->sizeHint());

    ui->passEdit1->setMaxLength(MAX_PASSPHRASE_SIZE);
    ui->passEdit2->setMaxLength(MAX_PASSPHRASE_SIZE);
    ui->passEdit3->setMaxLength(MAX_PASSPHRASE_SIZE);

    setShadow(ui->layoutEdit);
    setShadow(ui->layoutEdit2);

    // Setup Caps Lock detection.
    ui->passEdit1->installEventFilter(this);
    ui->passEdit2->installEventFilter(this);
    ui->passEdit3->installEventFilter(this);

    this->model = model;

    QString title;
    switch (mode) {
    case Mode::Encrypt: // Ask passphrase x2
        ui->warningLabel->setText(tr("Enter the new passphrase to the wallet.
Please use a passphrase of ten or more random characters, or eight or more words."));
        ui->passLabel1->hide();
        ui->passEdit1->hide();
        ui->layoutEdit->hide();
        title = tr("Encrypt wallet");
        initWatch(ui->layoutEdit2);
        break;
    case Mode::UnlockAnonymize:
        ui->warningLabel->setText(tr("This operation needs your wallet passphrase to unlock the wallet."));
        ui->passLabel2->hide();
        ui->passEdit2->hide();
        ui->layoutEdit2->hide();
        ui->passLabel3->hide();
        ui->passEdit3->hide();
        title = tr("Unlock wallet\nfor staking");
        initWatch(ui->layoutEdit);
        break;
    case Mode::Unlock: // Ask passphrase
        ui->warningLabel->setText(tr("This operation needs your wallet passphrase to unlock the wallet."));
        ui->passLabel2->hide();
        ui->passEdit2->hide();
        ui->layoutEdit2->hide();
        ui->passLabel3->hide();
        ui->passEdit3->hide();
        title = tr("Unlock wallet");
        initWatch(ui->layoutEdit);
        break;
    case Mode::Decrypt: // Ask passphrase
        ui->warningLabel->setText(tr("This operation needs your wallet passphrase to decrypt the wallet."));
        ui->passLabel2->hide();
        ui->passEdit2->hide();
        ui->layoutEdit2->hide();
        ui->passLabel3->hide();
        ui->passEdit3->hide();
        title = tr("Decrypt wallet");
        initWatch(ui->layoutEdit);
        break;
    case Mode::ChangePass: // Ask old passphrase + new passphrase x2
        title = tr("Change passphrase");
        ui->warningLabel->setText(tr("Enter the old and new passphrase to the wallet."));
        initWatch(ui->layoutEdit);
        break;
    }

    ui->labelTitle->setText(title);

    textChanged();
    connect(btnWatch, SIGNAL(clicked()), this, SLOT(onWatchClicked()));
    connect(ui->passEdit1, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
    connect(ui->passEdit2, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
    connect(ui->passEdit3, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
    connect(ui->pushButtonOk, SIGNAL(clicked()), this, SLOT(accept()));
    connect(ui->btnEsc, SIGNAL(clicked()), this, SLOT(close()));
}

void AskPassphraseDialog::onWatchClicked(){
    int state = btnWatch->checkState();
    ui->passEdit3->setEchoMode(state == Qt::Checked ? QLineEdit::Normal : QLineEdit::Password );
    ui->passEdit2->setEchoMode(state== Qt::Checked ? QLineEdit::Normal : QLineEdit::Password );
    ui->passEdit1->setEchoMode(state == Qt::Checked ? QLineEdit::Normal : QLineEdit::Password );
}

AskPassphraseDialog::~AskPassphraseDialog()
{
    // Attempt to overwrite text so that they do not linger around in memory
    ui->passEdit1->setText(QString(" ").repeated(ui->passEdit1->text().size()));
    ui->passEdit2->setText(QString(" ").repeated(ui->passEdit2->text().size()));
    ui->passEdit3->setText(QString(" ").repeated(ui->passEdit3->text().size()));
    delete ui;
}

void AskPassphraseDialog::accept()
{
    SecureString oldpass, newpass1, newpass2;
    if (!model)
        return;
    oldpass.reserve(MAX_PASSPHRASE_SIZE);
    newpass1.reserve(MAX_PASSPHRASE_SIZE);
    newpass2.reserve(MAX_PASSPHRASE_SIZE);
    // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
    // Alternately, find a way to make this input mlock()'d to begin with.
    oldpass.assign(ui->passEdit1->text().toStdString().c_str());
    newpass1.assign(ui->passEdit2->text().toStdString().c_str());
    newpass2.assign(ui->passEdit3->text().toStdString().c_str());

    switch (mode) {
    case Mode::Encrypt: {
        if (newpass1.empty() || newpass2.empty()) {
            // Cannot encrypt with empty passphrase
            break;
        }
        QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm wallet encryption"),
            tr("Warning: If you encrypt your wallet and lose your passphrase, you will LOSE ALL OF YOUR PIV!") + "

" + tr("Are you sure you wish to encrypt your wallet?"),
            QMessageBox::Yes | QMessageBox::Cancel,
            QMessageBox::Cancel);
        if (retval == QMessageBox::Yes) {
            if (newpass1 == newpass2) {
                newpassCache = newpass1;
                PIVXGUI* window = static_cast<PIVXGUI*>(parentWidget());
                LoadingDialog *dialog = new LoadingDialog(window);
                dialog->execute(this, 1);
                openDialogWithOpaqueBackgroundFullScreen(dialog, window);
            } else {
                QMessageBox::critical(this, tr("Wallet encryption failed"),
                    tr("The supplied passphrases do not match."));
            }
        } else {
            QDialog::reject(); // Cancelled
        }
    } break;
    case Mode::UnlockAnonymize:
        if (!model->setWalletLocked(false, oldpass, true)) {
            QMessageBox::critical(this, tr("Wallet unlock failed"),
                                  tr("The passphrase entered for the wallet decryption was incorrect."));
        } else {
            QDialog::accept(); // Success
        }
        break;
    case Mode::Unlock:
        if (!model->setWalletLocked(false, oldpass, false)) {
            QMessageBox::critical(this, tr("Wallet unlock failed"),
                tr("The passphrase entered for the wallet decryption was incorrect."));
        } else {
            QDialog::accept(); // Success
        }
        break;
    case Mode::Decrypt:
        if (!model->setWalletEncrypted(false, oldpass)) {
            QMessageBox::critical(this, tr("Wallet decryption failed"),
                tr("The passphrase entered for the wallet decryption was incorrect."));
        } else {
            QDialog::accept(); // Success
        }
        break;
    case Mode::ChangePass:
        if (newpass1 == newpass2) {
            if (model->changePassphrase(oldpass, newpass1)) {
                QMessageBox::information(this, tr("Wallet encrypted"),
                    tr("Wallet passphrase was successfully changed."));
                QDialog::accept(); // Success
            } else {
                QMessageBox::critical(this, tr("Wallet encryption failed"),
                    tr("The passphrase entered for the wallet decryption was incorrect."));
            }
        } else {
            QMessageBox::critical(this, tr("Wallet encryption failed"),
                tr("The supplied passphrases do not match."));
        }
        break;
    }
}

void AskPassphraseDialog::textChanged()
{
    // Validate input, set Ok button to enabled when acceptable
    bool acceptable = false;
    switch (mode) {
    case Mode::Encrypt: // New passphrase x2
        acceptable = !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty();
        break;
    case Mode::UnlockAnonymize: // Old passphrase x1
    case Mode::Unlock:          // Old passphrase x1
    case Mode::Decrypt:
        acceptable = !ui->passEdit1->text().isEmpty();
        break;
    case Mode::ChangePass: // Old passphrase x1, new passphrase x2
        acceptable = !ui->passEdit1->text().isEmpty() && !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty();
        break;
    }
    ui->pushButtonOk->setEnabled(acceptable);
}

bool AskPassphraseDialog::event(QEvent* event)
{
    // Detect Caps Lock key press.
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent* ke = static_cast<QKeyEvent*>(event);
        if (ke->key() == Qt::Key_CapsLock) {
            fCapsLock = !fCapsLock;
        }
        if (fCapsLock) {
            ui->capsLabel->setText(tr("Warning: The Caps Lock key is on!"));
            ui->capsLabel->setVisible(true);
        } else {
            ui->capsLabel->clear();
            ui->capsLabel->setVisible(false);
        }
    }
    return QWidget::event(event);
}

bool AskPassphraseDialog::eventFilter(QObject* object, QEvent* event)
{
    /* Detect Caps Lock.
     * There is no good OS-independent way to check a key state in Qt, but we
     * can detect Caps Lock by checking for the following condition:
     * Shift key is down and the result is a lower case character, or
     * Shift key is not down and the result is an upper case character.
     */
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent* ke = static_cast<QKeyEvent*>(event);
        QString str = ke->text();
        if (str.length() != 0) {
            const QChar* psz = str.unicode();
            bool fShift = (ke->modifiers() & Qt::ShiftModifier) != 0;
            if ((fShift && *psz >= 'a' && *psz <= 'z') || (!fShift && *psz >= 'A' && *psz <= 'Z')) {
                fCapsLock = true;
                ui->capsLabel->setText(tr("Warning: The Caps Lock key is on!"));
                ui->capsLabel->setVisible(true);
            } else if (psz->isLetter()) {
                fCapsLock = false;
                ui->capsLabel->clear();
                ui->capsLabel->setVisible(false);
            }
        }
    }
    return QDialog::eventFilter(object, event);
}

void AskPassphraseDialog::warningMessage() {
    QMessageBox::warning(this, tr("Wallet encrypted"),
                         "<qt>" +
                         tr("PIVX will close now to finish the encryption process. "
                            "Remember that encrypting your wallet cannot fully protect "
                            "your PIVs from being stolen by malware infecting your computer.") +
                         "

" +
                         tr("IMPORTANT: Any previous backups you have made of your wallet file "
                            "should be replaced with the newly generated, encrypted wallet file. "
                            "For security reasons, previous backups of the unencrypted wallet file "
                            "will become useless as soon as you start using the new, encrypted wallet.") +
                         "</qt>");
    QApplication::quit();
}

void AskPassphraseDialog::errorEncryptingWallet() {
    QMessageBox::critical(this, tr("Wallet encryption failed"),
                          tr("Wallet encryption failed due to an internal error. Your wallet was not encrypted."));
}

void AskPassphraseDialog::run(int type){
    if (type == 1) {
        if (!newpassCache.empty()) {
            QMetaObject::invokeMethod(this, "hide", Qt::QueuedConnection);
            if (model->setWalletEncrypted(true, newpassCache)) {
                QMetaObject::invokeMethod(this, "warningMessage", Qt::QueuedConnection);
            } else {
                QMetaObject::invokeMethod(this, "errorEncryptingWallet", Qt::QueuedConnection);
            }
            newpassCache.clear();
            QDialog::accept(); // Success
        }
    }
}
void AskPassphraseDialog::onError(int type, QString error){
    newpassCache = "";
}

void AskPassphraseDialog::initWatch(QWidget *parent) {
    btnWatch = new QCheckBox(parent);
    btnWatch->setProperty("cssClass", "btn-watch-password");
    btnWatch->setChecked(false);
    QSize BUTTON_CONTACT_SIZE = QSize(24, 24);
    btnWatch->setMinimumSize(BUTTON_CONTACT_SIZE);
    btnWatch->setMaximumSize(BUTTON_CONTACT_SIZE);
    btnWatch->show();
    btnWatch->raise();

    int posXX = ui->layoutEdit->width() - 30;
    int posYY = 8;
    btnWatch->move(450, posYY);
}<|endoftext|>


<commit_before>#include "askpassphrasedialog.h"
#include "ui_askpassphrasedialog.h"

#include "guiconstants.h"
#include "walletmodel.h"

#include <QMessageBox>
#include <QPushButton>
#include <QKeyEvent>

AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget *parent) :
    QDialog(parent),
    ui(new Ui::AskPassphraseDialog),
    mode(mode),
    model(0),
    fCapsLock(false)
{
    ui->setupUi(this);
    ui->passEdit1->setMaxLength(MAX_PASSPHRASE_SIZE);
    ui->passEdit2->setMaxLength(MAX_PASSPHRASE_SIZE);
    ui->passEdit3->setMaxLength(MAX_PASSPHRASE_SIZE);

    // Setup Caps Lock detection.
    ui->passEdit1->installEventFilter(this);
    ui->passEdit2->installEventFilter(this);
    ui->passEdit3->installEventFilter(this);

    switch(mode)
    {
        case Encrypt: // Ask passphrase x2
            ui->passLabel1->hide();
            ui->passEdit1->hide();
            ui->warningLabel->setText(tr("Enter the new passphrase to the wallet.
Please use a passphrase of 10 or more random characters, or eight or more words."));
            setWindowTitle(tr("Encrypt wallet"));
            break;
        case Unlock: // Ask passphrase
            ui->warningLabel->setText(tr("This operation needs your wallet passphrase to unlock the wallet."));
            ui->passLabel2->hide();
            ui->passEdit2->hide();
            ui->passLabel3->hide();
            ui->passEdit3->hide();
            setWindowTitle(tr("Unlock wallet"));
            break;
        case Decrypt:   // Ask passphrase
            ui->warningLabel->setText(tr("This operation needs your wallet passphrase to decrypt the wallet."));
            ui->passLabel2->hide();
            ui->passEdit2->hide();
            ui->passLabel3->hide();
            ui->passEdit3->hide();
            setWindowTitle(tr("Decrypt wallet"));
            break;
        case ChangePass: // Ask old passphrase + new passphrase x2
            setWindowTitle(tr("Change passphrase"));
            ui->warningLabel->setText(tr("Enter the old and new passphrase to the wallet."));
            break;
    }

    textChanged();
    connect(ui->passEdit1, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
    connect(ui->passEdit2, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
    connect(ui->passEdit3, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
}

AskPassphraseDialog::~AskPassphraseDialog()
{
    // Attempt to overwrite text so that they do not linger around in memory
    ui->passEdit1->setText(QString(" ").repeated(ui->passEdit1->text().size()));
    ui->passEdit2->setText(QString(" ").repeated(ui->passEdit2->text().size()));
    ui->passEdit3->setText(QString(" ").repeated(ui->passEdit3->text().size()));
    delete ui;
}

void AskPassphraseDialog::setModel(WalletModel *model)
{
    this->model = model;
}

void AskPassphraseDialog::accept()
{
    SecureString oldpass, newpass1, newpass2;
    if(!model)
        return;
    oldpass.reserve(MAX_PASSPHRASE_SIZE);
    newpass1.reserve(MAX_PASSPHRASE_SIZE);
    newpass2.reserve(MAX_PASSPHRASE_SIZE);
    // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
    // Alternately, find a way to make this input mlock()'d to begin with.
    oldpass.assign(ui->passEdit1->text().toStdString().c_str());
    newpass1.assign(ui->passEdit2->text().toStdString().c_str());
    newpass2.assign(ui->passEdit3->text().toStdString().c_str());

    switch(mode)
    {
    case Encrypt: {
        if(newpass1.empty() || newpass2.empty())
        {
            // Cannot encrypt with empty passphrase
            break;
        }
        QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm wallet encryption"),
                 tr("WARNING: If you encrypt your wallet and lose your passphrase, you will LOSE ALL OF YOUR BITCOINS!\nAre you sure you wish to encrypt your wallet?"),
                 QMessageBox::Yes|QMessageBox::Cancel,
                 QMessageBox::Cancel);
        if(retval == QMessageBox::Yes)
        {
            if(newpass1 == newpass2)
            {
                if(model->setWalletEncrypted(true, newpass1))
                {
                    QMessageBox::warning(this, tr("Wallet encrypted"),
                                         tr("Bitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer."));
                    QApplication::quit();
                }
                else
                {
                    QMessageBox::critical(this, tr("Wallet encryption failed"),
                                         tr("Wallet encryption failed due to an internal error. Your wallet was not encrypted."));
                }
                QDialog::accept(); // Success
            }
            else
            {
                QMessageBox::critical(this, tr("Wallet encryption failed"),
                                     tr("The supplied passphrases do not match."));
            }
        }
        else
        {
            QDialog::reject(); // Cancelled
        }
        } break;
    case Unlock:
        if(!model->setWalletLocked(false, oldpass))
        {
            QMessageBox::critical(this, tr("Wallet unlock failed"),
                                  tr("The passphrase entered for the wallet decryption was incorrect."));
        }
        else
        {
            QDialog::accept(); // Success
        }
        break;
    case Decrypt:
        if(!model->setWalletEncrypted(false, oldpass))
        {
            QMessageBox::critical(this, tr("Wallet decryption failed"),
                                  tr("The passphrase entered for the wallet decryption was incorrect."));
        }
        else
        {
            QDialog::accept(); // Success
        }
        break;
    case ChangePass:
        if(newpass1 == newpass2)
        {
            if(model->changePassphrase(oldpass, newpass1))
            {
                QMessageBox::information(this, tr("Wallet encrypted"),
                                     tr("Wallet passphrase was succesfully changed."));
                QDialog::accept(); // Success
            }
            else
            {
                QMessageBox::critical(this, tr("Wallet encryption failed"),
                                     tr("The passphrase entered for the wallet decryption was incorrect."));
            }
        }
        else
        {
            QMessageBox::critical(this, tr("Wallet encryption failed"),
                                 tr("The supplied passphrases do not match."));
        }
        break;
    }
}

void AskPassphraseDialog::textChanged()
{
    // Validate input, set Ok button to enabled when accepable
    bool acceptable = false;
    switch(mode)
    {
    case Encrypt: // New passphrase x2
        acceptable = !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty();
        break;
    case Unlock: // Old passphrase x1
    case Decrypt:
        acceptable = !ui->passEdit1->text().isEmpty();
        break;
    case ChangePass: // Old passphrase x1, new passphrase x2
        acceptable = !ui->passEdit1->text().isEmpty() && !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty();
        break;
    }
    ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(acceptable);
}

bool AskPassphraseDialog::event(QEvent *event)
{
    // Detect Caps Lock key press.
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *ke = static_cast<QKeyEvent *>(event);
        if (ke->key() == Qt::Key_CapsLock) {
            fCapsLock = !fCapsLock;
        }
        if (fCapsLock) {
            ui->capsLabel->setText(tr("Warning: The Caps Lock key is on."));
        } else {
            ui->capsLabel->clear();
        }
    }
    return QWidget::event(event);
}

bool AskPassphraseDialog::eventFilter(QObject *, QEvent *event)
{
    /* Detect Caps Lock.
     * There is no good OS-independent way to check a key state in Qt, but we
     * can detect Caps Lock by checking for the following condition:
     * Shift key is down and the result is a lower case character, or
     * Shift key is not down and the result is an upper case character.
     */
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *ke = static_cast<QKeyEvent *>(event);
        QString str = ke->text();
        if (str.length() != 0) {
            const QChar *psz = str.unicode();
            bool fShift = (ke->modifiers() & Qt::ShiftModifier) != 0;
            if ((fShift && psz->isLower()) || (!fShift && psz->isUpper())) {
                fCapsLock = true;
                ui->capsLabel->setText(tr("Warning: The Caps Lock key is on."));
            } else if (psz->isLetter()) {
                fCapsLock = false;
                ui->capsLabel->clear();
            }
        }
    }
    return false;
}
<commit_msg>When encrypting the wallet, warn user that old backups will become useless.<commit_after>#include "askpassphrasedialog.h"
#include "ui_askpassphrasedialog.h"

#include "guiconstants.h"
#include "walletmodel.h"

#include <QMessageBox>
#include <QPushButton>
#include <QKeyEvent>

AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget *parent) :
    QDialog(parent),
    ui(new Ui::AskPassphraseDialog),
    mode(mode),
    model(0),
    fCapsLock(false)
{
    ui->setupUi(this);
    ui->passEdit1->setMaxLength(MAX_PASSPHRASE_SIZE);
    ui->passEdit2->setMaxLength(MAX_PASSPHRASE_SIZE);
    ui->passEdit3->setMaxLength(MAX_PASSPHRASE_SIZE);

    // Setup Caps Lock detection.
    ui->passEdit1->installEventFilter(this);
    ui->passEdit2->installEventFilter(this);
    ui->passEdit3->installEventFilter(this);

    switch(mode)
    {
        case Encrypt: // Ask passphrase x2
            ui->passLabel1->hide();
            ui->passEdit1->hide();
            ui->warningLabel->setText(tr("Enter the new passphrase to the wallet.
Please use a passphrase of 10 or more random characters, or eight or more words."));
            setWindowTitle(tr("Encrypt wallet"));
            break;
        case Unlock: // Ask passphrase
            ui->warningLabel->setText(tr("This operation needs your wallet passphrase to unlock the wallet."));
            ui->passLabel2->hide();
            ui->passEdit2->hide();
            ui->passLabel3->hide();
            ui->passEdit3->hide();
            setWindowTitle(tr("Unlock wallet"));
            break;
        case Decrypt:   // Ask passphrase
            ui->warningLabel->setText(tr("This operation needs your wallet passphrase to decrypt the wallet."));
            ui->passLabel2->hide();
            ui->passEdit2->hide();
            ui->passLabel3->hide();
            ui->passEdit3->hide();
            setWindowTitle(tr("Decrypt wallet"));
            break;
        case ChangePass: // Ask old passphrase + new passphrase x2
            setWindowTitle(tr("Change passphrase"));
            ui->warningLabel->setText(tr("Enter the old and new passphrase to the wallet."));
            break;
    }

    textChanged();
    connect(ui->passEdit1, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
    connect(ui->passEdit2, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
    connect(ui->passEdit3, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
}

AskPassphraseDialog::~AskPassphraseDialog()
{
    // Attempt to overwrite text so that they do not linger around in memory
    ui->passEdit1->setText(QString(" ").repeated(ui->passEdit1->text().size()));
    ui->passEdit2->setText(QString(" ").repeated(ui->passEdit2->text().size()));
    ui->passEdit3->setText(QString(" ").repeated(ui->passEdit3->text().size()));
    delete ui;
}

void AskPassphraseDialog::setModel(WalletModel *model)
{
    this->model = model;
}

void AskPassphraseDialog::accept()
{
    SecureString oldpass, newpass1, newpass2;
    if(!model)
        return;
    oldpass.reserve(MAX_PASSPHRASE_SIZE);
    newpass1.reserve(MAX_PASSPHRASE_SIZE);
    newpass2.reserve(MAX_PASSPHRASE_SIZE);
    // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
    // Alternately, find a way to make this input mlock()'d to begin with.
    oldpass.assign(ui->passEdit1->text().toStdString().c_str());
    newpass1.assign(ui->passEdit2->text().toStdString().c_str());
    newpass2.assign(ui->passEdit3->text().toStdString().c_str());

    switch(mode)
    {
    case Encrypt: {
        if(newpass1.empty() || newpass2.empty())
        {
            // Cannot encrypt with empty passphrase
            break;
        }
        QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm wallet encryption"),
                 tr("WARNING: If you encrypt your wallet and lose your passphrase, you will LOSE ALL OF YOUR BITCOINS!\nAre you sure you wish to encrypt your wallet?"),
                 QMessageBox::Yes|QMessageBox::Cancel,
                 QMessageBox::Cancel);
        if(retval == QMessageBox::Yes)
        {
            if(newpass1 == newpass2)
            {
                if(model->setWalletEncrypted(true, newpass1))
                {
                    QMessageBox::warning(this, tr("Wallet encrypted"),
                                         "<qt>" +
                                         tr("Bitcoin will close now to finish the encryption process. "
                                         "Remember that encrypting your wallet cannot fully protect "
                                         "your bitcoins from being stolen by malware infecting your computer.") +
                                         "

" +
                                         tr("IMPORTANT: Any previous backups you have made of your wallet file "
                                         "should be replaced with the newly generated, encrypted wallet file. "
                                         "For security reasons, previous backups of the unencrypted wallet file "
                                         "will become useless as soon as you start using the new, encrypted wallet.") +
                                         "</qt>");
                    QApplication::quit();
                }
                else
                {
                    QMessageBox::critical(this, tr("Wallet encryption failed"),
                                         tr("Wallet encryption failed due to an internal error. Your wallet was not encrypted."));
                }
                QDialog::accept(); // Success
            }
            else
            {
                QMessageBox::critical(this, tr("Wallet encryption failed"),
                                     tr("The supplied passphrases do not match."));
            }
        }
        else
        {
            QDialog::reject(); // Cancelled
        }
        } break;
    case Unlock:
        if(!model->setWalletLocked(false, oldpass))
        {
            QMessageBox::critical(this, tr("Wallet unlock failed"),
                                  tr("The passphrase entered for the wallet decryption was incorrect."));
        }
        else
        {
            QDialog::accept(); // Success
        }
        break;
    case Decrypt:
        if(!model->setWalletEncrypted(false, oldpass))
        {
            QMessageBox::critical(this, tr("Wallet decryption failed"),
                                  tr("The passphrase entered for the wallet decryption was incorrect."));
        }
        else
        {
            QDialog::accept(); // Success
        }
        break;
    case ChangePass:
        if(newpass1 == newpass2)
        {
            if(model->changePassphrase(oldpass, newpass1))
            {
                QMessageBox::information(this, tr("Wallet encrypted"),
                                     tr("Wallet passphrase was succesfully changed."));
                QDialog::accept(); // Success
            }
            else
            {
                QMessageBox::critical(this, tr("Wallet encryption failed"),
                                     tr("The passphrase entered for the wallet decryption was incorrect."));
            }
        }
        else
        {
            QMessageBox::critical(this, tr("Wallet encryption failed"),
                                 tr("The supplied passphrases do not match."));
        }
        break;
    }
}

void AskPassphraseDialog::textChanged()
{
    // Validate input, set Ok button to enabled when accepable
    bool acceptable = false;
    switch(mode)
    {
    case Encrypt: // New passphrase x2
        acceptable = !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty();
        break;
    case Unlock: // Old passphrase x1
    case Decrypt:
        acceptable = !ui->passEdit1->text().isEmpty();
        break;
    case ChangePass: // Old passphrase x1, new passphrase x2
        acceptable = !ui->passEdit1->text().isEmpty() && !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty();
        break;
    }
    ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(acceptable);
}

bool AskPassphraseDialog::event(QEvent *event)
{
    // Detect Caps Lock key press.
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *ke = static_cast<QKeyEvent *>(event);
        if (ke->key() == Qt::Key_CapsLock) {
            fCapsLock = !fCapsLock;
        }
        if (fCapsLock) {
            ui->capsLabel->setText(tr("Warning: The Caps Lock key is on."));
        } else {
            ui->capsLabel->clear();
        }
    }
    return QWidget::event(event);
}

bool AskPassphraseDialog::eventFilter(QObject *, QEvent *event)
{
    /* Detect Caps Lock.
     * There is no good OS-independent way to check a key state in Qt, but we
     * can detect Caps Lock by checking for the following condition:
     * Shift key is down and the result is a lower case character, or
     * Shift key is not down and the result is an upper case character.
     */
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *ke = static_cast<QKeyEvent *>(event);
        QString str = ke->text();
        if (str.length() != 0) {
            const QChar *psz = str.unicode();
            bool fShift = (ke->modifiers() & Qt::ShiftModifier) != 0;
            if ((fShift && psz->isLower()) || (!fShift && psz->isUpper())) {
                fCapsLock = true;
                ui->capsLabel->setText(tr("Warning: The Caps Lock key is on."));
            } else if (psz->isLetter()) {
                fCapsLock = false;
                ui->capsLabel->clear();
            }
        }
    }
    return false;
}
<|endoftext|>


<commit_before>#include "psqlwrapper.h"

#include <QDebug>
#include <QSettings>
#include <QSqlError>
#include <QSqlQuery>
#include <QVariant>

bool PsqlWrapper::informationSchemaHidden = true;
bool PsqlWrapper::pgCatalogHidden = true;

PsqlWrapper::PsqlWrapper(QSqlDatabase *db)
  : QObject(NULL) {
  m_db = db;

  QSettings s;
  s.beginGroup(plid());
  informationSchemaHidden = s.value("informationSchemaHidden", true).toBool();
  pgCatalogHidden = s.value("pgCatalogHidden", true).toBool();
  s.endGroup();

  m_configDialog = new PsqlConfig(this);
}

SqlWrapper::WrapperFeatures PsqlWrapper::features() {
  return ODBC | Schemas;
}

SqlWrapper* PsqlWrapper::newInstance(QSqlDatabase *db) {
  return new PsqlWrapper(db);
}

void PsqlWrapper::save() {
  QSettings s;
  s.beginGroup(plid());
  s.setValue("informationSchemaHidden", informationSchemaHidden);
  s.setValue("pgCatalogHidden", pgCatalogHidden);
  s.endGroup();
}

/**
 * Récupération d'un schéma
 */
SqlSchema PsqlWrapper::schema(QString sch) {
  SqlSchema schema;

  if (!m_db) {
    return schema;
  }

  QString sql;
  sql += "SELECT '" + sch + "', t.table_name, t.table_type, c.column_name, ";
  sql += "c.is_nullable, c.data_type ";
  sql += "FROM INFORMATION_SCHEMA.COLUMNS C ";
  sql += "INNER JOIN INFORMATION_SCHEMA.TABLES T ";
  sql +=   "ON C.TABLE_NAME = T.TABLE_NAME ";
  sql +=      "AND C.TABLE_SCHEMA = T.TABLE_SCHEMA ";
  sql += "WHERE t.table_catalog='" + m_db->databaseName() + "' ";
  sql +=   "AND t.table_schema='" + sch + "' ";
  sql += "ORDER BY table_name, ordinal_position ";

  QSqlQuery query(*m_db);
  if (!query.exec(sql)) {
    qDebug() << query.lastError().text();
    return schema;
  }

  bool ruptureTable = false;
  SqlTable t;
  while (query.next()) {
    schema.name = query.value(0).toString();
    schema.defaultSchema = schema.name == "public";

    // Tables
    ruptureTable = t.name != query.value(1).toString();

    if (ruptureTable) {
      if (t.name.length() > 0) {
        schema.tables << t;
      }

      t = SqlTable();
      t.name = query.value(1).toString();
      if (query.value(2).toString() == "BASE TABLE") {
        t.type = Table;
      } else if (query.value(2).toString() == "VIEW") {
        t.type = ViewTable;
      }
    }

    // Colonnes
    SqlColumn c;
    c.name = query.value(3).toString();
    c.permitsNull = query.value(4).toBool();
    c.primaryKey = false;
    t.columns << c;
  }

  if (t.name.length() > 0) {
    schema.tables << t;
  }

  return schema;
}

/**
 * Récupération de la liste des tables
 */
QList<SqlSchema> PsqlWrapper::schemas() {
  QList<SqlSchema> schemas;

  if (!m_db) {
    return schemas;
  }

  QString sql;
  sql += "SELECT t.table_schema, t.table_name, t.table_type, c.column_name, ";
  sql += "c.is_nullable, c.data_type ";
  sql += "FROM INFORMATION_SCHEMA.COLUMNS C ";
  sql += "INNER JOIN INFORMATION_SCHEMA.TABLES T ";
  sql +=   "ON C.TABLE_NAME = T.TABLE_NAME ";
  sql +=      "AND C.TABLE_SCHEMA = T.TABLE_SCHEMA ";
  sql +=   "WHERE t.table_catalog='" + m_db->databaseName() + "' ";
  if (informationSchemaHidden) {
    sql +=   "AND t.table_schema <> 'information_schema' ";
  }
  if (pgCatalogHidden) {
    sql +=   "AND t.table_schema <> 'pg_catalog' ";
  }
  sql +=   "ORDER BY table_schema, table_name, ordinal_position ";

  QSqlQuery query(*m_db);
  if (!query.exec(sql)) {
    qDebug() << query.lastError().text();
    return schemas;
  }

  bool first = true;
  bool ruptureSchema = false;
  bool ruptureTable = false;
  SqlSchema s;
  SqlTable t;
  while (query.next()) {
    // Schémas
    ruptureSchema = s.name != query.value(0).toString();

    if (ruptureSchema && s.name != "") {
      s.tables << t;
      schemas << s;
    }

    if (ruptureSchema) {
      s = SqlSchema();
      s.name = query.value(0).toString();
      s.defaultSchema = s.name == "public";
    }

    // Tables
    ruptureTable = t.name != query.value(1).toString();

    if (first || (ruptureTable && !ruptureSchema && t.name != "")) {
      first = false;
      s.tables << t;
    }

    if (ruptureTable) {
      t = SqlTable();
      t.name = query.value(1).toString();
      if (query.value(2).toString() == "BASE TABLE") {
        t.type = Table;
      } else if (query.value(2).toString() == "VIEW") {
        t.type = ViewTable;
      }
    }

    // Colonnes
    SqlColumn c;
    c.name = query.value(3).toString();
    c.permitsNull = query.value(4).toBool();
    c.primaryKey = false;
    c.defaultValue = query.value(6);
    t.columns << c;
  }

  if (s.name.length() > 0) {
    if (t.name.length() > 0) {
      s.tables << t;
    }
    schemas << s;
  }

  return schemas;
}

SqlTable PsqlWrapper::table(QString t) {
  SqlTable table;

  if (!m_db) {
    return table;
  }

  QString sql;
  sql += "SELECT schemaname, typname, 'T', attname, attnotnull, attlen, attnum ";
  sql += "FROM pg_attribute, pg_type, pg_tables ";
  sql += "WHERE attrelid = typrelid ";
  sql +=   "AND attname NOT IN ('cmin', 'cmax', 'ctid', 'oid', ";
  sql +=      "'tableoid', 'xmin', 'xmax') ";
  sql +=   "AND typname = tablename ";
  sql +=   "AND typname = '" + t + "' ";

  sql += "UNION ";

  sql += "SELECT schemaname, typname, 'V', attname, attnotnull, attlen, attnum ";
  sql += "FROM pg_attribute, pg_type, pg_views ";
  sql += "WHERE attrelid = typrelid ";
  sql +=   "AND attname NOT IN ('cmin', 'cmax', 'ctid', 'oid', ";
  sql +=      "'tableoid', 'xmin', 'xmax') ";
  sql +=   "AND typname = viewname ";
  sql +=   "AND typname = '" + t + "' ";

  sql += "ORDER BY schemaname, typname, attnum ";

  QSqlQuery query(*m_db);
  if (!query.exec(sql)) {
    qDebug() << query.lastError().text();
    return table;
  }

  bool first = true;
  while (query.next()) {
    if (first) {
      table.name = query.value(1).toString();
      if (query.value(2).toString() == "T") {
        table.type = Table;
      } else if (query.value(2).toString() == "V") {
        table.type = ViewTable;
      }
      first = false;
    }

    // Colonnes
    SqlColumn c;
    c.name = query.value(3).toString();
    c.permitsNull = query.value(4).toBool();
    c.primaryKey = false;
    table.columns << c;
  }

  return table;
}

Q_EXPORT_PLUGIN2(dbm_psql_wrapper, PsqlWrapper)
<commit_msg>Problème table + schémas<commit_after>#include "psqlwrapper.h"

#include <QDebug>
#include <QSettings>
#include <QSqlError>
#include <QSqlQuery>
#include <QVariant>

bool PsqlWrapper::informationSchemaHidden = true;
bool PsqlWrapper::pgCatalogHidden = true;

PsqlWrapper::PsqlWrapper(QSqlDatabase *db)
  : QObject(NULL) {
  m_db = db;

  QSettings s;
  s.beginGroup(plid());
  informationSchemaHidden = s.value("informationSchemaHidden", true).toBool();
  pgCatalogHidden = s.value("pgCatalogHidden", true).toBool();
  s.endGroup();

  m_configDialog = new PsqlConfig(this);
}

SqlWrapper::WrapperFeatures PsqlWrapper::features() {
  return ODBC | Schemas;
}

SqlWrapper* PsqlWrapper::newInstance(QSqlDatabase *db) {
  return new PsqlWrapper(db);
}

void PsqlWrapper::save() {
  QSettings s;
  s.beginGroup(plid());
  s.setValue("informationSchemaHidden", informationSchemaHidden);
  s.setValue("pgCatalogHidden", pgCatalogHidden);
  s.endGroup();
}

/**
 * Récupération d'un schéma
 */
SqlSchema PsqlWrapper::schema(QString sch) {
  SqlSchema schema;

  if (!m_db) {
    return schema;
  }

  QString sql;
  sql += "SELECT '" + sch + "', t.table_name, t.table_type, c.column_name, ";
  sql += "c.is_nullable, c.data_type, c.column_default ";
  sql += "FROM INFORMATION_SCHEMA.COLUMNS C ";
  sql += "INNER JOIN INFORMATION_SCHEMA.TABLES T ";
  sql +=   "ON C.TABLE_NAME = T.TABLE_NAME ";
  sql +=      "AND C.TABLE_SCHEMA = T.TABLE_SCHEMA ";
  sql += "WHERE t.table_catalog='" + m_db->databaseName() + "' ";
  sql +=   "AND t.table_schema='" + sch + "' ";
  sql += "ORDER BY table_name, ordinal_position ";

  QSqlQuery query(*m_db);
  if (!query.exec(sql)) {
    qDebug() << query.lastError().text();
    return schema;
  }

  bool ruptureTable = false;
  SqlTable t;
  while (query.next()) {
    schema.name = query.value(0).toString();
    schema.defaultSchema = schema.name == "public";

    // Tables
    ruptureTable = t.name != query.value(1).toString();

    if (ruptureTable) {
      if (t.name.length() > 0) {
        schema.tables << t;
      }

      t = SqlTable();
      t.name = query.value(1).toString();
      if (query.value(2).toString() == "BASE TABLE") {
        t.type = Table;
      } else if (query.value(2).toString() == "VIEW") {
        t.type = ViewTable;
      }
    }

    // Colonnes
    SqlColumn c;
    c.name = query.value(3).toString();
    c.permitsNull = query.value(4).toBool();
    c.primaryKey = false;
    t.columns << c;
  }

  if (t.name.length() > 0) {
    schema.tables << t;
  }

  return schema;
}

/**
 * Récupération de la liste des tables
 */
QList<SqlSchema> PsqlWrapper::schemas() {
  QList<SqlSchema> schemas;

  if (!m_db) {
    return schemas;
  }

  QString sql;
  sql += "SELECT t.table_schema, t.table_name, t.table_type, c.column_name, ";
  sql += "c.is_nullable, c.data_type, c.column_default ";
  sql += "FROM INFORMATION_SCHEMA.COLUMNS C ";
  sql += "INNER JOIN INFORMATION_SCHEMA.TABLES T ";
  sql +=   "ON C.TABLE_NAME = T.TABLE_NAME ";
  sql +=      "AND C.TABLE_SCHEMA = T.TABLE_SCHEMA ";
  sql +=   "WHERE t.table_catalog='" + m_db->databaseName() + "' ";
  if (informationSchemaHidden) {
    sql +=   "AND t.table_schema <> 'information_schema' ";
  }
  if (pgCatalogHidden) {
    sql +=   "AND t.table_schema <> 'pg_catalog' ";
  }
  sql +=   "ORDER BY table_schema, table_name, ordinal_position ";

  QSqlQuery query(*m_db);
  if (!query.exec(sql)) {
    qDebug() << query.lastError().text();
    return schemas;
  }

  bool first = true;
  bool ruptureSchema = false;
  bool ruptureTable = false;
  SqlSchema s;
  SqlTable t;
  while (query.next()) {
    // Schémas
    ruptureSchema = s.name != query.value(0).toString();

    if (ruptureSchema && s.name != "") {
      s.tables << t;
      schemas << s;
    }

    if (ruptureSchema) {
      s = SqlSchema();
      s.name = query.value(0).toString();
      s.defaultSchema = s.name == "public";
    }

    // Tables
    ruptureTable = t.name != query.value(1).toString();

    if (first || (ruptureTable && !ruptureSchema && t.name != "")) {
      first = false;
      s.tables << t;
    }

    if (ruptureTable) {
      t = SqlTable();
      t.name = query.value(1).toString();
      if (query.value(2).toString() == "BASE TABLE") {
        t.type = Table;
      } else if (query.value(2).toString() == "VIEW") {
        t.type = ViewTable;
      }
    }

    // Colonnes
    SqlColumn c;
    c.name = query.value(3).toString();
    c.permitsNull = query.value(4).toBool();
    c.primaryKey = false;
    c.defaultValue = query.value(6);
    t.columns << c;
  }

  if (s.name.length() > 0) {
    if (t.name.length() > 0) {
      s.tables << t;
    }
    schemas << s;
  }

  return schemas;
}

SqlTable PsqlWrapper::table(QString t) {
  SqlTable table;

  if (!m_db) {
    return table;
  }

  QString sch = "";
  if (t.contains(".")) {
    sch = t.left(t.indexOf("."));
    t = t.right(t.indexOf(".") + 2);
  }

  qDebug() << sch << t;

  QString sql;
  sql += "SELECT t.table_schema, t.table_name, t.table_type, c.column_name, ";
  sql += "c.is_nullable, c.data_type, c.column_default ";
  sql += "FROM INFORMATION_SCHEMA.COLUMNS C ";
  sql += "INNER JOIN INFORMATION_SCHEMA.TABLES T ";
  sql +=   "ON C.TABLE_NAME = T.TABLE_NAME ";
  sql +=      "AND C.TABLE_SCHEMA = T.TABLE_SCHEMA ";
  sql +=   "WHERE t.table_catalog='" + m_db->databaseName() + "' ";
  if (sch.length() > 0) {
    sql +=   "AND t.table_schema = '" + sch + "'";
  }
  sql +=     "AND t.table_name = '" + t + "' ";
  sql +=   "ORDER BY ordinal_position ";

  QSqlQuery query(*m_db);
  if (!query.exec(sql)) {
    qDebug() << query.lastError().text();
    return table;
  }

  bool first = true;
  while (query.next()) {
    if (first) {
      table.name = query.value(1).toString();
      if (query.value(2).toString() == "T") {
        table.type = Table;
      } else if (query.value(2).toString() == "V") {
        table.type = ViewTable;
      }
      first = false;
    }

    // Colonnes
    SqlColumn c;
    c.name = query.value(3).toString();
    c.permitsNull = query.value(4).toBool();
    c.primaryKey = false;
    table.columns << c;
  }

  return table;
}

Q_EXPORT_PLUGIN2(dbm_psql_wrapper, PsqlWrapper)
<|endoftext|>