diff --git a/src/plugins/updateinfo/settingspage.cpp b/src/plugins/updateinfo/settingspage.cpp
index ad12cd5528e..afc89a30739 100644
--- a/src/plugins/updateinfo/settingspage.cpp
+++ b/src/plugins/updateinfo/settingspage.cpp
@@ -37,8 +37,6 @@
namespace UpdateInfo {
namespace Internal {
-const char FILTER_OPTIONS_PAGE_ID[] = "Update";
-
class UpdateInfoSettingsPageWidget final : public Core::IOptionsPageWidget
{
Q_DECLARE_TR_FUNCTIONS(UpdateInfo::Internal::UpdateInfoSettingsPage)
@@ -60,6 +58,7 @@ public:
}
m_ui.m_updatesGroupBox->setChecked(m_plugin->isAutomaticCheck());
+ m_ui.m_checkForNewQtVersions->setChecked(m_plugin->isCheckingForQtVersions());
updateLastCheckDate();
checkRunningChanged(m_plugin->isCheckForUpdatesRunning());
@@ -153,6 +152,7 @@ void UpdateInfoSettingsPageWidget::apply()
{
m_plugin->setCheckUpdateInterval(currentCheckInterval());
m_plugin->setAutomaticCheck(m_ui.m_updatesGroupBox->isChecked());
+ m_plugin->setCheckingForQtVersions(m_ui.m_checkForNewQtVersions->isChecked());
}
// SettingsPage
diff --git a/src/plugins/updateinfo/settingspage.ui b/src/plugins/updateinfo/settingspage.ui
index 5efc6b1b88e..09d87019167 100644
--- a/src/plugins/updateinfo/settingspage.ui
+++ b/src/plugins/updateinfo/settingspage.ui
@@ -26,13 +26,6 @@
true
- -
-
-
- Check interval basis:
-
-
-
-
@@ -49,13 +42,6 @@
- -
-
-
- -1
-
-
-
-
@@ -76,6 +62,20 @@
+ -
+
+
+ Check interval basis:
+
+
+
+ -
+
+
+ -1
+
+
+
-
@@ -83,6 +83,13 @@
+ -
+
+
+ Check for new Qt versions
+
+
+
@@ -135,6 +142,12 @@
Qt::Vertical
+
+
+ 0
+ 0
+
+
diff --git a/src/plugins/updateinfo/updateinfoplugin.cpp b/src/plugins/updateinfo/updateinfoplugin.cpp
index edabb344064..353a5d703fe 100644
--- a/src/plugins/updateinfo/updateinfoplugin.cpp
+++ b/src/plugins/updateinfo/updateinfoplugin.cpp
@@ -43,22 +43,27 @@
#include
#include
#include
+#include
#include
#include
#include
#include
#include
+#include
-namespace {
- static const char UpdaterGroup[] = "Updater";
- static const char MaintenanceToolKey[] = "MaintenanceTool";
- static const char AutomaticCheckKey[] = "AutomaticCheck";
- static const char CheckIntervalKey[] = "CheckUpdateInterval";
- static const char LastCheckDateKey[] = "LastCheckDate";
- static const quint32 OneMinute = 60000;
- static const quint32 OneHour = 3600000;
- static const char InstallUpdates[] = "UpdateInfo.InstallUpdates";
-}
+Q_LOGGING_CATEGORY(log, "qtc.updateinfo", QtWarningMsg)
+
+const char UpdaterGroup[] = "Updater";
+const char MaintenanceToolKey[] = "MaintenanceTool";
+const char AutomaticCheckKey[] = "AutomaticCheck";
+const char CheckForNewQtVersionsKey[] = "CheckForNewQtVersions";
+const char CheckIntervalKey[] = "CheckUpdateInterval";
+const char LastCheckDateKey[] = "LastCheckDate";
+const char LastMaxQtVersionKey[] = "LastMaxQtVersion";
+const quint32 OneMinute = 60000;
+const quint32 OneHour = 3600000;
+const char InstallUpdates[] = "UpdateInfo.InstallUpdates";
+const char InstallQtUpdates[] = "UpdateInfo.InstallQtUpdates";
using namespace Core;
@@ -78,9 +83,11 @@ public:
{
bool automaticCheck = true;
UpdateInfoPlugin::CheckUpdateInterval checkInterval = UpdateInfoPlugin::WeeklyCheck;
+ bool checkForQtVersions = true;
};
Settings m_settings;
QDate m_lastCheckDate;
+ QVersionNumber m_lastMaxQtVersion;
};
UpdateInfoPlugin::UpdateInfoPlugin()
@@ -140,6 +147,14 @@ void UpdateInfoPlugin::startCheckForUpdates()
[](int /*exitCode*/) {
return Utils::QtcProcess::FinishedWithSuccess;
});
+ if (d->m_settings.checkForQtVersions) {
+ d->m_checkUpdatesCommand
+ ->addJob({Utils::FilePath::fromString(d->m_maintenanceTool),
+ {"se", "qt[.]qt[0-9][.][0-9]+$", "-g", "*=false,ifw.package.*=true"}},
+ 60 * 3, // 3 minutes timeout
+ /*workingDirectory=*/{},
+ [](int /*exitCode*/) { return Utils::QtcProcess::FinishedWithSuccess; });
+ }
d->m_checkUpdatesCommand->execute();
d->m_progress = d->m_checkUpdatesCommand->futureProgress();
if (d->m_progress) {
@@ -189,44 +204,173 @@ static QList availableUpdates(const QDomDocument &document)
return result;
}
+struct QtPackage
+{
+ QString displayName;
+ QVersionNumber version;
+ bool installed;
+ bool isPrerelease;
+};
+
+static QList availableQtPackages(const QDomDocument &document)
+{
+ if (document.isNull() || !document.firstChildElement().hasChildNodes())
+ return {};
+ QList result;
+ const QDomNodeList packages = document.firstChildElement().elementsByTagName("package");
+ for (int i = 0; i < packages.size(); ++i) {
+ const QDomNode node = packages.item(i);
+ if (node.isElement()) {
+ const QDomElement element = node.toElement();
+ if (element.hasAttribute("displayname") && element.hasAttribute("name")
+ && element.hasAttribute("version")) {
+ QtPackage package{element.attribute("displayname"),
+ QVersionNumber::fromString(element.attribute("version")),
+ element.hasAttribute("installedVersion")};
+ // Heuristic: Prerelease if the name is not "Qt x.y.z"
+ // (prereleases are named "Qt x.y.z-alpha" etc)
+ package.isPrerelease = package.displayName
+ != QString("Qt %1").arg(package.version.toString());
+ result.append(package);
+ }
+ }
+ }
+ std::sort(result.begin(), result.end(), [](const QtPackage &p1, const QtPackage &p2) {
+ return p1.version > p2.version;
+ });
+ return result;
+}
+
+// Expects packages to be sorted, high version first.
+static Utils::optional highestInstalledQt(const QList &packages)
+{
+ const auto highestInstalledIt = std::find_if(packages.cbegin(),
+ packages.cend(),
+ [](const QtPackage &p) { return p.installed; });
+ if (highestInstalledIt == packages.cend()) // Qt not installed
+ return {};
+ return *highestInstalledIt;
+}
+
+// Expects packages to be sorted, high version first.
+static Utils::optional qtToNagAbout(const QList &allPackages,
+ QVersionNumber *highestSeen)
+{
+ // Filter out any Qt prereleases
+ const QList packages = Utils::filtered(allPackages, [](const QtPackage &p) {
+ return !p.isPrerelease;
+ });
+ if (packages.isEmpty())
+ return {};
+ const QtPackage highest = packages.constFirst();
+ qCDebug(log) << "Highest available (non-prerelease) Qt:" << highest.version;
+ qCDebug(log) << "Highest previously seen (non-prerelease) Qt:" << *highestSeen;
+ // if the highestSeen version is null, we don't know if the Qt version is new, and better don't nag
+ const bool isNew = !highestSeen->isNull() && highest.version > *highestSeen;
+ if (highestSeen->isNull() || isNew)
+ *highestSeen = highest.version;
+ if (!isNew)
+ return {};
+ const Utils::optional highestInstalled = highestInstalledQt(packages);
+ qCDebug(log) << "Highest installed Qt:"
+ << qPrintable(highestInstalled ? highestInstalled->version.toString()
+ : QString("none"));
+ if (!highestInstalled) // don't nag if no Qt is installed at all
+ return {};
+ if (highestInstalled->version == highest.version)
+ return {};
+ return highest;
+}
+
+static void showUpdateInfo(const QList &updates, const std::function &startUpdater)
+{
+ Utils::InfoBarEntry info(InstallUpdates,
+ UpdateInfoPlugin::tr("New updates are available. Start the update?"));
+ info.addCustomButton(UpdateInfoPlugin::tr("Start Update"), [startUpdater] {
+ Core::ICore::infoBar()->removeInfo(InstallUpdates);
+ startUpdater();
+ });
+ info.setDetailsWidgetCreator([updates]() -> QWidget * {
+ const QString updateText = Utils::transform(updates, [](const Update &u) {
+ return u.version.isEmpty()
+ ? u.name
+ : UpdateInfoPlugin::tr("%1 (%2)",
+ "Package name and version")
+ .arg(u.name, u.version);
+ }).join("");
+ auto label = new QLabel;
+ label->setText("" + UpdateInfoPlugin::tr("Available updates:") + "
");
+ label->setContentsMargins(0, 0, 0, 8);
+ return label;
+ });
+ Core::ICore::infoBar()->removeInfo(InstallUpdates); // remove any existing notifications
+ Core::ICore::infoBar()->unsuppressInfo(InstallUpdates);
+ Core::ICore::infoBar()->addInfo(info);
+}
+
+static void showQtUpdateInfo(const QtPackage &package,
+ const std::function &startPackageManager)
+{
+ Utils::InfoBarEntry info(InstallQtUpdates,
+ UpdateInfoPlugin::tr(
+ "%1 is available. Check the Qt blog for details.")
+ .arg(package.displayName));
+ info.addCustomButton(UpdateInfoPlugin::tr("Start Package Manager"), [startPackageManager] {
+ Core::ICore::infoBar()->removeInfo(InstallQtUpdates);
+ startPackageManager();
+ });
+ info.addCustomButton(UpdateInfoPlugin::tr("Open Settings"), [] {
+ Core::ICore::infoBar()->removeInfo(InstallQtUpdates);
+ Core::ICore::showOptionsDialog(FILTER_OPTIONS_PAGE_ID);
+ });
+ Core::ICore::infoBar()->removeInfo(InstallQtUpdates); // remove any existing notifications
+ Core::ICore::infoBar()->unsuppressInfo(InstallQtUpdates);
+ Core::ICore::infoBar()->addInfo(info);
+}
+
void UpdateInfoPlugin::checkForUpdatesFinished()
{
setLastCheckDate(QDate::currentDate());
QDomDocument document;
- document.setContent(d->m_collectedOutput);
+ // since the output can contain two toplevel items from the two separate MaintenanceTool runs,
+ // surround with a toplevel element
+ const QString xml = d->m_collectedOutput.isEmpty()
+ ? QString()
+ : ("" + d->m_collectedOutput + "");
+ qCDebug(log) << "--- MaintenanceTool output (combined):";
+ qCDebug(log) << qPrintable(xml);
+ document.setContent(xml);
stopCheckForUpdates();
- if (!document.isNull() && document.firstChildElement().hasChildNodes()) {
+ const QList updates = availableUpdates(document);
+ const QList qtPackages = availableQtPackages(document);
+ if (log().isDebugEnabled()) {
+ qCDebug(log) << "--- Available updates:";
+ for (const Update &u : updates)
+ qCDebug(log) << u.name << u.version;
+ qCDebug(log) << "--- Available Qt packages:";
+ for (const QtPackage &p : qtPackages) {
+ qCDebug(log) << p.displayName << p.version << "installed:" << p.installed
+ << "prerelease:" << p.isPrerelease;
+ }
+ }
+ Utils::optional qtToNag = qtToNagAbout(qtPackages, &d->m_lastMaxQtVersion);
+
+ if (!updates.isEmpty() || qtToNag) {
// progress details are shown until user interaction for the "no updates" case,
// so we can show the "No updates found" text, but if we have updates we don't
// want to keep it around
if (d->m_progress)
d->m_progress->setKeepOnFinish(FutureProgress::HideOnFinish);
emit newUpdatesAvailable(true);
- Utils::InfoBarEntry info(InstallUpdates, tr("New updates are available. Start the update?"));
- info.addCustomButton(tr("Start Update"), [this] {
- Core::ICore::infoBar()->removeInfo(InstallUpdates);
- startUpdater();
- });
- const QList updates = availableUpdates(document);
- info.setDetailsWidgetCreator([updates]() -> QWidget * {
- const QString updateText = Utils::transform(updates, [](const Update &u) {
- return u.version.isEmpty()
- ? u.name
- : tr("%1 (%2)", "Package name and version")
- .arg(u.name, u.version);
- }).join("");
- auto label = new QLabel;
- label->setText("" + tr("Available updates:") + "
");
- label->setContentsMargins(0, 0, 0, 8);
- return label;
- });
- Core::ICore::infoBar()->removeInfo(InstallUpdates); // remove any existing notifications
- Core::ICore::infoBar()->unsuppressInfo(InstallUpdates);
- Core::ICore::infoBar()->addInfo(info);
+ if (!updates.isEmpty())
+ showUpdateInfo(updates, [this] { startUpdater(); });
+ if (qtToNag)
+ showQtUpdateInfo(*qtToNag, [this] { startPackageManager(); });
} else {
emit newUpdatesAvailable(false);
if (d->m_progress)
@@ -298,6 +442,11 @@ void UpdateInfoPlugin::loadSettings() const
if (ok)
d->m_settings.checkInterval = static_cast(newValue);
}
+ const QString lastMaxQtVersionString = settings->value(updaterKey + LastMaxQtVersionKey)
+ .toString();
+ d->m_lastMaxQtVersion = QVersionNumber::fromString(lastMaxQtVersionString);
+ d->m_settings.checkForQtVersions
+ = settings->value(updaterKey + CheckForNewQtVersionsKey, def.checkForQtVersions).toBool();
}
void UpdateInfoPlugin::saveSettings()
@@ -318,6 +467,10 @@ void UpdateInfoPlugin::saveSettings()
settings->setValueWithDefault(CheckIntervalKey,
QString::fromUtf8(me.valueToKey(d->m_settings.checkInterval)),
QString::fromUtf8(me.valueToKey(def.checkInterval)));
+ settings->setValueWithDefault(LastMaxQtVersionKey, d->m_lastMaxQtVersion.toString());
+ settings->setValueWithDefault(CheckForNewQtVersionsKey,
+ d->m_settings.checkForQtVersions,
+ def.checkForQtVersions);
settings->endGroup();
}
@@ -351,6 +504,16 @@ void UpdateInfoPlugin::setCheckUpdateInterval(UpdateInfoPlugin::CheckUpdateInter
d->m_settings.checkInterval = interval;
}
+bool UpdateInfoPlugin::isCheckingForQtVersions() const
+{
+ return d->m_settings.checkForQtVersions;
+}
+
+void UpdateInfoPlugin::setCheckingForQtVersions(bool on)
+{
+ d->m_settings.checkForQtVersions = on;
+}
+
QDate UpdateInfoPlugin::lastCheckDate() const
{
return d->m_lastCheckDate;
@@ -384,7 +547,14 @@ QDate UpdateInfoPlugin::nextCheckDate(CheckUpdateInterval interval) const
void UpdateInfoPlugin::startUpdater()
{
- Utils::QtcProcess::startDetached({Utils::FilePath::fromString(d->m_maintenanceTool), {"--updater"}});
+ Utils::QtcProcess::startDetached(
+ {Utils::FilePath::fromString(d->m_maintenanceTool), {"--updater"}});
+}
+
+void UpdateInfoPlugin::startPackageManager()
+{
+ Utils::QtcProcess::startDetached(
+ {Utils::FilePath::fromString(d->m_maintenanceTool), {"--start-package-manager"}});
}
} //namespace Internal
diff --git a/src/plugins/updateinfo/updateinfoplugin.h b/src/plugins/updateinfo/updateinfoplugin.h
index 98c2b9904d6..78177fee919 100644
--- a/src/plugins/updateinfo/updateinfoplugin.h
+++ b/src/plugins/updateinfo/updateinfoplugin.h
@@ -35,6 +35,8 @@ namespace UpdateInfo {
namespace Internal {
+const char FILTER_OPTIONS_PAGE_ID[] = "Update";
+
class UpdateInfoPluginPrivate;
class UpdateInfoPlugin final : public ExtensionSystem::IPlugin
@@ -61,6 +63,9 @@ public:
CheckUpdateInterval checkUpdateInterval() const;
void setCheckUpdateInterval(CheckUpdateInterval interval);
+ bool isCheckingForQtVersions() const;
+ void setCheckingForQtVersions(bool on);
+
QDate lastCheckDate() const;
QDate nextCheckDate() const;
QDate nextCheckDate(CheckUpdateInterval interval) const;
@@ -81,6 +86,7 @@ private:
void doAutoCheckForUpdates();
void startUpdater();
+ void startPackageManager();
void stopCheckForUpdates();
void collectCheckForUpdatesOutput(const QString &contents);