Falkon Develop
Cross-platform Qt-based web browser
speeddial.cpp
Go to the documentation of this file.
1/* ============================================================
2* Falkon - Qt web browser
3* Copyright (C) 2010-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 "speeddial.h"
19#include "pagethumbnailer.h"
20#include "settings.h"
21#include "datapaths.h"
22#include "qztools.h"
23#include "autosaver.h"
24
25#include <QDir>
26#include <QCryptographicHash>
27#include <QFileDialog>
28#include <QImage>
29#include <QJsonDocument>
30
31#define ENSURE_LOADED if (!m_loaded) loadSettings();
32
33SpeedDial::SpeedDial(QObject* parent)
34 : QObject(parent)
35 , m_maxPagesInRow(4)
36 , m_sizeOfSpeedDials(231)
37 , m_sdcentered(false)
38 , m_loaded(false)
39 , m_regenerateScript(true)
40{
41 m_autoSaver = new AutoSaver(this);
42 connect(m_autoSaver, &AutoSaver::save, this, &SpeedDial::saveSettings);
43 connect(this, &SpeedDial::pagesChanged, m_autoSaver, &AutoSaver::changeOccurred);
44}
45
47{
48 m_autoSaver->saveIfNecessary();
49}
50
52{
53 m_loaded = true;
54
55 Settings settings;
56 settings.beginGroup(QSL("SpeedDial"));
57 QString allPages = settings.value(QSL("pages"), QString()).toString();
58 setBackgroundImage(settings.value(QSL("background"), QString()).toString());
59 m_backgroundImageSize = settings.value(QSL("backsize"), QSL("auto")).toString();
60 m_maxPagesInRow = settings.value(QSL("pagesrow"), 4).toInt();
61 m_sizeOfSpeedDials = settings.value(QSL("sdsize"), 231).toInt();
62 m_sdcentered = settings.value(QSL("sdcenter"), false).toBool();
63 m_lockDials = settings.value(QSL("lock-dials"), false).toBool();
64 settings.endGroup();
65
66 if (allPages.isEmpty()) {
67 allPages = QL1S("url:\"https://www.falkon.org\"|title:\"Falkon\";"
68 "url:\"https://store.falkon.org\"|title:\"Falkon Store\";"
69 "url:\"https://www.kde.org\"|title:\"KDE Planet\";"
70 "url:\"https://planet.kde.org\"|title:\"KDE Community\";");
71 }
72 changed(allPages);
73
74 m_thumbnailsDir = DataPaths::currentProfilePath() + QSL("/thumbnails/");
75
76 // If needed, create thumbnails directory
77 if (!QDir(m_thumbnailsDir).exists()) {
78 QDir(DataPaths::currentProfilePath()).mkdir(QSL("thumbnails"));
79 }
80}
81
82void SpeedDial::saveSettings()
83{
85
86 Settings settings;
87 settings.beginGroup(QSL("SpeedDial"));
88 settings.setValue(QSL("pages"), generateAllPages());
89 settings.setValue(QSL("background"), m_backgroundImageUrl);
90 settings.setValue(QSL("backsize"), m_backgroundImageSize);
91 settings.setValue(QSL("pagesrow"), m_maxPagesInRow);
92 settings.setValue(QSL("sdsize"), m_sizeOfSpeedDials);
93 settings.setValue(QSL("sdcenter"), m_sdcentered);
94 settings.setValue(QSL("lock-dials"), m_lockDials);
95 settings.endGroup();
96}
97
99{
101
102 QString urlString = url.toString();
103 if (urlString.endsWith(QL1C('/')))
104 urlString = urlString.left(urlString.size() - 1);
105
106 for (const Page &page : std::as_const(m_pages)) {
107 if (page.url == urlString) {
108 return page;
109 }
110 }
111
112 return {};
113}
114
116{
118
119 if (key < 0 || m_pages.count() <= key) {
120 return {};
121 }
122
123 return QUrl::fromEncoded(m_pages.at(key).url.toUtf8());
124}
125
126void SpeedDial::addPage(const QUrl &url, const QString &title)
127{
129
130 if (url.isEmpty()) {
131 return;
132 }
133
134 Page page;
135 page.title = escapeTitle(title);
136 page.url = escapeUrl(url.toString());
137
138 m_pages.append(page);
139 m_regenerateScript = true;
140
141 Q_EMIT pagesChanged();
142}
143
145{
147
148 if (!page.isValid()) {
149 return;
150 }
151
153 m_pages.removeAll(page);
154 m_regenerateScript = true;
155
156 Q_EMIT pagesChanged();
157}
158
160{
162
163 return m_maxPagesInRow;
164}
165
167{
169
170 return m_sizeOfSpeedDials;
171}
172
174{
176
177 return m_sdcentered;
178}
179
181{
183
184 return m_lockDials;
185}
186
188{
190
191 return m_backgroundImage;
192}
193
195{
196 return m_backgroundImageUrl;
197}
198
200{
202
203 return m_backgroundImageSize;
204}
205
207{
209
210 if (!m_regenerateScript) {
211 return m_initialScript;
212 }
213
214 m_regenerateScript = false;
215 m_initialScript.clear();
216
217 QVariantList pages;
218
219 for (const Page &page : std::as_const(m_pages)) {
220 QString imgSource = m_thumbnailsDir + QString::fromLatin1(QCryptographicHash::hash(page.url.toUtf8(), QCryptographicHash::Md4).toHex()) + QSL(".png");
221
222 if (!QFile(imgSource).exists()) {
223 imgSource = QSL("qrc:html/loading.gif");
224
225 if (!page.isValid()) {
226 imgSource.clear();
227 }
228 }
229 else {
230 imgSource = QzTools::pixmapToDataUrl(QPixmap(imgSource)).toString();
231 }
232
233 QVariantMap map;
234 map[QSL("url")] = page.url;
235 map[QSL("title")] = page.title;
236 map[QSL("img")] = imgSource;
237 pages.append(map);
238 }
239
240 m_initialScript = QString::fromUtf8(QJsonDocument::fromVariant(pages).toJson(QJsonDocument::Compact));
241 return m_initialScript;
242}
243
244void SpeedDial::changed(const QString &allPages)
245{
246 const QStringList entries = allPages.split(QLatin1String("\";"), Qt::SkipEmptyParts);
247 m_pages.clear();
248
249 for (const QString &entry : entries) {
250 if (entry.isEmpty()) {
251 continue;
252 }
253
254 const QStringList tmp = entry.split(QLatin1String("\"|"), Qt::SkipEmptyParts);
255 if (tmp.count() != 2) {
256 continue;
257 }
258
259 Page page;
260 page.url = tmp.at(0).mid(5);
261 page.title = tmp.at(1).mid(7);
262
263 if (page.url.endsWith(QL1C('/')))
264 page.url = page.url.left(page.url.size() - 1);
265
266 m_pages.append(page);
267 }
268
269 m_regenerateScript = true;
270 Q_EMIT pagesChanged();
271}
272
273void SpeedDial::loadThumbnail(const QString &url, bool loadTitle)
274{
275 auto* thumbnailer = new PageThumbnailer(this);
276 thumbnailer->setUrl(QUrl::fromEncoded(url.toUtf8()));
277 thumbnailer->setLoadTitle(loadTitle);
278 connect(thumbnailer, &PageThumbnailer::thumbnailCreated, this, &SpeedDial::thumbnailCreated);
279
280 thumbnailer->start();
281}
282
283void SpeedDial::removeImageForUrl(const QString &url)
284{
285 QString fileName = m_thumbnailsDir + QString::fromLatin1(QCryptographicHash::hash(url.toUtf8(), QCryptographicHash::Md4).toHex()) + QSL(".png");
286
287 if (QFile(fileName).exists()) {
288 QFile(fileName).remove();
289 }
290}
291
293{
294 const QString fileTypes = QSL("%3(*.png *.jpg *.jpeg *.bmp *.gif *.svg *.tiff)").arg(tr("Image files"));
295 const QString image = QzTools::getOpenFileName(QSL("SpeedDial-GetOpenFileName"), nullptr, tr("Click to select image..."), QDir::homePath(), fileTypes);
296
297 if (image.isEmpty())
298 return {};
299
300 return {QzTools::pixmapToDataUrl(QPixmap(image)).toString(), QString::fromUtf8(QUrl::fromLocalFile(image).toEncoded())};
301}
302
303QString SpeedDial::urlFromUserInput(const QString &url)
304{
305 return QUrl::fromUserInput(url).toString();
306}
307
308void SpeedDial::setBackgroundImage(const QString &image)
309{
310 m_backgroundImage = QzTools::pixmapToDataUrl(QPixmap(QUrl(image).toLocalFile())).toString();
311 m_backgroundImageUrl = image;
312}
313
314void SpeedDial::setBackgroundImageSize(const QString &size)
315{
316 m_backgroundImageSize = size;
317}
318
320{
321 m_maxPagesInRow = count;
322}
323
324void SpeedDial::setSdSize(int count)
325{
326 m_sizeOfSpeedDials = count;
327}
328
329void SpeedDial::setSdCentered(bool centered)
330{
331 m_sdcentered = centered;
332
333 m_autoSaver->changeOccurred();
334}
335
336void SpeedDial::setLockDials(bool lockDials)
337{
338 m_lockDials = lockDials;
339}
340
341void SpeedDial::thumbnailCreated(const QPixmap &pixmap)
342{
343 auto* thumbnailer = qobject_cast<PageThumbnailer*>(sender());
344 if (!thumbnailer) {
345 return;
346 }
347
348 bool loadTitle = thumbnailer->loadTitle();
349 QString title = thumbnailer->title();
350 QString url = thumbnailer->url().toString();
351 QString fileName = m_thumbnailsDir + QString::fromLatin1(QCryptographicHash::hash(url.toUtf8(), QCryptographicHash::Md4).toHex()) + QSL(".png");
352
353 if (pixmap.isNull()) {
354 fileName = QSL(":/html/broken-page.svg");
355 title = tr("Unable to load");
356 }
357 else {
358 if (!pixmap.save(fileName, "PNG")) {
359 qWarning() << "SpeedDial::thumbnailCreated Cannot save thumbnail to " << fileName;
360 }
361 //fileName = QUrl::fromLocalFile(fileName).toString();
362 }
363
364 m_regenerateScript = true;
365 thumbnailer->deleteLater();
366
367 if (loadTitle)
368 Q_EMIT pageTitleLoaded(url, title);
369
370 Q_EMIT thumbnailLoaded(url, QzTools::pixmapToDataUrl(QPixmap(fileName)).toString());
371}
372
373QString SpeedDial::escapeTitle(QString title) const
374{
375 title.replace(QLatin1Char('"'), QLatin1String("&quot;"));
376 title.replace(QLatin1Char('\''), QLatin1String("&apos;"));
377 return title;
378}
379
380QString SpeedDial::escapeUrl(QString url) const
381{
382 url.remove(QLatin1Char('"'));
383 url.remove(QLatin1Char('\''));
384 return url;
385}
386
387QString SpeedDial::generateAllPages()
388{
389 QString allPages;
390
391 for (const Page &page : std::as_const(m_pages)) {
392 const QString string = QSL(R"(url:"%1"|title:"%2";)").arg(page.url, page.title);
393 allPages.append(string);
394 }
395
396 return allPages;
397}
398
399QList<SpeedDial::Page> SpeedDial::pages()
400{
402
403 return m_pages;
404}
void changeOccurred()
Definition: autosaver.cpp:39
void save()
void saveIfNecessary()
Definition: autosaver.cpp:31
static QString currentProfilePath()
Definition: datapaths.cpp:95
void thumbnailCreated(const QPixmap &)
static QUrl pixmapToDataUrl(const QPixmap &pix)
Definition: qztools.cpp:83
static QString getOpenFileName(const QString &name, QWidget *parent=nullptr, const QString &caption=QString(), const QString &dir=QString(), const QString &filter=QString(), QString *selectedFilter=nullptr, QFileDialog::Options options=QFileDialog::Options())
Definition: qztools.cpp:659
void beginGroup(const QString &prefix)
Definition: settings.cpp:79
void endGroup()
Definition: settings.cpp:84
QVariant value(const QString &key, const QVariant &defaultValue=QVariant())
Definition: settings.cpp:74
void setValue(const QString &key, const QVariant &defaultValue=QVariant())
Definition: settings.cpp:69
void pageTitleLoaded(const QString &url, const QString &title)
int sdSize()
Definition: speeddial.cpp:166
void thumbnailLoaded(const QString &url, const QString &src)
void setBackgroundImageSize(const QString &size)
Definition: speeddial.cpp:314
QStringList getOpenFileName()
Definition: speeddial.cpp:292
QString initialScript()
Definition: speeddial.cpp:206
void removeImageForUrl(const QString &url)
Definition: speeddial.cpp:283
QUrl urlForShortcut(int key)
Definition: speeddial.cpp:115
QString backgroundImageSize()
Definition: speeddial.cpp:199
void removePage(const Page &page)
Definition: speeddial.cpp:144
bool sdCenter()
Definition: speeddial.cpp:173
QString urlFromUserInput(const QString &url)
Definition: speeddial.cpp:303
void pagesChanged()
SpeedDial(QObject *parent=nullptr)
Definition: speeddial.cpp:33
bool lockDials()
Definition: speeddial.cpp:180
void loadSettings()
Definition: speeddial.cpp:51
QString backgroundImageUrl()
Definition: speeddial.cpp:194
Page pageForUrl(const QUrl &url)
Definition: speeddial.cpp:98
QString backgroundImage()
Definition: speeddial.cpp:187
void setSdSize(int count)
Definition: speeddial.cpp:324
QList< Page > pages()
Definition: speeddial.cpp:399
void setBackgroundImage(const QString &image)
Definition: speeddial.cpp:308
void setSdCentered(bool centered)
Definition: speeddial.cpp:329
void changed(const QString &allPages)
Definition: speeddial.cpp:244
void setPagesInRow(int count)
Definition: speeddial.cpp:319
void addPage(const QUrl &url, const QString &title)
Definition: speeddial.cpp:126
void setLockDials(bool lockDials)
Definition: speeddial.cpp:336
void loadThumbnail(const QString &url, bool loadTitle)
Definition: speeddial.cpp:273
int pagesInRow()
Definition: speeddial.cpp:159
#define QL1S(x)
Definition: qzcommon.h:44
#define QL1C(x)
Definition: qzcommon.h:48
#define QSL(x)
Definition: qzcommon.h:40
#define ENSURE_LOADED
Definition: speeddial.cpp:31
bool isValid() const
Definition: speeddial.h:41
QString url
Definition: speeddial.h:39
QString title
Definition: speeddial.h:38