summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJavier S. Pedro <maemo@javispedro.com>2013-04-02 04:27:30 +0200
committerJavier S. Pedro <maemo@javispedro.com>2013-04-02 04:27:30 +0200
commit7107c0f96f10303ea49bf5dc27f525e4cbc191d9 (patch)
treea6d32dc1103cd5340690db0afd18c56cc951c887
parent9f12671ee8dd38a130a16c9146001c9c2494d77c (diff)
downloadtapasboard-7107c0f96f10303ea49bf5dc27f525e4cbc191d9.tar.gz
tapasboard-7107c0f96f10303ea49bf5dc27f525e4cbc191d9.zip
add some initial support for showing images
-rw-r--r--board.cpp36
-rw-r--r--board.h1
-rw-r--r--boardmanager.cpp15
-rw-r--r--boardmanager.h5
-rw-r--r--global.h4
-rw-r--r--imageprovider.cpp110
-rw-r--r--imageprovider.h25
-rw-r--r--main.cpp6
-rw-r--r--tapasboard.pro6
9 files changed, 178 insertions, 30 deletions
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(), "<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()
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 <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;
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 <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
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<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