diff --git a/src/certificate.cpp b/src/certificate.cpp index 94c2ab933c405915b5a32e21c6119e70d51fad42..326ccaf7fab6adb448bb70b5bff4f3483eb41f75 100644 --- a/src/certificate.cpp +++ b/src/certificate.cpp @@ -658,3 +658,16 @@ QVariant Certificate::detailResult(Certificate::Details detail) const }; return QVariant(); } + +/** + * Get the details of this certificate as a QAbstractItemModel + * + * Please note that the object ownership will be transferred. To avoid memory + * leaks, the users of this object must delete it once they are done with it. + */ +QAbstractItemModel* Certificate::model() const +{ + return CertificateModel::instance()->model(this); +} + +#include <certificate.moc> diff --git a/src/certificate.h b/src/certificate.h index 6b4232d5e73466e176155bab766dfa2e483bcb3a..d29ec45c2a6ca65ca7614869f5cf9868a652a93c 100644 --- a/src/certificate.h +++ b/src/certificate.h @@ -22,6 +22,7 @@ //Qt #include <QUrl> +class QAbstractItemModel; class CertificatePrivate; @@ -176,6 +177,7 @@ public: Certificate::Type type() const; Certificate::CheckValues checkResult (Certificate::Checks check ) const; QVariant detailResult(Certificate::Details detail) const; + QAbstractItemModel* model() const; QString getName (Certificate::Checks check ); QString getName (Certificate::Details details ); diff --git a/src/certificatemodel.cpp b/src/certificatemodel.cpp index 8e7a8c5367b27fbb2f2d279cf482fc31fab89fd4..a17f308074598eaef4334a9fa91918c8b71aa8a6 100644 --- a/src/certificatemodel.cpp +++ b/src/certificatemodel.cpp @@ -24,6 +24,7 @@ #include <QtCore/QThread> #include <QtCore/QMutex> #include <QtCore/QMutexLocker> +#include <QtCore/QAbstractProxyModel> //LibSTDC++ #include <functional> @@ -34,27 +35,20 @@ struct CertificateNode { - enum class Level { - CATEGORY = 0, - CERTIFICATE = 1, - DETAILS_CATEGORY = 2, - DETAILS = 3, - }; - - CertificateNode(int index, Level level, CertificateNode* parent, Certificate* cert); + CertificateNode(int index, CertificateModel::NodeType level, CertificateNode* parent, Certificate* cert); void setStrings(const QString& col1, const QVariant& col2, const QString& tooltip); //Attributes - QVector<CertificateNode*> m_lChildren ; - CertificateNode* m_pParent ; - Certificate* m_pCertificate; - CertificateNode::Level m_Level ; - int m_Index ; - QString m_Col1 ; - QVariant m_Col2 ; - QString m_ToolTip ; - std::function<void()> m_fLoader ; - bool m_IsLoaded ; + QVector<CertificateNode*> m_lChildren ; + CertificateNode* m_pParent ; + Certificate* m_pCertificate; + CertificateModel::NodeType m_Level ; + int m_Index ; + QString m_Col1 ; + QVariant m_Col2 ; + QString m_ToolTip ; + std::function<void()> m_fLoader ; + bool m_IsLoaded ; }; class BackgroundLoader : public QThread @@ -76,16 +70,23 @@ Q_SIGNALS: void listLoaded(const QList<QByteArray>& list); }; -QByteArray BackgroundLoader::loadCert(const QByteArray& id) +class CertificateProxyModel : public QAbstractProxyModel { - if (m_lIdList.isEmpty()) //WARNING potential race - QList<QByteArray> m_lIdList = CertificateSerializationDelegate::instance()->listCertificates(); + Q_OBJECT +public: + CertificateProxyModel(CertificateModel* parent, CertificateNode* root); - if (m_lIdList.indexOf(id) == -1) //TODO use an hash map for this - return QByteArray(); + //Model implementation + virtual QModelIndex mapFromSource( const QModelIndex& sourceIndex ) const override; + virtual QModelIndex mapToSource ( const QModelIndex& proxyIndex ) const override; + virtual QModelIndex index ( int row, int column, const QModelIndex& parent=QModelIndex()) const override; + virtual QModelIndex parent ( const QModelIndex& index ) const override; + virtual int rowCount ( const QModelIndex& parent = QModelIndex() ) const override; + virtual int columnCount ( const QModelIndex& parent = QModelIndex() ) const override; - return CertificateSerializationDelegate::instance()->loadCertificate(id); -} +private: + CertificateNode* m_pRoot; +}; class CertificateModelPrivate { @@ -96,12 +97,15 @@ public: //Helper CertificateNode* defaultCategory(); CertificateNode* addToTree(Certificate* cert, CertificateNode* category = nullptr); + QModelIndex createIndex(int r ,int c , void* p); + QAbstractItemModel* getModelCommon(CertificateNode* node); //Attributes QVector<CertificateNode*> m_lTopLevelNodes ; QHash<QString,Certificate*> m_hCertificates ; CertificateNode* m_pDefaultCategory; - QMutex m_CertLoader; + QMutex m_CertLoader ; + QHash<const Certificate*,CertificateNode*> m_hNodes ; //Singleton static CertificateModel* m_spInstance; @@ -110,6 +114,17 @@ private: CertificateModel* q_ptr; }; +QByteArray BackgroundLoader::loadCert(const QByteArray& id) +{ + if (m_lIdList.isEmpty()) //WARNING potential race + QList<QByteArray> m_lIdList = CertificateSerializationDelegate::instance()->listCertificates(); + + if (m_lIdList.indexOf(id) == -1) //TODO use an hash map for this + return QByteArray(); + + return CertificateSerializationDelegate::instance()->loadCertificate(id); +} + CertificateModel* CertificateModelPrivate::m_spInstance = nullptr; CertificateModelPrivate::~CertificateModelPrivate() @@ -120,10 +135,10 @@ CertificateModelPrivate::~CertificateModelPrivate() } } -CertificateNode::CertificateNode(int index, Level level, CertificateNode* parent, Certificate* cert) : +CertificateNode::CertificateNode(int index, CertificateModel::NodeType level, CertificateNode* parent, Certificate* cert) : m_pParent(parent), m_pCertificate(cert), m_Level(level), m_Index(index), m_IsLoaded(true) { - + CertificateModel::instance()->d_ptr->m_hNodes[cert] = this; } CertificateModelPrivate::CertificateModelPrivate(CertificateModel* parent) : q_ptr(parent), @@ -185,7 +200,7 @@ CertificateNode* CertificateModelPrivate::defaultCategory() if (!m_pDefaultCategory) { const int idx = m_hCertificates.size(); - m_pDefaultCategory = new CertificateNode(idx, CertificateNode::Level::CATEGORY, nullptr, nullptr); + m_pDefaultCategory = new CertificateNode(idx, CertificateModel::NodeType::CATEGORY, nullptr, nullptr); m_pDefaultCategory->setStrings(QObject::tr("Default"),QObject::tr("Certificate not associated with a group"),QString()); q_ptr->beginInsertRows(QModelIndex(), idx, idx); @@ -196,6 +211,11 @@ CertificateNode* CertificateModelPrivate::defaultCategory() return m_pDefaultCategory; } +QModelIndex CertificateModelPrivate::createIndex(int r ,int c , void* p) +{ + return q_ptr->createIndex(r,c,p); +} + CertificateNode* CertificateModelPrivate::addToTree(Certificate* cert, CertificateNode* category) { QMutexLocker(&this->m_CertLoader); @@ -205,7 +225,7 @@ CertificateNode* CertificateModelPrivate::addToTree(Certificate* cert, Certifica const int idx = category->m_lChildren.size(); - CertificateNode* node = new CertificateNode(idx, CertificateNode::Level::CERTIFICATE, category, cert); + CertificateNode* node = new CertificateNode(idx, CertificateModel::NodeType::CERTIFICATE, category, cert); node->setStrings(QObject::tr("A certificate"),QObject::tr("An organisation"),QString()); @@ -221,8 +241,8 @@ CertificateNode* CertificateModelPrivate::addToTree(Certificate* cert, Certifica //Insert the check and details categories q_ptr->beginInsertRows(index, 0, static_cast<int>(CertificateModel::Columns::NAME )); - CertificateNode* details = new CertificateNode(static_cast<int>(CertificateModel::Columns::NAME ), CertificateNode::Level::DETAILS_CATEGORY, node, nullptr); - CertificateNode* checks = new CertificateNode(static_cast<int>(CertificateModel::Columns::VALUE), CertificateNode::Level::DETAILS_CATEGORY, node, nullptr); + CertificateNode* details = new CertificateNode(static_cast<int>(CertificateModel::Columns::NAME ), CertificateModel::NodeType::DETAILS_CATEGORY, node, nullptr); + CertificateNode* checks = new CertificateNode(static_cast<int>(CertificateModel::Columns::VALUE), CertificateModel::NodeType::DETAILS_CATEGORY, node, nullptr); details->setStrings(QObject::tr("Details"),QString(),QObject::tr("The content of the certificate") ); checks ->setStrings(QObject::tr("Checks") ,QString(),QObject::tr("Various security related information") ); node->m_lChildren << details; node->m_lChildren << checks; @@ -234,7 +254,7 @@ CertificateNode* CertificateModelPrivate::addToTree(Certificate* cert, Certifica const QModelIndex detailsI(q_ptr->createIndex(details->m_Index,static_cast<int>(CertificateModel::Columns::NAME ),details)); q_ptr->beginInsertRows(detailsI, static_cast<int>(CertificateModel::Columns::NAME ), detailsC); for (const Certificate::Details detail : EnumIterator<Certificate::Details>()) { - CertificateNode* d = new CertificateNode(details->m_lChildren.size(), CertificateNode::Level::DETAILS, details, nullptr); + CertificateNode* d = new CertificateNode(details->m_lChildren.size(), CertificateModel::NodeType::DETAILS, details, nullptr); d->setStrings(cert->getName(detail),cert->detailResult(detail),cert->getDescription(detail) ); details->m_lChildren << d; } @@ -245,7 +265,7 @@ CertificateNode* CertificateModelPrivate::addToTree(Certificate* cert, Certifica q_ptr->beginInsertRows(checksI, static_cast<int>(CertificateModel::Columns::NAME ), checksC); for (const Certificate::Checks check : EnumIterator<Certificate::Checks>()) { if (cert->checkResult(check) != Certificate::CheckValues::UNSUPPORTED) { - CertificateNode* d = new CertificateNode(checks->m_lChildren.size(), CertificateNode::Level::DETAILS, checks, nullptr); + CertificateNode* d = new CertificateNode(checks->m_lChildren.size(), CertificateModel::NodeType::DETAILS, checks, nullptr); d->setStrings(cert->getName(check),static_cast<bool>(cert->checkResult(check)),cert->getDescription(check)); checks->m_lChildren << d; } @@ -278,6 +298,8 @@ QVariant CertificateModel::data( const QModelIndex& index, int role) const return index.column()?node->m_Col2:node->m_Col1; case Qt::ToolTipRole: return node->m_ToolTip; + case static_cast<int>(Role::NodeType): + return QVariant::fromValue(node->m_Level); }; return QVariant(); } @@ -290,7 +312,7 @@ int CertificateModel::rowCount( const QModelIndex& parent) const const CertificateNode* node = static_cast<CertificateNode*>(parent.internalPointer()); //Load that info only when it is needed - if (node->m_Level == CertificateNode::Level::CERTIFICATE && (!node->m_IsLoaded)) + if (node->m_Level == CertificateModel::NodeType::CERTIFICATE && (!node->m_IsLoaded)) node->m_fLoader(); return node->m_lChildren.size(); } @@ -380,4 +402,108 @@ Certificate* CertificateModel::getCertificateFromContent(const QByteArray& rawCo return cert; } +CertificateProxyModel::CertificateProxyModel(CertificateModel* parent, CertificateNode* root) : QAbstractProxyModel(parent),m_pRoot(root) +{ + setSourceModel(parent); +} + +QModelIndex CertificateProxyModel::mapFromSource(const QModelIndex& sourceIndex) const +{ + if (!sourceIndex.isValid()) + return QModelIndex(); + CertificateModel::NodeType type = qvariant_cast<CertificateModel::NodeType>(sourceIndex.data((int)CertificateModel::Role::NodeType)); + switch (type) { + case CertificateModel::NodeType::CATEGORY : + case CertificateModel::NodeType::CERTIFICATE : + return QModelIndex(); + case CertificateModel::NodeType::DETAILS_CATEGORY: + case CertificateModel::NodeType::DETAILS : + return createIndex(sourceIndex.row(),sourceIndex.column(),sourceIndex.internalPointer()); + } + return QModelIndex(); +} + +QModelIndex CertificateProxyModel::mapToSource(const QModelIndex& proxyIndex) const +{ + return CertificateModel::instance()->d_ptr->createIndex(proxyIndex.row(),proxyIndex.column(),proxyIndex.internalPointer()); +} + +QModelIndex CertificateProxyModel::index( int row, int column, const QModelIndex& parent) const +{ + if ((parent.isValid() && parent.model() != this) || column > 1) + return QModelIndex(); + + CertificateNode* node = parent.isValid()?static_cast<CertificateNode*>(parent.internalPointer()):m_pRoot; + + if ((!node) || row >= node->m_lChildren.size()) + return QModelIndex(); + + return createIndex(row, column, node->m_lChildren[row]); +} + +QModelIndex CertificateProxyModel::parent( const QModelIndex& index ) const +{ + if ((index.model() != this) || (!index.isValid())) + return QModelIndex(); + + CertificateNode* node = static_cast<CertificateNode*>(index.internalPointer()); + + if (!node) + return QModelIndex(); + + return (node->m_pParent == m_pRoot)?QModelIndex() : createIndex(node->m_pParent->m_Index, index.column(), node->m_pParent); +} + +int CertificateProxyModel::rowCount( const QModelIndex& parent ) const +{ + return parent.isValid()? sourceModel()->rowCount(mapToSource(parent)) : m_pRoot->m_lChildren.size(); +} + +int CertificateProxyModel::columnCount( const QModelIndex& parent ) const +{ + return sourceModel()->columnCount(mapToSource(parent)); +} + +QAbstractItemModel* CertificateModelPrivate::getModelCommon(CertificateNode* node) +{ + if (node) { + if (node->m_Level == CertificateModel::NodeType::CERTIFICATE && (!node->m_IsLoaded)) + node->m_fLoader(); + + CertificateProxyModel* m = new CertificateProxyModel(q_ptr,node); + + return m; + } + + return nullptr; +} + +/** + * This model is a proxy of CertificateModel with only the current certificate + * + * Please note that the object ownership will be transferred. To avoid memory + * leaks, the users of this object must delete it once they are done with it. + */ +QAbstractItemModel* CertificateModel::model(const Certificate* cert) const +{ + if (!cert) + return nullptr; + return d_ptr->getModelCommon(d_ptr->m_hNodes[cert]); +} + +/** + * This model is a proxy of CertificateModel with only the current certificate + * + * Please note that the object ownership will be transferred. To avoid memory + * leaks, the users of this object must delete it once they are done with it. + */ +QAbstractItemModel* CertificateModel::model(const QModelIndex& idx) const +{ + if ((!idx.isValid()) || (idx.model() != this)) + return nullptr; + + CertificateNode* node = static_cast<CertificateNode*>(idx.internalPointer()); + return d_ptr->getModelCommon(node); +} + #include <certificatemodel.moc> diff --git a/src/certificatemodel.h b/src/certificatemodel.h index da5baee1818cf95fc07a58342da6b0d00be2a7af..737521b7201156d163e3131f89a24eade3177a58 100644 --- a/src/certificatemodel.h +++ b/src/certificatemodel.h @@ -28,12 +28,25 @@ class LIB_EXPORT CertificateModel : public QAbstractItemModel { Q_OBJECT public: + friend class CertificateProxyModel; + friend class CertificateNode; + + enum class Role { + NodeType = 100, + }; enum class Columns { NAME = 0, VALUE = 1, }; + enum class NodeType { + CATEGORY = 0, + CERTIFICATE = 1, + DETAILS_CATEGORY = 2, + DETAILS = 3, + }; + //Constructor explicit CertificateModel(QObject* parent = nullptr); virtual ~CertificateModel(); @@ -49,6 +62,10 @@ public: virtual QVariant headerData ( int section, Qt::Orientation, int role = Qt::DisplayRole ) const override; virtual QHash<int,QByteArray> roleNames() const override; + //Getters + QAbstractItemModel* model(const Certificate* cert) const; + QAbstractItemModel* model(const QModelIndex& idx) const; + //Mutator Certificate* getCertificate(const QUrl& path, Certificate::Type type = Certificate::Type::NONE); Certificate* getCertificateFromContent(const QByteArray& rawContent, bool save = true); @@ -60,5 +77,6 @@ private: CertificateModelPrivate* d_ptr; Q_DECLARE_PRIVATE(CertificateModel) }; +Q_DECLARE_METATYPE(CertificateModel::NodeType) #endif \ No newline at end of file