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