diff options
-rw-r--r-- | board.cpp | 36 | ||||
-rw-r--r-- | board.h | 1 | ||||
-rw-r--r-- | boardmanager.cpp | 15 | ||||
-rw-r--r-- | boardmanager.h | 5 | ||||
-rw-r--r-- | global.h | 4 | ||||
-rw-r--r-- | imageprovider.cpp | 110 | ||||
-rw-r--r-- | imageprovider.h | 25 | ||||
-rw-r--r-- | main.cpp | 6 | ||||
-rw-r--r-- | tapasboard.pro | 6 |
9 files changed, 178 insertions, 30 deletions
@@ -118,6 +118,19 @@ QString Board::bbcodeToRichText(QString text) const text.replace(pair.first, pair.second); } + // Unfortunately, we have to process images separately + static QRegExp img_bbcode("\\[img\\]([^[]*)\\[/img\\]", Qt::CaseInsensitive); + int pos = 0; + while ((pos = img_bbcode.indexIn(text, pos)) != -1) { + QString imageUrl = img_bbcode.cap(1); + QString providerUrl = image_provider->getProviderImageUrl(imageUrl); + qDebug() << "Image" << imageUrl << "->" << providerUrl; + text.replace(pos, img_bbcode.matchedLength(), "<img src=\"" + providerUrl + "\" />"); + } + + // HTML newlines + text.replace("\n", "<br>"); + return text; } @@ -171,28 +184,9 @@ QString Board::createSlug(const QString &forumUrl) return url; } -QString Board::getDbDir() -{ - QString path; - -#ifdef Q_OS_LINUX - char * xdg_cache_dir = getenv("XDG_CACHE_HOME"); - if (xdg_cache_dir) { - path = QString::fromLocal8Bit(xdg_cache_dir) + "/tapasboard"; - } else { - path = QDir::homePath() + "/.cache/tapasboard"; - } - if (!QDir().mkpath(path)) { - qWarning() << "Failed to create directory for databases:" << path; - } -#endif - - return path; -} - QString Board::getDbPathFor(const QString &slug) { - return getDbDir() + "/" + slug + ".sqlite"; + return board_manager->getCachePath() + "/" + slug + ".sqlite"; } QString Board::getTempDbPathFor(const QString& slug) @@ -330,8 +324,6 @@ void Board::initializeBbCode() _bbcodes << qMakePair(QRegExp("\\[/url\\]", Qt::CaseInsensitive), QString("</a>")); _bbcodes << qMakePair(QRegExp("\\[hr\\]", Qt::CaseInsensitive), QString("<hr>")); - - _bbcodes << qMakePair(QRegExp("\n"), QString("<br>")); } void Board::fetchConfigIfOutdated() @@ -52,7 +52,6 @@ signals: private: static QString createSlug(const QString& forumUrl); - static QString getDbDir(); static QString getDbPathFor(const QString& slug); static QString getTempDbPathFor(const QString& slug); bool checkCompatibleDb(); diff --git a/boardmanager.cpp b/boardmanager.cpp index e2473a6..5fbcf07 100644 --- a/boardmanager.cpp +++ b/boardmanager.cpp @@ -1,3 +1,7 @@ +#include <QtCore/QDir> +#include <QtCore/QDebug> +#include <QtGui/QDesktopServices> + #include "board.h" #include "boardmanager.h" @@ -16,3 +20,14 @@ Board* BoardManager::getBoard(const QString &url) _boards.insert(url, db); return db; } + +QString BoardManager::getCachePath() const +{ + QString path = QDesktopServices::storageLocation(QDesktopServices::CacheLocation); + + if (!QDir().mkpath(path)) { + qWarning() << "Failed to create directory for databases:" << path; + } + + return path; +} diff --git a/boardmanager.h b/boardmanager.h index 8ec4c24..30ebafc 100644 --- a/boardmanager.h +++ b/boardmanager.h @@ -12,12 +12,9 @@ class BoardManager : public QObject public: explicit BoardManager(QObject *parent = 0); - Board *getBoard(const QString& url); -signals: - -public slots: + QString getCachePath() const; private: QHash<QString, Board*> _boards; @@ -2,6 +2,7 @@ #define GLOBAL_H #include "boardmanager.h" +#include "imageprovider.h" /** Time the forum config settings should be considered up to date, in days. */ #define BOARD_CONFIG_TTL 2 @@ -27,7 +28,8 @@ /** Number of posts per "block" in topic view */ #define TOPIC_PAGE_SIZE 20 - +// Some singletons extern BoardManager *board_manager; +extern ImageProvider *image_provider; #endif // GLOBAL_H diff --git a/imageprovider.cpp b/imageprovider.cpp new file mode 100644 index 0000000..6fb2807 --- /dev/null +++ b/imageprovider.cpp @@ -0,0 +1,110 @@ +#include <QtCore/QCryptographicHash> +#include <QtCore/QDir> +#include <QtCore/QEventLoop> +#include <QtCore/QUrl> +#include <QtCore/QDebug> +#include <QtNetwork/QNetworkRequest> +#include <QtNetwork/QNetworkReply> + +#include "global.h" +#include "imageprovider.h" + +// Warning: QML might call requestImage() from another thread. Be careful. + +ImageProvider::ImageProvider() : + QDeclarativeImageProvider(Image), + _cachePath(board_manager->getCachePath() + "/images"), + _manager(new QNetworkAccessManager) +{ + QDir dir; + if (!dir.mkpath(_cachePath)) { + qWarning() << "Could not create image cache path"; + } +} + +ImageProvider::~ImageProvider() +{ + delete _manager; +} + +QImage ImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize) +{ + QString remoteUrl = QUrl::fromPercentEncoding(id.toUtf8()); + QString localPath = getCachedImagePath(remoteUrl); + + qDebug() << "Loading image for " << remoteUrl; + + if (!QFile::exists(localPath)) { + if (!fetchImage(remoteUrl)) { + qWarning() << "Failed to fetch remote image" << remoteUrl; + } + } else { + qDebug() << "Local file exists" << localPath; + } + + QImage image(localPath); + QImage result; + + if (image.isNull()) { + qWarning() << "Failed to load local image" << localPath; + } + + if (requestedSize.isValid()) { + result = image.scaled(requestedSize, Qt::KeepAspectRatio); + } else { + result = image; + } + if (size) { + *size = result.size(); + } + + return result; +} + +QString ImageProvider::getProviderImageUrl(const QString &remoteUrl) +{ + return "image://tapasboard/" + QString::fromUtf8(QUrl::toPercentEncoding(remoteUrl)); +} + +QString ImageProvider::getCachedImagePath(const QString &remoteUrl) +{ + static const QRegExp regexp("[^a-z0-9]+"); + QString url = remoteUrl.toLower(); + + // Grab the extension before applying the regexp + QString extension; + int dot_pos = url.lastIndexOf('.'); + + if (dot_pos != -1) { + extension = url.mid(dot_pos); + } + + url.replace(regexp, "_"); + + return _cachePath + "/" + url + extension; +} + +bool ImageProvider::fetchImage(const QString &remoteUrl) +{ + QString localPath = getCachedImagePath(remoteUrl); + + QNetworkRequest request(remoteUrl); + QNetworkReply *reply = _manager->get(request); + qDebug() << "Start download of" << remoteUrl; + + QEventLoop loop; + loop.connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); + + loop.exec(); + + qDebug() << "End download of" << remoteUrl; + QFile localFile(localPath); + if (localFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + localFile.write(reply->readAll()); + localFile.close(); + } + + reply->deleteLater(); + + return true; +} diff --git a/imageprovider.h b/imageprovider.h new file mode 100644 index 0000000..43c8b28 --- /dev/null +++ b/imageprovider.h @@ -0,0 +1,25 @@ +#ifndef IMAGEPROVIDER_H +#define IMAGEPROVIDER_H + +#include <QtNetwork/QNetworkAccessManager> +#include <QtDeclarative/QDeclarativeImageProvider> + +class ImageProvider : public QDeclarativeImageProvider +{ +public: + ImageProvider(); + ~ImageProvider(); + + QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize = QSize()); + + QString getProviderImageUrl(const QString &remoteUrl); + QString getCachedImagePath(const QString &remoteUrl); + + bool fetchImage(const QString &remoteUrl); + +private: + QString _cachePath; + QNetworkAccessManager *_manager; +}; + +#endif // IMAGEPROVIDER_H @@ -4,12 +4,14 @@ #include "global.h" #include "board.h" +#include "imageprovider.h" #include "favoritesmodel.h" #include "boardmodel.h" #include "forummodel.h" #include "topicmodel.h" BoardManager *board_manager; +ImageProvider *image_provider; Q_DECL_EXPORT int main(int argc, char *argv[]) { @@ -21,12 +23,16 @@ Q_DECL_EXPORT int main(int argc, char *argv[]) QScopedPointer<BoardManager> manager(new BoardManager); board_manager = manager.data(); // Set the global pointer to this singleton + QScopedPointer<ImageProvider> provider(new ImageProvider); + image_provider = provider.data(); + qmlRegisterType<FavoritesModel>("com.javispedro.tapasboard", 1, 0, "FavoritesModel"); qmlRegisterType<BoardModel>("com.javispedro.tapasboard", 1, 0, "BoardModel"); qmlRegisterType<ForumModel>("com.javispedro.tapasboard", 1, 0, "ForumModel"); qmlRegisterType<TopicModel>("com.javispedro.tapasboard", 1, 0, "TopicModel"); QmlApplicationViewer viewer; + viewer.engine()->addImageProvider("tapasboard", image_provider); viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto); viewer.setMainQmlFile(QLatin1String("qml/tapasboard/main.qml")); viewer.showExpanded(); diff --git a/tapasboard.pro b/tapasboard.pro index c0c2026..a13dfc7 100644 --- a/tapasboard.pro +++ b/tapasboard.pro @@ -45,7 +45,8 @@ SOURCES += main.cpp \ fetchtopicsaction.cpp \ topicmodel.cpp \ fetchpostsaction.cpp \ - favoritesmodel.cpp + favoritesmodel.cpp \ + imageprovider.cpp # Please do not modify the following two lines. Required for deployment. include(qmlapplicationviewer/qmlapplicationviewer.pri) @@ -75,4 +76,5 @@ HEADERS += \ fetchtopicsaction.h \ topicmodel.h \ fetchpostsaction.h \ - favoritesmodel.h + favoritesmodel.h \ + imageprovider.h |