diff options
author | Eike Ziller <eike.ziller@qt.io> | 2020-01-08 13:38:08 +0100 |
---|---|---|
committer | Eike Ziller <eike.ziller@qt.io> | 2020-01-09 15:04:18 +0000 |
commit | ce69a9af83f4fb9e3074175f382c3b1da382d67f (patch) | |
tree | af550938d144967c8b431a16904e42d383c2f441 /src/plugins/qtsupport | |
parent | e5a9e981ad268cc8deb42f9c819e0a435ba4929f (diff) |
Provide option to "link" Qt Creator to a Qt installation
Allows making e.g. a Qt Creator-only offline installation aware of a Qt
installation, with auto-registration of Qt versions and kits.
Change-Id: Ia92dd9d4dbf34acb3a01713207699ff3f14cea12
Reviewed-by: David Schulz <david.schulz@qt.io>
Reviewed-by: hjk <hjk@qt.io>
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
Diffstat (limited to 'src/plugins/qtsupport')
-rw-r--r-- | src/plugins/qtsupport/qtoptionspage.cpp | 197 | ||||
-rw-r--r-- | src/plugins/qtsupport/qtoptionspage.h | 3 | ||||
-rw-r--r-- | src/plugins/qtsupport/qtsupportplugin.cpp | 27 | ||||
-rw-r--r-- | src/plugins/qtsupport/qtversionmanager.ui | 7 |
4 files changed, 224 insertions, 10 deletions
diff --git a/src/plugins/qtsupport/qtoptionspage.cpp b/src/plugins/qtsupport/qtoptionspage.cpp index d059b02dee..c48126247f 100644 --- a/src/plugins/qtsupport/qtoptionspage.cpp +++ b/src/plugins/qtsupport/qtoptionspage.cpp @@ -32,34 +32,40 @@ #include "qtversionmanager.h" #include "qtversionfactory.h" -#include <coreplugin/progressmanager/progressmanager.h> +#include <app/app_version.h> #include <coreplugin/coreconstants.h> +#include <coreplugin/dialogs/restartdialog.h> +#include <coreplugin/icore.h> +#include <coreplugin/progressmanager/progressmanager.h> #include <coreplugin/variablechooser.h> -#include <projectexplorer/toolchain.h> -#include <projectexplorer/toolchainmanager.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectexplorericons.h> +#include <projectexplorer/toolchain.h> +#include <projectexplorer/toolchainmanager.h> +#include <utils/algorithm.h> #include <utils/buildablehelperlibrary.h> #include <utils/hostosinfo.h> #include <utils/pathchooser.h> #include <utils/qtcassert.h> #include <utils/runextensions.h> -#include <utils/algorithm.h> #include <utils/treemodel.h> #include <utils/utilsicons.h> +#include <QDesktopServices> #include <QDir> -#include <QMessageBox> #include <QFileDialog> -#include <QTextBrowser> -#include <QDesktopServices> +#include <QMessageBox> #include <QSortFilterProxyModel> +#include <QTextBrowser> +#include <QTimer> #include <utility> using namespace ProjectExplorer; using namespace Utils; +const char kInstallSettingsKey[] = "Settings/InstallSettings"; + namespace QtSupport { namespace Internal { @@ -191,7 +197,6 @@ void QtOptionsPage::finish() //----------------------------------------------------- - QtOptionsPageWidget::QtOptionsPageWidget(QWidget *parent) : QWidget(parent) , m_specifyNameString(tr("<specify a name>")) @@ -208,6 +213,8 @@ QtOptionsPageWidget::QtOptionsPageWidget(QWidget *parent) m_ui->setupUi(this); + setupLinkWithQtButton(); + m_infoBrowser->setOpenLinks(false); m_infoBrowser->setTextInteractionFlags(Qt::TextBrowserInteraction); connect(m_infoBrowser, &QTextBrowser::anchorClicked, @@ -751,6 +758,58 @@ void QtOptionsPageWidget::updateWidgets() m_versionUi->editPathPushButton->setEnabled(enabled && !isAutodetected); } +static QString settingsFile(const QString &baseDir) +{ + return baseDir + (baseDir.isEmpty() ? "" : "/") + Core::Constants::IDE_SETTINGSVARIANT_STR + '/' + + Core::Constants::IDE_CASED_ID + ".ini"; +} + +static Utils::optional<QString> currentlyLinkedQtDir(bool *hasInstallSettings) +{ + const QString installSettingsFilePath = settingsFile(Core::ICore::resourcePath()); + const bool installSettingsExist = QFile::exists(installSettingsFilePath); + if (hasInstallSettings) + *hasInstallSettings = installSettingsExist; + if (installSettingsExist) { + const QVariant value = QSettings(installSettingsFilePath, QSettings::IniFormat) + .value(kInstallSettingsKey); + if (value.isValid()) + return value.toString(); + } + return {}; +} + +static QString linkingPurposeText() +{ + return QtOptionsPageWidget::tr( + "Linking with a Qt installation automatically registers Qt versions and kits."); +} + +void QtOptionsPageWidget::setupLinkWithQtButton() +{ + bool installSettingsExist; + const Utils::optional<QString> installSettingsValue = currentlyLinkedQtDir( + &installSettingsExist); + QStringList tip; + tip << linkingPurposeText(); + if (!FilePath::fromString(Core::ICore::resourcePath()).isWritablePath()) { + m_ui->linkWithQtButton->setEnabled(false); + tip << tr("%1's resource directory is not writable.").arg(Core::Constants::IDE_DISPLAY_NAME); + } + // guard against redirecting Qt Creator that is part of a Qt installations + // TODO this fails for pre-releases in the online installer + // TODO this will fail when make Qt Creator non-required in the Qt installers + if (installSettingsExist && !installSettingsValue) { + m_ui->linkWithQtButton->setEnabled(false); + tip << tr("%1 is part of a Qt installation.").arg(Core::Constants::IDE_DISPLAY_NAME); + } + const QString link = installSettingsValue ? *installSettingsValue : QString(); + if (!link.isEmpty()) + tip << tr("%1 is currently linked to \"%2\".").arg(Core::Constants::IDE_DISPLAY_NAME, link); + m_ui->linkWithQtButton->setToolTip(tip.join("\n\n")); + connect(m_ui->linkWithQtButton, &QPushButton::clicked, this, &QtOptionsPageWidget::linkWithQt); +} + void QtOptionsPageWidget::updateCurrentQtName() { QtVersionItem *item = currentItem(); @@ -781,8 +840,126 @@ void QtOptionsPageWidget::apply() }); QtVersionManager::setNewQtVersions(versions); - connect(QtVersionManager::instance(), &QtVersionManager::qtVersionsChanged, - this, &QtOptionsPageWidget::updateQtVersions); + connect(QtVersionManager::instance(), + &QtVersionManager::qtVersionsChanged, + this, + &QtOptionsPageWidget::updateQtVersions); +} + +// TODO whenever we move the output of sdktool to a different location in the installer, +// this needs to be adapted accordingly +const QStringList kSubdirsToCheck = {"", + "Qt Creator.app/Contents/Resources", + "Contents/Resources", + "Tools/QtCreator/share/qtcreator"}; + +static Utils::optional<QString> settingsDirForQtDir(const QString &qtDir) +{ + const QStringList dirsToCheck = Utils::transform(kSubdirsToCheck, [qtDir](const QString &dir) { + return QString(qtDir + '/' + dir); + }); + const QString validDir = Utils::findOrDefault(dirsToCheck, [](const QString &dir) { + return QFile::exists(settingsFile(dir)); + }); + if (!validDir.isEmpty()) + return validDir; + return {}; +} + +static bool validateQtInstallDir(FancyLineEdit *input, QString *errorString) +{ + const QString qtDir = input->text(); + if (!settingsDirForQtDir(qtDir)) { + if (errorString) { + const QStringList filesToCheck = Utils::transform(kSubdirsToCheck, + [](const QString &dir) { + return settingsFile(dir); + }); + *errorString = QtOptionsPageWidget::tr( + "<html><body>Qt installation information was not found in \"%1\". " + "Choose a directory that contains one of the files <pre>%2</pre>") + .arg(qtDir, filesToCheck.join('\n')); + } + return false; + } + return true; +} + +static QString defaultQtInstallationPath() +{ + if (HostOsInfo::isWindowsHost()) + return "C:/Qt"; + return QDir::homePath() + "/Qt"; +} + +void QtOptionsPageWidget::linkWithQt() +{ + const QString title = tr("Choose Qt Installation"); + const QString restartText = tr("The change will take effect after restart."); + bool askForRestart = false; + QDialog dialog(Core::ICore::dialogParent()); + dialog.setWindowTitle(title); + auto layout = new QVBoxLayout; + dialog.setLayout(layout); + layout->addWidget(new QLabel(linkingPurposeText())); + auto pathLayout = new QHBoxLayout; + layout->addLayout(pathLayout); + auto pathLabel = new QLabel(tr("Qt installation path:")); + pathLabel->setToolTip( + tr("Choose the Qt installation directory, or a directory that contains \"%1\".") + .arg(settingsFile(""))); + pathLayout->addWidget(pathLabel); + auto pathInput = new PathChooser; + pathLayout->addWidget(pathInput); + pathInput->setExpectedKind(PathChooser::ExistingDirectory); + pathInput->setPromptDialogTitle(title); + pathInput->setMacroExpander(nullptr); + pathInput->setValidationFunction([pathInput](FancyLineEdit *input, QString *errorString) { + if (pathInput->defaultValidationFunction() + && !pathInput->defaultValidationFunction()(input, errorString)) + return false; + return validateQtInstallDir(input, errorString); + }); + const Utils::optional<QString> currentLink = currentlyLinkedQtDir(nullptr); + pathInput->setPath(currentLink ? *currentLink : defaultQtInstallationPath()); + auto buttons = new QDialogButtonBox; + layout->addWidget(buttons); + auto linkButton = buttons->addButton(tr("Link with Qt"), QDialogButtonBox::AcceptRole); + connect(linkButton, &QPushButton::clicked, &dialog, &QDialog::accept); + auto cancelButton = buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole); + connect(cancelButton, &QPushButton::clicked, &dialog, &QDialog::reject); + auto unlinkButton = buttons->addButton(tr("Remove Link"), QDialogButtonBox::DestructiveRole); + unlinkButton->setEnabled(currentLink.has_value()); + connect(unlinkButton, &QPushButton::clicked, &dialog, [&dialog, &askForRestart] { + bool removeSettingsFile = false; + const QString filePath = settingsFile(Core::ICore::resourcePath()); + { + QSettings installSettings(filePath, QSettings::IniFormat); + installSettings.remove(kInstallSettingsKey); + if (installSettings.allKeys().isEmpty()) + removeSettingsFile = true; + } + if (removeSettingsFile) + QFile::remove(filePath); + askForRestart = true; + dialog.reject(); + }); + connect(pathInput, &PathChooser::validChanged, linkButton, &QPushButton::setEnabled); + linkButton->setEnabled(pathInput->isValid()); + + dialog.exec(); + if (dialog.result() == QDialog::Accepted) { + const Utils::optional<QString> settingsDir = settingsDirForQtDir(pathInput->rawPath()); + if (QTC_GUARD(settingsDir)) { + QSettings(settingsFile(Core::ICore::resourcePath()), QSettings::IniFormat) + .setValue(kInstallSettingsKey, *settingsDir); + askForRestart = true; + } + } + if (askForRestart) { + Core::RestartDialog restartDialog(Core::ICore::dialogParent(), restartText); + restartDialog.exec(); + } } } // namespace Internal diff --git a/src/plugins/qtsupport/qtoptionspage.h b/src/plugins/qtsupport/qtoptionspage.h index ece24c4b51..c094218b38 100644 --- a/src/plugins/qtsupport/qtoptionspage.h +++ b/src/plugins/qtsupport/qtoptionspage.h @@ -64,10 +64,13 @@ public: ~QtOptionsPageWidget(); void apply(); + static void linkWithQt(); + private: void updateDescriptionLabel(); void userChangedCurrentVersion(); void updateWidgets(); + void setupLinkWithQtButton(); BaseQtVersion *currentVersion() const; QtVersionItem *currentItem() const; void showDebuggingBuildLog(const QtVersionItem *item); diff --git a/src/plugins/qtsupport/qtsupportplugin.cpp b/src/plugins/qtsupport/qtsupportplugin.cpp index 9193a3071b..9059207f8c 100644 --- a/src/plugins/qtsupport/qtsupportplugin.cpp +++ b/src/plugins/qtsupport/qtsupportplugin.cpp @@ -40,6 +40,7 @@ #include "uicgenerator.h" #include <coreplugin/icore.h> +#include <coreplugin/infobar.h> #include <coreplugin/jsexpander.h> #include <projectexplorer/jsonwizard/jsonwizardfactory.h> @@ -115,6 +116,30 @@ static BaseQtVersion *qtVersion() return QtKitAspect::qtVersion(project->activeTarget()->kit()); } +const char kLinkWithQtInstallationSetting[] = "LinkWithQtInstallation"; + +static void askAboutQtInstallation() +{ + // if the install settings exist, the Qt Creator installation is (probably) already linked to + // a Qt installation, so don't ask + if (QFile::exists(ICore::settings(QSettings::SystemScope)->fileName()) + || !ICore::infoBar()->canInfoBeAdded(kLinkWithQtInstallationSetting)) + return; + + InfoBarEntry info( + kLinkWithQtInstallationSetting, + QtSupportPlugin::tr( + "Link with a Qt installation to automatically register Qt versions and kits? To do " + "this later, select Options > Kits > Qt Versions > Link with Qt."), + InfoBarEntry::GlobalSuppression::Enabled); + info.setCustomButtonInfo(QtSupportPlugin::tr("Link with Qt"), [] { + ICore::infoBar()->removeInfo(kLinkWithQtInstallationSetting); + ICore::infoBar()->globallySuppressInfo(kLinkWithQtInstallationSetting); + QTimer::singleShot(0, ICore::mainWindow(), &QtOptionsPageWidget::linkWithQt); + }); + ICore::infoBar()->addInfo(info); +} + void QtSupportPlugin::extensionsInitialized() { Utils::MacroExpander *expander = Utils::globalMacroExpander(); @@ -136,6 +161,8 @@ void QtSupportPlugin::extensionsInitialized() BaseQtVersion *qt = qtVersion(); return qt ? qt->binPath().toUserOutput() : QString(); }); + + askAboutQtInstallation(); } } // Internal diff --git a/src/plugins/qtsupport/qtversionmanager.ui b/src/plugins/qtsupport/qtversionmanager.ui index 6f303b06be..42bac27091 100644 --- a/src/plugins/qtsupport/qtversionmanager.ui +++ b/src/plugins/qtsupport/qtversionmanager.ui @@ -100,6 +100,13 @@ </spacer> </item> <item> + <widget class="QPushButton" name="linkWithQtButton"> + <property name="text"> + <string>Link with Qt...</string> + </property> + </widget> + </item> + <item> <widget class="QPushButton" name="cleanUpButton"> <property name="text"> <string>Clean Up</string> |