File indexing completed on 2024-05-05 03:56:44
0001 /* 0002 SPDX-FileCopyrightText: 2009 Stephen Kelly <steveire@gmail.com> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #ifndef KSELECTIONPROXYMODEL_H 0008 #define KSELECTIONPROXYMODEL_H 0009 0010 #include <QAbstractProxyModel> 0011 #include <QItemSelectionModel> 0012 0013 #include "kitemmodels_export.h" 0014 0015 #include <memory> 0016 0017 class KSelectionProxyModelPrivate; 0018 0019 /** 0020 @class KSelectionProxyModel kselectionproxymodel.h KSelectionProxyModel 0021 0022 @brief A Proxy Model which presents a subset of its source model to observers. 0023 0024 The KSelectionProxyModel is most useful as a convenience for displaying the selection in one view in 0025 another view. The selectionModel of the initial view is used to create a proxied model which is filtered 0026 based on the configuration of this class. 0027 0028 For example, when a user clicks a mail folder in one view in an email application, the contained emails 0029 should be displayed in another view. 0030 0031 This takes away the need for the developer to handle the selection between the views, including all the 0032 mapToSource, mapFromSource and setRootIndex calls. 0033 0034 @code 0035 MyModel *sourceModel = new MyModel(this); 0036 QTreeView *leftView = new QTreeView(this); 0037 leftView->setModel(sourceModel); 0038 0039 KSelectionProxyModel *selectionProxy = new KSelectionProxyModel(leftView->selectionModel(), this); 0040 selectionProxy->setSourceModel(sourceModel); 0041 0042 QTreeView *rightView = new QTreeView(this); 0043 rightView->setModel(selectionProxy); 0044 @endcode 0045 0046 \image html selectionproxymodelsimpleselection.png "A Selection in one view creating a model for use with another view." 0047 0048 The KSelectionProxyModel can handle complex selections. 0049 0050 \image html selectionproxymodelmultipleselection.png "Non-contiguous selection creating a new simple model in a second view." 0051 0052 The contents of the secondary view depends on the selection in the primary view, and the configuration of the proxy model. 0053 See KSelectionProxyModel::setFilterBehavior for the different possible configurations. 0054 0055 For example, if the filterBehavior is SubTrees, selecting another item in an already selected subtree has no effect. 0056 0057 \image html selectionproxymodelmultipleselection-withdescendant.png "Selecting an item and its descendant." 0058 0059 See the test application in tests/proxymodeltestapp to try out the valid configurations. 0060 0061 \image html kselectionproxymodel-testapp.png "KSelectionProxyModel test application" 0062 0063 Obviously, the KSelectionProxyModel may be used in a view, or further processed with other proxy models. 0064 See KAddressBook and AkonadiConsole in kdepim for examples which use a further KDescendantsProxyModel 0065 and QSortFilterProxyModel on top of a KSelectionProxyModel. 0066 0067 Additionally, this class can be used to programmatically choose some items from the source model to display in the view. For example, 0068 this is how the Favourite Folder View in KMail works, and is also used in unit testing. 0069 0070 See also: https://doc.qt.io/qt-5/model-view-programming.html#proxy-models 0071 0072 @since 4.4 0073 @author Stephen Kelly <steveire@gmail.com> 0074 0075 */ 0076 class KITEMMODELS_EXPORT KSelectionProxyModel : public QAbstractProxyModel 0077 { 0078 Q_OBJECT 0079 Q_PROPERTY(FilterBehavior filterBehavior READ filterBehavior WRITE setFilterBehavior NOTIFY filterBehaviorChanged) 0080 Q_PROPERTY(QItemSelectionModel *selectionModel READ selectionModel WRITE setSelectionModel NOTIFY selectionModelChanged) 0081 public: 0082 /** 0083 ctor. 0084 0085 @p selectionModel The selection model used to filter what is presented by the proxy. 0086 */ 0087 // KF6: Remove the selectionModel from the constructor here. 0088 explicit KSelectionProxyModel(QItemSelectionModel *selectionModel, QObject *parent = nullptr); 0089 0090 /** 0091 Default constructor. Allow the creation of a KSelectionProxyModel in QML 0092 code. QML will assign a parent after construction. 0093 */ 0094 // KF6: Remove in favor of the constructor above. 0095 explicit KSelectionProxyModel(); 0096 0097 /** 0098 dtor 0099 */ 0100 ~KSelectionProxyModel() override; 0101 0102 /** 0103 reimp. 0104 */ 0105 void setSourceModel(QAbstractItemModel *sourceModel) override; 0106 0107 QItemSelectionModel *selectionModel() const; 0108 void setSelectionModel(QItemSelectionModel *selectionModel); 0109 0110 enum FilterBehavior { 0111 SubTrees, 0112 SubTreeRoots, 0113 SubTreesWithoutRoots, 0114 ExactSelection, 0115 ChildrenOfExactSelection, 0116 InvalidBehavior, 0117 }; 0118 Q_ENUM(FilterBehavior) 0119 0120 /** 0121 Set the filter behaviors of this model. 0122 The filter behaviors of the model govern the content of the model based on the selection of the contained QItemSelectionModel. 0123 0124 See kdeui/proxymodeltestapp to try out the different proxy model behaviors. 0125 0126 The most useful behaviors are SubTrees, ExactSelection and ChildrenOfExactSelection. 0127 0128 The default behavior is SubTrees. This means that this proxy model will contain the roots of the items in the source model. 0129 Any descendants which are also selected have no additional effect. 0130 For example if the source model is like: 0131 0132 @verbatim 0133 (root) 0134 - A 0135 - B 0136 - C 0137 - D 0138 - E 0139 - F 0140 - G 0141 - H 0142 - I 0143 - J 0144 - K 0145 - L 0146 @endverbatim 0147 0148 And A, B, C and D are selected, the proxy will contain: 0149 0150 @verbatim 0151 (root) 0152 - A 0153 - B 0154 - C 0155 - D 0156 - E 0157 - F 0158 - G 0159 @endverbatim 0160 0161 That is, selecting 'D' or 'C' if 'B' is also selected has no effect. If 'B' is de-selected, then 'C' amd 'D' become top-level items: 0162 0163 @verbatim 0164 (root) 0165 - A 0166 - C 0167 - D 0168 - E 0169 - F 0170 - G 0171 @endverbatim 0172 0173 This is the behavior used by KJots when rendering books. 0174 0175 If the behavior is set to SubTreeRoots, then the children of selected indexes are not part of the model. If 'A', 'B' and 'D' are selected, 0176 0177 @verbatim 0178 (root) 0179 - A 0180 - B 0181 @endverbatim 0182 0183 Note that although 'D' is selected, it is not part of the proxy model, because its parent 'B' is already selected. 0184 0185 SubTreesWithoutRoots has the effect of not making the selected items part of the model, but making their children part of the model instead. If 'A', 'B' 0186 and 'I' are selected: 0187 0188 @verbatim 0189 (root) 0190 - C 0191 - D 0192 - E 0193 - F 0194 - G 0195 - J 0196 - K 0197 - L 0198 @endverbatim 0199 0200 Note that 'A' has no children, so selecting it has no outward effect on the model. 0201 0202 ChildrenOfExactSelection causes the proxy model to contain the children of the selected indexes,but further descendants are omitted. 0203 Additionally, if descendants of an already selected index are selected, their children are part of the proxy model. 0204 For example, if 'A', 'B', 'D' and 'I' are selected: 0205 0206 @verbatim 0207 (root) 0208 - C 0209 - D 0210 - E 0211 - G 0212 - J 0213 - K 0214 - L 0215 @endverbatim 0216 0217 This would be useful for example if showing containers (for example maildirs) in one view and their items in another. Sub-maildirs would still appear in 0218 the proxy, but could be filtered out using a QSortfilterProxyModel. 0219 0220 The ExactSelection behavior causes the selected items to be part of the proxy model, even if their ancestors are already selected, but children of 0221 selected items are not included. 0222 0223 Again, if 'A', 'B', 'D' and 'I' are selected: 0224 0225 @verbatim 0226 (root) 0227 - A 0228 - B 0229 - D 0230 - I 0231 @endverbatim 0232 0233 This is the behavior used by the Favourite Folder View in KMail. 0234 0235 */ 0236 void setFilterBehavior(FilterBehavior behavior); 0237 FilterBehavior filterBehavior() const; 0238 0239 QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override; 0240 QModelIndex mapToSource(const QModelIndex &proxyIndex) const override; 0241 0242 QItemSelection mapSelectionFromSource(const QItemSelection &selection) const override; 0243 QItemSelection mapSelectionToSource(const QItemSelection &selection) const override; 0244 0245 Qt::ItemFlags flags(const QModelIndex &index) const override; 0246 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; 0247 int rowCount(const QModelIndex &parent = QModelIndex()) const override; 0248 QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; 0249 0250 QMimeData *mimeData(const QModelIndexList &indexes) const override; 0251 QStringList mimeTypes() const override; 0252 Qt::DropActions supportedDropActions() const override; 0253 bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; 0254 0255 bool hasChildren(const QModelIndex &parent = QModelIndex()) const override; 0256 QModelIndex index(int, int, const QModelIndex & = QModelIndex()) const override; 0257 QModelIndex parent(const QModelIndex &) const override; 0258 int columnCount(const QModelIndex & = QModelIndex()) const override; 0259 0260 virtual QModelIndexList match(const QModelIndex &start, 0261 int role, 0262 const QVariant &value, 0263 int hits = 1, 0264 Qt::MatchFlags flags = Qt::MatchFlags(Qt::MatchStartsWith | Qt::MatchWrap)) const override; 0265 0266 Q_SIGNALS: 0267 /** 0268 @internal 0269 Emitted before @p removeRootIndex, an index in the sourceModel is removed from 0270 the root selected indexes. This may be unrelated to rows removed from the model, 0271 depending on configuration. 0272 */ 0273 void rootIndexAboutToBeRemoved(const QModelIndex &removeRootIndex, QPrivateSignal); 0274 0275 /** 0276 @internal 0277 Emitted when @p newIndex, an index in the sourceModel is added to the root selected 0278 indexes. This may be unrelated to rows inserted to the model, 0279 depending on configuration. 0280 */ 0281 void rootIndexAdded(const QModelIndex &newIndex, QPrivateSignal); 0282 0283 /** 0284 @internal 0285 Emitted before @p selection, a selection in the sourceModel, is removed from 0286 the root selection. 0287 */ 0288 void rootSelectionAboutToBeRemoved(const QItemSelection &selection, QPrivateSignal); 0289 0290 /** 0291 @internal 0292 Emitted after @p selection, a selection in the sourceModel, is added to 0293 the root selection. 0294 */ 0295 void rootSelectionAdded(const QItemSelection &selection, QPrivateSignal); 0296 0297 void selectionModelChanged(QPrivateSignal); 0298 void filterBehaviorChanged(QPrivateSignal); 0299 0300 protected: 0301 QList<QPersistentModelIndex> sourceRootIndexes() const; 0302 0303 private: 0304 Q_DECLARE_PRIVATE(KSelectionProxyModel) 0305 //@cond PRIVATE 0306 std::unique_ptr<KSelectionProxyModelPrivate> const d_ptr; 0307 0308 Q_PRIVATE_SLOT(d_func(), void sourceRowsAboutToBeInserted(const QModelIndex &, int, int)) 0309 Q_PRIVATE_SLOT(d_func(), void sourceRowsInserted(const QModelIndex &, int, int)) 0310 Q_PRIVATE_SLOT(d_func(), void sourceRowsAboutToBeRemoved(const QModelIndex &, int, int)) 0311 Q_PRIVATE_SLOT(d_func(), void sourceRowsRemoved(const QModelIndex &, int, int)) 0312 Q_PRIVATE_SLOT(d_func(), void sourceRowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)) 0313 Q_PRIVATE_SLOT(d_func(), void sourceRowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)) 0314 Q_PRIVATE_SLOT(d_func(), void sourceModelAboutToBeReset()) 0315 Q_PRIVATE_SLOT(d_func(), void sourceModelReset()) 0316 Q_PRIVATE_SLOT(d_func(), void sourceLayoutAboutToBeChanged()) 0317 Q_PRIVATE_SLOT(d_func(), void sourceLayoutChanged()) 0318 Q_PRIVATE_SLOT(d_func(), void sourceDataChanged(const QModelIndex &, const QModelIndex &)) 0319 Q_PRIVATE_SLOT(d_func(), void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)) 0320 Q_PRIVATE_SLOT(d_func(), void sourceModelDestroyed()) 0321 0322 //@endcond 0323 }; 0324 0325 #endif