/* * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "UBDownloadManager.h" #include "core/UBApplication.h" #include "gui/UBMainWindow.h" /** The unique instance of the download manager */ static UBDownloadManager* pInstance = NULL; /** * \brief Constructor * @param parent as the parent widget * @param name as the object name */ UBDownloadManager::UBDownloadManager(QObject *parent, const char *name):QObject(parent) { setObjectName(name); init(); connect(this, SIGNAL(fileAddedToDownload()), this, SLOT(onUpdateDownloadLists())); } /** * \brief Destructor */ UBDownloadManager::~UBDownloadManager() { } /** * \brief Get the download manager * @return a pointer on the download manager */ UBDownloadManager* UBDownloadManager::downloadManager() { if(NULL == pInstance) { pInstance = new UBDownloadManager(); } return pInstance; } /** * \brief Add a file to the download list * @param desc as the given file description */ void UBDownloadManager::addFileToDownload(sDownloadFileDesc desc) { // Set the ID for this download desc.id = mLastID; mLastID++; // Add the file to the pending download list mPendingDL.append(desc); // If the download is modal, show the download dialog if(desc.modal) { // Update the download order (priority to modal files) updateDownloadOrder(); UBApplication::mainWindow->showDownloadWidget(); } emit fileAddedToDownload(); } /** * \brief Initialize the download manager */ void UBDownloadManager::init() { mCrntDL.clear(); mPendingDL.clear(); mReplies.clear(); mLastID = 1; mDLAvailability.clear(); for(int i=0; i<SIMULTANEOUS_DOWNLOAD; i++) { mDLAvailability.append(-1); } } /** * \brief Update the download order. The modal downloads will be put in priority. */ void UBDownloadManager::updateDownloadOrder() { QVector<sDownloadFileDesc> modalFiles; QVector<sDownloadFileDesc> nonModalfiles; for(int i=0; i<mPendingDL.size(); i++) { sDownloadFileDesc crnt = mPendingDL.at(i); if(crnt.modal) { modalFiles.append(crnt); } else { nonModalfiles.append(crnt); } } mPendingDL = modalFiles + nonModalfiles; } /** * \brief Update the download list. If a current download is finished, we take a * file from the pending download list and add it to the download list. */ void UBDownloadManager::onUpdateDownloadLists() { for(int i=0; i<SIMULTANEOUS_DOWNLOAD; i++) { if(mPendingDL.empty()) { // If we fall here that means that there is no pending download break; } if(-1 == mDLAvailability.at(i)) { // Pending downloads exist and a download 'slot' is available // Let's move the first pending download to the current download // list and fill the slot sDownloadFileDesc desc = mPendingDL.at(0); mCrntDL.append(desc); mPendingDL.remove(0); mDLAvailability.remove(i); mDLAvailability.insert(i, desc.id); // Start the download of this file startFileDownload(desc); } } } /** * \brief Get the list of the current downloads * @return a QVector of current downloads */ QVector<sDownloadFileDesc> UBDownloadManager::currentDownloads() { return mCrntDL; } /** * \brief Get the list of the pending downloads * @return a QVector of pending downloads */ QVector<sDownloadFileDesc> UBDownloadManager::pendingDownloads() { return mPendingDL; } /** * \brief Update the file transfer information * @param desc as the current downloaded file description */ void UBDownloadManager::onDownloadProgress(int id, qint64 received, qint64 total) { updateFileCurrentSize(id, received, total); } /** * \brief Called when the download of the given file is finished * @param desc as the current downloaded file description */ void UBDownloadManager::onDownloadFinished(int id, bool pSuccess, QUrl sourceUrl, QString pContentTypeHeader, QByteArray pData, QPointF pPos, QSize pSize, bool isBackground) { for(int i=0; i<mCrntDL.size(); i++) { sDownloadFileDesc desc = mCrntDL.at(i); if(id == desc.id) { if(desc.modal) { // The downloaded file is modal so we must put it on the board emit addDownloadedFileToBoard(pSuccess, sourceUrl, pContentTypeHeader, pData, pPos, pSize, isBackground); } break; } } // Then do this updateFileCurrentSize(id); } /** * \brief Update the description of the given current downloaded file * @param desc as the current downloaded file description */ void UBDownloadManager::updateFileCurrentSize(int id, qint64 received, qint64 total) { for(int i=0; i<mCrntDL.size();i++) { if(mCrntDL.at(i).id == id) { sDownloadFileDesc desc = mCrntDL.at(i); if(received >= 0 && total >= 0) { // ------------------------------------- // [=============== x% ==== ] // ------------------------------------- desc.currentSize = received; desc.totalSize = total; emit downloadUpdated(id, received, total); } else { // ------------------------------------- // [=============== 100% ==============] // ------------------------------------- // received and total are negative. That means that the download is finished desc.currentSize = mCrntDL.at(i).totalSize; // Remove the finished file from the current download list mCrntDL.remove(i); // Here we don't forget to remove the reply related to the finished download mReplies.remove(id); // Free the download slot used by the finished file for(int j=0; j<mDLAvailability.size();j++) { if(id == mDLAvailability.at(j)) { mDLAvailability.remove(j); mDLAvailability.insert(j, -1); break; } } // Here we check if some modal downloads remain checkIfModalRemains(); // Then we update the list of downloads onUpdateDownloadLists(); emit downloadFinished(id); break; } mCrntDL.remove(i); mCrntDL.insert(i,desc); break; } } } /** * \brief Start the download of a file * @param desc as the given file description */ void UBDownloadManager::startFileDownload(sDownloadFileDesc desc) { UBDownloadHttpFile* http = new UBDownloadHttpFile(desc.id, this); connect(http, SIGNAL(downloadProgress(int, qint64,qint64)), this, SLOT(onDownloadProgress(int,qint64,qint64))); connect(http, SIGNAL(downloadFinished(int, bool, QUrl, QString, QByteArray, QPointF, QSize, bool)), this, SLOT(onDownloadFinished(int, bool, QUrl, QString, QByteArray, QPointF, QSize, bool))); // We send here the request and store its reply in order to be able to cancel it if needed mReplies[desc.id] = http->get(QUrl(desc.url)); } /** * \brief Verify if modal downloads remains and notify everyone if it is not the case. */ void UBDownloadManager::checkIfModalRemains() { bool bModal = false; for(int i=0; i<mCrntDL.size();i++) { if(mCrntDL.at(i).modal) { bModal = true; break; } } if(!bModal) { for(int j=0; j<mPendingDL.size(); j++) { if(mPendingDL.at(j).modal) { bModal = true; break; } } } if(bModal || (mCrntDL.empty() && mPendingDL.empty())) { // Close the modal window UBApplication::mainWindow->hideDownloadWidget(); // Notify that no modal downloads are pending emit downloadModalFinished(); } } /** * \brief Cancel all downloads */ void UBDownloadManager::cancelDownloads() { // Stop the current downloads QMap<int, QNetworkReply*>::iterator it = mReplies.begin(); for(; it!=mReplies.end();it++) { dynamic_cast<QNetworkReply*>(it.value())->abort(); } // Clear all the lists init(); checkIfModalRemains(); // Notify everyone that the downloads have been canceled. emit cancelAllDownloads(); } void UBDownloadManager::onDownloadError(int id) { QNetworkReply* pReply = mReplies.value(id); if(NULL != pReply) { // Check which error occured: switch(pReply->error()) { case QNetworkReply::OperationCanceledError: // For futur developments: do something in case of download aborting (message? remove the download?) break; default: // Check the documentation of QNetworkReply in Qt Assistant for the different error cases break; } } } // ------------------------------------------------------------------------------ /** * \brief Constructor * @param parent as the parent widget * @param name as the object name */ UBDownloadHttpFile::UBDownloadHttpFile(int fileId, QObject *parent):UBHttpGet(parent) { mId = fileId; connect(this, SIGNAL(downloadFinished(bool,QUrl,QString,QByteArray,QPointF,QSize,bool)), this, SLOT(onDownloadFinished(bool,QUrl,QString,QByteArray,QPointF,QSize,bool))); connect(this, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(onDownloadProgress(qint64,qint64))); } /** * \brief Destructor */ UBDownloadHttpFile::~UBDownloadHttpFile() { } /** * \brief Handles the download progress notification * @param bytesReceived as the number of received bytes * @param bytesTotal as the total number of bytes */ void UBDownloadHttpFile::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { emit downloadProgress(mId, bytesReceived, bytesTotal); } /** * \brief Handles the download finished notification * @param pSuccess as the success indicator * @param sourceUrl as the source URL * @param pContentTypeHeader as the response content type header * @param pData as the packet data * @param pPos as the item position in the board * @param psize as the item size (GUI) * @param isBackground as the background mdoe indicator */ void UBDownloadHttpFile::onDownloadFinished(bool pSuccess, QUrl sourceUrl, QString pContentTypeHeader, QByteArray pData, QPointF pPos, QSize pSize, bool isBackground) { if(pSuccess) { // Notify the end of the download emit downloadFinished(mId, pSuccess, sourceUrl, pContentTypeHeader, pData, pPos, pSize, isBackground); } else { // Notify the fact that and error occured during the download emit downloadError(mId); } }