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 
QStringListModel(QObject * parent)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 
QStringListModel(const QStringList & strings,QObject * parent)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 
rowCount(const QModelIndex & parent) const119 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 */
sibling(int row,int column,const QModelIndex & idx) const130 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 */
itemData(const QModelIndex & index) const142 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 */
setItemData(const QModelIndex & index,const QMap<int,QVariant> & roles)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 
data(const QModelIndex & index,int role) const183 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 
flags(const QModelIndex & index) const202 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 
setData(const QModelIndex & index,const QVariant & value,int role)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 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
235 /*!
236     \reimp
237     \since 6.0
238  */
clearItemData(const QModelIndex & index)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 
insertRows(int row,int count,const QModelIndex & parent)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(QModelIndex(), row, row + count - 1);
264 
265     for (int r = 0; r < count; ++r)
266         lst.insert(row, 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 
removeRows(int row,int count,const QModelIndex & parent)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(QModelIndex(), row, row + count - 1);
292 
293     const auto it = lst.begin() + row;
294     lst.erase(it, it + count);
295 
296     endRemoveRows();
297 
298     return true;
299 }
300 
301 /*!
302     \since 5.13
303     \reimp
304 */
moveRows(const QModelIndex & sourceParent,int sourceRow,int count,const QModelIndex & destinationParent,int destinationChild)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(sourceParent)
309         || destinationChild < 0
310         || destinationChild > rowCount(destinationParent)
311         || sourceRow == destinationChild
312         || sourceRow == destinationChild - 1
313         || count <= 0
314         || sourceParent.isValid()
315         || destinationParent.isValid()) {
316         return false;
317     }
318     if (!beginMoveRows(QModelIndex(), sourceRow, sourceRow + count - 1, QModelIndex(), 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(fromRow, destinationChild);
328     endMoveRows();
329     return true;
330 }
331 
ascendingLessThan(const QPair<QString,int> & s1,const QPair<QString,int> & s2)332 static bool ascendingLessThan(const QPair<QString, int> &s1, const QPair<QString, int> &s2)
333 {
334     return s1.first < s2.first;
335 }
336 
decendingLessThan(const QPair<QString,int> & s1,const QPair<QString,int> & s2)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 */
sort(int,Qt::SortOrder order)345 void QStringListModel::sort(int, Qt::SortOrder order)
346 {
347     emit layoutAboutToBeChanged(QList<QPersistentModelIndex>(), VerticalSortHint);
348 
349     QVector<QPair<QString, int> > list;
350     const int lstCount = lst.count();
351     list.reserve(lstCount);
352     for (int i = 0; i < lstCount; ++i)
353         list.append(QPair<QString, int>(lst.at(i), i));
354 
355     if (order == Qt::AscendingOrder)
356         std::sort(list.begin(), list.end(), ascendingLessThan);
357     else
358         std::sort(list.begin(), list.end(), decendingLessThan);
359 
360     lst.clear();
361     QVector<int> forwarding(lstCount);
362     for (int i = 0; i < lstCount; ++i) {
363         lst.append(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(numOldIndexes);
371     for (int i = 0; i < numOldIndexes; ++i)
372         newList.append(index(forwarding.at(oldList.at(i).row()), 0));
373     changePersistentIndexList(oldList, newList);
374 
375     emit layoutChanged(QList<QPersistentModelIndex>(), VerticalSortHint);
376 }
377 
378 /*!
379     Returns the string list used by the model to store data.
380 */
stringList() const381 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 */
setStringList(const QStringList & strings)392 void QStringListModel::setStringList(const QStringList &strings)
393 {
394     beginResetModel();
395     lst = strings;
396     endResetModel();
397 }
398 
399 /*!
400   \reimp
401 */
supportedDropActions() const402 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