diff --git a/src/app/main.cpp b/src/app/main.cpp index e0c9cd72bd2..e343aaad812 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -56,6 +57,7 @@ #include #include +#include #include #include @@ -356,6 +358,9 @@ struct Options QString settingsPath; QString installSettingsPath; QStringList customPluginPaths; + // list of arguments that were handled and not passed to the application or plugin manager + QStringList preAppArguments; + // list of arguments to be passed to the application or plugin manager std::vector appArguments; Utils::optional userLibraryPath; bool hasTestOption = false; @@ -375,17 +380,22 @@ Options parseCommandLine(int argc, char *argv[]) if (arg == SETTINGS_OPTION && hasNext) { ++it; options.settingsPath = QDir::fromNativeSeparators(nextArg); + options.preAppArguments << arg << nextArg; } else if (arg == INSTALL_SETTINGS_OPTION && hasNext) { ++it; options.installSettingsPath = QDir::fromNativeSeparators(nextArg); + options.preAppArguments << arg << nextArg; } else if (arg == PLUGINPATH_OPTION && hasNext) { ++it; options.customPluginPaths += QDir::fromNativeSeparators(nextArg); + options.preAppArguments << arg << nextArg; } else if (arg == USER_LIBRARY_PATH_OPTION && hasNext) { ++it; options.userLibraryPath = nextArg; + options.preAppArguments << arg << nextArg; } else if (arg == TEMPORARY_CLEAN_SETTINGS1 || arg == TEMPORARY_CLEAN_SETTINGS2) { options.wantsCleanSettings = true; + options.preAppArguments << arg; } else { // arguments that are still passed on to the application if (arg == TEST_OPTION) options.hasTestOption = true; @@ -396,8 +406,49 @@ Options parseCommandLine(int argc, char *argv[]) return options; } +class Restarter +{ +public: + Restarter(int argc, char *argv[]) + { + Q_UNUSED(argc) + m_executable = QString::fromLocal8Bit(argv[0]); + m_workingPath = QDir::currentPath(); + } + + void setArguments(const QStringList &args) { m_args = args; } + + QStringList arguments() const { return m_args; } + + int restartOrExit(int exitCode) + { + return qApp->property("restart").toBool() ? restart(exitCode) : exitCode; + } + + int restart(int exitCode) + { + QProcess::startDetached(m_executable, m_args, m_workingPath); + return exitCode; + } + +private: + QString m_executable; + QStringList m_args; + QString m_workingPath; +}; + +QStringList lastSessionArgument() +{ + // using insider information here is not particularly beautiful, anyhow + const bool hasProjectExplorer = Utils::anyOf(PluginManager::plugins(), + Utils::equal(&PluginSpec::name, + QString("ProjectExplorer"))); + return hasProjectExplorer ? QStringList({"-lastsession"}) : QStringList(); +} + int main(int argc, char **argv) { + Restarter restarter(argc, argv); Utils::Environment::systemEnvironment(); // cache system environment before we do any changes // Manually determine various command line options @@ -553,6 +604,8 @@ int main(int argc, char **argv) return -1; } } + restarter.setArguments(options.preAppArguments + PluginManager::argumentsForRestart() + + lastSessionArgument()); const PluginSpecSet plugins = PluginManager::plugins(); PluginSpec *coreplugin = nullptr; @@ -638,5 +691,5 @@ int main(int argc, char **argv) // shutdown plugin manager on the exit QObject::connect(&app, &QCoreApplication::aboutToQuit, &pluginManager, &PluginManager::shutdown); - return app.exec(); + return restarter.restartOrExit(app.exec()); } diff --git a/src/libs/extensionsystem/optionsparser.cpp b/src/libs/extensionsystem/optionsparser.cpp index 22c45df31f0..6b960600426 100644 --- a/src/libs/extensionsystem/optionsparser.cpp +++ b/src/libs/extensionsystem/optionsparser.cpp @@ -63,6 +63,7 @@ OptionsParser::OptionsParser(const QStringList &args, if (m_foundAppOptions) m_foundAppOptions->clear(); m_pmPrivate->arguments.clear(); + m_pmPrivate->argumentsForRestart.clear(); } bool OptionsParser::parse() @@ -185,6 +186,7 @@ bool OptionsParser::checkForLoadOption() m_isDependencyRefreshNeeded = true; } } + m_pmPrivate->argumentsForRestart << QLatin1String(LOAD_OPTION) << m_currentArg; } return true; } @@ -213,6 +215,7 @@ bool OptionsParser::checkForNoLoadOption() m_isDependencyRefreshNeeded = true; } } + m_pmPrivate->argumentsForRestart << QLatin1String(NO_LOAD_OPTION) << m_currentArg; } return true; } @@ -247,8 +250,11 @@ bool OptionsParser::checkForPluginOption() if (!spec) return false; spec->addArgument(m_currentArg); - if (requiresParameter && nextToken(RequiredToken)) + m_pmPrivate->argumentsForRestart << m_currentArg; + if (requiresParameter && nextToken(RequiredToken)) { spec->addArgument(m_currentArg); + m_pmPrivate->argumentsForRestart << m_currentArg; + } return true; } diff --git a/src/libs/extensionsystem/pluginmanager.cpp b/src/libs/extensionsystem/pluginmanager.cpp index e5633e860b5..8173a5e0a23 100644 --- a/src/libs/extensionsystem/pluginmanager.cpp +++ b/src/libs/extensionsystem/pluginmanager.cpp @@ -536,6 +536,17 @@ QStringList PluginManager::arguments() return d->arguments; } +/*! + The arguments that should be used when automatically restarting the application. + This includes plugin manager related options for enabling or disabling plugins, + but excludes others, like the arguments returned by arguments() and the appOptions + passed to the parseOptions() method. +*/ +QStringList PluginManager::argumentsForRestart() +{ + return d->argumentsForRestart; +} + /*! List of all plugin specifications that have been found in the plugin search paths. This list is valid directly after the setPluginPaths() call. diff --git a/src/libs/extensionsystem/pluginmanager.h b/src/libs/extensionsystem/pluginmanager.h index d65571bfb7c..d90723b986b 100644 --- a/src/libs/extensionsystem/pluginmanager.h +++ b/src/libs/extensionsystem/pluginmanager.h @@ -106,6 +106,7 @@ public: // command line arguments static QStringList arguments(); + static QStringList argumentsForRestart(); static bool parseOptions(const QStringList &args, const QMap &appOptions, QMap *foundAppOptions, diff --git a/src/libs/extensionsystem/pluginmanager_p.h b/src/libs/extensionsystem/pluginmanager_p.h index 95fba4414ad..83dd521b85c 100644 --- a/src/libs/extensionsystem/pluginmanager_p.h +++ b/src/libs/extensionsystem/pluginmanager_p.h @@ -118,6 +118,7 @@ public: QEventLoop *shutdownEventLoop = nullptr; // used for async shutdown QStringList arguments; + QStringList argumentsForRestart; QScopedPointer m_profileTimer; QHash m_profileTotal; int m_profileElapsedMS = 0; diff --git a/src/plugins/coreplugin/CMakeLists.txt b/src/plugins/coreplugin/CMakeLists.txt index af7c3c4edaa..825884c6559 100644 --- a/src/plugins/coreplugin/CMakeLists.txt +++ b/src/plugins/coreplugin/CMakeLists.txt @@ -25,6 +25,7 @@ add_qtc_plugin(Core dialogs/openwithdialog.cpp dialogs/openwithdialog.h dialogs/openwithdialog.ui dialogs/promptoverwritedialog.cpp dialogs/promptoverwritedialog.h dialogs/readonlyfilesdialog.cpp dialogs/readonlyfilesdialog.h dialogs/readonlyfilesdialog.ui + dialogs/restartdialog.cpp dialogs/restartdialog.h dialogs/saveitemsdialog.cpp dialogs/saveitemsdialog.h dialogs/saveitemsdialog.ui dialogs/settingsdialog.cpp dialogs/settingsdialog.h dialogs/shortcutsettings.cpp dialogs/shortcutsettings.h diff --git a/src/plugins/coreplugin/coreplugin.pro b/src/plugins/coreplugin/coreplugin.pro index 2f7ff16397c..ad6d1f0a89a 100644 --- a/src/plugins/coreplugin/coreplugin.pro +++ b/src/plugins/coreplugin/coreplugin.pro @@ -101,6 +101,7 @@ SOURCES += corejsextensions.cpp \ documentmanager.cpp \ iversioncontrol.cpp \ dialogs/addtovcsdialog.cpp \ + dialogs/restartdialog.cpp \ ioutputpane.cpp \ patchtool.cpp \ windowsupport.cpp \ @@ -216,6 +217,7 @@ HEADERS += corejsextensions.h \ textdocument.h \ documentmanager.h \ dialogs/addtovcsdialog.h \ + dialogs/restartdialog.h \ patchtool.h \ windowsupport.h \ opendocumentstreeview.h \ diff --git a/src/plugins/coreplugin/coreplugin.qbs b/src/plugins/coreplugin/coreplugin.qbs index 346489102ed..adc10de5871 100644 --- a/src/plugins/coreplugin/coreplugin.qbs +++ b/src/plugins/coreplugin/coreplugin.qbs @@ -216,6 +216,7 @@ Project { "openwithdialog.cpp", "openwithdialog.h", "openwithdialog.ui", "promptoverwritedialog.cpp", "promptoverwritedialog.h", "readonlyfilesdialog.cpp", "readonlyfilesdialog.h", "readonlyfilesdialog.ui", + "restartdialog.cpp", "restartdialog.h", "saveitemsdialog.cpp", "saveitemsdialog.h", "saveitemsdialog.ui", "settingsdialog.cpp", "settingsdialog.h", "shortcutsettings.cpp", "shortcutsettings.h", diff --git a/src/plugins/coreplugin/dialogs/restartdialog.cpp b/src/plugins/coreplugin/dialogs/restartdialog.cpp new file mode 100644 index 00000000000..38b9738b90b --- /dev/null +++ b/src/plugins/coreplugin/dialogs/restartdialog.cpp @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "restartdialog.h" + +#include + +#include +#include + +namespace Core { + +RestartDialog::RestartDialog(QWidget *parent, const QString &text) + : QMessageBox(parent) +{ + setWindowTitle(tr("Restart Required")); + setText(text); + setIcon(QMessageBox::Information); + addButton(tr("Later"), QMessageBox::NoRole); + addButton(tr("Restart Now"), QMessageBox::YesRole); + + connect(this, &QDialog::accepted, this, [] { + QTimer::singleShot(0, ICore::instance(), [] { ICore::restart(); }); + }); +} + +} // namespace Core diff --git a/src/plugins/coreplugin/dialogs/restartdialog.h b/src/plugins/coreplugin/dialogs/restartdialog.h new file mode 100644 index 00000000000..07b3d263744 --- /dev/null +++ b/src/plugins/coreplugin/dialogs/restartdialog.h @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include + +#include + +namespace Core { + +class CORE_EXPORT RestartDialog : public QMessageBox +{ +public: + RestartDialog(QWidget *parent, const QString &text); +}; + +} // namespace Core diff --git a/src/plugins/coreplugin/generalsettings.cpp b/src/plugins/coreplugin/generalsettings.cpp index f0db3135e93..9bc5a8fca10 100644 --- a/src/plugins/coreplugin/generalsettings.cpp +++ b/src/plugins/coreplugin/generalsettings.cpp @@ -28,6 +28,8 @@ #include "icore.h" #include "infobar.h" +#include + #include #include #include @@ -199,9 +201,11 @@ QString GeneralSettings::language() const void GeneralSettings::setLanguage(const QString &locale) { QSettings *settings = ICore::settings(); - if (settings->value(QLatin1String("General/OverrideLanguage")).toString() != locale) - QMessageBox::information(ICore::mainWindow(), tr("Restart Required"), - tr("The language change will take effect after restart.")); + if (settings->value(QLatin1String("General/OverrideLanguage")).toString() != locale) { + RestartDialog dialog(ICore::dialogParent(), + tr("The language change will take effect after restart.")); + dialog.exec(); + } if (locale.isEmpty()) settings->remove(QLatin1String("General/OverrideLanguage")); diff --git a/src/plugins/coreplugin/icore.cpp b/src/plugins/coreplugin/icore.cpp index d8ba2450207..83b3f2e2e08 100644 --- a/src/plugins/coreplugin/icore.cpp +++ b/src/plugins/coreplugin/icore.cpp @@ -710,6 +710,11 @@ void ICore::setupScreenShooter(const QString &name, QWidget *w, const QRect &rc) new ScreenShooter(w, name, rc); } +void ICore::restart() +{ + m_mainwindow->restart(); +} + void ICore::saveSettings(SaveSettingsReason reason) { emit m_instance->saveSettingsRequested(reason); diff --git a/src/plugins/coreplugin/icore.h b/src/plugins/coreplugin/icore.h index 67d8a2bbffe..d6e426b3ed6 100644 --- a/src/plugins/coreplugin/icore.h +++ b/src/plugins/coreplugin/icore.h @@ -153,6 +153,8 @@ public: MainWindowClosing, }; + static void restart(); + public slots: static void saveSettings(SaveSettingsReason reason); diff --git a/src/plugins/coreplugin/mainwindow.cpp b/src/plugins/coreplugin/mainwindow.cpp index da53dd75287..a72db32b53d 100644 --- a/src/plugins/coreplugin/mainwindow.cpp +++ b/src/plugins/coreplugin/mainwindow.cpp @@ -329,8 +329,24 @@ void MainWindow::extensionsInitialized() QTimer::singleShot(0, m_coreImpl, &ICore::coreOpened); } +static void setRestart(bool restart) +{ + qApp->setProperty("restart", restart); +} + +void MainWindow::restart() +{ + setRestart(true); + exit(); +} + void MainWindow::closeEvent(QCloseEvent *event) { + const auto cancelClose = [event] { + event->ignore(); + setRestart(false); + }; + // work around QTBUG-43344 static bool alreadyClosed = false; if (alreadyClosed) { @@ -342,13 +358,13 @@ void MainWindow::closeEvent(QCloseEvent *event) // Save opened files if (!DocumentManager::saveAllModifiedDocuments()) { - event->ignore(); + cancelClose(); return; } foreach (const std::function &listener, m_preCloseListeners) { if (!listener()) { - event->ignore(); + cancelClose(); return; } } diff --git a/src/plugins/coreplugin/mainwindow.h b/src/plugins/coreplugin/mainwindow.h index a3a537b231a..865a6e8aaa7 100644 --- a/src/plugins/coreplugin/mainwindow.h +++ b/src/plugins/coreplugin/mainwindow.h @@ -108,6 +108,8 @@ public: void saveSettings(); + void restart(); + public slots: void openFileWith(); void exit(); diff --git a/src/plugins/coreplugin/plugindialog.cpp b/src/plugins/coreplugin/plugindialog.cpp index 9163e1a2af3..fa523fa318b 100644 --- a/src/plugins/coreplugin/plugindialog.cpp +++ b/src/plugins/coreplugin/plugindialog.cpp @@ -25,6 +25,10 @@ #include "plugindialog.h" +#include "icore.h" + +#include "dialogs/restartdialog.h" + #include #include #include @@ -116,6 +120,11 @@ PluginDialog::PluginDialog(QWidget *parent) void PluginDialog::closeDialog() { ExtensionSystem::PluginManager::writeSettings(); + if (s_isRestartRequired) { + RestartDialog restartDialog(ICore::dialogParent(), + tr("Plugin changes will take effect after restart.")); + restartDialog.exec(); + } accept(); } diff --git a/src/plugins/coreplugin/themechooser.cpp b/src/plugins/coreplugin/themechooser.cpp index 0c01084f4ca..62db2eb8189 100644 --- a/src/plugins/coreplugin/themechooser.cpp +++ b/src/plugins/coreplugin/themechooser.cpp @@ -28,6 +28,8 @@ #include "manhattanstyle.h" #include "themechooser.h" +#include "dialogs/restartdialog.h" + #include #include #include @@ -178,11 +180,11 @@ void ThemeChooser::apply() QSettings *settings = ICore::settings(); const QString currentThemeId = ThemeEntry::themeSetting().toString(); if (currentThemeId != themeId) { - QMessageBox::information(ICore::mainWindow(), tr("Restart Required"), - tr("The theme change will take effect after restart.")); - // save filename of selected theme in global config settings->setValue(QLatin1String(Constants::SETTINGS_THEME), themeId); + RestartDialog restartDialog(ICore::dialogParent(), + tr("The theme change will take effect after restart.")); + restartDialog.exec(); } }