File indexing completed on 2024-04-21 03:56:04

0001 /*
0002     SPDX-FileCopyrightText: 2009 Stephen Kelly <steveire@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #ifndef DYNAMICTREEMODEL_H
0008 #define DYNAMICTREEMODEL_H
0009 
0010 #include <QAbstractItemModel>
0011 
0012 #include <QHash>
0013 #include <QList>
0014 
0015 #include "indexfinder.h"
0016 
0017 #include "proxymodeltestsuite_export.h"
0018 
0019 template<typename T>
0020 class QList;
0021 
0022 class ModelMoveCommand;
0023 
0024 class PROXYMODELTESTSUITE_EXPORT DynamicTreeModel : public QAbstractItemModel
0025 {
0026     Q_OBJECT
0027 public:
0028     enum Roles {
0029         DynamicTreeModelId = Qt::UserRole,
0030 
0031         LastRole,
0032     };
0033 
0034     explicit DynamicTreeModel(QObject *parent = nullptr);
0035 
0036     QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
0037     QModelIndex parent(const QModelIndex &index) const override;
0038     int rowCount(const QModelIndex &index = QModelIndex()) const override;
0039     int columnCount(const QModelIndex &index = QModelIndex()) const override;
0040 
0041     QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
0042     bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
0043 
0044     bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
0045     Qt::ItemFlags flags(const QModelIndex &index) const override;
0046     Qt::DropActions supportedDropActions() const override;
0047     QStringList mimeTypes() const override;
0048     QMimeData *mimeData(const QModelIndexList &indexes) const override;
0049     QModelIndexList match(const QModelIndex &start,
0050                           int role,
0051                           const QVariant &value,
0052                           int hits = 1,
0053                           Qt::MatchFlags flags = Qt::MatchFlags(Qt::MatchStartsWith | Qt::MatchWrap)) const override;
0054 
0055     void clear();
0056     QList<int> indexToPath(const QModelIndex &idx) const;
0057     ModelMoveCommand *getMoveCommand(const QList<int> &srcPath, int startRow, int endRow);
0058 
0059 protected Q_SLOTS:
0060 
0061     /**
0062     Finds the parent id of the string with id @p searchId.
0063 
0064     Returns -1 if not found.
0065     */
0066     qint64 findParentId(qint64 searchId) const;
0067 
0068 private:
0069     QHash<qint64, QString> m_items;
0070     QHash<qint64, QList<QList<qint64>>> m_childItems;
0071     qint64 nextId;
0072     qint64 newId()
0073     {
0074         return nextId++;
0075     }
0076 
0077     QModelIndex m_nextParentIndex;
0078     int m_nextRow;
0079 
0080     int m_depth;
0081     int maxDepth;
0082 
0083     friend class ModelInsertCommand;
0084     friend class ModelInsertWithDescendantsCommand;
0085     friend class ModelRemoveCommand;
0086     friend class ModelDataChangeCommand;
0087     friend class ModelMoveCommand;
0088     friend class ModelMoveLayoutChangeCommand;
0089     friend class ModelResetCommand;
0090     friend class ModelLayoutChangeCommand;
0091     //   friend class ModelSortIndexCommand;
0092     friend class ModelSortIndexLayoutChangeCommand;
0093     friend class ModelInsertAndRemoveQueuedCommand;
0094 };
0095 
0096 class PROXYMODELTESTSUITE_EXPORT ModelChangeCommand : public QObject
0097 {
0098     Q_OBJECT
0099 public:
0100     ModelChangeCommand(DynamicTreeModel *model, QObject *parent = nullptr);
0101 
0102     ~ModelChangeCommand() override
0103     {
0104     }
0105 
0106     void setAncestorRowNumbers(const QList<int> &rowNumbers)
0107     {
0108         m_rowNumbers = rowNumbers;
0109     }
0110     QList<int> srcAncestors() const
0111     {
0112         return m_rowNumbers;
0113     }
0114 
0115     QModelIndex findIndex(const QList<int> &rows) const;
0116 
0117     void setStartRow(int row)
0118     {
0119         m_startRow = row;
0120     }
0121 
0122     void setEndRow(int row)
0123     {
0124         m_endRow = row;
0125     }
0126 
0127     void setNumCols(int cols)
0128     {
0129         m_numCols = cols;
0130     }
0131 
0132     virtual void doCommand() = 0;
0133 
0134     QModelIndex parentIndex() const
0135     {
0136         return findIndex(m_rowNumbers);
0137     }
0138     int startRow() const
0139     {
0140         return m_startRow;
0141     }
0142     int endRow() const
0143     {
0144         return m_endRow;
0145     }
0146 
0147 protected:
0148     DynamicTreeModel *m_model;
0149     QList<int> m_rowNumbers;
0150     int m_startRow;
0151     int m_endRow;
0152     int m_numCols;
0153 };
0154 
0155 typedef QList<ModelChangeCommand *> ModelChangeCommandList;
0156 
0157 /**
0158   @brief Inserts a sub tree into the dynamictreemodel.
0159 
0160   As an alternative to setStartRow and setEndRow, the interpret command may be used.
0161 
0162   The interpret command is used to set the structure of the subtree.
0163 
0164   For example,
0165   @code
0166   cmd = new ModelInsertCommand(m_model, this);
0167   cmd->interpret(
0168     "- A"
0169     "- B"
0170     "- - C"
0171     "- D"
0172   );
0173   @endcode
0174 
0175   Will emit an insert for 3 rows, the second of which will have a child row. The interpretation
0176   string may be complex as long as it is valid. The text at the end of each row does not need to be consistent.
0177   There is a define DUMPTREE to make this command print the tree it inserts for better readability.
0178 
0179   @code
0180   cmd->interpret(
0181     "- A"
0182     "- - B"
0183     "- - C"
0184     "- - - C"
0185     "- - C"
0186     "- - - C"
0187     "- - - C"
0188     "- - C"
0189     "- D"
0190     "- - E"
0191     "- - F"
0192   );
0193   @endcode
0194 
0195   The string is valid if (depth of row (N + 1)) <= ( (depth of row N) + 1). For example, the following is invalid
0196   because the depth of B is 2 and the depth of A is 0.
0197 
0198   @code
0199   cmd->interpret(
0200     "- A"
0201     "- - - B"
0202     "- - C"
0203   @endcode
0204 */
0205 class PROXYMODELTESTSUITE_EXPORT ModelInsertCommand : public ModelChangeCommand
0206 {
0207     Q_OBJECT
0208 
0209     struct Token {
0210         enum Type {
0211             Branch,
0212             Leaf,
0213         };
0214         Type type;
0215         QString content;
0216     };
0217 
0218 public:
0219     explicit ModelInsertCommand(DynamicTreeModel *model, QObject *parent = nullptr);
0220     ~ModelInsertCommand() override
0221     {
0222     }
0223 
0224     void interpret(const QString &treeString);
0225 
0226     void doCommand() override;
0227     void doInsertTree(const QModelIndex &parent);
0228 
0229 protected:
0230     QList<Token> tokenize(const QString &treeString) const;
0231 
0232     QList<int> getDepths(const QString &treeString) const;
0233 
0234     QString m_treeString;
0235 };
0236 
0237 class PROXYMODELTESTSUITE_EXPORT ModelInsertAndRemoveQueuedCommand : public ModelChangeCommand
0238 {
0239     Q_OBJECT
0240 
0241 public:
0242     explicit ModelInsertAndRemoveQueuedCommand(DynamicTreeModel *model, QObject *parent = nullptr);
0243     ~ModelInsertAndRemoveQueuedCommand() override
0244     {
0245     }
0246 
0247     void doCommand() override;
0248 
0249 Q_SIGNALS:
0250     void beginInsertRows(const QModelIndex &parent, int start, int end);
0251     void endInsertRows();
0252     void beginRemoveRows(const QModelIndex &parent, int start, int end);
0253     void endRemoveRows();
0254 
0255 protected Q_SLOTS:
0256     void queuedBeginInsertRows(const QModelIndex &parent, int start, int end);
0257     void queuedEndInsertRows();
0258     void queuedBeginRemoveRows(const QModelIndex &parent, int start, int end);
0259     void queuedEndRemoveRows();
0260 
0261 protected:
0262     void purgeItem(qint64 parent);
0263 };
0264 
0265 class PROXYMODELTESTSUITE_EXPORT ModelRemoveCommand : public ModelChangeCommand
0266 {
0267     Q_OBJECT
0268 public:
0269     explicit ModelRemoveCommand(DynamicTreeModel *model, QObject *parent = nullptr);
0270     ~ModelRemoveCommand() override
0271     {
0272     }
0273 
0274     void doCommand() override;
0275 
0276     void purgeItem(qint64 parent);
0277 };
0278 
0279 class PROXYMODELTESTSUITE_EXPORT ModelDataChangeCommand : public ModelChangeCommand
0280 {
0281     Q_OBJECT
0282 public:
0283     explicit ModelDataChangeCommand(DynamicTreeModel *model, QObject *parent = nullptr);
0284 
0285     ~ModelDataChangeCommand() override
0286     {
0287     }
0288 
0289     void doCommand() override;
0290 
0291     void setStartColumn(int column)
0292     {
0293         m_startColumn = column;
0294     }
0295 
0296 protected:
0297     int m_startColumn;
0298 };
0299 
0300 class PROXYMODELTESTSUITE_EXPORT ModelMoveCommand : public ModelChangeCommand
0301 {
0302     Q_OBJECT
0303 public:
0304     explicit ModelMoveCommand(DynamicTreeModel *model, QObject *parent);
0305 
0306     ~ModelMoveCommand() override
0307     {
0308     }
0309 
0310     virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow);
0311 
0312     void doCommand() override;
0313 
0314     virtual void emitPostSignal();
0315 
0316     void setDestAncestors(const QList<int> &rows)
0317     {
0318         m_destRowNumbers = rows;
0319     }
0320     QList<int> destAncestors() const
0321     {
0322         return m_destRowNumbers;
0323     }
0324 
0325     void setDestRow(int row)
0326     {
0327         m_destRow = row;
0328     }
0329 
0330 protected:
0331     QList<int> m_destRowNumbers;
0332     int m_destRow;
0333 };
0334 
0335 class PROXYMODELTESTSUITE_EXPORT ModelMoveLayoutChangeCommand : public ModelMoveCommand
0336 {
0337     Q_OBJECT
0338 public:
0339     explicit ModelMoveLayoutChangeCommand(DynamicTreeModel *model, QObject *parent);
0340     ~ModelMoveLayoutChangeCommand() override;
0341 
0342     bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow) override;
0343 
0344     void emitPostSignal() override;
0345 
0346     void setEndOfMoveSourceAncestors(const QList<int> &rows)
0347     {
0348         m_endOfMoveSourceAncestors = rows;
0349     }
0350     void setEndOfMoveDestAncestors(const QList<int> &rows)
0351     {
0352         m_endOfMoveDestAncestors = rows;
0353     }
0354 
0355 private:
0356     QModelIndexList m_beforeMoveList;
0357     QList<int> m_endOfMoveSourceAncestors;
0358     QList<int> m_endOfMoveDestAncestors;
0359 };
0360 
0361 class PROXYMODELTESTSUITE_EXPORT ModelResetCommand : public ModelChangeCommand
0362 {
0363     Q_OBJECT
0364 public:
0365     ModelResetCommand(DynamicTreeModel *model, QObject *parent = nullptr);
0366     ~ModelResetCommand() override;
0367 
0368     void setInitialTree(const QString &treeString);
0369 
0370     void doCommand() override;
0371 
0372 private:
0373     QString m_treeString;
0374 };
0375 
0376 class PROXYMODELTESTSUITE_EXPORT ModelLayoutChangeCommand : public ModelChangeCommand
0377 {
0378     Q_OBJECT
0379 public:
0380     ModelLayoutChangeCommand(DynamicTreeModel *model, QObject *parent = nullptr);
0381     ~ModelLayoutChangeCommand() override;
0382 
0383     struct PersistentChange {
0384         QList<int> oldPath;
0385         QList<int> newPath;
0386     };
0387 
0388     void setPersistentChanges(const QList<PersistentChange> &changes);
0389 
0390     void setInitialTree(const QString &treeString);
0391 
0392     void doCommand() override;
0393 
0394 private:
0395     QString m_treeString;
0396     QList<PersistentChange> m_changes;
0397 };
0398 
0399 #endif