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/qvector.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(arow: row, acolumn: 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, options: CheckIndexOption::IndexIsValid | CheckIndexOption::ParentIsInvalid)) |
145 | return QMap<int, QVariant>{}; |
146 | const QVariant displayData = lst.at(i: index.row()); |
147 | return QMap<int, QVariant>{{ |
148 | std::make_pair<int>(x: Qt::DisplayRole, y: displayData), |
149 | std::make_pair<int>(x: Qt::EditRole, y: 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(first: roles.keyBegin(), last: roles.keyEnd(), pred: [](int role) -> bool { |
163 | return role != Qt::DisplayRole && role != Qt::EditRole; |
164 | })) { |
165 | return false; |
166 | } |
167 | auto roleIter = roles.constFind(akey: Qt::EditRole); |
168 | if (roleIter == roles.constEnd()) |
169 | roleIter = roles.constFind(akey: Qt::DisplayRole); |
170 | Q_ASSERT(roleIter != roles.constEnd()); |
171 | return setData(index, value: roleIter.value(), role: 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(i: 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(i: index.row()) == valueString) |
226 | return true; |
227 | lst.replace(i: index.row(), t: valueString); |
228 | emit dataChanged(topLeft: index, bottomRight: index, roles: {Qt::DisplayRole, Qt::EditRole}); |
229 | return true; |
230 | } |
231 | return false; |
232 | } |
233 | |
234 | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) |
235 | /*! |
236 | \reimp |
237 | \since 6.0 |
238 | */ |
239 | bool QStringListModel::clearItemData(const QModelIndex &index) |
240 | { |
241 | return setData(index, QVariant(), Qt::EditRole); |
242 | } |
243 | #endif |
244 | |
245 | /*! |
246 | Inserts \a count rows into the model, beginning at the given \a row. |
247 | |
248 | The \a parent index of the rows is optional and is only used for |
249 | consistency with QAbstractItemModel. By default, a null index is |
250 | specified, indicating that the rows are inserted in the top level of |
251 | the model. |
252 | |
253 | Returns \c true if the insertion was successful. |
254 | |
255 | \sa QAbstractItemModel::insertRows() |
256 | */ |
257 | |
258 | bool QStringListModel::insertRows(int row, int count, const QModelIndex &parent) |
259 | { |
260 | if (count < 1 || row < 0 || row > rowCount(parent)) |
261 | return false; |
262 | |
263 | beginInsertRows(parent: QModelIndex(), first: row, last: row + count - 1); |
264 | |
265 | for (int r = 0; r < count; ++r) |
266 | lst.insert(i: row, t: QString()); |
267 | |
268 | endInsertRows(); |
269 | |
270 | return true; |
271 | } |
272 | |
273 | /*! |
274 | Removes \a count rows from the model, beginning at the given \a row. |
275 | |
276 | The \a parent index of the rows is optional and is only used for |
277 | consistency with QAbstractItemModel. By default, a null index is |
278 | specified, indicating that the rows are removed in the top level of |
279 | the model. |
280 | |
281 | Returns \c true if the row removal was successful. |
282 | |
283 | \sa QAbstractItemModel::removeRows() |
284 | */ |
285 | |
286 | bool QStringListModel::removeRows(int row, int count, const QModelIndex &parent) |
287 | { |
288 | if (count <= 0 || row < 0 || (row + count) > rowCount(parent)) |
289 | return false; |
290 | |
291 | beginRemoveRows(parent: QModelIndex(), first: row, last: row + count - 1); |
292 | |
293 | const auto it = lst.begin() + row; |
294 | lst.erase(afirst: it, alast: it + count); |
295 | |
296 | endRemoveRows(); |
297 | |
298 | return true; |
299 | } |
300 | |
301 | /*! |
302 | \since 5.13 |
303 | \reimp |
304 | */ |
305 | bool QStringListModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) |
306 | { |
307 | if (sourceRow < 0 |
308 | || sourceRow + count - 1 >= rowCount(parent: sourceParent) |
309 | || destinationChild < 0 |
310 | || destinationChild > rowCount(parent: destinationParent) |
311 | || sourceRow == destinationChild |
312 | || sourceRow == destinationChild - 1 |
313 | || count <= 0 |
314 | || sourceParent.isValid() |
315 | || destinationParent.isValid()) { |
316 | return false; |
317 | } |
318 | if (!beginMoveRows(sourceParent: QModelIndex(), sourceFirst: sourceRow, sourceLast: sourceRow + count - 1, destinationParent: QModelIndex(), destinationRow: destinationChild)) |
319 | return false; |
320 | |
321 | int fromRow = sourceRow; |
322 | if (destinationChild < sourceRow) |
323 | fromRow += count - 1; |
324 | else |
325 | destinationChild--; |
326 | while (count--) |
327 | lst.move(from: fromRow, to: destinationChild); |
328 | endMoveRows(); |
329 | return true; |
330 | } |
331 | |
332 | static bool ascendingLessThan(const QPair<QString, int> &s1, const QPair<QString, int> &s2) |
333 | { |
334 | return s1.first < s2.first; |
335 | } |
336 | |
337 | static bool decendingLessThan(const QPair<QString, int> &s1, const QPair<QString, int> &s2) |
338 | { |
339 | return s1.first > s2.first; |
340 | } |
341 | |
342 | /*! |
343 | \reimp |
344 | */ |
345 | void QStringListModel::sort(int, Qt::SortOrder order) |
346 | { |
347 | emit layoutAboutToBeChanged(parents: QList<QPersistentModelIndex>(), hint: VerticalSortHint); |
348 | |
349 | QVector<QPair<QString, int> > list; |
350 | const int lstCount = lst.count(); |
351 | list.reserve(asize: lstCount); |
352 | for (int i = 0; i < lstCount; ++i) |
353 | list.append(t: QPair<QString, int>(lst.at(i), i)); |
354 | |
355 | if (order == Qt::AscendingOrder) |
356 | std::sort(first: list.begin(), last: list.end(), comp: ascendingLessThan); |
357 | else |
358 | std::sort(first: list.begin(), last: list.end(), comp: decendingLessThan); |
359 | |
360 | lst.clear(); |
361 | QVector<int> forwarding(lstCount); |
362 | for (int i = 0; i < lstCount; ++i) { |
363 | lst.append(t: list.at(i).first); |
364 | forwarding[list.at(i).second] = i; |
365 | } |
366 | |
367 | QModelIndexList oldList = persistentIndexList(); |
368 | QModelIndexList newList; |
369 | const int numOldIndexes = oldList.count(); |
370 | newList.reserve(alloc: numOldIndexes); |
371 | for (int i = 0; i < numOldIndexes; ++i) |
372 | newList.append(t: index(row: forwarding.at(i: oldList.at(i).row()), column: 0)); |
373 | changePersistentIndexList(from: oldList, to: newList); |
374 | |
375 | emit layoutChanged(parents: QList<QPersistentModelIndex>(), hint: VerticalSortHint); |
376 | } |
377 | |
378 | /*! |
379 | Returns the string list used by the model to store data. |
380 | */ |
381 | QStringList QStringListModel::stringList() const |
382 | { |
383 | return lst; |
384 | } |
385 | |
386 | /*! |
387 | Sets the model's internal string list to \a strings. The model will |
388 | notify any attached views that its underlying data has changed. |
389 | |
390 | \sa dataChanged() |
391 | */ |
392 | void QStringListModel::setStringList(const QStringList &strings) |
393 | { |
394 | beginResetModel(); |
395 | lst = strings; |
396 | endResetModel(); |
397 | } |
398 | |
399 | /*! |
400 | \reimp |
401 | */ |
402 | Qt::DropActions QStringListModel::supportedDropActions() const |
403 | { |
404 | return QAbstractItemModel::supportedDropActions() | Qt::MoveAction; |
405 | } |
406 | |
407 | QT_END_NAMESPACE |
408 | |
409 | #include "moc_qstringlistmodel.cpp" |
410 | |