From 97c87cf3e9b5e150152898e7907577c3ca3fdd86 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Fri, 11 Nov 2016 12:28:53 +0100 Subject: Add update channel configuration dialog to the about window If software updates are enabled the about dialog will show the currently configured update channel and provides a link to a dialog to change the update channel. Test-Information: Builds on macOS 10.12.1, unit tests pass, and dialogs behave as expected. Did not test Sparkle updating. Change-Id: I05d5014f0d719ba9b2146c1e599db4f7fde80558 diff --git a/SwifTools/AutoUpdater/AutoUpdater.h b/SwifTools/AutoUpdater/AutoUpdater.h index ed53e11..274bf50 100644 --- a/SwifTools/AutoUpdater/AutoUpdater.h +++ b/SwifTools/AutoUpdater/AutoUpdater.h @@ -6,6 +6,8 @@ #pragma once +#include + #include namespace Swift { @@ -13,6 +15,7 @@ namespace Swift { public: virtual ~AutoUpdater(); + virtual void setAppcastFeed(const std::string& appcastFeed) = 0; virtual void checkForUpdates() = 0; virtual bool recommendRestartToUpdate() = 0; diff --git a/SwifTools/AutoUpdater/SparkleAutoUpdater.h b/SwifTools/AutoUpdater/SparkleAutoUpdater.h index 41a25f0..dd22e73 100644 --- a/SwifTools/AutoUpdater/SparkleAutoUpdater.h +++ b/SwifTools/AutoUpdater/SparkleAutoUpdater.h @@ -20,9 +20,10 @@ namespace Swift { */ class SparkleAutoUpdater : public AutoUpdater { public: - SparkleAutoUpdater(const std::string& url); + SparkleAutoUpdater(const std::string& appcastFeed); ~SparkleAutoUpdater(); + void setAppcastFeed(const std::string& appcastFeed); void checkForUpdates(); bool recommendRestartToUpdate(); diff --git a/SwifTools/AutoUpdater/SparkleAutoUpdater.mm b/SwifTools/AutoUpdater/SparkleAutoUpdater.mm index 6b27ba7..ed5f094 100644 --- a/SwifTools/AutoUpdater/SparkleAutoUpdater.mm +++ b/SwifTools/AutoUpdater/SparkleAutoUpdater.mm @@ -21,7 +21,7 @@ class SparkleAutoUpdater::Private { bool restartToUpdate = false; }; -SparkleAutoUpdater::SparkleAutoUpdater(const std::string& url) : d(new Private()) { +SparkleAutoUpdater::SparkleAutoUpdater(const std::string& appcastFeed) : d(new Private()) { d->updater = [SUUpdater sharedUpdater]; [d->updater retain]; @@ -37,7 +37,7 @@ SparkleAutoUpdater::SparkleAutoUpdater(const std::string& url) : d(new Private() [d->updater setUpdateCheckInterval: 86400]; [d->updater setAutomaticallyDownloadsUpdates: true]; - NSURL* nsurl = [NSURL URLWithString: std2NSString(url)]; + NSURL* nsurl = [NSURL URLWithString: std2NSString(appcastFeed)]; [d->updater setFeedURL: nsurl]; } @@ -45,6 +45,11 @@ SparkleAutoUpdater::~SparkleAutoUpdater() { [d->updater release]; } +void SparkleAutoUpdater::setAppcastFeed(const std::string& appcastFeed) { + NSURL* nsurl = [NSURL URLWithString: std2NSString(appcastFeed)]; + [d->updater setFeedURL: nsurl]; +} + void SparkleAutoUpdater::checkForUpdates() { //[d->updater resetUpdateCycle]; // This is useful for testing to force a check ot start. [d->updater checkForUpdatesInBackground]; diff --git a/Swift/QtUI/QtAboutWidget.cpp b/Swift/QtUI/QtAboutWidget.cpp index d90e35a..9047525 100644 --- a/Swift/QtUI/QtAboutWidget.cpp +++ b/Swift/QtUI/QtAboutWidget.cpp @@ -20,10 +20,13 @@ #include #include +#include +#include +#include namespace Swift { -QtAboutWidget::QtAboutWidget() : QDialog() { +QtAboutWidget::QtAboutWidget(SettingsProvider* settingsProvider) : QDialog(), settingsProvider_(settingsProvider) { #ifndef Q_OS_MAC setWindowTitle(QString(tr("About %1")).arg("Swift")); #endif @@ -46,6 +49,19 @@ QtAboutWidget::QtAboutWidget() : QDialog() { versionLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); mainLayout->addWidget(versionLabel); + settingsChangedConnection_ = settingsProvider_->onSettingChanged.connect([&](const std::string& path) { + if (path == QtUISettingConstants::SOFTWARE_UPDATE_CHANNEL.getKey() || path == QtUISettingConstants::ENABLE_SOFTWARE_UPDATES.getKey()) { + updateUpdateInfoLabel(); + } + }); + + updateInfoLabel_ = new QLabel("", this); + updateInfoLabel_->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard | Qt::LinksAccessibleByMouse); + connect(updateInfoLabel_, SIGNAL(linkActivated(const QString &)), this, SLOT(handleChangeUpdateChannelClicked())); + mainLayout->addWidget(updateInfoLabel_); + + updateUpdateInfoLabel(); + if (QCoreApplication::translate("TRANSLATION_INFO", "TRANSLATION_AUTHOR") != "TRANSLATION_AUTHOR") { mainLayout->addWidget(new QLabel(QString("
") + QString(tr("Using the English translation by\n%1")).arg(QCoreApplication::translate("TRANSLATION_INFO", "TRANSLATION_AUTHOR")).replace("\n", "
") + "
", this)); } @@ -89,6 +105,11 @@ void QtAboutWidget::handleChangelogClicked() { openPlainTextWindow(":/ChangeLog.md"); } +void QtAboutWidget::handleChangeUpdateChannelClicked() { + auto feedSelectionDialog = new QtUpdateFeedSelectionDialog(settingsProvider_); + feedSelectionDialog->show(); +} + void QtAboutWidget::openPlainTextWindow(const QString& path) { QTextEdit* text = new QTextEdit(); text->setAttribute(Qt::WA_DeleteOnClose); @@ -108,4 +129,35 @@ void QtAboutWidget::openPlainTextWindow(const QString& path) { } } +void QtAboutWidget::updateUpdateInfoLabel() { + if (settingsProvider_->getSetting(QtUISettingConstants::ENABLE_SOFTWARE_UPDATES)) { + if (!settingsProvider_->getSetting(QtUISettingConstants::SOFTWARE_UPDATE_CHANNEL).empty()) { + QString updateFeedDescription; + auto addUpdateFeedDialogLink = false; + if (settingsProvider_->getSetting(QtUISettingConstants::SOFTWARE_UPDATE_CHANNEL) == UpdateFeeds::StableChannel) { + updateFeedDescription = tr("You are receiving updates from the Stable update channel."); + addUpdateFeedDialogLink = true; + } + else if (settingsProvider_->getSetting(QtUISettingConstants::SOFTWARE_UPDATE_CHANNEL) == UpdateFeeds::DevelopmentChannel) { + updateFeedDescription = tr("You are receiving updates from the Development update channel."); + addUpdateFeedDialogLink = true; + } + else if (settingsProvider_->getSetting(QtUISettingConstants::SOFTWARE_UPDATE_CHANNEL) == UpdateFeeds::TestingChannel) { + updateFeedDescription = tr("You are receiving updates from the Testing update channel."); + addUpdateFeedDialogLink = true; + } + auto updateFeedDialogLink = QString( addUpdateFeedDialogLink ? "%1" : "" ).arg(tr("Change the update channel.")); + updateInfoLabel_->setText(QString("
%1
%2
").arg(updateFeedDescription, updateFeedDialogLink)); + updateInfoLabel_->show(); + } + else { + updateInfoLabel_->hide(); + } + } + else { + updateInfoLabel_->hide(); + } + +} + } diff --git a/Swift/QtUI/QtAboutWidget.h b/Swift/QtUI/QtAboutWidget.h index c8b0356..fb54c6e 100644 --- a/Swift/QtUI/QtAboutWidget.h +++ b/Swift/QtUI/QtAboutWidget.h @@ -6,20 +6,33 @@ #pragma once +#include + #include +class QLabel; + namespace Swift { + class SettingsProvider; + class QtAboutWidget : public QDialog { Q_OBJECT public: - QtAboutWidget(); + QtAboutWidget(SettingsProvider* settings); private slots: void handleLicenseClicked(); void handleChangelogClicked(); + void handleChangeUpdateChannelClicked(); private: void openPlainTextWindow(const QString& path); + void updateUpdateInfoLabel(); + + private: + SettingsProvider* settingsProvider_; + QLabel* updateInfoLabel_ = nullptr; + boost::signals2::scoped_connection settingsChangedConnection_; }; } diff --git a/Swift/QtUI/QtLoginWindow.cpp b/Swift/QtUI/QtLoginWindow.cpp index 6e9510d..d91f694 100644 --- a/Swift/QtUI/QtLoginWindow.cpp +++ b/Swift/QtUI/QtLoginWindow.cpp @@ -442,7 +442,7 @@ void QtLoginWindow::handleCertficateChecked(bool checked) { void QtLoginWindow::handleAbout() { if (!aboutDialog_) { - aboutDialog_ = new QtAboutWidget(); + aboutDialog_ = new QtAboutWidget(settings_); aboutDialog_->show(); } else { diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp index 3aff999..3f6ce8e 100644 --- a/Swift/QtUI/QtSwift.cpp +++ b/Swift/QtUI/QtSwift.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #if defined(SWIFTEN_PLATFORM_WINDOWS) #include @@ -84,12 +85,6 @@ namespace Swift{ -#if defined(SWIFTEN_PLATFORM_MACOSX) -#define SWIFT_APPCAST_URL "https://swift.im/appcast/swift-mac-dev.xml" -#else -#define SWIFT_APPCAST_URL "" -#endif - po::options_description QtSwift::getOptionsDescription() { po::options_description result("Options"); result.add_options() @@ -141,6 +136,22 @@ void QtSwift::loadEmoticonsFile(const QString& fileName, std::mapgetSetting(QtUISettingConstants::ENABLE_SOFTWARE_UPDATES)) { - autoUpdater_ = autoUpdaterFactory.createAutoUpdater(SWIFT_APPCAST_URL); + if (autoUpdaterFactory.isSupported() && settingsHierachy_->getSetting(QtUISettingConstants::ENABLE_SOFTWARE_UPDATES) + && !settingsHierachy_->getSetting(QtUISettingConstants::SOFTWARE_UPDATE_CHANNEL).empty()) { + autoUpdater_ = autoUpdaterFactory.createAutoUpdater(updateChannelToFeed(settingsHierachy_->getSetting(QtUISettingConstants::SOFTWARE_UPDATE_CHANNEL))); autoUpdater_->checkForUpdates(); autoUpdater_->onSuggestRestartToUserToUpdate.connect(boost::bind(&QtSwift::handleRecommendRestartToInstallUpdate, this)); + + settingsHierachy_->onSettingChanged.connect([&](const std::string& path) { + if (path == QtUISettingConstants::SOFTWARE_UPDATE_CHANNEL.getKey()) { + autoUpdater_->setAppcastFeed(updateChannelToFeed(settingsHierachy_->getSetting(QtUISettingConstants::SOFTWARE_UPDATE_CHANNEL))); + autoUpdater_->checkForUpdates(); + } + }); } } @@ -328,7 +347,7 @@ void QtSwift::handleAboutToQuit() { } void QtSwift::handleRecommendRestartToInstallUpdate() { - notifier_->showMessage(Notifier::SystemMessage, Q2PSTRING(tr("Swift Update Available")), Q2PSTRING(tr("Restart Swift now or later to update to the new Swift version")), "", [](){}); + notifier_->showMessage(Notifier::SystemMessage, Q2PSTRING(tr("Swift Update Available")), Q2PSTRING(tr("Restart Swift to update to the new Swift version.")), "", [](){}); } } diff --git a/Swift/QtUI/QtSwift.h b/Swift/QtUI/QtSwift.h index 64b79b8..3ad5714 100644 --- a/Swift/QtUI/QtSwift.h +++ b/Swift/QtUI/QtSwift.h @@ -67,6 +67,7 @@ namespace Swift { private: XMLSettingsProvider* loadSettingsFile(const QString& fileName); void loadEmoticonsFile(const QString& fileName, std::map& emoticons); + static const std::string& updateChannelToFeed(const std::string& channel); private: QtEventLoop clientMainThreadCaller_; diff --git a/Swift/QtUI/QtUISettingConstants.cpp b/Swift/QtUI/QtUISettingConstants.cpp index c81a234..24cc6dc 100644 --- a/Swift/QtUI/QtUISettingConstants.cpp +++ b/Swift/QtUI/QtUISettingConstants.cpp @@ -6,6 +6,8 @@ #include +#include + namespace Swift { const SettingsProvider::Setting QtUISettingConstants::COMPACT_ROSTER("compactRoster", false); @@ -22,4 +24,6 @@ const SettingsProvider::Setting QtUISettingConstants::SPELL_CHECKER const SettingsProvider::Setting QtUISettingConstants::TRELLIS_GRID_SIZE("trellisGridSize", ""); const SettingsProvider::Setting QtUISettingConstants::TRELLIS_GRID_POSITIONS("trellisGridPositions", ""); const SettingsProvider::Setting QtUISettingConstants::ENABLE_SOFTWARE_UPDATES("enableSoftwareUpdates", true); +const SettingsProvider::Setting QtUISettingConstants::SOFTWARE_UPDATE_CHANNEL("softwareUpdateChannel", UpdateFeeds::StableChannel); + } diff --git a/Swift/QtUI/QtUISettingConstants.h b/Swift/QtUI/QtUISettingConstants.h index 4616656..a569587 100644 --- a/Swift/QtUI/QtUISettingConstants.h +++ b/Swift/QtUI/QtUISettingConstants.h @@ -6,6 +6,8 @@ #pragma once +#include + #include namespace Swift { @@ -37,10 +39,15 @@ namespace Swift { */ static const SettingsProvider::Setting TRELLIS_GRID_POSITIONS; /** - * The #ENABLE_SOFTWARE_UPDATES settings specifies, whether Swift + * The #ENABLE_SOFTWARE_UPDATES setting specifies, whether Swift * should automatically check for software updates in regular * intervals and install them automatically. */ static const SettingsProvider::Setting ENABLE_SOFTWARE_UPDATES; + /** + * The #SOFTWARE_UPDATE_CHANNEL setting defines what update channel + * Swift uses to check for, and receive, updates. + */ + static const SettingsProvider::Setting SOFTWARE_UPDATE_CHANNEL; }; } diff --git a/Swift/QtUI/QtUpdateFeedSelectionDialog.cpp b/Swift/QtUI/QtUpdateFeedSelectionDialog.cpp new file mode 100644 index 0000000..1f4058a --- /dev/null +++ b/Swift/QtUI/QtUpdateFeedSelectionDialog.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include + +#include + +#include + +#include +#include + +namespace Swift { + +QtUpdateFeedSelectionDialog::QtUpdateFeedSelectionDialog(SettingsProvider* settingsProvider) : QDialog(), settings_(settingsProvider) { + ui.setupUi(this); + + connect(ui.currentChannelComboBox, static_cast(&QComboBox::currentIndexChanged), [&] (int newIndex) { + setDescriptionForIndex(newIndex); + }); + + auto updateChannel = settings_->getSetting(QtUISettingConstants::SOFTWARE_UPDATE_CHANNEL); + if (updateChannel == UpdateFeeds::StableChannel) { + ui.currentChannelComboBox->setCurrentIndex(0); + } + else if (updateChannel == UpdateFeeds::TestingChannel) { + ui.currentChannelComboBox->setCurrentIndex(1); + } + else if (updateChannel == UpdateFeeds::DevelopmentChannel) { + ui.currentChannelComboBox->setCurrentIndex(2); + } + + connect(this, &QDialog::accepted, [&]() { + auto newUpdateChannel = std::string(""); + switch (ui.currentChannelComboBox->currentIndex()) { + case 0: + newUpdateChannel = UpdateFeeds::StableChannel; + break; + case 1: + newUpdateChannel = UpdateFeeds::TestingChannel; + break; + case 2: + newUpdateChannel = UpdateFeeds::DevelopmentChannel; + break; + } + settings_->storeSetting(QtUISettingConstants::SOFTWARE_UPDATE_CHANNEL, newUpdateChannel); + }); + + setAttribute(Qt::WA_DeleteOnClose); +} + +void QtUpdateFeedSelectionDialog::setDescriptionForIndex(int index) { + switch (index) { + case 0: + ui.stableDescriptionLabel->show(); + ui.testingDescriptionLabel->hide(); + ui.developmentDescriptionLabel->hide(); + break; + case 1: + ui.stableDescriptionLabel->hide(); + ui.testingDescriptionLabel->show(); + ui.developmentDescriptionLabel->hide(); + break; + case 2: + ui.stableDescriptionLabel->hide(); + ui.testingDescriptionLabel->hide(); + ui.developmentDescriptionLabel->show(); + break; + default: + ui.stableDescriptionLabel->hide(); + ui.testingDescriptionLabel->hide(); + ui.developmentDescriptionLabel->hide(); + break; + } + setFixedSize(sizeHint()); +} + + + +} diff --git a/Swift/QtUI/QtUpdateFeedSelectionDialog.h b/Swift/QtUI/QtUpdateFeedSelectionDialog.h new file mode 100644 index 0000000..80b986f --- /dev/null +++ b/Swift/QtUI/QtUpdateFeedSelectionDialog.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include + +#include + +namespace Swift { + +class SettingsProvider; + +class QtUpdateFeedSelectionDialog : public QDialog { + Q_OBJECT + public: + QtUpdateFeedSelectionDialog(SettingsProvider* settingsProvider); + + private: + void setDescriptionForIndex(int index); + + private: + Ui::QtUpdateFeedSelectionDialog ui; + SettingsProvider* settings_ = nullptr; +}; + +} diff --git a/Swift/QtUI/QtUpdateFeedSelectionDialog.ui b/Swift/QtUI/QtUpdateFeedSelectionDialog.ui new file mode 100644 index 0000000..4107f3a --- /dev/null +++ b/Swift/QtUI/QtUpdateFeedSelectionDialog.ui @@ -0,0 +1,146 @@ + + + QtUpdateFeedSelectionDialog + + + + 0 + 0 + 335 + 158 + + + + Select Update Channel + + + + + + + + + -1 + + + + Stable Channel + + + + + + + + Testing Channel + + + + + + + + Development Channel + + + + + + + + + + + This release channel includes our stable releases. They went throught internal QA testing and had previous RC releases to find critical bugs. + + + Qt::PlainText + + + Qt::AlignJustify|Qt::AlignTop + + + true + + + Qt::NoTextInteraction + + + + + + + true + + + This release channel includes our stable releases, beta releases and release candidates. They should be free from obvious bugs and are released for wider testing to find more obscure bugs. + + + Qt::AlignJustify|Qt::AlignTop + + + true + + + + + + + This release channel includes our stable releases, beta releases, release candidates and development releases. The development releases are not thoroughly tested and might contained bugs. + + + Qt::AlignJustify|Qt::AlignTop + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + QtUpdateFeedSelectionDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + QtUpdateFeedSelectionDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript index fd47dd4..acfe7d0 100644 --- a/Swift/QtUI/SConscript +++ b/Swift/QtUI/SConscript @@ -199,6 +199,7 @@ sources = [ "QtURLValidator.cpp", "QtResourceHelper.cpp", "QtSpellCheckHighlighter.cpp", + "QtUpdateFeedSelectionDialog.cpp", "Trellis/QtDynamicGridLayout.cpp", "Trellis/QtDNDTabBar.cpp", "Trellis/QtGridSelectionDialog.cpp" @@ -299,6 +300,7 @@ myenv.Uic4("QtConnectionSettings.ui") myenv.Uic4("QtHighlightEditor.ui") myenv.Uic4("QtBlockListEditorWindow.ui") myenv.Uic4("QtSpellCheckerWindow.ui") +myenv.Uic4("QtUpdateFeedSelectionDialog.ui") myenv.Qrc("DefaultTheme.qrc") myenv.Qrc("Swift.qrc") diff --git a/Swift/QtUI/SwiftUpdateFeeds.h b/Swift/QtUI/SwiftUpdateFeeds.h new file mode 100644 index 0000000..a6476f5 --- /dev/null +++ b/Swift/QtUI/SwiftUpdateFeeds.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include + +namespace Swift { + +namespace UpdateFeeds { + const std::string StableChannel = "stable"; + const std::string TestingChannel = "testing"; + const std::string DevelopmentChannel = "development"; + +#if defined(SWIFTEN_PLATFORM_MACOSX) + const std::string StableAppcastFeed = "https://swift.im/downloads/swift-stable-appcast-mac.xml"; + const std::string TestingAppcastFeed = "https://swift.im/downloads/swift-testing-appcast-mac.xml"; + const std::string DevelopmentAppcastFeed = "https://swift.im/downloads/swift-development-appcast-mac.xml"; +#else + const std::string StableAppcastFeed = ""; + const std::string TestingAppcastFeed = ""; + const std::string DevelopmentAppcastFeed = ""; +#endif +} + +} -- cgit v0.10.2-6-g49f6