Lomiri
Loading...
Searching...
No Matches
qlimitproxymodelqml.cpp
1/*
2 * Copyright (C) 2012, 2013 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17// self
18#include "qlimitproxymodelqml.h"
19
20// Qt
21#include <QDebug>
22
23QLimitProxyModelQML::QLimitProxyModelQML(QObject *parent)
24 : QIdentityProxyModel(parent)
25 , m_limit(-1)
26 , m_sourceInserting(false)
27 , m_sourceRemoving(false)
28 , m_dataChangedBegin(-1)
29 , m_dataChangedEnd(-1)
30{
31 connect(this, &QLimitProxyModelQML::modelReset, this, &QLimitProxyModelQML::countChanged);
32 connect(this, &QLimitProxyModelQML::rowsInserted, this, &QLimitProxyModelQML::countChanged);
33 connect(this, &QLimitProxyModelQML::rowsRemoved, this, &QLimitProxyModelQML::countChanged);
34}
35
36QHash<int, QByteArray> QLimitProxyModelQML::roleNames() const
37{
38 return sourceModel() ? sourceModel()->roleNames() : QHash<int, QByteArray>();
39}
40
41void
42QLimitProxyModelQML::setModel(QAbstractItemModel *itemModel)
43{
44 if (itemModel != sourceModel()) {
45 if (sourceModel() != nullptr) {
46 sourceModel()->disconnect(this);
47 }
48
49 setSourceModel(itemModel);
50
51 if (sourceModel() != nullptr) {
52 // Disconnect the QIdentityProxyModel handling for rows removed/added...
53 disconnect(sourceModel(), &QAbstractItemModel::rowsAboutToBeInserted, this, nullptr);
54 disconnect(sourceModel(), &QAbstractItemModel::rowsInserted, this, nullptr);
55 disconnect(sourceModel(), &QAbstractItemModel::rowsAboutToBeRemoved, this, nullptr);
56 disconnect(sourceModel(), &QAbstractItemModel::rowsRemoved, this, nullptr);
57
58 // ... and use our own
59 connect(sourceModel(), &QAbstractItemModel::rowsAboutToBeInserted,
60 this, &QLimitProxyModelQML::sourceRowsAboutToBeInserted);
61 connect(sourceModel(), &QAbstractItemModel::rowsInserted,
62 this, &QLimitProxyModelQML::sourceRowsInserted);
63 connect(sourceModel(), &QAbstractItemModel::rowsAboutToBeRemoved,
64 this, &QLimitProxyModelQML::sourceRowsAboutToBeRemoved);
65 connect(sourceModel(), &QAbstractItemModel::rowsRemoved,
66 this, &QLimitProxyModelQML::sourceRowsRemoved);
67 }
68 Q_EMIT modelChanged();
69 }
70}
71
72int
73QLimitProxyModelQML::rowCount(const QModelIndex &parent) const
74{
75 if (parent.isValid()) // We are not a tree
76 return 0;
77
78 const int unlimitedCount = QIdentityProxyModel::rowCount(parent);
79 return m_limit < 0 ? unlimitedCount : qMin(m_limit, unlimitedCount);
80}
81
82int
83QLimitProxyModelQML::limit() const
84{
85 return m_limit;
86}
87
88void
89QLimitProxyModelQML::setLimit(int limit)
90{
91 if (limit != m_limit) {
92 bool inserting = false;
93 bool removing = false;
94 const int oldCount = rowCount();
95 const int unlimitedCount = QIdentityProxyModel::rowCount();
96 if (m_limit < 0) {
97 if (limit < oldCount) {
98 removing = true;
99 beginRemoveRows(QModelIndex(), limit, oldCount - 1);
100 }
101 } else if (limit < 0) {
102 if (m_limit < unlimitedCount) {
103 inserting = true;
104 beginInsertRows(QModelIndex(), m_limit, unlimitedCount - 1);
105 }
106 } else {
107 if (limit > m_limit && unlimitedCount > m_limit) {
108 inserting = true;
109 beginInsertRows(QModelIndex(), m_limit, qMin(limit, unlimitedCount) - 1);
110 } else if (limit < m_limit && limit < oldCount) {
111 removing = true;
112 beginRemoveRows(QModelIndex(), limit, oldCount - 1);
113 }
114 }
115
116 m_limit = limit;
117
118 if (inserting) {
119 endInsertRows();
120 } else if (removing) {
121 endRemoveRows();
122 }
123
124 Q_EMIT limitChanged();
125 }
126}
127
128void
129QLimitProxyModelQML::sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
130{
131 if (m_limit < 0) {
132 beginInsertRows(mapFromSource(parent), start, end);
133 m_sourceInserting = true;
134 } else if (start < m_limit) {
135 const int nSourceAddedItems = end - start + 1;
136 const int currentCount = QIdentityProxyModel::rowCount();
137 if (currentCount + nSourceAddedItems <= m_limit) {
138 // After Inserting items we will be under the limit
139 // so just proceed with the insertion normally
140 beginInsertRows(mapFromSource(parent), start, end);
141 m_sourceInserting = true;
142 } else if (currentCount >= m_limit) {
143 // We are already over the limit so to our users we are not adding items, just
144 // changing it's data, i.e we had something like
145 // A B C D E
146 // with a limit of 5
147 // after inserting (let's say three 'F' at position 1) we will have
148 // A F F F B
149 // so we just need to signal a dataChanged from 1 to 4
150 m_dataChangedBegin = start;
151 m_dataChangedEnd = m_limit - 1;
152 } else { // currentCount < m_limit && currentCount + nSourceAddedItems > m_limit
153 // We have less items than the limit but after adding them we will be over
154 // To our users this means we need to insert some items and change the
155 // data of some others, i.e we had something like
156 // A B C
157 // with a limit of 5
158 // after inserting (let's say three 'F' at position 1) we will have
159 // A F F F B
160 // so we need to signal an insetion from position 1 to 2, instead of from
161 // position 1 to 3 and a after that a data changed from 3 to 4
162 const int nItemsToInsert = m_limit - currentCount;
163 beginInsertRows(mapFromSource(parent), start, start + nItemsToInsert - 1);
164 m_sourceInserting = true;
165 m_dataChangedBegin = start + nItemsToInsert;
166 m_dataChangedEnd = m_limit - 1;
167 if (m_dataChangedBegin > m_dataChangedEnd) {
168 // Just in case we were empty and insert 6 items with a limit of 5
169 // We don't want to signal a dataChanged from 5 to 4
170 m_dataChangedBegin = -1;
171 m_dataChangedEnd = -1;
172 }
173 }
174 }
175}
176
177void
178QLimitProxyModelQML::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
179{
180 if (m_limit < 0) {
181 beginRemoveRows(mapFromSource(parent), start, end);
182 m_sourceRemoving = true;
183 } else if (start < m_limit) {
184 const int nSourceRemovedItems = end - start + 1;
185 const int currentCount = QIdentityProxyModel::rowCount();
186 if (currentCount <= m_limit) {
187 // We are already under the limit so
188 // so just proceed with the removal normally
189 beginRemoveRows(mapFromSource(parent), start, end);
190 m_sourceRemoving = true;
191 } else if (currentCount - nSourceRemovedItems >= m_limit) {
192 // Even after removing items we will be at or over the limit
193 // So to our users we are not removing anything, just changing the data
194 // i.e. we had a internal model with
195 // A B C D E F G H
196 // and a limit of 5, our users just see
197 // A B C D E
198 // so if we remove 3 items starting at 1 we have to expose
199 // A E F G H
200 // that is, a dataChanged from 1 to 4
201 m_dataChangedBegin = start;
202 m_dataChangedEnd = m_limit - 1;
203 } else { // currentCount > m_limit && currentCount - nSourceRemovedItems < m_limit
204 // We have more items than the limit but after removing we will be below it
205 // So to our users we both removing and changing the data
206 // i.e. we had a internal model with
207 // A B C D E F G
208 // and a limit of 5, our users just see
209 // A B C D E
210 // so if we remove items from 1 to 3 we have to expose
211 // A E F G
212 // that is, a remove from 4 to 4 and a dataChanged from 1 to 3
213 const int nItemsToRemove = m_limit - (currentCount - nSourceRemovedItems);
214 beginRemoveRows(mapFromSource(parent), m_limit - nItemsToRemove, m_limit - 1);
215 m_sourceRemoving = true;
216 m_dataChangedBegin = start;
217 m_dataChangedEnd = m_limit - nItemsToRemove - 1;
218 if (m_dataChangedBegin > m_dataChangedEnd) {
219 m_dataChangedBegin = -1;
220 m_dataChangedEnd = -1;
221 }
222 }
223 }
224}
225
226void
227QLimitProxyModelQML::sourceRowsInserted(const QModelIndex & /*parent*/, int /*start*/, int /*end*/)
228{
229 if (m_sourceInserting) {
230 endInsertRows();
231 m_sourceInserting = false;
232 }
233 if (m_dataChangedBegin != -1 && m_dataChangedEnd != -1) {
234 dataChanged(index(m_dataChangedBegin, 0), index(m_dataChangedEnd, 0));
235 m_dataChangedBegin = -1;
236 m_dataChangedEnd = -1;
237 }
238}
239
240void
241QLimitProxyModelQML::sourceRowsRemoved(const QModelIndex & /*parent*/, int /*start*/, int /*end*/)
242{
243 if (m_sourceRemoving) {
244 endRemoveRows();
245 m_sourceRemoving = false;
246 }
247 if (m_dataChangedBegin != -1 && m_dataChangedEnd != -1) {
248 dataChanged(index(m_dataChangedBegin, 0), index(m_dataChangedEnd, 0));
249 m_dataChangedBegin = -1;
250 m_dataChangedEnd = -1;
251 }
252}