1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtCore module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | /* |
41 | A simple model that uses a QStringList as its data source. |
42 | */ |
43 | |
44 | #include "qstringlistmodel.h" |
45 | |
46 | #include <QtCore/qlist.h> |
47 | |
48 | #include <algorithm> |
49 | |
50 | QT_BEGIN_NAMESPACE |
51 | |
52 | /*! |
53 | \class QStringListModel |
54 | \inmodule QtCore |
55 | \brief The QStringListModel class provides a model that supplies strings to views. |
56 | |
57 | \ingroup model-view |
58 | |
59 | QStringListModel is an editable model that can be used for simple |
60 | cases where you need to display a number of strings in a view |
61 | widget, such as a QListView or a QComboBox. |
62 | |
63 | The model provides all the standard functions of an editable |
64 | model, representing the data in the string list as a model with |
65 | one column and a number of rows equal to the number of items in |
66 | the list. |
67 | |
68 | Model indexes corresponding to items are obtained with the |
69 | \l{QAbstractListModel::index()}{index()} function, and item flags |
70 | are obtained with flags(). Item data is read with the data() |
71 | function and written with setData(). The number of rows (and |
72 | number of items in the string list) can be found with the |
73 | rowCount() function. |
74 | |
75 | The model can be constructed with an existing string list, or |
76 | strings can be set later with the setStringList() convenience |
77 | function. Strings can also be inserted in the usual way with the |
78 | insertRows() function, and removed with removeRows(). The contents |
79 | of the string list can be retrieved with the stringList() |
80 | convenience function. |
81 | |
82 | An example usage of QStringListModel: |
83 | |
84 | \snippet qstringlistmodel/main.cpp 0 |
85 | |
86 | \sa QAbstractListModel, QAbstractItemModel, {Model Classes} |
87 | */ |
88 | |
89 | /*! |
90 | Constructs a string list model with the given \a parent. |
91 | */ |
92 | |
93 | QStringListModel::QStringListModel(QObject *parent) |
94 | : QAbstractListModel(parent) |
95 | { |
96 | } |
97 | |
98 | /*! |
99 | Constructs a string list model containing the specified \a strings |
100 | with the given \a parent. |
101 | */ |
102 | |
103 | QStringListModel::QStringListModel(const QStringList &strings, QObject *parent) |
104 | : QAbstractListModel(parent), lst(strings) |
105 | { |
106 | } |
107 | |
108 | /*! |
109 | Returns the number of rows in the model. This value corresponds to the |
110 | number of items in the model's internal string list. |
111 | |
112 | The optional \a parent argument is in most models used to specify |
113 | the parent of the rows to be counted. Because this is a list if a |
114 | valid parent is specified, the result will always be 0. |
115 | |
116 | \sa insertRows(), removeRows(), QAbstractItemModel::rowCount() |
117 | */ |
118 | |
119 | int QStringListModel::rowCount(const QModelIndex &parent) const |
120 | { |
121 | if (parent.isValid()) |
122 | return 0; |
123 | |
124 | return lst.count(); |
125 | } |
126 | |
127 | /*! |
128 | \reimp |
129 | */ |
130 | QModelIndex QStringListModel::sibling(int row, int column, const QModelIndex &idx) const |
131 | { |
132 | if (!idx.isValid() || column != 0 || row >= lst.count() || row < 0) |
133 | return QModelIndex(); |
134 | |
135 | return createIndex(row, 0); |
136 | } |
137 | |
138 | /*! |
139 | \reimp |
140 | \since 5.13 |
141 | */ |
142 | QMap<int, QVariant> QStringListModel::itemData(const QModelIndex &index) const |
143 | { |
144 | if (!checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::ParentIsInvalid)) |
145 | return QMap<int, QVariant>{}; |
146 | const QVariant displayData = lst.at(index.row()); |
147 | return QMap<int, QVariant>{{ |
148 | std::make_pair<int>(Qt::DisplayRole, displayData), |
149 | std::make_pair<int>(Qt::EditRole, displayData) |
150 | }}; |
151 | } |
152 | |
153 | /*! |
154 | \reimp |
155 | \since 5.13 |
156 | If \a roles contains both Qt::DisplayRole and Qt::EditRole, the latter will take precedence |
157 | */ |
158 | bool QStringListModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles) |
159 | { |
160 | if (roles.isEmpty()) |
161 | return false; |
162 | if (std::any_of(roles.keyBegin(), roles.keyEnd(), [](int role) -> bool { |
163 | return role != Qt::DisplayRole && role != Qt::EditRole; |
164 | })) { |
165 | return false; |
166 | } |
167 | auto roleIter = roles.constFind(Qt::EditRole); |
168 | if (roleIter == roles.constEnd()) |
169 | roleIter = roles.constFind(Qt::DisplayRole); |
170 | Q_ASSERT(roleIter != roles.constEnd()); |
171 | return setData(index, roleIter.value(), roleIter.key()); |
172 | } |
173 | |
174 | /*! |
175 | Returns data for the specified \a role, from the item with the |
176 | given \a index. |
177 | |
178 | If the view requests an invalid index, an invalid variant is returned. |
179 | |
180 | \sa setData() |
181 | */ |
182 | |
183 | QVariant QStringListModel::data(const QModelIndex &index, int role) const |
184 | { |
185 | if (index.row() < 0 || index.row() >= lst.size()) |
186 | return QVariant(); |
187 | |
188 | if (role == Qt::DisplayRole || role == Qt::EditRole) |
189 | return lst.at(index.row()); |
190 | |
191 | return QVariant(); |
192 | } |
193 | |
194 | /*! |
195 | Returns the flags for the item with the given \a index. |
196 | |
197 | Valid items are enabled, selectable, editable, drag enabled and drop enabled. |
198 | |
199 | \sa QAbstractItemModel::flags() |
200 | */ |
201 | |
202 | Qt::ItemFlags QStringListModel::flags(const QModelIndex &index) const |
203 | { |
204 | if (!index.isValid()) |
205 | return QAbstractListModel::flags(index) | Qt::ItemIsDropEnabled; |
206 | |
207 | return QAbstractListModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; |
208 | } |
209 | |
210 | /*! |
211 | Sets the data for the specified \a role in the item with the given |
212 | \a index in the model, to the provided \a value. |
213 | |
214 | The dataChanged() signal is emitted if the item is changed. |
215 | Returns \c true after emitting the dataChanged() signal. |
216 | |
217 | \sa Qt::ItemDataRole, data() |
218 | */ |
219 | |
220 | bool QStringListModel::setData(const QModelIndex &index, const QVariant &value, int role) |
221 | { |
222 | if (index.row() >= 0 && index.row() < lst.size() |
223 | && (role == Qt::EditRole || role == Qt::DisplayRole)) { |
224 | const QString valueString = value.toString(); |
225 | if (lst.at(index.row()) == valueString) |
226 | return true; |
227 | lst.replace(index.row(), valueString); |
228 | emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole}); |
229 | return true; |
230 | } |
231 | return false; |
232 | } |
233 | |
234 | /*! |
235 | \reimp |
236 | \since 6.0 |
237 | */ |
238 | bool QStringListModel::clearItemData(const QModelIndex &index) |
239 | { |
240 | return setData(index, QVariant(), Qt::EditRole); |
241 | } |
242 | |
243 | /*! |
244 | Inserts \a count rows into the model, beginning at the given \a row. |
245 | |
246 | The \a parent index of the rows is optional and is only used for |
247 | consistency with QAbstractItemModel. By default, a null index is |
248 | specified, indicating that the rows are inserted in the top level of |
249 | the model. |
250 | |
251 | Returns \c true if the insertion was successful. |
252 | |
253 | \sa QAbstractItemModel::insertRows() |
254 | */ |
255 | |
256 | bool QStringListModel::insertRows(int row, int count, const QModelIndex &parent) |
257 | { |
258 | if (count < 1 || row < 0 || row > rowCount(parent)) |
259 | return false; |
260 | |
261 | beginInsertRows(QModelIndex(), row, row + count - 1); |
262 | |
263 | for (int r = 0; r < count; ++r) |
264 | lst.insert(row, QString()); |
265 | |
266 | endInsertRows(); |
267 | |
268 | return true; |
269 | } |
270 | |
271 | /*! |
272 | Removes \a count rows from the model, beginning at the given \a row. |
273 | |
274 | The \a parent index of the rows is optional and is only used for |
275 | consistency with QAbstractItemModel. By default, a null index is |
276 | specified, indicating that the rows are removed in the top level of |
277 | the model. |
278 | |
279 | Returns \c true if the row removal was successful. |
280 | |
281 | \sa QAbstractItemModel::removeRows() |
282 | */ |
283 | |
284 | bool QStringListModel::removeRows(int row, int count, const QModelIndex &parent) |
285 | { |
286 | if (count <= 0 || row < 0 || (row + count) > rowCount(parent)) |
287 | return false; |
288 | |
289 | beginRemoveRows(QModelIndex(), row, row + count - 1); |
290 | |
291 | const auto it = lst.begin() + row; |
292 | lst.erase(it, it + count); |
293 | |
294 | endRemoveRows(); |
295 | |
296 | return true; |
297 | } |
298 | |
299 | /*! |
300 | \since 5.13 |
301 | \reimp |
302 | */ |
303 | bool QStringListModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) |
304 | { |
305 | if (sourceRow < 0 |
306 | || sourceRow + count - 1 >= rowCount(sourceParent) |
307 | || destinationChild < 0 |
308 | || destinationChild > rowCount(destinationParent) |
309 | || sourceRow == destinationChild |
310 | || sourceRow == destinationChild - 1 |
311 | || count <= 0 |
312 | || sourceParent.isValid() |
313 | || destinationParent.isValid()) { |
314 | return false; |
315 | } |
316 | if (!beginMoveRows(QModelIndex(), sourceRow, sourceRow + count - 1, QModelIndex(), destinationChild)) |
317 | return false; |
318 | |
319 | int fromRow = sourceRow; |
320 | if (destinationChild < sourceRow) |
321 | fromRow += count - 1; |
322 | else |
323 | destinationChild--; |
324 | while (count--) |
325 | lst.move(fromRow, destinationChild); |
326 | endMoveRows(); |
327 | return true; |
328 | } |
329 | |
330 | static bool ascendingLessThan(const QPair<QString, int> &s1, const QPair<QString, int> &s2) |
331 | { |
332 | return s1.first < s2.first; |
333 | } |
334 | |
335 | static bool decendingLessThan(const QPair<QString, int> &s1, const QPair<QString, int> &s2) |
336 | { |
337 | return s1.first > s2.first; |
338 | } |
339 | |
340 | /*! |
341 | \reimp |
342 | */ |
343 | void QStringListModel::sort(int, Qt::SortOrder order) |
344 | { |
345 | emit layoutAboutToBeChanged(QList<QPersistentModelIndex>(), VerticalSortHint); |
346 | |
347 | QList<QPair<QString, int>> list; |
348 | const int lstCount = lst.count(); |
349 | list.reserve(lstCount); |
350 | for (int i = 0; i < lstCount; ++i) |
351 | list.append(QPair<QString, int>(lst.at(i), i)); |
352 | |
353 | if (order == Qt::AscendingOrder) |
354 | std::sort(list.begin(), list.end(), ascendingLessThan); |
355 | else |
356 | std::sort(list.begin(), list.end(), decendingLessThan); |
357 | |
358 | lst.clear(); |
359 | QList<int> forwarding(lstCount); |
360 | for (int i = 0; i < lstCount; ++i) { |
361 | lst.append(list.at(i).first); |
362 | forwarding[list.at(i).second] = i; |
363 | } |
364 | |
365 | QModelIndexList oldList = persistentIndexList(); |
366 | QModelIndexList newList; |
367 | const int numOldIndexes = oldList.count(); |
368 | newList.reserve(numOldIndexes); |
369 | for (int i = 0; i < numOldIndexes; ++i) |
370 | newList.append(index(forwarding.at(oldList.at(i).row()), 0)); |
371 | changePersistentIndexList(oldList, newList); |
372 | |
373 | emit layoutChanged(QList<QPersistentModelIndex>(), VerticalSortHint); |
374 | } |
375 | |
376 | /*! |
377 | Returns the string list used by the model to store data. |
378 | */ |
379 | QStringList QStringListModel::stringList() const |
380 | { |
381 | return lst; |
382 | } |
383 | |
384 | /*! |
385 | Sets the model's internal string list to \a strings. The model will |
386 | notify any attached views that its underlying data has changed. |
387 | |
388 | \sa dataChanged() |
389 | */ |
390 | void QStringListModel::setStringList(const QStringList &strings) |
391 | { |
392 | beginResetModel(); |
393 | lst = strings; |
394 | endResetModel(); |
395 | } |
396 | |
397 | /*! |
398 | \reimp |
399 | */ |
400 | Qt::DropActions QStringListModel::supportedDropActions() const |
401 | { |
402 | return QAbstractItemModel::supportedDropActions() | Qt::MoveAction; |
403 | } |
404 | |
405 | QT_END_NAMESPACE |
406 | |
407 | #include "moc_qstringlistmodel.cpp" |
408 | |