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
50QT_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
93QStringListModel::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
103QStringListModel::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
119int 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*/
130QModelIndex 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*/
142QMap<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*/
158bool 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
183QVariant 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
202Qt::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
220bool 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 */
238bool 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
256bool 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
284bool 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*/
303bool 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
330static bool ascendingLessThan(const QPair<QString, int> &s1, const QPair<QString, int> &s2)
331{
332 return s1.first < s2.first;
333}
334
335static 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*/
343void 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*/
379QStringList 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*/
390void QStringListModel::setStringList(const QStringList &strings)
391{
392 beginResetModel();
393 lst = strings;
394 endResetModel();
395}
396
397/*!
398 \reimp
399*/
400Qt::DropActions QStringListModel::supportedDropActions() const
401{
402 return QAbstractItemModel::supportedDropActions() | Qt::MoveAction;
403}
404
405QT_END_NAMESPACE
406
407#include "moc_qstringlistmodel.cpp"
408