diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt
index 43fa94ed7..ea63ba08c 100644
--- a/src/Gui/CMakeLists.txt
+++ b/src/Gui/CMakeLists.txt
@@ -99,6 +99,8 @@ set(Gui_MOC_HDRS
Control.h
DemoMode.h
DownloadDialog.h
+ DownloadItem.h
+ DownloadManager.h
DlgActionsImp.h
DlgActivateWindowImp.h
DlgCommandsImp.h
@@ -220,6 +222,8 @@ SET(Gui_UIC_SRCS
DlgTreeWidget.ui
DlgLocationAngle.ui
DlgLocationPos.ui
+ DownloadManager.ui
+ DownloadItem.ui
MouseButtons.ui
SceneInspector.ui
InputVector.ui
@@ -282,6 +286,8 @@ SET(Dialog_CPP_SRCS
TextureMapping.cpp
Transform.cpp
DownloadDialog.cpp
+ DownloadItem.cpp
+ DownloadManager.cpp
)
SET(Dialog_HPP_SRCS
@@ -307,6 +313,8 @@ SET(Dialog_HPP_SRCS
TextureMapping.h
Transform.h
DownloadDialog.h
+ DownloadItem.h
+ DownloadManager.h
)
SET(Dialog_SRCS
@@ -327,6 +335,8 @@ SET(Dialog_SRCS
DlgProjectUtility.ui
DlgTipOfTheDay.ui
DlgTreeWidget.ui
+ DownloadManager.ui
+ DownloadItem.ui
MouseButtons.ui
InputVector.ui
Placement.ui
diff --git a/src/Gui/DlgAuthorization.ui b/src/Gui/DlgAuthorization.ui
index b9e5592a7..afbed6e51 100644
--- a/src/Gui/DlgAuthorization.ui
+++ b/src/Gui/DlgAuthorization.ui
@@ -6,8 +6,8 @@
0
0
- 304
- 189
+ 284
+ 128
diff --git a/src/Gui/DownloadItem.cpp b/src/Gui/DownloadItem.cpp
new file mode 100644
index 000000000..29b14a68b
--- /dev/null
+++ b/src/Gui/DownloadItem.cpp
@@ -0,0 +1,536 @@
+/***************************************************************************
+ * Copyright (c) 2013 Werner Mayer *
+ * *
+ * This file is part of the FreeCAD CAx development system. *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Library General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 Library General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Library General Public *
+ * License along with this library; see the file COPYING.LIB. If not, *
+ * write to the Free Software Foundation, Inc., 59 Temple Place, *
+ * Suite 330, Boston, MA 02111-1307, USA *
+ * *
+ ***************************************************************************/
+
+
+#include "PreCompiled.h"
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "DownloadItem.h"
+#include "DownloadManager.h"
+#include "Application.h"
+#include "Document.h"
+#include "MainWindow.h"
+#include "FileDialog.h"
+#include "ui_DlgAuthorization.h"
+
+using namespace Gui::Dialog;
+
+
+EditTableView::EditTableView(QWidget *parent)
+ : QTableView(parent)
+{
+}
+
+void EditTableView::keyPressEvent(QKeyEvent *event)
+{
+ if ((event->key() == Qt::Key_Delete
+ || event->key() == Qt::Key_Backspace)
+ && model()) {
+ removeOne();
+ } else {
+ QAbstractItemView::keyPressEvent(event);
+ }
+}
+
+void EditTableView::removeOne()
+{
+ if (!model() || !selectionModel())
+ return;
+ int row = currentIndex().row();
+ model()->removeRow(row, rootIndex());
+ QModelIndex idx = model()->index(row, 0, rootIndex());
+ if (!idx.isValid())
+ idx = model()->index(row - 1, 0, rootIndex());
+ selectionModel()->select(idx, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
+}
+
+void EditTableView::removeAll()
+{
+ if (model())
+ model()->removeRows(0, model()->rowCount(rootIndex()), rootIndex());
+}
+
+// ----------------------------------------------------------------------------
+
+SqueezeLabel::SqueezeLabel(QWidget *parent) : QLabel(parent)
+{
+}
+
+void SqueezeLabel::paintEvent(QPaintEvent *event)
+{
+ QFontMetrics fm = fontMetrics();
+ if (fm.width(text()) > contentsRect().width()) {
+ QString elided = fm.elidedText(text(), Qt::ElideMiddle, width());
+ QString oldText = text();
+ setText(elided);
+ QLabel::paintEvent(event);
+ setText(oldText);
+ } else {
+ QLabel::paintEvent(event);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+#define AUTOSAVE_IN 1000 * 3 // seconds
+#define MAXWAIT 1000 * 15 // seconds
+
+AutoSaver::AutoSaver(QObject *parent) : QObject(parent)
+{
+ Q_ASSERT(parent);
+}
+
+AutoSaver::~AutoSaver()
+{
+ if (m_timer.isActive())
+ qWarning() << "AutoSaver: still active when destroyed, changes not saved.";
+}
+
+void AutoSaver::changeOccurred()
+{
+ if (m_firstChange.isNull())
+ m_firstChange.start();
+
+ if (m_firstChange.elapsed() > MAXWAIT) {
+ saveIfNeccessary();
+ } else {
+ m_timer.start(AUTOSAVE_IN, this);
+ }
+}
+
+void AutoSaver::timerEvent(QTimerEvent *event)
+{
+ if (event->timerId() == m_timer.timerId()) {
+ saveIfNeccessary();
+ } else {
+ QObject::timerEvent(event);
+ }
+}
+
+void AutoSaver::saveIfNeccessary()
+{
+ if (!m_timer.isActive())
+ return;
+ m_timer.stop();
+ m_firstChange = QTime();
+ if (!QMetaObject::invokeMethod(parent(), "save", Qt::DirectConnection)) {
+ qWarning() << "AutoSaver: error invoking slot save() on parent";
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+NetworkAccessManager::NetworkAccessManager(QObject *parent)
+ : QNetworkAccessManager(parent)
+{
+ connect(this, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)),
+ SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
+ connect(this, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)),
+ SLOT(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)));
+
+ QNetworkDiskCache *diskCache = new QNetworkDiskCache(this);
+ QString location = QDesktopServices::storageLocation(QDesktopServices::CacheLocation);
+ diskCache->setCacheDirectory(location);
+ setCache(diskCache);
+}
+
+void NetworkAccessManager::authenticationRequired(QNetworkReply *reply, QAuthenticator *auth)
+{
+ QWidget *mainWindow = Gui::getMainWindow();
+
+ QDialog dialog(mainWindow);
+ dialog.setWindowFlags(Qt::Sheet);
+
+ Ui_DlgAuthorization passwordDialog;
+ passwordDialog.setupUi(&dialog);
+ dialog.adjustSize();
+
+ QString introMessage = tr("Enter username and password for \"%1\" at %2");
+ introMessage = introMessage.arg(Qt::escape(reply->url().toString())).arg(Qt::escape(reply->url().toString()));
+ passwordDialog.siteDescription->setText(introMessage);
+ passwordDialog.siteDescription->setWordWrap(true);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ auth->setUser(passwordDialog.username->text());
+ auth->setPassword(passwordDialog.password->text());
+ }
+}
+
+void NetworkAccessManager::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth)
+{
+ QWidget *mainWindow = Gui::getMainWindow();
+
+ QDialog dialog(mainWindow);
+ dialog.setWindowFlags(Qt::Sheet);
+
+ Ui_DlgAuthorization proxyDialog;
+ proxyDialog.setupUi(&dialog);
+ dialog.adjustSize();
+
+ QString introMessage = tr("Connect to proxy \"%1\" using:");
+ introMessage = introMessage.arg(Qt::escape(proxy.hostName()));
+ proxyDialog.siteDescription->setText(introMessage);
+ proxyDialog.siteDescription->setWordWrap(true);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ auth->setUser(proxyDialog.username->text());
+ auth->setPassword(proxyDialog.password->text());
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+DownloadItem::DownloadItem(QNetworkReply *reply, bool requestFileName, QWidget *parent)
+ : QWidget(parent)
+ , m_reply(reply)
+ , m_requestFileName(requestFileName)
+ , m_bytesReceived(0)
+{
+ setupUi(this);
+ QPalette p = downloadInfoLabel->palette();
+ p.setColor(QPalette::Text, Qt::darkGray);
+ downloadInfoLabel->setPalette(p);
+ progressBar->setMaximum(0);
+ tryAgainButton->hide();
+ connect(stopButton, SIGNAL(clicked()), this, SLOT(stop()));
+ connect(openButton, SIGNAL(clicked()), this, SLOT(open()));
+ connect(tryAgainButton, SIGNAL(clicked()), this, SLOT(tryAgain()));
+
+ init();
+}
+
+void DownloadItem::init()
+{
+ if (!m_reply)
+ return;
+
+ // attach to the m_reply
+ m_url = m_reply->url();
+ m_reply->setParent(this);
+ connect(m_reply, SIGNAL(readyRead()), this, SLOT(downloadReadyRead()));
+ connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)),
+ this, SLOT(error(QNetworkReply::NetworkError)));
+ connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)),
+ this, SLOT(downloadProgress(qint64, qint64)));
+ connect(m_reply, SIGNAL(metaDataChanged()),
+ this, SLOT(metaDataChanged()));
+ connect(m_reply, SIGNAL(finished()),
+ this, SLOT(finished()));
+
+ // reset info
+ downloadInfoLabel->clear();
+ progressBar->setValue(0);
+ getFileName();
+
+ // start timer for the download estimation
+ m_downloadTime.start();
+
+ if (m_reply->error() != QNetworkReply::NoError) {
+ error(m_reply->error());
+ finished();
+ }
+}
+
+void DownloadItem::getFileName()
+{
+ QSettings settings;
+ settings.beginGroup(QLatin1String("downloadmanager"));
+ //QString defaultLocation = QDesktopServices::storageLocation(QDesktopServices::DesktopLocation);
+ QString defaultLocation = Gui::FileDialog::getWorkingDirectory();
+ QString downloadDirectory = settings.value(QLatin1String("downloadDirectory"), defaultLocation).toString();
+ if (!downloadDirectory.isEmpty())
+ downloadDirectory += QLatin1Char('/');
+
+ QString defaultFileName = saveFileName(downloadDirectory);
+ QString fileName = defaultFileName;
+ if (m_requestFileName) {
+ fileName = QFileDialog::getSaveFileName(this, tr("Save File"), defaultFileName);
+ if (fileName.isEmpty()) {
+ m_reply->close();
+ fileNameLabel->setText(tr("Download canceled: %1").arg(QFileInfo(defaultFileName).fileName()));
+ return;
+ }
+ }
+ m_output.setFileName(fileName);
+ fileNameLabel->setText(QFileInfo(m_output.fileName()).fileName());
+ if (m_requestFileName)
+ downloadReadyRead();
+}
+
+QString DownloadItem::saveFileName(const QString &directory) const
+{
+ // Move this function into QNetworkReply to also get file name sent from the server
+ QString path = m_url.path();
+ if (!m_fileName.isEmpty())
+ path = m_fileName;
+ QFileInfo info(path);
+ QString baseName = info.completeBaseName();
+ QString endName = info.suffix();
+
+ if (baseName.isEmpty()) {
+ baseName = QLatin1String("unnamed_download");
+ qDebug() << "DownloadManager:: downloading unknown file:" << m_url;
+ }
+ QString name = directory + baseName + QLatin1Char('.') + endName;
+ if (QFile::exists(name)) {
+ // already exists, don't overwrite
+ int i = 1;
+ do {
+ name = directory + baseName + QLatin1Char('-') + QString::number(i++) + QLatin1Char('.') + endName;
+ } while (QFile::exists(name));
+ }
+ return name;
+}
+
+
+void DownloadItem::stop()
+{
+ setUpdatesEnabled(false);
+ stopButton->setEnabled(false);
+ stopButton->hide();
+ tryAgainButton->setEnabled(true);
+ tryAgainButton->show();
+ setUpdatesEnabled(true);
+ m_reply->abort();
+}
+
+void DownloadItem::open()
+{
+ QFileInfo info(m_output);
+ QString selectedFilter;
+ QStringList fileList;
+ fileList << info.absoluteFilePath();
+ SelectModule::Dict dict = SelectModule::importHandler(fileList, selectedFilter);
+
+ // load the files with the associated modules
+ if (!dict.isEmpty()) {
+ Gui::Document* doc = Gui::Application::Instance->activeDocument();
+ if (doc) {
+ for (SelectModule::Dict::iterator it = dict.begin(); it != dict.end(); ++it) {
+ Gui::Application::Instance->importFrom(it.key().toUtf8(),
+ doc->getDocument()->getName(), it.value().toAscii());
+ }
+ }
+ else {
+ for (SelectModule::Dict::iterator it = dict.begin(); it != dict.end(); ++it) {
+ Gui::Application::Instance->open(it.key().toUtf8(), it.value().toAscii());
+ }
+ }
+ }
+ else {
+ QUrl url = QUrl::fromLocalFile(info.absolutePath());
+ QDesktopServices::openUrl(url);
+ }
+}
+
+void DownloadItem::tryAgain()
+{
+ if (!tryAgainButton->isEnabled())
+ return;
+
+ tryAgainButton->setEnabled(false);
+ tryAgainButton->setVisible(false);
+ stopButton->setEnabled(true);
+ stopButton->setVisible(true);
+ progressBar->setVisible(true);
+
+ QNetworkReply *r = DownloadManager::getInstance()->networkAccessManager()->get(QNetworkRequest(m_url));
+ if (m_reply)
+ m_reply->deleteLater();
+ if (m_output.exists())
+ m_output.remove();
+ m_reply = r;
+ init();
+ /*emit*/ statusChanged();
+}
+
+void DownloadItem::downloadReadyRead()
+{
+ if (m_requestFileName && m_output.fileName().isEmpty())
+ return;
+ if (!m_output.isOpen()) {
+ // in case someone else has already put a file there
+ if (!m_requestFileName)
+ getFileName();
+ if (!m_output.open(QIODevice::WriteOnly)) {
+ downloadInfoLabel->setText(tr("Error opening save file: %1")
+ .arg(m_output.errorString()));
+ stopButton->click();
+ /*emit*/ statusChanged();
+ return;
+ }
+ /*emit*/ statusChanged();
+ }
+ if (-1 == m_output.write(m_reply->readAll())) {
+ downloadInfoLabel->setText(tr("Error saving: %1")
+ .arg(m_output.errorString()));
+ stopButton->click();
+ }
+}
+
+void DownloadItem::error(QNetworkReply::NetworkError)
+{
+ qDebug() << "DownloadItem::error" << m_reply->errorString() << m_url;
+ downloadInfoLabel->setText(tr("Network Error: %1").arg(m_reply->errorString()));
+ tryAgainButton->setEnabled(true);
+ tryAgainButton->setVisible(true);
+}
+
+void DownloadItem::metaDataChanged()
+{
+ if (m_reply->hasRawHeader(QByteArray("Content-Disposition"))) {
+ QByteArray header = m_reply->rawHeader(QByteArray("Content-Disposition"));
+ int index = header.indexOf("filename=");
+ if (index > 0) {
+ header = header.mid(index+9);
+ m_fileName = QUrl::fromPercentEncoding(header);
+ }
+ else {
+ index = header.indexOf("filename*=UTF-8''");
+ if (index > 0) {
+ header = header.mid(index+17);
+ m_fileName = QUrl::fromPercentEncoding(header);
+ }
+ }
+ }
+
+ QVariant statusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
+ if (!statusCode.isValid())
+ return;
+ int status = statusCode.toInt();
+ if (status != 200) {
+ QString reason = m_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
+ qDebug() << reason;
+ }
+}
+
+void DownloadItem::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
+{
+ m_bytesReceived = bytesReceived;
+ if (bytesTotal == -1) {
+ progressBar->setValue(0);
+ progressBar->setMaximum(0);
+ } else {
+ progressBar->setValue(bytesReceived);
+ progressBar->setMaximum(bytesTotal);
+ }
+ updateInfoLabel();
+}
+
+void DownloadItem::updateInfoLabel()
+{
+ if (m_reply->error() == QNetworkReply::NoError)
+ return;
+
+ qint64 bytesTotal = progressBar->maximum();
+ bool running = !downloadedSuccessfully();
+
+ // update info label
+ double speed = m_bytesReceived * 1000.0 / m_downloadTime.elapsed();
+ double timeRemaining = ((double)(bytesTotal - m_bytesReceived)) / speed;
+ QString timeRemainingString = tr("seconds");
+ if (timeRemaining > 60) {
+ timeRemaining = timeRemaining / 60;
+ timeRemainingString = tr("minutes");
+ }
+ timeRemaining = floor(timeRemaining);
+
+ // When downloading the eta should never be 0
+ if (timeRemaining == 0)
+ timeRemaining = 1;
+
+ QString info;
+ if (running) {
+ QString remaining;
+ if (bytesTotal != 0)
+ remaining = tr("- %4 %5 remaining")
+ .arg(timeRemaining)
+ .arg(timeRemainingString);
+ info = QString(tr("%1 of %2 (%3/sec) %4"))
+ .arg(dataString(m_bytesReceived))
+ .arg(bytesTotal == 0 ? tr("?") : dataString(bytesTotal))
+ .arg(dataString((int)speed))
+ .arg(remaining);
+ } else {
+ if (m_bytesReceived == bytesTotal)
+ info = dataString(m_output.size());
+ else
+ info = tr("%1 of %2 - Stopped")
+ .arg(dataString(m_bytesReceived))
+ .arg(dataString(bytesTotal));
+ }
+ downloadInfoLabel->setText(info);
+}
+
+QString DownloadItem::dataString(int size) const
+{
+ QString unit;
+ if (size < 1024) {
+ unit = tr("bytes");
+ } else if (size < 1024*1024) {
+ size /= 1024;
+ unit = tr("kB");
+ } else {
+ size /= 1024*1024;
+ unit = tr("MB");
+ }
+ return QString(QLatin1String("%1 %2")).arg(size).arg(unit);
+}
+
+bool DownloadItem::downloading() const
+{
+ return (progressBar->isVisible());
+}
+
+bool DownloadItem::downloadedSuccessfully() const
+{
+ return (stopButton->isHidden() && tryAgainButton->isHidden());
+}
+
+void DownloadItem::finished()
+{
+ progressBar->hide();
+ stopButton->setEnabled(false);
+ stopButton->hide();
+ m_output.close();
+ updateInfoLabel();
+ /*emit*/ statusChanged();
+}
+
+#include "moc_DownloadItem.cpp"
diff --git a/src/Gui/DownloadItem.h b/src/Gui/DownloadItem.h
new file mode 100644
index 000000000..ea828eb43
--- /dev/null
+++ b/src/Gui/DownloadItem.h
@@ -0,0 +1,153 @@
+/***************************************************************************
+ * Copyright (c) 2013 Werner Mayer *
+ * *
+ * This file is part of the FreeCAD CAx development system. *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Library General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 Library General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Library General Public *
+ * License along with this library; see the file COPYING.LIB. If not, *
+ * write to the Free Software Foundation, Inc., 59 Temple Place, *
+ * Suite 330, Boston, MA 02111-1307, USA *
+ * *
+ ***************************************************************************/
+
+
+#ifndef GUI_DIALOG_DOWNLOADITEM_H
+#define GUI_DIALOG_DOWNLOADITEM_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+class AutoSaver;
+class QFileIconProvider;
+
+class EditTableView : public QTableView
+{
+ Q_OBJECT
+
+public:
+ EditTableView(QWidget *parent = 0);
+ void keyPressEvent(QKeyEvent *event);
+
+public Q_SLOTS:
+ void removeOne();
+ void removeAll();
+};
+
+class SqueezeLabel : public QLabel
+{
+ Q_OBJECT
+
+public:
+ SqueezeLabel(QWidget *parent = 0);
+
+protected:
+ void paintEvent(QPaintEvent *event);
+
+};
+
+/*
+ This class will call the save() slot on the parent object when the parent changes.
+ It will wait several seconds after changed() to combining multiple changes and
+ prevent continuous writing to disk.
+ */
+class AutoSaver : public QObject {
+
+Q_OBJECT
+
+public:
+ AutoSaver(QObject *parent);
+ ~AutoSaver();
+ void saveIfNeccessary();
+
+public Q_SLOTS:
+ void changeOccurred();
+
+protected:
+ void timerEvent(QTimerEvent *event);
+
+private:
+ QBasicTimer m_timer;
+ QTime m_firstChange;
+
+};
+
+class NetworkAccessManager : public QNetworkAccessManager
+{
+ Q_OBJECT
+
+public:
+ NetworkAccessManager(QObject *parent = 0);
+
+private Q_SLOTS:
+ void authenticationRequired(QNetworkReply *reply, QAuthenticator *auth);
+ void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth);
+};
+
+#include "ui_DownloadItem.h"
+
+namespace Gui {
+namespace Dialog {
+class DownloadModel;
+
+class DownloadItem : public QWidget, public Ui_DownloadItem
+{
+ Q_OBJECT
+
+Q_SIGNALS:
+ void statusChanged();
+
+public:
+ DownloadItem(QNetworkReply *reply = 0, bool requestFileName = false, QWidget *parent = 0);
+ bool downloading() const;
+ bool downloadedSuccessfully() const;
+
+ QUrl m_url;
+ QString m_fileName;
+
+ QFile m_output;
+ QNetworkReply *m_reply;
+
+private Q_SLOTS:
+ void stop();
+ void tryAgain();
+ void open();
+
+ void downloadReadyRead();
+ void error(QNetworkReply::NetworkError code);
+ void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
+ void metaDataChanged();
+ void finished();
+
+private:
+ void getFileName();
+ void init();
+ void updateInfoLabel();
+ QString dataString(int size) const;
+
+ QString saveFileName(const QString &directory) const;
+
+ bool m_requestFileName;
+ qint64 m_bytesReceived;
+ QTime m_downloadTime;
+};
+
+} // namespace Dialog
+} // namespace Gui
+
+#endif // GUI_DIALOG_DOWNLOADITEM_H
diff --git a/src/Gui/DownloadItem.ui b/src/Gui/DownloadItem.ui
new file mode 100644
index 000000000..b6dd8b34b
--- /dev/null
+++ b/src/Gui/DownloadItem.ui
@@ -0,0 +1,141 @@
+
+
+ DownloadItem
+
+
+
+ 0
+ 0
+ 423
+ 98
+
+
+
+ Form
+
+
+
+ 0
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Ico
+
+
+
+ -
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Filename
+
+
+
+ -
+
+
+ 0
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Vertical
+
+
+
+ 17
+ 1
+
+
+
+
+ -
+
+
+ false
+
+
+
+ :/icons/view-refresh.svg:/icons/view-refresh.svg
+
+
+
+ -
+
+
+
+ :/icons/process-stop.svg:/icons/process-stop.svg
+
+
+
+ -
+
+
+
+ :/icons/document-open.svg:/icons/document-open.svg
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 17
+ 5
+
+
+
+
+
+
+
+
+
+
+ SqueezeLabel
+ QWidget
+
+
+
+
+
+
+
+
+
diff --git a/src/Gui/DownloadManager.cpp b/src/Gui/DownloadManager.cpp
new file mode 100644
index 000000000..8846863ec
--- /dev/null
+++ b/src/Gui/DownloadManager.cpp
@@ -0,0 +1,303 @@
+/***************************************************************************
+ * Copyright (c) 2013 Werner Mayer *
+ * *
+ * This file is part of the FreeCAD CAx development system. *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Library General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 Library General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Library General Public *
+ * License along with this library; see the file COPYING.LIB. If not, *
+ * write to the Free Software Foundation, Inc., 59 Temple Place, *
+ * Suite 330, Boston, MA 02111-1307, USA *
+ * *
+ ***************************************************************************/
+
+
+#include "PreCompiled.h"
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "DownloadItem.h"
+#include "DownloadManager.h"
+#include "ui_DownloadManager.h"
+#include "DockWindowManager.h"
+#include "MainWindow.h"
+
+using namespace Gui::Dialog;
+
+/* TRANSLATOR Gui::Dialog::DownloadManager */
+
+DownloadManager* DownloadManager::self = 0;
+
+DownloadManager* DownloadManager::getInstance()
+{
+ if (!self)
+ self = new DownloadManager(Gui::getMainWindow());
+ return self;
+}
+
+DownloadManager::DownloadManager(QWidget *parent)
+ : QDialog(parent)
+ , m_autoSaver(new AutoSaver(this))
+ , m_manager(new NetworkAccessManager(this))
+ , m_iconProvider(0)
+ , m_removePolicy(Never)
+ , ui(new Ui_DownloadManager())
+{
+ ui->setupUi(this);
+ ui->downloadsView->setShowGrid(false);
+ ui->downloadsView->verticalHeader()->hide();
+ ui->downloadsView->horizontalHeader()->hide();
+ ui->downloadsView->setAlternatingRowColors(true);
+ ui->downloadsView->horizontalHeader()->setStretchLastSection(true);
+ m_model = new DownloadModel(this);
+ ui->downloadsView->setModel(m_model);
+ connect(ui->cleanupButton, SIGNAL(clicked()), this, SLOT(cleanup()));
+ load();
+
+ Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance();
+ QDockWidget* dw = pDockMgr->addDockWindow(QT_TR_NOOP("Download Manager"),
+ this, Qt::BottomDockWidgetArea);
+ dw->setFeatures(QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetFloatable);
+ dw->show();
+}
+
+DownloadManager::~DownloadManager()
+{
+ m_autoSaver->changeOccurred();
+ m_autoSaver->saveIfNeccessary();
+ if (m_iconProvider)
+ delete m_iconProvider;
+ delete ui;
+}
+
+int DownloadManager::activeDownloads() const
+{
+ int count = 0;
+ for (int i = 0; i < m_downloads.count(); ++i) {
+ if (m_downloads.at(i)->stopButton->isEnabled())
+ ++count;
+ }
+ return count;
+}
+
+void DownloadManager::download(const QNetworkRequest &request, bool requestFileName)
+{
+ if (request.url().isEmpty())
+ return;
+ handleUnsupportedContent(m_manager->get(request), requestFileName);
+}
+
+void DownloadManager::handleUnsupportedContent(QNetworkReply *reply, bool requestFileName)
+{
+ if (!reply || reply->url().isEmpty())
+ return;
+ QVariant header = reply->header(QNetworkRequest::ContentLengthHeader);
+ bool ok;
+ int size = header.toInt(&ok);
+ if (ok && size == 0)
+ return;
+
+ DownloadItem *item = new DownloadItem(reply, requestFileName, this);
+ addItem(item);
+}
+
+void DownloadManager::addItem(DownloadItem *item)
+{
+ connect(item, SIGNAL(statusChanged()), this, SLOT(updateRow()));
+ int row = m_downloads.count();
+ m_model->beginInsertRows(QModelIndex(), row, row);
+ m_downloads.append(item);
+ m_model->endInsertRows();
+ updateItemCount();
+ show();
+ ui->downloadsView->setIndexWidget(m_model->index(row, 0), item);
+ QIcon icon = style()->standardIcon(QStyle::SP_FileIcon);
+ item->fileIcon->setPixmap(icon.pixmap(48, 48));
+ ui->downloadsView->setRowHeight(row, item->sizeHint().height());
+}
+
+void DownloadManager::updateRow()
+{
+ DownloadItem *item = qobject_cast(sender());
+ int row = m_downloads.indexOf(item);
+ if (-1 == row)
+ return;
+ if (!m_iconProvider)
+ m_iconProvider = new QFileIconProvider();
+ QIcon icon = m_iconProvider->icon(item->m_output.fileName());
+ if (icon.isNull())
+ icon = style()->standardIcon(QStyle::SP_FileIcon);
+ item->fileIcon->setPixmap(icon.pixmap(48, 48));
+ ui->downloadsView->setRowHeight(row, item->minimumSizeHint().height());
+
+ bool remove = false;
+ QWebSettings *globalSettings = QWebSettings::globalSettings();
+ if (!item->downloading()
+ && globalSettings->testAttribute(QWebSettings::PrivateBrowsingEnabled))
+ remove = true;
+
+ if (item->downloadedSuccessfully()
+ && removePolicy() == DownloadManager::SuccessFullDownload) {
+ remove = true;
+ }
+ if (remove)
+ m_model->removeRow(row);
+
+ ui->cleanupButton->setEnabled(m_downloads.count() - activeDownloads() > 0);
+}
+
+DownloadManager::RemovePolicy DownloadManager::removePolicy() const
+{
+ return m_removePolicy;
+}
+
+void DownloadManager::setRemovePolicy(RemovePolicy policy)
+{
+ if (policy == m_removePolicy)
+ return;
+ m_removePolicy = policy;
+ m_autoSaver->changeOccurred();
+}
+
+void DownloadManager::save() const
+{
+ QSettings settings;
+ settings.beginGroup(QLatin1String("downloadmanager"));
+ QMetaEnum removePolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("RemovePolicy"));
+ settings.setValue(QLatin1String("removeDownloadsPolicy"), QLatin1String(removePolicyEnum.valueToKey(m_removePolicy)));
+ settings.setValue(QLatin1String("size"), size());
+ if (m_removePolicy == Exit)
+ return;
+
+ for (int i = 0; i < m_downloads.count(); ++i) {
+ QString key = QString(QLatin1String("download_%1_")).arg(i);
+ settings.setValue(key + QLatin1String("url"), m_downloads[i]->m_url);
+ settings.setValue(key + QLatin1String("location"), QFileInfo(m_downloads[i]->m_output).filePath());
+ settings.setValue(key + QLatin1String("done"), m_downloads[i]->downloadedSuccessfully());
+ }
+ int i = m_downloads.count();
+ QString key = QString(QLatin1String("download_%1_")).arg(i);
+ while (settings.contains(key + QLatin1String("url"))) {
+ settings.remove(key + QLatin1String("url"));
+ settings.remove(key + QLatin1String("location"));
+ settings.remove(key + QLatin1String("done"));
+ key = QString(QLatin1String("download_%1_")).arg(++i);
+ }
+}
+
+void DownloadManager::load()
+{
+ QSettings settings;
+ settings.beginGroup(QLatin1String("downloadmanager"));
+ QSize size = settings.value(QLatin1String("size")).toSize();
+ if (size.isValid())
+ resize(size);
+ QByteArray value = settings.value(QLatin1String("removeDownloadsPolicy"), QLatin1String("Never")).toByteArray();
+ QMetaEnum removePolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("RemovePolicy"));
+ m_removePolicy = removePolicyEnum.keyToValue(value) == -1 ?
+ Never :
+ static_cast(removePolicyEnum.keyToValue(value));
+
+ int i = 0;
+ QString key = QString(QLatin1String("download_%1_")).arg(i);
+ while (settings.contains(key + QLatin1String("url"))) {
+ QUrl url = settings.value(key + QLatin1String("url")).toUrl();
+ QString fileName = settings.value(key + QLatin1String("location")).toString();
+ bool done = settings.value(key + QLatin1String("done"), true).toBool();
+ if (!url.isEmpty() && !fileName.isEmpty()) {
+ DownloadItem *item = new DownloadItem(0, this);
+ item->m_output.setFileName(fileName);
+ item->fileNameLabel->setText(QFileInfo(item->m_output.fileName()).fileName());
+ item->m_url = url;
+ item->stopButton->setVisible(false);
+ item->stopButton->setEnabled(false);
+ item->tryAgainButton->setVisible(!done);
+ item->tryAgainButton->setEnabled(!done);
+ item->progressBar->setVisible(!done);
+ addItem(item);
+ }
+ key = QString(QLatin1String("download_%1_")).arg(++i);
+ }
+ ui->cleanupButton->setEnabled(m_downloads.count() - activeDownloads() > 0);
+}
+
+void DownloadManager::cleanup()
+{
+ if (m_downloads.isEmpty())
+ return;
+ m_model->removeRows(0, m_downloads.count());
+ updateItemCount();
+ if (m_downloads.isEmpty() && m_iconProvider) {
+ delete m_iconProvider;
+ m_iconProvider = 0;
+ }
+ m_autoSaver->changeOccurred();
+}
+
+void DownloadManager::updateItemCount()
+{
+ int count = m_downloads.count();
+ ui->itemCount->setText(count == 1 ? tr("1 Download") : tr("%1 Downloads").arg(count));
+}
+
+// ----------------------------------------------------------------------------
+
+DownloadModel::DownloadModel(DownloadManager *downloadManager, QObject *parent)
+ : QAbstractListModel(parent)
+ , m_downloadManager(downloadManager)
+{
+}
+
+QVariant DownloadModel::data(const QModelIndex &index, int role) const
+{
+ if (index.row() < 0 || index.row() >= rowCount(index.parent()))
+ return QVariant();
+ if (role == Qt::ToolTipRole)
+ if (!m_downloadManager->m_downloads.at(index.row())->downloadedSuccessfully())
+ return m_downloadManager->m_downloads.at(index.row())->downloadInfoLabel->text();
+ return QVariant();
+}
+
+int DownloadModel::rowCount(const QModelIndex &parent) const
+{
+ return (parent.isValid()) ? 0 : m_downloadManager->m_downloads.count();
+}
+
+bool DownloadModel::removeRows(int row, int count, const QModelIndex &parent)
+{
+ if (parent.isValid())
+ return false;
+
+ int lastRow = row + count - 1;
+ for (int i = lastRow; i >= row; --i) {
+ if (m_downloadManager->m_downloads.at(i)->downloadedSuccessfully()
+ || m_downloadManager->m_downloads.at(i)->tryAgainButton->isEnabled()) {
+ beginRemoveRows(parent, i, i);
+ m_downloadManager->m_downloads.takeAt(i)->deleteLater();
+ endRemoveRows();
+ }
+ }
+ m_downloadManager->m_autoSaver->changeOccurred();
+ return true;
+}
+
+#include "moc_DownloadManager.cpp"
diff --git a/src/Gui/DownloadManager.h b/src/Gui/DownloadManager.h
new file mode 100644
index 000000000..48d847eef
--- /dev/null
+++ b/src/Gui/DownloadManager.h
@@ -0,0 +1,116 @@
+/***************************************************************************
+ * Copyright (c) 2013 Werner Mayer *
+ * *
+ * This file is part of the FreeCAD CAx development system. *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Library General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 Library General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Library General Public *
+ * License along with this library; see the file COPYING.LIB. If not, *
+ * write to the Free Software Foundation, Inc., 59 Temple Place, *
+ * Suite 330, Boston, MA 02111-1307, USA *
+ * *
+ ***************************************************************************/
+
+
+#ifndef GUI_DIALOG_DOWNLOADMANAGER_H
+#define GUI_DIALOG_DOWNLOADMANAGER_H
+
+#include
+#include
+#include
+#include
+
+class AutoSaver;
+class QFileIconProvider;
+
+namespace Gui {
+namespace Dialog {
+class DownloadItem;
+class DownloadModel;
+class Ui_DownloadManager;
+
+class GuiExport DownloadManager : public QDialog
+{
+ Q_OBJECT
+ Q_PROPERTY(RemovePolicy removePolicy READ removePolicy WRITE setRemovePolicy)
+ Q_ENUMS(RemovePolicy)
+
+public:
+ enum RemovePolicy {
+ Never,
+ Exit,
+ SuccessFullDownload
+ };
+
+ static DownloadManager* getInstance();
+
+private:
+ DownloadManager(QWidget *parent = 0);
+ ~DownloadManager();
+
+public:
+ int activeDownloads() const;
+ QNetworkAccessManager * networkAccessManager()
+ { return m_manager; }
+
+ RemovePolicy removePolicy() const;
+ void setRemovePolicy(RemovePolicy policy);
+
+public Q_SLOTS:
+ void download(const QNetworkRequest &request, bool requestFileName = false);
+ inline void download(const QUrl &url, bool requestFileName = false)
+ { download(QNetworkRequest(url), requestFileName); }
+ void handleUnsupportedContent(QNetworkReply *reply, bool requestFileName = false);
+ void cleanup();
+
+private Q_SLOTS:
+ void save() const;
+ void updateRow();
+
+private:
+ void addItem(DownloadItem *item);
+ void updateItemCount();
+ void load();
+
+ AutoSaver *m_autoSaver;
+ DownloadModel *m_model;
+ QNetworkAccessManager *m_manager;
+ QFileIconProvider *m_iconProvider;
+ QList m_downloads;
+ RemovePolicy m_removePolicy;
+ friend class DownloadModel;
+
+private:
+ Ui_DownloadManager* ui;
+ static DownloadManager* self;
+};
+
+class DownloadModel : public QAbstractListModel
+{
+ friend class DownloadManager;
+ Q_OBJECT
+
+public:
+ DownloadModel(DownloadManager *downloadManager, QObject *parent = 0);
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
+
+private:
+ DownloadManager *m_downloadManager;
+
+};
+
+} // namespace Dialog
+} // namespace Gui
+
+#endif // GUI_DIALOG_DOWNLOADMANAGER_H
diff --git a/src/Gui/DownloadManager.ui b/src/Gui/DownloadManager.ui
new file mode 100644
index 000000000..a040b0585
--- /dev/null
+++ b/src/Gui/DownloadManager.ui
@@ -0,0 +1,83 @@
+
+ Gui::Dialog::DownloadManager
+
+
+
+ 0
+ 0
+ 332
+ 252
+
+
+
+ Downloads
+
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ -
+
+
-
+
+
+ false
+
+
+ Clean up
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 58
+ 24
+
+
+
+
+
+
+ -
+
+
+ 0 Items
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 148
+ 20
+
+
+
+
+
+
+
+
+ EditTableView
+ QTableView
+
+
+
+
+
+
diff --git a/src/Gui/Icons/process-stop.svg b/src/Gui/Icons/process-stop.svg
new file mode 100644
index 000000000..04ce3a79f
--- /dev/null
+++ b/src/Gui/Icons/process-stop.svg
@@ -0,0 +1,336 @@
+
+
+
diff --git a/src/Gui/Icons/resource.qrc b/src/Gui/Icons/resource.qrc
index e506168b6..e27da77af 100644
--- a/src/Gui/Icons/resource.qrc
+++ b/src/Gui/Icons/resource.qrc
@@ -49,6 +49,7 @@
edit-edit.svg
help-browser.svg
preferences-system.svg
+ process-stop.svg
window-new.svg
camera-photo.svg
applications-accessories.svg
diff --git a/src/Gui/MainWindow.cpp b/src/Gui/MainWindow.cpp
index e9b621430..a46388adb 100644
--- a/src/Gui/MainWindow.cpp
+++ b/src/Gui/MainWindow.cpp
@@ -64,6 +64,8 @@
#include "MainWindow.h"
#include "Application.h"
#include "Assistant.h"
+#include "DownloadDialog.h"
+#include "DownloadManager.h"
#include "WaitCursor.h"
#include "Action.h"
@@ -1543,6 +1545,12 @@ void MainWindow::loadUrls(App::Document* doc, const QList& url)
(const char*)info.absoluteFilePath().toUtf8());
}
}
+ else if (it->scheme().toLower() == QLatin1String("http")) {
+ Gui::Dialog::DownloadManager::getInstance()->download(*it);
+ }
+ else if (it->scheme().toLower() == QLatin1String("ftp")) {
+ Gui::Dialog::DownloadManager::getInstance()->download(*it);
+ }
}
const char *docName = doc ? doc->getName() : "Unnamed";
diff --git a/src/Mod/Web/Gui/BrowserView.cpp b/src/Mod/Web/Gui/BrowserView.cpp
index 5e53f872d..7e73a404c 100644
--- a/src/Mod/Web/Gui/BrowserView.cpp
+++ b/src/Mod/Web/Gui/BrowserView.cpp
@@ -56,6 +56,7 @@
#include
#include
#include
+#include
#include
#include
@@ -119,6 +120,8 @@ BrowserView::BrowserView(QWidget* parent)
this, SLOT(onLinkClicked(const QUrl &)));
connect(view->page(), SIGNAL(downloadRequested(const QNetworkRequest &)),
this, SLOT(onDownloadRequested(const QNetworkRequest &)));
+ connect(view->page(), SIGNAL(unsupportedContent(QNetworkReply*)),
+ this, SLOT(onUnsupportedContent(QNetworkReply*)));
}
/** Destroys the object and frees any allocated resources */
@@ -181,8 +184,12 @@ bool BrowserView::chckHostAllowed(const QString& host)
void BrowserView::onDownloadRequested(const QNetworkRequest & request)
{
- Dialog::DownloadDialog dlg (request.url(),this);
- dlg.exec();
+ Gui::Dialog::DownloadManager::getInstance()->download(request);
+}
+
+void BrowserView::onUnsupportedContent(QNetworkReply* reply)
+{
+ Gui::Dialog::DownloadManager::getInstance()->handleUnsupportedContent(reply);
}
void BrowserView::load(const char* URL)
diff --git a/src/Mod/Web/Gui/BrowserView.h b/src/Mod/Web/Gui/BrowserView.h
index 553ccd160..3ba3ee035 100644
--- a/src/Mod/Web/Gui/BrowserView.h
+++ b/src/Mod/Web/Gui/BrowserView.h
@@ -96,6 +96,7 @@ protected Q_SLOTS:
void onLinkClicked (const QUrl& url);
bool chckHostAllowed(const QString& host);
void onDownloadRequested(const QNetworkRequest& request);
+ void onUnsupportedContent(QNetworkReply* reply);
private:
WebView* view;