Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
bookmarkmodel.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
4#include "bookmarkitem.h"
5
6#include <QtCore/QIODevice>
7#include <QtCore/QMimeData>
8#include <QtCore/QStack>
9
10#include <QtWidgets/QApplication>
11#include <QtWidgets/QStyle>
12#include <QtWidgets/QTreeView>
13
14using namespace Qt::StringLiterals;
15
16const quint32 VERSION = 0xe53798;
17const QLatin1StringView MIMETYPE("application/bookmarks.assistant");
18
19BookmarkModel::BookmarkModel()
20 : QAbstractItemModel()
21 , m_folder(false)
22 , m_editable(false)
23 , rootItem(nullptr)
24{
25}
26
28{
29 delete rootItem;
30}
31
34{
35 QByteArray ba;
36 QDataStream stream(&ba, QIODevice::WriteOnly);
37 stream << qint32(VERSION);
38
39 const QModelIndex &root = index(0,0, QModelIndex()).parent();
40 for (int i = 0; i < rowCount(root); ++i)
41 collectItems(index(i, 0, root), 0, &stream);
42
43 return ba;
44}
45
46void
47BookmarkModel::setBookmarks(const QByteArray &bookmarks)
48{
49 beginResetModel();
50
51 delete rootItem;
52 folderIcon = QApplication::style()->standardIcon(QStyle::SP_DirClosedIcon);
53 bookmarkIcon = QIcon(":/qt-project.org/assistant/images/bookmark.png"_L1);
54
55 rootItem = new BookmarkItem(DataVector() << tr("Name") << tr("Address")
56 << true);
57
58 QStack<BookmarkItem*> parents;
59 QDataStream stream(bookmarks);
60
61 quint32 version;
62 stream >> version;
63 if (version < VERSION) {
64 stream.device()->seek(0);
65 BookmarkItem *toolbar =
66 new BookmarkItem(DataVector() << tr("Bookmarks Toolbar") << "Folder"_L1 << true);
67 rootItem->addChild(toolbar);
68
69 BookmarkItem *menu =
70 new BookmarkItem(DataVector() << tr("Bookmarks Menu") << "Folder"_L1 << true);
71 rootItem->addChild(menu);
72 parents.push(menu);
73 } else {
74 parents.push(rootItem);
75 }
76
77 qint32 depth;
78 bool expanded;
79 QString name, url;
80 while (!stream.atEnd()) {
81 stream >> depth >> name >> url >> expanded;
82 while ((parents.size() - 1) != depth)
83 parents.pop();
84
85 BookmarkItem *item = new BookmarkItem(DataVector() << name << url << expanded);
86 if (url == "Folder"_L1) {
87 parents.top()->addChild(item);
88 parents.push(item);
89 } else {
90 parents.top()->addChild(item);
91 }
92 }
93
94 cache.clear();
95 setupCache(index(0,0, QModelIndex().parent()));
96 endResetModel();
97}
98
99void
101{
102 m_editable = editable;
103}
104
105void
107{
108 for (QModelIndex index : std::as_const(cache))
109 treeView->setExpanded(index, index.data(UserRoleExpanded).toBool());
110}
111
113BookmarkModel::addItem(const QModelIndex &parent, bool isFolder)
114{
115 m_folder = isFolder;
116 QModelIndex next;
117 if (insertRow(rowCount(parent), parent))
118 next = index(rowCount(parent) - 1, 0, parent);
119 m_folder = false;
120
121 return next;
122}
123
124bool
125BookmarkModel::removeItem(const QModelIndex &index)
126{
127 if (!index.isValid())
128 return false;
129
130 QModelIndexList indexes;
131 if (rowCount(index) > 0)
132 indexes = collectItems(index);
133 indexes.append(index);
134
135 for (const QModelIndex &itemToRemove : std::as_const(indexes)) {
136 if (!removeRow(itemToRemove.row(), itemToRemove.parent()))
137 return false;
138 cache.remove(itemFromIndex(itemToRemove));
139 }
140 return true;
141}
142
143int
144BookmarkModel::rowCount(const QModelIndex &index) const
145{
146 if (BookmarkItem *item = itemFromIndex(index))
147 return item->childCount();
148 return 0;
149}
150
151int
152BookmarkModel::columnCount(const QModelIndex &/*index*/) const
153{
154 return 2;
155}
156
158BookmarkModel::parent(const QModelIndex &index) const
159{
160 if (!index.isValid())
161 return QModelIndex();
162
163 if (BookmarkItem *childItem = itemFromIndex(index)) {
164 if (BookmarkItem *parent = childItem->parent()) {
165 if (parent != rootItem)
166 return createIndex(parent->childNumber(), 0, parent);
167 }
168 }
169 return QModelIndex();
170}
171
173BookmarkModel::index(int row, int column, const QModelIndex &index) const
174{
175 if (index.isValid() && (index.column() != 0 && index.column() != 1))
176 return QModelIndex();
177
178 if (BookmarkItem *parent = itemFromIndex(index)) {
179 if (BookmarkItem *childItem = parent->child(row))
180 return createIndex(row, column, childItem);
181 }
182 return QModelIndex();
183}
184
187{
188 return /* Qt::CopyAction | */Qt::MoveAction;
189}
190
192BookmarkModel::flags(const QModelIndex &index) const
193{
194 if (!index.isValid())
195 return Qt::NoItemFlags;
196
197 Qt::ItemFlags defaultFlags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
198
199 if (m_editable)
200 defaultFlags |= Qt::ItemIsEditable;
201
202 if (itemFromIndex(index) && index.data(UserRoleFolder).toBool()) {
203 if (index.column() > 0)
204 return defaultFlags &~ Qt::ItemIsEditable;
205 return defaultFlags | Qt::ItemIsDropEnabled;
206 }
207
208 return defaultFlags | Qt::ItemIsDragEnabled;
209}
210
212BookmarkModel::data(const QModelIndex &index, int role) const
213{
214 if (index.isValid()) {
215 if (BookmarkItem *item = itemFromIndex(index)) {
216 switch (role) {
217 case Qt::EditRole:
218 case Qt::DisplayRole:
219 if (index.data(UserRoleFolder).toBool() && index.column() == 1)
220 return QString();
221 return item->data(index.column());
222
223 case Qt::DecorationRole:
224 if (index.column() == 0)
225 return index.data(UserRoleFolder).toBool()
226 ? folderIcon : bookmarkIcon;
227 break;
228
229 default:
230 return item->data(role);
231 }
232 }
233 }
234 return QVariant();
235}
236
237void BookmarkModel::setData(const QModelIndex &index, const DataVector &data)
238{
239 if (BookmarkItem *item = itemFromIndex(index)) {
240 item->setData(data);
241 emit dataChanged(index, index);
242 }
243}
244
245bool
246BookmarkModel::setData(const QModelIndex &index, const QVariant &value, int role)
247{
248 bool result = false;
249 if (role != Qt::EditRole && role != UserRoleExpanded)
250 return result;
251
252 if (BookmarkItem *item = itemFromIndex(index)) {
253 if (role == Qt::EditRole) {
254 const bool isFolder = index.data(UserRoleFolder).toBool();
255 if (!isFolder || index.column() == 0)
256 result = item->setData(index.column(), value);
257 } else if (role == UserRoleExpanded) {
258 result = item->setData(UserRoleExpanded, value);
259 }
260 }
261
262 if (result)
263 emit dataChanged(index, index);
264 return result;
265}
266
268BookmarkModel::headerData(int section, Qt::Orientation orientation,
269 int role) const
270{
271 if (rootItem && orientation == Qt::Horizontal && role == Qt::DisplayRole)
272 return rootItem->data(section);
273 return QVariant();
274}
275
278{
279 return cache.value(item, QModelIndex());
280}
281
283BookmarkModel::itemFromIndex(const QModelIndex &index) const
284{
285 if (index.isValid())
286 return static_cast<BookmarkItem*>(index.internalPointer());
287 return rootItem;
288}
289
291BookmarkModel::indexListFor(const QString &label) const
292{
293 QList<QPersistentModelIndex> hits;
294 const QModelIndexList &list = collectItems(QModelIndex());
295 for (const QModelIndex &index : list) {
296 if (index.data().toString().contains(label, Qt::CaseInsensitive))
297 hits.prepend(index); // list is reverse sorted
298 }
299 return hits;
300}
301
302bool
303BookmarkModel::insertRows(int position, int rows, const QModelIndex &parent)
304{
305 if (parent.isValid() && !parent.data(UserRoleFolder).toBool())
306 return false;
307
308 bool success = false;
309 if (BookmarkItem *parentItem = itemFromIndex(parent)) {
310 beginInsertRows(parent, position, position + rows - 1);
311 success = parentItem->insertChildren(m_folder, position, rows);
312 if (success) {
313 const QModelIndex &current = index(position, 0, parent);
314 cache.insert(itemFromIndex(current), current);
315 }
316 endInsertRows();
317 }
318 return success;
319}
320
321bool
322BookmarkModel::removeRows(int position, int rows, const QModelIndex &index)
323{
324 bool success = false;
325 if (BookmarkItem *parent = itemFromIndex(index)) {
326 beginRemoveRows(index, position, position + rows - 1);
327 success = parent->removeChildren(position, rows);
328 endRemoveRows();
329 }
330 return success;
331}
332
335{
336 return QStringList() << MIMETYPE;
337}
338
339QMimeData*
340BookmarkModel::mimeData(const QModelIndexList &indexes) const
341{
342 if (indexes.isEmpty())
343 return nullptr;
344
345 QByteArray data;
346 QDataStream stream(&data, QIODevice::WriteOnly);
347
348 for (const QModelIndex &index : indexes) {
349 if (index.column() == 0)
350 collectItems(index, 0, &stream);
351 }
352
353 QMimeData *mimeData = new QMimeData();
354 mimeData->setData(MIMETYPE, data);
355 return mimeData;
356}
357
358bool
359BookmarkModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
360 int row, int column, const QModelIndex &parent)
361{
362 if (action == Qt::IgnoreAction)
363 return true;
364
365 if (!data->hasFormat(MIMETYPE) || column > 0)
366 return false;
367
368 QByteArray ba = data->data(MIMETYPE);
369 QDataStream stream(&ba, QIODevice::ReadOnly);
370 while (stream.atEnd())
371 return false;
372
373 qint32 depth;
374 bool expanded;
375 QString name, url;
376 while (!stream.atEnd()) {
377 stream >> depth >> name >> url >> expanded;
378 if (insertRow(qMax(0, row), parent)) {
379 const QModelIndex &current = index(qMax(0, row), 0, parent);
380 if (current.isValid()) {
381 BookmarkItem* item = itemFromIndex(current);
382 item->setData(DataVector() << name << url << expanded);
383 }
384 }
385 }
386 return true;
387}
388
389void
390BookmarkModel::setupCache(const QModelIndex &parent)
391{
392 const QModelIndexList &list = collectItems(parent);
393 for (const QModelIndex &index : list)
394 cache.insert(itemFromIndex(index), index);
395}
396
397QModelIndexList
398BookmarkModel::collectItems(const QModelIndex &parent) const
399{
400 QModelIndexList list;
401 for (int i = rowCount(parent) - 1; i >= 0 ; --i) {
402 const QModelIndex &next = index(i, 0, parent);
403 if (data(next, UserRoleFolder).toBool())
404 list += collectItems(next);
405 list.append(next);
406 }
407 return list;
408}
409
410void
411BookmarkModel::collectItems(const QModelIndex &parent, qint32 depth,
412 QDataStream *stream) const
413{
414 if (parent.isValid()) {
415 *stream << depth;
416 *stream << parent.data().toString();
417 *stream << parent.data(UserRoleUrl).toString();
418 *stream << parent.data(UserRoleExpanded).toBool();
419
420 for (int i = 0; i < rowCount(parent); ++i) {
421 if (parent.data(UserRoleFolder).toBool())
422 collectItems(index(i, 0 , parent), depth + 1, stream);
423 }
424 }
425}
@ UserRoleExpanded
@ UserRoleFolder
@ UserRoleUrl
QList< QVariant > DataVector
const QLatin1StringView MIMETYPE("application/bookmarks.assistant")
const quint32 VERSION
BookmarkItem * child(int number) const
bool insertChildren(bool isFolder, int position, int count)
void addChild(BookmarkItem *child)
bool removeChildren(int position, int count)
int childCount() const
BookmarkItem * parent() const
void setData(const DataVector &data)
int childNumber() const
void setItemsEditable(bool editable)
QByteArray bookmarks() const
int columnCount(const QModelIndex &index=QModelIndex()) const override
Returns the number of columns for the children of the given parent.
Qt::ItemFlags flags(const QModelIndex &index) const override
Returns the item flags for the given index.
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
Returns the data for the given role and section in the header with the specified orientation.
QModelIndex index(int row, int column, const QModelIndex &index) const override
Returns the index of the item in the model specified by the given row, column and parent index.
~BookmarkModel() override
bool insertRows(int position, int rows, const QModelIndex &parent) override
QModelIndex addItem(const QModelIndex &parent, bool isFolder=false)
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
Handles the data supplied by a drag and drop operation that ended with the given action.
BookmarkItem * itemFromIndex(const QModelIndex &index) const
bool removeItem(const QModelIndex &index)
void setData(const QModelIndex &index, const QList< QVariant > &data)
bool setData(const QModelIndex &index, const QVariant &value, int role) override
Sets the role data for the item at index to value.
QStringList mimeTypes() const override
Returns the list of allowed MIME types.
QModelIndex indexFromItem(BookmarkItem *item) const
void expandFoldersIfNeeeded(QTreeView *treeView)
int rowCount(const QModelIndex &index=QModelIndex()) const override
Returns the number of rows under the given parent.
void setBookmarks(const QByteArray &bookmarks)
QVariant data(const QModelIndex &index, int role) const override
Returns the data stored under the given role for the item referred to by the index.
QList< QPersistentModelIndex > indexListFor(const QString &label) const
QModelIndex parent(const QModelIndex &index) const override
QMimeData * mimeData(const QModelIndexList &indexes) const override
Returns an object that contains serialized items of data corresponding to the list of indexes specifi...
Qt::DropActions supportedDropActions() const override
bool removeRows(int position, int rows, const QModelIndex &parent) override