1 /* 2 SPDX-FileCopyrightText: 2009 Stephen Kelly <steveire@gmail.com> 3 4 SPDX-License-Identifier: LGPL-2.0-or-later 5 */ 6 7 #ifndef KREPARENTINGPROXYMODEL_H 8 #define KREPARENTINGPROXYMODEL_H 9 10 #include <QAbstractProxyModel> 11 12 class KReparentingProxyModelPrivate; 13 14 /** 15 @brief Restructures a source model, changing the parents of items. 16 17 Subclasses can change the structure of a source model by reimplementing 18 the isDescendantOf method. 19 20 For example, if the source model is a list, 21 22 @verbatim 23 0 24 - A 25 - B 26 - C 27 - D 28 - E 29 @endverbatim 30 31 It could be converted to a tree by an implementation something like: 32 33 @code 34 bool MyReparentingModel::isDescedantOf(const QModelIndex& ancestor, const QModelIndex& descendant ) const 35 { 36 return ( 37 (ancestor.data().toString() == "A" && descendant.data().toString() == "B") 38 || (ancestor.data().toString() == "A" && descendant.data().toString() == "C") 39 || (ancestor.data().toString() == "B" && descendant.data().toString() == "C") 40 || (ancestor.data().toString() == "A" && descendant.data().toString() == "D") 41 ) 42 ? true : KReparentingProxyModel::isDescendantOf(ancestor, descendant); 43 } 44 @endcode 45 46 to get this result: 47 48 @verbatim 49 0 50 - A 51 - - B 52 - - - C 53 - - D 54 - E 55 @endverbatim 56 57 Note that the implementation returns true for a query if "C" is a descendant of "A". 58 The implementation must return the correct value for all of its descendants, not only its direct parent. 59 The actual location to insert the descendant in the tree is determined internally by a binary find algorithm. 60 61 The KReparentingProxyModel performs movement of items in the left-right directions, but not the up-down directions. 62 63 \image html reparenting1.png "KReparentingProxyModel moving rows left to right" 64 65 \image html reparenting2.png "KReparentingProxyModel can not move items both left-right and up-down" 66 67 Reordering the rows in a model is the domain of QSortFilterProxyModel. An intermediate QSortFilterProxyModel 68 can be used to achieve the desired result. 69 70 \image html reparenting3.png "KReparentingProxyModel and QSortFilterProxyModel working in concert to move items both left-right and up-down" 71 72 @code 73 QAbstractItemModel *model = getModel(); 74 75 QSortFilterProxyModel *sorter = getSorter(); 76 sorter->setSourceModel(model); 77 78 KReparentingProxyModel *reparenter = getReparenter(); 79 reparenter->setSourceModel(sorter); 80 81 QTreeView *view = getView(); 82 view->setModel(reparenter); 83 @endcode 84 85 */ 86 class KReparentingProxyModel : public QAbstractProxyModel 87 { 88 Q_OBJECT 89 public: 90 KReparentingProxyModel(QObject *parent = nullptr); 91 92 ~KReparentingProxyModel() override; 93 94 QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override; 95 96 QModelIndex mapToSource(const QModelIndex &proxyIndex) const override; 97 98 void setSourceModel(QAbstractItemModel *sourceModel) override; 99 100 /** 101 Reimplement this to return whether @p descendant is a descendant of @p ancestor. 102 */ 103 virtual bool isDescendantOf(const QModelIndex &ancestor, const QModelIndex &descendant) const; 104 105 int columnCount(const QModelIndex &parent = QModelIndex()) const override; 106 107 QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const override; 108 109 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; 110 111 QModelIndex parent(const QModelIndex &child) const override; 112 113 int rowCount(const QModelIndex &parent = QModelIndex()) const override; 114 115 bool hasChildren(const QModelIndex &parent = QModelIndex()) const override; 116 117 Qt::DropActions supportedDropActions() const override; 118 119 protected: 120 void beginChangeRule(); 121 void endChangeRule(); 122 123 private: 124 Q_DECLARE_PRIVATE(KReparentingProxyModel) 125 //@cond PRIVATE 126 KReparentingProxyModelPrivate *d_ptr; 127 128 Q_PRIVATE_SLOT(d_func(), void sourceRowsAboutToBeInserted(const QModelIndex &, int, int)) 129 Q_PRIVATE_SLOT(d_func(), void sourceRowsInserted(const QModelIndex &, int, int)) 130 Q_PRIVATE_SLOT(d_func(), void sourceRowsAboutToBeRemoved(const QModelIndex &, int, int)) 131 Q_PRIVATE_SLOT(d_func(), void sourceRowsRemoved(const QModelIndex &, int, int)) 132 Q_PRIVATE_SLOT(d_func(), void sourceRowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)) 133 Q_PRIVATE_SLOT(d_func(), void sourceRowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)) 134 Q_PRIVATE_SLOT(d_func(), void sourceModelAboutToBeReset()) 135 Q_PRIVATE_SLOT(d_func(), void sourceModelReset()) 136 Q_PRIVATE_SLOT(d_func(), void sourceLayoutAboutToBeChanged()) 137 Q_PRIVATE_SLOT(d_func(), void sourceLayoutChanged()) 138 Q_PRIVATE_SLOT(d_func(), void sourceDataChanged(const QModelIndex &, const QModelIndex &)) 139 140 //@endcond 141 }; 142 143 #endif 144