File indexing completed on 2024-05-12 04:58:18
0001 /* ============================================================ 0002 * Falkon - Qt web browser 0003 * Copyright (C) 2010-2017 David Rosca <nowrep@gmail.com> 0004 * 0005 * This program is free software: you can redistribute it and/or modify 0006 * it under the terms of the GNU General Public License as published by 0007 * the Free Software Foundation, either version 3 of the License, or 0008 * (at your option) any later version. 0009 * 0010 * This program is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 * GNU General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU General Public License 0016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0017 * ============================================================ */ 0018 #include "speeddial.h" 0019 #include "pagethumbnailer.h" 0020 #include "settings.h" 0021 #include "datapaths.h" 0022 #include "qztools.h" 0023 #include "autosaver.h" 0024 0025 #include <QDir> 0026 #include <QCryptographicHash> 0027 #include <QFileDialog> 0028 #include <QImage> 0029 #include <QJsonDocument> 0030 0031 #define ENSURE_LOADED if (!m_loaded) loadSettings(); 0032 0033 SpeedDial::SpeedDial(QObject* parent) 0034 : QObject(parent) 0035 , m_maxPagesInRow(4) 0036 , m_sizeOfSpeedDials(231) 0037 , m_sdcentered(false) 0038 , m_loaded(false) 0039 , m_regenerateScript(true) 0040 { 0041 m_autoSaver = new AutoSaver(this); 0042 connect(m_autoSaver, &AutoSaver::save, this, &SpeedDial::saveSettings); 0043 connect(this, &SpeedDial::pagesChanged, m_autoSaver, &AutoSaver::changeOccurred); 0044 } 0045 0046 SpeedDial::~SpeedDial() 0047 { 0048 m_autoSaver->saveIfNecessary(); 0049 } 0050 0051 void SpeedDial::loadSettings() 0052 { 0053 m_loaded = true; 0054 0055 Settings settings; 0056 settings.beginGroup(QSL("SpeedDial")); 0057 QString allPages = settings.value(QSL("pages"), QString()).toString(); 0058 setBackgroundImage(settings.value(QSL("background"), QString()).toString()); 0059 m_backgroundImageSize = settings.value(QSL("backsize"), QSL("auto")).toString(); 0060 m_maxPagesInRow = settings.value(QSL("pagesrow"), 4).toInt(); 0061 m_sizeOfSpeedDials = settings.value(QSL("sdsize"), 231).toInt(); 0062 m_sdcentered = settings.value(QSL("sdcenter"), false).toBool(); 0063 m_lockDials = settings.value(QSL("lock-dials"), false).toBool(); 0064 settings.endGroup(); 0065 0066 if (allPages.isEmpty()) { 0067 allPages = QL1S("url:\"https://www.falkon.org\"|title:\"Falkon\";" 0068 "url:\"https://store.falkon.org\"|title:\"Falkon Store\";" 0069 "url:\"https://www.kde.org\"|title:\"KDE Planet\";" 0070 "url:\"https://planet.kde.org\"|title:\"KDE Community\";"); 0071 } 0072 changed(allPages); 0073 0074 m_thumbnailsDir = DataPaths::currentProfilePath() + QSL("/thumbnails/"); 0075 0076 // If needed, create thumbnails directory 0077 if (!QDir(m_thumbnailsDir).exists()) { 0078 QDir(DataPaths::currentProfilePath()).mkdir(QSL("thumbnails")); 0079 } 0080 } 0081 0082 void SpeedDial::saveSettings() 0083 { 0084 ENSURE_LOADED; 0085 0086 Settings settings; 0087 settings.beginGroup(QSL("SpeedDial")); 0088 settings.setValue(QSL("pages"), generateAllPages()); 0089 settings.setValue(QSL("background"), m_backgroundImageUrl); 0090 settings.setValue(QSL("backsize"), m_backgroundImageSize); 0091 settings.setValue(QSL("pagesrow"), m_maxPagesInRow); 0092 settings.setValue(QSL("sdsize"), m_sizeOfSpeedDials); 0093 settings.setValue(QSL("sdcenter"), m_sdcentered); 0094 settings.setValue(QSL("lock-dials"), m_lockDials); 0095 settings.endGroup(); 0096 } 0097 0098 SpeedDial::Page SpeedDial::pageForUrl(const QUrl &url) 0099 { 0100 ENSURE_LOADED; 0101 0102 QString urlString = url.toString(); 0103 if (urlString.endsWith(QL1C('/'))) 0104 urlString = urlString.left(urlString.size() - 1); 0105 0106 for (const Page &page : std::as_const(m_pages)) { 0107 if (page.url == urlString) { 0108 return page; 0109 } 0110 } 0111 0112 return {}; 0113 } 0114 0115 QUrl SpeedDial::urlForShortcut(int key) 0116 { 0117 ENSURE_LOADED; 0118 0119 if (key < 0 || m_pages.count() <= key) { 0120 return {}; 0121 } 0122 0123 return QUrl::fromEncoded(m_pages.at(key).url.toUtf8()); 0124 } 0125 0126 void SpeedDial::addPage(const QUrl &url, const QString &title) 0127 { 0128 ENSURE_LOADED; 0129 0130 if (url.isEmpty()) { 0131 return; 0132 } 0133 0134 Page page; 0135 page.title = escapeTitle(title); 0136 page.url = escapeUrl(url.toString()); 0137 0138 m_pages.append(page); 0139 m_regenerateScript = true; 0140 0141 Q_EMIT pagesChanged(); 0142 } 0143 0144 void SpeedDial::removePage(const Page &page) 0145 { 0146 ENSURE_LOADED; 0147 0148 if (!page.isValid()) { 0149 return; 0150 } 0151 0152 removeImageForUrl(page.url); 0153 m_pages.removeAll(page); 0154 m_regenerateScript = true; 0155 0156 Q_EMIT pagesChanged(); 0157 } 0158 0159 int SpeedDial::pagesInRow() 0160 { 0161 ENSURE_LOADED; 0162 0163 return m_maxPagesInRow; 0164 } 0165 0166 int SpeedDial::sdSize() 0167 { 0168 ENSURE_LOADED; 0169 0170 return m_sizeOfSpeedDials; 0171 } 0172 0173 bool SpeedDial::sdCenter() 0174 { 0175 ENSURE_LOADED; 0176 0177 return m_sdcentered; 0178 } 0179 0180 bool SpeedDial::lockDials() 0181 { 0182 ENSURE_LOADED; 0183 0184 return m_lockDials; 0185 } 0186 0187 QString SpeedDial::backgroundImage() 0188 { 0189 ENSURE_LOADED; 0190 0191 return m_backgroundImage; 0192 } 0193 0194 QString SpeedDial::backgroundImageUrl() 0195 { 0196 return m_backgroundImageUrl; 0197 } 0198 0199 QString SpeedDial::backgroundImageSize() 0200 { 0201 ENSURE_LOADED; 0202 0203 return m_backgroundImageSize; 0204 } 0205 0206 QString SpeedDial::initialScript() 0207 { 0208 ENSURE_LOADED; 0209 0210 if (!m_regenerateScript) { 0211 return m_initialScript; 0212 } 0213 0214 m_regenerateScript = false; 0215 m_initialScript.clear(); 0216 0217 QVariantList pages; 0218 0219 for (const Page &page : std::as_const(m_pages)) { 0220 QString imgSource = m_thumbnailsDir + QString::fromLatin1(QCryptographicHash::hash(page.url.toUtf8(), QCryptographicHash::Md4).toHex()) + QSL(".png"); 0221 0222 if (!QFile(imgSource).exists()) { 0223 imgSource = QSL("qrc:html/loading.gif"); 0224 0225 if (!page.isValid()) { 0226 imgSource.clear(); 0227 } 0228 } 0229 else { 0230 imgSource = QzTools::pixmapToDataUrl(QPixmap(imgSource)).toString(); 0231 } 0232 0233 QVariantMap map; 0234 map[QSL("url")] = page.url; 0235 map[QSL("title")] = page.title; 0236 map[QSL("img")] = imgSource; 0237 pages.append(map); 0238 } 0239 0240 m_initialScript = QString::fromUtf8(QJsonDocument::fromVariant(pages).toJson(QJsonDocument::Compact)); 0241 return m_initialScript; 0242 } 0243 0244 void SpeedDial::changed(const QString &allPages) 0245 { 0246 const QStringList entries = allPages.split(QLatin1String("\";"), Qt::SkipEmptyParts); 0247 m_pages.clear(); 0248 0249 for (const QString &entry : entries) { 0250 if (entry.isEmpty()) { 0251 continue; 0252 } 0253 0254 const QStringList tmp = entry.split(QLatin1String("\"|"), Qt::SkipEmptyParts); 0255 if (tmp.count() != 2) { 0256 continue; 0257 } 0258 0259 Page page; 0260 page.url = tmp.at(0).mid(5); 0261 page.title = tmp.at(1).mid(7); 0262 0263 if (page.url.endsWith(QL1C('/'))) 0264 page.url = page.url.left(page.url.size() - 1); 0265 0266 m_pages.append(page); 0267 } 0268 0269 m_regenerateScript = true; 0270 Q_EMIT pagesChanged(); 0271 } 0272 0273 void SpeedDial::loadThumbnail(const QString &url, bool loadTitle) 0274 { 0275 auto* thumbnailer = new PageThumbnailer(this); 0276 thumbnailer->setUrl(QUrl::fromEncoded(url.toUtf8())); 0277 thumbnailer->setLoadTitle(loadTitle); 0278 connect(thumbnailer, &PageThumbnailer::thumbnailCreated, this, &SpeedDial::thumbnailCreated); 0279 0280 thumbnailer->start(); 0281 } 0282 0283 void SpeedDial::removeImageForUrl(const QString &url) 0284 { 0285 QString fileName = m_thumbnailsDir + QString::fromLatin1(QCryptographicHash::hash(url.toUtf8(), QCryptographicHash::Md4).toHex()) + QSL(".png"); 0286 0287 if (QFile(fileName).exists()) { 0288 QFile(fileName).remove(); 0289 } 0290 } 0291 0292 QStringList SpeedDial::getOpenFileName() 0293 { 0294 const QString fileTypes = QSL("%3(*.png *.jpg *.jpeg *.bmp *.gif *.svg *.tiff)").arg(tr("Image files")); 0295 const QString image = QzTools::getOpenFileName(QSL("SpeedDial-GetOpenFileName"), nullptr, tr("Click to select image..."), QDir::homePath(), fileTypes); 0296 0297 if (image.isEmpty()) 0298 return {}; 0299 0300 return {QzTools::pixmapToDataUrl(QPixmap(image)).toString(), QString::fromUtf8(QUrl::fromLocalFile(image).toEncoded())}; 0301 } 0302 0303 QString SpeedDial::urlFromUserInput(const QString &url) 0304 { 0305 return QUrl::fromUserInput(url).toString(); 0306 } 0307 0308 void SpeedDial::setBackgroundImage(const QString &image) 0309 { 0310 m_backgroundImage = QzTools::pixmapToDataUrl(QPixmap(QUrl(image).toLocalFile())).toString(); 0311 m_backgroundImageUrl = image; 0312 } 0313 0314 void SpeedDial::setBackgroundImageSize(const QString &size) 0315 { 0316 m_backgroundImageSize = size; 0317 } 0318 0319 void SpeedDial::setPagesInRow(int count) 0320 { 0321 m_maxPagesInRow = count; 0322 } 0323 0324 void SpeedDial::setSdSize(int count) 0325 { 0326 m_sizeOfSpeedDials = count; 0327 } 0328 0329 void SpeedDial::setSdCentered(bool centered) 0330 { 0331 m_sdcentered = centered; 0332 0333 m_autoSaver->changeOccurred(); 0334 } 0335 0336 void SpeedDial::setLockDials(bool lockDials) 0337 { 0338 m_lockDials = lockDials; 0339 } 0340 0341 void SpeedDial::thumbnailCreated(const QPixmap &pixmap) 0342 { 0343 auto* thumbnailer = qobject_cast<PageThumbnailer*>(sender()); 0344 if (!thumbnailer) { 0345 return; 0346 } 0347 0348 bool loadTitle = thumbnailer->loadTitle(); 0349 QString title = thumbnailer->title(); 0350 QString url = thumbnailer->url().toString(); 0351 QString fileName = m_thumbnailsDir + QString::fromLatin1(QCryptographicHash::hash(url.toUtf8(), QCryptographicHash::Md4).toHex()) + QSL(".png"); 0352 0353 if (pixmap.isNull()) { 0354 fileName = QSL(":/html/broken-page.svg"); 0355 title = tr("Unable to load"); 0356 } 0357 else { 0358 if (!pixmap.save(fileName, "PNG")) { 0359 qWarning() << "SpeedDial::thumbnailCreated Cannot save thumbnail to " << fileName; 0360 } 0361 //fileName = QUrl::fromLocalFile(fileName).toString(); 0362 } 0363 0364 m_regenerateScript = true; 0365 thumbnailer->deleteLater(); 0366 0367 if (loadTitle) 0368 Q_EMIT pageTitleLoaded(url, title); 0369 0370 Q_EMIT thumbnailLoaded(url, QzTools::pixmapToDataUrl(QPixmap(fileName)).toString()); 0371 } 0372 0373 QString SpeedDial::escapeTitle(QString title) const 0374 { 0375 title.replace(QLatin1Char('"'), QLatin1String(""")); 0376 title.replace(QLatin1Char('\''), QLatin1String("'")); 0377 return title; 0378 } 0379 0380 QString SpeedDial::escapeUrl(QString url) const 0381 { 0382 url.remove(QLatin1Char('"')); 0383 url.remove(QLatin1Char('\'')); 0384 return url; 0385 } 0386 0387 QString SpeedDial::generateAllPages() 0388 { 0389 QString allPages; 0390 0391 for (const Page &page : std::as_const(m_pages)) { 0392 const QString string = QSL(R"(url:"%1"|title:"%2";)").arg(page.url, page.title); 0393 allPages.append(string); 0394 } 0395 0396 return allPages; 0397 } 0398 0399 QList<SpeedDial::Page> SpeedDial::pages() 0400 { 0401 ENSURE_LOADED; 0402 0403 return m_pages; 0404 }