From 1c49173c8f62d3b9e609d22a027a42376db5d3de Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 12 Nov 2015 17:03:11 +0100 Subject: Start refactoring download helpers and icon management --- src/FileDownloader.cpp | 65 +++++++++++++++++++++++++++++++++++----------- src/FileDownloader.h | 27 ++++++++++++------- src/dialog.cpp | 11 +++++--- src/dialog.h | 4 ++- src/session.h | 3 ++- src/sessionsiconholder.cpp | 47 +++++++++++++++++++-------------- src/sessionsiconholder.h | 12 ++++++--- src/sessiontreemodel.cpp | 56 +-------------------------------------- src/sessiontreemodel.h | 3 --- src/vsession.cpp | 62 ++++++++++++++++++++++++++++++++++++++++--- src/vsession.h | 2 +- src/xsession.cpp | 5 ++-- src/xsession.h | 2 +- 13 files changed, 181 insertions(+), 118 deletions(-) diff --git a/src/FileDownloader.cpp b/src/FileDownloader.cpp index 1a0297f..b9f79be 100644 --- a/src/FileDownloader.cpp +++ b/src/FileDownloader.cpp @@ -9,30 +9,65 @@ #include "FileDownloader.h" -FileDownloader::FileDownloader(QObject *parent) : - QObject(parent) { - connect(&m_WebCtrl, SIGNAL(finished(QNetworkReply*)), - SLOT(fileDownloaded(QNetworkReply*))); +// Maximum size of download +#define MAXSIZE (200000) + +static QNetworkAccessManager m_WebCtrl; + +FileDownloader::FileDownloader(const QUrl& fileUrl, QObject *parent) : + QObject(parent), started(false), url(fileUrl) { } FileDownloader::~FileDownloader() { } -void FileDownloader::connectSlot(QObject* obj, const char* slot) { - QObject::connect(this, SIGNAL(downloaded(QString&, QByteArray)), - obj, slot); +bool FileDownloader::downloadFile() { + if (this->started) + return true; + QNetworkReply *reply = m_WebCtrl.get(QNetworkRequest(this->url)); + if (reply == NULL) + return false; + this->started = true; + connect(reply, SIGNAL(finished()), SLOT(fileDownloaded())); + connect(reply, SIGNAL(downloadProgress(qint64, qint64)), SLOT(downloadProgress(qint64, qint64))); + return true; } -void FileDownloader::fileDownloaded(QNetworkReply* pReply) { - QByteArray downloadedData = pReply->readAll(); +/* + * Slots from networkreply + */ + +void FileDownloader::downloadFailed(QNetworkReply::NetworkError) { + QNetworkReply *reply = (QNetworkReply*)this->sender(); + killReply(reply); + emit downloaded(this->url, QByteArray()); +} + +void FileDownloader::fileDownloaded() { + QNetworkReply *reply = (QNetworkReply*)this->sender(); + if (reply == NULL) + return; + QByteArray downloadedData(reply->readAll()); + killReply(reply); //emit a signal - pReply->deleteLater(); - emit downloaded(this->fileName, downloadedData); - this->deleteLater(); // TODO: RAII - Object should not delete itself + emit downloaded(this->url, downloadedData); +} + +void FileDownloader::downloadProgress(qint64 received, qint64 totalSize) { + QNetworkReply *reply = (QNetworkReply*)this->sender(); + if (reply == NULL) + return; + if (received > MAXSIZE || totalSize > MAXSIZE) { + killReply(reply); + emit downloaded(this->url, QByteArray()); + } } -void FileDownloader::downloadFile(const QUrl& fileUrl) { - this->fileName = fileUrl.toString(); - m_WebCtrl.get(QNetworkRequest(fileUrl)); +void FileDownloader::killReply(QNetworkReply *reply) { + if (reply == NULL) + return; + reply->blockSignals(true); + reply->abort(); + reply->deleteLater(); } diff --git a/src/FileDownloader.h b/src/FileDownloader.h index fccfa7a..f66f7f9 100644 --- a/src/FileDownloader.h +++ b/src/FileDownloader.h @@ -17,28 +17,35 @@ class FileDownloader : public QObject { Q_OBJECT -public: - explicit FileDownloader(QObject *parent = 0); - virtual ~FileDownloader(); +public: - void downloadFile(const QUrl& fileUrl); + explicit FileDownloader(const QUrl& fileUrl, QObject *parent = 0); - void connectSlot(QObject* obj, const char* slot); + virtual ~FileDownloader(); - QByteArray downloadedData() const; + bool downloadFile(); signals: - void downloaded(QString& fileName, QByteArray downloadedData); + + /** + * Triggered when the download has finished. + * On error, downloadedData will be empty. + */ + void downloaded(const QUrl& url, const QByteArray& downloadedData); private slots: - void fileDownloaded(QNetworkReply* pReply); + void fileDownloaded(); + void downloadFailed(QNetworkReply::NetworkError code); + void downloadProgress(qint64 received, qint64 totalSize); private: - QNetworkAccessManager m_WebCtrl; - QString fileName; + void killReply(QNetworkReply *reply); + + bool started; + QUrl url; }; diff --git a/src/dialog.cpp b/src/dialog.cpp index 7cf8cd1..605aeb4 100644 --- a/src/dialog.cpp +++ b/src/dialog.cpp @@ -63,13 +63,12 @@ Dialog::Dialog(QWidget *parent) // TODO: Implement bug report dialog :) ui->buttonBugReport->setEnabled(false); + QObject::connect(SessionsIconHolder::get(), SIGNAL(iconDownloaded(const QUrl&, const QIcon&)), + this, SLOT(iconDownloaded(const QUrl&, const QIcon&))); } Dialog::~Dialog() { delete ui; - delete model_[0]; - delete model_[1]; - delete model_[2]; } void Dialog::changeEvent(QEvent *e) { @@ -600,3 +599,9 @@ void Dialog::keyPressEvent(QKeyEvent* event) { case Qt::Key_H: this->on_helpNewsButton_clicked(); break; } } + +void Dialog::iconDownloaded(const QUrl& url, const QIcon&) { + qDebug() << "Icon downloaded... (" << url << ")"; + // TODO: Check which model(s) contain an entry with this icon + model_[2]->updateView(); +} diff --git a/src/dialog.h b/src/dialog.h index 0f45466..59d2e48 100644 --- a/src/dialog.h +++ b/src/dialog.h @@ -10,6 +10,7 @@ #include "session.h" #include "sessiontreemodel.h" +#include "sessionsiconholder.h" namespace Ui { class Dialog; @@ -50,6 +51,7 @@ class Dialog : public QDialog { void onTabButtonChanged(int tab); void configClearButton(); void setListModel(QAbstractItemModel *model); + void keyPressEvent(QKeyEvent * e); private slots: void on_pushButtonStart_clicked(); @@ -62,7 +64,7 @@ class Dialog : public QDialog { void treeView_selectionChanged(const QModelIndex& current, const QModelIndex&); void on_helpNewsButton_clicked(); void onCenterTimer(); - void keyPressEvent(QKeyEvent * e); + void iconDownloaded(const QUrl& url, const QIcon& icon); public slots: void addSessionsAfterDownload(QNetworkReply* reply); diff --git a/src/session.h b/src/session.h index e1af027..515bd2d 100644 --- a/src/session.h +++ b/src/session.h @@ -2,6 +2,7 @@ #define VMCHOOSER_SESSION_H_ class QString; +class QIcon; class Session { public: @@ -13,7 +14,7 @@ class Session { virtual int priority() const = 0; virtual QString shortDescription() const = 0; virtual QString description() const = 0; - virtual QString icon() const = 0; + virtual QIcon icon() const = 0; virtual bool run() const = 0; virtual int type() const = 0; diff --git a/src/sessionsiconholder.cpp b/src/sessionsiconholder.cpp index b52d93d..7cce455 100644 --- a/src/sessionsiconholder.cpp +++ b/src/sessionsiconholder.cpp @@ -12,21 +12,27 @@ #include #include #include +#include #include "globals.h" #include "sessionsiconholder.h" #include "sessiontreemodel.h" #include "FileDownloader.h" -SessionsIconHolder::SessionsIconHolder(SessionTreeModel& sessionTreeModel):treeModel(sessionTreeModel) { +SessionsIconHolder* SessionsIconHolder::instance = NULL; + +static inline QString url2filename(const QString& url) { + return iconsTempPath + QString(QCryptographicHash::hash(url.toUtf8(), QCryptographicHash::Md5).toHex()); +} + +SessionsIconHolder::SessionsIconHolder() { QDir().mkpath(iconsTempPath); } -void SessionsIconHolder::afterDownload(QString& url, QByteArray downloadedData) { +void SessionsIconHolder::afterDownload(const QUrl& url, const QByteArray& downloadedData) { // save the data to disk - QString file_name = url.replace("http://", ""); - file_name = file_name.replace("/", "_"); - QString file_path = iconsTempPath + file_name; + QString strUrl(url.toString()); + QString file_path(url2filename(strUrl)); QFile file(file_path); if (!file.open(QFile::WriteOnly)) { if (debugMode) { @@ -46,10 +52,9 @@ void SessionsIconHolder::afterDownload(QString& url, QByteArray downloadedData) file.close(); QIcon icon(file_path); - icons.insert(url, icon); + icons.insert(strUrl, icon); - // trigger the SessionTreeModel to update the view - treeModel.updateView(); + emit iconDownloaded(url, icon); } QIcon SessionsIconHolder::getIcon(const QString& name) { @@ -60,10 +65,11 @@ QIcon SessionsIconHolder::getIcon(const QString& name) { // else load icon from resource QIcon icon; - if (QResource(":" + name.toLower() + ".svg").isValid()) { - icon = QIcon(":" + name.toLower() + ".svg"); - } else if (QResource(":" + name.toLower()).isValid()) { - icon = QIcon(":" + name.toLower()); + QString resName(":" + name.toLower()); + if (QResource(resName + ".svg").isValid()) { + icon = QIcon(resName + ".svg"); + } else if (QResource(resName).isValid()) { + icon = QIcon(resName); } else { icon = QIcon(); } @@ -80,20 +86,23 @@ QIcon SessionsIconHolder::getIcon(const QUrl& url) { } // search the icon in the tmp folder - QString file_name = url.toString().replace("http://", ""); - file_name = file_name.replace("/", "_"); - QString file_path = iconsTempPath + file_name; + QString strUrl(url.toString()); + QString file_path(url2filename(strUrl)); if (QFile::exists(file_path)) { QIcon icon(file_path); - icons.insert(url.toString(), icon); + icons.insert(strUrl, icon); return icon; } + // Put empty icon in map while we're downloading, so successive calls won't trigger + // more downloads before the running download finishes + icons.insert(strUrl, QIcon()); // else load icon from url - FileDownloader* fileDownloader = new FileDownloader(this); - fileDownloader->connectSlot(this, SLOT(afterDownload(QString&, QByteArray))); - fileDownloader->downloadFile(url); + FileDownloader* fileDownloader = new FileDownloader(url, this); + QObject::connect(fileDownloader, SIGNAL(downloaded(const QUrl&, const QByteArray&)), + this, SLOT(afterDownload(const QUrl&, const QByteArray&))); + fileDownloader->downloadFile(); return QIcon(); } diff --git a/src/sessionsiconholder.h b/src/sessionsiconholder.h index d960654..a13156e 100644 --- a/src/sessionsiconholder.h +++ b/src/sessionsiconholder.h @@ -21,20 +21,24 @@ class SessionTreeModel; -class SessionsIconHolder : QObject { +class SessionsIconHolder : public QObject { Q_OBJECT private: QHash icons; - SessionTreeModel& treeModel; + SessionsIconHolder(); + static SessionsIconHolder* instance; + +signals: + void iconDownloaded(const QUrl& url, const QIcon& icon); public: - SessionsIconHolder(SessionTreeModel& sessionTreeModel); QIcon getIcon(const QString& name); QIcon getIcon(const QUrl& url); + static SessionsIconHolder* get() { if (instance == NULL) instance = new SessionsIconHolder(); return instance; } public slots: - void afterDownload(QString& iconName, QByteArray downloadedData); + void afterDownload(const QUrl& url, const QByteArray& downloadedData); }; #endif /* SESSIONSICONHOLDER_H_ */ diff --git a/src/sessiontreemodel.cpp b/src/sessiontreemodel.cpp index 7b88015..aaf47b9 100644 --- a/src/sessiontreemodel.cpp +++ b/src/sessiontreemodel.cpp @@ -13,7 +13,6 @@ SessionTreeModel::SessionTreeModel(QObject *parent) : QAbstractItemModel(parent) { root_ = new SessionTreeItem("dummy"); - iconHolder = new SessionsIconHolder(*this); } SessionTreeModel::~SessionTreeModel() { @@ -55,60 +54,7 @@ QVariant SessionTreeModel::data(const QModelIndex &index, int role) const { return s->description(); if (role == Qt::DecorationRole) { if (index.column() == 0) { // TODO: is this line needed? - QString icon(s->icon()); - - if (icon.isEmpty()) { - // Nothing... - } else if (icon.startsWith("http://")) { - // try to load icon from url - QIcon url_icon = iconHolder->getIcon(QUrl(icon)); - if (!url_icon.isNull()) { - return url_icon; - } - } else if (QFileInfo(icon).isAbsolute()) { - // try to load icon from file - return QIcon(icon); - } else { - // try to load icon from QResource - QIcon res_icon = iconHolder->getIcon(icon); - if (!res_icon.isNull()) { - return res_icon; - } - } - // fallback to os icon - if (s->type() == Session::VSESSION) { - const VSession* vs = (VSession*) s; - QString os(vs->getAttribute("os", "param").toLower()); - if (!os.isEmpty()) { - QIcon osi = iconHolder->getIcon(os); - if (!osi.isNull()) - return osi; - if (os == "dos") - return iconHolder->getIcon("dos"); - if (os.startsWith("windows7")) - return iconHolder->getIcon("win7"); - if (os.startsWith("win31")) - return iconHolder->getIcon("win311"); - if (os.startsWith("windows8")) - return iconHolder->getIcon("win8"); - if (os.startsWith("win2000")) - return iconHolder->getIcon("win2000"); - if (os.startsWith("winxp")) - return iconHolder->getIcon("winxp"); - if (os.startsWith("debian")) - return iconHolder->getIcon("debian"); - if (os.startsWith("ubuntu")) - return iconHolder->getIcon("ubuntu"); - if (os.startsWith("win")) - return iconHolder->getIcon("windows"); - if (os.contains("linux")) - return iconHolder->getIcon("linux"); - } - if (vs->imgtype() == VMWARE) - return iconHolder->getIcon("vmware"); - if (vs->imgtype() == VBOX) - return iconHolder->getIcon("virtualbox"); - } + return s->icon(); } } } else if (role == Qt::DisplayRole) { diff --git a/src/sessiontreemodel.h b/src/sessiontreemodel.h index 9079c59..481c5f7 100644 --- a/src/sessiontreemodel.h +++ b/src/sessiontreemodel.h @@ -5,8 +5,6 @@ #include #include -#include "sessionsiconholder.h" - class SessionTreeItem; class Session; @@ -37,7 +35,6 @@ class SessionTreeModel : public QAbstractItemModel { private: SessionTreeItem* root_; - SessionsIconHolder* iconHolder; }; #endif // VMCHOOSER_SESSIONTREEMODEL_H diff --git a/src/vsession.cpp b/src/vsession.cpp index 5cd9d0b..45587f1 100644 --- a/src/vsession.cpp +++ b/src/vsession.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #if 0 #include // available since Qt 4.7 #endif @@ -14,7 +15,7 @@ #include // for getpwuid #include "globals.h" #include "vsession.h" -#include "httpxmldownloader.h" +#include "sessionsiconholder.h" bool VSession::init(const QString& xml, const QString& baseDirPath) { this->baseDirPath_ = baseDirPath; @@ -38,8 +39,63 @@ void VSession::addNodeWithAttribute(const QString& nodeName, node.setAttribute(attribute, value); } -QString VSession::icon() const { - return getAttribute("icon"); +QIcon VSession::icon() const { + QString icon(getAttribute("icon")); + SessionsIconHolder *iconHolder = SessionsIconHolder::get(); + if (icon.startsWith("http://")) { + // try to load icon from url + QIcon url_icon(iconHolder->getIcon(QUrl(icon))); + if (!url_icon.isNull()) { + return url_icon; + } + } else if (QFileInfo(icon).isAbsolute()) { + // try to load icon from file + QIcon file_icon(icon); + if (!file_icon.isNull()) { + return file_icon; + } + } + if (!icon.isEmpty()) { + QIcon res_icon(iconHolder->getIcon(icon)); + if (!res_icon.isNull()) { + return res_icon; + } + } + // Everything failed, try to guess the OS + QString os(getAttribute("os", "param").toLower()); + if (!os.isEmpty()) { + QIcon osi = iconHolder->getIcon(os); + if (!osi.isNull()) + return osi; + // These match vmware guestOS keywords mostly, extend for vbox... + if (os == "dos") + return iconHolder->getIcon("dos"); + if (os.startsWith("windows7")) + return iconHolder->getIcon("win7"); + if (os.startsWith("win31")) + return iconHolder->getIcon("win311"); + if (os.startsWith("windows8")) + return iconHolder->getIcon("win8"); + if (os.startsWith("win2000")) + return iconHolder->getIcon("win2000"); + if (os.startsWith("winxp")) + return iconHolder->getIcon("winxp"); + if (os.startsWith("debian")) + return iconHolder->getIcon("debian"); + if (os.startsWith("ubuntu")) + return iconHolder->getIcon("ubuntu"); + if (os.startsWith("win")) + return iconHolder->getIcon("windows"); + if (os.contains("linux")) + return iconHolder->getIcon("linux"); + } + // TODO: Maybe parse title of entry for an OS guess? + // Fallback to generic virtualizer icon + if (imgtype() == VMWARE) + return iconHolder->getIcon("vmware"); + if (imgtype() == VBOX) + return iconHolder->getIcon("virtualbox"); + return QIcon(); } QString VSession::toXml() const { diff --git a/src/vsession.h b/src/vsession.h index ea04e4e..e542818 100644 --- a/src/vsession.h +++ b/src/vsession.h @@ -33,7 +33,7 @@ class VSession : public Session { return getAttribute("description"); } - QString icon() const; + QIcon icon() const; QString os() const { return getAttribute("os"); diff --git a/src/xsession.cpp b/src/xsession.cpp index 3cd0e7e..3ae4320 100644 --- a/src/xsession.cpp +++ b/src/xsession.cpp @@ -5,6 +5,7 @@ #include #include "xsession.h" +#include "sessionsiconholder.h" void XSession::init(const QString& name, const QString& exec, const QString& comment, const QString& icon) { @@ -86,7 +87,7 @@ int XSession::priority() const { return 0; } -QString XSession::icon() const { +QIcon XSession::icon() const { QString icon(this->icon_); if (icon.isEmpty()) { @@ -105,7 +106,7 @@ QString XSession::icon() const { } } - return icon; + return SessionsIconHolder::get()->getIcon(icon); } bool XSession::run() const { diff --git a/src/xsession.h b/src/xsession.h index 4140c38..75deffc 100644 --- a/src/xsession.h +++ b/src/xsession.h @@ -27,7 +27,7 @@ class XSession : public Session { return this->comment_; } - QString icon() const; + QIcon icon() const; bool run() const; -- cgit v1.2.3-55-g7522