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

source code of qtbase/src/corelib/itemmodels/qstringlistmodel.cpp