Falkon Develop
Cross-platform Qt-based web browser
iconprovider.cpp
Go to the documentation of this file.
1/* ============================================================
2* Falkon - Qt web browser
3* Copyright (C) 2010-2018 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 "iconprovider.h"
19#include "mainapplication.h"
20#include "networkmanager.h"
21#include "sqldatabase.h"
22#include "autosaver.h"
23#include "webview.h"
24#include "qztools.h"
25
26#include <QTimer>
27#include <QBuffer>
28#include <QFutureWatcher>
29#include <QtConcurrent/QtConcurrentRun>
30
31Q_GLOBAL_STATIC(IconProvider, qz_icon_provider)
32
33static QByteArray encodeUrl(const QUrl &url)
34{
35 return url.toEncoded(QUrl::RemoveFragment | QUrl::StripTrailingSlash);
36}
37
39 : QWidget()
40{
41 m_autoSaver = new AutoSaver(this);
42 connect(m_autoSaver, &AutoSaver::save, this, &IconProvider::saveIconsToDatabase);
43}
44
46{
47 // Don't save icons in private mode.
48 if (mApp->isPrivate()) {
49 return;
50 }
51
52 const QIcon icon = view->icon(true);
53 if (icon.isNull()) {
54 return;
55 }
56
57 const QStringList ignoredSchemes = {
58 QStringLiteral("falkon"),
59 QStringLiteral("ftp"),
60 QStringLiteral("file"),
61 QStringLiteral("view-source"),
62 QStringLiteral("data"),
63 QStringLiteral("about")
64 };
65
66 if (ignoredSchemes.contains(view->url().scheme())) {
67 return;
68 }
69
70 for (int i = 0; i < m_iconBuffer.size(); ++i) {
71 if (m_iconBuffer[i].first == view->url()) {
72 m_iconBuffer.removeAt(i);
73 break;
74 }
75 }
76
77 BufferedIcon item;
78 item.first = view->url();
79 item.second = icon.pixmap(16).toImage();
80
81 m_autoSaver->changeOccurred();
82 m_iconBuffer.append(item);
83}
84
86{
87 return QIcon::fromTheme(QSL("bookmarks"), m_bookmarkIcon);
88}
89
90void IconProvider::setBookmarkIcon(const QIcon &icon)
91{
92 m_bookmarkIcon = icon;
93}
94
95QIcon IconProvider::standardIcon(QStyle::StandardPixmap icon)
96{
97 switch (icon) {
98 case QStyle::SP_MessageBoxCritical:
99 return QIcon::fromTheme(QSL("dialog-error"), QApplication::style()->standardIcon(icon));
100
101 case QStyle::SP_MessageBoxInformation:
102 return QIcon::fromTheme(QSL("dialog-information"), QApplication::style()->standardIcon(icon));
103
104 case QStyle::SP_MessageBoxQuestion:
105 return QIcon::fromTheme(QSL("dialog-question"), QApplication::style()->standardIcon(icon));
106
107 case QStyle::SP_MessageBoxWarning:
108 return QIcon::fromTheme(QSL("dialog-warning"), QApplication::style()->standardIcon(icon));
109
110 case QStyle::SP_DialogCloseButton:
111 return QIcon::fromTheme(QSL("dialog-close"), QApplication::style()->standardIcon(icon));
112
113 case QStyle::SP_BrowserStop:
114 return QIcon::fromTheme(QSL("process-stop"), QApplication::style()->standardIcon(icon));
115
116 case QStyle::SP_BrowserReload:
117 return QIcon::fromTheme(QSL("view-refresh"), QApplication::style()->standardIcon(icon));
118
119 case QStyle::SP_FileDialogToParent:
120 return QIcon::fromTheme(QSL("go-up"), QApplication::style()->standardIcon(icon));
121
122 case QStyle::SP_ArrowUp:
123 return QIcon::fromTheme(QSL("go-up"), QApplication::style()->standardIcon(icon));
124
125 case QStyle::SP_ArrowDown:
126 return QIcon::fromTheme(QSL("go-down"), QApplication::style()->standardIcon(icon));
127
128 case QStyle::SP_ArrowForward:
129 if (QApplication::layoutDirection() == Qt::RightToLeft) {
130 return QIcon::fromTheme(QSL("go-previous"), QApplication::style()->standardIcon(icon));
131 }
132 return QIcon::fromTheme(QSL("go-next"), QApplication::style()->standardIcon(icon));
133
134 case QStyle::SP_ArrowBack:
135 if (QApplication::layoutDirection() == Qt::RightToLeft) {
136 return QIcon::fromTheme(QSL("go-next"), QApplication::style()->standardIcon(icon));
137 }
138 return QIcon::fromTheme(QSL("go-previous"), QApplication::style()->standardIcon(icon));
139
140 default:
141 return QApplication::style()->standardIcon(icon);
142 }
143}
144
146{
147 return QIcon::fromTheme(QSL("tab-new"), QIcon(QSL(":/icons/menu/tab-new.svg")));
148}
149
151{
152 return QIcon::fromTheme(QSL("window-new"), QIcon(QSL(":/icons/menu/window-new.svg")));
153}
154
156{
157 return QIcon::fromTheme(QSL("view-private-symbolic"), QIcon(QSL(":/icons/menu/privatebrowsing.png")));
158}
159
161{
162 return QIcon::fromTheme(QSL("configure"), QIcon(QSL(":/icons/menu/settings.svg")));
163}
164
166{
167 return QPixmap::fromImage(instance()->emptyWebImage());
168}
169
171{
172 if (instance()->m_emptyWebImage.isNull()) {
173 instance()->m_emptyWebImage = QIcon(QSL(":icons/other/webpage.svg")).pixmap(16).toImage();
174 }
175
176 return instance()->m_emptyWebImage;
177}
178
179QIcon IconProvider::iconForUrl(const QUrl &url, bool allowNull)
180{
181 return instance()->iconFromImage(imageForUrl(url, allowNull));
182}
183
184QImage IconProvider::imageForUrl(const QUrl &url, bool allowNull)
185{
186 if (url.path().isEmpty()) {
187 return allowNull ? QImage() : IconProvider::emptyWebImage();
188 }
189
190 QMutexLocker locker(&instance()->m_iconCacheMutex);
191
192 const QByteArray encodedUrl = encodeUrl(url);
193
194 if (QImage *img = instance()->m_urlImageCache.object(encodedUrl)) {
195 return img->isNull() && !allowNull ? IconProvider::emptyWebImage() : *img;
196 }
197
198 const auto iconBuffer = instance()->m_iconBuffer;
199 for (const BufferedIcon &ic : iconBuffer) {
200 if (encodeUrl(ic.first) == encodedUrl) {
201 return ic.second;
202 }
203 }
204
205 QSqlQuery query(SqlDatabase::instance()->database());
206 query.prepare(QSL("SELECT icon FROM icons WHERE url GLOB ? LIMIT 1"));
207 query.addBindValue(QSL("%1*").arg(QzTools::escapeSqlGlobString(QString::fromUtf8(encodedUrl))));
208 query.exec();
209
210 auto *img = new QImage;
211 if (query.next()) {
212 img->loadFromData(query.value(0).toByteArray());
213 }
214 instance()->m_urlImageCache.insert(encodedUrl, img);
215
216 return img->isNull() && !allowNull ? IconProvider::emptyWebImage() : *img;
217}
218
219QIcon IconProvider::iconForDomain(const QUrl &url, bool allowNull)
220{
221 return instance()->iconFromImage(imageForDomain(url, allowNull));
222}
223
224QImage IconProvider::imageForDomain(const QUrl &url, bool allowNull)
225{
226 if (url.host().isEmpty()) {
227 return allowNull ? QImage() : IconProvider::emptyWebImage();
228 }
229
230 QMutexLocker locker(&instance()->m_iconCacheMutex);
231
232 const auto iconBuffer = instance()->m_iconBuffer;
233 for (const BufferedIcon &ic : iconBuffer) {
234 if (ic.first.host() == url.host()) {
235 return ic.second;
236 }
237 }
238
239 QSqlQuery query(SqlDatabase::instance()->database());
240 query.prepare(QSL("SELECT icon FROM icons WHERE url GLOB ? LIMIT 1"));
241 query.addBindValue(QSL("*%1*").arg(QzTools::escapeSqlGlobString(url.host())));
242 query.exec();
243
244 if (query.next()) {
245 return QImage::fromData(query.value(0).toByteArray());
246 }
247
248 return allowNull ? QImage() : IconProvider::emptyWebImage();
249}
250
252{
253 return qz_icon_provider();
254}
255
257{
258 QMutexLocker locker(&instance()->m_iconCacheMutex);
259
260 for (const BufferedIcon &ic : std::as_const(m_iconBuffer)) {
261 QByteArray ba;
262 QBuffer buffer(&ba);
263 buffer.open(QIODevice::WriteOnly);
264 ic.second.save(&buffer, "PNG");
265
266 const QByteArray encodedUrl = encodeUrl(ic.first);
267 m_urlImageCache.remove(encodedUrl);
268
269 auto job = new SqlQueryJob(QSL("INSERT OR REPLACE INTO icons (icon, url) VALUES (?,?)"), this);
270 job->addBindValue(buffer.data());
271 job->addBindValue(QString::fromUtf8(encodedUrl));
272 job->start();
273 }
274
275 m_iconBuffer.clear();
276}
277
279{
280 // Delete icons for entries older than 6 months
281 const QDateTime date = QDateTime::currentDateTime().addMonths(-6);
282
283 QSqlQuery query(SqlDatabase::instance()->database());
284 query.prepare(QSL("DELETE FROM icons WHERE url IN (SELECT url FROM history WHERE date < ?)"));
285 query.addBindValue(date.toMSecsSinceEpoch());
286 query.exec();
287
288 query.clear();
289 query.exec(QSL("VACUUM"));
290}
291
292QIcon IconProvider::iconFromImage(const QImage &image)
293{
294 return QIcon(QPixmap::fromImage(image));
295}
void changeOccurred()
Definition: autosaver.cpp:39
void save()
static QImage imageForUrl(const QUrl &url, bool allowNull=false)
void saveIcon(WebView *view)
static QIcon settingsIcon()
static QIcon iconForUrl(const QUrl &url, bool allowNull=false)
static QIcon privateBrowsingIcon()
QIcon bookmarkIcon
Definition: iconprovider.h:41
void setBookmarkIcon(const QIcon &icon)
static IconProvider * instance()
static QIcon iconForDomain(const QUrl &url, bool allowNull=false)
static QImage imageForDomain(const QUrl &url, bool allowNull=false)
static QIcon emptyWebIcon()
static QIcon newWindowIcon()
void clearOldIconsInDatabase()
static QIcon newTabIcon()
void saveIconsToDatabase()
static QIcon standardIcon(QStyle::StandardPixmap icon)
static QImage emptyWebImage()
static QString escapeSqlGlobString(QString urlString)
Definition: qztools.cpp:247
static SqlDatabase * instance()
QIcon icon(bool allowNull=false) const
Definition: webview.cpp:90
#define mApp
i
Definition: i18n.py:23
#define QSL(x)
Definition: qzcommon.h:40