From a94daf6834f11f6694e01c2f8d5822fd7c61f338 Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Fri, 15 Jul 2016 12:00:00 +0300 Subject: [PATCH] First implementation of Update Notification. --HG-- branch : feature --- src/app/valentina/main.cpp | 8 + src/app/valentina/valentina.pro | 9 + src/libs/fervor/LICENSE | 7 + src/libs/fervor/fervor.pri | 15 + src/libs/fervor/fervor.pro | 107 +++++ src/libs/fervor/fvavailableupdate.cpp | 74 +++ src/libs/fervor/fvavailableupdate.h | 52 +++ src/libs/fervor/fvupdater.cpp | 589 ++++++++++++++++++++++++ src/libs/fervor/fvupdater.h | 128 +++++ src/libs/fervor/fvupdatewindow.cpp | 72 +++ src/libs/fervor/fvupdatewindow.h | 53 +++ src/libs/fervor/fvupdatewindow.ui | 98 ++++ src/libs/ifc/xml/vabstractconverter.cpp | 9 +- src/libs/ifc/xml/vabstractconverter.h | 5 +- src/libs/libs.pro | 3 +- src/libs/vmisc/vcommonsettings.cpp | 13 + src/libs/vmisc/vcommonsettings.h | 8 +- 17 files changed, 1241 insertions(+), 9 deletions(-) create mode 100644 src/libs/fervor/LICENSE create mode 100644 src/libs/fervor/fervor.pri create mode 100644 src/libs/fervor/fervor.pro create mode 100644 src/libs/fervor/fvavailableupdate.cpp create mode 100644 src/libs/fervor/fvavailableupdate.h create mode 100644 src/libs/fervor/fvupdater.cpp create mode 100644 src/libs/fervor/fvupdater.h create mode 100644 src/libs/fervor/fvupdatewindow.cpp create mode 100644 src/libs/fervor/fvupdatewindow.h create mode 100644 src/libs/fervor/fvupdatewindow.ui diff --git a/src/app/valentina/main.cpp b/src/app/valentina/main.cpp index bdc2b26d0..48c3fa982 100644 --- a/src/app/valentina/main.cpp +++ b/src/app/valentina/main.cpp @@ -28,6 +28,8 @@ #include "mainwindow.h" #include "core/vapplication.h" +#include "../fervor/fvupdater.h" + #include // For QT_REQUIRE_VERSION #include @@ -55,6 +57,12 @@ int main(int argc, char *argv[]) app.InitOptions(); + // Set feed URL before doing anything else + FvUpdater::sharedUpdater()->SetFeedURL("http://localhost/updateapp/Appcast.xml"); + + // Check for updates automatically + FvUpdater::sharedUpdater()->CheckForUpdatesSilent(); + MainWindow w; #if !defined(Q_OS_MAC) app.setWindowIcon(QIcon(":/icon/64x64/icon64x64.png")); diff --git a/src/app/valentina/valentina.pro b/src/app/valentina/valentina.pro index f1c0ba838..ede57e369 100644 --- a/src/app/valentina/valentina.pro +++ b/src/app/valentina/valentina.pro @@ -536,6 +536,15 @@ DEPENDPATH += $$PWD/../../libs/vgeometry win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vgeometry/$${DESTDIR}/vgeometry.lib else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vgeometry/$${DESTDIR}/libvgeometry.a +# Fervor static library (depend on VMisc, IFC) +unix|win32: LIBS += -L$$OUT_PWD/../../libs/fervor/$${DESTDIR}/ -lfervor + +INCLUDEPATH += $$PWD/../../libs/fervor +DEPENDPATH += $$PWD/../../libs/fervor + +win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/fervor/$${DESTDIR}/fervor.lib +else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/fervor/$${DESTDIR}/libfervor.a + # IFC static library (depend on QMuParser, VMisc) unix|win32: LIBS += -L$$OUT_PWD/../../libs/ifc/$${DESTDIR}/ -lifc diff --git a/src/libs/fervor/LICENSE b/src/libs/fervor/LICENSE new file mode 100644 index 000000000..d9c310de8 --- /dev/null +++ b/src/libs/fervor/LICENSE @@ -0,0 +1,7 @@ +Copyright (c) 2012 Linas Valiukas and others. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/libs/fervor/fervor.pri b/src/libs/fervor/fervor.pri new file mode 100644 index 000000000..2cac3bc15 --- /dev/null +++ b/src/libs/fervor/fervor.pri @@ -0,0 +1,15 @@ +# ADD TO EACH PATH $$PWD VARIABLE!!!!!! +# This need for corect working file translations.pro + +SOURCES += \ + $$PWD/fvupdatewindow.cpp \ + $$PWD/fvupdater.cpp \ + $$PWD/fvavailableupdate.cpp + +HEADERS += \ + $$PWD/fvupdatewindow.h \ + $$PWD/fvupdater.h \ + $$PWD/fvavailableupdate.h + +FORMS += \ + $$PWD/fvupdatewindow.ui diff --git a/src/libs/fervor/fervor.pro b/src/libs/fervor/fervor.pro new file mode 100644 index 000000000..e6a45f916 --- /dev/null +++ b/src/libs/fervor/fervor.pro @@ -0,0 +1,107 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2016-07-14T11:55:03 +# +#------------------------------------------------- + +# File with common stuff for whole project +include(../../../common.pri) + +QT += network widgets + +# Name of library +TARGET = fervor + +# We want to create a library +TEMPLATE = lib + +CONFIG += \ + staticlib \# Making static library + c++11 # We use C++11 standard + +# Use out-of-source builds (shadow builds) +CONFIG -= debug_and_release debug_and_release_target + +# Since Qt 5.4.0 the source code location is recorded only in debug builds. +# We need this information also in release builds. For this need define QT_MESSAGELOGCONTEXT. +DEFINES += QT_MESSAGELOGCONTEXT + +include(fervor.pri) + +# This is static library so no need in "make install" + +# directory for executable file +DESTDIR = bin + +# files created moc +MOC_DIR = moc + +# objecs files +OBJECTS_DIR = obj + +# Directory for files created uic +UI_DIR = uic + +# Set using ccache. Function enable_ccache() defined in common.pri. +$$enable_ccache() + +CONFIG(debug, debug|release){ + # Debug mode + unix { + #Turn on compilers warnings. + *-g++{ + QMAKE_CXXFLAGS += \ + # Key -isystem disable checking errors in system headers. + -isystem "$${OUT_PWD}/$${MOC_DIR}" \ + -isystem "$${OUT_PWD}/$${UI_DIR}" \ + $$GCC_DEBUG_CXXFLAGS # See common.pri for more details. + + noAddressSanitizer{ # For enable run qmake with CONFIG+=noAddressSanitizer + # do nothing + } else { + #gcc’s 4.8.0 Address Sanitizer + #http://blog.qt.digia.com/blog/2013/04/17/using-gccs-4-8-0-address-sanitizer-with-qt/ + QMAKE_CXXFLAGS += -fsanitize=address -fno-omit-frame-pointer + QMAKE_CFLAGS += -fsanitize=address -fno-omit-frame-pointer + QMAKE_LFLAGS += -fsanitize=address + } + } + clang*{ + QMAKE_CXXFLAGS += \ + # Key -isystem disable checking errors in system headers. + -isystem "$${OUT_PWD}/$${MOC_DIR}" \ + -isystem "$${OUT_PWD}/$${UI_DIR}" \ + $$CLANG_DEBUG_CXXFLAGS # See common.pri for more details. + } + *-icc-*{ + QMAKE_CXXFLAGS += \ + -isystem "$${OUT_PWD}/$${MOC_DIR}" \ + -isystem "$${OUT_PWD}/$${UI_DIR}" \ + $$ICC_DEBUG_CXXFLAGS + } + } else { + *-g++{ + QMAKE_CXXFLAGS += $$GCC_DEBUG_CXXFLAGS # See common.pri for more details. + } + } + +}else{ + # Release mode + !win32-msvc*:CONFIG += silent + DEFINES += V_NO_ASSERT + !unix:*-g++{ + QMAKE_CXXFLAGS += -fno-omit-frame-pointer # Need for exchndl.dll + } + + noDebugSymbols{ # For enable run qmake with CONFIG+=noDebugSymbols + # do nothing + } else { + !macx:!win32-msvc*{ + # Turn on debug symbols in release mode on Unix systems. + # On Mac OS X temporarily disabled. TODO: find way how to strip binary file. + QMAKE_CXXFLAGS_RELEASE += -g -gdwarf-3 + QMAKE_CFLAGS_RELEASE += -g -gdwarf-3 + QMAKE_LFLAGS_RELEASE = + } + } +} diff --git a/src/libs/fervor/fvavailableupdate.cpp b/src/libs/fervor/fvavailableupdate.cpp new file mode 100644 index 000000000..daf5e0997 --- /dev/null +++ b/src/libs/fervor/fvavailableupdate.cpp @@ -0,0 +1,74 @@ +/*************************************************************************************************** + ** + ** Copyright (c) 2012 Linas Valiukas and others. + ** + ** Permission is hereby granted, free of charge, to any person obtaining a copy of this + ** software and associated documentation files (the "Software"), to deal in the Software + ** without restriction, including without limitation the rights to use, copy, modify, + ** merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + ** permit persons to whom the Software is furnished to do so, subject to the following conditions: + ** + ** The above copyright notice and this permission notice shall be included in all copies or + ** substantial portions of the Software. + ** + ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + ** NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + ** DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ** + ******************************************************************************************************/ + +#include "fvavailableupdate.h" + +//--------------------------------------------------------------------------------------------------------------------- +FvAvailableUpdate::FvAvailableUpdate(QObject *parent) + : QObject(parent), + m_enclosureUrl(), + m_enclosureVersion(), + m_enclosurePlatform() +{ + // noop +} + +//--------------------------------------------------------------------------------------------------------------------- +QUrl FvAvailableUpdate::GetEnclosureUrl() const +{ + return m_enclosureUrl; +} + +//--------------------------------------------------------------------------------------------------------------------- +void FvAvailableUpdate::SetEnclosureUrl(const QUrl &enclosureUrl) +{ + m_enclosureUrl = enclosureUrl; +} + +//--------------------------------------------------------------------------------------------------------------------- +void FvAvailableUpdate::SetEnclosureUrl(const QString &enclosureUrl) +{ + SetEnclosureUrl(QUrl(enclosureUrl)); +} + +//--------------------------------------------------------------------------------------------------------------------- +QString FvAvailableUpdate::GetEnclosureVersion() const +{ + return m_enclosureVersion; +} + +//--------------------------------------------------------------------------------------------------------------------- +void FvAvailableUpdate::SetEnclosureVersion(const QString &enclosureVersion) +{ + m_enclosureVersion = enclosureVersion; +} + +//--------------------------------------------------------------------------------------------------------------------- +QString FvAvailableUpdate::GetEnclosurePlatform() const +{ + return m_enclosurePlatform; +} + +//--------------------------------------------------------------------------------------------------------------------- +void FvAvailableUpdate::SetEnclosurePlatform(const QString &enclosurePlatform) +{ + m_enclosurePlatform = enclosurePlatform; +} diff --git a/src/libs/fervor/fvavailableupdate.h b/src/libs/fervor/fvavailableupdate.h new file mode 100644 index 000000000..7192df064 --- /dev/null +++ b/src/libs/fervor/fvavailableupdate.h @@ -0,0 +1,52 @@ +/*************************************************************************************************** + ** + ** Copyright (c) 2012 Linas Valiukas and others. + ** + ** Permission is hereby granted, free of charge, to any person obtaining a copy of this + ** software and associated documentation files (the "Software"), to deal in the Software + ** without restriction, including without limitation the rights to use, copy, modify, + ** merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + ** permit persons to whom the Software is furnished to do so, subject to the following conditions: + ** + ** The above copyright notice and this permission notice shall be included in all copies or + ** substantial portions of the Software. + ** + ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + ** NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + ** DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ** + ******************************************************************************************************/ + +#ifndef FVAVAILABLEUPDATE_H +#define FVAVAILABLEUPDATE_H + +#include +#include + +class FvAvailableUpdate : public QObject +{ + Q_OBJECT +public: + explicit FvAvailableUpdate(QObject *parent = nullptr); + + QUrl GetEnclosureUrl() const; + void SetEnclosureUrl(const QUrl &enclosureUrl); + void SetEnclosureUrl(const QString &enclosureUrl); + + QString GetEnclosureVersion() const; + void SetEnclosureVersion(const QString &enclosureVersion); + + QString GetEnclosurePlatform() const; + void SetEnclosurePlatform(const QString &enclosurePlatform); + +private: + Q_DISABLE_COPY(FvAvailableUpdate) + + QUrl m_enclosureUrl; + QString m_enclosureVersion; + QString m_enclosurePlatform; +}; + +#endif // FVAVAILABLEUPDATE_H diff --git a/src/libs/fervor/fvupdater.cpp b/src/libs/fervor/fvupdater.cpp new file mode 100644 index 000000000..fd41e71e1 --- /dev/null +++ b/src/libs/fervor/fvupdater.cpp @@ -0,0 +1,589 @@ +/*************************************************************************************************** + ** + ** Copyright (c) 2012 Linas Valiukas and others. + ** + ** Permission is hereby granted, free of charge, to any person obtaining a copy of this + ** software and associated documentation files (the "Software"), to deal in the Software + ** without restriction, including without limitation the rights to use, copy, modify, + ** merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + ** permit persons to whom the Software is furnished to do so, subject to the following conditions: + ** + ** The above copyright notice and this permission notice shall be included in all copies or + ** substantial portions of the Software. + ** + ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + ** NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + ** DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ** + ******************************************************************************************************/ + +#include "fvupdater.h" +#include "../vmisc/projectversion.h" +#include "../vmisc/vabstractapplication.h" +#include "../ifc/xml/vabstractconverter.h" +#include "../ifc/exception/vexception.h" + +#include +#include +#include +#include + +QPointer FvUpdater::m_Instance; + +//--------------------------------------------------------------------------------------------------------------------- +FvUpdater* FvUpdater::sharedUpdater() +{ + static QMutex mutex; + if (m_Instance.isNull()) + { + mutex.lock(); + + if (m_Instance.isNull()) + { + m_Instance = new FvUpdater; + } + + mutex.unlock(); + } + + return m_Instance.data(); +} + +//--------------------------------------------------------------------------------------------------------------------- +void FvUpdater::drop() +{ + static QMutex mutex; + mutex.lock(); + delete m_Instance; + mutex.unlock(); +} + +//--------------------------------------------------------------------------------------------------------------------- +FvUpdater::FvUpdater() + : QObject(nullptr), + m_updaterWindow(nullptr), + m_proposedUpdate(nullptr), + m_silentAsMuchAsItCouldGet(true), + m_feedURL(), + m_qnam(), + m_reply(nullptr), + m_httpRequestAborted(false), + m_xml() +{ + // noop +} + +//--------------------------------------------------------------------------------------------------------------------- +FvUpdater::~FvUpdater() +{ + hideUpdaterWindow(); +} + +//--------------------------------------------------------------------------------------------------------------------- +void FvUpdater::showUpdaterWindowUpdatedWithCurrentUpdateProposal() +{ + // Destroy window if already exists + hideUpdaterWindow(); + + // Create a new window + m_updaterWindow = new FvUpdateWindow(qApp->getMainWindow()); + m_updaterWindow->UpdateWindowWithCurrentProposedUpdate(); + m_updaterWindow->show(); +} + +//--------------------------------------------------------------------------------------------------------------------- +void FvUpdater::hideUpdaterWindow() +{ + if (m_updaterWindow) + { + m_updaterWindow->close(); + } +} + +//--------------------------------------------------------------------------------------------------------------------- +void FvUpdater::SetFeedURL(const QUrl &feedURL) +{ + m_feedURL = feedURL; +} + +//--------------------------------------------------------------------------------------------------------------------- +void FvUpdater::SetFeedURL(const QString &feedURL) +{ + SetFeedURL(QUrl(feedURL)); +} + +//--------------------------------------------------------------------------------------------------------------------- +QString FvUpdater::GetFeedURL() const +{ + return m_feedURL.toString(); +} + +//--------------------------------------------------------------------------------------------------------------------- +QPointer FvUpdater::GetProposedUpdate() +{ + return m_proposedUpdate; +} + +//--------------------------------------------------------------------------------------------------------------------- +void FvUpdater::InstallUpdate() +{ + qDebug() << "Install update"; + + UpdateInstallationConfirmed(); +} + +//--------------------------------------------------------------------------------------------------------------------- +void FvUpdater::SkipUpdate() +{ + qDebug() << "Skip update"; + + FvAvailableUpdate* proposedUpdate = GetProposedUpdate(); + if (not proposedUpdate) + { + qWarning() << "Proposed update is NULL (shouldn't be at this point)"; + return; + } + + // Start ignoring this particular version + IgnoreVersion(proposedUpdate->GetEnclosureVersion()); + + hideUpdaterWindow(); +} + +//--------------------------------------------------------------------------------------------------------------------- +void FvUpdater::RemindMeLater() +{ + qDebug() << "Remind me later"; + + hideUpdaterWindow(); +} + +//--------------------------------------------------------------------------------------------------------------------- +void FvUpdater::UpdateInstallationConfirmed() +{ + qDebug() << "Confirm update installation"; + + QPointer proposedUpdate = GetProposedUpdate(); + if (proposedUpdate.isNull()) + { + qWarning() << "Proposed update is NULL (shouldn't be at this point)"; + return; + } + + // Open a link + if (not QDesktopServices::openUrl(proposedUpdate->GetEnclosureUrl())) + { + showErrorDialog(tr("Unable to open a browser."), true); + return; + } + + hideUpdaterWindow(); +} + +//--------------------------------------------------------------------------------------------------------------------- +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()) + { + qCritical() << "QApplication::applicationName is not set. Please do that."; + return false; + } + + if (QApplication::applicationVersion().isEmpty()) + { + qCritical() << "QApplication::applicationVersion is not set. Please do that."; + return false; + } + + cancelDownloadFeed(); + m_httpRequestAborted = false; + startDownloadFeed(m_feedURL); + + return true; +} + +//--------------------------------------------------------------------------------------------------------------------- +bool FvUpdater::CheckForUpdatesSilent() +{ + return CheckForUpdates(true); +} + +//--------------------------------------------------------------------------------------------------------------------- +bool FvUpdater::CheckForUpdatesNotSilent() +{ + return CheckForUpdates(false); +} + +//--------------------------------------------------------------------------------------------------------------------- +void FvUpdater::startDownloadFeed(const QUrl &url) +{ + m_xml.clear(); + + QNetworkRequest request; + request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/xml")); + request.setHeader(QNetworkRequest::UserAgentHeader, QApplication::applicationName()); + request.setUrl(url); + + m_reply = m_qnam.get(request); + + connect(m_reply, &QNetworkReply::readyRead, this, &FvUpdater::httpFeedReadyRead); + connect(m_reply, &QNetworkReply::downloadProgress, this, &FvUpdater::httpFeedUpdateDataReadProgress); + connect(m_reply, &QNetworkReply::finished, this, &FvUpdater::httpFeedDownloadFinished); +} + +//--------------------------------------------------------------------------------------------------------------------- +void FvUpdater::cancelDownloadFeed() +{ + 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()); +} + +//--------------------------------------------------------------------------------------------------------------------- +void FvUpdater::httpFeedUpdateDataReadProgress(qint64 bytesRead, qint64 totalBytes) +{ + Q_UNUSED(bytesRead); + Q_UNUSED(totalBytes); + + if (m_httpRequestAborted) + { + return; + } +} + +//--------------------------------------------------------------------------------------------------------------------- +void FvUpdater::httpFeedDownloadFinished() +{ + if (m_httpRequestAborted) + { + m_reply->deleteLater(); + return; + } + + const QVariant redirectionTarget = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (m_reply->error()) + { + // Error. + showErrorDialog(tr("Feed download failed: %1.").arg(m_reply->errorString()), false); + } + else if (not redirectionTarget.isNull()) + { + const QUrl newUrl = m_feedURL.resolved(redirectionTarget.toUrl()); + + m_feedURL = newUrl; + m_reply->deleteLater(); + + startDownloadFeed(m_feedURL); + return; + } + else + { + // Done. + xmlParseFeed(); + } + + m_reply->deleteLater(); + m_reply = 0; +} + +//--------------------------------------------------------------------------------------------------------------------- +bool FvUpdater::xmlParseFeed() +{ + QString xmlEnclosureUrl, xmlEnclosureVersion, xmlEnclosurePlatform; + + // Parse + while (not m_xml.atEnd()) + { + m_xml.readNext(); + + if (m_xml.isStartElement()) + { + if (m_xml.name() == QLatin1Literal("item")) + { + xmlEnclosureUrl.clear(); + xmlEnclosureVersion.clear(); + xmlEnclosurePlatform.clear(); + } + else if (m_xml.name() == QLatin1Literal("enclosure")) + { + const QXmlStreamAttributes attribs = m_xml.attributes(); + const QString fervorPlatform = QStringLiteral("fervor:platform"); + + if (attribs.hasAttribute(fervorPlatform)) + { + if (CurrentlyRunningOnPlatform(attribs.value(fervorPlatform).toString().trimmed())) + { + xmlEnclosurePlatform = attribs.value(fervorPlatform).toString().trimmed(); + + const QString attributeUrl = QStringLiteral("url"); + if (attribs.hasAttribute(attributeUrl)) + { + xmlEnclosureUrl = attribs.value(attributeUrl).toString().trimmed(); + } + else + { + xmlEnclosureUrl.clear(); + } + + const QString fervorVersion = QStringLiteral("fervor:version"); + if (attribs.hasAttribute(fervorVersion)) + { + const QString candidateVersion = attribs.value(fervorVersion).toString().trimmed(); + if (not candidateVersion.isEmpty()) + { + xmlEnclosureVersion = candidateVersion; + } + } + } + } + } + } + else if (m_xml.isEndElement()) + { + if (m_xml.name() == QLatin1Literal("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. + + return searchDownloadedFeedForUpdates(xmlEnclosureUrl, + xmlEnclosureVersion, + xmlEnclosurePlatform); + } + } + + 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; + + } + } + + // No updates were found if we're at this point + // (not a single element found) + showInformationDialog(tr("No updates were found."), false); + + return false; +} + +//--------------------------------------------------------------------------------------------------------------------- +bool FvUpdater::searchDownloadedFeedForUpdates(const QString &xmlEnclosureUrl, + const QString &xmlEnclosureVersion, + const QString &xmlEnclosurePlatform) +{ + qDebug() << "Enclosure URL:" << xmlEnclosureUrl; + qDebug() << "Enclosure version:" << xmlEnclosureVersion; + qDebug() << "Enclosure platform:" << xmlEnclosurePlatform; + + // Validate + if (xmlEnclosureUrl.isEmpty() || xmlEnclosureVersion.isEmpty() || xmlEnclosurePlatform.isEmpty()) + { + showErrorDialog(tr("Feed error: invalid \"enclosure\" with the download link"), false); + return false; + } + + // Relevant version? + if (VersionIsIgnored(xmlEnclosureVersion)) + { + qDebug() << "Version '" << xmlEnclosureVersion << "' is ignored, too old or something like that."; + + showInformationDialog(tr("No updates were found."), false); + + 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. + // + + if (m_proposedUpdate) + { + delete m_proposedUpdate; + } + m_proposedUpdate = new FvAvailableUpdate(this); + m_proposedUpdate->SetEnclosureUrl(xmlEnclosureUrl); + m_proposedUpdate->SetEnclosureVersion(xmlEnclosureVersion); + m_proposedUpdate->SetEnclosurePlatform(xmlEnclosurePlatform); + + // Show "look, there's an update" window + showUpdaterWindowUpdatedWithCurrentUpdateProposal(); + + return true; +} + +//--------------------------------------------------------------------------------------------------------------------- +bool FvUpdater::VersionIsIgnored(const QString &version) +{ + // We assume that variable 'version' contains either: + // 1) The current version of the application (ignore) + // 2) The version that was skipped before and thus stored in QSettings (ignore) + // 3) A newer version (don't ignore) + // 'version' is not likely to contain an older version in any case. + + int decVersion = 0x0; + try + { + decVersion = VAbstractConverter::GetVersion(version); + } + catch (const VException &e) + { + Q_UNUSED(e) + return true; // Ignore invalid version + } + + if (decVersion == APP_VERSION) + { + return true; + } + + const int lastSkippedVersion = qApp->Settings()->GetLatestSkippedVersion(); + if (lastSkippedVersion != 0x0) + { + if (decVersion == lastSkippedVersion) + { + // Implicitly skipped version - skip + return true; + } + } + + if (decVersion > APP_VERSION) + { + // Newer version - do not skip + return false; + } + + // Fallback - skip + return true; +} + +//--------------------------------------------------------------------------------------------------------------------- +void FvUpdater::IgnoreVersion(const QString &version) +{ + int decVersion = 0x0; + try + { + decVersion = VAbstractConverter::GetVersion(version); + } + catch (const VException &e) + { + Q_UNUSED(e) + return ; // Ignore invalid version + } + + if (decVersion == APP_VERSION) + { + // Don't ignore the current version + return; + } + + qApp->Settings()->SetLatestSkippedVersion(decVersion); +} + +//--------------------------------------------------------------------------------------------------------------------- +bool FvUpdater::CurrentlyRunningOnPlatform(const QString &platform) +{ + const QStringList platforms = QStringList() << "Q_OS_LINUX" + << "Q_OS_MAC" + << "Q_OS_WIN32"; + + switch (platforms.indexOf(platform.toUpper().trimmed())) + { + case 0: // Q_OS_LINUX +#ifdef Q_OS_LINUX // Defined on Linux. + return true; +#endif + break; + case 1: // Q_OS_MAC +#ifdef Q_OS_MAC // Defined on MAC OS (synonym for Darwin). + return true; +#endif + break; + case 2: // Q_OS_WIN32 +#ifdef Q_OS_WIN32 // Defined on all supported versions of Windows. + return true; +#endif + break; + default: + break; + } + + // Fallback + return false; +} + +//--------------------------------------------------------------------------------------------------------------------- +void FvUpdater::showErrorDialog(const QString &message, bool showEvenInSilentMode) +{ + if (m_silentAsMuchAsItCouldGet) + { + if (not 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(); +} + +//--------------------------------------------------------------------------------------------------------------------- +void FvUpdater::showInformationDialog(const QString &message, bool showEvenInSilentMode) +{ + if (m_silentAsMuchAsItCouldGet) + { + if (not 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(); +} diff --git a/src/libs/fervor/fvupdater.h b/src/libs/fervor/fvupdater.h new file mode 100644 index 000000000..0191ae589 --- /dev/null +++ b/src/libs/fervor/fvupdater.h @@ -0,0 +1,128 @@ +/*************************************************************************************************** + ** + ** Copyright (c) 2012 Linas Valiukas and others. + ** + ** Permission is hereby granted, free of charge, to any person obtaining a copy of this + ** software and associated documentation files (the "Software"), to deal in the Software + ** without restriction, including without limitation the rights to use, copy, modify, + ** merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + ** permit persons to whom the Software is furnished to do so, subject to the following conditions: + ** + ** The above copyright notice and this permission notice shall be included in all copies or + ** substantial portions of the Software. + ** + ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + ** NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + ** DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ** + ******************************************************************************************************/ + +#ifndef FVUPDATER_H +#define FVUPDATER_H + +#include +#include +#include +#include +#include +#include + +#include "fvupdatewindow.h" +#include "fvavailableupdate.h" + +class FvUpdater : public QObject +{ + Q_OBJECT + +public: + // Singleton + static FvUpdater* sharedUpdater(); + static void drop(); + + // Set / get feed URL + void SetFeedURL(const QUrl &feedURL); + void SetFeedURL(const QString &feedURL); + QString GetFeedURL() const; + +public slots: + // Check for updates + bool CheckForUpdates(bool silentAsMuchAsItCouldGet = true); + + // Aliases + bool CheckForUpdatesSilent(); + bool CheckForUpdatesNotSilent(); + +protected: + friend class FvUpdateWindow; // Uses GetProposedUpdate() and others + QPointer GetProposedUpdate(); + +protected slots: + // Update window button slots + void InstallUpdate(); + void SkipUpdate(); + void RemindMeLater(); + + // Update confirmation dialog button slots + void UpdateInstallationConfirmed(); + +private slots: + void httpFeedReadyRead(); + void httpFeedUpdateDataReadProgress(qint64 bytesRead, qint64 totalBytes); + void httpFeedDownloadFinished(); + +private: + // + // Singleton business + // + Q_DISABLE_COPY(FvUpdater) + FvUpdater(); // Hide main constructor + virtual ~FvUpdater(); // Hide main destructor + + static QPointer m_Instance; // Singleton instance + + QPointer m_updaterWindow; // Updater window (NULL if not shown) + + // Available update (NULL if not fetched) + QPointer m_proposedUpdate; + + // If true, don't show the error dialogs and the "no updates." dialog + // (silentAsMuchAsItCouldGet from CheckForUpdates() goes here) + // Useful for automatic update checking upon application startup. + bool m_silentAsMuchAsItCouldGet; + + // + // HTTP feed fetcher infrastructure + // + QUrl m_feedURL; // Feed URL that will be fetched + QNetworkAccessManager m_qnam; + QNetworkReply* m_reply; + bool m_httpRequestAborted; + + QXmlStreamReader m_xml; // XML data collector and parser + + void showUpdaterWindowUpdatedWithCurrentUpdateProposal(); // Show updater window + void hideUpdaterWindow(); // Hide + destroy m_updaterWindow + + void startDownloadFeed(const QUrl &url); // Start downloading feed + void cancelDownloadFeed(); // Stop downloading the current feed + + // Dialogs (notifications) + // Show an error message + void showErrorDialog(const QString &message, bool showEvenInSilentMode = false); + // Show an informational message + void showInformationDialog(const QString &message, bool showEvenInSilentMode = false); + + + bool xmlParseFeed(); // Parse feed in m_xml + bool searchDownloadedFeedForUpdates(const QString &xmlEnclosureUrl, + const QString &xmlEnclosureVersion, + const QString &xmlEnclosurePlatform); + + static bool VersionIsIgnored(const QString &version); + static void IgnoreVersion(const QString &version); + static bool CurrentlyRunningOnPlatform(const QString &platform); +}; + +#endif // FVUPDATER_H diff --git a/src/libs/fervor/fvupdatewindow.cpp b/src/libs/fervor/fvupdatewindow.cpp new file mode 100644 index 000000000..16525e05e --- /dev/null +++ b/src/libs/fervor/fvupdatewindow.cpp @@ -0,0 +1,72 @@ +/*************************************************************************************************** + ** + ** Copyright (c) 2012 Linas Valiukas and others. + ** + ** Permission is hereby granted, free of charge, to any person obtaining a copy of this + ** software and associated documentation files (the "Software"), to deal in the Software + ** without restriction, including without limitation the rights to use, copy, modify, + ** merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + ** permit persons to whom the Software is furnished to do so, subject to the following conditions: + ** + ** The above copyright notice and this permission notice shall be included in all copies or + ** substantial portions of the Software. + ** + ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + ** NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + ** DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ** + ******************************************************************************************************/ + +#include "fvupdatewindow.h" +#include "ui_fvupdatewindow.h" +#include "fvupdater.h" +#include "fvavailableupdate.h" + +#include +#include +#include + +//--------------------------------------------------------------------------------------------------------------------- +FvUpdateWindow::FvUpdateWindow(QWidget *parent) + : QDialog(parent), + m_ui(new Ui::FvUpdateWindow), + m_appIconScene(nullptr) +{ + m_ui->setupUi(this); + + // Delete on close + setAttribute(Qt::WA_DeleteOnClose, true); + + // Set the "new version is available" string + const QString newVersString = m_ui->newVersionIsAvailableLabel->text().arg(qApp->applicationDisplayName()); + m_ui->newVersionIsAvailableLabel->setText(newVersString); + + // Connect buttons + connect(m_ui->installUpdateButton, &QPushButton::clicked, FvUpdater::sharedUpdater(), &FvUpdater::InstallUpdate); + connect(m_ui->skipThisVersionButton, &QPushButton::clicked, FvUpdater::sharedUpdater(), &FvUpdater::SkipUpdate); + connect(m_ui->remindMeLaterButton, &QPushButton::clicked, FvUpdater::sharedUpdater(), &FvUpdater::RemindMeLater); +} + +//--------------------------------------------------------------------------------------------------------------------- +FvUpdateWindow::~FvUpdateWindow() +{ + delete m_ui; +} + +//--------------------------------------------------------------------------------------------------------------------- +bool FvUpdateWindow::UpdateWindowWithCurrentProposedUpdate() +{ + QPointer proposedUpdate = FvUpdater::sharedUpdater()->GetProposedUpdate(); + if (proposedUpdate.isNull()) + { + return false; + } + + const QString downloadString = m_ui->wouldYouLikeToDownloadLabel->text() + .arg(qApp->applicationDisplayName(), proposedUpdate->GetEnclosureVersion(), qApp->applicationVersion()); + m_ui->wouldYouLikeToDownloadLabel->setText(downloadString); + + return true; +} diff --git a/src/libs/fervor/fvupdatewindow.h b/src/libs/fervor/fvupdatewindow.h new file mode 100644 index 000000000..37cbacd4c --- /dev/null +++ b/src/libs/fervor/fvupdatewindow.h @@ -0,0 +1,53 @@ +/*************************************************************************************************** + ** + ** Copyright (c) 2012 Linas Valiukas and others. + ** + ** Permission is hereby granted, free of charge, to any person obtaining a copy of this + ** software and associated documentation files (the "Software"), to deal in the Software + ** without restriction, including without limitation the rights to use, copy, modify, + ** merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + ** permit persons to whom the Software is furnished to do so, subject to the following conditions: + ** + ** The above copyright notice and this permission notice shall be included in all copies or + ** substantial portions of the Software. + ** + ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + ** NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + ** DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ** + ******************************************************************************************************/ + +#ifndef FVUPDATEWINDOW_H +#define FVUPDATEWINDOW_H + +#include + +class QGraphicsScene; + +namespace Ui +{ + class FvUpdateWindow; +} + +class FvUpdateWindow : public QDialog +{ + Q_OBJECT + +public: + explicit FvUpdateWindow(QWidget *parent = nullptr); + virtual ~FvUpdateWindow(); + + // Update the current update proposal from FvUpdater + bool UpdateWindowWithCurrentProposedUpdate(); + +private: + Q_DISABLE_COPY(FvUpdateWindow) + + Ui::FvUpdateWindow* m_ui; + QGraphicsScene* m_appIconScene; + +}; + +#endif // FVUPDATEWINDOW_H diff --git a/src/libs/fervor/fvupdatewindow.ui b/src/libs/fervor/fvupdatewindow.ui new file mode 100644 index 000000000..da38a53f5 --- /dev/null +++ b/src/libs/fervor/fvupdatewindow.ui @@ -0,0 +1,98 @@ + + + FvUpdateWindow + + + + 0 + 0 + 520 + 95 + + + + Software Update + + + + :/icon/64x64/icon64x64.png:/icon/64x64/icon64x64.png + + + + + + + + + 75 + true + + + + A new version of %1 is available! + + + + + + + %1 %2 is now available - you have %3. Would you like to download it now? + + + + + + + + + Skip This Version + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Remind Me Later + + + + + + + + + + Get Update + + + true + + + true + + + + + + + + + + + + + + diff --git a/src/libs/ifc/xml/vabstractconverter.cpp b/src/libs/ifc/xml/vabstractconverter.cpp index 2c89eda77..079da3610 100644 --- a/src/libs/ifc/xml/vabstractconverter.cpp +++ b/src/libs/ifc/xml/vabstractconverter.cpp @@ -33,6 +33,7 @@ #include #include #include +#include //--------------------------------------------------------------------------------------------------------------------- VAbstractConverter::VAbstractConverter(const QString &fileName) @@ -107,7 +108,7 @@ QString VAbstractConverter::GetVersionStr() const } //--------------------------------------------------------------------------------------------------------------------- -int VAbstractConverter::GetVersion(const QString &version) const +int VAbstractConverter::GetVersion(const QString &version) { ValidateVersion(version); @@ -138,11 +139,11 @@ int VAbstractConverter::GetVersion(const QString &version) const } //--------------------------------------------------------------------------------------------------------------------- -void VAbstractConverter::ValidateVersion(const QString &version) const +void VAbstractConverter::ValidateVersion(const QString &version) { - const QRegExp rx(QStringLiteral("^(0|([1-9][0-9]*)).(0|([1-9][0-9]*)).(0|([1-9][0-9]*))$")); + const QRegularExpression rx(QStringLiteral("^(0|([1-9][0-9]*)).(0|([1-9][0-9]*)).(0|([1-9][0-9]*))$")); - if (rx.exactMatch(version) == false) + if (rx.match(version).hasMatch() == false) { const QString errorMsg(tr("Version \"%1\" invalid.").arg(version)); throw VException(errorMsg); diff --git a/src/libs/ifc/xml/vabstractconverter.h b/src/libs/ifc/xml/vabstractconverter.h index 5a3060664..6a7a28992 100644 --- a/src/libs/ifc/xml/vabstractconverter.h +++ b/src/libs/ifc/xml/vabstractconverter.h @@ -43,12 +43,13 @@ public: void Convert(); virtual bool SaveDocument(const QString &fileName, QString &error) const Q_DECL_OVERRIDE; + static int GetVersion(const QString &version); + protected: int ver; QString fileName; void ValidateInputFile(const QString ¤tSchema) const; - int GetVersion(const QString &version) const; Q_NORETURN void InvalidVersion(int ver) const; void Save() const; void SetVersion(const QString &version); @@ -72,7 +73,7 @@ private: QString GetVersionStr() const; - void ValidateVersion(const QString &version) const; + static void ValidateVersion(const QString &version); void ReserveFile() const; }; diff --git a/src/libs/libs.pro b/src/libs/libs.pro index af365e150..334d77b84 100644 --- a/src/libs/libs.pro +++ b/src/libs/libs.pro @@ -11,4 +11,5 @@ SUBDIRS = \ vmisc \ vwidgets \ vtools \ - vformat + vformat \ + fervor diff --git a/src/libs/vmisc/vcommonsettings.cpp b/src/libs/vmisc/vcommonsettings.cpp index 82daa3b75..5c8686b78 100644 --- a/src/libs/vmisc/vcommonsettings.cpp +++ b/src/libs/vmisc/vcommonsettings.cpp @@ -65,6 +65,7 @@ const QString VCommonSettings::SettingGeneralGeometry = QString const QString VCommonSettings::SettingGeneralWindowState = QStringLiteral("windowState"); const QString VCommonSettings::SettingGeneralToolbarsState = QStringLiteral("toolbarsState"); const QString VCommonSettings::SettingPreferenceDialogSize = QStringLiteral("preferenceDialogSize"); +const QString VCommonSettings::SettingLatestSkippedVersion = QStringLiteral("lastestSkippedVersion"); static const QString commonIniFilename = QStringLiteral("common"); @@ -429,3 +430,15 @@ void VCommonSettings::SetPreferenceDialogSize(const QSize& sz) { setValue(SettingPreferenceDialogSize, sz); } + +//--------------------------------------------------------------------------------------------------------------------- +int VCommonSettings::GetLatestSkippedVersion() const +{ + return value(SettingLatestSkippedVersion, 0x0).toInt(); +} + +//--------------------------------------------------------------------------------------------------------------------- +void VCommonSettings::SetLatestSkippedVersion(int value) +{ + setValue(SettingLatestSkippedVersion, value); +} diff --git a/src/libs/vmisc/vcommonsettings.h b/src/libs/vmisc/vcommonsettings.h index d8e516282..42d954348 100644 --- a/src/libs/vmisc/vcommonsettings.h +++ b/src/libs/vmisc/vcommonsettings.h @@ -99,8 +99,11 @@ public: QByteArray GetToolbarsState() const; void SetToolbarsState(const QByteArray &value); - QSize GetPreferenceDialogSize() const; - void SetPreferenceDialogSize(const QSize& sz); + QSize GetPreferenceDialogSize() const; + void SetPreferenceDialogSize(const QSize& sz); + + int GetLatestSkippedVersion() const; + void SetLatestSkippedVersion(int value); private: Q_DISABLE_COPY(VCommonSettings) @@ -128,6 +131,7 @@ private: static const QString SettingGeneralWindowState; static const QString SettingGeneralToolbarsState; static const QString SettingPreferenceDialogSize; + static const QString SettingLatestSkippedVersion; }; #endif // VCOMMONSETTINGS_H