// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "runconfigurationaspects.h" #include "buildmanager.h" #include "buildpropertiessettings.h" #include "devicesupport/devicekitaspects.h" #include "devicesupport/devicemanager.h" #include "devicesupport/idevice.h" #include "environmentaspect.h" #include "projectexplorersettings.h" #include "projectexplorertr.h" #include "target.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Utils; using namespace Layouting; namespace ProjectExplorer { /*! \class ProjectExplorer::TerminalAspect \inmodule QtCreator \brief The TerminalAspect class lets a user specify that an executable should be run in a separate terminal. The initial value is provided as a hint from the build systems. */ TerminalAspect::TerminalAspect(AspectContainer *container) : BaseAspect(container) { setDisplayName(Tr::tr("Terminal")); setId("TerminalAspect"); setSettingsKey("RunConfiguration.UseTerminal"); addDataExtractor(this, &TerminalAspect::useTerminal, &Data::useTerminal); addDataExtractor(this, &TerminalAspect::isUserSet, &Data::isUserSet); calculateUseTerminal(); ProjectExplorerSettings::registerCallback(this, &ProjectExplorerSettings::terminalMode, [this] { calculateUseTerminal(); }); } /*! \reimp */ void TerminalAspect::addToLayoutImpl(Layout &parent) { QTC_CHECK(!m_checkBox); m_checkBox = createSubWidget(Tr::tr("Run in terminal")); m_checkBox->setChecked(m_useTerminal); m_checkBox->setEnabled(isEnabled()); parent.addItems({empty, m_checkBox.data()}); connect(m_checkBox.data(), &QAbstractButton::clicked, this, [this] { m_userSet = true; m_useTerminal = m_checkBox->isChecked(); emit changed(); }); } /*! \reimp */ void TerminalAspect::fromMap(const Store &map) { if (map.contains(settingsKey())) { m_useTerminal = map.value(settingsKey()).toBool(); m_userSet = true; } else { m_userSet = false; } if (m_checkBox) m_checkBox->setChecked(m_useTerminal); } /*! \reimp */ void TerminalAspect::toMap(Store &data) const { if (m_userSet) data.insert(settingsKey(), m_useTerminal); } void TerminalAspect::calculateUseTerminal() { if (m_userSet) return; bool useTerminal; switch (ProjectExplorerSettings::get(container()).terminalMode()) { case TerminalMode::On: useTerminal = true; break; case TerminalMode::Off: useTerminal = false; break; default: useTerminal = m_useTerminalHint; } if (m_useTerminal != useTerminal) { m_useTerminal = useTerminal; emit changed(); } if (m_checkBox) m_checkBox->setChecked(m_useTerminal); } /*! Returns whether a separate terminal should be used. */ bool TerminalAspect::useTerminal() const { return m_useTerminal && isEnabled(); } /*! Sets the initial value to \a hint. */ void TerminalAspect::setUseTerminalHint(bool hint) { m_useTerminalHint = hint; calculateUseTerminal(); } /*! Returns whether the user set the value. */ bool TerminalAspect::isUserSet() const { return m_userSet; } /*! \class ProjectExplorer::WorkingDirectoryAspect \inmodule QtCreator \brief The WorkingDirectoryAspect class lets the user specify a working directory for running the executable. */ WorkingDirectoryAspect::WorkingDirectoryAspect(AspectContainer *container) : BaseAspect(container) { setDisplayName(Tr::tr("Working Directory")); setLabelText(Tr::tr("Working directory:")); setId("WorkingDirectoryAspect"); setSettingsKey("RunConfiguration.WorkingDirectory"); setDefaultWorkingDirectory( FilePath::fromUserInput(buildPropertiesSettings().workingDirectoryTemplate.value())); } void WorkingDirectoryAspect::setEnvironment(EnvironmentAspect *envAspect) { m_envAspect = envAspect; } /*! \reimp */ void WorkingDirectoryAspect::addToLayoutImpl(Layout &builder) { QTC_CHECK(!m_chooser); m_chooser = new PathChooser; if (QTC_GUARD(macroExpander())) m_chooser->setMacroExpander(macroExpander()); m_chooser->setHistoryCompleter(settingsKey()); m_chooser->setExpectedKind(Utils::PathChooser::Directory); m_chooser->setPromptDialogTitle(Tr::tr("Select Working Directory")); m_chooser->setBaseDirectory(m_defaultWorkingDirectory); m_chooser->setFilePath(m_workingDirectory.isEmpty() ? m_defaultWorkingDirectory : m_workingDirectory); connect(m_chooser.data(), &PathChooser::textChanged, this, [this] { m_workingDirectory = m_chooser->unexpandedFilePath(); m_resetButton->setEnabled(m_workingDirectory != m_defaultWorkingDirectory); }); m_resetButton = new QToolButton; m_resetButton->setToolTip(Tr::tr("Reset to Default")); m_resetButton->setIcon(Utils::Icons::RESET.icon()); connect(m_resetButton.data(), &QAbstractButton::clicked, this, &WorkingDirectoryAspect::resetPath); m_resetButton->setEnabled(m_workingDirectory != m_defaultWorkingDirectory); if (m_envAspect) { connect(m_envAspect, &EnvironmentAspect::environmentChanged, m_chooser.data(), [this] { m_chooser->setEnvironment(m_envAspect->environment()); }); m_chooser->setEnvironment(m_envAspect->environment()); } m_chooser->setReadOnly(isReadOnly()); m_resetButton->setEnabled(!isReadOnly()); registerSubWidget(m_chooser); registerSubWidget(m_resetButton); addLabeledItems(builder, {m_chooser.data(), m_resetButton.data()}); } void WorkingDirectoryAspect::resetPath() { m_chooser->setFilePath(m_defaultWorkingDirectory); } /*! \reimp */ void WorkingDirectoryAspect::fromMap(const Store &map) { m_workingDirectory = FilePath::fromString(map.value(settingsKey()).toString()); m_defaultWorkingDirectory = FilePath::fromString(map.value(settingsKey() + ".default").toString()); if (m_workingDirectory.isEmpty()) m_workingDirectory = m_defaultWorkingDirectory; if (m_chooser) m_chooser->setFilePath(m_workingDirectory.isEmpty() ? m_defaultWorkingDirectory : m_workingDirectory); } /*! \reimp */ void WorkingDirectoryAspect::toMap(Store &data) const { const QString wd = m_workingDirectory == m_defaultWorkingDirectory ? QString() : m_workingDirectory.toUrlishString(); saveToMap(data, wd, QString(), settingsKey()); saveToMap(data, m_defaultWorkingDirectory.toUrlishString(), QString(), settingsKey() + ".default"); } /*! Returns the selected directory. Macros in the value are expanded using \a expander. */ FilePath WorkingDirectoryAspect::workingDirectory() const { const FilePath workingDir = macroExpander()->expand(m_workingDirectory); if (m_envAspect) return m_envAspect->environment().expandVariables(workingDir); return workingDir.deviceEnvironment().expandVariables(workingDir); } FilePath WorkingDirectoryAspect::defaultWorkingDirectory() const { return m_defaultWorkingDirectory; } /*! Returns the selected directory. Macros in the value are not expanded. */ FilePath WorkingDirectoryAspect::unexpandedWorkingDirectory() const { return m_workingDirectory; } /*! Sets the default value to \a defaultWorkingDir. */ void WorkingDirectoryAspect::setDefaultWorkingDirectory(const FilePath &defaultWorkingDir) { if (defaultWorkingDir == m_defaultWorkingDirectory) return; Utils::FilePath oldDefaultDir = m_defaultWorkingDirectory; m_defaultWorkingDirectory = defaultWorkingDir; if (m_chooser) m_chooser->setBaseDirectory(m_defaultWorkingDirectory); if (m_workingDirectory.isEmpty() || m_workingDirectory == oldDefaultDir) { if (m_chooser) m_chooser->setFilePath(m_defaultWorkingDirectory); m_workingDirectory = defaultWorkingDir; } } /*! \internal */ PathChooser *WorkingDirectoryAspect::pathChooser() const { return m_chooser; } /*! \class ProjectExplorer::ArgumentsAspect \inmodule QtCreator \brief The ArgumentsAspect class lets a user specify command line arguments for an executable. */ ArgumentsAspect::ArgumentsAspect(AspectContainer *container) : BaseAspect(container) { setDisplayName(Tr::tr("Arguments")); setLabelText(Tr::tr("Command line arguments:")); setId("ArgumentsAspect"); setSettingsKey("RunConfiguration.Arguments"); addDataExtractor(this, &ArgumentsAspect::arguments, &Data::arguments); } /*! Returns the main value of this aspect. Macros in the value are expanded using \a expander. */ QString ArgumentsAspect::arguments() const { if (m_currentlyExpanding) return m_arguments; m_currentlyExpanding = true; const Result expanded = macroExpander()->expandProcessArgs(m_arguments); QTC_ASSERT_RESULT(expanded, return m_arguments); m_currentlyExpanding = false; return *expanded; } /*! Returns the main value of this aspect. Macros in the value are not expanded. */ QString ArgumentsAspect::unexpandedArguments() const { return m_arguments; } /*! Sets the main value of this aspect to \a arguments. */ void ArgumentsAspect::setArguments(const QString &arguments) { if (arguments != m_arguments) { m_arguments = arguments; emit changed(); } if (m_chooser && m_chooser->text() != arguments) m_chooser->setText(arguments); if (m_multiLineChooser && m_multiLineChooser->toPlainText() != arguments) m_multiLineChooser->setPlainText(arguments); } /*! Adds a button to reset the main value of this aspect to the value computed by \a resetter. */ void ArgumentsAspect::setResetter(const std::function &resetter) { m_resetter = resetter; } /*! Resets the main value of this aspect. */ void ArgumentsAspect::resetArguments() { QString arguments; if (m_resetter) arguments = m_resetter(); setArguments(arguments); } /*! \reimp */ void ArgumentsAspect::fromMap(const Store &map) { QVariant args = map.value(settingsKey()); // Until 3.7 a QStringList was stored for Remote Linux if (args.typeId() == QMetaType::QStringList) m_arguments = ProcessArgs::joinArgs(args.toStringList(), OsTypeLinux); else m_arguments = args.toString(); m_multiLine = map.value(settingsKey() + ".multi", false).toBool(); if (m_multiLineButton) m_multiLineButton->setChecked(m_multiLine); if (!m_multiLine && m_chooser) m_chooser->setText(m_arguments); if (m_multiLine && m_multiLineChooser) m_multiLineChooser->setPlainText(m_arguments); } /*! \reimp */ void ArgumentsAspect::toMap(Store &map) const { saveToMap(map, m_arguments, QString(), settingsKey()); saveToMap(map, m_multiLine, false, settingsKey() + ".multi"); } /*! \internal */ QWidget *ArgumentsAspect::setupChooser() { if (m_multiLine) { if (!m_multiLineChooser) { m_multiLineChooser = new QPlainTextEdit; connect(m_multiLineChooser.data(), &QPlainTextEdit::textChanged, this, [this] { setArguments(m_multiLineChooser->toPlainText()); }); } m_multiLineChooser->setPlainText(m_arguments); m_multiLineChooser->setReadOnly(isReadOnly()); return m_multiLineChooser.data(); } if (!m_chooser) { m_chooser = new FancyLineEdit; m_chooser->setHistoryCompleter(settingsKey()); connect(m_chooser.data(), &QLineEdit::textChanged, this, &ArgumentsAspect::setArguments); } m_chooser->setText(m_arguments); m_chooser->setReadOnly(isReadOnly()); return m_chooser.data(); } /*! \reimp */ void ArgumentsAspect::addToLayoutImpl(Layout &builder) { QTC_CHECK(!m_chooser && !m_multiLineChooser && !m_multiLineButton); const auto container = new QWidget; const auto containerLayout = new QHBoxLayout(container); containerLayout->setContentsMargins(0, 0, 0, 0); containerLayout->addWidget(setupChooser()); m_multiLineButton = new ExpandButton; m_multiLineButton->setToolTip(Tr::tr("Toggle multi-line mode.")); m_multiLineButton->setChecked(m_multiLine); connect(m_multiLineButton, &QCheckBox::clicked, this, [this](bool checked) { if (m_multiLine == checked) return; m_multiLine = checked; setupChooser(); QWidget *oldWidget = nullptr; QWidget *newWidget = nullptr; if (m_multiLine) { oldWidget = m_chooser.data(); newWidget = m_multiLineChooser.data(); } else { oldWidget = m_multiLineChooser.data(); newWidget = m_chooser.data(); } QTC_ASSERT(!oldWidget == !newWidget, return); if (oldWidget) { QTC_ASSERT(oldWidget->parentWidget()->layout(), return); oldWidget->parentWidget()->layout()->replaceWidget(oldWidget, newWidget); delete oldWidget; } }); containerLayout->addWidget(m_multiLineButton); containerLayout->setAlignment(m_multiLineButton, Qt::AlignTop); if (m_resetter) { m_resetButton = new QToolButton; m_resetButton->setToolTip(Tr::tr("Reset to Default")); m_resetButton->setIcon(Icons::RESET.icon()); connect(m_resetButton.data(), &QAbstractButton::clicked, this, &ArgumentsAspect::resetArguments); containerLayout->addWidget(m_resetButton); containerLayout->setAlignment(m_resetButton, Qt::AlignTop); } registerSubWidget(container); addLabeledItem(builder, container); } void ArgumentsAspect::setFocusToInputField() { if (m_chooser) m_chooser->setFocus(); else if (m_multiLineChooser) m_multiLineChooser->setFocus(); } /*! \class ProjectExplorer::ExecutableAspect \inmodule QtCreator \brief The ExecutableAspect class provides a building block to provide an executable for a RunConfiguration. It combines a StringAspect that is typically updated automatically by the build system's parsing results with an optional manual override. */ ExecutableAspect::ExecutableAspect(AspectContainer *container) : BaseAspect(container) { setDisplayName(Tr::tr("Executable")); setId("ExecutableAspect"); setReadOnly(true); addDataExtractor(this, &ExecutableAspect::executable, &Data::executable); m_executable.setPlaceHolderText(Tr::tr("Enter the path to the executable")); m_executable.setLabelText(Tr::tr("Executable:")); connect(&m_executable, &StringAspect::changed, this, &ExecutableAspect::changed); } /*! \internal */ static IDevice::ConstPtr executionDevice(const Kit *k, ExecutableAspect::ExecutionDeviceSelector selector) { if (k) { if (selector == ExecutableAspect::RunDevice) return RunDeviceKitAspect::device(k); if (selector == ExecutableAspect::BuildDevice) return BuildDeviceKitAspect::device(k); } return DeviceManager::defaultDesktopDevice(); } ExecutableAspect::~ExecutableAspect() { delete m_alternativeExecutable; m_alternativeExecutable = nullptr; } void ExecutableAspect::setDeviceSelector(Kit *kit, ExecutionDeviceSelector selector) { m_kit = kit; m_selector = selector; const IDevice::ConstPtr dev = executionDevice(m_kit, m_selector); const OsType osType = dev ? dev->osType() : HostOsInfo::hostOs(); m_executable.setDisplayFilter([osType](const QString &pathName) { return OsSpecificAspects::pathWithNativeSeparators(osType, pathName); }); } /*! Sets the settings key for history completion to \a historyCompleterKey. \sa Utils::PathChooser::setHistoryCompleter() */ void ExecutableAspect::setHistoryCompleter(const Key &historyCompleterKey) { m_executable.setHistoryCompleter(historyCompleterKey); if (m_alternativeExecutable) m_alternativeExecutable->setHistoryCompleter(historyCompleterKey); } /*! Sets the acceptable kind of path values to \a expectedKind. \sa Utils::PathChooser::setExpectedKind() */ void ExecutableAspect::setExpectedKind(const PathChooser::Kind expectedKind) { m_executable.setExpectedKind(expectedKind); if (m_alternativeExecutable) m_alternativeExecutable->setExpectedKind(expectedKind); } /*! Sets the environment in which paths will be searched when the expected kind of paths is chosen as PathChooser::Command or PathChooser::ExistingCommand to \a env. */ void ExecutableAspect::setEnvironment(const Environment &env) { m_executable.setEnvironment(env); if (m_alternativeExecutable) m_alternativeExecutable->setEnvironment(env); } void ExecutableAspect::setReadOnly(bool readOnly) { BaseAspect::setReadOnly(readOnly); m_executable.setReadOnly(readOnly); } void ExecutableAspect::setFocusToInputField() { m_executable.setFocusToInputField(); } /*! Makes an auto-detected executable overridable by the user. The \a overridingKey specifies the settings key for the user-provided executable, the \a useOverridableKey the settings key for the fact that it is actually overridden the user. \sa Utils::StringAspect::makeCheckable() */ void ExecutableAspect::makeOverridable(const Key &overridingKey, const Key &useOverridableKey) { QTC_ASSERT(!m_alternativeExecutable, return); m_alternativeExecutable = new FilePathAspect; m_alternativeExecutable->setLabelText(Tr::tr("Alternate executable on device:")); m_alternativeExecutable->setSettingsKey(overridingKey); m_alternativeExecutable->makeCheckable(CheckBoxPlacement::Right, Tr::tr("Use this command instead"), useOverridableKey); connect(m_alternativeExecutable, &StringAspect::changed, this, &ExecutableAspect::changed); } /*! Returns the path of the executable specified by this aspect. In case the user selected a manual override this will be the value specified by the user. \sa makeOverridable() */ FilePath ExecutableAspect::executable() const { FilePath exe = m_alternativeExecutable && m_alternativeExecutable->isChecked() ? (*m_alternativeExecutable)() : m_executable(); if (const IDevice::ConstPtr dev = executionDevice(m_kit, m_selector)) exe = dev->rootPath().withNewMappedPath(exe); return exe; } /*! \reimp */ void ExecutableAspect::addToLayoutImpl(Layout &builder) { builder.addItem(m_executable); if (m_executable.pathChooser()) { connect(BuildManager::instance(), &BuildManager::buildQueueFinished, m_executable.pathChooser(), &PathChooser::triggerChanged); } if (m_alternativeExecutable) { builder.flush(); builder.addItem(m_alternativeExecutable); } } /*! Sets the label text for the main chooser to \a labelText. \sa Utils::StringAspect::setLabelText() */ void ExecutableAspect::setLabelText(const QString &labelText) { m_executable.setLabelText(labelText); } /*! Sets the place holder text for the main chooser to \a placeHolderText. \sa Utils::StringAspect::setPlaceHolderText() */ void ExecutableAspect::setPlaceHolderText(const QString &placeHolderText) { m_executable.setPlaceHolderText(placeHolderText); } /*! Sets the value of the main chooser to \a executable. */ void ExecutableAspect::setExecutable(const FilePath &executable) { m_executable.setValue(executable); m_executable.setShowToolTipOnLabel(true); } /*! Sets the settings key to \a key. */ void ExecutableAspect::setSettingsKey(const Key &key) { BaseAspect::setSettingsKey(key); m_executable.setSettingsKey(key); } /*! \reimp */ void ExecutableAspect::fromMap(const Store &map) { m_executable.fromMap(map); if (m_alternativeExecutable) m_alternativeExecutable->fromMap(map); } /*! \reimp */ void ExecutableAspect::toMap(Store &map) const { m_executable.toMap(map); if (m_alternativeExecutable) m_alternativeExecutable->toMap(map); } /*! \class ProjectExplorer::UseLibraryPathsAspect \inmodule QtCreator \brief The UseLibraryPathsAspect class lets a user specify whether build library search paths should be added to the relevant environment variables. This modifies DYLD_LIBRARY_PATH and DYLD_FRAMEWORK_PATH on Mac, PATH on Windows and LD_LIBRARY_PATH everywhere else. */ UseLibraryPathsAspect::UseLibraryPathsAspect(AspectContainer *container) : BoolAspect(container) { setId("UseLibraryPath"); setSettingsKey("RunConfiguration.UseLibrarySearchPath"); if (HostOsInfo::isMacHost()) { setLabel(Tr::tr("Add build library search path to DYLD_LIBRARY_PATH and DYLD_FRAMEWORK_PATH"), LabelPlacement::AtCheckBox); } else if (HostOsInfo::isWindowsHost()) { setLabel(Tr::tr("Add build library search path to PATH"), LabelPlacement::AtCheckBox); } else { setLabel(Tr::tr("Add build library search path to LD_LIBRARY_PATH"), LabelPlacement::AtCheckBox); } setValue(ProjectExplorerSettings::get(container).addLibraryPathsToRunEnv()); } /*! \class ProjectExplorer::UseDyldSuffixAspect \inmodule QtCreator \brief The UseDyldSuffixAspect class lets a user specify whether the DYLD_IMAGE_SUFFIX environment variable should be used on Mac. */ UseDyldSuffixAspect::UseDyldSuffixAspect(AspectContainer *container) : BoolAspect(container) { setId("UseDyldSuffix"); setSettingsKey("RunConfiguration.UseDyldImageSuffix"); setLabel(Tr::tr("Use debug version of frameworks (DYLD_IMAGE_SUFFIX=_debug)"), LabelPlacement::AtCheckBox); } /*! \class ProjectExplorer::RunAsRootAspect \inmodule QtCreator \brief The RunAsRootAspect class lets a user specify whether the application should run with root permissions. */ RunAsRootAspect::RunAsRootAspect(AspectContainer *container) : BoolAspect(container) { setId("RunAsRoot"); setSettingsKey("RunConfiguration.RunAsRoot"); setLabel(Tr::tr("Run as root user"), LabelPlacement::AtCheckBox); // Not technically correct, but sensible approximation. // Client code with more context can override. setVisible(HostOsInfo::isAnyUnixHost()); } /*! \class ProjectExplorer::RunAsAspect \inmodule QtCreator \brief The RunAsAspect class lets a user specify that the application should run under a specific account. */ RunAsAspect::RunAsAspect(Utils::AspectContainer *container) : AspectContainer(container) { setId("RunAs"); setDisplayName(Tr::tr("Run as user")); setLabelText(Tr::tr("Run as user:")); m_selection.setId("RunAsSelection"); m_selection.setSettingsKey("RunConfiguration.RunAsRoot"); // Backward compat. m_selection.addOption(Tr::tr("Default")); m_selection.addOption(Tr::tr("root")); m_selection.addOption(Tr::tr("Other")); m_selection.setDefaultValue(0); m_user.setId("RunAsName"); m_user.setSettingsKey("RunConfiguration.RunAsName"); m_user.setDisplayStyle(StringAspect::LineEditDisplay); updateUserNameEnabled(); connect(&m_selection, &SelectionAspect::changed, this, &RunAsAspect::updateUserNameEnabled); // Not technically correct, but sensible approximation. // Client code with more context can override. setVisible(HostOsInfo::isAnyUnixHost()); } QString RunAsAspect::user() const { switch (m_selection()) { case 0: break; case 1: return "root"; case 2: return m_user(); } return {}; } void RunAsAspect::fromMap(const Utils::Store &map) { AspectContainer::fromMap(map); updateUserNameEnabled(); } void RunAsAspect::addToLayoutImpl(Layouting::Layout &parent) { parent.addItems({createLabel(), m_selection, m_user, st}); } void RunAsAspect::updateUserNameEnabled() { m_user.setEnabled(m_selection.value() == 2); } /*! \class ProjectExplorer::EnableCategoriesFilterAspect \inmodule QtCreator \brief The EnableCategoriesFilterAspect class lets a user specify whether the application output should show the categories filtering widget. */ EnableCategoriesFilterAspect::EnableCategoriesFilterAspect(AspectContainer *container) : BoolAspect(container) { setId("EnableCategoriesFilter"); setSettingsKey("RunConfiguration.EnableCategoriesFilter"); setLabel(Tr::tr("Enable logging category filtering"), LabelPlacement::AtCheckBox); setToolTip( Tr::tr( "Enables filtering for logging categories (QLoggingCategory) in the Application " "Output. Requires Qt 6.11 or later.")); } Interpreter::Interpreter() : id(QUuid::createUuid().toString()) {} Interpreter::Interpreter(const QString &_id, const QString &_name, const FilePath &_command, const DetectionSource &_detectionSource) : id(_id) , name(_name) , command(_command) , detectionSource(_detectionSource) {} void Interpreter::fromMap(const Utils::Store &store) { id = store.value("Interpreter.id").toString(); name = store.value("Interpreter.name").toString(); command = FilePath::fromSettings(store.value("Interpreter.command")); detectionSource.fromMap(store); } void Interpreter::toMap(Utils::Store &store) const { store.insert("Interpreter.id", id); store.insert("Interpreter.name", name); store.insert("Interpreter.command", command.toSettings()); detectionSource.toMap(store); } static QString launcherType2UiString(const QString &type) { if (type == "test") return Tr::tr("Test"); else if (type == "emulator") return Tr::tr("Emulator"); return QString(); } Launcher::Launcher(const LauncherInfo &launcherInfo, const FilePath &sourceDirectory) : id(launcherInfo.type) , arguments(launcherInfo.arguments) { if (launcherInfo.type != "unused") { command = launcherInfo.command; if (command.isRelativePath()) command = sourceDirectory.resolvePath(command); displayName = QString("%1 (%2)").arg(launcherType2UiString(launcherInfo.type), CommandLine(command, arguments).displayName()); } } Launcher::Launcher(const LauncherInfo &testLauncherInfo, const LauncherInfo &emulatorLauncherInfo, const Utils::FilePath &sourceDirectory) : id(testLauncherInfo.type + " + " + emulatorLauncherInfo.type) , command(testLauncherInfo.command) , arguments(testLauncherInfo.arguments) { if (command.isRelativePath()) command = sourceDirectory.resolvePath(command); FilePath command1 = emulatorLauncherInfo.command; if (command1.isRelativePath()) command1 = sourceDirectory.resolvePath(command1); arguments.append(command1.toUrlishString()); arguments.append(emulatorLauncherInfo.arguments); displayName = QString("%1 + %2 (%3)").arg(launcherType2UiString(testLauncherInfo.type), launcherType2UiString(emulatorLauncherInfo.type), CommandLine(command, arguments).displayName()); } /*! \class ProjectExplorer::LauncherAspect \inmodule QtCreator \brief With the LauncherAspect class, a user can specify a launcher program for use with executable files for which a launcher program is optionally available. */ LauncherAspect::LauncherAspect(AspectContainer *container) : BaseAspect(container) { addDataExtractor(this, &LauncherAspect::currentLauncher, &Data::launcher); } Launcher LauncherAspect::currentLauncher() const { return Utils::findOrDefault(m_launchers, Utils::equal(&Launcher::id, m_currentId)); } void LauncherAspect::updateLaunchers(const QList &launchers) { if (m_launchers == launchers) return; m_launchers = launchers; if (m_comboBox) updateComboBox(); } void LauncherAspect::setDefaultLauncher(const Launcher &launcher) { if (m_defaultId == launcher.id) return; m_defaultId = launcher.id; if (m_currentId.isEmpty()) setCurrentLauncher(launcher); } void LauncherAspect::setCurrentLauncher(const Launcher &launcher) { if (m_comboBox) { const int index = m_launchers.indexOf(launcher); if (index < 0 || index >= m_comboBox->count()) return; m_comboBox->setCurrentIndex(index); } else { setCurrentLauncherId(launcher.id); } } void LauncherAspect::fromMap(const Store &map) { setCurrentLauncherId(map.value(settingsKey(), m_defaultId).toString()); } void LauncherAspect::toMap(Store &map) const { if (m_currentId != m_defaultId) saveToMap(map, m_currentId, QString(), settingsKey()); } void LauncherAspect::addToLayoutImpl(Layout &builder) { if (QTC_GUARD(m_comboBox.isNull())) m_comboBox = new QComboBox; updateComboBox(); connect(m_comboBox, &QComboBox::currentIndexChanged, this, &LauncherAspect::updateCurrentLauncher); builder.addItems({Tr::tr("Launcher:"), m_comboBox.data()}); } void LauncherAspect::setCurrentLauncherId(const QString &id) { if (id == m_currentId) return; m_currentId = id; emit changed(); } void LauncherAspect::updateCurrentLauncher() { const int index = m_comboBox->currentIndex(); if (index < 0) return; QTC_ASSERT(index < m_launchers.size(), return); m_comboBox->setToolTip(m_launchers[index].command.toUserOutput()); setCurrentLauncherId(m_launchers[index].id); } void LauncherAspect::updateComboBox() { int currentIndex = -1; int defaultIndex = -1; m_comboBox->clear(); for (const Launcher &launcher : std::as_const(m_launchers)) { int index = m_comboBox->count(); m_comboBox->addItem(launcher.displayName); m_comboBox->setItemData(index, launcher.command.toUserOutput(), Qt::ToolTipRole); if (launcher.id == m_currentId) currentIndex = index; if (launcher.id == m_defaultId) defaultIndex = index; } if (currentIndex >= 0) m_comboBox->setCurrentIndex(currentIndex); else if (defaultIndex >= 0) m_comboBox->setCurrentIndex(defaultIndex); updateCurrentLauncher(); } /*! \class ProjectExplorer::X11ForwardingAspect \inmodule QtCreator \brief The X11ForwardingAspect class lets a user specify a display for a remotely running X11 client. */ static QString defaultDisplay() { return qtcEnvironmentVariable("DISPLAY"); } X11ForwardingAspect::X11ForwardingAspect(AspectContainer *container) : StringAspect(container) { setLabelText(Tr::tr("X11 Forwarding:")); setDisplayStyle(LineEditDisplay); setId("X11ForwardingAspect"); setSettingsKey("RunConfiguration.X11Forwarding"); makeCheckable(CheckBoxPlacement::Right, Tr::tr("Enable"), "RunConfiguration.UseX11Forwarding"); setValue(defaultDisplay()); addDataExtractor(this, &X11ForwardingAspect::display, &Data::display); } QString X11ForwardingAspect::display() const { return !isChecked() ? QString() : macroExpander()->expand(value()); } /*! \class ProjectExplorer::SymbolFileAspect \inmodule QtCreator \brief The SymbolFileAspect class lets a user specify a symbol file for debugging. */ SymbolFileAspect::SymbolFileAspect(AspectContainer *container) : FilePathAspect(container) {} MainScriptAspect::MainScriptAspect(AspectContainer *container) : FilePathAspect(container) {} } // namespace ProjectExplorer