Falkon Develop
Cross-platform Qt-based web browser
bookmarksmodel.cpp
Go to the documentation of this file.
1/* ============================================================
2* Falkon - Qt web browser
3* Copyright (C) 2014-2017 David Rosca <nowrep@gmail.com>
4*
5* This program is free software: you can redistribute it and/or modify
6* it under the terms of the GNU General Public License as published by
7* the Free Software Foundation, either version 3 of the License, or
8* (at your option) any later version.
9*
10* This program is distributed in the hope that it will be useful,
11* but WITHOUT ANY WARRANTY; without even the implied warranty of
12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13* GNU General Public License for more details.
14*
15* You should have received a copy of the GNU General Public License
16* along with this program. If not, see <http://www.gnu.org/licenses/>.
17* ============================================================ */
18#include "bookmarksmodel.h"
19#include "bookmarkitem.h"
20#include "bookmarks.h"
21
22#include <QApplication>
23#include <QTimer>
24#include <QStyle>
25#include <QIODevice>
26
27//#define BOOKMARKSMODEL_DEBUG
28
29#ifdef BOOKMARKSMODEL_DEBUG
30#include "modeltest.h"
31#endif
32
33BookmarksModel::BookmarksModel(BookmarkItem* root, Bookmarks* bookmarks, QObject* parent)
34 : QAbstractItemModel(parent)
35 , m_root(root)
36 , m_bookmarks(bookmarks)
37{
38 if (m_bookmarks) {
39 connect(m_bookmarks, &Bookmarks::bookmarkChanged, this, &BookmarksModel::bookmarkChanged);
40 }
41
42#ifdef BOOKMARKSMODEL_DEBUG
43 new ModelTest(this, this);
44#endif
45}
46
48{
49 Q_ASSERT(parent);
50 Q_ASSERT(item);
51 Q_ASSERT(row >= 0);
52 Q_ASSERT(row <= parent->children().count());
53
54 beginInsertRows(index(parent), row, row);
55 parent->addChild(item, row);
56 endInsertRows();
57}
58
60{
61 Q_ASSERT(item);
62 Q_ASSERT(item->parent());
63
64 int idx = item->parent()->children().indexOf(item);
65
66 beginRemoveRows(index(item->parent()), idx, idx);
68 endRemoveRows();
69}
70
71Qt::ItemFlags BookmarksModel::flags(const QModelIndex &index) const
72{
73 BookmarkItem* itm = item(index);
74
75 if (!index.isValid() || !itm) {
76 return Qt::NoItemFlags;
77 }
78
79 Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
80
81 if (itm->isFolder()) {
82 flags |= Qt::ItemIsDropEnabled;
83 }
84
85 if (m_bookmarks && m_bookmarks->canBeModified(itm)) {
86 flags |= Qt::ItemIsDragEnabled;
87 }
88
89 return flags;
90}
91
92QVariant BookmarksModel::data(const QModelIndex &index, int role) const
93{
94 BookmarkItem* itm = item(index);
95
96 if (!itm) {
97 return {};
98 }
99
100 switch (role) {
101 case TypeRole:
102 return itm->type();
103 case UrlRole:
104 return itm->url();
105 case UrlStringRole:
106 return itm->urlString();
107 case TitleRole:
108 return itm->title();
109 case DescriptionRole:
110 return itm->description();
111 case KeywordRole:
112 return itm->keyword();
113 case VisitCountRole:
114 return -1;
115 case ExpandedRole:
116 return itm->isExpanded();
118 return itm->isSidebarExpanded();
119 case Qt::ToolTipRole:
120 if (index.column() == 0 && itm->isUrl()) {
121 return QSL("%1\n%2").arg(itm->title(), QString::fromUtf8(itm->url().toEncoded()));
122 }
123 // fallthrough
124 case Qt::DisplayRole:
125 switch (index.column()) {
126 case 0:
127 return itm->title();
128 case 1:
129 return QString::fromUtf8(itm->url().toEncoded());
130 default:
131 return {};
132 }
133 case Qt::DecorationRole:
134 if (index.column() == 0) {
135 return itm->icon();
136 }
137 return {};
138 default:
139 return {};
140 }
141}
142
143QVariant BookmarksModel::headerData(int section, Qt::Orientation orientation, int role) const
144{
145 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
146 switch (section) {
147 case 0:
148 return tr("Title");
149 case 1:
150 return tr("Address");
151 }
152 }
153
154 return QAbstractItemModel::headerData(section, orientation, role);
155}
156
157int BookmarksModel::rowCount(const QModelIndex &parent) const
158{
159 if (parent.column() > 0) {
160 return 0;
161 }
162
163 BookmarkItem* itm = item(parent);
164 return itm->children().count();
165}
166
167int BookmarksModel::columnCount(const QModelIndex &parent) const
168{
169 if (parent.column() > 0) {
170 return 0;
171 }
172
173 return 2;
174}
175
176bool BookmarksModel::hasChildren(const QModelIndex &parent) const
177{
178 BookmarkItem* itm = item(parent);
179 return !itm->children().isEmpty();
180}
181
183{
184 return Qt::CopyAction | Qt::MoveAction;
185}
186
187#define MIMETYPE QLatin1String("application/falkon.bookmarks")
188
189QStringList BookmarksModel::mimeTypes() const
190{
191 QStringList types;
192 types.append(MIMETYPE);
193 return types;
194}
195
196QMimeData* BookmarksModel::mimeData(const QModelIndexList &indexes) const
197{
198 auto* mimeData = new QMimeData();
199 QByteArray encodedData;
200
201 QDataStream stream(&encodedData, QIODevice::WriteOnly);
202
203 for (const QModelIndex &index : indexes) {
204 // If item's parent (=folder) is also selected, we will just move the whole folder
205 if (index.isValid() && index.column() == 0 && !indexes.contains(index.parent())) {
206 stream << index.row() << (quintptr) index.internalPointer();
207 }
208 }
209
210 mimeData->setData(MIMETYPE, encodedData);
211 return mimeData;
212}
213
214bool BookmarksModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
215{
216 Q_UNUSED(column)
217
218 if (action == Qt::IgnoreAction) {
219 return true;
220 }
221
222 if (!m_bookmarks || !data->hasFormat(MIMETYPE) || !parent.isValid()) {
223 return false;
224 }
225
226 BookmarkItem* parentItm = item(parent);
227 Q_ASSERT(parentItm->isFolder());
228
229 QByteArray encodedData = data->data(MIMETYPE);
230 QDataStream stream(&encodedData, QIODevice::ReadOnly);
231 QList<BookmarkItem*> items;
232
233 while (!stream.atEnd()) {
234 int row;
235 quintptr ptr;
236
237 stream >> row >> ptr;
238
239 QModelIndex index = createIndex(row, 0, (void*) ptr);
240 BookmarkItem* itm = item(index);
241
242 Q_ASSERT(index.isValid());
243 Q_ASSERT(itm != m_bookmarks->rootItem());
244
245 // Cannot move bookmark to itself
246 if (itm == parentItm) {
247 return false;
248 }
249
250 items.append(itm);
251 }
252
253 row = qMax(row, 0);
254
255 for (BookmarkItem* itm : std::as_const(items)) {
256 // If we are moving an item through the folder and item is above the row to insert,
257 // we must decrease row by one (by the dropped folder)
258 if (itm->parent() == parentItm && itm->parent()->children().indexOf(itm) < row) {
259 row--;
260 }
261
262 m_bookmarks->removeBookmark(itm);
263 m_bookmarks->insertBookmark(parentItm, row++, itm);
264 }
265
266 return true;
267}
268
269QModelIndex BookmarksModel::parent(const QModelIndex &child) const
270{
271 if (!child.isValid()) {
272 return {};
273 }
274
275 BookmarkItem* itm = item(child);
276 return index(itm->parent());
277}
278
279QModelIndex BookmarksModel::index(int row, int column, const QModelIndex &parent) const
280{
281 if (!hasIndex(row, column, parent)) {
282 return {};
283 }
284
285 BookmarkItem* parentItem = item(parent);
286 return createIndex(row, column, parentItem->children().at(row));
287}
288
289QModelIndex BookmarksModel::index(BookmarkItem* item, int column) const
290{
292
293 if (!parent) {
294 return {};
295 }
296
297 return createIndex(parent->children().indexOf(item), column, item);
298}
299
300BookmarkItem* BookmarksModel::item(const QModelIndex &index) const
301{
302 auto* itm = static_cast<BookmarkItem*>(index.internalPointer());
303 return itm ? itm : m_root;
304}
305
306void BookmarksModel::bookmarkChanged(BookmarkItem* item)
307{
308 QModelIndex idx = index(item);
309 Q_EMIT dataChanged(idx, idx);
310}
311
312
313// BookmarksFilterModel
315 : QSortFilterProxyModel(parent)
316{
317 setSourceModel(parent);
318 setFilterCaseSensitivity(Qt::CaseInsensitive);
319
320 m_filterTimer = new QTimer(this);
321 m_filterTimer->setSingleShot(true);
322 m_filterTimer->setInterval(300);
323
324 connect(m_filterTimer, &QTimer::timeout, this, &BookmarksFilterModel::startFiltering);
325}
326
328{
329 m_pattern = pattern;
330
331 m_filterTimer->start();
332}
333
334bool BookmarksFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
335{
336 const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
337
338 if (index.data(BookmarksModel::TypeRole).toInt() == BookmarkItem::Folder) {
339 return true;
340 }
341
342 return (index.data(BookmarksModel::TitleRole).toString().contains(m_pattern, filterCaseSensitivity()) ||
343 index.data(BookmarksModel::UrlStringRole).toString().contains(m_pattern, filterCaseSensitivity()) ||
344 index.data(BookmarksModel::DescriptionRole).toString().contains(m_pattern, filterCaseSensitivity()) ||
345 index.data(BookmarksModel::KeywordRole).toString().compare(m_pattern, filterCaseSensitivity()) == 0);
346}
347
348void BookmarksFilterModel::startFiltering()
349{
350 QSortFilterProxyModel::setFilterFixedString(m_pattern);
351}
352
354 : QMimeData()
355{
356}
357
359{
360 return m_item;
361}
362
364{
365 m_item = item;
366}
367
368bool BookmarksButtonMimeData::hasFormat(const QString &format) const
369{
370 return mimeType() == format;
371}
372
374{
375 return {mimeType()};
376}
377
379{
380 return QSL("application/falkon.bookmarktoolbutton.bookmarkitem");
381}
#define MIMETYPE
bool isExpanded() const
QString keyword() const
bool isFolder() const
QString urlString() const
void removeChild(BookmarkItem *child)
QString description() const
QUrl url() const
bool isUrl() const
BookmarkItem * parent() const
QList< BookmarkItem * > children() const
QString title() const
bool isSidebarExpanded() const
Type type() const
void setBookmarkItem(BookmarkItem *item)
bool hasFormat(const QString &format) const override
QStringList formats() const override
static QString mimeType()
BookmarkItem * item() const
void setFilterFixedString(const QString &pattern)
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
BookmarksFilterModel(QAbstractItemModel *parent)
BookmarkItem * rootItem() const
Definition: bookmarks.cpp:69
void bookmarkChanged(BookmarkItem *item)
bool canBeModified(BookmarkItem *item) const
Definition: bookmarks.cpp:104
bool removeBookmark(BookmarkItem *item)
Definition: bookmarks.cpp:157
void insertBookmark(BookmarkItem *parent, int row, BookmarkItem *item)
Definition: bookmarks.cpp:144
int columnCount(const QModelIndex &parent) const override
void removeBookmark(BookmarkItem *item)
QStringList mimeTypes() const override
int rowCount(const QModelIndex &parent) const override
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
QMimeData * mimeData(const QModelIndexList &indexes) const override
BookmarkItem * item(const QModelIndex &index) const
QVariant data(const QModelIndex &index, int role) const override
QModelIndex parent(const QModelIndex &child) const override
BookmarksModel(BookmarkItem *root, Bookmarks *bookmarks, QObject *parent=nullptr)
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
Qt::ItemFlags flags(const QModelIndex &index) const override
void addBookmark(BookmarkItem *parent, int row, BookmarkItem *item)
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
bool hasChildren(const QModelIndex &parent) const override
Qt::DropActions supportedDropActions() const override
#define QSL(x)
Definition: qzcommon.h:40