From 7107c0f96f10303ea49bf5dc27f525e4cbc191d9 Mon Sep 17 00:00:00 2001 From: "Javier S. Pedro" Date: Tue, 2 Apr 2013 04:27:30 +0200 Subject: add some initial support for showing images --- board.cpp | 36 +++++++----------- board.h | 1 - boardmanager.cpp | 15 ++++++++ boardmanager.h | 5 +-- global.h | 4 +- imageprovider.cpp | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ imageprovider.h | 25 +++++++++++++ main.cpp | 6 +++ tapasboard.pro | 6 ++- 9 files changed, 178 insertions(+), 30 deletions(-) create mode 100644 imageprovider.cpp create mode 100644 imageprovider.h diff --git a/board.cpp b/board.cpp index b3e113e..1be4076 100644 --- a/board.cpp +++ b/board.cpp @@ -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(), ""); + } + + // HTML newlines + text.replace("\n", "
"); + 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("")); _bbcodes << qMakePair(QRegExp("\\[hr\\]", Qt::CaseInsensitive), QString("
")); - - _bbcodes << qMakePair(QRegExp("\n"), QString("
")); } void Board::fetchConfigIfOutdated() diff --git a/board.h b/board.h index e8aecaa..f76efbe 100644 --- a/board.h +++ b/board.h @@ -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 +#include +#include + #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 _boards; diff --git a/global.h b/global.h index e2b0329..9eefb17 100644 --- a/global.h +++ b/global.h @@ -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 +#include +#include +#include +#include +#include +#include + +#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 +#include + +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 diff --git a/main.cpp b/main.cpp index ba01c2d..3c8f813 100644 --- a/main.cpp +++ b/main.cpp @@ -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 manager(new BoardManager); board_manager = manager.data(); // Set the global pointer to this singleton + QScopedPointer provider(new ImageProvider); + image_provider = provider.data(); + qmlRegisterType("com.javispedro.tapasboard", 1, 0, "FavoritesModel"); qmlRegisterType("com.javispedro.tapasboard", 1, 0, "BoardModel"); qmlRegisterType("com.javispedro.tapasboard", 1, 0, "ForumModel"); qmlRegisterType("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 -- cgit v1.2.3