diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..f8f649e
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,10 @@
+language: cpp
+
+before_install:
+ - sudo apt-get install build-essential libqt4-core libqt4-dev libqt4-gui qt4-dev-tools libssl-dev
+
+script:
+ - cd sample
+ - qmake
+ - make
+ - sudo make install
diff --git a/Fervor.pri b/Fervor.pri
index 6249172..1cd2079 100644
--- a/Fervor.pri
+++ b/Fervor.pri
@@ -34,7 +34,7 @@ SOURCES += \
$$PWD/fvplatform.cpp \
$$PWD/fvignoredversions.cpp \
$$PWD/fvavailableupdate.cpp \
- $$PWD/fvupdateconfirmdialog.cpp
+ $$PWD/fvdownloaddialog.cpp
HEADERS += \
$$PWD/fvupdatewindow.h \
@@ -43,10 +43,12 @@ HEADERS += \
$$PWD/fvplatform.h \
$$PWD/fvignoredversions.h \
$$PWD/fvavailableupdate.h \
- $$PWD/fvupdateconfirmdialog.h
+ $$PWD/fvdownloaddialog.h
FORMS += $$PWD/fvupdatewindow.ui \
- $$PWD/fvupdateconfirmdialog.ui
+ $$PWD/fvdownloaddialog.ui
TRANSLATIONS += $$PWD/fervor_lt.ts
CODECFORTR = UTF-8
+
+RESOURCES += $$PWD/fv_resources.qrc
diff --git a/README.mdown b/README.mdown
index dc33579..6a4a943 100644
--- a/README.mdown
+++ b/README.mdown
@@ -1,11 +1,10 @@
-Check out the [`autoupdate` branch](fervor/tree/autoupdate) by Torben Dannhauer too!
-
-----
+## Fervor
-Fervor is a simple, multiplatform ([Qt](http://qt.nokia.com/)-based) application update tool, inspired by [Sparkle](http://sparkle.andymatuschak.org/).
+
+Check out the [`autoupdate` branch](fervor/tree/autoupdate) by Torben Dannhauer too!
-# Description
+---
Fervor is a software library that you include into your own [Qt](http://qt.nokia.com/)-based application in order to enable the application to automatically check for updates and suggest to install them.
@@ -13,8 +12,7 @@ When installed and enabled, Fervor downloads a "flavoured" RSS feed (dubbed "app
When a newer version of the application is found in the "appcast" (e.g. the user is using 1.0, and 1.1 is available), a dialog is presented to the user (see below for example) that allows the user to choose whether he/she wants to install the update, be reminded about the update later, or skip a particular proposed version altogether. A dialog also shows some release notes about the proposed update that help the user to choose whether or not to install an update.
-At the moment, Fervor is not as cool as [Sparkle](http://sparkle.andymatuschak.org/) -- it is not able to install the actual update automatically (the user is given an option to download and install the update manually). Pull requests with unattended install modules for `.dmg`, `.pkg` (Mac OS X), `.msi` (Windows), `.rpm`, `.deb` (Linux) are welcome!
-
+Fervor also supports downloading and installing the updates directly from the application using an integrated dowload dialog.
# Features
@@ -30,9 +28,6 @@ At the moment, Fervor is not as cool as [Sparkle](http://sparkle.andymatuschak.o

-
-
-
# Installation and Usage
(This is a description of the sample application located in `sample/`.)
diff --git a/bin/autostyle.cmd b/bin/autostyle.cmd
new file mode 100755
index 0000000..afcfdc4
--- /dev/null
+++ b/bin/autostyle.cmd
@@ -0,0 +1,21 @@
+:: Description: This script changes the style format of
+:: all the source code of the project.
+
+:: Setup the command line
+@echo off
+title Autostyle
+
+:: Go to the directory where the script is run
+cd /d %~dp0
+
+:: Style and format the source code recursively
+astyle --style=allman -C -S -xG -Y -XW -w -f -F -p -xd -k3 -y -xj -c -K -L --suffix=none --recursive ../*.cpp ../*.h ../*.hxx ../*.cxx
+
+:: Notify the user that we have finished
+echo.
+echo Code styling complete!
+echo.
+
+:: Let the user see the output
+pause
+
diff --git a/bin/autostyle.sh b/bin/autostyle.sh
new file mode 100755
index 0000000..c6d83f9
--- /dev/null
+++ b/bin/autostyle.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+# Description: This script changes the style format of
+# all the source code of the project.
+
+# Run only on the directory of the script
+cd "$(dirname ${BASH_SOURCE[0]})"
+
+# Style and format recursively
+astyle --style=allman -C -S -xG -Y -XW -w -f -F -p -xd -k3 -y -xj -c -K -L --suffix=none --recursive ../*.cpp ../*.h ../*.hxx ../*.cxx
+
+# Notify the user that we are done
+echo
+echo "Code styling complete!"
+echo
+
+# Let the user see the output
+read -n1 -r -p "Press any key to continue..." key
+clear
diff --git a/bin/readme.txt b/bin/readme.txt
new file mode 100755
index 0000000..b8cd6bf
--- /dev/null
+++ b/bin/readme.txt
@@ -0,0 +1 @@
+The scripts contained in this folder will require you to install the [astyle](http://astyle.sf.net) source code styler.
\ No newline at end of file
diff --git a/fv_resources.qrc b/fv_resources.qrc
new file mode 100755
index 0000000..ac0c9e0
--- /dev/null
+++ b/fv_resources.qrc
@@ -0,0 +1,5 @@
+
+
+ update.png
+
+
diff --git a/fvdownloaddialog.cpp b/fvdownloaddialog.cpp
new file mode 100755
index 0000000..36b31bb
--- /dev/null
+++ b/fvdownloaddialog.cpp
@@ -0,0 +1,293 @@
+/*
+ * (C) Copyright 2014 Alex Spataru
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Lesser General Public License
+ * (LGPL) version 2.1 which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/lgpl-2.1.html
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ */
+
+#include "fvdownloaddialog.h"
+#include "ui_fvdownloaddialog.h"
+
+#include "fvupdater.h"
+#include "fvavailableupdate.h"
+
+#include
+#include
+
+FvDownloadDialog::FvDownloadDialog (QWidget *parent)
+ : QWidget (parent)
+ , ui (new Ui::FvDownloadDialog)
+{
+
+ // Setup the UI
+ ui->setupUi (this);
+ setAttribute(Qt::WA_DeleteOnClose);
+
+ // Make the window look like a dialog
+ QIcon _blank;
+ setWindowIcon (_blank);
+ setWindowModality (Qt::WindowModal);
+ setWindowFlags (Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint);
+
+ // Connect SIGNALS/SLOTS
+ connect (ui->stopButton, SIGNAL (clicked()), this, SLOT (cancelDownload()));
+ connect (ui->openButton, SIGNAL (clicked()), this, SLOT (installUpdate()));
+
+ // Configure open button
+ ui->openButton->setEnabled (false);
+ ui->openButton->setVisible (false);
+
+ // Initialize the network access manager
+ m_manager = new QNetworkAccessManager (this);
+
+ // Avoid SSL issues
+ connect (m_manager, SIGNAL (sslErrors (QNetworkReply *, QList)), this,
+ SLOT (ignoreSslErrors (QNetworkReply *, QList)));
+}
+
+FvDownloadDialog::~FvDownloadDialog (void)
+{
+ delete ui;
+}
+
+void FvDownloadDialog::beginDownload ()
+{
+ FvAvailableUpdate* proposedUpdate = FvUpdater::sharedUpdater()->GetProposedUpdate();
+
+ if (! proposedUpdate) {
+ qDebug() << "Cannot get download URL!";
+ return;
+ }
+
+ // Know from where to download the updates
+ QUrl url = QUrl(proposedUpdate->GetEnclosureUrl().toString());
+
+ // Reset the UI
+ ui->progressBar->setValue (0);
+ ui->stopButton->setText (tr ("Stop"));
+ ui->downloadLabel->setText (tr ("Downloading updates"));
+ ui->timeLabel->setText (tr ("Time remaining") + ": " + tr ("unknown"));
+
+ // Begin the download
+ m_reply = m_manager->get (QNetworkRequest (url));
+ m_start_time = QDateTime::currentDateTime().toTime_t();
+
+ // Update the progress bar value automatically
+ connect (m_reply, SIGNAL (downloadProgress (qint64, qint64)), this,
+ SLOT (updateProgress (qint64, qint64)));
+
+ // Write the file to the hard disk once the download is finished
+ connect (m_reply, SIGNAL (finished()), this, SLOT (downloadFinished()));
+
+ // Show the dialog
+ showNormal();
+}
+
+void FvDownloadDialog::installUpdate (void)
+{
+ QMessageBox msg;
+ msg.setIcon (QMessageBox::Question);
+ msg.setText ("" +
+ tr ("To apply the update(s), you must first quit %1")
+ .arg (qApp->applicationName()) +
+ "");
+ msg.setInformativeText (tr ("Do you want to quit %1 now?").arg (qApp->applicationName()));
+ msg.setStandardButtons (QMessageBox::Yes | QMessageBox::No);
+
+ if (msg.exec() == QMessageBox::Yes)
+ {
+ openDownload();
+ qApp->closeAllWindows();
+ }
+
+ else
+ {
+ ui->openButton->setEnabled (true);
+ ui->openButton->setVisible (true);
+ ui->timeLabel->setText (tr ("Click the \"Open\" button to apply the update"));
+ }
+}
+
+void FvDownloadDialog::openDownload (void)
+{
+ if (!m_path.isEmpty())
+ {
+ QString url = m_path;
+
+ if (url.startsWith ("/"))
+ url = "file://" + url;
+
+ else
+ url = "file:///" + url;
+
+ QDesktopServices::openUrl (url);
+ }
+}
+
+void FvDownloadDialog::cancelDownload (void)
+{
+ if (!m_reply->isFinished())
+ {
+ QMessageBox _message;
+ _message.setWindowTitle (tr ("Updater"));
+ _message.setIcon (QMessageBox::Question);
+ _message.setStandardButtons (QMessageBox::Yes | QMessageBox::No);
+ _message.setText (tr ("Are you sure you want to cancel the download?"));
+
+ if (_message.exec() == QMessageBox::Yes)
+ {
+ hide();
+ m_reply->abort();
+ }
+ }
+
+ else
+ hide();
+}
+
+void FvDownloadDialog::downloadFinished (void)
+{
+ QByteArray data = m_reply->readAll();
+
+ // File is invalid, alert the user and cancel operation
+ if (data.isEmpty()) {
+ QMessageBox::warning(this, tr("Software Updater"),
+ tr("Downloaded data is empty!"));
+
+ close();
+ return;
+ }
+
+ ui->stopButton->setText (tr ("Close"));
+ ui->downloadLabel->setText (tr ("Download complete!"));
+ ui->timeLabel->setText (tr ("The installer will open in a separate window..."));
+
+ if (!data.isEmpty())
+ {
+ QStringList list = m_reply->url().toString().split ("/");
+ QFile file (QDir::tempPath() + "/" + list.at (list.count() - 1));
+ QMutex _mutex;
+
+ if (file.open (QIODevice::WriteOnly))
+ {
+ _mutex.lock();
+ file.write (data);
+ m_path = file.fileName();
+ file.close();
+ _mutex.unlock();
+ }
+
+ installUpdate();
+ }
+}
+
+void FvDownloadDialog::updateProgress (qint64 received, qint64 total)
+{
+ // We know the size of the download, so we can calculate the progress....
+ if (total > 0 && received > 0)
+ {
+ ui->progressBar->setMinimum (0);
+ ui->progressBar->setMaximum (100);
+
+ int _progress = (int) ((received * 100) / total);
+ ui->progressBar->setValue (_progress);
+
+ QString _total_string;
+ QString _received_string;
+
+ float _total = total;
+ float _received = received;
+
+ if (_total < 1024)
+ _total_string = tr ("%1 bytes").arg (_total);
+
+ else if (_total < 1024 * 1024)
+ {
+ _total = roundNumber (_total / 1024);
+ _total_string = tr ("%1 KB").arg (_total);
+ }
+
+ else
+ {
+ _total = roundNumber (_total / (1024 * 1024));
+ _total_string = tr ("%1 MB").arg (_total);
+ }
+
+ if (_received < 1024)
+ _received_string = tr ("%1 bytes").arg (_received);
+
+ else if (received < 1024 * 1024)
+ {
+ _received = roundNumber (_received / 1024);
+ _received_string = tr ("%1 KB").arg (_received);
+ }
+
+ else
+ {
+ _received = roundNumber (_received / (1024 * 1024));
+ _received_string = tr ("%1 MB").arg (_received);
+ }
+
+ ui->downloadLabel->setText (tr ("Downloading updates") + " (" + _received_string + " " + tr ("of") + " " + _total_string + ")");
+
+ uint _diff = QDateTime::currentDateTime().toTime_t() - m_start_time;
+
+ if (_diff > 0)
+ {
+ QString _time_string;
+ float _time_remaining = total / (received / _diff);
+
+ if (_time_remaining > 7200)
+ {
+ _time_remaining /= 3600;
+ _time_string = tr ("About %1 hours").arg (int (_time_remaining + 0.5));
+ }
+
+ else if (_time_remaining > 60)
+ {
+ _time_remaining /= 60;
+ _time_string = tr ("About %1 minutes").arg (int (_time_remaining + 0.5));
+ }
+
+ else if (_time_remaining <= 60)
+ _time_string = tr ("%1 seconds").arg (int (_time_remaining + 0.5));
+
+ ui->timeLabel->setText (tr ("Time remaining") + ": " + _time_string);
+ }
+ }
+
+ // We do not know the size of the download, so we avoid scaring the shit out
+ // of the user
+ else
+ {
+ ui->progressBar->setValue (-1);
+ ui->progressBar->setMinimum (0);
+ ui->progressBar->setMaximum (0);
+ ui->downloadLabel->setText (tr ("Downloading updates"));
+ ui->timeLabel->setText (tr ("Time remaining") + ": " + tr ("Unknown"));
+ }
+}
+
+void FvDownloadDialog::ignoreSslErrors (QNetworkReply *reply,
+ const QList& error)
+{
+#ifndef Q_OS_IOS
+ reply->ignoreSslErrors (error);
+#else
+ Q_UNUSED (reply);
+ Q_UNUSED (error);
+#endif
+}
+
+float FvDownloadDialog::roundNumber (const float& input)
+{
+ return roundf (input * 100) / 100;
+}
diff --git a/fvdownloaddialog.h b/fvdownloaddialog.h
new file mode 100755
index 0000000..4f2e9c8
--- /dev/null
+++ b/fvdownloaddialog.h
@@ -0,0 +1,51 @@
+#ifndef FV_DOWNLOAD_DIALOG_H
+#define FV_DOWNLOAD_DIALOG_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+namespace Ui
+{
+class FvDownloadDialog;
+}
+
+class FvDownloadDialog : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ explicit FvDownloadDialog (QWidget *parent = 0);
+ ~FvDownloadDialog (void);
+
+ void beginDownload ();
+
+ private slots:
+ void openDownload (void);
+ void installUpdate (void);
+ void cancelDownload (void);
+ void downloadFinished (void);
+ void updateProgress (qint64 received, qint64 total);
+ void ignoreSslErrors (QNetworkReply *reply, const QList& error);
+
+ private:
+ Ui::FvDownloadDialog *ui;
+
+ QString m_path;
+ bool m_download_paused;
+
+ QNetworkReply *m_reply;
+ QNetworkAccessManager *m_manager;
+
+ uint m_start_time;
+
+ float roundNumber (const float& input);
+};
+
+#endif
diff --git a/fvdownloaddialog.ui b/fvdownloaddialog.ui
new file mode 100755
index 0000000..e12fc29
--- /dev/null
+++ b/fvdownloaddialog.ui
@@ -0,0 +1,179 @@
+
+
+ FvDownloadDialog
+
+
+
+ 0
+ 0
+ 464
+ 164
+
+
+
+
+ 0
+ 0
+
+
+
+ Software Updater
+
+
+
+ 0
+
+
+ 12
+
+
+ 12
+
+
+ 12
+
+
+ 12
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 72
+ 72
+
+
+
+ div.image {
+ width: 72px;
+ height: 72px;
+ background-image: url('data:image/png;base64,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');
+}
+
+
+
+
+
+
+ :/icons/update.png
+
+
+
+ -
+
+
+
+ 0
+
+
-
+
+
+
+ 75
+ true
+
+
+
+ Downloading updates
+
+
+
+ -
+
+
+
+ 320
+ 0
+
+
+
+ 0
+
+
+ false
+
+
+
+ -
+
+
+ Time remaining: 0 minutes
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+ 12
+
+
+ 12
+
+
+ 12
+
+
+ 12
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Open
+
+
+
+ -
+
+
+ Stop
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fvupdateconfirmdialog.cpp b/fvupdateconfirmdialog.cpp
deleted file mode 100644
index 33afeb0..0000000
--- a/fvupdateconfirmdialog.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-#include "fvupdateconfirmdialog.h"
-#include "fvavailableupdate.h"
-#include "fvupdater.h"
-#include "ui_fvupdateconfirmdialog.h"
-#include
-
-
-FvUpdateConfirmDialog::FvUpdateConfirmDialog(QWidget *parent) :
- QDialog(parent),
- m_ui(new Ui::FvUpdateConfirmDialog)
-{
- m_ui->setupUi(this);
-
- // Delete on close
- setAttribute(Qt::WA_DeleteOnClose, true);
-
- // Set the "close app, then reopen" string
- QString closeReopenString = m_ui->downloadThisUpdateLabel->text().arg(QString::fromUtf8(FV_APP_NAME));
- m_ui->downloadThisUpdateLabel->setText(closeReopenString);
-
- // Connect buttons
- connect(m_ui->confirmButtonBox, SIGNAL(accepted()),
- FvUpdater::sharedUpdater(), SLOT(UpdateInstallationConfirmed()));
- connect(m_ui->confirmButtonBox, SIGNAL(rejected()),
- FvUpdater::sharedUpdater(), SLOT(UpdateInstallationNotConfirmed()));
-}
-
-FvUpdateConfirmDialog::~FvUpdateConfirmDialog()
-{
- delete m_ui;
-}
-
-bool FvUpdateConfirmDialog::UpdateWindowWithCurrentProposedUpdate()
-{
- FvAvailableUpdate* proposedUpdate = FvUpdater::sharedUpdater()->GetProposedUpdate();
- if (! proposedUpdate) {
- return false;
- }
-
- QString downloadLinkString = m_ui->updateFileLinkLabel->text()
- .arg(proposedUpdate->GetEnclosureUrl().toString());
- m_ui->updateFileLinkLabel->setText(downloadLinkString);
-
- return true;
-}
-
-void FvUpdateConfirmDialog::closeEvent(QCloseEvent* event)
-{
- FvUpdater::sharedUpdater()->updateConfirmationDialogWasClosed();
- event->accept();
-}
diff --git a/fvupdateconfirmdialog.h b/fvupdateconfirmdialog.h
deleted file mode 100644
index 19db4ad..0000000
--- a/fvupdateconfirmdialog.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef FVUPDATECONFIRMDIALOG_H
-#define FVUPDATECONFIRMDIALOG_H
-
-#include
-
-namespace Ui {
-class FvUpdateConfirmDialog;
-}
-
-class FvUpdateConfirmDialog : public QDialog
-{
- Q_OBJECT
-
-public:
- explicit FvUpdateConfirmDialog(QWidget *parent = 0);
- ~FvUpdateConfirmDialog();
-
- // Update the current update proposal from FvUpdater
- bool UpdateWindowWithCurrentProposedUpdate();
-
- void closeEvent(QCloseEvent* event);
-
-private:
- Ui::FvUpdateConfirmDialog* m_ui;
-};
-
-#endif // FVUPDATECONFIRMDIALOG_H
diff --git a/fvupdateconfirmdialog.ui b/fvupdateconfirmdialog.ui
deleted file mode 100644
index a7aa039..0000000
--- a/fvupdateconfirmdialog.ui
+++ /dev/null
@@ -1,65 +0,0 @@
-
-
- FvUpdateConfirmDialog
-
-
-
- 0
- 0
- 480
- 160
-
-
-
- Software Update
-
-
- -
-
-
- The update file is located at:
-
-
-
- -
-
-
- <a href="%1">%1</a>
-
-
- true
-
-
- Qt::TextBrowserInteraction
-
-
-
- -
-
-
- Download this update, close "%1", install it, and then reopen "%1".
-
-
-
- -
-
-
- When you click "OK", this link will be opened in your browser.
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QDialogButtonBox::Cancel|QDialogButtonBox::Ok
-
-
-
-
-
-
-
-
diff --git a/fvupdater.cpp b/fvupdater.cpp
index d82c0b4..fc75ec7 100644
--- a/fvupdater.cpp
+++ b/fvupdater.cpp
@@ -1,6 +1,6 @@
#include "fvupdater.h"
#include "fvupdatewindow.h"
-#include "fvupdateconfirmdialog.h"
+#include "fvdownloaddialog.h"
#include "fvplatform.h"
#include "fvignoredversions.h"
#include "fvavailableupdate.h"
@@ -19,7 +19,7 @@
#ifdef FV_DEBUG
- // Unit tests
+// Unit tests
# include "fvversioncomparatortest.h"
#endif
@@ -29,384 +29,391 @@ FvUpdater* FvUpdater::m_Instance = 0;
FvUpdater* FvUpdater::sharedUpdater()
{
- static QMutex mutex;
- if (! m_Instance) {
- mutex.lock();
+ static QMutex mutex;
+ if (! m_Instance) {
+ mutex.lock();
- if (! m_Instance) {
- m_Instance = new FvUpdater;
- }
+ if (! m_Instance) {
+ m_Instance = new FvUpdater;
+ }
- mutex.unlock();
- }
+ mutex.unlock();
+ }
- return m_Instance;
+ return m_Instance;
}
void FvUpdater::drop()
{
- static QMutex mutex;
- mutex.lock();
- delete m_Instance;
- m_Instance = 0;
- mutex.unlock();
+ static QMutex mutex;
+ mutex.lock();
+ delete m_Instance;
+ m_Instance = 0;
+ mutex.unlock();
}
FvUpdater::FvUpdater() : QObject(0)
{
- m_reply = 0;
- m_updaterWindow = 0;
- m_updateConfirmationDialog = 0;
- m_proposedUpdate = 0;
+ m_reply = 0;
+ m_updaterWindow = 0;
+ m_download_dialog = 0;
+ m_proposedUpdate = 0;
- // Translation mechanism
- installTranslator();
+ // Translation mechanism
+ installTranslator();
#ifdef FV_DEBUG
- // Unit tests
- FvVersionComparatorTest* test = new FvVersionComparatorTest();
- test->runAll();
- delete test;
+ // Unit tests
+ FvVersionComparatorTest* test = new FvVersionComparatorTest();
+ test->runAll();
+ delete test;
#endif
}
FvUpdater::~FvUpdater()
{
- if (m_proposedUpdate) {
- delete m_proposedUpdate;
- m_proposedUpdate = 0;
- }
+ if (m_proposedUpdate) {
+ delete m_proposedUpdate;
+ m_proposedUpdate = 0;
+ }
- hideUpdateConfirmationDialog();
- hideUpdaterWindow();
+ hideDownloadDialog();
+ hideUpdaterWindow();
}
void FvUpdater::installTranslator()
{
- QTranslator translator;
- QString locale = QLocale::system().name();
- translator.load(QString("fervor_") + locale);
+ QTranslator translator;
+ QString locale = QLocale::system().name();
+ translator.load(QString("fervor_") + locale);
//QTextCodec::setCodecForTr(QTextCodec::codecForName("utf8"));
- qApp->installTranslator(&translator);
+ qApp->installTranslator(&translator);
}
void FvUpdater::showUpdaterWindowUpdatedWithCurrentUpdateProposal()
{
- // Destroy window if already exists
- hideUpdaterWindow();
+ // Destroy window if already exists
+ hideUpdaterWindow();
- // Create a new window
- m_updaterWindow = new FvUpdateWindow();
- m_updaterWindow->UpdateWindowWithCurrentProposedUpdate();
- m_updaterWindow->show();
+ // Create a new window
+ m_updaterWindow = new FvUpdateWindow();
+ m_updaterWindow->UpdateWindowWithCurrentProposedUpdate();
+ m_updaterWindow->show();
}
void FvUpdater::hideUpdaterWindow()
{
- if (m_updaterWindow) {
- if (! m_updaterWindow->close()) {
- qWarning() << "Update window didn't close, leaking memory from now on";
- }
+ if (m_updaterWindow) {
+ if (! m_updaterWindow->close()) {
+ qWarning() << "Update window didn't close, leaking memory from now on";
+ }
- // not deleting because of Qt::WA_DeleteOnClose
+ // not deleting because of Qt::WA_DeleteOnClose
- m_updaterWindow = 0;
- }
+ m_updaterWindow = 0;
+ }
}
void FvUpdater::updaterWindowWasClosed()
{
- // (Re-)nullify a pointer to a destroyed QWidget or you're going to have a bad time.
- m_updaterWindow = 0;
+ // (Re-)nullify a pointer to a destroyed QWidget or you're going to have a bad time.
+ m_updaterWindow = 0;
}
-
-void FvUpdater::showUpdateConfirmationDialogUpdatedWithCurrentUpdateProposal()
-{
- // Destroy dialog if already exists
- hideUpdateConfirmationDialog();
-
- // Create a new window
- m_updateConfirmationDialog = new FvUpdateConfirmDialog();
- m_updateConfirmationDialog->UpdateWindowWithCurrentProposedUpdate();
- m_updateConfirmationDialog->show();
-}
-
-void FvUpdater::hideUpdateConfirmationDialog()
+void FvUpdater::hideDownloadDialog()
{
- if (m_updateConfirmationDialog) {
- if (! m_updateConfirmationDialog->close()) {
- qWarning() << "Update confirmation dialog didn't close, leaking memory from now on";
- }
+ if (m_download_dialog) {
+ if (! m_download_dialog->close()) {
+ qWarning() << "Update confirmation dialog didn't close, leaking memory from now on";
+ }
- // not deleting because of Qt::WA_DeleteOnClose
+ // not deleting because of Qt::WA_DeleteOnClose
- m_updateConfirmationDialog = 0;
- }
+ m_download_dialog = 0;
+ }
}
-void FvUpdater::updateConfirmationDialogWasClosed()
+void FvUpdater::downloadDialogWasClosed()
{
- // (Re-)nullify a pointer to a destroyed QWidget or you're going to have a bad time.
- m_updateConfirmationDialog = 0;
+ // (Re-)nullify a pointer to a destroyed QWidget or you're going to have a bad time.
+ m_download_dialog = 0;
}
void FvUpdater::SetFeedURL(QUrl feedURL)
{
- m_feedURL = feedURL;
+ m_feedURL = feedURL;
}
void FvUpdater::SetFeedURL(QString feedURL)
{
- SetFeedURL(QUrl(feedURL));
+ SetFeedURL(QUrl(feedURL));
}
QString FvUpdater::GetFeedURL()
{
- return m_feedURL.toString();
+ return m_feedURL.toString();
}
FvAvailableUpdate* FvUpdater::GetProposedUpdate()
{
- return m_proposedUpdate;
+ return m_proposedUpdate;
}
void FvUpdater::InstallUpdate()
{
- qDebug() << "Install update";
+ qDebug() << "Install update";
+
+ // Destroy dialog if already exists
+ hideDownloadDialog();
- showUpdateConfirmationDialogUpdatedWithCurrentUpdateProposal();
+ // Create a new window
+ m_download_dialog = new FvDownloadDialog();
+ m_download_dialog->beginDownload();
+
+ // Hide update information dialog
+ hideUpdaterWindow();
}
void FvUpdater::SkipUpdate()
{
- qDebug() << "Skip update";
+ qDebug() << "Skip update";
- FvAvailableUpdate* proposedUpdate = GetProposedUpdate();
- if (! proposedUpdate) {
- qWarning() << "Proposed update is NULL (shouldn't be at this point)";
- return;
- }
+ FvAvailableUpdate* proposedUpdate = GetProposedUpdate();
+ if (! proposedUpdate) {
+ qWarning() << "Proposed update is NULL (shouldn't be at this point)";
+ return;
+ }
- // Start ignoring this particular version
- FVIgnoredVersions::IgnoreVersion(proposedUpdate->GetEnclosureVersion());
+ // Start ignoring this particular version
+ FVIgnoredVersions::IgnoreVersion(proposedUpdate->GetEnclosureVersion());
- hideUpdaterWindow();
- hideUpdateConfirmationDialog(); // if any; shouldn't be shown at this point, but who knows
+ hideUpdaterWindow();
+ hideDownloadDialog(); // if any; shouldn't be shown at this point, but who knows
}
void FvUpdater::RemindMeLater()
{
- qDebug() << "Remind me later";
+ qDebug() << "Remind me later";
- hideUpdaterWindow();
- hideUpdateConfirmationDialog(); // if any; shouldn't be shown at this point, but who knows
+ hideUpdaterWindow();
+ hideDownloadDialog(); // if any; shouldn't be shown at this point, but who knows
}
void FvUpdater::UpdateInstallationConfirmed()
{
- qDebug() << "Confirm update installation";
-
- FvAvailableUpdate* proposedUpdate = GetProposedUpdate();
- if (! proposedUpdate) {
- qWarning() << "Proposed update is NULL (shouldn't be at this point)";
- return;
- }
-
- // Open a link
- if (! QDesktopServices::openUrl(proposedUpdate->GetEnclosureUrl())) {
- showErrorDialog(tr("Unable to open this link in a browser. Please do it manually."), true);
- return;
- }
-
- hideUpdaterWindow();
- hideUpdateConfirmationDialog();
+ qDebug() << "Confirm update installation";
+
+ FvAvailableUpdate* proposedUpdate = GetProposedUpdate();
+ if (! proposedUpdate) {
+ qWarning() << "Proposed update is NULL (shouldn't be at this point)";
+ return;
+ }
+
+ // Open a link
+ if (! QDesktopServices::openUrl(proposedUpdate->GetEnclosureUrl())) {
+ showErrorDialog(tr("Unable to open this link in a browser. Please do it manually."), true);
+ return;
+ }
+
+ hideUpdaterWindow();
+ hideDownloadDialog();
}
void FvUpdater::UpdateInstallationNotConfirmed()
{
- qDebug() << "Do not confirm update installation";
+ qDebug() << "Do not confirm update installation";
- hideUpdateConfirmationDialog(); // if any; shouldn't be shown at this point, but who knows
- // leave the "update proposal window" inact
+ hideDownloadDialog(); // if any; shouldn't be shown at this point, but who knows
+ // leave the "update proposal window" inact
}
bool FvUpdater::CheckForUpdates(bool silentAsMuchAsItCouldGet)
{
- if (m_feedURL.isEmpty()) {
- qCritical() << "Please set feed URL via setFeedURL() before calling CheckForUpdates().";
- return false;
- }
-
- m_silentAsMuchAsItCouldGet = silentAsMuchAsItCouldGet;
-
- // Check if application's organization name and domain are set, fail otherwise
- // (nowhere to store QSettings to)
- if (QApplication::organizationName().isEmpty()) {
- qCritical() << "QApplication::organizationName is not set. Please do that.";
- return false;
- }
- if (QApplication::organizationDomain().isEmpty()) {
- qCritical() << "QApplication::organizationDomain is not set. Please do that.";
- return false;
- }
-
- // Set application name / version is not set yet
- if (QApplication::applicationName().isEmpty()) {
- QString appName = QString::fromUtf8(FV_APP_NAME);
- qWarning() << "QApplication::applicationName is not set, setting it to '" << appName << "'";
- QApplication::setApplicationName(appName);
- }
- if (QApplication::applicationVersion().isEmpty()) {
- QString appVersion = QString::fromUtf8(FV_APP_VERSION);
- qWarning() << "QApplication::applicationVersion is not set, setting it to '" << appVersion << "'";
- QApplication::setApplicationVersion(appVersion);
- }
-
- cancelDownloadFeed();
- m_httpRequestAborted = false;
- startDownloadFeed(m_feedURL);
-
- return true;
+ if (m_feedURL.isEmpty()) {
+ qCritical() << "Please set feed URL via setFeedURL() before calling CheckForUpdates().";
+ return false;
+ }
+
+ m_silentAsMuchAsItCouldGet = silentAsMuchAsItCouldGet;
+
+ // Check if application's organization name and domain are set, fail otherwise
+ // (nowhere to store QSettings to)
+ if (QApplication::organizationName().isEmpty()) {
+ qCritical() << "QApplication::organizationName is not set. Please do that.";
+ return false;
+ }
+ if (QApplication::organizationDomain().isEmpty()) {
+ qCritical() << "QApplication::organizationDomain is not set. Please do that.";
+ return false;
+ }
+
+ // Set application name / version is not set yet
+ if (QApplication::applicationName().isEmpty()) {
+ QString appName = QString::fromUtf8(FV_APP_NAME);
+ qWarning() << "QApplication::applicationName is not set, setting it to '" << appName << "'";
+ QApplication::setApplicationName(appName);
+ }
+ if (QApplication::applicationVersion().isEmpty()) {
+ QString appVersion = QString::fromUtf8(FV_APP_VERSION);
+ qWarning() << "QApplication::applicationVersion is not set, setting it to '" << appVersion << "'";
+ QApplication::setApplicationVersion(appVersion);
+ }
+
+ cancelDownloadFeed();
+ m_httpRequestAborted = false;
+ startDownloadFeed(m_feedURL);
+
+ return true;
}
bool FvUpdater::CheckForUpdatesSilent()
{
- return CheckForUpdates(true);
+ return CheckForUpdates(true);
}
bool FvUpdater::CheckForUpdatesNotSilent()
{
- return CheckForUpdates(false);
+ return CheckForUpdates(false);
}
void FvUpdater::startDownloadFeed(QUrl url)
{
- m_xml.clear();
+ m_xml.clear();
+
+ QNetworkRequest request;
+ request.setHeader(QNetworkRequest::ContentTypeHeader, "application/xml");
+
+ // The QNetworkRequest::UserAgentHeader seems to work only in Qt5...
+ // however, creating the header "manually" allows the library to
+ // work also in Qt4.
+ //
- QNetworkRequest request;
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/xml");
- request.setHeader(QNetworkRequest::UserAgentHeader, QApplication::applicationName());
- request.setUrl(url);
+ QString _agent = QString("%1/%2 (%3)")
+ .arg(QApplication::applicationName())
+ .arg(QApplication::applicationVersion())
+ .arg(QApplication::organizationName());
- m_reply = m_qnam.get(request);
+ request.setRawHeader("User-Agent", _agent.toUtf8());
+ request.setUrl(url);
- connect(m_reply, SIGNAL(readyRead()), this, SLOT(httpFeedReadyRead()));
- connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(httpFeedUpdateDataReadProgress(qint64, qint64)));
- connect(m_reply, SIGNAL(finished()), this, SLOT(httpFeedDownloadFinished()));
+ m_reply = m_qnam.get(request);
+
+ connect(m_reply, SIGNAL(readyRead()), this, SLOT(httpFeedReadyRead()));
+ connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(httpFeedUpdateDataReadProgress(qint64, qint64)));
+ connect(m_reply, SIGNAL(finished()), this, SLOT(httpFeedDownloadFinished()));
}
void FvUpdater::cancelDownloadFeed()
{
- if (m_reply) {
- m_httpRequestAborted = true;
- m_reply->abort();
- }
+ if (m_reply) {
+ m_httpRequestAborted = true;
+ m_reply->abort();
+ }
}
void FvUpdater::httpFeedReadyRead()
{
- // this slot gets called every time the QNetworkReply has new data.
- // We read all of its new data and write it into the file.
- // That way we use less RAM than when reading it at the finished()
- // signal of the QNetworkReply
- m_xml.addData(m_reply->readAll());
+ // this slot gets called every time the QNetworkReply has new data.
+ // We read all of its new data and write it into the file.
+ // That way we use less RAM than when reading it at the finished()
+ // signal of the QNetworkReply
+ m_xml.addData(m_reply->readAll());
}
void FvUpdater::httpFeedUpdateDataReadProgress(qint64 bytesRead,
- qint64 totalBytes)
+ qint64 totalBytes)
{
- Q_UNUSED(bytesRead);
- Q_UNUSED(totalBytes);
+ Q_UNUSED(bytesRead);
+ Q_UNUSED(totalBytes);
- if (m_httpRequestAborted) {
- return;
- }
+ if (m_httpRequestAborted) {
+ return;
+ }
}
void FvUpdater::httpFeedDownloadFinished()
{
- if (m_httpRequestAborted) {
- m_reply->deleteLater();
- return;
- }
+ if (m_httpRequestAborted) {
+ m_reply->deleteLater();
+ return;
+ }
- QVariant redirectionTarget = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
- if (m_reply->error()) {
+ QVariant redirectionTarget = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
+ if (m_reply->error()) {
- // Error.
- showErrorDialog(tr("Feed download failed: %1.").arg(m_reply->errorString()), false);
+ // Error.
+ showErrorDialog(tr("Feed download failed: %1.").arg(m_reply->errorString()), false);
- } else if (! redirectionTarget.isNull()) {
- QUrl newUrl = m_feedURL.resolved(redirectionTarget.toUrl());
+ } else if (! redirectionTarget.isNull()) {
+ QUrl newUrl = m_feedURL.resolved(redirectionTarget.toUrl());
- m_feedURL = newUrl;
- m_reply->deleteLater();
+ m_feedURL = newUrl;
+ m_reply->deleteLater();
- startDownloadFeed(m_feedURL);
- return;
+ startDownloadFeed(m_feedURL);
+ return;
- } else {
+ } else {
- // Done.
- xmlParseFeed();
+ // Done.
+ xmlParseFeed();
- }
+ }
- m_reply->deleteLater();
- m_reply = 0;
+ m_reply->deleteLater();
+ m_reply = 0;
}
bool FvUpdater::xmlParseFeed()
{
- QString currentTag, currentQualifiedTag;
+ QString currentTag, currentQualifiedTag;
- QString xmlTitle, xmlLink, xmlReleaseNotesLink, xmlPubDate, xmlEnclosureUrl,
- xmlEnclosureVersion, xmlEnclosurePlatform, xmlEnclosureType;
- unsigned long xmlEnclosureLength = 0;
+ QString xmlTitle, xmlLink, xmlReleaseNotesLink, xmlPubDate, xmlEnclosureUrl,
+ xmlEnclosureVersion, xmlEnclosurePlatform, xmlEnclosureType;
+ unsigned long xmlEnclosureLength = 0;
- // Parse
- while (! m_xml.atEnd()) {
+ // Parse
+ while (! m_xml.atEnd()) {
- m_xml.readNext();
+ m_xml.readNext();
- if (m_xml.isStartElement()) {
+ if (m_xml.isStartElement()) {
- currentTag = m_xml.name().toString();
- currentQualifiedTag = m_xml.qualifiedName().toString();
+ currentTag = m_xml.name().toString();
+ currentQualifiedTag = m_xml.qualifiedName().toString();
- if (m_xml.name() == "item") {
+ if (m_xml.name() == "item") {
- xmlTitle.clear();
- xmlLink.clear();
- xmlReleaseNotesLink.clear();
- xmlPubDate.clear();
- xmlEnclosureUrl.clear();
- xmlEnclosureVersion.clear();
- xmlEnclosurePlatform.clear();
- xmlEnclosureLength = 0;
- xmlEnclosureType.clear();
+ xmlTitle.clear();
+ xmlLink.clear();
+ xmlReleaseNotesLink.clear();
+ xmlPubDate.clear();
+ xmlEnclosureUrl.clear();
+ xmlEnclosureVersion.clear();
+ xmlEnclosurePlatform.clear();
+ xmlEnclosureLength = 0;
+ xmlEnclosureType.clear();
- } else if (m_xml.name() == "enclosure") {
+ } else if (m_xml.name() == "enclosure") {
- QXmlStreamAttributes attribs = m_xml.attributes();
+ QXmlStreamAttributes attribs = m_xml.attributes();
- if (attribs.hasAttribute("fervor:platform")) {
+ if (attribs.hasAttribute("fervor:platform")) {
- if (FvPlatform::CurrentlyRunningOnPlatform(attribs.value("fervor:platform").toString().trimmed())) {
+ if (FvPlatform::CurrentlyRunningOnPlatform(attribs.value("fervor:platform").toString().trimmed())) {
- xmlEnclosurePlatform = attribs.value("fervor:platform").toString().trimmed();
+ xmlEnclosurePlatform = attribs.value("fervor:platform").toString().trimmed();
- if (attribs.hasAttribute("url")) {
- xmlEnclosureUrl = attribs.value("url").toString().trimmed();
- } else {
- xmlEnclosureUrl = "";
- }
+ if (attribs.hasAttribute("url")) {
+ xmlEnclosureUrl = attribs.value("url").toString().trimmed();
+ } else {
+ xmlEnclosureUrl = "";
+ }
// First check for Sparkle's version, then overwrite with Fervor's version (if any)
if (attribs.hasAttribute("sparkle:version")) {
@@ -422,86 +429,86 @@ bool FvUpdater::xmlParseFeed()
}
}
- if (attribs.hasAttribute("length")) {
- xmlEnclosureLength = attribs.value("length").toString().toLong();
- } else {
- xmlEnclosureLength = 0;
- }
- if (attribs.hasAttribute("type")) {
- xmlEnclosureType = attribs.value("type").toString().trimmed();
- } else {
- xmlEnclosureType = "";
- }
+ if (attribs.hasAttribute("length")) {
+ xmlEnclosureLength = attribs.value("length").toString().toLong();
+ } else {
+ xmlEnclosureLength = 0;
+ }
+ if (attribs.hasAttribute("type")) {
+ xmlEnclosureType = attribs.value("type").toString().trimmed();
+ } else {
+ xmlEnclosureType = "";
+ }
- }
+ }
- }
+ }
- }
+ }
- } else if (m_xml.isEndElement()) {
+ } else if (m_xml.isEndElement()) {
- if (m_xml.name() == "item") {
+ if (m_xml.name() == "item") {
- // That's it - we have analyzed a single - and we'll stop
- // here (because the topmost is the most recent one, and thus
- // the newest version.
+ // That's it - we have analyzed a single
- and we'll stop
+ // here (because the topmost is the most recent one, and thus
+ // the newest version.
- return searchDownloadedFeedForUpdates(xmlTitle,
- xmlLink,
- xmlReleaseNotesLink,
- xmlPubDate,
- xmlEnclosureUrl,
- xmlEnclosureVersion,
- xmlEnclosurePlatform,
- xmlEnclosureLength,
- xmlEnclosureType);
+ return searchDownloadedFeedForUpdates(xmlTitle,
+ xmlLink,
+ xmlReleaseNotesLink,
+ xmlPubDate,
+ xmlEnclosureUrl,
+ xmlEnclosureVersion,
+ xmlEnclosurePlatform,
+ xmlEnclosureLength,
+ xmlEnclosureType);
- }
+ }
- } else if (m_xml.isCharacters() && ! m_xml.isWhitespace()) {
+ } else if (m_xml.isCharacters() && ! m_xml.isWhitespace()) {
- if (currentTag == "title") {
- xmlTitle += m_xml.text().toString().trimmed();
+ if (currentTag == "title") {
+ xmlTitle += m_xml.text().toString().trimmed();
- } else if (currentTag == "link") {
- xmlLink += m_xml.text().toString().trimmed();
+ } else if (currentTag == "link") {
+ xmlLink += m_xml.text().toString().trimmed();
- } else if (currentQualifiedTag == "sparkle:releaseNotesLink") {
- xmlReleaseNotesLink += m_xml.text().toString().trimmed();
+ } else if (currentQualifiedTag == "sparkle:releaseNotesLink") {
+ xmlReleaseNotesLink += m_xml.text().toString().trimmed();
- } else if (currentTag == "pubDate") {
- xmlPubDate += m_xml.text().toString().trimmed();
+ } else if (currentTag == "pubDate") {
+ xmlPubDate += m_xml.text().toString().trimmed();
- }
+ }
- }
+ }
- if (m_xml.error() && m_xml.error() != QXmlStreamReader::PrematureEndOfDocumentError) {
+ if (m_xml.error() && m_xml.error() != QXmlStreamReader::PrematureEndOfDocumentError) {
- showErrorDialog(tr("Feed parsing failed: %1 %2.").arg(QString::number(m_xml.lineNumber()), m_xml.errorString()), false);
- return false;
+ showErrorDialog(tr("Feed parsing failed: %1 %2.").arg(QString::number(m_xml.lineNumber()), m_xml.errorString()), false);
+ return false;
- }
- }
+ }
+ }
// No updates were found if we're at this point
// (not a single
- element found)
showInformationDialog(tr("No updates were found."), false);
- return false;
+ return false;
}
bool FvUpdater::searchDownloadedFeedForUpdates(QString xmlTitle,
- QString xmlLink,
- QString xmlReleaseNotesLink,
- QString xmlPubDate,
- QString xmlEnclosureUrl,
- QString xmlEnclosureVersion,
- QString xmlEnclosurePlatform,
- unsigned long xmlEnclosureLength,
- QString xmlEnclosureType)
+ QString xmlLink,
+ QString xmlReleaseNotesLink,
+ QString xmlPubDate,
+ QString xmlEnclosureUrl,
+ QString xmlEnclosureVersion,
+ QString xmlEnclosurePlatform,
+ unsigned long xmlEnclosureLength,
+ QString xmlEnclosureType)
{
qDebug() << "Title:" << xmlTitle;
qDebug() << "Link:" << xmlLink;
@@ -513,89 +520,79 @@ bool FvUpdater::searchDownloadedFeedForUpdates(QString xmlTitle,
qDebug() << "Enclosure length:" << xmlEnclosureLength;
qDebug() << "Enclosure type:" << xmlEnclosureType;
- // Validate
- if (xmlReleaseNotesLink.isEmpty()) {
- if (xmlLink.isEmpty()) {
- showErrorDialog(tr("Feed error: \"release notes\" link is empty"), false);
- return false;
- } else {
- xmlReleaseNotesLink = xmlLink;
- }
- } else {
- xmlLink = xmlReleaseNotesLink;
- }
- if (! (xmlLink.startsWith("http://") || xmlLink.startsWith("https://"))) {
- showErrorDialog(tr("Feed error: invalid \"release notes\" link"), false);
- return false;
- }
- if (xmlEnclosureUrl.isEmpty() || xmlEnclosureVersion.isEmpty() || xmlEnclosurePlatform.isEmpty()) {
+ // Validate
+ if (xmlReleaseNotesLink.isEmpty()) {
+ if (xmlLink.isEmpty()) {
+ showErrorDialog(tr("Feed error: \"release notes\" link is empty"), false);
+ return false;
+ } else {
+ xmlReleaseNotesLink = xmlLink;
+ }
+ } else {
+ xmlLink = xmlReleaseNotesLink;
+ }
+ if (! (xmlLink.startsWith("http://") || xmlLink.startsWith("https://"))) {
+ showErrorDialog(tr("Feed error: invalid \"release notes\" link"), false);
+ return false;
+ }
+ if (xmlEnclosureUrl.isEmpty() || xmlEnclosureVersion.isEmpty() || xmlEnclosurePlatform.isEmpty()) {
showErrorDialog(tr("Feed error: invalid \"enclosure\" with the download link"), false);
- return false;
- }
+ return false;
+ }
- // Relevant version?
- if (FVIgnoredVersions::VersionIsIgnored(xmlEnclosureVersion)) {
- qDebug() << "Version '" << xmlEnclosureVersion << "' is ignored, too old or something like that.";
+ // Relevant version?
+ if (FVIgnoredVersions::VersionIsIgnored(xmlEnclosureVersion)) {
+ qDebug() << "Version '" << xmlEnclosureVersion << "' is ignored, too old or something like that.";
- showInformationDialog(tr("No updates were found."), false);
+ showInformationDialog(tr("No updates were found."), false);
- return true; // Things have succeeded when you think of it.
- }
+ return true; // Things have succeeded when you think of it.
+ }
- //
- // Success! At this point, we have found an update that can be proposed
- // to the user.
- //
+ //
+ // Success! At this point, we have found an update that can be proposed
+ // to the user.
+ //
- if (m_proposedUpdate) {
- delete m_proposedUpdate; m_proposedUpdate = 0;
- }
- m_proposedUpdate = new FvAvailableUpdate();
- m_proposedUpdate->SetTitle(xmlTitle);
- m_proposedUpdate->SetReleaseNotesLink(xmlReleaseNotesLink);
- m_proposedUpdate->SetPubDate(xmlPubDate);
- m_proposedUpdate->SetEnclosureUrl(xmlEnclosureUrl);
- m_proposedUpdate->SetEnclosureVersion(xmlEnclosureVersion);
- m_proposedUpdate->SetEnclosurePlatform(xmlEnclosurePlatform);
- m_proposedUpdate->SetEnclosureLength(xmlEnclosureLength);
- m_proposedUpdate->SetEnclosureType(xmlEnclosureType);
+ if (m_proposedUpdate) {
+ delete m_proposedUpdate; m_proposedUpdate = 0;
+ }
+ m_proposedUpdate = new FvAvailableUpdate();
+ m_proposedUpdate->SetTitle(xmlTitle);
+ m_proposedUpdate->SetReleaseNotesLink(xmlReleaseNotesLink);
+ m_proposedUpdate->SetPubDate(xmlPubDate);
+ m_proposedUpdate->SetEnclosureUrl(xmlEnclosureUrl);
+ m_proposedUpdate->SetEnclosureVersion(xmlEnclosureVersion);
+ m_proposedUpdate->SetEnclosurePlatform(xmlEnclosurePlatform);
+ m_proposedUpdate->SetEnclosureLength(xmlEnclosureLength);
+ m_proposedUpdate->SetEnclosureType(xmlEnclosureType);
- // Show "look, there's an update" window
- showUpdaterWindowUpdatedWithCurrentUpdateProposal();
+ // Show "look, there's an update" window
+ showUpdaterWindowUpdatedWithCurrentUpdateProposal();
- return true;
+ return true;
}
void FvUpdater::showErrorDialog(QString message, bool showEvenInSilentMode)
{
- if (m_silentAsMuchAsItCouldGet) {
- if (! showEvenInSilentMode) {
- // Don't show errors in the silent mode
- return;
- }
- }
-
- QMessageBox dlFailedMsgBox;
- dlFailedMsgBox.setIcon(QMessageBox::Critical);
- dlFailedMsgBox.setText(tr("Error"));
- dlFailedMsgBox.setInformativeText(message);
- dlFailedMsgBox.exec();
+ if (! (m_silentAsMuchAsItCouldGet && !showEvenInSilentMode)) {
+ QMessageBox dlFailedMsgBox;
+ dlFailedMsgBox.setIcon(QMessageBox::Critical);
+ dlFailedMsgBox.setText(tr("Error"));
+ dlFailedMsgBox.setInformativeText(message);
+ dlFailedMsgBox.exec();
+ }
}
void FvUpdater::showInformationDialog(QString message, bool showEvenInSilentMode)
{
- if (m_silentAsMuchAsItCouldGet) {
- if (! showEvenInSilentMode) {
- // Don't show information dialogs in the silent mode
- return;
- }
- }
-
- QMessageBox dlInformationMsgBox;
- dlInformationMsgBox.setIcon(QMessageBox::Information);
- dlInformationMsgBox.setText(tr("Information"));
- dlInformationMsgBox.setInformativeText(message);
- dlInformationMsgBox.exec();
+ if (! (m_silentAsMuchAsItCouldGet && !showEvenInSilentMode)) {
+ QMessageBox dlInformationMsgBox;
+ dlInformationMsgBox.setIcon(QMessageBox::Information);
+ dlInformationMsgBox.setText(tr("Information"));
+ dlInformationMsgBox.setInformativeText(message);
+ dlInformationMsgBox.exec();
+ }
}
diff --git a/fvupdater.h b/fvupdater.h
index 04b69fa..edd85c6 100644
--- a/fvupdater.h
+++ b/fvupdater.h
@@ -6,10 +6,11 @@
#include
#include
#include
+
+
class FvUpdateWindow;
-class FvUpdateConfirmDialog;
class FvAvailableUpdate;
-
+class FvDownloadDialog;
class FvUpdater : public QObject
{
@@ -46,7 +47,7 @@ public slots:
protected:
friend class FvUpdateWindow; // Uses GetProposedUpdate() and others
- friend class FvUpdateConfirmDialog; // Uses GetProposedUpdate() and others
+ friend class FvDownloadDialog; // Uses GetProposedUpdate() and others
FvAvailableUpdate* GetProposedUpdate();
@@ -80,14 +81,12 @@ protected slots:
// Windows / dialogs
//
FvUpdateWindow* m_updaterWindow; // Updater window (NULL if not shown)
- void showUpdaterWindowUpdatedWithCurrentUpdateProposal(); // Show updater window
+ void showUpdaterWindowUpdatedWithCurrentUpdateProposal(); // Show updater window
void hideUpdaterWindow(); // Hide + destroy m_updaterWindow
void updaterWindowWasClosed(); // Sent by the updater window when it gets closed
- FvUpdateConfirmDialog* m_updateConfirmationDialog; // Update confirmation dialog (NULL if not shown)
- void showUpdateConfirmationDialogUpdatedWithCurrentUpdateProposal(); // Show update confirmation dialog
- void hideUpdateConfirmationDialog(); // Hide + destroy m_updateConfirmationDialog
- void updateConfirmationDialogWasClosed(); // Sent by the update confirmation dialog when it gets closed
+ void hideDownloadDialog(); // Hide + destroy m_updateConfirmationDialog
+ void downloadDialogWasClosed(); // Sent by the update confirmation dialog when it gets closed
// Available update (NULL if not fetched)
FvAvailableUpdate* m_proposedUpdate;
@@ -145,6 +144,11 @@ private slots:
//
void installTranslator(); // Initialize translation mechanism
+ //
+ // Download dialog
+ //
+ FvDownloadDialog *m_download_dialog;
+
};
#endif // FVUPDATER_H
diff --git a/sample/ui_fvupdateconfirmdialog.h b/sample/ui_fvupdateconfirmdialog.h
new file mode 100644
index 0000000..c0799fb
--- /dev/null
+++ b/sample/ui_fvupdateconfirmdialog.h
@@ -0,0 +1,93 @@
+/********************************************************************************
+** Form generated from reading UI file 'fvupdateconfirmdialog.ui'
+**
+** Created by: Qt User Interface Compiler version 4.8.6
+**
+** WARNING! All changes made in this file will be lost when recompiling UI file!
+********************************************************************************/
+
+#ifndef UI_FVUPDATECONFIRMDIALOG_H
+#define UI_FVUPDATECONFIRMDIALOG_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+QT_BEGIN_NAMESPACE
+
+class Ui_FvUpdateConfirmDialog
+{
+public:
+ QVBoxLayout *verticalLayout;
+ QLabel *updateFileIsLocatedLabel;
+ QLabel *updateFileLinkLabel;
+ QLabel *downloadThisUpdateLabel;
+ QLabel *whenYouClickOkLabel;
+ QDialogButtonBox *confirmButtonBox;
+
+ void setupUi(QDialog *FvUpdateConfirmDialog)
+ {
+ if (FvUpdateConfirmDialog->objectName().isEmpty())
+ FvUpdateConfirmDialog->setObjectName(QString::fromUtf8("FvUpdateConfirmDialog"));
+ FvUpdateConfirmDialog->resize(480, 160);
+ verticalLayout = new QVBoxLayout(FvUpdateConfirmDialog);
+ verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));
+ updateFileIsLocatedLabel = new QLabel(FvUpdateConfirmDialog);
+ updateFileIsLocatedLabel->setObjectName(QString::fromUtf8("updateFileIsLocatedLabel"));
+
+ verticalLayout->addWidget(updateFileIsLocatedLabel);
+
+ updateFileLinkLabel = new QLabel(FvUpdateConfirmDialog);
+ updateFileLinkLabel->setObjectName(QString::fromUtf8("updateFileLinkLabel"));
+ updateFileLinkLabel->setOpenExternalLinks(true);
+ updateFileLinkLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
+
+ verticalLayout->addWidget(updateFileLinkLabel);
+
+ downloadThisUpdateLabel = new QLabel(FvUpdateConfirmDialog);
+ downloadThisUpdateLabel->setObjectName(QString::fromUtf8("downloadThisUpdateLabel"));
+
+ verticalLayout->addWidget(downloadThisUpdateLabel);
+
+ whenYouClickOkLabel = new QLabel(FvUpdateConfirmDialog);
+ whenYouClickOkLabel->setObjectName(QString::fromUtf8("whenYouClickOkLabel"));
+
+ verticalLayout->addWidget(whenYouClickOkLabel);
+
+ confirmButtonBox = new QDialogButtonBox(FvUpdateConfirmDialog);
+ confirmButtonBox->setObjectName(QString::fromUtf8("confirmButtonBox"));
+ confirmButtonBox->setOrientation(Qt::Horizontal);
+ confirmButtonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
+
+ verticalLayout->addWidget(confirmButtonBox);
+
+
+ retranslateUi(FvUpdateConfirmDialog);
+
+ QMetaObject::connectSlotsByName(FvUpdateConfirmDialog);
+ } // setupUi
+
+ void retranslateUi(QDialog *FvUpdateConfirmDialog)
+ {
+ FvUpdateConfirmDialog->setWindowTitle(QApplication::translate("FvUpdateConfirmDialog", "Software Update", 0, QApplication::UnicodeUTF8));
+ updateFileIsLocatedLabel->setText(QApplication::translate("FvUpdateConfirmDialog", "The update file is located at:", 0, QApplication::UnicodeUTF8));
+ updateFileLinkLabel->setText(QApplication::translate("FvUpdateConfirmDialog", "%1", 0, QApplication::UnicodeUTF8));
+ downloadThisUpdateLabel->setText(QApplication::translate("FvUpdateConfirmDialog", "Download this update, close \"%1\", install it, and then reopen \"%1\".", 0, QApplication::UnicodeUTF8));
+ whenYouClickOkLabel->setText(QApplication::translate("FvUpdateConfirmDialog", "When you click \"OK\", this link will be opened in your browser.", 0, QApplication::UnicodeUTF8));
+ } // retranslateUi
+
+};
+
+namespace Ui {
+ class FvUpdateConfirmDialog: public Ui_FvUpdateConfirmDialog {};
+} // namespace Ui
+
+QT_END_NAMESPACE
+
+#endif // UI_FVUPDATECONFIRMDIALOG_H
diff --git a/update.png b/update.png
new file mode 100755
index 0000000..a4a3ef2
Binary files /dev/null and b/update.png differ