diff options
author | Tim Jenssen <tim.jenssen@qt.io> | 2023-06-02 21:23:26 +0200 |
---|---|---|
committer | Tim Jenssen <tim.jenssen@qt.io> | 2023-06-02 19:26:05 +0000 |
commit | 002d84cb15e314a207346fdfd6cf78055d1b8b92 (patch) | |
tree | f3354186e1b3f7e80a2786acfaff6656eb7cdb09 /src/plugins/projectexplorer | |
parent | bc6487068892be8c99404998c93dc6125551cf18 (diff) | |
parent | e759ce310fe6f30724f29b809bf0bcab1df88e1d (diff) |
Merge remote-tracking branch 'origin/11.0' into qds/dev
Conflicts: src/plugins/qmldesigner/designercore/metainfo/metainfo.cpp
src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp
tests/auto/qml/qmlprojectmanager/fileformat/fileformat.qbs
tests/auto/qml/qmlprojectmanager/fileformat/tst_fileformat.cpp
Change-Id: I257f1908917bcc58805619b53b6866f2f73ca544
Diffstat (limited to 'src/plugins/projectexplorer')
166 files changed, 3318 insertions, 4974 deletions
diff --git a/src/plugins/projectexplorer/CMakeLists.txt b/src/plugins/projectexplorer/CMakeLists.txt index d05509636e..d16e868a5c 100644 --- a/src/plugins/projectexplorer/CMakeLists.txt +++ b/src/plugins/projectexplorer/CMakeLists.txt @@ -28,6 +28,7 @@ add_qtc_plugin(ProjectExplorer codestylesettingspropertiespage.cpp codestylesettingspropertiespage.h compileoutputwindow.cpp compileoutputwindow.h configtaskhandler.cpp configtaskhandler.h + copystep.cpp copystep.h copytaskhandler.cpp copytaskhandler.h currentprojectfilter.cpp currentprojectfilter.h currentprojectfind.cpp currentprojectfind.h @@ -65,8 +66,7 @@ add_qtc_plugin(ProjectExplorer devicesupport/idevicefactory.cpp devicesupport/idevicefactory.h devicesupport/idevicefwd.h devicesupport/idevicewidget.h - devicesupport/localprocesslist.cpp devicesupport/localprocesslist.h - devicesupport/sshdeviceprocesslist.cpp devicesupport/sshdeviceprocesslist.h + devicesupport/processlist.cpp devicesupport/processlist.h devicesupport/sshparameters.cpp devicesupport/sshparameters.h devicesupport/sshsettings.cpp devicesupport/sshsettings.h devicesupport/sshsettingspage.cpp devicesupport/sshsettingspage.h @@ -115,7 +115,6 @@ add_qtc_plugin(ProjectExplorer ldparser.cpp ldparser.h lldparser.cpp lldparser.h linuxiccparser.cpp linuxiccparser.h - localenvironmentaspect.cpp localenvironmentaspect.h makestep.cpp makestep.h miniprojecttargetselector.cpp miniprojecttargetselector.h msvcparser.cpp msvcparser.h @@ -135,13 +134,12 @@ add_qtc_plugin(ProjectExplorer projectexplorerconstants.cpp projectexplorerconstants.h projectexplorericons.cpp projectexplorericons.h - projectexplorersettings.h - projectexplorersettingspage.cpp projectexplorersettingspage.h + projectexplorersettings.cpp projectexplorersettings.h projectexplorertr.h projectfilewizardextension.cpp projectfilewizardextension.h projectimporter.cpp projectimporter.h projectmacro.cpp projectmacro.h - projectmanager.h + projectmanager.cpp projectmanager.h projectmodels.cpp projectmodels.h projectnodes.cpp projectnodes.h projectpanelfactory.cpp projectpanelfactory.h @@ -159,10 +157,6 @@ add_qtc_plugin(ProjectExplorer runsettingspropertiespage.cpp runsettingspropertiespage.h sanitizerparser.cpp sanitizerparser.h selectablefilesmodel.cpp selectablefilesmodel.h - session.cpp session.h - sessiondialog.cpp sessiondialog.h - sessionmodel.cpp sessionmodel.h - sessionview.cpp sessionview.h showineditortaskhandler.cpp showineditortaskhandler.h showoutputtaskhandler.cpp showoutputtaskhandler.h simpleprojectwizard.cpp simpleprojectwizard.h diff --git a/src/plugins/projectexplorer/ProjectExplorer.json.in b/src/plugins/projectexplorer/ProjectExplorer.json.in index d96dcc26c6..c9b54ad441 100644 --- a/src/plugins/projectexplorer/ProjectExplorer.json.in +++ b/src/plugins/projectexplorer/ProjectExplorer.json.in @@ -24,14 +24,6 @@ \"Name\" : \"-ensure-kit-for-binary\", \"Parameter\" : \"file path\", \"Description\" : \"Create kit with architecture matching a given application or library\" - }, - { - \"Name\" : \"-lastsession\", - \"Description\" : \"Restore the last session\" - }, - { - \"Name\" : \"<session>\", - \"Description\" : \"Restore a saved session\" } ], $$dependencyList, diff --git a/src/plugins/projectexplorer/abstractprocessstep.cpp b/src/plugins/projectexplorer/abstractprocessstep.cpp index c838458aa5..806d9159f9 100644 --- a/src/plugins/projectexplorer/abstractprocessstep.cpp +++ b/src/plugins/projectexplorer/abstractprocessstep.cpp @@ -12,14 +12,15 @@ #include <utils/fileutils.h> #include <utils/outputformatter.h> +#include <utils/process.h> #include <utils/qtcassert.h> -#include <utils/qtcprocess.h> #include <QTextDecoder> #include <algorithm> #include <memory> +using namespace Tasking; using namespace Utils; namespace ProjectExplorer { @@ -76,7 +77,7 @@ public: void cleanUp(int exitCode, QProcess::ExitStatus status); AbstractProcessStep *q; - std::unique_ptr<QtcProcess> m_process; + std::unique_ptr<Process> m_process; std::unique_ptr<TaskTree> m_taskTree; ProcessParameters m_param; ProcessParameters *m_displayedParams = &m_param; @@ -182,9 +183,9 @@ void AbstractProcessStep::doRun() setupStreams(); - d->m_process.reset(new QtcProcess); + d->m_process.reset(new Process); setupProcess(d->m_process.get()); - connect(d->m_process.get(), &QtcProcess::done, this, &AbstractProcessStep::handleProcessDone); + connect(d->m_process.get(), &Process::done, this, &AbstractProcessStep::handleProcessDone); d->m_process->start(); } @@ -209,7 +210,7 @@ void AbstractProcessStep::setupStreams() d->stderrStream = std::make_unique<QTextDecoder>(QTextCodec::codecForLocale()); } -void AbstractProcessStep::setupProcess(QtcProcess *process) +void AbstractProcessStep::setupProcess(Process *process) { process->setUseCtrlCStub(HostOsInfo::isWindowsHost()); process->setWorkingDirectory(d->m_param.effectiveWorkingDirectory()); @@ -224,15 +225,15 @@ void AbstractProcessStep::setupProcess(QtcProcess *process) if (d->m_lowPriority && ProjectExplorerPlugin::projectExplorerSettings().lowBuildPriority) process->setLowPriority(); - connect(process, &QtcProcess::readyReadStandardOutput, this, [this, process] { + connect(process, &Process::readyReadStandardOutput, this, [this, process] { emit addOutput(d->stdoutStream->toUnicode(process->readAllRawStandardOutput()), OutputFormat::Stdout, DontAppendNewline); }); - connect(process, &QtcProcess::readyReadStandardError, this, [this, process] { + connect(process, &Process::readyReadStandardError, this, [this, process] { emit addOutput(d->stderrStream->toUnicode(process->readAllRawStandardError()), OutputFormat::Stderr, DontAppendNewline); }); - connect(process, &QtcProcess::started, this, [this] { + connect(process, &Process::started, this, [this] { ProcessParameters *params = displayedParameters(); emit addOutput(Tr::tr("Starting: \"%1\" %2") .arg(params->effectiveCommand().toUserOutput(), params->prettyArguments()), @@ -240,7 +241,7 @@ void AbstractProcessStep::setupProcess(QtcProcess *process) }); } -void AbstractProcessStep::runTaskTree(const Tasking::Group &recipe) +void AbstractProcessStep::runTaskTree(const Group &recipe) { setupStreams(); @@ -306,7 +307,7 @@ bool AbstractProcessStep::setupProcessParameters(ProcessParameters *params) cons const bool looksGood = executable.isEmpty() || executable.ensureReachable(workingDirectory); QTC_ASSERT(looksGood, return false); - params->setWorkingDirectory(workingDirectory.onDevice(executable)); + params->setWorkingDirectory(executable.withNewPath(workingDirectory.path())); return true; } diff --git a/src/plugins/projectexplorer/abstractprocessstep.h b/src/plugins/projectexplorer/abstractprocessstep.h index 8112541447..fc1ed13135 100644 --- a/src/plugins/projectexplorer/abstractprocessstep.h +++ b/src/plugins/projectexplorer/abstractprocessstep.h @@ -10,10 +10,11 @@ namespace Utils { class CommandLine; enum class ProcessResult; -class QtcProcess; -namespace Tasking { class Group; } +class Process; } +namespace Tasking { class Group; } + namespace ProjectExplorer { class ProcessParameters; @@ -51,8 +52,8 @@ protected: virtual void finish(Utils::ProcessResult result); bool checkWorkingDirectory(); - void setupProcess(Utils::QtcProcess *process); - void runTaskTree(const Utils::Tasking::Group &recipe); + void setupProcess(Utils::Process *process); + void runTaskTree(const Tasking::Group &recipe); ProcessParameters *displayedParameters() const; private: diff --git a/src/plugins/projectexplorer/allprojectsfilter.cpp b/src/plugins/projectexplorer/allprojectsfilter.cpp index ea4d86dba7..42e110d350 100644 --- a/src/plugins/projectexplorer/allprojectsfilter.cpp +++ b/src/plugins/projectexplorer/allprojectsfilter.cpp @@ -3,14 +3,17 @@ #include "allprojectsfilter.h" +#include "project.h" #include "projectexplorer.h" #include "projectexplorertr.h" -#include "session.h" -#include "project.h" +#include "projectmanager.h" #include <utils/algorithm.h> +#include <QFuture> + using namespace Core; +using namespace Utils; namespace ProjectExplorer::Internal { @@ -18,38 +21,29 @@ AllProjectsFilter::AllProjectsFilter() { setId("Files in any project"); setDisplayName(Tr::tr("Files in Any Project")); - setDescription(Tr::tr("Matches all files of all open projects. Append \"+<number>\" or " - "\":<number>\" to jump to the given line number. Append another " - "\"+<number>\" or \":<number>\" to jump to the column number as well.")); + setDescription(Tr::tr("Locates files of all open projects. Append \"+<number>\" or " + "\":<number>\" to jump to the given line number. Append another " + "\"+<number>\" or \":<number>\" to jump to the column number as well.")); setDefaultShortcutString("a"); setDefaultIncludedByDefault(true); + setRefreshRecipe(Tasking::Sync([this] { m_cache.invalidate(); })); connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::fileListChanged, - this, &AllProjectsFilter::markFilesAsOutOfDate); -} - -void AllProjectsFilter::markFilesAsOutOfDate() -{ - setFileIterator(nullptr); -} - -void AllProjectsFilter::prepareSearch(const QString &entry) -{ - Q_UNUSED(entry) - if (!fileIterator()) { - Utils::FilePaths paths; - for (Project *project : SessionManager::projects()) - paths.append(project->files(Project::SourceFiles)); - Utils::sort(paths); - setFileIterator(new BaseFileFilter::ListIterator(paths)); - } - BaseFileFilter::prepareSearch(entry); -} - -void AllProjectsFilter::refresh(QFutureInterface<void> &future) -{ - Q_UNUSED(future) - QMetaObject::invokeMethod(this, &AllProjectsFilter::markFilesAsOutOfDate, Qt::QueuedConnection); + this, [this] { m_cache.invalidate(); }); + m_cache.setGeneratorProvider([] { + // This body runs in main thread + FilePaths filePaths; + for (Project *project : ProjectManager::projects()) + filePaths.append(project->files(Project::SourceFiles)); + return [filePaths](const QFuture<void> &future) { + // This body runs in non-main thread + FilePaths sortedPaths = filePaths; + if (future.isCanceled()) + return FilePaths(); + Utils::sort(sortedPaths); + return sortedPaths; + }; + }); } -} // ProjectExplorer::Internal +} // namespace ProjectExplorer::Internal diff --git a/src/plugins/projectexplorer/allprojectsfilter.h b/src/plugins/projectexplorer/allprojectsfilter.h index ae9fdffe92..6782ef4611 100644 --- a/src/plugins/projectexplorer/allprojectsfilter.h +++ b/src/plugins/projectexplorer/allprojectsfilter.h @@ -3,25 +3,18 @@ #pragma once -#include <coreplugin/locator/basefilefilter.h> +#include <coreplugin/locator/ilocatorfilter.h> -#include <QFutureInterface> +namespace ProjectExplorer::Internal { -namespace ProjectExplorer { -namespace Internal { - -class AllProjectsFilter : public Core::BaseFileFilter +class AllProjectsFilter : public Core::ILocatorFilter { - Q_OBJECT - public: AllProjectsFilter(); - void refresh(QFutureInterface<void> &future) override; - void prepareSearch(const QString &entry) override; private: - void markFilesAsOutOfDate(); + Core::LocatorMatcherTasks matchers() final { return {m_cache.matcher()}; } + Core::LocatorFileCache m_cache; }; -} // namespace Internal -} // namespace ProjectExplorer +} // namespace ProjectExplorer::Internal diff --git a/src/plugins/projectexplorer/allprojectsfind.cpp b/src/plugins/projectexplorer/allprojectsfind.cpp index ac4afc1d6e..d61b156052 100644 --- a/src/plugins/projectexplorer/allprojectsfind.cpp +++ b/src/plugins/projectexplorer/allprojectsfind.cpp @@ -7,7 +7,7 @@ #include "project.h" #include "projectexplorer.h" #include "projectexplorertr.h" -#include "session.h" +#include "projectmanager.h" #include <coreplugin/editormanager/editormanager.h> @@ -44,7 +44,7 @@ QString AllProjectsFind::displayName() const bool AllProjectsFind::isEnabled() const { - return BaseFileFind::isEnabled() && SessionManager::hasProjects(); + return BaseFileFind::isEnabled() && ProjectManager::hasProjects(); } FileIterator *AllProjectsFind::files(const QStringList &nameFilters, @@ -52,7 +52,7 @@ FileIterator *AllProjectsFind::files(const QStringList &nameFilters, const QVariant &additionalParameters) const { Q_UNUSED(additionalParameters) - return filesForProjects(nameFilters, exclusionFilters, SessionManager::projects()); + return filesForProjects(nameFilters, exclusionFilters, ProjectManager::projects()); } FileIterator *AllProjectsFind::filesForProjects(const QStringList &nameFilters, diff --git a/src/plugins/projectexplorer/appoutputpane.cpp b/src/plugins/projectexplorer/appoutputpane.cpp index 6f7e22bacd..3c76c2e62a 100644 --- a/src/plugins/projectexplorer/appoutputpane.cpp +++ b/src/plugins/projectexplorer/appoutputpane.cpp @@ -8,7 +8,6 @@ #include "projectexplorericons.h" #include "projectexplorertr.h" #include "runcontrol.h" -#include "session.h" #include "showoutputtaskhandler.h" #include "windebuginterface.h" @@ -17,6 +16,7 @@ #include <coreplugin/coreconstants.h> #include <coreplugin/icore.h> #include <coreplugin/outputwindow.h> +#include <coreplugin/session.h> #include <texteditor/behaviorsettings.h> #include <texteditor/fontsettings.h> #include <texteditor/texteditorsettings.h> @@ -46,6 +46,7 @@ static Q_LOGGING_CATEGORY(appOutputLog, "qtc.projectexplorer.appoutput", QtWarningMsg); +using namespace Core; using namespace Utils; namespace ProjectExplorer { diff --git a/src/plugins/projectexplorer/buildaspects.cpp b/src/plugins/projectexplorer/buildaspects.cpp index adf37a3362..606911654f 100644 --- a/src/plugins/projectexplorer/buildaspects.cpp +++ b/src/plugins/projectexplorer/buildaspects.cpp @@ -41,7 +41,6 @@ BuildDirectoryAspect::BuildDirectoryAspect(const BuildConfiguration *bc) { setSettingsKey("ProjectExplorer.BuildConfiguration.BuildDirectory"); setLabelText(Tr::tr("Build directory:")); - setDisplayStyle(PathChooserDisplay); setExpectedKind(Utils::PathChooser::Directory); setValidationFunction([this](FancyLineEdit *edit, QString *error) { const FilePath fixedDir = fixupDir(FilePath::fromUserInput(edit->text())); @@ -107,12 +106,12 @@ void BuildDirectoryAspect::fromMap(const QVariantMap &map) } } -void BuildDirectoryAspect::addToLayout(Layouting::LayoutBuilder &builder) +void BuildDirectoryAspect::addToLayout(Layouting::LayoutItem &parent) { - StringAspect::addToLayout(builder); + StringAspect::addToLayout(parent); d->problemLabel = new InfoLabel({}, InfoLabel::Warning); d->problemLabel->setElideMode(Qt::ElideNone); - builder.addRow({{}, d->problemLabel.data()}); + parent.addItems({{}, d->problemLabel.data()}); updateProblemLabel(); if (!d->sourceDir.isEmpty()) { connect(this, &StringAspect::checkedChanged, this, [this] { diff --git a/src/plugins/projectexplorer/buildaspects.h b/src/plugins/projectexplorer/buildaspects.h index 9788cc69ca..bcc8987cd0 100644 --- a/src/plugins/projectexplorer/buildaspects.h +++ b/src/plugins/projectexplorer/buildaspects.h @@ -7,12 +7,11 @@ #include <utils/aspects.h> -namespace Utils { class FilePath; } - namespace ProjectExplorer { + class BuildConfiguration; -class PROJECTEXPLORER_EXPORT BuildDirectoryAspect : public Utils::StringAspect +class PROJECTEXPLORER_EXPORT BuildDirectoryAspect : public Utils::FilePathAspect { Q_OBJECT public: @@ -23,7 +22,7 @@ public: bool isShadowBuild() const; void setProblem(const QString &description); - void addToLayout(Utils::Layouting::LayoutBuilder &builder) override; + void addToLayout(Layouting::LayoutItem &parent) override; static Utils::FilePath fixupDir(const Utils::FilePath &dir); diff --git a/src/plugins/projectexplorer/buildconfiguration.cpp b/src/plugins/projectexplorer/buildconfiguration.cpp index ff14b7ab07..70d5a348c1 100644 --- a/src/plugins/projectexplorer/buildconfiguration.cpp +++ b/src/plugins/projectexplorer/buildconfiguration.cpp @@ -17,8 +17,8 @@ #include "projectexplorer.h" #include "projectexplorertr.h" #include "project.h" +#include "projectmanager.h" #include "projecttree.h" -#include "session.h" #include "target.h" #include <coreplugin/fileutils.h> @@ -167,6 +167,10 @@ BuildConfiguration::BuildConfiguration(Target *target, Utils::Id id) expander->registerVariable("buildDir", Tr::tr("Build directory"), [this] { return buildDirectory().toUserOutput(); }); + expander->registerFileVariables("BuildConfig:BuildDirectory", + Tr::tr("Build directory"), + [this] { return buildDirectory(); }); + expander->registerVariable("BuildConfig:Name", Tr::tr("Name of the build configuration"), [this] { return displayName(); }); @@ -209,7 +213,7 @@ BuildConfiguration::BuildConfiguration(Target *target, Utils::Id id) connect(target, &Target::parsingStarted, this, &BuildConfiguration::enabledChanged); connect(target, &Target::parsingFinished, this, &BuildConfiguration::enabledChanged); connect(this, &BuildConfiguration::enabledChanged, this, [this] { - if (isActive() && project() == SessionManager::startupProject()) { + if (isActive() && project() == ProjectManager::startupProject()) { ProjectExplorerPlugin::updateActions(); ProjectExplorerPlugin::updateRunActions(); } @@ -318,12 +322,15 @@ NamedWidget *BuildConfiguration::createConfigWidget() widget = named; } - Layouting::Form builder; + Layouting::Form form; for (BaseAspect *aspect : aspects()) { - if (aspect->isVisible()) - aspect->addToLayout(builder.finishRow()); + if (aspect->isVisible()) { + form.addItem(aspect); + form.addItem(Layouting::br); + } } - builder.attachTo(widget, Layouting::WithoutMargins); + form.addItem(Layouting::noMargin); + form.attachTo(widget); return named; } @@ -612,13 +619,27 @@ FilePath BuildConfiguration::buildDirectoryFromTemplate(const FilePath &projectD [buildType] { return buildTypeName(buildType); }); exp.registerSubProvider([kit] { return kit->macroExpander(); }); - QString buildDir = ProjectExplorerPlugin::buildDirectoryTemplate(); - qCDebug(bcLog) << "build dir template:" << buildDir; + FilePath buildDir = FilePath::fromUserInput(ProjectExplorerPlugin::buildDirectoryTemplate()); + qCDebug(bcLog) << "build dir template:" << buildDir.toUserOutput(); buildDir = exp.expand(buildDir); - qCDebug(bcLog) << "expanded build:" << buildDir; - buildDir.replace(" ", "-"); + qCDebug(bcLog) << "expanded build:" << buildDir.toUserOutput(); + buildDir = buildDir.withNewPath(buildDir.path().replace(" ", "-")); + + auto buildDevice = BuildDeviceKitAspect::device(kit); + + if (buildDir.isAbsolutePath()) { + bool isReachable = buildDevice->ensureReachable(buildDir); + if (!isReachable) + return {}; + return buildDevice->rootPath().withNewMappedPath(buildDir); + } + + bool isReachable = buildDevice->ensureReachable(projectDir); + if (!isReachable) + return {}; - return projectDir.resolvePath(buildDir); + const FilePath baseDir = buildDevice->rootPath().withNewMappedPath(projectDir); + return baseDir.resolvePath(buildDir); } /// // IBuildConfigurationFactory diff --git a/src/plugins/projectexplorer/buildmanager.cpp b/src/plugins/projectexplorer/buildmanager.cpp index 57bfde6e46..28169513cb 100644 --- a/src/plugins/projectexplorer/buildmanager.cpp +++ b/src/plugins/projectexplorer/buildmanager.cpp @@ -16,8 +16,8 @@ #include "projectexplorerconstants.h" #include "projectexplorersettings.h" #include "projectexplorertr.h" +#include "projectmanager.h" #include "runcontrol.h" -#include "session.h" #include "target.h" #include "task.h" #include "taskhub.h" @@ -258,7 +258,7 @@ BuildManager::BuildManager(QObject *parent, QAction *cancelBuildAction) m_instance = this; d = new BuildManagerPrivate; - connect(SessionManager::instance(), &SessionManager::aboutToRemoveProject, + connect(ProjectManager::instance(), &ProjectManager::aboutToRemoveProject, this, &BuildManager::aboutToRemoveProject); d->m_outputWindow = new Internal::CompileOutputWindow(cancelBuildAction); @@ -318,19 +318,19 @@ void BuildManager::rebuildProjectWithoutDependencies(Project *project) void BuildManager::buildProjectWithDependencies(Project *project, ConfigSelection configSelection) { - queue(SessionManager::projectOrder(project), {Id(Constants::BUILDSTEPS_BUILD)}, + queue(ProjectManager::projectOrder(project), {Id(Constants::BUILDSTEPS_BUILD)}, configSelection); } void BuildManager::cleanProjectWithDependencies(Project *project, ConfigSelection configSelection) { - queue(SessionManager::projectOrder(project), {Id(Constants::BUILDSTEPS_CLEAN)}, + queue(ProjectManager::projectOrder(project), {Id(Constants::BUILDSTEPS_CLEAN)}, configSelection); } void BuildManager::rebuildProjectWithDependencies(Project *project, ConfigSelection configSelection) { - queue(SessionManager::projectOrder(project), + queue(ProjectManager::projectOrder(project), {Id(Constants::BUILDSTEPS_CLEAN), Id(Constants::BUILDSTEPS_BUILD)}, configSelection); } @@ -384,7 +384,7 @@ BuildForRunConfigStatus BuildManager::potentiallyBuildForRunConfig(RunConfigurat } Project * const pro = rc->target()->project(); - const int queueCount = queue(SessionManager::projectOrder(pro), stepIds, + const int queueCount = queue(ProjectManager::projectOrder(pro), stepIds, ConfigSelection::Active, rc); if (rc->target()->activeBuildConfiguration()) rc->target()->activeBuildConfiguration()->restrictNextBuild(nullptr); @@ -442,16 +442,6 @@ int BuildManager::getErrorTaskCount() return errors; } -void BuildManager::setCompileOutputSettings(const CompileOutputSettings &settings) -{ - d->m_outputWindow->setSettings(settings); -} - -const CompileOutputSettings &BuildManager::compileOutputSettings() -{ - return d->m_outputWindow->settings(); -} - QString BuildManager::displayNameForStepId(Id stepId) { if (stepId == Constants::BUILDSTEPS_CLEAN) { @@ -853,7 +843,7 @@ bool BuildManager::buildLists(const QList<BuildStepList *> bsls, const QStringLi return false; } - if (d->m_outputWindow->settings().popUp) + if (CompileOutputSettings::instance().popUp()) d->m_outputWindow->popup(IOutputPane::NoModeSwitch); startBuildQueue(); return true; @@ -866,7 +856,7 @@ void BuildManager::appendStep(BuildStep *step, const QString &name) d->m_outputWindow->popup(IOutputPane::NoModeSwitch); return; } - if (d->m_outputWindow->settings().popUp) + if (CompileOutputSettings::instance().popUp()) d->m_outputWindow->popup(IOutputPane::NoModeSwitch); startBuildQueue(); } diff --git a/src/plugins/projectexplorer/buildmanager.h b/src/plugins/projectexplorer/buildmanager.h index d7d2c91562..d9478db409 100644 --- a/src/plugins/projectexplorer/buildmanager.h +++ b/src/plugins/projectexplorer/buildmanager.h @@ -10,12 +10,10 @@ #include <QStringList> namespace ProjectExplorer { -class RunConfiguration; - -namespace Internal { class CompileOutputSettings; } -class Task; class Project; +class RunConfiguration; +class Task; enum class BuildForRunConfigStatus { Building, NotBuilding, BuildFailed }; enum class ConfigSelection { All, Active }; @@ -66,9 +64,6 @@ public: static int getErrorTaskCount(); - static void setCompileOutputSettings(const Internal::CompileOutputSettings &settings); - static const Internal::CompileOutputSettings &compileOutputSettings(); - static QString displayNameForStepId(Utils::Id stepId); public slots: diff --git a/src/plugins/projectexplorer/buildpropertiessettings.cpp b/src/plugins/projectexplorer/buildpropertiessettings.cpp index a9126551e3..6df8fdd328 100644 --- a/src/plugins/projectexplorer/buildpropertiessettings.cpp +++ b/src/plugins/projectexplorer/buildpropertiessettings.cpp @@ -16,15 +16,33 @@ namespace ProjectExplorer { const char DEFAULT_BUILD_DIRECTORY_TEMPLATE[] = "../%{JS: Util.asciify(\"build-%{Project:Name}-%{Kit:FileSystemName}-%{BuildConfig:Name}\")}"; -BuildPropertiesSettings::BuildTriStateAspect::BuildTriStateAspect() - : TriStateAspect{Tr::tr("Enable"), Tr::tr("Disable"), Tr::tr("Use Project Default")} +BuildPropertiesSettings::BuildTriStateAspect::BuildTriStateAspect(AspectContainer *container) + : TriStateAspect(container, Tr::tr("Enable"), Tr::tr("Disable"), Tr::tr("Use Project Default")) {} BuildPropertiesSettings::BuildPropertiesSettings() { setAutoApply(false); - registerAspect(&buildDirectoryTemplate); + setId("AB.ProjectExplorer.BuildPropertiesSettingsPage"); + setDisplayName(Tr::tr("Default Build Properties")); + setCategory(ProjectExplorer::Constants::BUILD_AND_RUN_SETTINGS_CATEGORY); + setSettings(this); + + setLayouter([this] { + using namespace Layouting; + + return Column { + Form { + buildDirectoryTemplate, br, + separateDebugInfo, br, + qmlDebugging, br, + qtQuickCompiler + }, + st + }; + }); + buildDirectoryTemplate.setDisplayStyle(StringAspect::LineEditDisplay); buildDirectoryTemplate.setSettingsKey("Directories/BuildDirectory.TemplateV2"); buildDirectoryTemplate.setDefaultValue(DEFAULT_BUILD_DIRECTORY_TEMPLATE); @@ -32,19 +50,12 @@ BuildPropertiesSettings::BuildPropertiesSettings() buildDirectoryTemplate.setUseGlobalMacroExpander(); buildDirectoryTemplate.setUseResetButton(); - registerAspect(&buildDirectoryTemplateOld); // TODO: Remove in ~4.16 - buildDirectoryTemplateOld.setSettingsKey("Directories/BuildDirectory.Template"); - buildDirectoryTemplateOld.setDefaultValue(DEFAULT_BUILD_DIRECTORY_TEMPLATE); - - registerAspect(&separateDebugInfo); separateDebugInfo.setSettingsKey("ProjectExplorer/Settings/SeparateDebugInfo"); separateDebugInfo.setLabelText(Tr::tr("Separate debug info:")); - registerAspect(&qmlDebugging); qmlDebugging.setSettingsKey("ProjectExplorer/Settings/QmlDebugging"); qmlDebugging.setLabelText(Tr::tr("QML debugging:")); - registerAspect(&qtQuickCompiler); qtQuickCompiler.setSettingsKey("ProjectExplorer/Settings/QtQuickCompiler"); qtQuickCompiler.setLabelText(Tr::tr("Use qmlcachegen:")); @@ -54,51 +65,9 @@ BuildPropertiesSettings::BuildPropertiesSettings() &qtQuickCompiler, &BaseAspect::setVisible); } -void BuildPropertiesSettings::readSettings(QSettings *s) -{ - AspectContainer::readSettings(s); - - // TODO: Remove in ~4.16 - QString v = buildDirectoryTemplate.value(); - if (v.isEmpty()) - v = buildDirectoryTemplateOld.value(); - if (v.isEmpty()) - v = DEFAULT_BUILD_DIRECTORY_TEMPLATE; - v.replace("%{CurrentProject:Name}", "%{Project:Name}"); - v.replace("%{CurrentKit:FileSystemName}", "%{Kit:FileSystemName}"); - v.replace("%{CurrentBuild:Name}", "%{BuildConfig:Name}"); - buildDirectoryTemplate.setValue(v); -} - QString BuildPropertiesSettings::defaultBuildDirectoryTemplate() { return QString(DEFAULT_BUILD_DIRECTORY_TEMPLATE); } -namespace Internal { - -BuildPropertiesSettingsPage::BuildPropertiesSettingsPage(BuildPropertiesSettings *settings) -{ - setId("AB.ProjectExplorer.BuildPropertiesSettingsPage"); - setDisplayName(Tr::tr("Default Build Properties")); - setCategory(ProjectExplorer::Constants::BUILD_AND_RUN_SETTINGS_CATEGORY); - setSettings(settings); - - setLayouter([settings](QWidget *widget) { - BuildPropertiesSettings &s = *settings; - using namespace Layouting; - - Column { - Form { - s.buildDirectoryTemplate, - s.separateDebugInfo, - s.qmlDebugging, - s.qtQuickCompiler - }, - st - }.attachTo(widget); - }); -} - -} // Internal } // ProjectExplorer diff --git a/src/plugins/projectexplorer/buildpropertiessettings.h b/src/plugins/projectexplorer/buildpropertiessettings.h index 75ec957a4e..3b1b2b7c69 100644 --- a/src/plugins/projectexplorer/buildpropertiessettings.h +++ b/src/plugins/projectexplorer/buildpropertiessettings.h @@ -7,11 +7,9 @@ #include <coreplugin/dialogs/ioptionspage.h> -#include <utils/aspects.h> - namespace ProjectExplorer { -class PROJECTEXPLORER_EXPORT BuildPropertiesSettings : public Utils::AspectContainer +class PROJECTEXPLORER_EXPORT BuildPropertiesSettings : public Core::PagedSettings { public: BuildPropertiesSettings(); @@ -19,28 +17,16 @@ public: class BuildTriStateAspect : public Utils::TriStateAspect { public: - BuildTriStateAspect(); + explicit BuildTriStateAspect(AspectContainer *container); }; - Utils::StringAspect buildDirectoryTemplate; - Utils::StringAspect buildDirectoryTemplateOld; // TODO: Remove in ~4.16 - BuildTriStateAspect separateDebugInfo; - BuildTriStateAspect qmlDebugging; - BuildTriStateAspect qtQuickCompiler; + Utils::StringAspect buildDirectoryTemplate{this}; + BuildTriStateAspect separateDebugInfo{this}; + BuildTriStateAspect qmlDebugging{this}; + BuildTriStateAspect qtQuickCompiler{this}; Utils::BoolAspect showQtSettings; - void readSettings(QSettings *settings); - QString defaultBuildDirectoryTemplate(); }; -namespace Internal { - -class BuildPropertiesSettingsPage final : public Core::IOptionsPage -{ -public: - explicit BuildPropertiesSettingsPage(BuildPropertiesSettings *settings); -}; - -} // namespace Internal } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/buildsettingspropertiespage.cpp b/src/plugins/projectexplorer/buildsettingspropertiespage.cpp index 410449083c..2caa44712e 100644 --- a/src/plugins/projectexplorer/buildsettingspropertiespage.cpp +++ b/src/plugins/projectexplorer/buildsettingspropertiespage.cpp @@ -10,10 +10,10 @@ #include "project.h" #include "projectconfigurationmodel.h" #include "projectexplorertr.h" -#include "session.h" #include "target.h" #include <coreplugin/icore.h> +#include <coreplugin/session.h> #include <utils/algorithm.h> #include <utils/qtcassert.h> @@ -185,7 +185,7 @@ void BuildSettingsWidget::currentIndexChanged(int index) { auto buildConfiguration = qobject_cast<BuildConfiguration *>( m_target->buildConfigurationModel()->projectConfigurationAt(index)); - SessionManager::setActiveBuildConfiguration(m_target, buildConfiguration, SetActive::Cascade); + m_target->setActiveBuildConfiguration(buildConfiguration, SetActive::Cascade); } void BuildSettingsWidget::updateActiveConfiguration() @@ -222,7 +222,7 @@ void BuildSettingsWidget::createConfiguration(const BuildInfo &info_) return; m_target->addBuildConfiguration(bc); - SessionManager::setActiveBuildConfiguration(m_target, bc, SetActive::Cascade); + m_target->setActiveBuildConfiguration(bc, SetActive::Cascade); } QString BuildSettingsWidget::uniqueName(const QString & name) @@ -286,7 +286,7 @@ void BuildSettingsWidget::cloneConfiguration() bc->setDisplayName(name); const FilePath buildDirectory = bc->buildDirectory(); if (buildDirectory != m_target->project()->projectDirectory()) { - const std::function<bool(const FilePath &)> isBuildDirOk = [this](const FilePath &candidate) { + const FilePathPredicate isBuildDirOk = [this](const FilePath &candidate) { if (candidate.exists()) return false; return !anyOf(m_target->buildConfigurations(), [&candidate](const BuildConfiguration *bc) { @@ -295,7 +295,7 @@ void BuildSettingsWidget::cloneConfiguration() bc->setBuildDirectory(makeUniquelyNumbered(buildDirectory, isBuildDirOk)); } m_target->addBuildConfiguration(bc); - SessionManager::setActiveBuildConfiguration(m_target, bc, SetActive::Cascade); + m_target->setActiveBuildConfiguration(bc, SetActive::Cascade); } void BuildSettingsWidget::deleteConfiguration(BuildConfiguration *deleteConfiguration) diff --git a/src/plugins/projectexplorer/buildstep.cpp b/src/plugins/projectexplorer/buildstep.cpp index c078a73d82..32be37048e 100644 --- a/src/plugins/projectexplorer/buildstep.cpp +++ b/src/plugins/projectexplorer/buildstep.cpp @@ -147,7 +147,7 @@ QWidget *BuildStep::doCreateConfigWidget() setSummaryText(m_summaryUpdater()); }; - for (BaseAspect *aspect : std::as_const(m_aspects)) + for (BaseAspect *aspect : std::as_const(*this)) connect(aspect, &BaseAspect::changed, widget, recreateSummary); connect(buildConfiguration(), &BuildConfiguration::buildDirectoryChanged, @@ -160,12 +160,13 @@ QWidget *BuildStep::doCreateConfigWidget() QWidget *BuildStep::createConfigWidget() { - Layouting::Form builder; - for (BaseAspect *aspect : std::as_const(m_aspects)) { + Layouting::Form form; + for (BaseAspect *aspect : std::as_const(*this)) { if (aspect->isVisible()) - aspect->addToLayout(builder.finishRow()); + form.addItem(aspect); } - auto widget = builder.emerge(Layouting::WithoutMargins); + form.addItem(Layouting::noMargin); + auto widget = form.emerge(); if (m_addMacroExpander) VariableChooser::addSupportForChildWidgets(widget, macroExpander()); @@ -361,7 +362,7 @@ bool BuildStepFactory::canHandle(BuildStepList *bsl) const return false; } - if (!m_isRepeatable && bsl->contains(m_info.id)) + if (!m_isRepeatable && bsl->contains(m_stepId)) return false; if (m_supportedConfiguration.isValid()) { @@ -375,14 +376,42 @@ bool BuildStepFactory::canHandle(BuildStepList *bsl) const return true; } +QString BuildStepFactory::displayName() const +{ + return m_displayName; +} + +void BuildStepFactory::cloneStepCreator(Id exitstingStepId, Id overrideNewStepId) +{ + m_stepId = {}; + m_creator = {}; + for (BuildStepFactory *factory : BuildStepFactory::allBuildStepFactories()) { + if (factory->m_stepId == exitstingStepId) { + m_creator = factory->m_creator; + m_stepId = factory->m_stepId; + m_displayName = factory->m_displayName; + // Other bits are intentionally not copied as they are unlikely to be + // useful in the cloner's context. The cloner can/has to finish the + // setup on its own. + break; + } + } + // Existence should be guaranteed by plugin dependencies. In case it fails, + // bark and keep the factory in a state where the invalid m_stepId keeps it + // inaction. + QTC_ASSERT(m_creator, return); + if (overrideNewStepId.isValid()) + m_stepId = overrideNewStepId; +} + void BuildStepFactory::setDisplayName(const QString &displayName) { - m_info.displayName = displayName; + m_displayName = displayName; } -void BuildStepFactory::setFlags(BuildStepInfo::Flags flags) +void BuildStepFactory::setFlags(BuildStep::Flags flags) { - m_info.flags = flags; + m_flags = flags; } void BuildStepFactory::setSupportedStepList(Id id) @@ -415,20 +444,21 @@ void BuildStepFactory::setSupportedDeviceTypes(const QList<Id> &ids) m_supportedDeviceTypes = ids; } -BuildStepInfo BuildStepFactory::stepInfo() const +BuildStep::Flags BuildStepFactory::stepFlags() const { - return m_info; + return m_flags; } Id BuildStepFactory::stepId() const { - return m_info.id; + return m_stepId; } BuildStep *BuildStepFactory::create(BuildStepList *parent) { - BuildStep *step = m_info.creator(parent); - step->setDefaultDisplayName(m_info.displayName); + QTC_ASSERT(m_creator, return nullptr); + BuildStep *step = m_creator(parent); + step->setDefaultDisplayName(m_displayName); return step; } diff --git a/src/plugins/projectexplorer/buildstep.h b/src/plugins/projectexplorer/buildstep.h index 547a5e2027..46be75f563 100644 --- a/src/plugins/projectexplorer/buildstep.h +++ b/src/plugins/projectexplorer/buildstep.h @@ -76,6 +76,12 @@ public: enum OutputNewlineSetting { DoAppendNewline, DontAppendNewline }; + enum Flags { + Uncreatable = 1 << 0, + Unclonable = 1 << 1, + UniqueStep = 1 << 8 // Can't be used twice in a BuildStepList + }; + bool widgetExpandedByDefault() const; void setWidgetExpandedByDefault(bool widgetExpandedByDefault); @@ -136,23 +142,6 @@ private: QString m_summaryText; }; -class PROJECTEXPLORER_EXPORT BuildStepInfo -{ -public: - enum Flags { - Uncreatable = 1 << 0, - Unclonable = 1 << 1, - UniqueStep = 1 << 8 // Can't be used twice in a BuildStepList - }; - - using BuildStepCreator = std::function<BuildStep *(BuildStepList *)>; - - Utils::Id id; - QString displayName; - Flags flags = Flags(); - BuildStepCreator creator; -}; - class PROJECTEXPLORER_EXPORT BuildStepFactory { public: @@ -163,23 +152,26 @@ public: static const QList<BuildStepFactory *> allBuildStepFactories(); - BuildStepInfo stepInfo() const; + BuildStep::Flags stepFlags() const; Utils::Id stepId() const; BuildStep *create(BuildStepList *parent); BuildStep *restore(BuildStepList *parent, const QVariantMap &map); bool canHandle(BuildStepList *bsl) const; + QString displayName() const; + protected: using BuildStepCreator = std::function<BuildStep *(BuildStepList *)>; template <class BuildStepType> void registerStep(Utils::Id id) { - QTC_CHECK(!m_info.creator); - m_info.id = id; - m_info.creator = [id](BuildStepList *bsl) { return new BuildStepType(bsl, id); }; + QTC_CHECK(!m_creator); + m_stepId = id; + m_creator = [id](BuildStepList *bsl) { return new BuildStepType(bsl, id); }; } + void cloneStepCreator(Utils::Id exitstingStepId, Utils::Id overrideNewStepId = {}); void setSupportedStepList(Utils::Id id); void setSupportedStepLists(const QList<Utils::Id> &ids); @@ -189,10 +181,13 @@ protected: void setSupportedDeviceTypes(const QList<Utils::Id> &ids); void setRepeatable(bool on) { m_isRepeatable = on; } void setDisplayName(const QString &displayName); - void setFlags(BuildStepInfo::Flags flags); + void setFlags(BuildStep::Flags flags); private: - BuildStepInfo m_info; + Utils::Id m_stepId; + QString m_displayName; + BuildStep::Flags m_flags = {}; + BuildStepCreator m_creator; Utils::Id m_supportedProjectType; QList<Utils::Id> m_supportedDeviceTypes; diff --git a/src/plugins/projectexplorer/buildsteplist.cpp b/src/plugins/projectexplorer/buildsteplist.cpp index 8d6159541e..0094bf0b68 100644 --- a/src/plugins/projectexplorer/buildsteplist.cpp +++ b/src/plugins/projectexplorer/buildsteplist.cpp @@ -43,16 +43,6 @@ QVariantMap BuildStepList::toMap() const { QVariantMap map; - { - // Only written for compatibility reasons within the 4.11 cycle - const char CONFIGURATION_ID_KEY[] = "ProjectExplorer.ProjectConfiguration.Id"; - const char DISPLAY_NAME_KEY[] = "ProjectExplorer.ProjectConfiguration.DisplayName"; - const char DEFAULT_DISPLAY_NAME_KEY[] = "ProjectExplorer.ProjectConfiguration.DefaultDisplayName"; - map.insert(QLatin1String(CONFIGURATION_ID_KEY), m_id.toSetting()); - map.insert(QLatin1String(DISPLAY_NAME_KEY), displayName()); - map.insert(QLatin1String(DEFAULT_DISPLAY_NAME_KEY), displayName()); - } - // Save build steps map.insert(QString::fromLatin1(STEPS_COUNT_KEY), m_steps.count()); for (int i = 0; i < m_steps.count(); ++i) diff --git a/src/plugins/projectexplorer/buildstepspage.cpp b/src/plugins/projectexplorer/buildstepspage.cpp index 241d2a9835..21fb4c2093 100644 --- a/src/plugins/projectexplorer/buildstepspage.cpp +++ b/src/plugins/projectexplorer/buildstepspage.cpp @@ -209,14 +209,14 @@ void BuildStepListWidget::updateAddBuildStepMenu() if (!factory->canHandle(m_buildStepList)) continue; - const BuildStepInfo &info = factory->stepInfo(); - if (info.flags & BuildStepInfo::Uncreatable) + const BuildStep::Flags flags = factory->stepFlags(); + if (flags & BuildStep::Uncreatable) continue; - if ((info.flags & BuildStepInfo::UniqueStep) && m_buildStepList->contains(info.id)) + if ((flags & BuildStep::UniqueStep) && m_buildStepList->contains(factory->stepId())) continue; - QAction *action = menu->addAction(info.displayName); + QAction *action = menu->addAction(factory->displayName()); connect(action, &QAction::triggered, this, [factory, this] { BuildStep *newStep = factory->create(m_buildStepList); QTC_ASSERT(newStep, return); diff --git a/src/plugins/projectexplorer/buildsystem.cpp b/src/plugins/projectexplorer/buildsystem.cpp index 0a2c2f3a85..9a120a9863 100644 --- a/src/plugins/projectexplorer/buildsystem.cpp +++ b/src/plugins/projectexplorer/buildsystem.cpp @@ -7,9 +7,9 @@ #include "extracompiler.h" #include "projectexplorer.h" #include "projectexplorertr.h" +#include "projectmanager.h" #include "runconfiguration.h" #include "runcontrol.h" -#include "session.h" #include "target.h" #include <coreplugin/messagemanager.h> @@ -64,7 +64,7 @@ BuildSystem::BuildSystem(Target *target) connect(&d->m_delayedParsingTimer, &QTimer::timeout, this, [this] { - if (SessionManager::hasProject(project())) + if (ProjectManager::hasProject(project())) triggerParsing(); else requestDelayedParse(); @@ -325,7 +325,6 @@ void BuildSystem::setDeploymentData(const DeploymentData &deploymentData) if (d->m_deploymentData != deploymentData) { d->m_deploymentData = deploymentData; emit deploymentDataChanged(); - emit applicationTargetsChanged(); emit target()->deploymentDataChanged(); } } @@ -337,10 +336,7 @@ DeploymentData BuildSystem::deploymentData() const void BuildSystem::setApplicationTargets(const QList<BuildTargetInfo> &appTargets) { - if (Utils::toSet(appTargets) != Utils::toSet(d->m_appTargets)) { - d->m_appTargets = appTargets; - emit applicationTargetsChanged(); - } + d->m_appTargets = appTargets; } const QList<BuildTargetInfo> BuildSystem::applicationTargets() const diff --git a/src/plugins/projectexplorer/buildsystem.h b/src/plugins/projectexplorer/buildsystem.h index 0d19c9110e..1cd1e41a06 100644 --- a/src/plugins/projectexplorer/buildsystem.h +++ b/src/plugins/projectexplorer/buildsystem.h @@ -152,7 +152,6 @@ signals: void parsingStarted(); void parsingFinished(bool success); void deploymentDataChanged(); - void applicationTargetsChanged(); void testInformationUpdated(); protected: diff --git a/src/plugins/projectexplorer/codestylesettingspropertiespage.cpp b/src/plugins/projectexplorer/codestylesettingspropertiespage.cpp index 2ff0285a61..f7ed131e57 100644 --- a/src/plugins/projectexplorer/codestylesettingspropertiespage.cpp +++ b/src/plugins/projectexplorer/codestylesettingspropertiespage.cpp @@ -48,12 +48,13 @@ CodeStyleSettingsWidget::CodeStyleSettingsWidget(Project *project) connect(languageComboBox, &QComboBox::currentIndexChanged, stackedWidget, &QStackedWidget::setCurrentIndex); - using namespace Utils::Layouting; + using namespace Layouting; Column { Row { new QLabel(Tr::tr("Language:")), languageComboBox, st }, - stackedWidget - }.attachTo(this, WithoutMargins); + stackedWidget, + noMargin + }.attachTo(this); } } // ProjectExplorer::Internal diff --git a/src/plugins/projectexplorer/compileoutputwindow.cpp b/src/plugins/projectexplorer/compileoutputwindow.cpp index f345792ca8..bafc31fc14 100644 --- a/src/plugins/projectexplorer/compileoutputwindow.cpp +++ b/src/plugins/projectexplorer/compileoutputwindow.cpp @@ -17,7 +17,9 @@ #include <texteditor/texteditorsettings.h> #include <texteditor/fontsettings.h> #include <texteditor/behaviorsettings.h> + #include <utils/algorithm.h> +#include <utils/layoutbuilder.h> #include <utils/outputformatter.h> #include <utils/proxyaction.h> #include <utils/theme/theme.h> @@ -43,9 +45,6 @@ namespace Internal { const char SETTINGS_KEY[] = "ProjectExplorer/CompileOutput/Zoom"; const char C_COMPILE_OUTPUT[] = "ProjectExplorer.CompileOutput"; -const char POP_UP_KEY[] = "ProjectExplorer/Settings/ShowCompilerOutput"; -const char WRAP_OUTPUT_KEY[] = "ProjectExplorer/Settings/WrapBuildOutput"; -const char MAX_LINES_KEY[] = "ProjectExplorer/Settings/MaxBuildOutputLines"; const char OPTIONS_PAGE_ID[] = "C.ProjectExplorer.CompileOutputOptions"; CompileOutputWindow::CompileOutputWindow(QAction *cancelBuildAction) : @@ -101,8 +100,17 @@ CompileOutputWindow::CompileOutputWindow(QAction *cancelBuildAction) : Tr::tr("O")); ExtensionSystem::PluginManager::addObject(m_handler); setupContext(C_COMPILE_OUTPUT, m_outputWindow); - loadSettings(); updateFromSettings(); + + m_outputWindow->setWordWrapEnabled(m_settings.wrapOutput()); + m_outputWindow->setMaxCharCount(m_settings.maxCharCount()); + + connect(&m_settings.wrapOutput, &Utils::BaseAspect::changed, m_outputWindow, [this] { + m_outputWindow->setWordWrapEnabled(m_settings.wrapOutput()); + }); + connect(&m_settings.maxCharCount, &Utils::BaseAspect::changed, m_outputWindow, [this] { + m_outputWindow->setMaxCharCount(m_settings.maxCharCount()); + }); } CompileOutputWindow::~CompileOutputWindow() @@ -115,10 +123,7 @@ CompileOutputWindow::~CompileOutputWindow() void CompileOutputWindow::updateFromSettings() { - m_outputWindow->setWordWrapEnabled(m_settings.wrapOutput); - m_outputWindow->setMaxCharCount(m_settings.maxCharCount); } - bool CompileOutputWindow::hasFocus() const { return m_outputWindow->window()->focusWidget() == m_outputWindow; @@ -213,13 +218,6 @@ void CompileOutputWindow::reset() m_outputWindow->reset(); } -void CompileOutputWindow::setSettings(const CompileOutputSettings &settings) -{ - m_settings = settings; - storeSettings(); - updateFromSettings(); -} - Utils::OutputFormatter *CompileOutputWindow::outputFormatter() const { return m_outputWindow->outputFormatter(); @@ -231,75 +229,49 @@ void CompileOutputWindow::updateFilter() filterUsesRegexp(), filterIsInverted()); } -const bool kPopUpDefault = false; -const bool kWrapOutputDefault = true; +// CompileOutputSettings -void CompileOutputWindow::loadSettings() -{ - QSettings * const s = Core::ICore::settings(); - m_settings.popUp = s->value(POP_UP_KEY, kPopUpDefault).toBool(); - m_settings.wrapOutput = s->value(WRAP_OUTPUT_KEY, kWrapOutputDefault).toBool(); - m_settings.maxCharCount = s->value(MAX_LINES_KEY, - Core::Constants::DEFAULT_MAX_CHAR_COUNT).toInt() * 100; -} +static CompileOutputSettings *s_compileOutputSettings; -void CompileOutputWindow::storeSettings() const +CompileOutputSettings &CompileOutputSettings::instance() { - Utils::QtcSettings *const s = Core::ICore::settings(); - s->setValueWithDefault(POP_UP_KEY, m_settings.popUp, kPopUpDefault); - s->setValueWithDefault(WRAP_OUTPUT_KEY, m_settings.wrapOutput, kWrapOutputDefault); - s->setValueWithDefault(MAX_LINES_KEY, - m_settings.maxCharCount / 100, - Core::Constants::DEFAULT_MAX_CHAR_COUNT); + return *s_compileOutputSettings; } -class CompileOutputSettingsWidget : public Core::IOptionsPageWidget +CompileOutputSettings::CompileOutputSettings() { -public: - CompileOutputSettingsWidget() - { - const CompileOutputSettings &settings = BuildManager::compileOutputSettings(); - m_wrapOutputCheckBox.setText(Tr::tr("Word-wrap output")); - m_wrapOutputCheckBox.setChecked(settings.wrapOutput); - m_popUpCheckBox.setText(Tr::tr("Open Compile Output when building")); - m_popUpCheckBox.setChecked(settings.popUp); - m_maxCharsBox.setMaximum(100000000); - m_maxCharsBox.setValue(settings.maxCharCount); - const auto layout = new QVBoxLayout(this); - layout->addWidget(&m_wrapOutputCheckBox); - layout->addWidget(&m_popUpCheckBox); - const auto maxCharsLayout = new QHBoxLayout; - const QString msg = Tr::tr("Limit output to %1 characters"); - const QStringList parts = msg.split("%1") << QString() << QString(); - maxCharsLayout->addWidget(new QLabel(parts.at(0).trimmed())); - maxCharsLayout->addWidget(&m_maxCharsBox); - maxCharsLayout->addWidget(new QLabel(parts.at(1).trimmed())); - maxCharsLayout->addStretch(1); - layout->addLayout(maxCharsLayout); - layout->addStretch(1); - } - - void apply() final - { - CompileOutputSettings s; - s.wrapOutput = m_wrapOutputCheckBox.isChecked(); - s.popUp = m_popUpCheckBox.isChecked(); - s.maxCharCount = m_maxCharsBox.value(); - BuildManager::setCompileOutputSettings(s); - } - -private: - QCheckBox m_wrapOutputCheckBox; - QCheckBox m_popUpCheckBox; - QSpinBox m_maxCharsBox; -}; + s_compileOutputSettings = this; -CompileOutputSettingsPage::CompileOutputSettingsPage() -{ setId(OPTIONS_PAGE_ID); setDisplayName(Tr::tr("Compile Output")); setCategory(Constants::BUILD_AND_RUN_SETTINGS_CATEGORY); - setWidgetCreator([] { return new CompileOutputSettingsWidget; }); + + wrapOutput.setSettingsKey("ProjectExplorer/Settings/WrapBuildOutput"); + wrapOutput.setDefaultValue(true); + wrapOutput.setLabelText(Tr::tr("Word-wrap output")); + + popUp.setSettingsKey("ProjectExplorer/Settings/ShowCompilerOutput"); + popUp.setLabelText(Tr::tr("Open Compile Output when building")); + + maxCharCount.setSettingsKey("ProjectExplorer/Settings/MaxBuildOutputLines"); + maxCharCount.setRange(1, Core::Constants::DEFAULT_MAX_CHAR_COUNT); + maxCharCount.setDefaultValue(Core::Constants::DEFAULT_MAX_CHAR_COUNT); + maxCharCount.setToSettingsTransformation([](const QVariant &v) { return v.toInt() / 100; }); + maxCharCount.setFromSettingsTransformation([](const QVariant &v) { return v.toInt() * 100; }); + + setLayouter([this] { + using namespace Layouting; + const QString msg = Tr::tr("Limit output to %1 characters"); + const QStringList parts = msg.split("%1") << QString() << QString(); + return Column { + wrapOutput, + popUp, + Row { parts.at(0), maxCharCount, parts.at(1), st }, + st + }; + }); + + readSettings(); } } // Internal diff --git a/src/plugins/projectexplorer/compileoutputwindow.h b/src/plugins/projectexplorer/compileoutputwindow.h index e1f000de9a..89c7b749f2 100644 --- a/src/plugins/projectexplorer/compileoutputwindow.h +++ b/src/plugins/projectexplorer/compileoutputwindow.h @@ -26,6 +26,18 @@ namespace Internal { class ShowOutputTaskHandler; class CompileOutputTextEdit; +class CompileOutputSettings final : public Core::PagedSettings +{ +public: + CompileOutputSettings(); + + static CompileOutputSettings &instance(); + + Utils::BoolAspect popUp{this}; + Utils::BoolAspect wrapOutput{this}; + Utils::IntegerAspect maxCharCount{this}; +}; + class CompileOutputWindow final : public Core::IOutputPane { Q_OBJECT @@ -57,19 +69,13 @@ public: void flush(); void reset(); - const CompileOutputSettings &settings() const { return m_settings; } - void setSettings(const CompileOutputSettings &settings); - Utils::OutputFormatter *outputFormatter() const; private: void updateFilter() override; const QList<Core::OutputWindow *> outputWindows() const override { return {m_outputWindow}; } - void loadSettings(); - void storeSettings() const; void updateFromSettings(); - Core::OutputWindow *m_outputWindow; ShowOutputTaskHandler *m_handler; QToolButton *m_cancelBuildButton; @@ -77,11 +83,5 @@ private: CompileOutputSettings m_settings; }; -class CompileOutputSettingsPage final : public Core::IOptionsPage -{ -public: - CompileOutputSettingsPage(); -}; - } // namespace Internal } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/copystep.cpp b/src/plugins/projectexplorer/copystep.cpp new file mode 100644 index 0000000000..db3b16098e --- /dev/null +++ b/src/plugins/projectexplorer/copystep.cpp @@ -0,0 +1,114 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "copystep.h" + +#include "projectexplorerconstants.h" +#include "projectexplorertr.h" + +#include <utils/aspects.h> + +using namespace Utils; + +namespace ProjectExplorer::Internal { + +const char SOURCE_KEY[] = "ProjectExplorer.CopyStep.Source"; +const char TARGET_KEY[] = "ProjectExplorer.CopyStep.Target"; + +class CopyStepBase : public BuildStep +{ +public: + CopyStepBase(BuildStepList *bsl, Id id) + : BuildStep(bsl, id) + { + m_sourceAspect.setSettingsKey(SOURCE_KEY); + m_sourceAspect.setLabelText(Tr::tr("Source:")); + + m_targetAspect.setSettingsKey(TARGET_KEY); + m_targetAspect.setLabelText(Tr::tr("Target:")); + + addMacroExpander(); + } + +protected: + bool init() final + { + m_source = m_sourceAspect(); + m_target = m_targetAspect(); + return m_source.exists(); + } + + void doRun() final + { + // FIXME: asyncCopy does not handle directories yet. + QTC_ASSERT(m_source.isFile(), emit finished(false)); + m_source.asyncCopy(m_target, this, [this](const expected_str<void> &cont) { + if (!cont) { + addOutput(cont.error(), OutputFormat::ErrorMessage); + addOutput(Tr::tr("Copying failed"), OutputFormat::ErrorMessage); + emit finished(false); + } else { + addOutput(Tr::tr("Copying finished"), OutputFormat::NormalMessage); + emit finished(true); + } + }); + } + + FilePathAspect m_sourceAspect{this}; + FilePathAspect m_targetAspect{this}; + +private: + FilePath m_source; + FilePath m_target; +}; + +class CopyFileStep final : public CopyStepBase +{ +public: + CopyFileStep(BuildStepList *bsl, Id id) + : CopyStepBase(bsl, id) + { + // Expected kind could be stricter in theory, but since this here is + // a last stand fallback, better not impose extra "nice to have" + // work on the system. + m_sourceAspect.setExpectedKind(PathChooser::Any); // "File" + m_targetAspect.setExpectedKind(PathChooser::Any); // "SaveFile" + + setSummaryUpdater([] { + return QString("<b>" + Tr::tr("Copy file") + "</b>"); + }); + } +}; + +class CopyDirectoryStep final : public CopyStepBase +{ +public: + CopyDirectoryStep(BuildStepList *bsl, Id id) + : CopyStepBase(bsl, id) + { + m_sourceAspect.setExpectedKind(PathChooser::Directory); + m_targetAspect.setExpectedKind(PathChooser::Directory); + + setSummaryUpdater([] { + return QString("<b>" + Tr::tr("Copy directory recursively") + "</b>"); + }); + } +}; + +// Factories + +CopyFileStepFactory::CopyFileStepFactory() +{ + registerStep<CopyFileStep>(Constants::COPY_FILE_STEP); + //: Default CopyStep display name + setDisplayName(Tr::tr("Copy file")); +} + +CopyDirectoryStepFactory::CopyDirectoryStepFactory() +{ + registerStep<CopyDirectoryStep>(Constants::COPY_DIRECTORY_STEP); + //: Default CopyStep display name + setDisplayName(Tr::tr("Copy directory recursively")); +} + +} // ProjectExplorer::Internal diff --git a/src/plugins/projectexplorer/copystep.h b/src/plugins/projectexplorer/copystep.h new file mode 100644 index 0000000000..07940f3a89 --- /dev/null +++ b/src/plugins/projectexplorer/copystep.h @@ -0,0 +1,22 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "buildstep.h" + +namespace ProjectExplorer::Internal { + +class CopyFileStepFactory final : public BuildStepFactory +{ +public: + CopyFileStepFactory(); +}; + +class CopyDirectoryStepFactory final : public BuildStepFactory +{ +public: + CopyDirectoryStepFactory(); +}; + +} // ProjectExplorer::Internal diff --git a/src/plugins/projectexplorer/currentprojectfilter.cpp b/src/plugins/projectexplorer/currentprojectfilter.cpp index 69b88ddf46..d0f9ed99b0 100644 --- a/src/plugins/projectexplorer/currentprojectfilter.cpp +++ b/src/plugins/projectexplorer/currentprojectfilter.cpp @@ -7,42 +7,28 @@ #include "projectexplorertr.h" #include "projecttree.h" -#include <utils/algorithm.h> - using namespace Core; using namespace ProjectExplorer; using namespace ProjectExplorer::Internal; +using namespace Utils; CurrentProjectFilter::CurrentProjectFilter() - : BaseFileFilter() { setId("Files in current project"); setDisplayName(Tr::tr("Files in Current Project")); - setDescription(Tr::tr("Matches all files from the current document's project. Append \"+<number>\" " - "or \":<number>\" to jump to the given line number. Append another " - "\"+<number>\" or \":<number>\" to jump to the column number as well.")); + setDescription(Tr::tr("Locates files from the current document's project. Append \"+<number>\" " + "or \":<number>\" to jump to the given line number. Append another " + "\"+<number>\" or \":<number>\" to jump to the column number as well.")); setDefaultShortcutString("p"); - setDefaultIncludedByDefault(false); + setRefreshRecipe(Tasking::Sync([this] { invalidate(); })); connect(ProjectTree::instance(), &ProjectTree::currentProjectChanged, this, &CurrentProjectFilter::currentProjectChanged); -} - -void CurrentProjectFilter::markFilesAsOutOfDate() -{ - setFileIterator(nullptr); -} -void CurrentProjectFilter::prepareSearch(const QString &entry) -{ - Q_UNUSED(entry) - if (!fileIterator()) { - Utils::FilePaths paths; - if (m_project) - paths = m_project->files(Project::SourceFiles); - setFileIterator(new BaseFileFilter::ListIterator(paths)); - } - BaseFileFilter::prepareSearch(entry); + m_cache.setGeneratorProvider([this] { + const FilePaths paths = m_project ? m_project->files(Project::SourceFiles) : FilePaths(); + return LocatorFileCache::filePathsGenerator(paths); + }); } void CurrentProjectFilter::currentProjectChanged() @@ -50,21 +36,12 @@ void CurrentProjectFilter::currentProjectChanged() Project *project = ProjectTree::currentProject(); if (project == m_project) return; - if (m_project) - disconnect(m_project, &Project::fileListChanged, - this, &CurrentProjectFilter::markFilesAsOutOfDate); - - if (project) - connect(project, &Project::fileListChanged, - this, &CurrentProjectFilter::markFilesAsOutOfDate); + if (m_project) + disconnect(m_project, &Project::fileListChanged, this, &CurrentProjectFilter::invalidate); m_project = project; - markFilesAsOutOfDate(); -} + if (m_project) + connect(m_project, &Project::fileListChanged, this, &CurrentProjectFilter::invalidate); -void CurrentProjectFilter::refresh(QFutureInterface<void> &future) -{ - Q_UNUSED(future) - QMetaObject::invokeMethod(this, &CurrentProjectFilter::markFilesAsOutOfDate, - Qt::QueuedConnection); + invalidate(); } diff --git a/src/plugins/projectexplorer/currentprojectfilter.h b/src/plugins/projectexplorer/currentprojectfilter.h index b9d63db293..6c50070790 100644 --- a/src/plugins/projectexplorer/currentprojectfilter.h +++ b/src/plugins/projectexplorer/currentprojectfilter.h @@ -3,31 +3,24 @@ #pragma once -#include <coreplugin/locator/basefilefilter.h> +#include <coreplugin/locator/ilocatorfilter.h> -#include <QFutureInterface> +namespace ProjectExplorer { class Project; } -namespace ProjectExplorer { +namespace ProjectExplorer::Internal { -class Project; - -namespace Internal { - -class CurrentProjectFilter : public Core::BaseFileFilter +class CurrentProjectFilter : public Core::ILocatorFilter { - Q_OBJECT - public: CurrentProjectFilter(); - void refresh(QFutureInterface<void> &future) override; - void prepareSearch(const QString &entry) override; private: + Core::LocatorMatcherTasks matchers() final { return {m_cache.matcher()}; } void currentProjectChanged(); - void markFilesAsOutOfDate(); + void invalidate() { m_cache.invalidate(); } + Core::LocatorFileCache m_cache; Project *m_project = nullptr; }; -} // namespace Internal -} // namespace ProjectExplorer +} // namespace ProjectExplorer::Internal diff --git a/src/plugins/projectexplorer/currentprojectfind.cpp b/src/plugins/projectexplorer/currentprojectfind.cpp index 7ecbd99069..3bde2bb62f 100644 --- a/src/plugins/projectexplorer/currentprojectfind.cpp +++ b/src/plugins/projectexplorer/currentprojectfind.cpp @@ -5,8 +5,8 @@ #include "project.h" #include "projectexplorertr.h" +#include "projectmanager.h" #include "projecttree.h" -#include "session.h" #include <utils/qtcassert.h> #include <utils/filesearch.h> @@ -23,7 +23,7 @@ CurrentProjectFind::CurrentProjectFind() { connect(ProjectTree::instance(), &ProjectTree::currentProjectChanged, this, &CurrentProjectFind::handleProjectChanged); - connect(SessionManager::instance(), &SessionManager::projectDisplayNameChanged, + connect(ProjectManager::instance(), &ProjectManager::projectDisplayNameChanged, this, [this](ProjectExplorer::Project *p) { if (p == ProjectTree::currentProject()) emit displayNameChanged(); @@ -61,14 +61,13 @@ FileIterator *CurrentProjectFind::files(const QStringList &nameFilters, const QStringList &exclusionFilters, const QVariant &additionalParameters) const { - QTC_ASSERT(additionalParameters.isValid(), - return new FileListIterator(FilePaths(), QList<QTextCodec *>())); + QTC_ASSERT(additionalParameters.isValid(), return new FileListIterator); const FilePath projectFile = FilePath::fromVariant(additionalParameters); - for (Project *project : SessionManager::projects()) { + for (Project *project : ProjectManager::projects()) { if (project && projectFile == project->projectFilePath()) return filesForProjects(nameFilters, exclusionFilters, {project}); } - return new FileListIterator(FilePaths(), QList<QTextCodec *>()); + return new FileListIterator; } QString CurrentProjectFind::label() const @@ -87,7 +86,7 @@ void CurrentProjectFind::handleProjectChanged() void CurrentProjectFind::recheckEnabled(Core::SearchResult *search) { const FilePath projectFile = FilePath::fromVariant(getAdditionalParameters(search)); - for (Project *project : SessionManager::projects()) { + for (Project *project : ProjectManager::projects()) { if (projectFile == project->projectFilePath()) { search->setSearchAgainEnabled(true); return; diff --git a/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp b/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp index 9d25bb7dfe..00f7225d0d 100644 --- a/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp +++ b/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp @@ -3,7 +3,6 @@ #include "customexecutablerunconfiguration.h" -#include "localenvironmentaspect.h" #include "projectexplorerconstants.h" #include "projectexplorertr.h" #include "runconfigurationaspects.h" @@ -24,7 +23,8 @@ CustomExecutableRunConfiguration::CustomExecutableRunConfiguration(Target *targe CustomExecutableRunConfiguration::CustomExecutableRunConfiguration(Target *target, Id id) : RunConfiguration(target, id) { - auto envAspect = addAspect<LocalEnvironmentAspect>(target); + auto envAspect = addAspect<EnvironmentAspect>(); + envAspect->setSupportForBuildEnvironment(target); auto exeAspect = addAspect<ExecutableAspect>(target, ExecutableAspect::HostDevice); exeAspect->setSettingsKey("ProjectExplorer.CustomExecutableRunConfiguration.Executable"); diff --git a/src/plugins/projectexplorer/customparserconfigdialog.cpp b/src/plugins/projectexplorer/customparserconfigdialog.cpp index 4ebc880c14..cbfd1adfbc 100644 --- a/src/plugins/projectexplorer/customparserconfigdialog.cpp +++ b/src/plugins/projectexplorer/customparserconfigdialog.cpp @@ -101,7 +101,7 @@ CustomParserConfigDialog::CustomParserConfigDialog(QWidget *parent) auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); - using namespace Utils::Layouting; + using namespace Layouting; auto tabWarning = new QWidget; Column { diff --git a/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp b/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp index f2b33378b5..7d1174839d 100644 --- a/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp +++ b/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp @@ -8,7 +8,7 @@ #include <utils/environment.h> #include <utils/fileutils.h> #include <utils/hostosinfo.h> -#include <utils/qtcprocess.h> +#include <utils/process.h> #include <utils/temporarydirectory.h> #include <QFileInfo> @@ -64,7 +64,7 @@ static bool const QMap<QString, QString> &fieldMap, QString *stdOut /* = 0 */, QString *errorMessage) { - Utils::QtcProcess process; + Utils::Process process; const QString binary = script.front(); QStringList arguments; const int binarySize = script.size(); diff --git a/src/plugins/projectexplorer/dependenciespanel.cpp b/src/plugins/projectexplorer/dependenciespanel.cpp index 1799126a5b..daac52b8d7 100644 --- a/src/plugins/projectexplorer/dependenciespanel.cpp +++ b/src/plugins/projectexplorer/dependenciespanel.cpp @@ -5,9 +5,10 @@ #include "project.h" #include "projectexplorertr.h" -#include "session.h" +#include "projectmanager.h" #include <coreplugin/icore.h> +#include <coreplugin/session.h> #include <utils/algorithm.h> #include <utils/detailswidget.h> @@ -32,19 +33,18 @@ DependenciesModel::DependenciesModel(Project *project, QObject *parent) { resetModel(); - SessionManager *sessionManager = SessionManager::instance(); - connect(sessionManager, &SessionManager::projectRemoved, + connect(ProjectManager::instance(), &ProjectManager::projectRemoved, this, &DependenciesModel::resetModel); - connect(sessionManager, &SessionManager::projectAdded, + connect(ProjectManager::instance(), &ProjectManager::projectAdded, this, &DependenciesModel::resetModel); - connect(sessionManager, &SessionManager::sessionLoaded, + connect(Core::SessionManager::instance(), &Core::SessionManager::sessionLoaded, this, &DependenciesModel::resetModel); } void DependenciesModel::resetModel() { beginResetModel(); - m_projects = SessionManager::projects(); + m_projects = ProjectManager::projects(); m_projects.removeAll(m_project); Utils::sort(m_projects, [](Project *a, Project *b) { return a->displayName() < b->displayName(); @@ -77,7 +77,7 @@ QVariant DependenciesModel::data(const QModelIndex &index, int role) const case Qt::ToolTipRole: return p->projectFilePath().toUserOutput(); case Qt::CheckStateRole: - return SessionManager::hasDependency(m_project, p) ? Qt::Checked : Qt::Unchecked; + return ProjectManager::hasDependency(m_project, p) ? Qt::Checked : Qt::Unchecked; case Qt::DecorationRole: return Utils::FileIconProvider::icon(p->projectFilePath()); default: @@ -92,7 +92,7 @@ bool DependenciesModel::setData(const QModelIndex &index, const QVariant &value, const auto c = static_cast<Qt::CheckState>(value.toInt()); if (c == Qt::Checked) { - if (SessionManager::addDependency(m_project, p)) { + if (ProjectManager::addDependency(m_project, p)) { emit dataChanged(index, index); return true; } else { @@ -100,8 +100,8 @@ bool DependenciesModel::setData(const QModelIndex &index, const QVariant &value, Tr::tr("This would create a circular dependency.")); } } else if (c == Qt::Unchecked) { - if (SessionManager::hasDependency(m_project, p)) { - SessionManager::removeDependency(m_project, p); + if (ProjectManager::hasDependency(m_project, p)) { + ProjectManager::removeDependency(m_project, p); emit dataChanged(index, index); return true; } @@ -215,9 +215,9 @@ DependenciesWidget::DependenciesWidget(Project *project, QWidget *parent) : Proj m_cascadeSetActiveCheckBox = new QCheckBox; m_cascadeSetActiveCheckBox->setText(Tr::tr("Synchronize configuration")); m_cascadeSetActiveCheckBox->setToolTip(Tr::tr("Synchronize active kit, build, and deploy configuration between projects.")); - m_cascadeSetActiveCheckBox->setChecked(SessionManager::isProjectConfigurationCascading()); + m_cascadeSetActiveCheckBox->setChecked(ProjectManager::isProjectConfigurationCascading()); connect(m_cascadeSetActiveCheckBox, &QCheckBox::toggled, - SessionManager::instance(), &SessionManager::setProjectConfigurationCascading); + ProjectManager::instance(), &ProjectManager::setProjectConfigurationCascading); layout->addWidget(m_cascadeSetActiveCheckBox, 1, 0, 2, 1); } diff --git a/src/plugins/projectexplorer/desktoprunconfiguration.cpp b/src/plugins/projectexplorer/desktoprunconfiguration.cpp index 82d0123b11..8b8cdf2b79 100644 --- a/src/plugins/projectexplorer/desktoprunconfiguration.cpp +++ b/src/plugins/projectexplorer/desktoprunconfiguration.cpp @@ -4,7 +4,6 @@ #include "desktoprunconfiguration.h" #include "buildsystem.h" -#include "localenvironmentaspect.h" #include "projectexplorerconstants.h" #include "projectexplorertr.h" #include "runconfigurationaspects.h" @@ -43,7 +42,8 @@ private: DesktopRunConfiguration::DesktopRunConfiguration(Target *target, Id id, Kind kind) : RunConfiguration(target, id), m_kind(kind) { - auto envAspect = addAspect<LocalEnvironmentAspect>(target); + auto envAspect = addAspect<EnvironmentAspect>(); + envAspect->setSupportForBuildEnvironment(target); addAspect<ExecutableAspect>(target, ExecutableAspect::RunDevice); addAspect<ArgumentsAspect>(macroExpander()); @@ -87,7 +87,8 @@ void DesktopRunConfiguration::updateTargetInformation() BuildTargetInfo bti = buildTargetInfo(); auto terminalAspect = aspect<TerminalAspect>(); - terminalAspect->setUseTerminalHint(bti.usesTerminal); + terminalAspect->setUseTerminalHint(bti.targetFilePath.needsDevice() ? false : bti.usesTerminal); + terminalAspect->setEnabled(!bti.targetFilePath.needsDevice()); if (m_kind == Qmake) { @@ -121,7 +122,7 @@ void DesktopRunConfiguration::updateTargetInformation() aspect<ExecutableAspect>()->setExecutable(bti.targetFilePath); aspect<WorkingDirectoryAspect>()->setDefaultWorkingDirectory(bti.workingDirectory); - emit aspect<LocalEnvironmentAspect>()->environmentChanged(); + emit aspect<EnvironmentAspect>()->environmentChanged(); } } diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp index a09c8bac76..099040d6b2 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp @@ -3,11 +3,11 @@ #include "desktopdevice.h" -#include "desktopprocesssignaloperation.h" -#include "deviceprocesslist.h" -#include "localprocesslist.h" #include "../projectexplorerconstants.h" #include "../projectexplorertr.h" +#include "desktopprocesssignaloperation.h" +#include "deviceprocesslist.h" +#include "processlist.h" #include <coreplugin/fileutils.h> @@ -15,18 +15,19 @@ #include <utils/environment.h> #include <utils/hostosinfo.h> #include <utils/portlist.h> +#include <utils/process.h> #include <utils/qtcassert.h> -#include <utils/qtcprocess.h> #include <utils/terminalcommand.h> +#include <utils/terminalhooks.h> #include <utils/url.h> #include <QCoreApplication> #include <QDateTime> #ifdef Q_OS_WIN -#include <windows.h> -#include <stdlib.h> #include <cstring> +#include <stdlib.h> +#include <windows.h> #endif using namespace ProjectExplorer::Constants; @@ -34,58 +35,11 @@ using namespace Utils; namespace ProjectExplorer { -static void startTerminalEmulator(const QString &workingDir, const Environment &env) -{ -#ifdef Q_OS_WIN - STARTUPINFO si; - ZeroMemory(&si, sizeof(si)); - si.cb = sizeof(si); - - PROCESS_INFORMATION pinfo; - ZeroMemory(&pinfo, sizeof(pinfo)); - - static const auto quoteWinCommand = [](const QString &program) { - const QChar doubleQuote = QLatin1Char('"'); - - // add the program as the first arg ... it works better - QString programName = program; - programName.replace(QLatin1Char('/'), QLatin1Char('\\')); - if (!programName.startsWith(doubleQuote) && !programName.endsWith(doubleQuote) - && programName.contains(QLatin1Char(' '))) { - programName.prepend(doubleQuote); - programName.append(doubleQuote); - } - return programName; - }; - const QString cmdLine = quoteWinCommand(qtcEnvironmentVariable("COMSPEC")); - // cmdLine is assumed to be detached - - // https://blogs.msdn.microsoft.com/oldnewthing/20090601-00/?p=18083 - - const QString totalEnvironment = env.toStringList().join(QChar(QChar::Null)) + QChar(QChar::Null); - LPVOID envPtr = (env != Environment::systemEnvironment()) - ? (WCHAR *)(totalEnvironment.utf16()) : nullptr; - - const bool success = CreateProcessW(0, (WCHAR *)cmdLine.utf16(), - 0, 0, FALSE, CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT, - envPtr, workingDir.isEmpty() ? 0 : (WCHAR *)workingDir.utf16(), - &si, &pinfo); - - if (success) { - CloseHandle(pinfo.hThread); - CloseHandle(pinfo.hProcess); - } -#else - const TerminalCommand term = TerminalCommand::terminalEmulator(); - QProcess process; - process.setProgram(term.command.nativePath()); - process.setArguments(ProcessArgs::splitArgs(term.openArgs)); - process.setProcessEnvironment(env.toProcessEnvironment()); - process.setWorkingDirectory(workingDir); - process.startDetached(); -#endif -} +class DesktopDevicePrivate : public QObject +{}; DesktopDevice::DesktopDevice() + : d(new DesktopDevicePrivate()) { setFileAccess(DesktopDeviceFileAccess::instance()); @@ -98,20 +52,26 @@ DesktopDevice::DesktopDevice() setMachineType(IDevice::Hardware); setOsType(HostOsInfo::hostOs()); - const QString portRange = - QString::fromLatin1("%1-%2").arg(DESKTOP_PORT_START).arg(DESKTOP_PORT_END); + const QString portRange + = QString::fromLatin1("%1-%2").arg(DESKTOP_PORT_START).arg(DESKTOP_PORT_END); setFreePorts(Utils::PortList::fromString(portRange)); setOpenTerminal([](const Environment &env, const FilePath &path) { - const QFileInfo fileInfo = path.toFileInfo(); - const QString workingDir = QDir::toNativeSeparators(fileInfo.isDir() ? - fileInfo.absoluteFilePath() : - fileInfo.absolutePath()); const Environment realEnv = env.hasChanges() ? env : Environment::systemEnvironment(); - startTerminalEmulator(workingDir, realEnv); + + const FilePath shell = Terminal::defaultShellForDevice(path); + + Process process; + process.setTerminalMode(TerminalMode::Detached); + process.setEnvironment(realEnv); + process.setCommand({shell, {}}); + process.setWorkingDirectory(path); + process.start(); }); } +DesktopDevice::~DesktopDevice() = default; + IDevice::DeviceInfo DesktopDevice::deviceInformation() const { return DeviceInfo(); @@ -125,11 +85,6 @@ IDeviceWidget *DesktopDevice::createWidget() // range can be confusing to the user. Hence, disabling the widget for now. } -bool DesktopDevice::canAutoDetectPorts() const -{ - return true; -} - bool DesktopDevice::canCreateProcessModel() const { return true; @@ -137,7 +92,7 @@ bool DesktopDevice::canCreateProcessModel() const DeviceProcessList *DesktopDevice::createProcessListModel(QObject *parent) const { - return new Internal::LocalProcessList(sharedFromThis(), parent); + return new ProcessList(sharedFromThis(), parent); } DeviceProcessSignalOperation::Ptr DesktopDevice::signalOperation() const @@ -145,31 +100,6 @@ DeviceProcessSignalOperation::Ptr DesktopDevice::signalOperation() const return DeviceProcessSignalOperation::Ptr(new DesktopProcessSignalOperation()); } -PortsGatheringMethod DesktopDevice::portsGatheringMethod() const -{ - return { - [this](QAbstractSocket::NetworkLayerProtocol protocol) -> CommandLine { - // We might encounter the situation that protocol is given IPv6 - // but the consumer of the free port information decides to open - // an IPv4(only) port. As a result the next IPv6 scan will - // report the port again as open (in IPv6 namespace), while the - // same port in IPv4 namespace might still be blocked, and - // re-use of this port fails. - // GDBserver behaves exactly like this. - - Q_UNUSED(protocol) - - if (HostOsInfo::isWindowsHost() || HostOsInfo::isMacHost()) - return {filePath("netstat"), {"-a", "-n"}}; - if (HostOsInfo::isLinuxHost()) - return {filePath("/bin/sh"), {"-c", "cat /proc/net/tcp*"}}; - return {}; - }, - - &Port::parseFromNetstatOutput - }; -} - QUrl DesktopDevice::toolControlChannel(const ControlChannelHint &) const { QUrl url; diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.h b/src/plugins/projectexplorer/devicesupport/desktopdevice.h index ee5ac1ca5e..d9cafbfda9 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopdevice.h +++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.h @@ -6,25 +6,27 @@ #include "../projectexplorer_export.h" #include "idevice.h" -#include "idevicefactory.h" #include <QApplication> +#include <memory> + namespace ProjectExplorer { class ProjectExplorerPlugin; +class DesktopDevicePrivate; namespace Internal { class DesktopDeviceFactory; } class PROJECTEXPLORER_EXPORT DesktopDevice : public IDevice { public: + ~DesktopDevice() override; + IDevice::DeviceInfo deviceInformation() const override; IDeviceWidget *createWidget() override; - bool canAutoDetectPorts() const override; bool canCreateProcessModel() const override; DeviceProcessList *createProcessListModel(QObject *parent) const override; - ProjectExplorer::PortsGatheringMethod portsGatheringMethod() const override; DeviceProcessSignalOperation::Ptr signalOperation() const override; QUrl toolControlChannel(const ControlChannelHint &) const override; bool usableAsBuildDevice() const override; @@ -40,6 +42,8 @@ protected: friend class ProjectExplorerPlugin; friend class Internal::DesktopDeviceFactory; + + std::unique_ptr<DesktopDevicePrivate> d; }; } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.cpp b/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.cpp index acce8bf29b..84e3b27408 100644 --- a/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.cpp @@ -4,6 +4,7 @@ #include "devicecheckbuildstep.h" #include "../kitinformation.h" +#include "../projectexplorerconstants.h" #include "../projectexplorertr.h" #include "devicemanager.h" @@ -12,60 +13,61 @@ #include <QMessageBox> -using namespace ProjectExplorer; +namespace ProjectExplorer { -DeviceCheckBuildStep::DeviceCheckBuildStep(BuildStepList *bsl, Utils::Id id) - : BuildStep(bsl, id) +class DeviceCheckBuildStep : public BuildStep { - setWidgetExpandedByDefault(false); -} +public: + DeviceCheckBuildStep(BuildStepList *bsl, Utils::Id id) + : BuildStep(bsl, id) + { + setWidgetExpandedByDefault(false); + } -bool DeviceCheckBuildStep::init() -{ - IDevice::ConstPtr device = DeviceKitAspect::device(kit()); - if (!device) { - Utils::Id deviceTypeId = DeviceTypeKitAspect::deviceTypeId(kit()); - IDeviceFactory *factory = IDeviceFactory::find(deviceTypeId); - if (!factory || !factory->canCreate()) { - emit addOutput(Tr::tr("No device configured."), BuildStep::OutputFormat::ErrorMessage); - return false; - } + bool init() override + { + IDevice::ConstPtr device = DeviceKitAspect::device(kit()); + if (!device) { + Utils::Id deviceTypeId = DeviceTypeKitAspect::deviceTypeId(kit()); + IDeviceFactory *factory = IDeviceFactory::find(deviceTypeId); + if (!factory || !factory->canCreate()) { + emit addOutput(Tr::tr("No device configured."), BuildStep::OutputFormat::ErrorMessage); + return false; + } - QMessageBox msgBox(QMessageBox::Question, Tr::tr("Set Up Device"), - Tr::tr("There is no device set up for this kit. Do you want to add a device?"), - QMessageBox::Yes|QMessageBox::No); - msgBox.setDefaultButton(QMessageBox::Yes); - if (msgBox.exec() == QMessageBox::No) { - emit addOutput(Tr::tr("No device configured."), BuildStep::OutputFormat::ErrorMessage); - return false; - } + QMessageBox msgBox(QMessageBox::Question, Tr::tr("Set Up Device"), + Tr::tr("There is no device set up for this kit. Do you want to add a device?"), + QMessageBox::Yes|QMessageBox::No); + msgBox.setDefaultButton(QMessageBox::Yes); + if (msgBox.exec() == QMessageBox::No) { + emit addOutput(Tr::tr("No device configured."), BuildStep::OutputFormat::ErrorMessage); + return false; + } - IDevice::Ptr newDevice = factory->create(); - if (newDevice.isNull()) { - emit addOutput(Tr::tr("No device configured."), BuildStep::OutputFormat::ErrorMessage); - return false; - } + IDevice::Ptr newDevice = factory->create(); + if (newDevice.isNull()) { + emit addOutput(Tr::tr("No device configured."), BuildStep::OutputFormat::ErrorMessage); + return false; + } - DeviceManager *dm = DeviceManager::instance(); - dm->addDevice(newDevice); + DeviceManager *dm = DeviceManager::instance(); + dm->addDevice(newDevice); - DeviceKitAspect::setDevice(kit(), newDevice); + DeviceKitAspect::setDevice(kit(), newDevice); + } + + return true; } - return true; -} + void doRun() override { emit finished(true); } +}; -void DeviceCheckBuildStep::doRun() -{ - emit finished(true); -} +// Factory -Utils::Id DeviceCheckBuildStep::stepId() +DeviceCheckBuildStepFactory::DeviceCheckBuildStepFactory() { - return "ProjectExplorer.DeviceCheckBuildStep"; + registerStep<DeviceCheckBuildStep>(Constants::DEVICE_CHECK_STEP); + setDisplayName(Tr::tr("Check for a configured device")); } -QString DeviceCheckBuildStep::displayName() -{ - return Tr::tr("Check for a configured device"); -} +} // ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.h b/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.h index 8b7d3687b0..6b7a8edb11 100644 --- a/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.h +++ b/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.h @@ -8,18 +8,10 @@ namespace ProjectExplorer { -class PROJECTEXPLORER_EXPORT DeviceCheckBuildStep : public BuildStep +class PROJECTEXPLORER_EXPORT DeviceCheckBuildStepFactory : public BuildStepFactory { - Q_OBJECT - public: - DeviceCheckBuildStep(BuildStepList *bsl, Utils::Id id); - - bool init() override; - void doRun() override; - - static Utils::Id stepId(); - static QString displayName(); + DeviceCheckBuildStepFactory(); }; -} // namespace ProjectExplorer +} // ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/devicefactoryselectiondialog.cpp b/src/plugins/projectexplorer/devicesupport/devicefactoryselectiondialog.cpp index ed47999432..987f2e0c39 100644 --- a/src/plugins/projectexplorer/devicesupport/devicefactoryselectiondialog.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicefactoryselectiondialog.cpp @@ -24,7 +24,7 @@ DeviceFactorySelectionDialog::DeviceFactorySelectionDialog(QWidget *parent) : m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); m_buttonBox->button(QDialogButtonBox::Ok)->setText(Tr::tr("Start Wizard")); - using namespace Utils::Layouting; + using namespace Layouting; Column { Tr::tr("Available device types:"), m_listWidget, diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp index 9f8e0594fd..aa017df46b 100644 --- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp @@ -14,9 +14,10 @@ #include <utils/environment.h> #include <utils/fsengine/fsengine.h> #include <utils/persistentsettings.h> +#include <utils/process.h> #include <utils/qtcassert.h> -#include <utils/qtcprocess.h> #include <utils/stringutils.h> +#include <utils/terminalhooks.h> #include <QHash> #include <QMutex> @@ -442,6 +443,13 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManager device->openTerminal(env, filePath); }; + deviceHooks.osType = [](const FilePath &filePath) { + auto device = DeviceManager::deviceForPath(filePath); + if (!device) + return OsTypeLinux; + return device->osType(); + }; + DeviceProcessHooks processHooks; processHooks.processImplHook = [](const FilePath &filePath) -> ProcessInterface * { @@ -456,7 +464,23 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManager return device->systemEnvironment(); }; - QtcProcess::setRemoteProcessHooks(processHooks); + Process::setRemoteProcessHooks(processHooks); + + Terminal::Hooks::instance().getTerminalCommandsForDevicesHook().set( + [this]() -> QList<Terminal::NameAndCommandLine> { + QList<Terminal::NameAndCommandLine> result; + for (const IDevice::ConstPtr device : d->devices) { + if (device->type() == Constants::DESKTOP_DEVICE_TYPE) + continue; + + const FilePath shell = Terminal::defaultShellForDevice(device->rootPath()); + + if (!shell.isEmpty()) + result << Terminal::NameAndCommandLine{device->displayName(), + CommandLine{shell, {}}}; + } + return result; + }); } DeviceManager::~DeviceManager() diff --git a/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp index 39af471345..a35387a3c2 100644 --- a/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp @@ -17,13 +17,16 @@ #include <coreplugin/icore.h> #include <utils/algorithm.h> +#include <utils/async.h> #include <utils/layoutbuilder.h> +#include <utils/optionpushbutton.h> #include <utils/qtcassert.h> #include <QComboBox> #include <QGroupBox> #include <QLabel> #include <QLineEdit> +#include <QMenu> #include <QPushButton> #include <QScrollArea> #include <QTextStream> @@ -100,21 +103,47 @@ void DeviceSettingsWidget::initGui() m_deviceStateTextLabel = new QLabel; m_osSpecificGroupBox = new QGroupBox(Tr::tr("Type Specific")); m_osSpecificGroupBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); - m_addConfigButton = new QPushButton(Tr::tr("&Add...")); m_removeConfigButton = new QPushButton(Tr::tr("&Remove")); m_defaultDeviceButton = new QPushButton(Tr::tr("Set As Default")); - auto line = new QFrame; - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - auto customButtonsContainer = new QWidget; - m_buttonsLayout = new QVBoxLayout(customButtonsContainer); + + OptionPushButton *addButton = new OptionPushButton(Tr::tr("&Add...")); + connect(addButton, &OptionPushButton::clicked, this, &DeviceSettingsWidget::addDevice); + + QMenu *deviceTypeMenu = new QMenu(addButton); + QAction *defaultAction = new QAction(Tr::tr("&Start Wizard to Add Device...")); + connect(defaultAction, &QAction::triggered, this, &DeviceSettingsWidget::addDevice); + deviceTypeMenu->addAction(defaultAction); + deviceTypeMenu->addSeparator(); + + for (IDeviceFactory *factory : IDeviceFactory::allDeviceFactories()) { + if (!factory->canCreate()) + continue; + if (!factory->quickCreationAllowed()) + continue; + + QAction *action = new QAction(Tr::tr("Add %1").arg(factory->displayName())); + deviceTypeMenu->addAction(action); + + connect(action, &QAction::triggered, this, [factory, this] { + IDevice::Ptr device = factory->construct(); + QTC_ASSERT(device, return); + m_deviceManager->addDevice(device); + m_removeConfigButton->setEnabled(true); + m_configurationComboBox->setCurrentIndex(m_deviceManagerModel->indexOf(device)); + saveSettings(); + }); + } + + addButton->setOptionalMenu(deviceTypeMenu); + + m_buttonsLayout = new QVBoxLayout; m_buttonsLayout->setContentsMargins({}); auto scrollAreaWidget = new QWidget; auto scrollArea = new QScrollArea; scrollArea->setWidgetResizable(true); scrollArea->setWidget(scrollAreaWidget); - using namespace Utils::Layouting; + using namespace Layouting; Column { m_generalGroupBox, m_osSpecificGroupBox, @@ -127,25 +156,27 @@ void DeviceSettingsWidget::initGui() Tr::tr("Current state:"), Row { m_deviceStateIconLabel, m_deviceStateTextLabel, st, }, br, }.attachTo(m_generalGroupBox); + // clang-format off Row { Column { Form { m_configurationLabel, m_configurationComboBox, br, }, scrollArea, }, Column { - m_addConfigButton, + addButton, + Space(30), m_removeConfigButton, m_defaultDeviceButton, - line, - customButtonsContainer, + m_buttonsLayout, st, }, }.attachTo(this); + // clang-format on bool hasDeviceFactories = Utils::anyOf(IDeviceFactory::allDeviceFactories(), &IDeviceFactory::canCreate); - m_addConfigButton->setEnabled(hasDeviceFactories); + addButton->setEnabled(hasDeviceFactories); int lastIndex = ICore::settings() ->value(QLatin1String(LastDeviceIndexKey), 0).toInt(); @@ -160,10 +191,10 @@ void DeviceSettingsWidget::initGui() this, &DeviceSettingsWidget::setDefaultDevice); connect(m_removeConfigButton, &QAbstractButton::clicked, this, &DeviceSettingsWidget::removeDevice); - connect(m_nameLineEdit, &QLineEdit::editingFinished, - this, &DeviceSettingsWidget::deviceNameEditingFinished); - connect(m_addConfigButton, &QAbstractButton::clicked, - this, &DeviceSettingsWidget::addDevice); + connect(m_nameLineEdit, + &QLineEdit::editingFinished, + this, + &DeviceSettingsWidget::deviceNameEditingFinished); } void DeviceSettingsWidget::addDevice() @@ -182,6 +213,8 @@ void DeviceSettingsWidget::addDevice() if (device.isNull()) return; + Utils::asyncRun([device] { device->checkOsType(); }); + m_deviceManager->addDevice(device); m_removeConfigButton->setEnabled(true); m_configurationComboBox->setCurrentIndex(m_deviceManagerModel->indexOf(device)); diff --git a/src/plugins/projectexplorer/devicesupport/devicesettingswidget.h b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.h index 5cd5cec85e..207b60cf74 100644 --- a/src/plugins/projectexplorer/devicesupport/devicesettingswidget.h +++ b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.h @@ -76,7 +76,6 @@ private: QLabel *m_deviceStateIconLabel; QLabel *m_deviceStateTextLabel; QGroupBox *m_osSpecificGroupBox; - QPushButton *m_addConfigButton; QPushButton *m_removeConfigButton; QPushButton *m_defaultDeviceButton; QVBoxLayout *m_buttonsLayout; diff --git a/src/plugins/projectexplorer/devicesupport/devicetestdialog.cpp b/src/plugins/projectexplorer/devicesupport/devicetestdialog.cpp index 91a83531b4..0277fc827a 100644 --- a/src/plugins/projectexplorer/devicesupport/devicetestdialog.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicetestdialog.cpp @@ -43,7 +43,7 @@ DeviceTestDialog::DeviceTestDialog(const IDevice::Ptr &deviceConfiguration, d->textEdit->setReadOnly(true); d->buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel); - using namespace Utils::Layouting; + using namespace Layouting; Column { d->textEdit, d->buttonBox, diff --git a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp index 8081c9745d..c3f3f195ca 100644 --- a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp +++ b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp @@ -9,8 +9,8 @@ #include <utils/port.h> #include <utils/portlist.h> +#include <utils/process.h> #include <utils/qtcassert.h> -#include <utils/qtcprocess.h> #include <utils/stringutils.h> #include <utils/url.h> @@ -22,7 +22,7 @@ namespace Internal { class DeviceUsedPortsGathererPrivate { public: - std::unique_ptr<QtcProcess> process; + std::unique_ptr<Process> process; QList<Port> usedPorts; IDevice::ConstPtr device; PortsGatheringMethod portsGatheringMethod; @@ -54,10 +54,10 @@ void DeviceUsedPortsGatherer::start() const QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::AnyIPProtocol; - d->process.reset(new QtcProcess); + d->process.reset(new Process); d->process->setCommand(d->portsGatheringMethod.commandLine(protocol)); - connect(d->process.get(), &QtcProcess::done, this, &DeviceUsedPortsGatherer::handleProcessDone); + connect(d->process.get(), &Process::done, this, &DeviceUsedPortsGatherer::handleProcessDone); d->process->start(); } @@ -116,12 +116,6 @@ void DeviceUsedPortsGatherer::handleProcessDone() stop(); } -DeviceUsedPortsGathererAdapter::DeviceUsedPortsGathererAdapter() -{ - connect(task(), &DeviceUsedPortsGatherer::portListReady, this, [this] { emit done(true); }); - connect(task(), &DeviceUsedPortsGatherer::error, this, [this] { emit done(false); }); -} - // PortGatherer PortsGatherer::PortsGatherer(RunControl *runControl) diff --git a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h index 3519c7d22f..20b0c9c9e8 100644 --- a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h +++ b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h @@ -7,8 +7,9 @@ #include <projectexplorer/runcontrol.h> +#include <solutions/tasking/tasktree.h> + #include <utils/portlist.h> -#include <utils/tasktree.h> namespace ProjectExplorer { @@ -43,11 +44,14 @@ private: Internal::DeviceUsedPortsGathererPrivate * const d; }; -class PROJECTEXPLORER_EXPORT DeviceUsedPortsGathererAdapter - : public Utils::Tasking::TaskAdapter<DeviceUsedPortsGatherer> +class PROJECTEXPLORER_EXPORT DeviceUsedPortsGathererTaskAdapter + : public Tasking::TaskAdapter<DeviceUsedPortsGatherer> { public: - DeviceUsedPortsGathererAdapter(); + DeviceUsedPortsGathererTaskAdapter() { + connect(task(), &DeviceUsedPortsGatherer::portListReady, this, [this] { emit done(true); }); + connect(task(), &DeviceUsedPortsGatherer::error, this, [this] { emit done(false); }); + } void start() final { task()->start(); } }; @@ -85,4 +89,5 @@ private: } // namespace ProjectExplorer -QTC_DECLARE_CUSTOM_TASK(PortGatherer, ProjectExplorer::DeviceUsedPortsGathererAdapter); +TASKING_DECLARE_TASK(DeviceUsedPortsGathererTask, + ProjectExplorer::DeviceUsedPortsGathererTaskAdapter); diff --git a/src/plugins/projectexplorer/devicesupport/filetransfer.cpp b/src/plugins/projectexplorer/devicesupport/filetransfer.cpp index bd386db0d1..0581579767 100644 --- a/src/plugins/projectexplorer/devicesupport/filetransfer.cpp +++ b/src/plugins/projectexplorer/devicesupport/filetransfer.cpp @@ -16,43 +16,18 @@ using namespace Utils; namespace ProjectExplorer { -FileTransferDirection FileToTransfer::direction() const -{ - if (m_source.needsDevice() == m_target.needsDevice()) - return FileTransferDirection::Invalid; - return m_source.needsDevice() ? FileTransferDirection::Download : FileTransferDirection::Upload; -} - QString FileTransferSetupData::defaultRsyncFlags() { return "-av"; } -static FileTransferDirection transferDirection(const FilesToTransfer &files) -{ - if (files.isEmpty()) - return FileTransferDirection::Invalid; - - const FileTransferDirection direction = files.first().direction(); - for (const FileToTransfer &file : files) { - if (file.direction() != direction) - return FileTransferDirection::Invalid; - } - return direction; -} - -static const FilePath &remoteFile(FileTransferDirection direction, const FileToTransfer &file) -{ - return direction == FileTransferDirection::Upload ? file.m_target : file.m_source; -} - -static IDeviceConstPtr matchedDevice(FileTransferDirection direction, const FilesToTransfer &files) +static IDeviceConstPtr matchedDevice(const FilesToTransfer &files) { if (files.isEmpty()) return {}; - const FilePath &filePath = remoteFile(direction, files.first()); + const FilePath filePath = files.first().m_target; for (const FileToTransfer &file : files) { - if (!filePath.isSameDevice(remoteFile(direction, file))) + if (!filePath.isSameDevice(file.m_target)) return {}; } return DeviceManager::deviceForPath(filePath); @@ -102,15 +77,11 @@ void FileTransferPrivate::start() if (m_setup.m_files.isEmpty()) return startFailed(Tr::tr("No files to transfer.")); - const FileTransferDirection direction = transferDirection(m_setup.m_files); - - IDeviceConstPtr device; - if (direction != FileTransferDirection::Invalid) - device = matchedDevice(direction, m_setup.m_files); + IDeviceConstPtr device = matchedDevice(m_setup.m_files); if (!device) { // Fall back to generic copy. - const FilePath &filePath = m_setup.m_files.first().m_target; + const FilePath filePath = m_setup.m_files.first().m_target; device = DeviceManager::deviceForPath(filePath); m_setup.m_method = FileTransferMethod::GenericCopy; } @@ -222,7 +193,7 @@ QString FileTransfer::transferMethodName(FileTransferMethod method) return {}; } -FileTransferAdapter::FileTransferAdapter() +FileTransferTaskAdapter::FileTransferTaskAdapter() { connect(task(), &FileTransfer::done, this, [this](const ProcessResultData &result) { emit done(result.m_exitStatus == QProcess::NormalExit diff --git a/src/plugins/projectexplorer/devicesupport/filetransfer.h b/src/plugins/projectexplorer/devicesupport/filetransfer.h index dcc04f05c0..f73a374168 100644 --- a/src/plugins/projectexplorer/devicesupport/filetransfer.h +++ b/src/plugins/projectexplorer/devicesupport/filetransfer.h @@ -7,7 +7,7 @@ #include "filetransferinterface.h" #include "idevicefwd.h" -#include <utils/tasktree.h> +#include <solutions/tasking/tasktree.h> namespace Utils { class ProcessResultData; } @@ -46,14 +46,14 @@ private: FileTransferPrivate *d; }; -class PROJECTEXPLORER_EXPORT FileTransferAdapter : public Utils::Tasking::TaskAdapter<FileTransfer> +class PROJECTEXPLORER_EXPORT FileTransferTaskAdapter : public Tasking::TaskAdapter<FileTransfer> { public: - FileTransferAdapter(); + FileTransferTaskAdapter(); void start() override { task()->start(); } }; -class PROJECTEXPLORER_EXPORT FileTransferTestAdapter : public FileTransferAdapter +class PROJECTEXPLORER_EXPORT FileTransferTestTaskAdapter : public FileTransferTaskAdapter { public: void start() final { task()->test(); } @@ -61,5 +61,5 @@ public: } // namespace ProjectExplorer -QTC_DECLARE_CUSTOM_TASK(Transfer, ProjectExplorer::FileTransferAdapter); -QTC_DECLARE_CUSTOM_TASK(TransferTest, ProjectExplorer::FileTransferTestAdapter); +TASKING_DECLARE_TASK(FileTransferTask, ProjectExplorer::FileTransferTaskAdapter); +TASKING_DECLARE_TASK(FileTransferTestTask, ProjectExplorer::FileTransferTestTaskAdapter); diff --git a/src/plugins/projectexplorer/devicesupport/filetransferinterface.h b/src/plugins/projectexplorer/devicesupport/filetransferinterface.h index 2907f9c257..ce4e662db4 100644 --- a/src/plugins/projectexplorer/devicesupport/filetransferinterface.h +++ b/src/plugins/projectexplorer/devicesupport/filetransferinterface.h @@ -11,12 +11,6 @@ namespace Utils { class ProcessResultData; } namespace ProjectExplorer { -enum class FileTransferDirection { - Invalid, - Upload, - Download -}; - enum class FileTransferMethod { Sftp, Rsync, @@ -29,8 +23,6 @@ class PROJECTEXPLORER_EXPORT FileToTransfer public: Utils::FilePath m_source; Utils::FilePath m_target; - - FileTransferDirection direction() const; }; using FilesToTransfer = QList<FileToTransfer>; diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp index b884237b3b..50f552444b 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp @@ -15,6 +15,7 @@ #include <coreplugin/icore.h> +#include <utils/commandline.h> #include <utils/devicefileaccess.h> #include <utils/displayname.h> #include <utils/icon.h> @@ -92,6 +93,7 @@ static Id newId() const char DisplayNameKey[] = "Name"; const char TypeKey[] = "OsType"; +const char ClientOsTypeKey[] = "ClientOsType"; const char IdKey[] = "InternalId"; const char OriginKey[] = "Origin"; const char MachineTypeKey[] = "Type"; @@ -369,6 +371,27 @@ const QList<IDevice::DeviceAction> IDevice::deviceActions() const return d->deviceActions; } +PortsGatheringMethod IDevice::portsGatheringMethod() const +{ + return {[this](QAbstractSocket::NetworkLayerProtocol protocol) -> CommandLine { + // We might encounter the situation that protocol is given IPv6 + // but the consumer of the free port information decides to open + // an IPv4(only) port. As a result the next IPv6 scan will + // report the port again as open (in IPv6 namespace), while the + // same port in IPv4 namespace might still be blocked, and + // re-use of this port fails. + // GDBserver behaves exactly like this. + + Q_UNUSED(protocol) + + if (filePath("/proc/net").isReadableDir()) + return {filePath("/bin/sh"), {"-c", "cat /proc/net/tcp*"}}; + + return {filePath("netstat"), {"-a", "-n"}}; + }, + &Port::parseFromCommandOutput}; +}; + DeviceProcessList *IDevice::createProcessListModel(QObject *parent) const { Q_UNUSED(parent) @@ -425,6 +448,8 @@ void IDevice::fromMap(const QVariantMap &map) d->type = typeFromMap(map); d->displayName.fromMap(map, DisplayNameKey); d->id = Id::fromSetting(map.value(QLatin1String(IdKey))); + d->osType = osTypeFromString( + map.value(QLatin1String(ClientOsTypeKey), osTypeToString(OsTypeLinux)).toString()); if (!d->id.isValid()) d->id = newId(); d->origin = static_cast<Origin>(map.value(QLatin1String(OriginKey), ManuallyAdded).toInt()); @@ -471,6 +496,7 @@ QVariantMap IDevice::toMap() const QVariantMap map; d->displayName.toMap(map, DisplayNameKey); map.insert(QLatin1String(TypeKey), d->type.toString()); + map.insert(QLatin1String(ClientOsTypeKey), osTypeToString(d->osType)); map.insert(QLatin1String(IdKey), d->id.toSetting()); map.insert(QLatin1String(OriginKey), d->origin); @@ -503,9 +529,6 @@ IDevice::Ptr IDevice::clone() const device->d->deviceState = d->deviceState; device->d->deviceActions = d->deviceActions; device->d->deviceIcons = d->deviceIcons; - // Os type is only set in the constructor, always to the same value. - // But make sure we notice if that changes in the future (which it shouldn't). - QTC_CHECK(device->d->osType == d->osType); device->d->osType = d->osType; device->fromMap(toMap()); return device; @@ -578,16 +601,6 @@ void IDevice::setDebugServerPath(const FilePath &path) d->debugServerPath = path; } -FilePath IDevice::debugDumperPath() const -{ - return d->debugDumperPath; -} - -void IDevice::setDebugDumperPath(const FilePath &path) -{ - d->debugDumperPath = path; -} - FilePath IDevice::qmlRunCommand() const { return d->qmlRunCommand; @@ -682,12 +695,12 @@ void DeviceProcessKiller::start() m_signalOperation->killProcess(m_processPath.path()); } -KillerAdapter::KillerAdapter() +DeviceProcessKillerTaskAdapter::DeviceProcessKillerTaskAdapter() { - connect(task(), &DeviceProcessKiller::done, this, &KillerAdapter::done); + connect(task(), &DeviceProcessKiller::done, this, &TaskInterface::done); } -void KillerAdapter::start() +void DeviceProcessKillerTaskAdapter::start() { task()->start(); } diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h index 6323b8589b..e28f88aaf4 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.h +++ b/src/plugins/projectexplorer/devicesupport/idevice.h @@ -6,11 +6,12 @@ #include "../projectexplorer_export.h" #include "idevicefwd.h" +#include <solutions/tasking/tasktree.h> + #include <utils/id.h> #include <utils/expected.h> #include <utils/filepath.h> #include <utils/hostosinfo.h> -#include <utils/tasktree.h> #include <QAbstractSocket> #include <QCoreApplication> @@ -35,7 +36,7 @@ class Icon; class PortList; class Port; class ProcessInterface; -class QtcProcess; +class Process; } // Utils namespace ProjectExplorer { @@ -139,10 +140,7 @@ public: void addDeviceAction(const DeviceAction &deviceAction); const QList<DeviceAction> deviceActions() const; - // Devices that can auto detect ports need not return a ports gathering method. Such devices can - // obtain a free port on demand. eg: Desktop device. - virtual bool canAutoDetectPorts() const { return false; } - virtual PortsGatheringMethod portsGatheringMethod() const { return {}; } + virtual PortsGatheringMethod portsGatheringMethod() const; virtual bool canCreateProcessModel() const { return false; } virtual DeviceProcessList *createProcessListModel(QObject *parent = nullptr) const; virtual bool hasDeviceTester() const { return false; } @@ -179,9 +177,6 @@ public: Utils::FilePath debugServerPath() const; void setDebugServerPath(const Utils::FilePath &path); - Utils::FilePath debugDumperPath() const; - void setDebugDumperPath(const Utils::FilePath &path); - Utils::FilePath qmlRunCommand() const; void setQmlRunCommand(const Utils::FilePath &path); @@ -221,6 +216,8 @@ public: virtual bool prepareForBuild(const Target *target); virtual std::optional<Utils::FilePath> clangdExecutable() const; + virtual void checkOsType() {} + protected: IDevice(); @@ -280,13 +277,14 @@ private: QString m_errorString; }; -class PROJECTEXPLORER_EXPORT KillerAdapter : public Utils::Tasking::TaskAdapter<DeviceProcessKiller> +class PROJECTEXPLORER_EXPORT DeviceProcessKillerTaskAdapter + : public Tasking::TaskAdapter<DeviceProcessKiller> { public: - KillerAdapter(); + DeviceProcessKillerTaskAdapter(); void start() final; }; } // namespace ProjectExplorer -QTC_DECLARE_CUSTOM_TASK(Killer, ProjectExplorer::KillerAdapter); +TASKING_DECLARE_TASK(DeviceProcessKillerTask, ProjectExplorer::DeviceProcessKillerTaskAdapter); diff --git a/src/plugins/projectexplorer/devicesupport/idevicefactory.cpp b/src/plugins/projectexplorer/devicesupport/idevicefactory.cpp index c33bc125bb..b517844570 100644 --- a/src/plugins/projectexplorer/devicesupport/idevicefactory.cpp +++ b/src/plugins/projectexplorer/devicesupport/idevicefactory.cpp @@ -63,12 +63,25 @@ bool IDeviceFactory::canCreate() const IDevice::Ptr IDeviceFactory::create() const { - return m_creator ? m_creator() : IDevice::Ptr(); + if (!m_creator) + return {}; + + IDevice::Ptr device = m_creator(); + if (!device) // e.g. Cancel used on the dialog to create a device + return {}; + device->setDefaultDisplayName(displayName()); + return device; } IDevice::Ptr IDeviceFactory::construct() const { - return m_constructor ? m_constructor() : IDevice::Ptr(); + if (!m_constructor) + return {}; + + IDevice::Ptr device = m_constructor(); + QTC_ASSERT(device, return {}); + device->setDefaultDisplayName(displayName()); + return device; } static QList<IDeviceFactory *> g_deviceFactories; @@ -105,6 +118,16 @@ void IDeviceFactory::setCreator(const std::function<IDevice::Ptr ()> &creator) m_creator = creator; } +void IDeviceFactory::setQuickCreationAllowed(bool on) +{ + m_quickCreationAllowed = on; +} + +bool IDeviceFactory::quickCreationAllowed() const +{ + return m_quickCreationAllowed; +} + void IDeviceFactory::setConstructionFunction(const std::function<IDevice::Ptr ()> &constructor) { m_constructor = constructor; diff --git a/src/plugins/projectexplorer/devicesupport/idevicefactory.h b/src/plugins/projectexplorer/devicesupport/idevicefactory.h index 87a54244ad..665059f5b2 100644 --- a/src/plugins/projectexplorer/devicesupport/idevicefactory.h +++ b/src/plugins/projectexplorer/devicesupport/idevicefactory.h @@ -26,6 +26,7 @@ public: bool canCreate() const; IDevicePtr construct() const; IDevicePtr create() const; + bool quickCreationAllowed() const; virtual bool canRestore(const QVariantMap &) const { return true; } @@ -41,6 +42,7 @@ protected: void setCombinedIcon(const Utils::FilePath &smallIcon, const Utils::FilePath &largeIcon); void setConstructionFunction(const std::function<IDevicePtr ()> &constructor); void setCreator(const std::function<IDevicePtr()> &creator); + void setQuickCreationAllowed(bool on); private: std::function<IDevicePtr()> m_creator; @@ -48,6 +50,7 @@ private: QString m_displayName; QIcon m_icon; std::function<IDevicePtr()> m_constructor; + bool m_quickCreationAllowed = false; }; } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/localprocesslist.cpp b/src/plugins/projectexplorer/devicesupport/localprocesslist.cpp deleted file mode 100644 index c6fd49012f..0000000000 --- a/src/plugins/projectexplorer/devicesupport/localprocesslist.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// 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 "localprocesslist.h" - -#include <projectexplorer/devicesupport/idevice.h> -#include <utils/processinfo.h> - -#include <QTimer> - -#if defined(Q_OS_UNIX) -#include <unistd.h> -#elif defined(Q_OS_WIN) -#include <windows.h> -#endif - -using namespace Utils; - -namespace ProjectExplorer { -namespace Internal { - -LocalProcessList::LocalProcessList(const IDevice::ConstPtr &device, QObject *parent) - : DeviceProcessList(device, parent) -{ -#if defined(Q_OS_UNIX) - setOwnPid(getpid()); -#elif defined(Q_OS_WIN) - setOwnPid(GetCurrentProcessId()); -#endif -} - -void LocalProcessList::doKillProcess(const ProcessInfo &processInfo) -{ - DeviceProcessSignalOperation::Ptr signalOperation = device()->signalOperation(); - connect(signalOperation.data(), &DeviceProcessSignalOperation::finished, - this, &LocalProcessList::reportDelayedKillStatus); - signalOperation->killProcess(processInfo.processId); -} - -void LocalProcessList::handleUpdate() -{ - reportProcessListUpdated(ProcessInfo::processInfoList()); -} - -void LocalProcessList::doUpdate() -{ - QTimer::singleShot(0, this, &LocalProcessList::handleUpdate); -} - -void LocalProcessList::reportDelayedKillStatus(const QString &errorMessage) -{ - if (errorMessage.isEmpty()) - reportProcessKilled(); - else - reportError(errorMessage); -} - -} // namespace Internal -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/processlist.cpp b/src/plugins/projectexplorer/devicesupport/processlist.cpp new file mode 100644 index 0000000000..11e0932832 --- /dev/null +++ b/src/plugins/projectexplorer/devicesupport/processlist.cpp @@ -0,0 +1,61 @@ +// 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 "processlist.h" + +#include <projectexplorer/devicesupport/idevice.h> +#include <utils/processinfo.h> + +#include <QTimer> + +#if defined(Q_OS_UNIX) +#include <unistd.h> +#elif defined(Q_OS_WIN) +#include <windows.h> +#endif + +using namespace Utils; + +namespace ProjectExplorer { + +ProcessList::ProcessList(const IDevice::ConstPtr &device, QObject *parent) + : DeviceProcessList(device, parent) +{ +#if defined(Q_OS_UNIX) + setOwnPid(getpid()); +#elif defined(Q_OS_WIN) + setOwnPid(GetCurrentProcessId()); +#endif +} + +void ProcessList::doKillProcess(const ProcessInfo &processInfo) +{ + m_signalOperation = device()->signalOperation(); + connect(m_signalOperation.data(), + &DeviceProcessSignalOperation::finished, + this, + &ProcessList::reportDelayedKillStatus); + m_signalOperation->killProcess(processInfo.processId); +} + +void ProcessList::handleUpdate() +{ + reportProcessListUpdated(ProcessInfo::processInfoList(DeviceProcessList::device()->rootPath())); +} + +void ProcessList::doUpdate() +{ + QTimer::singleShot(0, this, &ProcessList::handleUpdate); +} + +void ProcessList::reportDelayedKillStatus(const QString &errorMessage) +{ + if (errorMessage.isEmpty()) + reportProcessKilled(); + else + reportError(errorMessage); + + m_signalOperation.reset(); +} + +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/localprocesslist.h b/src/plugins/projectexplorer/devicesupport/processlist.h index e3445f47b2..caebaf22f9 100644 --- a/src/plugins/projectexplorer/devicesupport/localprocesslist.h +++ b/src/plugins/projectexplorer/devicesupport/processlist.h @@ -4,16 +4,16 @@ #pragma once #include "deviceprocesslist.h" +#include "idevice.h" namespace ProjectExplorer { -namespace Internal { -class LocalProcessList : public DeviceProcessList +class PROJECTEXPLORER_EXPORT ProcessList : public DeviceProcessList { Q_OBJECT public: - explicit LocalProcessList(const IDeviceConstPtr &device, QObject *parent = nullptr); + explicit ProcessList(const IDeviceConstPtr &device, QObject *parent = nullptr); private: void doUpdate() override; @@ -22,7 +22,9 @@ private: private: void handleUpdate(); void reportDelayedKillStatus(const QString &errorMessage); + +private: + DeviceProcessSignalOperation::Ptr m_signalOperation; }; -} // namespace Internal } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.cpp b/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.cpp deleted file mode 100644 index d64cde6e3e..0000000000 --- a/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// 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 "sshdeviceprocesslist.h" - -#include "idevice.h" -#include "../projectexplorertr.h" - -#include <utils/processinfo.h> -#include <utils/qtcassert.h> -#include <utils/qtcprocess.h> -#include <utils/stringutils.h> - -using namespace Utils; - -namespace ProjectExplorer { - -class SshDeviceProcessListPrivate -{ -public: - QtcProcess m_process; - DeviceProcessSignalOperation::Ptr m_signalOperation; -}; - -SshDeviceProcessList::SshDeviceProcessList(const IDevice::ConstPtr &device, QObject *parent) : - DeviceProcessList(device, parent), d(std::make_unique<SshDeviceProcessListPrivate>()) -{ - connect(&d->m_process, &QtcProcess::done, this, &SshDeviceProcessList::handleProcessDone); -} - -SshDeviceProcessList::~SshDeviceProcessList() = default; - -void SshDeviceProcessList::doUpdate() -{ - d->m_process.close(); - d->m_process.setCommand({device()->filePath("/bin/sh"), {"-c", listProcessesCommandLine()}}); - d->m_process.start(); -} - -void SshDeviceProcessList::doKillProcess(const ProcessInfo &process) -{ - d->m_signalOperation = device()->signalOperation(); - QTC_ASSERT(d->m_signalOperation, return); - connect(d->m_signalOperation.data(), &DeviceProcessSignalOperation::finished, - this, &SshDeviceProcessList::handleKillProcessFinished); - d->m_signalOperation->killProcess(process.processId); -} - -void SshDeviceProcessList::handleProcessDone() -{ - if (d->m_process.result() == ProcessResult::FinishedWithSuccess) { - reportProcessListUpdated(buildProcessList(d->m_process.cleanedStdOut())); - } else { - const QString errorString = d->m_process.exitStatus() == QProcess::NormalExit - ? Tr::tr("Process listing command failed with exit code %1.").arg(d->m_process.exitCode()) - : d->m_process.errorString(); - const QString stdErr = d->m_process.cleanedStdErr(); - const QString outputString - = stdErr.isEmpty() ? stdErr : Tr::tr("Remote stderr was: %1").arg(stdErr); - reportError(Utils::joinStrings({errorString, outputString}, '\n')); - } - setFinished(); -} - -void SshDeviceProcessList::handleKillProcessFinished(const QString &errorString) -{ - if (errorString.isEmpty()) - reportProcessKilled(); - else - reportError(Tr::tr("Error: Kill process failed: %1").arg(errorString)); - setFinished(); -} - -void SshDeviceProcessList::setFinished() -{ - d->m_process.close(); - if (d->m_signalOperation) { - d->m_signalOperation->disconnect(this); - d->m_signalOperation.clear(); - } -} - -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.h b/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.h deleted file mode 100644 index fd560f5375..0000000000 --- a/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "deviceprocesslist.h" - -#include <memory> - -namespace ProjectExplorer { - -class SshDeviceProcessListPrivate; - -class PROJECTEXPLORER_EXPORT SshDeviceProcessList : public DeviceProcessList -{ - Q_OBJECT -public: - explicit SshDeviceProcessList(const IDeviceConstPtr &device, QObject *parent = nullptr); - ~SshDeviceProcessList() override; - -private: - void handleProcessDone(); - void handleKillProcessFinished(const QString &errorString); - - virtual QString listProcessesCommandLine() const = 0; - virtual QList<Utils::ProcessInfo> buildProcessList(const QString &listProcessesReply) const = 0; - - void doUpdate() override; - void doKillProcess(const Utils::ProcessInfo &process) override; - - void setFinished(); - - const std::unique_ptr<SshDeviceProcessListPrivate> d; -}; - -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/sshparameters.cpp b/src/plugins/projectexplorer/devicesupport/sshparameters.cpp index 3cc6364a67..744e350eae 100644 --- a/src/plugins/projectexplorer/devicesupport/sshparameters.cpp +++ b/src/plugins/projectexplorer/devicesupport/sshparameters.cpp @@ -8,8 +8,8 @@ #include <utils/environment.h> #include <utils/fileutils.h> #include <utils/hostosinfo.h> +#include <utils/process.h> #include <utils/qtcassert.h> -#include <utils/qtcprocess.h> #include <QDebug> @@ -19,9 +19,17 @@ using namespace Utils; namespace ProjectExplorer { -SshParameters::SshParameters() +SshParameters::SshParameters() = default; + +QString SshParameters::userAtHost() const { - url.setPort(0); + QString res; + if (!m_userName.isEmpty()) + res = m_userName + '@'; + res += m_host; + if (m_port != 22) + res += QString(":%1").arg(m_port); + return res; } QStringList SshParameters::connectionOptions(const FilePath &binary) const @@ -61,7 +69,7 @@ QStringList SshParameters::connectionOptions(const FilePath &binary) const return args; } -bool SshParameters::setupSshEnvironment(QtcProcess *process) +bool SshParameters::setupSshEnvironment(Process *process) { Environment env = process->controlEnvironment(); if (!env.hasChanges()) @@ -81,10 +89,11 @@ bool SshParameters::setupSshEnvironment(QtcProcess *process) return hasDisplay; } - -static inline bool equals(const SshParameters &p1, const SshParameters &p2) +bool operator==(const SshParameters &p1, const SshParameters &p2) { - return p1.url == p2.url + return p1.m_host == p2.m_host + && p1.m_port == p2.m_port + && p1.m_userName == p2.m_userName && p1.authenticationType == p2.authenticationType && p1.privateKeyFile == p2.privateKeyFile && p1.hostKeyCheckingMode == p2.hostKeyCheckingMode @@ -92,16 +101,6 @@ static inline bool equals(const SshParameters &p1, const SshParameters &p2) && p1.timeout == p2.timeout; } -bool operator==(const SshParameters &p1, const SshParameters &p2) -{ - return equals(p1, p2); -} - -bool operator!=(const SshParameters &p1, const SshParameters &p2) -{ - return !equals(p1, p2); -} - #ifdef WITH_TESTS namespace SshTest { const QString getHostFromEnvironment() diff --git a/src/plugins/projectexplorer/devicesupport/sshparameters.h b/src/plugins/projectexplorer/devicesupport/sshparameters.h index 00b63e3aac..3ee483d5f3 100644 --- a/src/plugins/projectexplorer/devicesupport/sshparameters.h +++ b/src/plugins/projectexplorer/devicesupport/sshparameters.h @@ -7,9 +7,7 @@ #include <utils/filepath.h> -#include <QUrl> - -namespace Utils { class QtcProcess; } +namespace Utils { class Process; } namespace ProjectExplorer { @@ -29,28 +27,34 @@ public: SshParameters(); - QString host() const { return url.host(); } - quint16 port() const { return url.port(); } - QString userName() const { return url.userName(); } - QString userAtHost() const { return userName().isEmpty() ? host() : userName() + '@' + host(); } - void setHost(const QString &host) { url.setHost(host); } - void setPort(int port) { url.setPort(port); } - void setUserName(const QString &name) { url.setUserName(name); } + QString host() const { return m_host; } + quint16 port() const { return m_port; } + QString userName() const { return m_userName; } + + QString userAtHost() const; + + void setHost(const QString &host) { m_host = host; } + void setPort(int port) { m_port = port; } + void setUserName(const QString &name) { m_userName = name; } QStringList connectionOptions(const Utils::FilePath &binary) const; - QUrl url; Utils::FilePath privateKeyFile; QString x11DisplayName; int timeout = 0; // In seconds. AuthenticationType authenticationType = AuthenticationTypeAll; SshHostKeyCheckingMode hostKeyCheckingMode = SshHostKeyCheckingAllowNoMatch; - static bool setupSshEnvironment(Utils::QtcProcess *process); -}; + static bool setupSshEnvironment(Utils::Process *process); + + friend PROJECTEXPLORER_EXPORT bool operator==(const SshParameters &p1, const SshParameters &p2); + friend bool operator!=(const SshParameters &p1, const SshParameters &p2) { return !(p1 == p2); } -PROJECTEXPLORER_EXPORT bool operator==(const SshParameters &p1, const SshParameters &p2); -PROJECTEXPLORER_EXPORT bool operator!=(const SshParameters &p1, const SshParameters &p2); +private: + QString m_host; + quint16 m_port = 22; + QString m_userName; +}; #ifdef WITH_TESTS namespace SshTest { diff --git a/src/plugins/projectexplorer/editorconfiguration.cpp b/src/plugins/projectexplorer/editorconfiguration.cpp index fc3933d734..ff99bc25e8 100644 --- a/src/plugins/projectexplorer/editorconfiguration.cpp +++ b/src/plugins/projectexplorer/editorconfiguration.cpp @@ -5,7 +5,7 @@ #include "project.h" #include "projectexplorertr.h" -#include "session.h" +#include "projectmanager.h" #include <utils/algorithm.h> @@ -88,7 +88,7 @@ EditorConfiguration::EditorConfiguration() : d(std::make_unique<EditorConfigurat // if setCurrentDelegate is 0 values are read from *this prefs d->m_defaultCodeStyle->setCurrentDelegate(TextEditorSettings::codeStyle()); - connect(SessionManager::instance(), &SessionManager::aboutToRemoveProject, + connect(ProjectManager::instance(), &ProjectManager::aboutToRemoveProject, this, &EditorConfiguration::slotAboutToRemoveProject); } @@ -263,7 +263,7 @@ void EditorConfiguration::setUseGlobalSettings(bool use) const QList<Core::IEditor *> editors = Core::DocumentModel::editorsForOpenedDocuments(); for (Core::IEditor *editor : editors) { if (auto widget = TextEditorWidget::fromEditor(editor)) { - Project *project = SessionManager::projectForFile(editor->document()->filePath()); + Project *project = ProjectManager::projectForFile(editor->document()->filePath()); if (project && project->editorConfiguration() == this) switchSettings(widget); } @@ -399,7 +399,7 @@ TabSettings actualTabSettings(const Utils::FilePath &file, { if (baseTextdocument) return baseTextdocument->tabSettings(); - if (Project *project = SessionManager::projectForFile(file)) + if (Project *project = ProjectManager::projectForFile(file)) return project->editorConfiguration()->codeStyle()->tabSettings(); return TextEditorSettings::codeStyle()->tabSettings(); } diff --git a/src/plugins/projectexplorer/editorsettingspropertiespage.cpp b/src/plugins/projectexplorer/editorsettingspropertiespage.cpp index 52450b2bc8..e0c2b93bb2 100644 --- a/src/plugins/projectexplorer/editorsettingspropertiespage.cpp +++ b/src/plugins/projectexplorer/editorsettingspropertiespage.cpp @@ -48,7 +48,7 @@ EditorSettingsWidget::EditorSettingsWidget(Project *project) : m_project(project m_behaviorSettings = new TextEditor::BehaviorSettingsWidget(this); - using namespace Utils::Layouting; + using namespace Layouting; Row { m_showWrapColumn, @@ -63,7 +63,8 @@ EditorSettingsWidget::EditorSettingsWidget(Project *project) : m_project(project m_displaySettings, m_behaviorSettings, st, - }.attachTo(this, WithoutMargins); + noMargin + }.attachTo(this); const EditorConfiguration *config = m_project->editorConfiguration(); settingsToUi(config); diff --git a/src/plugins/projectexplorer/environmentaspect.cpp b/src/plugins/projectexplorer/environmentaspect.cpp index 66a2b3bc99..09bfeb9c80 100644 --- a/src/plugins/projectexplorer/environmentaspect.cpp +++ b/src/plugins/projectexplorer/environmentaspect.cpp @@ -3,8 +3,11 @@ #include "environmentaspect.h" +#include "buildconfiguration.h" #include "environmentaspectwidget.h" +#include "kit.h" #include "projectexplorertr.h" +#include "target.h" #include <utils/algorithm.h> #include <utils/qtcassert.h> @@ -12,6 +15,7 @@ using namespace Utils; namespace ProjectExplorer { +const char PRINT_ON_RUN_KEY[] = "PE.EnvironmentAspect.PrintOnRun"; // -------------------------------------------------------------------- // EnvironmentAspect: @@ -101,16 +105,39 @@ int EnvironmentAspect::addPreferredBaseEnvironment(const QString &displayName, return index; } +void EnvironmentAspect::setSupportForBuildEnvironment(Target *target) +{ + setIsLocal(true); + addSupportedBaseEnvironment(Tr::tr("Clean Environment"), {}); + + addSupportedBaseEnvironment(Tr::tr("System Environment"), [] { + return Environment::systemEnvironment(); + }); + addPreferredBaseEnvironment(Tr::tr("Build Environment"), [target] { + if (BuildConfiguration *bc = target->activeBuildConfiguration()) + return bc->environment(); + // Fallback for targets without buildconfigurations: + return target->kit()->buildEnvironment(); + }); + + connect(target, &Target::activeBuildConfigurationChanged, + this, &EnvironmentAspect::environmentChanged); + connect(target, &Target::buildEnvironmentChanged, + this, &EnvironmentAspect::environmentChanged); +} + void EnvironmentAspect::fromMap(const QVariantMap &map) { m_base = map.value(QLatin1String(BASE_KEY), -1).toInt(); m_userChanges = Utils::EnvironmentItem::fromStringList(map.value(QLatin1String(CHANGES_KEY)).toStringList()); + m_printOnRun = map.value(PRINT_ON_RUN_KEY).toBool(); } void EnvironmentAspect::toMap(QVariantMap &data) const { data.insert(QLatin1String(BASE_KEY), m_base); data.insert(QLatin1String(CHANGES_KEY), Utils::EnvironmentItem::toStringList(m_userChanges)); + data.insert(PRINT_ON_RUN_KEY, m_printOnRun); } QString EnvironmentAspect::currentDisplayName() const diff --git a/src/plugins/projectexplorer/environmentaspect.h b/src/plugins/projectexplorer/environmentaspect.h index 5b1d0d1c41..97ce26f017 100644 --- a/src/plugins/projectexplorer/environmentaspect.h +++ b/src/plugins/projectexplorer/environmentaspect.h @@ -39,6 +39,8 @@ public: int addPreferredBaseEnvironment(const QString &displayName, const std::function<Utils::Environment()> &getter); + void setSupportForBuildEnvironment(Target *target); + QString currentDisplayName() const; const QStringList displayNames() const; @@ -48,6 +50,10 @@ public: bool isLocal() const { return m_isLocal; } + bool isPrintOnRunAllowed() const { return m_allowPrintOnRun; } + bool isPrintOnRunEnabled() const { return m_printOnRun; } + void setPrintOnRun(bool enabled) { m_printOnRun = enabled; } + struct Data : BaseAspect::Data { Utils::Environment environment; @@ -66,6 +72,7 @@ protected: void toMap(QVariantMap &map) const override; void setIsLocal(bool local) { m_isLocal = local; } + void setAllowPrintOnRun(bool allow) { m_allowPrintOnRun = allow; } static constexpr char BASE_KEY[] = "PE.EnvironmentAspect.Base"; static constexpr char CHANGES_KEY[] = "PE.EnvironmentAspect.Changes"; @@ -84,6 +91,8 @@ private: QList<BaseEnvironment> m_baseEnvironments; int m_base = -1; bool m_isLocal = false; + bool m_allowPrintOnRun = true; + bool m_printOnRun = false; }; } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/environmentaspectwidget.cpp b/src/plugins/projectexplorer/environmentaspectwidget.cpp index 18babfcf61..34e48af42c 100644 --- a/src/plugins/projectexplorer/environmentaspectwidget.cpp +++ b/src/plugins/projectexplorer/environmentaspectwidget.cpp @@ -9,6 +9,7 @@ #include <utils/environment.h> #include <utils/qtcassert.h> +#include <QCheckBox> #include <QComboBox> #include <QHBoxLayout> #include <QLabel> @@ -63,6 +64,14 @@ EnvironmentAspectWidget::EnvironmentAspectWidget(EnvironmentAspect *aspect) m_environmentWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); topLayout->addWidget(m_environmentWidget); + if (m_aspect->isPrintOnRunAllowed()) { + const auto printOnRunCheckBox = new QCheckBox(Tr::tr("Show in output pane when running")); + printOnRunCheckBox->setChecked(m_aspect->isPrintOnRunEnabled()); + connect(printOnRunCheckBox, &QCheckBox::toggled, + m_aspect, &EnvironmentAspect::setPrintOnRun); + topLayout->addWidget(printOnRunCheckBox); + } + connect(m_environmentWidget, &EnvironmentWidget::userChangesChanged, this, &EnvironmentAspectWidget::userChangesEdited); diff --git a/src/plugins/projectexplorer/extraabi.cpp b/src/plugins/projectexplorer/extraabi.cpp index 24a2861103..19f970b11a 100644 --- a/src/plugins/projectexplorer/extraabi.cpp +++ b/src/plugins/projectexplorer/extraabi.cpp @@ -4,7 +4,6 @@ #include "extraabi.h" #include "abi.h" -#include "projectexplorertr.h" #include <coreplugin/icore.h> @@ -36,17 +35,15 @@ public: class AbiFlavorAccessor : public UpgradingSettingsAccessor { public: - AbiFlavorAccessor(); + AbiFlavorAccessor() + { + setDocType("QtCreatorExtraAbi"); + setApplicationDisplayName(Core::Constants::IDE_DISPLAY_NAME); + setBaseFilePath(Core::ICore::installerResourcePath("abi.xml")); + addVersionUpgrader(std::make_unique<AbiFlavorUpgraderV0>()); + } }; -AbiFlavorAccessor::AbiFlavorAccessor() : - UpgradingSettingsAccessor("QtCreatorExtraAbi", Tr::tr("ABI"), - Core::Constants::IDE_DISPLAY_NAME) -{ - setBaseFilePath(Core::ICore::installerResourcePath("abi.xml")); - - addVersionUpgrader(std::make_unique<AbiFlavorUpgraderV0>()); -} // -------------------------------------------------------------------- // ExtraAbi: diff --git a/src/plugins/projectexplorer/extracompiler.cpp b/src/plugins/projectexplorer/extracompiler.cpp index ab72d3608f..67bb4a63fc 100644 --- a/src/plugins/projectexplorer/extracompiler.cpp +++ b/src/plugins/projectexplorer/extracompiler.cpp @@ -5,23 +5,26 @@ #include "buildmanager.h" #include "kitinformation.h" -#include "session.h" +#include "projectmanager.h" #include "target.h" #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/idocument.h> -#include <utils/asynctask.h> +#include <extensionsystem/pluginmanager.h> + +#include <utils/async.h> #include <utils/expected.h> #include <utils/guard.h> -#include <utils/qtcprocess.h> +#include <utils/process.h> #include <QDateTime> -#include <QFutureInterface> #include <QLoggingCategory> #include <QThreadPool> #include <QTimer> +using namespace Core; +using namespace Tasking; using namespace Utils; namespace ProjectExplorer { @@ -37,15 +40,14 @@ public: FilePath source; FileNameToContentsHash contents; QDateTime compileTime; - Core::IEditor *lastEditor = nullptr; + IEditor *lastEditor = nullptr; QMetaObject::Connection activeBuildConfigConnection; QMetaObject::Connection activeEnvironmentConnection; - Utils::Guard lock; + Guard lock; bool dirty = false; QTimer timer; - FutureSynchronizer m_futureSynchronizer; std::unique_ptr<TaskTree> m_taskTree; }; @@ -63,16 +65,16 @@ ExtraCompiler::ExtraCompiler(const Project *project, const FilePath &source, connect(BuildManager::instance(), &BuildManager::buildStateChanged, this, &ExtraCompiler::onTargetsBuilt); - connect(SessionManager::instance(), &SessionManager::projectRemoved, + connect(ProjectManager::instance(), &ProjectManager::projectRemoved, this, [this](Project *project) { if (project == d->project) deleteLater(); }); - Core::EditorManager *editorManager = Core::EditorManager::instance(); - connect(editorManager, &Core::EditorManager::currentEditorChanged, + EditorManager *editorManager = EditorManager::instance(); + connect(editorManager, &EditorManager::currentEditorChanged, this, &ExtraCompiler::onEditorChanged); - connect(editorManager, &Core::EditorManager::editorAboutToClose, + connect(editorManager, &EditorManager::editorAboutToClose, this, &ExtraCompiler::onEditorAboutToClose); // Use existing target files, where possible. Otherwise run the compiler. @@ -135,7 +137,7 @@ QThreadPool *ExtraCompiler::extraCompilerThreadPool() return s_extraCompilerThreadPool(); } -Tasking::TaskItem ExtraCompiler::compileFileItem() +TaskItem ExtraCompiler::compileFileItem() { return taskItemImpl(fromFileProvider()); } @@ -228,12 +230,12 @@ void ExtraCompiler::onTargetsBuilt(Project *project) }); } -void ExtraCompiler::onEditorChanged(Core::IEditor *editor) +void ExtraCompiler::onEditorChanged(IEditor *editor) { // Handle old editor if (d->lastEditor) { - Core::IDocument *doc = d->lastEditor->document(); - disconnect(doc, &Core::IDocument::contentsChanged, + IDocument *doc = d->lastEditor->document(); + disconnect(doc, &IDocument::contentsChanged, this, &ExtraCompiler::setDirty); if (d->dirty) { @@ -246,7 +248,7 @@ void ExtraCompiler::onEditorChanged(Core::IEditor *editor) d->lastEditor = editor; // Handle new editor - connect(d->lastEditor->document(), &Core::IDocument::contentsChanged, + connect(d->lastEditor->document(), &IDocument::contentsChanged, this, &ExtraCompiler::setDirty); } else { d->lastEditor = nullptr; @@ -259,15 +261,15 @@ void ExtraCompiler::setDirty() d->timer.start(1000); } -void ExtraCompiler::onEditorAboutToClose(Core::IEditor *editor) +void ExtraCompiler::onEditorAboutToClose(IEditor *editor) { if (d->lastEditor != editor) return; // Oh no our editor is going to be closed // get the content first - Core::IDocument *doc = d->lastEditor->document(); - disconnect(doc, &Core::IDocument::contentsChanged, + IDocument *doc = d->lastEditor->document(); + disconnect(doc, &IDocument::contentsChanged, this, &ExtraCompiler::setDirty); if (d->dirty) { d->dirty = false; @@ -278,24 +280,17 @@ void ExtraCompiler::onEditorAboutToClose(Core::IEditor *editor) Environment ExtraCompiler::buildEnvironment() const { - if (Target *target = project()->activeTarget()) { - if (BuildConfiguration *bc = target->activeBuildConfiguration()) { - return bc->environment(); - } else { - EnvironmentItems changes = - EnvironmentKitAspect::environmentChanges(target->kit()); - Environment env = Environment::systemEnvironment(); - env.modify(changes); - return env; - } - } + Target *target = project()->activeTarget(); + if (!target) + return Environment::systemEnvironment(); - return Environment::systemEnvironment(); -} + if (BuildConfiguration *bc = target->activeBuildConfiguration()) + return bc->environment(); -Utils::FutureSynchronizer *ExtraCompiler::futureSynchronizer() const -{ - return &d->m_futureSynchronizer; + const EnvironmentItems changes = EnvironmentKitAspect::environmentChanges(target->kit()); + Environment env = Environment::systemEnvironment(); + env.modify(changes); + return env; } void ExtraCompiler::setContent(const FilePath &file, const QByteArray &contents) @@ -331,15 +326,16 @@ ProcessExtraCompiler::ProcessExtraCompiler(const Project *project, const FilePat ExtraCompiler(project, source, targets, parent) { } -Tasking::TaskItem ProcessExtraCompiler::taskItemImpl(const ContentProvider &provider) +TaskItem ProcessExtraCompiler::taskItemImpl(const ContentProvider &provider) { - const auto setupTask = [=](AsyncTask<FileNameToContentsHash> &async) { + const auto setupTask = [=](Async<FileNameToContentsHash> &async) { async.setThreadPool(extraCompilerThreadPool()); - async.setAsyncCallData(&ProcessExtraCompiler::runInThread, this, command(), - workingDirectory(), arguments(), provider, buildEnvironment()); - async.setFutureSynchronizer(futureSynchronizer()); + // The passed synchronizer has cancelOnWait set to true by default. + async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer()); + async.setConcurrentCallData(&ProcessExtraCompiler::runInThread, this, command(), + workingDirectory(), arguments(), provider, buildEnvironment()); }; - const auto taskDone = [=](const AsyncTask<FileNameToContentsHash> &async) { + const auto taskDone = [=](const Async<FileNameToContentsHash> &async) { if (!async.isResultAvailable()) return; const FileNameToContentsHash data = async.result(); @@ -349,7 +345,7 @@ Tasking::TaskItem ProcessExtraCompiler::taskItemImpl(const ContentProvider &prov setContent(it.key(), it.value()); updateCompileTime(); }; - return Tasking::Async<FileNameToContentsHash>(setupTask, taskDone); + return AsyncTask<FileNameToContentsHash>(setupTask, taskDone); } FilePath ProcessExtraCompiler::workingDirectory() const @@ -374,7 +370,7 @@ Tasks ProcessExtraCompiler::parseIssues(const QByteArray &stdErr) return {}; } -void ProcessExtraCompiler::runInThread(QFutureInterface<FileNameToContentsHash> &futureInterface, +void ProcessExtraCompiler::runInThread(QPromise<FileNameToContentsHash> &promise, const FilePath &cmd, const FilePath &workDir, const QStringList &args, const ContentProvider &provider, const Environment &env) @@ -386,7 +382,7 @@ void ProcessExtraCompiler::runInThread(QFutureInterface<FileNameToContentsHash> if (sourceContents.isNull() || !prepareToRun(sourceContents)) return; - QtcProcess process; + Process process; process.setEnvironment(env); if (!workDir.isEmpty()) @@ -397,15 +393,15 @@ void ProcessExtraCompiler::runInThread(QFutureInterface<FileNameToContentsHash> if (!process.waitForStarted()) return; - while (!futureInterface.isCanceled()) { + while (!promise.isCanceled()) { if (process.waitForFinished(200)) break; } - if (futureInterface.isCanceled()) + if (promise.isCanceled()) return; - futureInterface.reportResult(handleProcessFinished(&process)); + promise.addResult(handleProcessFinished(&process)); } } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/extracompiler.h b/src/plugins/projectexplorer/extracompiler.h index 34993f3940..1dbda0e424 100644 --- a/src/plugins/projectexplorer/extracompiler.h +++ b/src/plugins/projectexplorer/extracompiler.h @@ -10,7 +10,6 @@ #include <utils/environment.h> #include <utils/filepath.h> -#include <utils/tasktree.h> #include <QByteArray> #include <QHash> @@ -21,14 +20,12 @@ QT_BEGIN_NAMESPACE template <typename T> -class QFutureInterface; +class QPromise; class QThreadPool; QT_END_NAMESPACE -namespace Utils { -class FutureSynchronizer; -class QtcProcess; -} +namespace Tasking { class TaskItem; } +namespace Utils { class Process; } namespace ProjectExplorer { @@ -52,7 +49,7 @@ public: Utils::FilePaths targets() const; void forEachTarget(std::function<void(const Utils::FilePath &)> func) const; - Utils::Tasking::TaskItem compileFileItem(); + Tasking::TaskItem compileFileItem(); void compileFile(); bool isDirty() const; void block(); @@ -64,7 +61,6 @@ signals: protected: static QThreadPool *extraCompilerThreadPool(); - Utils::FutureSynchronizer *futureSynchronizer() const; void setContent(const Utils::FilePath &file, const QByteArray &content); void updateCompileTime(); Utils::Environment buildEnvironment() const; @@ -79,7 +75,7 @@ private: void compileContent(const QByteArray &content); void compileImpl(const ContentProvider &provider); void compileIfDirty(); - virtual Utils::Tasking::TaskItem taskItemImpl(const ContentProvider &provider) = 0; + virtual Tasking::TaskItem taskItemImpl(const ContentProvider &provider) = 0; const std::unique_ptr<ExtraCompilerPrivate> d; }; @@ -100,13 +96,13 @@ protected: virtual bool prepareToRun(const QByteArray &sourceContents); - virtual FileNameToContentsHash handleProcessFinished(Utils::QtcProcess *process) = 0; + virtual FileNameToContentsHash handleProcessFinished(Utils::Process *process) = 0; virtual Tasks parseIssues(const QByteArray &stdErr); private: - Utils::Tasking::TaskItem taskItemImpl(const ContentProvider &provider) final; - void runInThread(QFutureInterface<FileNameToContentsHash> &futureInterface, + Tasking::TaskItem taskItemImpl(const ContentProvider &provider) final; + void runInThread(QPromise<FileNameToContentsHash> &promise, const Utils::FilePath &cmd, const Utils::FilePath &workDir, const QStringList &args, const ContentProvider &provider, const Utils::Environment &env); diff --git a/src/plugins/projectexplorer/fileinsessionfinder.cpp b/src/plugins/projectexplorer/fileinsessionfinder.cpp index 78f75ce1d5..d8df6c7430 100644 --- a/src/plugins/projectexplorer/fileinsessionfinder.cpp +++ b/src/plugins/projectexplorer/fileinsessionfinder.cpp @@ -4,7 +4,7 @@ #include "fileinsessionfinder.h" #include "project.h" -#include "session.h" +#include "projectmanager.h" #include <utils/fileinprojectfinder.h> @@ -30,12 +30,12 @@ private: FileInSessionFinder::FileInSessionFinder() { - connect(SessionManager::instance(), &SessionManager::projectAdded, + connect(ProjectManager::instance(), &ProjectManager::projectAdded, this, [this](const Project *p) { invalidateFinder(); connect(p, &Project::fileListChanged, this, &FileInSessionFinder::invalidateFinder); }); - connect(SessionManager::instance(), &SessionManager::projectRemoved, + connect(ProjectManager::instance(), &ProjectManager::projectRemoved, this, [this](const Project *p) { invalidateFinder(); p->disconnect(this); @@ -45,11 +45,11 @@ FileInSessionFinder::FileInSessionFinder() FilePaths FileInSessionFinder::doFindFile(const FilePath &filePath) { if (!m_finderIsUpToDate) { - m_finder.setProjectDirectory(SessionManager::startupProject() - ? SessionManager::startupProject()->projectDirectory() + m_finder.setProjectDirectory(ProjectManager::startupProject() + ? ProjectManager::startupProject()->projectDirectory() : FilePath()); FilePaths allFiles; - for (const Project * const p : SessionManager::projects()) + for (const Project * const p : ProjectManager::projects()) allFiles << p->files(Project::SourceFiles); m_finder.setProjectFiles(allFiles); m_finderIsUpToDate = true; diff --git a/src/plugins/projectexplorer/filesinallprojectsfind.cpp b/src/plugins/projectexplorer/filesinallprojectsfind.cpp index 720cfbabae..0f9d6c40e2 100644 --- a/src/plugins/projectexplorer/filesinallprojectsfind.cpp +++ b/src/plugins/projectexplorer/filesinallprojectsfind.cpp @@ -5,7 +5,7 @@ #include "project.h" #include "projectexplorertr.h" -#include "session.h" +#include "projectmanager.h" #include <coreplugin/editormanager/editormanager.h> #include <utils/algorithm.h> @@ -52,7 +52,7 @@ Utils::FileIterator *FilesInAllProjectsFind::files(const QStringList &nameFilter const QVariant &additionalParameters) const { Q_UNUSED(additionalParameters) - const QSet<FilePath> dirs = Utils::transform<QSet>(SessionManager::projects(), [](Project *p) { + const QSet<FilePath> dirs = Utils::transform<QSet>(ProjectManager::projects(), [](Project *p) { return p->projectFilePath().parentDir(); }); return new SubDirFileIterator(FilePaths(dirs.constBegin(), dirs.constEnd()), diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index d2449bd69a..4012fbd669 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -20,8 +20,8 @@ #include <utils/environment.h> #include <utils/hostosinfo.h> #include <utils/pathchooser.h> +#include <utils/process.h> #include <utils/qtcassert.h> -#include <utils/qtcprocess.h> #include <QBuffer> #include <QCheckBox> @@ -119,7 +119,7 @@ static QString runGcc(const FilePath &gcc, const QStringList &arguments, const E if (!gcc.isExecutableFile()) return {}; - QtcProcess cpp; + Process cpp; Environment environment(env); environment.setupEnglishOutput(); @@ -196,7 +196,7 @@ HeaderPaths GccToolChain::gccHeaderPaths(const FilePath &gcc, } const FilePath headerPath - = FilePath::fromString(QString::fromUtf8(line)).onDevice(gcc).canonicalPath(); + = gcc.withNewPath(QString::fromUtf8(line)).canonicalPath(); if (!headerPath.isEmpty()) builtInHeaderPaths.append({headerPath, thisHeaderKind}); @@ -569,7 +569,7 @@ WarningFlags GccToolChain::warningFlags(const QStringList &cflags) const return flags; } -QStringList GccToolChain::includedFiles(const QStringList &flags, const QString &directoryPath) const +FilePaths GccToolChain::includedFiles(const QStringList &flags, const FilePath &directoryPath) const { return ToolChain::includedFiles("-include", flags, directoryPath, PossiblyConcatenatedFlag::No); } @@ -1040,10 +1040,9 @@ GccToolChainFactory::GccToolChainFactory() Toolchains GccToolChainFactory::autoDetect(const ToolchainDetector &detector) const { // GCC is almost never what you want on macOS, but it is by default found in /usr/bin - if (HostOsInfo::isMacHost() - && (!detector.device || detector.device->type() == Constants::DESKTOP_DEVICE_TYPE)) { + if (HostOsInfo::isMacHost() && detector.device->type() == Constants::DESKTOP_DEVICE_TYPE) return {}; - } + Toolchains tcs; static const auto tcChecker = [](const ToolChain *tc) { return tc->targetAbi().osFlavor() != Abi::WindowsMSysFlavor @@ -1086,7 +1085,7 @@ static FilePaths findCompilerCandidates(const ToolchainDetector &detector, { const IDevice::ConstPtr device = detector.device; const QFileInfo fi(compilerName); - if (device.isNull() && fi.isAbsolute() && fi.isFile()) + if (device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE && fi.isAbsolute() && fi.isFile()) return {FilePath::fromString(compilerName)}; QStringList nameFilters(compilerName); @@ -1385,8 +1384,10 @@ void GccToolChainConfigWidget::setFromToolchain() QSignalBlocker blocker(this); auto tc = static_cast<GccToolChain *>(toolChain()); m_compilerCommand->setFilePath(tc->compilerCommand()); - m_platformCodeGenFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->platformCodeGenFlags())); - m_platformLinkerFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->platformLinkerFlags())); + m_platformCodeGenFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->platformCodeGenFlags(), + HostOsInfo::hostOs())); + m_platformLinkerFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->platformLinkerFlags(), + HostOsInfo::hostOs())); if (m_abiWidget) { m_abiWidget->setAbis(tc->supportedAbis(), tc->targetAbi()); if (!m_isReadOnly && !m_compilerCommand->filePath().toString().isEmpty()) @@ -1569,7 +1570,7 @@ bool ClangToolChain::matchesCompilerCommand(const FilePath &command) const m_resolvedCompilerCommand = FilePath(); if (HostOsInfo::isMacHost() && compilerCommand().parentDir() == FilePath::fromString("/usr/bin")) { - QtcProcess xcrun; + Process xcrun; xcrun.setCommand({"/usr/bin/xcrun", {"-f", compilerCommand().fileName()}}); xcrun.runBlocking(); const FilePath output = FilePath::fromString(xcrun.cleanedStdOut().trimmed()); diff --git a/src/plugins/projectexplorer/gcctoolchain.h b/src/plugins/projectexplorer/gcctoolchain.h index 55ba61e9fb..1e6353d0a7 100644 --- a/src/plugins/projectexplorer/gcctoolchain.h +++ b/src/plugins/projectexplorer/gcctoolchain.h @@ -55,8 +55,8 @@ public: Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const override; Utils::WarningFlags warningFlags(const QStringList &cflags) const override; - QStringList includedFiles(const QStringList &flags, - const QString &directoryPath) const override; + Utils::FilePaths includedFiles(const QStringList &flags, + const Utils::FilePath &directoryPath) const override; MacroInspectionRunner createMacroInspectionRunner() const override; BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner(const Utils::Environment &env) const override; diff --git a/src/plugins/projectexplorer/ipotentialkit.h b/src/plugins/projectexplorer/ipotentialkit.h index ae032fb9e3..038a3a9fac 100644 --- a/src/plugins/projectexplorer/ipotentialkit.h +++ b/src/plugins/projectexplorer/ipotentialkit.h @@ -4,18 +4,16 @@ #pragma once #include <QObject> -#include <QMetaType> + #include "projectexplorer_export.h" namespace ProjectExplorer { -class PROJECTEXPLORER_EXPORT IPotentialKit : public QObject +class PROJECTEXPLORER_EXPORT IPotentialKit { - Q_OBJECT - public: IPotentialKit(); - ~IPotentialKit() override; + virtual ~IPotentialKit(); virtual QString displayName() const = 0; virtual void executeFromMenu() = 0; @@ -23,4 +21,4 @@ public: virtual bool isEnabled() const = 0; }; -} +} // ProjectExplorer diff --git a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp index 25a897bd79..49a9571189 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp @@ -16,10 +16,7 @@ #include <utils/algorithm.h> #include <utils/fancylineedit.h> -#include <utils/fileutils.h> #include <utils/qtcassert.h> -#include <utils/runextensions.h> -#include <utils/stringutils.h> #include <utils/theme/theme.h> #include <QApplication> @@ -29,7 +26,6 @@ #include <QDebug> #include <QDir> #include <QFormLayout> -#include <QFutureWatcher> #include <QItemSelectionModel> #include <QLabel> #include <QListView> @@ -603,25 +599,21 @@ void LineEditField::setupCompletion(FancyLineEdit *lineEdit) using namespace Utils; if (m_completion == Completion::None) return; - ILocatorFilter * const classesFilter = findOrDefault( - ILocatorFilter::allLocatorFilters(), - equal(&ILocatorFilter::id, Id("Classes"))); - if (!classesFilter) - return; - classesFilter->prepareSearch({}); - const auto watcher = new QFutureWatcher<LocatorFilterEntry>; - const auto handleResults = [this, lineEdit, watcher](int firstIndex, int endIndex) { + LocatorMatcher *matcher = new LocatorMatcher; + matcher->setParent(lineEdit); + matcher->setTasks(LocatorMatcher::matchers(MatcherType::Classes)); + const auto handleResults = [lineEdit, matcher, completion = m_completion] { QSet<QString> namespaces; QStringList classes; Project * const project = ProjectTree::currentProject(); - for (int i = firstIndex; i < endIndex; ++i) { + const LocatorFilterEntries entries = matcher->outputData(); + for (const LocatorFilterEntry &entry : entries) { static const auto isReservedName = [](const QString &name) { static const QRegularExpression rx1("^_[A-Z].*"); static const QRegularExpression rx2(".*::_[A-Z].*"); return name.contains("__") || rx1.match(name).hasMatch() || rx2.match(name).hasMatch(); }; - const LocatorFilterEntry &entry = watcher->resultAt(i); const bool hasNamespace = !entry.extraInfo.isEmpty() && !entry.extraInfo.startsWith('<') && !entry.extraInfo.contains("::<") && !isReservedName(entry.extraInfo) @@ -635,7 +627,7 @@ void LineEditField::setupCompletion(FancyLineEdit *lineEdit) if (hasNamespace) { if (isBaseClassCandidate) classes << (entry.extraInfo + "::" + entry.displayName); - if (m_completion == Completion::Namespaces) { + if (completion == Completion::Namespaces) { if (!project || entry.filePath.startsWith(project->projectDirectory().toString())) { namespaces << entry.extraInfo; @@ -644,7 +636,7 @@ void LineEditField::setupCompletion(FancyLineEdit *lineEdit) } } QStringList completionList; - if (m_completion == Completion::Namespaces) { + if (completion == Completion::Namespaces) { completionList = toList(namespaces); completionList = filtered(completionList, [&classes](const QString &ns) { return !classes.contains(ns); @@ -658,16 +650,9 @@ void LineEditField::setupCompletion(FancyLineEdit *lineEdit) completionList.sort(); lineEdit->setSpecialCompleter(new QCompleter(completionList, lineEdit)); }; - QObject::connect(watcher, &QFutureWatcher<LocatorFilterEntry>::resultsReadyAt, lineEdit, - handleResults); - QObject::connect(watcher, &QFutureWatcher<LocatorFilterEntry>::finished, - watcher, &QFutureWatcher<LocatorFilterEntry>::deleteLater); - watcher->setFuture(runAsync([classesFilter](QFutureInterface<LocatorFilterEntry> &f) { - const QList<LocatorFilterEntry> matches = classesFilter->matchesFor(f, {}); - if (!matches.isEmpty()) - f.reportResults(QVector<LocatorFilterEntry>(matches.cbegin(), matches.cend())); - f.reportFinished(); - })); + QObject::connect(matcher, &LocatorMatcher::done, lineEdit, handleResults); + QObject::connect(matcher, &LocatorMatcher::done, matcher, &QObject::deleteLater); + matcher->start(); } void LineEditField::setText(const QString &text) diff --git a/src/plugins/projectexplorer/jsonwizard/jsonsummarypage.cpp b/src/plugins/projectexplorer/jsonwizard/jsonsummarypage.cpp index 024044d7a0..ec55e3218f 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonsummarypage.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonsummarypage.cpp @@ -8,8 +8,8 @@ #include "../projectexplorerconstants.h" #include "../projectexplorertr.h" #include "../projectnodes.h" +#include "../projectmanager.h" #include "../projecttree.h" -#include "../session.h" #include <coreplugin/coreconstants.h> #include <coreplugin/iversioncontrol.h> @@ -209,7 +209,7 @@ Node *JsonSummaryPage::findWizardContextNode(Node *contextNode) const // Static cast from void * to avoid qobject_cast (which needs a valid object) in value(). auto project = static_cast<Project *>(m_wizard->value(Constants::PROJECT_POINTER).value<void *>()); - if (SessionManager::projects().contains(project) && project->rootProjectNode()) { + if (ProjectManager::projects().contains(project) && project->rootProjectNode()) { const FilePath path = FilePath::fromVariant(m_wizard->value(Constants::PREFERRED_PROJECT_NODE_PATH)); contextNode = project->rootProjectNode()->findNode([path](const Node *n) { return path == n->filePath(); diff --git a/src/plugins/projectexplorer/kitchooser.cpp b/src/plugins/projectexplorer/kitchooser.cpp index 14c848da74..9e45cacd7d 100644 --- a/src/plugins/projectexplorer/kitchooser.cpp +++ b/src/plugins/projectexplorer/kitchooser.cpp @@ -6,7 +6,7 @@ #include "kitmanager.h" #include "projectexplorerconstants.h" #include "projectexplorertr.h" -#include "session.h" +#include "projectmanager.h" #include "target.h" #include <coreplugin/icore.h> @@ -88,7 +88,7 @@ void KitChooser::populate() const Id lastKit = Id::fromSetting(ICore::settings()->value(lastKitKey)); bool didActivate = false; - if (Target *target = SessionManager::startupTarget()) { + if (Target *target = ProjectManager::startupTarget()) { Kit *kit = target->kit(); if (m_kitPredicate(kit)) { QString display = Tr::tr("Kit of Active Project: %1").arg(kitText(kit)); diff --git a/src/plugins/projectexplorer/kitinformation.cpp b/src/plugins/projectexplorer/kitinformation.cpp index dcb490b70d..fc8039b402 100644 --- a/src/plugins/projectexplorer/kitinformation.cpp +++ b/src/plugins/projectexplorer/kitinformation.cpp @@ -34,7 +34,6 @@ #include <QVBoxLayout> using namespace Utils; -using namespace Utils::Layouting; namespace ProjectExplorer { @@ -65,7 +64,7 @@ public: private: void makeReadOnly() override { m_chooser->setReadOnly(true); } - void addToLayout(LayoutBuilder &builder) override + void addToLayout(Layouting::LayoutItem &builder) override { addMutableAction(m_chooser); builder.addItem(Layouting::Span(2, m_chooser)); @@ -142,7 +141,7 @@ void SysRootKitAspect::addToMacroExpander(Kit *kit, MacroExpander *expander) con }); } -Id SysRootKitAspect::id() +Utils::Id SysRootKitAspect::id() { return "PE.Profile.SysRoot"; } @@ -231,7 +230,7 @@ public: } private: - void addToLayout(LayoutBuilder &builder) override + void addToLayout(Layouting::LayoutItem &builder) override { addMutableAction(m_mainWidget); builder.addItem(m_mainWidget); @@ -760,7 +759,7 @@ public: ~DeviceTypeKitAspectWidget() override { delete m_comboBox; } private: - void addToLayout(LayoutBuilder &builder) override + void addToLayout(Layouting::LayoutItem &builder) override { addMutableAction(m_comboBox); builder.addItem(m_comboBox); @@ -795,7 +794,7 @@ DeviceTypeKitAspect::DeviceTypeKitAspect() { setObjectName(QLatin1String("DeviceTypeInformation")); setId(DeviceTypeKitAspect::id()); - setDisplayName(Tr::tr("Device type")); + setDisplayName(Tr::tr("Run device type")); setDescription(Tr::tr("The type of device to run applications on.")); setPriority(33000); makeEssential(); @@ -896,7 +895,7 @@ public: } private: - void addToLayout(LayoutBuilder &builder) override + void addToLayout(Layouting::LayoutItem &builder) override { addMutableAction(m_comboBox); builder.addItem(m_comboBox); @@ -942,7 +941,7 @@ DeviceKitAspect::DeviceKitAspect() { setObjectName(QLatin1String("DeviceInformation")); setId(DeviceKitAspect::id()); - setDisplayName(Tr::tr("Device")); + setDisplayName(Tr::tr("Run device")); setDescription(Tr::tr("The device to run the applications on.")); setPriority(32000); @@ -1023,30 +1022,29 @@ KitAspect::ItemList DeviceKitAspect::toUserOutput(const Kit *k) const void DeviceKitAspect::addToMacroExpander(Kit *kit, MacroExpander *expander) const { QTC_ASSERT(kit, return); - expander->registerVariable("Device:HostAddress", Tr::tr("Host address"), - [kit]() -> QString { - const IDevice::ConstPtr device = DeviceKitAspect::device(kit); - return device ? device->sshParameters().host() : QString(); + expander->registerVariable("Device:HostAddress", Tr::tr("Host address"), [kit] { + const IDevice::ConstPtr device = DeviceKitAspect::device(kit); + return device ? device->sshParameters().host() : QString(); }); - expander->registerVariable("Device:SshPort", Tr::tr("SSH port"), - [kit]() -> QString { - const IDevice::ConstPtr device = DeviceKitAspect::device(kit); - return device ? QString::number(device->sshParameters().port()) : QString(); + expander->registerVariable("Device:SshPort", Tr::tr("SSH port"), [kit] { + const IDevice::ConstPtr device = DeviceKitAspect::device(kit); + return device ? QString::number(device->sshParameters().port()) : QString(); }); - expander->registerVariable("Device:UserName", Tr::tr("User name"), - [kit]() -> QString { - const IDevice::ConstPtr device = DeviceKitAspect::device(kit); - return device ? device->sshParameters().userName() : QString(); + expander->registerVariable("Device:UserName", Tr::tr("User name"), [kit] { + const IDevice::ConstPtr device = DeviceKitAspect::device(kit); + return device ? device->sshParameters().userName() : QString(); }); - expander->registerVariable("Device:KeyFile", Tr::tr("Private key file"), - [kit]() -> QString { - const IDevice::ConstPtr device = DeviceKitAspect::device(kit); - return device ? device->sshParameters().privateKeyFile.toString() : QString(); + expander->registerVariable("Device:KeyFile", Tr::tr("Private key file"), [kit] { + const IDevice::ConstPtr device = DeviceKitAspect::device(kit); + return device ? device->sshParameters().privateKeyFile.toString() : QString(); }); - expander->registerVariable("Device:Name", Tr::tr("Device name"), - [kit]() -> QString { - const IDevice::ConstPtr device = DeviceKitAspect::device(kit); - return device ? device->displayName() : QString(); + expander->registerVariable("Device:Name", Tr::tr("Device name"), [kit] { + const IDevice::ConstPtr device = DeviceKitAspect::device(kit); + return device ? device->displayName() : QString(); + }); + expander->registerFileVariables("Device::Root", Tr::tr("Device root directory"), [kit] { + const IDevice::ConstPtr device = DeviceKitAspect::device(kit); + return device ? device->rootPath() : FilePath{}; }); } @@ -1157,7 +1155,7 @@ public: } private: - void addToLayout(LayoutBuilder &builder) override + void addToLayout(Layouting::LayoutItem &builder) override { addMutableAction(m_comboBox); builder.addItem(m_comboBox); @@ -1266,31 +1264,31 @@ KitAspect::ItemList BuildDeviceKitAspect::toUserOutput(const Kit *k) const void BuildDeviceKitAspect::addToMacroExpander(Kit *kit, MacroExpander *expander) const { QTC_ASSERT(kit, return); - expander->registerVariable("BuildDevice:HostAddress", Tr::tr("Build host address"), - [kit]() -> QString { - const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit); - return device ? device->sshParameters().host() : QString(); + expander->registerVariable("BuildDevice:HostAddress", Tr::tr("Build host address"), [kit] { + const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit); + return device ? device->sshParameters().host() : QString(); }); - expander->registerVariable("BuildDevice:SshPort", Tr::tr("Build SSH port"), - [kit]() -> QString { - const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit); - return device ? QString::number(device->sshParameters().port()) : QString(); + expander->registerVariable("BuildDevice:SshPort", Tr::tr("Build SSH port"), [kit] { + const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit); + return device ? QString::number(device->sshParameters().port()) : QString(); }); - expander->registerVariable("BuildDevice:UserName", Tr::tr("Build user name"), - [kit]() -> QString { - const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit); - return device ? device->sshParameters().userName() : QString(); + expander->registerVariable("BuildDevice:UserName", Tr::tr("Build user name"), [kit] { + const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit); + return device ? device->sshParameters().userName() : QString(); }); - expander->registerVariable("BuildDevice:KeyFile", Tr::tr("Build private key file"), - [kit]() -> QString { - const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit); - return device ? device->sshParameters().privateKeyFile.toString() : QString(); + expander->registerVariable("BuildDevice:KeyFile", Tr::tr("Build private key file"), [kit] { + const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit); + return device ? device->sshParameters().privateKeyFile.toString() : QString(); }); - expander->registerVariable("BuildDevice:Name", Tr::tr("Build device name"), - [kit]() -> QString { - const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit); - return device ? device->displayName() : QString(); + expander->registerVariable("BuildDevice:Name", Tr::tr("Build device name"), [kit] { + const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit); + return device ? device->displayName() : QString(); }); + expander + ->registerFileVariables("BuildDevice::Root", Tr::tr("Build device root directory"), [kit] { + const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit); + return device ? device->rootPath() : FilePath{}; + }); } Id BuildDeviceKitAspect::id() @@ -1388,7 +1386,7 @@ public: } private: - void addToLayout(LayoutBuilder &builder) override + void addToLayout(Layouting::LayoutItem &builder) override { addMutableAction(m_mainWidget); builder.addItem(m_mainWidget); diff --git a/src/plugins/projectexplorer/kitmanager.cpp b/src/plugins/projectexplorer/kitmanager.cpp index cb67e11fac..86a3b533c1 100644 --- a/src/plugins/projectexplorer/kitmanager.cpp +++ b/src/plugins/projectexplorer/kitmanager.cpp @@ -747,7 +747,7 @@ KitAspectWidget::~KitAspectWidget() delete m_mutableAction; } -void KitAspectWidget::addToLayoutWithLabel(QWidget *parent) +void KitAspectWidget::addToLayoutWithLabel(Layouting::LayoutItem &parentItem, QWidget *parent) { QTC_ASSERT(parent, return); auto label = createSubWidget<QLabel>(m_kitInformation->displayName() + ':'); @@ -756,10 +756,9 @@ void KitAspectWidget::addToLayoutWithLabel(QWidget *parent) emit labelLinkActivated(link); }); - Layouting::LayoutExtender builder(parent->layout(), Layouting::WithFormAlignment); - builder.finishRow(); - builder.addItem(label); - addToLayout(builder); + parentItem.addItem(label); + addToLayout(parentItem); + parentItem.addItem(Layouting::br); } void KitAspectWidget::addMutableAction(QWidget *child) diff --git a/src/plugins/projectexplorer/kitmanager.h b/src/plugins/projectexplorer/kitmanager.h index a494fde323..f25f83ca7f 100644 --- a/src/plugins/projectexplorer/kitmanager.h +++ b/src/plugins/projectexplorer/kitmanager.h @@ -117,7 +117,7 @@ public: virtual void makeReadOnly() = 0; virtual void refresh() = 0; - void addToLayoutWithLabel(QWidget *parent); + void addToLayoutWithLabel(Layouting::LayoutItem &parentItem, QWidget *parent); static QString msgManage(); diff --git a/src/plugins/projectexplorer/kitmanagerconfigwidget.cpp b/src/plugins/projectexplorer/kitmanagerconfigwidget.cpp index 908238d3a7..822844f57d 100644 --- a/src/plugins/projectexplorer/kitmanagerconfigwidget.cpp +++ b/src/plugins/projectexplorer/kitmanagerconfigwidget.cpp @@ -23,6 +23,7 @@ #include <QRegularExpression> #include <QRegularExpressionValidator> #include <QFileDialog> +#include <QGridLayout> #include <QLabel> #include <QLineEdit> #include <QMenu> @@ -36,12 +37,14 @@ using namespace Utils; namespace ProjectExplorer { namespace Internal { -KitManagerConfigWidget::KitManagerConfigWidget(Kit *k) : +KitManagerConfigWidget::KitManagerConfigWidget(Kit *k, bool &isDefaultKit, bool &hasUniqueName) : m_iconButton(new QToolButton), m_nameEdit(new QLineEdit), m_fileSystemFriendlyNameLineEdit(new QLineEdit), m_kit(k), - m_modifiedKit(std::make_unique<Kit>(Utils::Id(WORKING_COPY_KIT_ID))) + m_modifiedKit(std::make_unique<Kit>(Utils::Id(WORKING_COPY_KIT_ID))), + m_isDefaultKit(isDefaultKit), + m_hasUniqueName(hasUniqueName) { setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); @@ -64,10 +67,13 @@ KitManagerConfigWidget::KitManagerConfigWidget(Kit *k) : this, &KitManagerConfigWidget::setFileSystemFriendlyName); using namespace Layouting; - Grid { + Grid page { + withFormAlignment, + columnStretch(1, 2), label, m_nameEdit, m_iconButton, br, - fsLabel, m_fileSystemFriendlyNameLineEdit - }.attachTo(this, WithFormAlignment); + fsLabel, m_fileSystemFriendlyNameLineEdit, br, + noMargin + }; m_iconButton->setToolTip(Tr::tr("Kit icon.")); auto setIconAction = new QAction(Tr::tr("Select Icon..."), this); @@ -97,7 +103,9 @@ KitManagerConfigWidget::KitManagerConfigWidget(Kit *k) : chooser->addMacroExpanderProvider([this] { return m_modifiedKit->macroExpander(); }); for (KitAspect *aspect : KitManager::kitAspects()) - addAspectToWorkingCopy(aspect); + addAspectToWorkingCopy(page, aspect); + + page.attachTo(this); updateVisibility(); @@ -187,14 +195,14 @@ QString KitManagerConfigWidget::validityMessage() const return m_modifiedKit->toHtml(tmp); } -void KitManagerConfigWidget::addAspectToWorkingCopy(KitAspect *aspect) +void KitManagerConfigWidget::addAspectToWorkingCopy(Layouting::LayoutItem &parent, KitAspect *aspect) { QTC_ASSERT(aspect, return); KitAspectWidget *widget = aspect->createConfigWidget(workingCopy()); QTC_ASSERT(widget, return); QTC_ASSERT(!m_widgets.contains(widget), return); - widget->addToLayoutWithLabel(this); + widget->addToLayoutWithLabel(parent, this); m_widgets.append(widget); connect(widget->mutableAction(), &QAction::toggled, @@ -213,11 +221,6 @@ void KitManagerConfigWidget::updateVisibility() } } -void KitManagerConfigWidget::setHasUniqueName(bool unique) -{ - m_hasUniqueName = unique; -} - void KitManagerConfigWidget::makeStickySubWidgetsReadOnly() { for (KitAspectWidget *w : std::as_const(m_widgets)) { @@ -231,31 +234,11 @@ Kit *KitManagerConfigWidget::workingCopy() const return m_modifiedKit.get(); } -bool KitManagerConfigWidget::configures(Kit *k) const -{ - return m_kit == k; -} - -void KitManagerConfigWidget::setIsDefaultKit(bool d) -{ - if (m_isDefaultKit == d) - return; - m_isDefaultKit = d; - emit dirty(); -} - bool KitManagerConfigWidget::isDefaultKit() const { return m_isDefaultKit; } -void KitManagerConfigWidget::removeKit() -{ - if (!m_kit) - return; - KitManager::deregisterKit(m_kit); -} - void KitManagerConfigWidget::setIcon() { const Utils::Id deviceType = DeviceTypeKitAspect::deviceTypeId(m_modifiedKit.get()); diff --git a/src/plugins/projectexplorer/kitmanagerconfigwidget.h b/src/plugins/projectexplorer/kitmanagerconfigwidget.h index 142eb8fa36..94218c5de8 100644 --- a/src/plugins/projectexplorer/kitmanagerconfigwidget.h +++ b/src/plugins/projectexplorer/kitmanagerconfigwidget.h @@ -25,7 +25,7 @@ class KitManagerConfigWidget : public QWidget Q_OBJECT public: - explicit KitManagerConfigWidget(Kit *k); + explicit KitManagerConfigWidget(Kit *k, bool &isDefaultKit, bool &hasUniqueName); ~KitManagerConfigWidget() override; QString displayName() const; @@ -35,19 +35,14 @@ public: void discard(); bool isDirty() const; QString validityMessage() const; - void addAspectToWorkingCopy(KitAspect *aspect); + void addAspectToWorkingCopy(Layouting::LayoutItem &parent, KitAspect *aspect); void makeStickySubWidgetsReadOnly(); Kit *workingCopy() const; - bool configures(Kit *k) const; bool isRegistering() const { return m_isRegistering; } - void setIsDefaultKit(bool d); bool isDefaultKit() const; - void removeKit(); void updateVisibility(); - void setHasUniqueName(bool unique); - signals: void dirty(); void isAutoDetectedChanged(); @@ -74,9 +69,9 @@ private: QList<KitAspectWidget *> m_widgets; Kit *m_kit; std::unique_ptr<Kit> m_modifiedKit; - bool m_isDefaultKit = false; + bool &m_isDefaultKit; bool m_fixingKit = false; - bool m_hasUniqueName = true; + bool &m_hasUniqueName; bool m_isRegistering = false; mutable QString m_cachedDisplayName; }; diff --git a/src/plugins/projectexplorer/kitmodel.cpp b/src/plugins/projectexplorer/kitmodel.cpp index 4b250f23c9..12f23fb281 100644 --- a/src/plugins/projectexplorer/kitmodel.cpp +++ b/src/plugins/projectexplorer/kitmodel.cpp @@ -24,57 +24,122 @@ namespace Internal { class KitNode : public TreeItem { public: - KitNode(Kit *k, KitModel *m) + KitNode(Kit *k, KitModel *m, QBoxLayout *parentLayout) + : m_kit(k), m_model(m), m_parentLayout(parentLayout) + {} + + ~KitNode() override { delete m_widget; } + + Kit *kit() const { return m_kit; } + + QVariant data(int, int role) const override { - widget = new KitManagerConfigWidget(k); + if (role == Qt::FontRole) { + QFont f = QApplication::font(); + if (isDirty()) + f.setBold(!f.bold()); + if (isDefaultKit()) + f.setItalic(f.style() != QFont::StyleItalic); + return f; + } + if (role == Qt::DisplayRole) { + QString baseName = displayName(); + if (isDefaultKit()) + //: Mark up a kit as the default one. + baseName = Tr::tr("%1 (default)").arg(baseName); + return baseName; + } - QObject::connect(widget, &KitManagerConfigWidget::dirty, m, [this] { update(); }); + if (role == Qt::DecorationRole) + return displayIcon(); - QObject::connect(widget, &KitManagerConfigWidget::isAutoDetectedChanged, m, [this, m] { - TreeItem *oldParent = parent(); - TreeItem *newParent = - m->rootItem()->childAt(widget->workingCopy()->isAutoDetected() ? 0 : 1); - if (oldParent && oldParent != newParent) { - m->takeItem(this); - newParent->appendChild(this); - } - }); + if (role == Qt::ToolTipRole) + return widget()->validityMessage(); + + return {}; } - ~KitNode() override + bool isDirty() const { - delete widget; + if (m_widget) + return m_widget->isDirty(); + return false; } - QVariant data(int, int role) const override + QIcon displayIcon() const { - if (widget) { - if (role == Qt::FontRole) { - QFont f = QApplication::font(); - if (widget->isDirty()) - f.setBold(!f.bold()); - if (widget->isDefaultKit()) - f.setItalic(f.style() != QFont::StyleItalic); - return f; - } - if (role == Qt::DisplayRole) { - QString baseName = widget->displayName(); - if (widget->isDefaultKit()) - //: Mark up a kit as the default one. - baseName = Tr::tr("%1 (default)").arg(baseName); - return baseName; - } - if (role == Qt::DecorationRole) { - return widget->displayIcon(); - } - if (role == Qt::ToolTipRole) { - return widget->validityMessage(); + if (m_widget) + return m_widget->displayIcon(); + return m_kit->displayIcon(); + } + + QString displayName() const + { + if (m_widget) + return m_widget->displayName(); + return m_kit->displayName(); + } + + bool isDefaultKit() const + { + return m_isDefaultKit; + } + + bool isRegistering() const + { + if (m_widget) + return m_widget->isRegistering(); + return false; + } + + void setIsDefaultKit(bool on) + { + if (m_isDefaultKit == on) + return; + m_isDefaultKit = on; + if (m_widget) + emit m_widget->dirty(); + } + + KitManagerConfigWidget *widget() const + { + const_cast<KitNode *>(this)->ensureWidget(); + return m_widget; + } + + void setHasUniqueName(bool on) + { + m_hasUniqueName = on; + } + +private: + void ensureWidget() + { + if (m_widget) + return; + + m_widget = new KitManagerConfigWidget(m_kit, m_isDefaultKit, m_hasUniqueName); + + QObject::connect(m_widget, &KitManagerConfigWidget::dirty, m_model, [this] { update(); }); + + QObject::connect(m_widget, &KitManagerConfigWidget::isAutoDetectedChanged, m_model, [this] { + TreeItem *oldParent = parent(); + TreeItem *newParent = + m_model->rootItem()->childAt(m_widget->workingCopy()->isAutoDetected() ? 0 : 1); + if (oldParent && oldParent != newParent) { + m_model->takeItem(this); + newParent->appendChild(this); } - } - return QVariant(); + }); + m_parentLayout->addWidget(m_widget); } - KitManagerConfigWidget *widget; + Kit *m_kit = m_kit; + KitModel *m_model = nullptr; + KitManagerConfigWidget *m_widget = nullptr; + QBoxLayout *m_parentLayout = nullptr; + bool m_isDefaultKit = false; + bool m_hasUniqueName = true; }; // -------------------------------------------------------------------------- @@ -113,7 +178,7 @@ KitModel::KitModel(QBoxLayout *parentLayout, QObject *parent) Kit *KitModel::kit(const QModelIndex &index) { KitNode *n = kitNode(index); - return n ? n->widget->workingCopy() : nullptr; + return n ? n->widget()->workingCopy() : nullptr; } KitNode *KitModel::kitNode(const QModelIndex &index) @@ -136,20 +201,20 @@ void KitModel::setDefaultKit(const QModelIndex &index) bool KitModel::isDefaultKit(Kit *k) const { - return m_defaultNode && m_defaultNode->widget->workingCopy() == k; + return m_defaultNode && m_defaultNode->widget()->workingCopy() == k; } KitManagerConfigWidget *KitModel::widget(const QModelIndex &index) { KitNode *n = kitNode(index); - return n ? n->widget : nullptr; + return n ? n->widget() : nullptr; } void KitModel::validateKitNames() { QHash<QString, int> nameHash; forItemsAtLevel<2>([&nameHash](KitNode *n) { - const QString displayName = n->widget->displayName(); + const QString displayName = n->displayName(); if (nameHash.contains(displayName)) ++nameHash[displayName]; else @@ -157,8 +222,8 @@ void KitModel::validateKitNames() }); forItemsAtLevel<2>([&nameHash](KitNode *n) { - const QString displayName = n->widget->displayName(); - n->widget->setHasUniqueName(nameHash.value(displayName) == 1); + const QString displayName = n->displayName(); + n->setHasUniqueName(nameHash.value(displayName) == 1); }); } @@ -166,8 +231,8 @@ void KitModel::apply() { // Add/update dirty nodes before removing kits. This ensures the right kit ends up as default. forItemsAtLevel<2>([](KitNode *n) { - if (n->widget->isDirty()) { - n->widget->apply(); + if (n->isDirty()) { + n->widget()->apply(); n->update(); } }); @@ -175,7 +240,7 @@ void KitModel::apply() // Remove unused kits: const QList<KitNode *> removeList = m_toRemoveList; for (KitNode *n : removeList) - n->widget->removeKit(); + KitManager::deregisterKit(n->kit()); emit layoutChanged(); // Force update. } @@ -197,7 +262,7 @@ void KitModel::markForRemoval(Kit *k) setDefaultNode(findItemAtLevel<2>([node](KitNode *kn) { return kn != node; })); takeItem(node); - if (node->widget->configures(nullptr)) + if (node->kit() == nullptr) delete node; else m_toRemoveList.append(node); @@ -209,7 +274,7 @@ Kit *KitModel::markForAddition(Kit *baseKit) const QString newName = newKitName(baseKit ? baseKit->unexpandedDisplayName() : QString()); KitNode *node = createNode(nullptr); m_manualRoot->appendChild(node); - Kit *k = node->widget->workingCopy(); + Kit *k = node->widget()->workingCopy(); KitGuard g(k); if (baseKit) { k->copyFrom(baseKit); @@ -229,7 +294,7 @@ Kit *KitModel::markForAddition(Kit *baseKit) void KitModel::updateVisibility() { forItemsAtLevel<2>([](const TreeItem *ti) { - static_cast<const KitNode *>(ti)->widget->updateVisibility(); + static_cast<const KitNode *>(ti)->widget()->updateVisibility(); }); } @@ -237,32 +302,31 @@ QString KitModel::newKitName(const QString &sourceName) const { QList<Kit *> allKits; forItemsAtLevel<2>([&allKits](const TreeItem *ti) { - allKits << static_cast<const KitNode *>(ti)->widget->workingCopy(); + allKits << static_cast<const KitNode *>(ti)->widget()->workingCopy(); }); return Kit::newKitName(sourceName, allKits); } KitNode *KitModel::findWorkingCopy(Kit *k) const { - return findItemAtLevel<2>([k](KitNode *n) { return n->widget->workingCopy() == k; }); + return findItemAtLevel<2>([k](KitNode *n) { return n->widget()->workingCopy() == k; }); } KitNode *KitModel::createNode(Kit *k) { - auto node = new KitNode(k, this); - m_parentLayout->addWidget(node->widget); + auto node = new KitNode(k, this, m_parentLayout); return node; } void KitModel::setDefaultNode(KitNode *node) { if (m_defaultNode) { - m_defaultNode->widget->setIsDefaultKit(false); + m_defaultNode->setIsDefaultKit(false); m_defaultNode->update(); } m_defaultNode = node; if (m_defaultNode) { - m_defaultNode->widget->setIsDefaultKit(true); + m_defaultNode->setIsDefaultKit(true); m_defaultNode->update(); } } @@ -271,7 +335,7 @@ void KitModel::addKit(Kit *k) { for (TreeItem *n : *m_manualRoot) { // Was added by us - if (static_cast<KitNode *>(n)->widget->isRegistering()) + if (static_cast<KitNode *>(n)->isRegistering()) return; } @@ -292,7 +356,7 @@ void KitModel::removeKit(Kit *k) { QList<KitNode *> nodes = m_toRemoveList; for (KitNode *n : std::as_const(nodes)) { - if (n->widget->configures(k)) { + if (n->kit() == k) { m_toRemoveList.removeOne(n); if (m_defaultNode == n) m_defaultNode = nullptr; @@ -303,7 +367,7 @@ void KitModel::removeKit(Kit *k) } KitNode *node = findItemAtLevel<2>([k](KitNode *n) { - return n->widget->configures(k); + return n->kit() == k; }); if (node == m_defaultNode) @@ -319,7 +383,7 @@ void KitModel::changeDefaultKit() { Kit *defaultKit = KitManager::defaultKit(); KitNode *node = findItemAtLevel<2>([defaultKit](KitNode *n) { - return n->widget->configures(defaultKit); + return n->kit() == defaultKit; }); setDefaultNode(node); } diff --git a/src/plugins/projectexplorer/kitoptionspage.cpp b/src/plugins/projectexplorer/kitoptionspage.cpp index d5c709194a..db578a403e 100644 --- a/src/plugins/projectexplorer/kitoptionspage.cpp +++ b/src/plugins/projectexplorer/kitoptionspage.cpp @@ -23,11 +23,13 @@ namespace ProjectExplorer { namespace Internal { -// -------------------------------------------------------------------------- -// KitOptionsPageWidget: -// -------------------------------------------------------------------------- +// KitOptionsPageWidget + +class KitOptionsPageWidget; -class KitOptionsPageWidget : public QWidget +static QPointer<KitOptionsPageWidget> kitOptionsPageWidget; + +class KitOptionsPageWidget : public Core::IOptionsPageWidget { public: KitOptionsPageWidget(); @@ -42,6 +44,8 @@ public: void makeDefaultKit(); void updateState(); + void apply() final { m_model->apply(); } + public: QTreeView *m_kitsView = nullptr; QPushButton *m_addButton = nullptr; @@ -58,6 +62,7 @@ public: KitOptionsPageWidget::KitOptionsPageWidget() { + kitOptionsPageWidget = this; m_kitsView = new QTreeView(this); m_kitsView->setUniformRowHeights(true); m_kitsView->header()->setStretchLastSection(true); @@ -239,38 +244,14 @@ QModelIndex KitOptionsPageWidget::currentIndex() const // KitOptionsPage: // -------------------------------------------------------------------------- -static KitOptionsPage *theKitOptionsPage = nullptr; - KitOptionsPage::KitOptionsPage() { - theKitOptionsPage = this; setId(Constants::KITS_SETTINGS_PAGE_ID); setDisplayName(Tr::tr("Kits")); setCategory(Constants::KITS_SETTINGS_CATEGORY); setDisplayCategory(Tr::tr("Kits")); setCategoryIconPath(":/projectexplorer/images/settingscategory_kits.png"); -} - -QWidget *KitOptionsPage::widget() -{ - if (!m_widget) - m_widget = new Internal::KitOptionsPageWidget; - - return m_widget; -} - -void KitOptionsPage::apply() -{ - if (m_widget) - m_widget->m_model->apply(); -} - -void KitOptionsPage::finish() -{ - if (m_widget) { - delete m_widget; - m_widget = nullptr; - } + setWidgetCreator([] { return new Internal::KitOptionsPageWidget; }); } void KitOptionsPage::showKit(Kit *k) @@ -278,18 +259,16 @@ void KitOptionsPage::showKit(Kit *k) if (!k) return; - (void) widget(); - QModelIndex index = m_widget->m_model->indexOf(k); - m_widget->m_selectionModel->select(index, + Internal::KitOptionsPageWidget *widget = Internal::kitOptionsPageWidget; + if (!widget) + return; + + QModelIndex index = widget->m_model->indexOf(k); + widget->m_selectionModel->select(index, QItemSelectionModel::Clear | QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); - m_widget->m_kitsView->scrollTo(index); -} - -KitOptionsPage *KitOptionsPage::instance() -{ - return theKitOptionsPage; + widget->m_kitsView->scrollTo(index); } } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/kitoptionspage.h b/src/plugins/projectexplorer/kitoptionspage.h index 5ce589cd6b..a7a4ba29db 100644 --- a/src/plugins/projectexplorer/kitoptionspage.h +++ b/src/plugins/projectexplorer/kitoptionspage.h @@ -7,12 +7,8 @@ #include <coreplugin/dialogs/ioptionspage.h> -#include <QPointer> - namespace ProjectExplorer { -namespace Internal { class KitOptionsPageWidget; } - class Kit; class PROJECTEXPLORER_EXPORT KitOptionsPage : public Core::IOptionsPage @@ -20,15 +16,7 @@ class PROJECTEXPLORER_EXPORT KitOptionsPage : public Core::IOptionsPage public: KitOptionsPage(); - QWidget *widget() override; - void apply() override; - void finish() override; - - void showKit(Kit *k); - static KitOptionsPage *instance(); - -private: - QPointer<Internal::KitOptionsPageWidget> m_widget; + static void showKit(Kit *k); }; } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/localenvironmentaspect.cpp b/src/plugins/projectexplorer/localenvironmentaspect.cpp deleted file mode 100644 index 7510d636e5..0000000000 --- a/src/plugins/projectexplorer/localenvironmentaspect.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// 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 "localenvironmentaspect.h" - -#include "buildconfiguration.h" -#include "kit.h" -#include "projectexplorertr.h" -#include "target.h" - -using namespace Utils; - -namespace ProjectExplorer { - -LocalEnvironmentAspect::LocalEnvironmentAspect(Target *target, bool includeBuildEnvironment) -{ - setIsLocal(true); - addSupportedBaseEnvironment(Tr::tr("Clean Environment"), {}); - - addSupportedBaseEnvironment(Tr::tr("System Environment"), [] { - return Environment::systemEnvironment(); - }); - - if (includeBuildEnvironment) { - addPreferredBaseEnvironment(Tr::tr("Build Environment"), [target] { - Environment env; - if (BuildConfiguration *bc = target->activeBuildConfiguration()) { - env = bc->environment(); - } else { // Fallback for targets without buildconfigurations: - env = target->kit()->buildEnvironment(); - } - return env; - }); - - connect(target, - &Target::activeBuildConfigurationChanged, - this, - &EnvironmentAspect::environmentChanged); - connect(target, - &Target::buildEnvironmentChanged, - this, - &EnvironmentAspect::environmentChanged); - } -} - -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/localenvironmentaspect.h b/src/plugins/projectexplorer/localenvironmentaspect.h deleted file mode 100644 index ffe2556947..0000000000 --- a/src/plugins/projectexplorer/localenvironmentaspect.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "environmentaspect.h" - -namespace ProjectExplorer { - -class PROJECTEXPLORER_EXPORT LocalEnvironmentAspect : public EnvironmentAspect -{ - Q_OBJECT - -public: - explicit LocalEnvironmentAspect(Target *parent, bool includeBuildEnvironment = true); -}; - -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/makestep.cpp b/src/plugins/projectexplorer/makestep.cpp index 57ebc2490b..ee9ffea832 100644 --- a/src/plugins/projectexplorer/makestep.cpp +++ b/src/plugins/projectexplorer/makestep.cpp @@ -18,13 +18,10 @@ #include <utils/hostosinfo.h> #include <utils/layoutbuilder.h> #include <utils/pathchooser.h> -#include <utils/qtcprocess.h> +#include <utils/process.h> #include <utils/utilsicons.h> #include <utils/variablechooser.h> -#include <QCheckBox> -#include <QLabel> -#include <QLineEdit> #include <QThread> #include <optional> @@ -49,9 +46,8 @@ MakeStep::MakeStep(BuildStepList *parent, Id id) setCommandLineProvider([this] { return effectiveMakeCommand(Execution); }); - m_makeCommandAspect = addAspect<StringAspect>(); + m_makeCommandAspect = addAspect<FilePathAspect>(); m_makeCommandAspect->setSettingsKey(id.withSuffix(MAKE_COMMAND_SUFFIX).toString()); - m_makeCommandAspect->setDisplayStyle(StringAspect::PathChooserDisplay); m_makeCommandAspect->setExpectedKind(PathChooser::ExistingCommand); m_makeCommandAspect->setBaseFileName(PathChooser::homePath()); m_makeCommandAspect->setHistoryCompleter("PE.MakeCommand.History"); @@ -318,14 +314,15 @@ CommandLine MakeStep::effectiveMakeCommand(MakeCommandType type) const QWidget *MakeStep::createConfigWidget() { Layouting::Form builder; - builder.addRow(m_makeCommandAspect); - builder.addRow(m_userArgumentsAspect); + builder.addRow({m_makeCommandAspect}); + builder.addRow({m_userArgumentsAspect}); builder.addRow({m_userJobCountAspect, m_overrideMakeflagsAspect, m_nonOverrideWarning}); if (m_disablingForSubDirsSupported) - builder.addRow(m_disabledForSubdirsAspect); - builder.addRow(m_buildTargetsAspect); + builder.addRow({m_disabledForSubdirsAspect}); + builder.addRow({m_buildTargetsAspect}); + builder.addItem(Layouting::noMargin); - auto widget = builder.emerge(Layouting::WithoutMargins); + auto widget = builder.emerge(); VariableChooser::addSupportForChildWidgets(widget, macroExpander()); @@ -383,22 +380,6 @@ QWidget *MakeStep::createConfigWidget() return widget; } -bool MakeStep::buildsTarget(const QString &target) const -{ - return m_buildTargetsAspect->value().contains(target); -} - -void MakeStep::setBuildTarget(const QString &target, bool on) -{ - QStringList old = m_buildTargetsAspect->value(); - if (on && !old.contains(target)) - old << target; - else if (!on && old.contains(target)) - old.removeOne(target); - - m_buildTargetsAspect->setValue(old); -} - QStringList MakeStep::availableTargets() const { return m_buildTargetsAspect->allValues(); diff --git a/src/plugins/projectexplorer/makestep.h b/src/plugins/projectexplorer/makestep.h index b08462db2f..2f12894ea6 100644 --- a/src/plugins/projectexplorer/makestep.h +++ b/src/plugins/projectexplorer/makestep.h @@ -55,11 +55,6 @@ public: Utils::Environment makeEnvironment() const; - // FIXME: All unused, remove in 4.15. - void setBuildTarget(const QString &buildTarget) { setSelectedBuildTarget(buildTarget); } - bool buildsTarget(const QString &target) const; - void setBuildTarget(const QString &target, bool on); - protected: void supportDisablingForSubdirs() { m_disablingForSubDirsSupported = true; } virtual QStringList displayArguments() const; @@ -78,7 +73,6 @@ private: QStringList jobArguments() const; Utils::MultiSelectionAspect *m_buildTargetsAspect = nullptr; - QStringList m_availableTargets; // FIXME: Unused, remove in 4.15. Utils::StringAspect *m_makeCommandAspect = nullptr; Utils::StringAspect *m_userArgumentsAspect = nullptr; Utils::IntegerAspect *m_userJobCountAspect = nullptr; diff --git a/src/plugins/projectexplorer/miniprojecttargetselector.cpp b/src/plugins/projectexplorer/miniprojecttargetselector.cpp index b169697cd8..3a1b01b724 100644 --- a/src/plugins/projectexplorer/miniprojecttargetselector.cpp +++ b/src/plugins/projectexplorer/miniprojecttargetselector.cpp @@ -13,8 +13,8 @@ #include "projectexplorerconstants.h" #include "projectexplorericons.h" #include "projectexplorertr.h" +#include "projectmanager.h" #include "runconfiguration.h" -#include "session.h" #include "target.h" #include <utils/algorithm.h> @@ -32,6 +32,7 @@ #include <coreplugin/modemanager.h> #include <QAction> +#include <QCollator> #include <QGuiApplication> #include <QItemDelegate> #include <QKeyEvent> @@ -141,8 +142,15 @@ private: static bool compareItems(const TreeItem *ti1, const TreeItem *ti2) { - const int result = caseFriendlyCompare(static_cast<const GenericItem *>(ti1)->rawDisplayName(), - static_cast<const GenericItem *>(ti2)->rawDisplayName()); + static const QCollator collator = [] { + QCollator collator; + collator.setNumericMode(true); + collator.setCaseSensitivity(Qt::CaseInsensitive); + return collator; + }(); + + const int result = collator.compare(static_cast<const GenericItem *>(ti1)->rawDisplayName(), + static_cast<const GenericItem *>(ti2)->rawDisplayName()); if (result != 0) return result < 0; return ti1 < ti2; @@ -253,9 +261,9 @@ public: explicit ProjectListView(QWidget *parent = nullptr) : SelectorView(parent) { const auto model = new GenericModel(this); - model->rebuild(transform<QList<QObject *>>(SessionManager::projects(), + model->rebuild(transform<QList<QObject *>>(ProjectManager::projects(), [](Project *p) { return p; })); - connect(SessionManager::instance(), &SessionManager::projectAdded, + connect(ProjectManager::instance(), &ProjectManager::projectAdded, this, [this, model](Project *project) { const GenericItem *projectItem = model->addItemForObject(project); QFontMetrics fn(font()); @@ -264,7 +272,7 @@ public: setOptimalWidth(width); restoreCurrentIndex(); }); - connect(SessionManager::instance(), &SessionManager::aboutToRemoveProject, + connect(ProjectManager::instance(), &ProjectManager::aboutToRemoveProject, this, [this, model](const Project *project) { GenericItem * const item = model->itemForObject(project); if (!item) @@ -272,7 +280,7 @@ public: model->destroyItem(item); resetOptimalWidth(); }); - connect(SessionManager::instance(), &SessionManager::startupProjectChanged, + connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged, this, [this, model](const Project *project) { const GenericItem * const item = model->itemForObject(project); if (item) @@ -288,7 +296,7 @@ public: this, [model](const QModelIndex &index) { const GenericItem * const item = model->itemForIndex(index); if (item && item->object()) - SessionManager::setStartupProject(qobject_cast<Project *>(item->object())); + ProjectManager::setStartupProject(qobject_cast<Project *>(item->object())); }); } @@ -296,7 +304,7 @@ private: void restoreCurrentIndex() { const GenericItem * const itemForStartupProject - = theModel()->itemForObject(SessionManager::startupProject()); + = theModel()->itemForObject(ProjectManager::startupProject()); if (itemForStartupProject) setCurrentIndex(theModel()->indexForItem(itemForStartupProject)); } @@ -389,6 +397,12 @@ private: TreeView::mouseReleaseEvent(event); } + void showEvent(QShowEvent* event) override + { + scrollTo(currentIndex()); + TreeView::showEvent(event); + } + QObject *objectAt(const QModelIndex &index) const { return theModel()->itemForIndex(index)->object(); @@ -551,6 +565,11 @@ int SelectorView::padding() ///////// // KitAreaWidget ///////// +void doLayout(KitAspectWidget *widget, Layouting::LayoutItem &builder) +{ + widget->addToLayout(builder); +} + class KitAreaWidget : public QWidget { Q_OBJECT @@ -573,18 +592,15 @@ public: delete layout(); - Layouting::LayoutBuilder builder(Layouting::LayoutBuilder::GridLayout); + Layouting::Grid grid; for (KitAspect *aspect : KitManager::kitAspects()) { if (k && k->isMutable(aspect->id())) { KitAspectWidget *widget = aspect->createConfigWidget(k); m_widgets << widget; - QLabel *label = new QLabel(aspect->displayName()); - builder.addItem(label); - widget->addToLayout(builder); - builder.finishRow(); + grid.addItems({aspect->displayName(), widget, Layouting::br}); } } - builder.attachTo(this); + grid.attachTo(this); layout()->setContentsMargins(3, 3, 3, 3); m_kit = k; @@ -653,7 +669,7 @@ MiniProjectTargetSelector::MiniProjectTargetSelector(QAction *targetSelectorActi QWidget(parent), m_projectAction(targetSelectorAction) { - setProperty("panelwidget", true); + StyleHelper::setPanelWidget(this); setContentsMargins(QMargins(0, 1, 1, 8)); setWindowFlags(Qt::Popup); @@ -696,22 +712,22 @@ MiniProjectTargetSelector::MiniProjectTargetSelector(QAction *targetSelectorActi m_listWidgets[RUN]->viewport()->setAttribute(Qt::WA_Hover); // Validate state: At this point the session is still empty! - Project *startup = SessionManager::startupProject(); + Project *startup = ProjectManager::startupProject(); QTC_CHECK(!startup); - QTC_CHECK(SessionManager::projects().isEmpty()); + QTC_CHECK(ProjectManager::projects().isEmpty()); connect(m_summaryLabel, &QLabel::linkActivated, this, &MiniProjectTargetSelector::switchToProjectsMode); - SessionManager *sessionManager = SessionManager::instance(); - connect(sessionManager, &SessionManager::startupProjectChanged, + ProjectManager *sessionManager = ProjectManager::instance(); + connect(sessionManager, &ProjectManager::startupProjectChanged, this, &MiniProjectTargetSelector::changeStartupProject); - connect(sessionManager, &SessionManager::projectAdded, + connect(sessionManager, &ProjectManager::projectAdded, this, &MiniProjectTargetSelector::projectAdded); - connect(sessionManager, &SessionManager::projectRemoved, + connect(sessionManager, &ProjectManager::projectRemoved, this, &MiniProjectTargetSelector::projectRemoved); - connect(sessionManager, &SessionManager::projectDisplayNameChanged, + connect(sessionManager, &ProjectManager::projectDisplayNameChanged, this, &MiniProjectTargetSelector::updateActionAndSummary); // for icon changes: @@ -720,17 +736,17 @@ MiniProjectTargetSelector::MiniProjectTargetSelector(QAction *targetSelectorActi connect(m_listWidgets[TARGET], &GenericListWidget::changeActiveProjectConfiguration, this, [this](QObject *pc) { - SessionManager::setActiveTarget(m_project, static_cast<Target *>(pc), SetActive::Cascade); + m_project->setActiveTarget(static_cast<Target *>(pc), SetActive::Cascade); }); connect(m_listWidgets[BUILD], &GenericListWidget::changeActiveProjectConfiguration, this, [this](QObject *pc) { - SessionManager::setActiveBuildConfiguration(m_project->activeTarget(), - static_cast<BuildConfiguration *>(pc), SetActive::Cascade); + m_project->activeTarget()->setActiveBuildConfiguration( + static_cast<BuildConfiguration *>(pc), SetActive::Cascade); }); connect(m_listWidgets[DEPLOY], &GenericListWidget::changeActiveProjectConfiguration, this, [this](QObject *pc) { - SessionManager::setActiveDeployConfiguration(m_project->activeTarget(), - static_cast<DeployConfiguration *>(pc), SetActive::Cascade); + m_project->activeTarget()->setActiveDeployConfiguration( + static_cast<DeployConfiguration *>(pc), SetActive::Cascade); }); connect(m_listWidgets[RUN], &GenericListWidget::changeActiveProjectConfiguration, this, [this](QObject *pc) { @@ -881,7 +897,7 @@ void MiniProjectTargetSelector::doLayout(bool keepSize) onlySummary = true; } else { if (visibleLineCount < 3) { - if (Utils::anyOf(SessionManager::projects(), &Project::needsConfiguration)) + if (Utils::anyOf(ProjectManager::projects(), &Project::needsConfiguration)) visibleLineCount = 3; } if (visibleLineCount) @@ -1126,7 +1142,7 @@ void MiniProjectTargetSelector::removedRunConfiguration(RunConfiguration *rc, bo void MiniProjectTargetSelector::updateProjectListVisible() { - int count = SessionManager::projects().size(); + int count = ProjectManager::projects().size(); bool visible = count > 1; m_projectListWidget->setVisible(visible); @@ -1139,7 +1155,7 @@ void MiniProjectTargetSelector::updateProjectListVisible() void MiniProjectTargetSelector::updateTargetListVisible() { int maxCount = 0; - for (Project *p : SessionManager::projects()) + for (Project *p : ProjectManager::projects()) maxCount = qMax(p->targets().size(), maxCount); bool visible = maxCount > 1; @@ -1152,7 +1168,7 @@ void MiniProjectTargetSelector::updateTargetListVisible() void MiniProjectTargetSelector::updateBuildListVisible() { int maxCount = 0; - for (Project *p : SessionManager::projects()) { + for (Project *p : ProjectManager::projects()) { const QList<Target *> targets = p->targets(); for (Target *t : targets) maxCount = qMax(t->buildConfigurations().size(), maxCount); @@ -1168,7 +1184,7 @@ void MiniProjectTargetSelector::updateBuildListVisible() void MiniProjectTargetSelector::updateDeployListVisible() { int maxCount = 0; - for (Project *p : SessionManager::projects()) { + for (Project *p : ProjectManager::projects()) { const QList<Target *> targets = p->targets(); for (Target *t : targets) maxCount = qMax(t->deployConfigurations().size(), maxCount); @@ -1184,7 +1200,7 @@ void MiniProjectTargetSelector::updateDeployListVisible() void MiniProjectTargetSelector::updateRunListVisible() { int maxCount = 0; - for (Project *p : SessionManager::projects()) { + for (Project *p : ProjectManager::projects()) { const QList<Target *> targets = p->targets(); for (Target *t : targets) maxCount = qMax(t->runConfigurations().size(), maxCount); @@ -1460,10 +1476,10 @@ void MiniProjectTargetSelector::updateActionAndSummary() ? Icons::DESKTOP_DEVICE.icon() : style()->standardIcon(QStyle::SP_ComputerIcon); - Project *project = SessionManager::startupProject(); + Project *project = ProjectManager::startupProject(); if (project) { projectName = project->displayName(); - for (Project *p : SessionManager::projects()) { + for (Project *p : ProjectManager::projects()) { if (p != project && p->displayName() == projectName) { fileName = project->projectFilePath().toUserOutput(); break; @@ -1515,7 +1531,7 @@ void MiniProjectTargetSelector::updateActionAndSummary() void MiniProjectTargetSelector::updateSummary() { QString summary; - if (Project *startupProject = SessionManager::startupProject()) { + if (Project *startupProject = ProjectManager::startupProject()) { if (!m_projectListWidget->isVisibleTo(this)) summary.append(Tr::tr("Project: <b>%1</b><br/>").arg(startupProject->displayName())); if (Target *activeTarget = startupProject->activeTarget()) { diff --git a/src/plugins/projectexplorer/msvcparser.cpp b/src/plugins/projectexplorer/msvcparser.cpp index 89c59443e1..8452afcc5a 100644 --- a/src/plugins/projectexplorer/msvcparser.cpp +++ b/src/plugins/projectexplorer/msvcparser.cpp @@ -61,7 +61,7 @@ static Task handleNmakeJomMessage(const QString &line) CompileTask task(type, line.mid(matchLength).trimmed()); task.details << line; - return std::move(task); + return task; } static Task::TaskType taskType(const QString &category) diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index d0856e29d4..d4d518de44 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -3,6 +3,7 @@ #include "msvctoolchain.h" +#include "devicesupport/idevice.h" #include "gcctoolchain.h" #include "msvcparser.h" #include "projectexplorer.h" @@ -14,12 +15,12 @@ #include <coreplugin/icore.h> #include <utils/algorithm.h> +#include <utils/async.h> #include <utils/environment.h> #include <utils/hostosinfo.h> #include <utils/pathchooser.h> +#include <utils/process.h> #include <utils/qtcassert.h> -#include <utils/qtcprocess.h> -#include <utils/runextensions.h> #include <utils/temporarydirectory.h> #include <utils/winutils.h> @@ -254,7 +255,7 @@ static std::optional<VisualStudioInstallation> detectCppBuildTools2017() static QVector<VisualStudioInstallation> detectVisualStudioFromVsWhere(const QString &vswhere) { QVector<VisualStudioInstallation> installations; - QtcProcess vsWhereProcess; + Process vsWhereProcess; vsWhereProcess.setCodec(QTextCodec::codecForName("UTF-8")); const int timeoutS = 5; vsWhereProcess.setTimeoutS(timeoutS); @@ -648,7 +649,7 @@ Macros MsvcToolChain::msvcPredefinedMacros(const QStringList &cxxflags, qWarning("%s: %s", Q_FUNC_INFO, qPrintable(saver.errorString())); return predefinedMacros; } - Utils::QtcProcess cpp; + Utils::Process cpp; cpp.setEnvironment(env); cpp.setWorkingDirectory(Utils::TemporaryDirectory::masterDirectoryFilePath()); QStringList arguments; @@ -747,10 +748,8 @@ static QString winExpandDelayedEnvReferences(QString in, const Utils::Environmen return in; } -void MsvcToolChain::environmentModifications( - QFutureInterface<MsvcToolChain::GenerateEnvResult> &future, - QString vcvarsBat, - QString varsBatArg) +void MsvcToolChain::environmentModifications(QPromise<MsvcToolChain::GenerateEnvResult> &promise, + QString vcvarsBat, QString varsBatArg) { const Utils::Environment inEnv = Utils::Environment::systemEnvironment(); Utils::Environment outEnv; @@ -776,7 +775,7 @@ void MsvcToolChain::environmentModifications( } } - future.reportResult({error, diff}); + promise.addResult(MsvcToolChain::GenerateEnvResult{error, diff}); } void MsvcToolChain::initEnvModWatcher(const QFuture<GenerateEnvResult> &future) @@ -1004,10 +1003,8 @@ bool MsvcToolChain::fromMap(const QVariantMap &data) data.value(QLatin1String(environModsKeyC)).toList()); rescanForCompiler(); - initEnvModWatcher(Utils::runAsync(envModThreadPool(), - &MsvcToolChain::environmentModifications, - m_vcvarsBat, - m_varsBatArg)); + initEnvModWatcher(Utils::asyncRun(envModThreadPool(), &MsvcToolChain::environmentModifications, + m_vcvarsBat, m_varsBatArg)); const bool valid = !m_vcvarsBat.isEmpty() && targetAbi().isValid(); if (!valid) @@ -1128,8 +1125,8 @@ WarningFlags MsvcToolChain::warningFlags(const QStringList &cflags) const return flags; } -QStringList MsvcToolChain::includedFiles(const QStringList &flags, - const QString &directoryPath) const +FilePaths MsvcToolChain::includedFiles(const QStringList &flags, + const FilePath &directoryPath) const { return ToolChain::includedFiles("/FI", flags, directoryPath, PossiblyConcatenatedFlag::Yes); } @@ -1236,10 +1233,8 @@ void MsvcToolChain::setupVarsBat(const Abi &abi, const QString &varsBat, const Q m_varsBatArg = varsBatArg; if (!varsBat.isEmpty()) { - initEnvModWatcher(Utils::runAsync(envModThreadPool(), - &MsvcToolChain::environmentModifications, - varsBat, - varsBatArg)); + initEnvModWatcher(Utils::asyncRun(envModThreadPool(), + &MsvcToolChain::environmentModifications, varsBat, varsBatArg)); } } @@ -1560,7 +1555,7 @@ static QVersionNumber clangClVersion(const FilePath &clangClPath) if (!dllversion.isEmpty()) return QVersionNumber::fromString(dllversion); - QtcProcess clangClProcess; + Process clangClProcess; clangClProcess.setCommand({clangClPath, {"--version"}}); clangClProcess.runBlocking(); if (clangClProcess.result() != ProcessResult::FinishedWithSuccess) @@ -1777,7 +1772,7 @@ Macros ClangClToolChain::msvcPredefinedMacros(const QStringList &cxxflags, if (!cxxflags.contains("--driver-mode=g++")) return MsvcToolChain::msvcPredefinedMacros(cxxflags, env); - QtcProcess cpp; + Process cpp; cpp.setEnvironment(env); cpp.setWorkingDirectory(Utils::TemporaryDirectory::masterDirectoryFilePath()); @@ -1915,7 +1910,7 @@ static void detectCppBuildTools2015(Toolchains *list) Toolchains MsvcToolChainFactory::autoDetect(const ToolchainDetector &detector) const { - if (!detector.device.isNull()) { + if (detector.device->type() != ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) { // FIXME currently no support for msvc toolchains on a device return {}; } @@ -2030,7 +2025,7 @@ bool ClangClToolChainFactory::canCreate() const Toolchains ClangClToolChainFactory::autoDetect(const ToolchainDetector &detector) const { - if (!detector.device.isNull()) { + if (detector.device->type() != ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) { // FIXME currently no support for msvc toolchains on a device return {}; } @@ -2127,7 +2122,7 @@ std::optional<QString> MsvcToolChain::generateEnvironmentSettings(const Utils::E return QString(); } - Utils::QtcProcess run; + Utils::Process run; // As of WinSDK 7.1, there is logic preventing the path from being set // correctly if "ORIGINALPATH" is already set. That can cause problems diff --git a/src/plugins/projectexplorer/msvctoolchain.h b/src/plugins/projectexplorer/msvctoolchain.h index 042bbfe38f..68f8b9dcf1 100644 --- a/src/plugins/projectexplorer/msvctoolchain.h +++ b/src/plugins/projectexplorer/msvctoolchain.h @@ -55,8 +55,8 @@ public: MacroInspectionRunner createMacroInspectionRunner() const override; Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const override; Utils::WarningFlags warningFlags(const QStringList &cflags) const override; - QStringList includedFiles(const QStringList &flags, - const QString &directoryPath) const override; + Utils::FilePaths includedFiles(const QStringList &flags, + const Utils::FilePath &directoryPath) const override; BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner( const Utils::Environment &env) const override; void addToEnvironment(Utils::Environment &env) const override; @@ -81,6 +81,8 @@ public: const QString &batchFile, const QString &batchArgs, QMap<QString, QString> &envPairs); + bool environmentInitialized() const { return !m_environmentModifications.isEmpty(); } + protected: class WarningFlagAdder { @@ -111,9 +113,8 @@ protected: std::optional<QString> error; Utils::EnvironmentItems environmentItems; }; - static void environmentModifications(QFutureInterface<GenerateEnvResult> &future, - QString vcvarsBat, - QString varsBatArg); + static void environmentModifications(QPromise<GenerateEnvResult> &future, + QString vcvarsBat, QString varsBatArg); void initEnvModWatcher(const QFuture<GenerateEnvResult> &future); protected: diff --git a/src/plugins/projectexplorer/processparameters.cpp b/src/plugins/projectexplorer/processparameters.cpp index 49b50dccb3..0b3d6d83c7 100644 --- a/src/plugins/projectexplorer/processparameters.cpp +++ b/src/plugins/projectexplorer/processparameters.cpp @@ -5,7 +5,7 @@ #include <utils/fileutils.h> #include <utils/macroexpander.h> -#include <utils/qtcprocess.h> +#include <utils/process.h> #include <utils/theme/theme.h> #include <utils/utilstr.h> diff --git a/src/plugins/projectexplorer/processstep.cpp b/src/plugins/projectexplorer/processstep.cpp index 3ced1622d0..a34ff10b61 100644 --- a/src/plugins/projectexplorer/processstep.cpp +++ b/src/plugins/projectexplorer/processstep.cpp @@ -34,9 +34,8 @@ public: ProcessStep::ProcessStep(BuildStepList *bsl, Id id) : AbstractProcessStep(bsl, id) { - auto command = addAspect<StringAspect>(); + auto command = addAspect<FilePathAspect>(); command->setSettingsKey(PROCESS_COMMAND_KEY); - command->setDisplayStyle(StringAspect::PathChooserDisplay); command->setLabelText(Tr::tr("Command:")); command->setExpectedKind(PathChooser::Command); command->setHistoryCompleter("PE.ProcessStepCommand.History"); @@ -46,10 +45,9 @@ ProcessStep::ProcessStep(BuildStepList *bsl, Id id) arguments->setDisplayStyle(StringAspect::LineEditDisplay); arguments->setLabelText(Tr::tr("Arguments:")); - auto workingDirectory = addAspect<StringAspect>(); + auto workingDirectory = addAspect<FilePathAspect>(); workingDirectory->setSettingsKey(PROCESS_WORKINGDIRECTORY_KEY); workingDirectory->setValue(Constants::DEFAULT_WORKING_DIR); - workingDirectory->setDisplayStyle(StringAspect::PathChooserDisplay); workingDirectory->setLabelText(Tr::tr("Working directory:")); workingDirectory->setExpectedKind(PathChooser::Directory); diff --git a/src/plugins/projectexplorer/project.cpp b/src/plugins/projectexplorer/project.cpp index fbd4f9e28f..c021d9b1a6 100644 --- a/src/plugins/projectexplorer/project.cpp +++ b/src/plugins/projectexplorer/project.cpp @@ -11,15 +11,17 @@ #include "environmentaspect.h" #include "kit.h" #include "kitinformation.h" +#include "msvctoolchain.h" #include "projectexplorer.h" #include "projectexplorerconstants.h" #include "projectexplorertr.h" +#include "projectmanager.h" #include "projectnodes.h" #include "runconfiguration.h" #include "runconfigurationaspects.h" -#include "session.h" #include "target.h" #include "taskhub.h" +#include "toolchainmanager.h" #include "userfileaccessor.h" #include <coreplugin/idocument.h> @@ -273,7 +275,7 @@ void Project::addTarget(std::unique_ptr<Target> &&t) // check activeTarget: if (!activeTarget()) - SessionManager::setActiveTarget(this, pointer, SetActive::Cascade); + setActiveTarget(pointer, SetActive::Cascade); } Target *Project::addTargetForDefaultKit() @@ -309,7 +311,7 @@ bool Project::removeTarget(Target *target) auto keep = take(d->m_targets, target); if (target == d->m_activeTarget) { Target *newActiveTarget = (d->m_targets.size() == 0 ? nullptr : d->m_targets.at(0).get()); - SessionManager::setActiveTarget(this, newActiveTarget, SetActive::Cascade); + setActiveTarget(newActiveTarget, SetActive::Cascade); } emit removedTarget(target); @@ -326,7 +328,7 @@ Target *Project::activeTarget() const return d->m_activeTarget; } -void Project::setActiveTarget(Target *target) +void Project::setActiveTargetHelper(Target *target) { if (d->m_activeTarget == target) return; @@ -414,6 +416,29 @@ Target *Project::target(Kit *k) const return findOrDefault(d->m_targets, equal(&Target::kit, k)); } +void Project::setActiveTarget(Target *target, SetActive cascade) +{ + if (isShuttingDown()) + return; + + setActiveTargetHelper(target); + + if (!target) // never cascade setting no target + return; + + if (cascade != SetActive::Cascade || !ProjectManager::isProjectConfigurationCascading()) + return; + + Utils::Id kitId = target->kit()->id(); + for (Project *otherProject : ProjectManager::projects()) { + if (otherProject == this) + continue; + if (Target *otherTarget = Utils::findOrDefault(otherProject->targets(), + [kitId](Target *t) { return t->kit()->id() == kitId; })) + otherProject->setActiveTargetHelper(otherTarget); + } +} + Tasks Project::projectIssues(const Kit *k) const { Tasks result; @@ -445,12 +470,12 @@ bool Project::copySteps(Target *sourceTarget, Target *newTarget) sourceBc->buildSystem()->name())); newTarget->addBuildConfiguration(newBc); if (sourceTarget->activeBuildConfiguration() == sourceBc) - SessionManager::setActiveBuildConfiguration(newTarget, newBc, SetActive::NoCascade); + newTarget->setActiveBuildConfiguration(newBc, SetActive::NoCascade); } if (!newTarget->activeBuildConfiguration()) { QList<BuildConfiguration *> bcs = newTarget->buildConfigurations(); if (!bcs.isEmpty()) - SessionManager::setActiveBuildConfiguration(newTarget, bcs.first(), SetActive::NoCascade); + newTarget->setActiveBuildConfiguration(bcs.first(), SetActive::NoCascade); } for (DeployConfiguration *sourceDc : sourceTarget->deployConfigurations()) { @@ -462,12 +487,12 @@ bool Project::copySteps(Target *sourceTarget, Target *newTarget) newDc->setDisplayName(sourceDc->displayName()); newTarget->addDeployConfiguration(newDc); if (sourceTarget->activeDeployConfiguration() == sourceDc) - SessionManager::setActiveDeployConfiguration(newTarget, newDc, SetActive::NoCascade); + newTarget->setActiveDeployConfiguration(newDc, SetActive::NoCascade); } if (!newTarget->activeBuildConfiguration()) { QList<DeployConfiguration *> dcs = newTarget->deployConfigurations(); if (!dcs.isEmpty()) - SessionManager::setActiveDeployConfiguration(newTarget, dcs.first(), SetActive::NoCascade); + newTarget->setActiveDeployConfiguration(dcs.first(), SetActive::NoCascade); } for (RunConfiguration *sourceRc : sourceTarget->runConfigurations()) { @@ -846,6 +871,34 @@ const Node *Project::nodeForFilePath(const FilePath &filePath, return nullptr; } +FilePaths Project::binariesForSourceFile(const FilePath &sourceFile) const +{ + if (!rootProjectNode()) + return {}; + const QList<Node *> fileNodes = rootProjectNode()->findNodes([&sourceFile](Node *n) { + return n->filePath() == sourceFile; + }); + FilePaths binaries; + for (const Node * const fileNode : fileNodes) { + for (ProjectNode *projectNode = fileNode->parentProjectNode(); projectNode; + projectNode = projectNode->parentProjectNode()) { + if (!projectNode->isProduct()) + continue; + if (projectNode->productType() == ProductType::App + || projectNode->productType() == ProductType::Lib) { + const QList<Node *> binaryNodes = projectNode->findNodes([](Node *n) { + return n->asFileNode() && (n->asFileNode()->fileType() == FileType::App + || n->asFileNode()->fileType() == FileType::Lib); + + }); + binaries << Utils::transform(binaryNodes, &Node::filePath); + } + break; + } + } + return binaries; +} + void Project::setProjectLanguages(Context language) { if (d->m_projectLanguages == language) @@ -1130,7 +1183,7 @@ void Project::addVariablesToMacroExpander(const QByteArray &prefix, }); expander->registerVariable(fullPrefix + "Kit:Name", //: %1 is something like "Active project" - Tr::tr("%1: The name the active kit.").arg(descriptor), + Tr::tr("%1: The name of the active kit.").arg(descriptor), [targetGetter]() -> QString { if (const Target *const target = targetGetter()) return target->kit()->displayName(); @@ -1435,8 +1488,7 @@ void ProjectExplorerPlugin::testProject_multipleBuildConfigs() Target * const target = theProject.project()->activeTarget(); QVERIFY(target); QCOMPARE(target->buildConfigurations().size(), 6); - SessionManager::setActiveBuildConfiguration(target, target->buildConfigurations().at(1), - SetActive::Cascade); + target->setActiveBuildConfiguration(target->buildConfigurations().at(1), SetActive::Cascade); BuildSystem * const bs = theProject.project()->activeTarget()->buildSystem(); QVERIFY(bs); QCOMPARE(bs, target->activeBuildConfiguration()->buildSystem()); @@ -1452,12 +1504,94 @@ void ProjectExplorerPlugin::testProject_multipleBuildConfigs() } QVERIFY(!bs->isWaitingForParse() && !bs->isParsing()); - QCOMPARE(SessionManager::startupProject(), theProject.project()); + QCOMPARE(ProjectManager::startupProject(), theProject.project()); QCOMPARE(ProjectTree::currentProject(), theProject.project()); QVERIFY(EditorManager::openEditor(projectDir.pathAppended("main.cpp"))); QVERIFY(ProjectTree::currentNode()); ProjectTree::instance()->expandAll(); - SessionManager::closeAllProjects(); // QTCREATORBUG-25655 + ProjectManager::closeAllProjects(); // QTCREATORBUG-25655 +} + +void ProjectExplorerPlugin::testSourceToBinaryMapping() +{ + // Find suitable kit. + Kit * const kit = findOr(KitManager::kits(), nullptr, [](const Kit *k) { + return k->isValid() && ToolChainKitAspect::cxxToolChain(k); + }); + if (!kit) + QSKIP("The test requires at least one kit with a toolchain."); + + const auto toolchain = ToolChainKitAspect::cxxToolChain(kit); + QVERIFY(toolchain); + if (const auto msvcToolchain = dynamic_cast<Internal::MsvcToolChain *>(toolchain)) { + while (!msvcToolchain->environmentInitialized()) { + QSignalSpy parsingFinishedSpy(ToolChainManager::instance(), + &ToolChainManager::toolChainUpdated); + QVERIFY(parsingFinishedSpy.wait(10000)); + } + } + + // Copy project from qrc. + QTemporaryDir * const tempDir = TemporaryDirectory::masterTemporaryDirectory(); + QVERIFY(tempDir->isValid()); + const FilePath projectDir = FilePath::fromString(tempDir->path() + "/multi-target-project"); + if (!projectDir.exists()) { + const auto result = FilePath(":/projectexplorer/testdata/multi-target-project") + .copyRecursively(projectDir); + QVERIFY2(result, qPrintable(result.error())); + const QFileInfoList files = QDir(projectDir.toString()).entryInfoList(QDir::Files); + for (const QFileInfo &f : files) + QFile(f.absoluteFilePath()).setPermissions(f.permissions() | QFile::WriteUser); + } + + // Load Project. + QFETCH(QString, projectFileName); + const auto theProject = openProject(projectDir.pathAppended(projectFileName)); + if (theProject.errorMessage().contains("text/")) { + QSKIP("This test requires the presence of the qmake/cmake/qbs project managers " + "to be fully functional"); + } + + QVERIFY2(theProject, qPrintable(theProject.errorMessage())); + theProject.project()->configureAsExampleProject(kit); + QCOMPARE(theProject.project()->targets().size(), 1); + Target * const target = theProject.project()->activeTarget(); + QVERIFY(target); + BuildSystem * const bs = target->buildSystem(); + QVERIFY(bs); + QCOMPARE(bs, target->activeBuildConfiguration()->buildSystem()); + if (bs->isWaitingForParse() || bs->isParsing()) { + QSignalSpy parsingFinishedSpy(bs, &BuildSystem::parsingFinished); + QVERIFY(parsingFinishedSpy.wait(10000)); + } + QVERIFY(!bs->isWaitingForParse() && !bs->isParsing()); + + if (QLatin1String(QTest::currentDataTag()) == QLatin1String("qbs")) { + BuildManager::buildProjectWithoutDependencies(theProject.project()); + if (BuildManager::isBuilding()) { + QSignalSpy buildingFinishedSpy(BuildManager::instance(), &BuildManager::buildQueueFinished); + QVERIFY(buildingFinishedSpy.wait(10000)); + } + QVERIFY(!BuildManager::isBuilding()); + QSignalSpy projectUpdateSpy(theProject.project(), &Project::fileListChanged); + QVERIFY(projectUpdateSpy.wait(5000)); + } + + // Check mapping + const auto binariesForSource = [&](const QString &fileName) { + return theProject.project()->binariesForSourceFile(projectDir.pathAppended(fileName)); + }; + QCOMPARE(binariesForSource("multi-target-project-main.cpp").size(), 1); + QCOMPARE(binariesForSource("multi-target-project-lib.cpp").size(), 1); + QCOMPARE(binariesForSource("multi-target-project-shared.h").size(), 2); +} + +void ProjectExplorerPlugin::testSourceToBinaryMapping_data() +{ + QTest::addColumn<QString>("projectFileName"); + QTest::addRow("cmake") << "CMakeLists.txt"; + QTest::addRow("qbs") << "multi-target-project.qbs"; + QTest::addRow("qmake") << "multi-target-project.pro"; } #endif // WITH_TESTS diff --git a/src/plugins/projectexplorer/project.h b/src/plugins/projectexplorer/project.h index ed336b5f7f..832fc3c942 100644 --- a/src/plugins/projectexplorer/project.h +++ b/src/plugins/projectexplorer/project.h @@ -36,11 +36,11 @@ class ProjectImporter; class ProjectNode; class ProjectPrivate; class Target; +enum class SetActive : int; // Documentation inside. class PROJECTEXPLORER_EXPORT Project : public QObject { - friend class SessionManager; // for setActiveTarget Q_OBJECT public: @@ -89,6 +89,8 @@ public: Target *activeTarget() const; Target *target(Utils::Id id) const; Target *target(Kit *k) const; + void setActiveTarget(Target *target, SetActive cascade); + virtual Tasks projectIssues(const Kit *k) const; static bool copySteps(Target *sourceTarget, Target *newTarget); @@ -107,6 +109,7 @@ public: bool isKnownFile(const Utils::FilePath &filename) const; const Node *nodeForFilePath(const Utils::FilePath &filePath, const NodeMatcher &extraMatcher = {}) const; + Utils::FilePaths binariesForSourceFile(const Utils::FilePath &sourceFile) const; virtual QVariantMap toMap() const; @@ -226,7 +229,7 @@ private: void removeProjectLanguage(Utils::Id id); void handleSubTreeChanged(FolderNode *node); - void setActiveTarget(Target *target); + void setActiveTargetHelper(Target *target); friend class ContainerNode; ProjectPrivate *d; diff --git a/src/plugins/projectexplorer/projectconfiguration.cpp b/src/plugins/projectexplorer/projectconfiguration.cpp index 6d5fa797a6..841be31731 100644 --- a/src/plugins/projectexplorer/projectconfiguration.cpp +++ b/src/plugins/projectexplorer/projectconfiguration.cpp @@ -19,11 +19,9 @@ const char DISPLAY_NAME_KEY[] = "ProjectExplorer.ProjectConfiguration.DisplayNam // ProjectConfiguration ProjectConfiguration::ProjectConfiguration(QObject *parent, Utils::Id id) - : QObject(parent) + : AspectContainer(parent) , m_id(id) { - m_aspects.setOwnsSubAspects(true); - QTC_CHECK(parent); QTC_CHECK(id.isValid()); setObjectName(id.toString()); @@ -89,7 +87,7 @@ QVariantMap ProjectConfiguration::toMap() const QVariantMap map; map.insert(QLatin1String(CONFIGURATION_ID_KEY), m_id.toSetting()); m_displayName.toMap(map, DISPLAY_NAME_KEY); - m_aspects.toMap(map); + AspectContainer::toMap(map); return map; } @@ -106,15 +104,10 @@ bool ProjectConfiguration::fromMap(const QVariantMap &map) QTC_ASSERT(id.toString().startsWith(m_id.toString()), return false); m_displayName.fromMap(map, DISPLAY_NAME_KEY); - m_aspects.fromMap(map); + AspectContainer::fromMap(map); return true; } -BaseAspect *ProjectConfiguration::aspect(Id id) const -{ - return m_aspects.aspect(id); -} - FilePath ProjectConfiguration::mapFromBuildDeviceToGlobalPath(const FilePath &path) const { IDevice::ConstPtr dev = BuildDeviceKitAspect::device(kit()); diff --git a/src/plugins/projectexplorer/projectconfiguration.h b/src/plugins/projectexplorer/projectconfiguration.h index cb716bf66e..8c5dac1d90 100644 --- a/src/plugins/projectexplorer/projectconfiguration.h +++ b/src/plugins/projectexplorer/projectconfiguration.h @@ -21,7 +21,7 @@ class Kit; class Project; class Target; -class PROJECTEXPLORER_EXPORT ProjectConfiguration : public QObject +class PROJECTEXPLORER_EXPORT ProjectConfiguration : public Utils::AspectContainer { Q_OBJECT @@ -54,26 +54,12 @@ public: static QString settingsIdKey(); - template<class Aspect, typename ...Args> - Aspect *addAspect(Args && ...args) - { - return m_aspects.addAspect<Aspect>(std::forward<Args>(args)...); - } - - const Utils::AspectContainer &aspects() const { return m_aspects; } - - Utils::BaseAspect *aspect(Utils::Id id) const; - template <typename T> T *aspect() const { return m_aspects.aspect<T>(); } - Utils::FilePath mapFromBuildDeviceToGlobalPath(const Utils::FilePath &path) const; signals: void displayNameChanged(); void toolTipChanged(); -protected: - Utils::AspectContainer m_aspects; - private: QPointer<Target> m_target; const Utils::Id m_id; diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index f76f10175b..d313cb433d 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -8,6 +8,7 @@ #include "buildsystem.h" #include "compileoutputwindow.h" #include "configtaskhandler.h" +#include "copystep.h" #include "customexecutablerunconfiguration.h" #include "customparserssettingspage.h" #include "customwizard/customwizard.h" @@ -34,11 +35,13 @@ #include "dependenciespanel.h" #include "devicesupport/desktopdevice.h" #include "devicesupport/desktopdevicefactory.h" +#include "devicesupport/devicecheckbuildstep.h" #include "devicesupport/devicemanager.h" #include "devicesupport/devicesettingspage.h" #include "devicesupport/sshsettings.h" #include "devicesupport/sshsettingspage.h" #include "editorsettingspropertiespage.h" +#include "environmentaspect.h" #include "filesinallprojectsfind.h" #include "jsonwizard/jsonwizardfactory.h" #include "jsonwizard/jsonwizardgeneratorfactory.h" @@ -54,7 +57,6 @@ #include "project.h" #include "projectexplorericons.h" #include "projectexplorersettings.h" -#include "projectexplorersettingspage.h" #include "projectexplorertr.h" #include "projectfilewizardextension.h" #include "projectmanager.h" @@ -63,11 +65,8 @@ #include "projecttreewidget.h" #include "projectwindow.h" #include "removetaskhandler.h" -#include "runconfigurationaspects.h" #include "sanitizerparser.h" #include "selectablefilesmodel.h" -#include "session.h" -#include "sessiondialog.h" #include "showineditortaskhandler.h" #include "simpleprojectwizard.h" #include "target.h" @@ -108,6 +107,7 @@ #include <coreplugin/modemanager.h> #include <coreplugin/navigationwidget.h> #include <coreplugin/outputpane.h> +#include <coreplugin/session.h> #include <coreplugin/vcsmanager.h> #include <extensionsystem/pluginmanager.h> @@ -127,6 +127,7 @@ #include <utils/qtcassert.h> #include <utils/removefiledialog.h> #include <utils/stringutils.h> +#include <utils/terminalhooks.h> #include <utils/tooltip/tooltip.h> #include <utils/utilsicons.h> @@ -173,6 +174,7 @@ */ using namespace Core; +using namespace ExtensionSystem; using namespace ProjectExplorer::Internal; using namespace Utils; @@ -240,7 +242,6 @@ const int P_ACTION_BUILDPROJECT = 80; // Menus const char M_RECENTPROJECTS[] = "ProjectExplorer.Menu.Recent"; const char M_UNLOADPROJECTS[] = "ProjectExplorer.Menu.Unload"; -const char M_SESSION[] = "ProjectExplorer.Menu.Session"; const char M_GENERATORS[] = "ProjectExplorer.Menu.Generators"; const char RUNMENUCONTEXTMENU[] = "Project.RunMenu"; @@ -254,7 +255,6 @@ const char BUILD_BEFORE_DEPLOY_SETTINGS_KEY[] = "ProjectExplorer/Settings/BuildB const char DEPLOY_BEFORE_RUN_SETTINGS_KEY[] = "ProjectExplorer/Settings/DeployBeforeRun"; const char SAVE_BEFORE_BUILD_SETTINGS_KEY[] = "ProjectExplorer/Settings/SaveBeforeBuild"; const char USE_JOM_SETTINGS_KEY[] = "ProjectExplorer/Settings/UseJom"; -const char AUTO_RESTORE_SESSION_SETTINGS_KEY[] = "ProjectExplorer/Settings/AutoRestoreLastSession"; const char ADD_LIBRARY_PATHS_TO_RUN_ENV_SETTINGS_KEY[] = "ProjectExplorer/Settings/AddLibraryPathsToRunEnv"; const char PROMPT_TO_STOP_RUN_CONTROL_SETTINGS_KEY[] = @@ -303,12 +303,12 @@ static const RunConfiguration *runConfigForNode(const Target *target, const Proj static bool hideBuildMenu() { - return Core::ICore::settings()->value(Constants::SETTINGS_MENU_HIDE_BUILD, false).toBool(); + return ICore::settings()->value(Constants::SETTINGS_MENU_HIDE_BUILD, false).toBool(); } static bool hideDebugMenu() { - return Core::ICore::settings()->value(Constants::SETTINGS_MENU_HIDE_DEBUG, false).toBool(); + return ICore::settings()->value(Constants::SETTINGS_MENU_HIDE_DEBUG, false).toBool(); } static bool canOpenTerminalWithRunEnv(const Project *project, const ProjectNode *node) @@ -337,7 +337,7 @@ static BuildConfiguration *currentBuildConfiguration() static Target *activeTarget() { - const Project * const project = SessionManager::startupProject(); + const Project * const project = ProjectManager::startupProject(); return project ? project->activeTarget() : nullptr; } @@ -405,40 +405,22 @@ protected: void restoreState(const QJsonObject &object) override; }; -class RunConfigurationLocatorFilter : public Core::ILocatorFilter +class RunConfigurationStartFilter final : public ILocatorFilter { public: - RunConfigurationLocatorFilter(); - - void prepareSearch(const QString &entry) override; - QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, - const QString &entry) override; + RunConfigurationStartFilter(); private: - void targetListUpdated(); - QList<Core::LocatorFilterEntry> m_result; -}; - -class RunRunConfigurationLocatorFilter final : public RunConfigurationLocatorFilter -{ -public: - RunRunConfigurationLocatorFilter(); - - void accept(const Core::LocatorFilterEntry &selection, - QString *newText, - int *selectionStart, - int *selectionLength) const final; + Core::LocatorMatcherTasks matchers() final; }; -class SwitchToRunConfigurationLocatorFilter final : public RunConfigurationLocatorFilter +class RunConfigurationSwitchFilter final : public ILocatorFilter { public: - SwitchToRunConfigurationLocatorFilter(); + RunConfigurationSwitchFilter(); - void accept(const Core::LocatorFilterEntry &selection, - QString *newText, - int *selectionStart, - int *selectionLength) const final; +private: + Core::LocatorMatcherTasks matchers() final; }; class ProjectExplorerPluginPrivate : public QObject @@ -468,12 +450,7 @@ public: void unloadProjectContextMenu(); void unloadOtherProjectsContextMenu(); void closeAllProjects(); - void showSessionManager(); - void updateSessionMenu(); - void setSession(QAction *action); - void determineSessionToRestoreAtStartup(); - void restoreSession(); void runProjectContextMenu(RunConfiguration *rc); void savePersistentSettings(); @@ -492,7 +469,7 @@ public: void deleteFile(); void handleRenameFile(); void handleSetStartupProject(); - void setStartupProject(ProjectExplorer::Project *project); + void setStartupProject(Project *project); bool closeAllFilesInProject(const Project *project); void updateRecentProjectMenu(); @@ -504,11 +481,11 @@ public: void openTerminalHere(const EnvironmentGetter &env); void openTerminalHereWithRunEnv(); - void invalidateProject(ProjectExplorer::Project *project); + void invalidateProject(Project *project); - void projectAdded(ProjectExplorer::Project *pro); - void projectRemoved(ProjectExplorer::Project *pro); - void projectDisplayNameChanged(ProjectExplorer::Project *pro); + void projectAdded(Project *pro); + void projectRemoved(Project *pro); + void projectDisplayNameChanged(Project *pro); void doUpdateRunActions(); @@ -525,12 +502,10 @@ public: void extendFolderNavigationWidgetFactory(); public: - QMenu *m_sessionMenu; QMenu *m_openWithMenu; QMenu *m_openTerminalMenu; QMultiMap<int, QObject*> m_actionMap; - QAction *m_sessionManagerAction; QAction *m_newAction; QAction *m_loadAction; ParameterAction *m_unloadAction; @@ -599,7 +574,6 @@ public: QAction *m_runSubProject; ProjectWindow *m_proWindow = nullptr; - QString m_sessionToRestoreAtStartup; QStringList m_profileMimeTypes; int m_activeRunControlCount = 0; @@ -618,11 +592,9 @@ public: BuildPropertiesSettings m_buildPropertiesSettings; QList<CustomParserSettings> m_customParsers; bool m_shouldHaveRunConfiguration = false; - bool m_shuttingDown = false; Id m_runMode = Constants::NO_RUN_MODE; ToolChainManager *m_toolChainManager = nullptr; - QStringList m_arguments; #ifdef WITH_JOURNALD JournaldWatcher m_journalWatcher; @@ -667,7 +639,7 @@ public: RemoveTaskHandler m_removeTaskHandler; ConfigTaskHandler m_configTaskHandler{Task::compilerMissingTask(), Constants::KITS_SETTINGS_PAGE_ID}; - SessionManager m_sessionManager; + ProjectManager m_sessionManager; AppOutputPane m_outputPane; ProjectTree m_projectTree; @@ -675,9 +647,11 @@ public: AllProjectsFilter m_allProjectsFilter; CurrentProjectFilter m_currentProjectFilter; AllProjectFilesFilter m_allProjectDirectoriesFilter; - RunRunConfigurationLocatorFilter m_runConfigurationLocatorFilter; - SwitchToRunConfigurationLocatorFilter m_switchRunConfigurationLocatorFilter; + RunConfigurationStartFilter m_runConfigurationStartFilter; + RunConfigurationSwitchFilter m_runConfigurationSwitchFilter; + CopyFileStepFactory m_copyFileStepFactory; + CopyDirectoryStepFactory m_copyDirectoryFactory; ProcessStepFactory m_processStepFactory; AllProjectsFind m_allProjectsFind; @@ -691,9 +665,7 @@ public: // Settings pages ProjectExplorerSettingsPage m_projectExplorerSettingsPage; - BuildPropertiesSettingsPage m_buildPropertiesSettingsPage{&m_buildPropertiesSettings}; AppOutputSettingsPage m_appOutputSettingsPage; - CompileOutputSettingsPage m_compileOutputSettingsPage; DeviceSettingsPage m_deviceSettingsPage; SshSettingsPage m_sshSettingsPage; CustomParsersSettingsPage m_customParsersSettingsPage; @@ -721,6 +693,7 @@ public: cmakeRunConfigFactory.runConfigurationId() }}; + DeviceCheckBuildStepFactory deviceCheckBuildStepFactory; SanitizerOutputFormatterFactory sanitizerFormatterFactory; }; @@ -743,7 +716,7 @@ static void openProjectsInDirectory(const FilePath &filePath) { const FilePaths projectFiles = projectsInDirectory(filePath); if (!projectFiles.isEmpty()) - Core::ICore::openFiles(projectFiles); + ICore::openFiles(projectFiles); } static QStringList projectNames(const QVector<FolderNode *> &folders) @@ -766,7 +739,7 @@ static QVector<FolderNode *> renamableFolderNodes(const FilePath &before, const return folderNodes; } -static QVector<FolderNode *> removableFolderNodes(const Utils::FilePath &filePath) +static QVector<FolderNode *> removableFolderNodes(const FilePath &filePath) { QVector<FolderNode *> folderNodes; ProjectTree::forEachNode([&](Node *node) { @@ -831,40 +804,33 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er IWizardFactory::registerFeatureProvider(new KitFeatureProvider); IWizardFactory::registerFactoryCreator([] { return new SimpleProjectWizard; }); - connect(&dd->m_welcomePage, &ProjectWelcomePage::manageSessions, - dd, &ProjectExplorerPluginPrivate::showSessionManager); - - SessionManager *sessionManager = &dd->m_sessionManager; - connect(sessionManager, &SessionManager::projectAdded, + ProjectManager *sessionManager = &dd->m_sessionManager; + connect(sessionManager, &ProjectManager::projectAdded, this, &ProjectExplorerPlugin::fileListChanged); - connect(sessionManager, &SessionManager::aboutToRemoveProject, + connect(sessionManager, &ProjectManager::aboutToRemoveProject, dd, &ProjectExplorerPluginPrivate::invalidateProject); - connect(sessionManager, &SessionManager::projectRemoved, + connect(sessionManager, &ProjectManager::projectRemoved, this, &ProjectExplorerPlugin::fileListChanged); - connect(sessionManager, &SessionManager::projectAdded, + connect(sessionManager, &ProjectManager::projectAdded, dd, &ProjectExplorerPluginPrivate::projectAdded); - connect(sessionManager, &SessionManager::projectRemoved, + connect(sessionManager, &ProjectManager::projectRemoved, dd, &ProjectExplorerPluginPrivate::projectRemoved); - connect(sessionManager, &SessionManager::projectDisplayNameChanged, + connect(sessionManager, &ProjectManager::projectDisplayNameChanged, dd, &ProjectExplorerPluginPrivate::projectDisplayNameChanged); - connect(sessionManager, &SessionManager::dependencyChanged, + connect(sessionManager, &ProjectManager::dependencyChanged, dd, &ProjectExplorerPluginPrivate::updateActions); - connect(sessionManager, &SessionManager::sessionLoaded, + connect(SessionManager::instance(), &SessionManager::sessionLoaded, dd, &ProjectExplorerPluginPrivate::updateActions); - connect(sessionManager, &SessionManager::sessionLoaded, + connect(SessionManager::instance(), &SessionManager::sessionLoaded, dd, &ProjectExplorerPluginPrivate::updateWelcomePage); - connect(sessionManager, &SessionManager::sessionLoaded, + connect(SessionManager::instance(), &SessionManager::sessionLoaded, dd, &ProjectExplorerPluginPrivate::loadSesssionTasks); - - connect(sessionManager, &SessionManager::projectAdded, dd, [](ProjectExplorer::Project *project) { - dd->m_allProjectDirectoriesFilter.addDirectory(project->projectDirectory()); - }); - connect(sessionManager, - &SessionManager::projectRemoved, - dd, - [](ProjectExplorer::Project *project) { - dd->m_allProjectDirectoriesFilter.removeDirectory(project->projectDirectory()); - }); + connect(SessionManager::instance(), &SessionManager::sessionCreated, + dd, &ProjectExplorerPluginPrivate::updateWelcomePage); + connect(SessionManager::instance(), &SessionManager::sessionRenamed, + dd, &ProjectExplorerPluginPrivate::updateWelcomePage); + connect(SessionManager::instance(), &SessionManager::sessionRemoved, + dd, &ProjectExplorerPluginPrivate::updateWelcomePage); ProjectTree *tree = &dd->m_projectTree; connect(tree, &ProjectTree::currentProjectChanged, dd, [] { @@ -903,7 +869,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er ICore::addPreCloseListener([]() -> bool { return coreAboutToClose(); }); - connect(SessionManager::instance(), &SessionManager::projectRemoved, + connect(ProjectManager::instance(), &ProjectManager::projectRemoved, &dd->m_outputPane, &AppOutputPane::projectRemoved); // ProjectPanelFactories @@ -1182,23 +1148,6 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er connect(mfile->menu(), &QMenu::aboutToShow, dd, &ProjectExplorerPluginPrivate::updateRecentProjectMenu); - // session menu - ActionContainer *msession = ActionManager::createMenu(Constants::M_SESSION); - msession->menu()->setTitle(Tr::tr("S&essions")); - msession->setOnAllDisabledBehavior(ActionContainer::Show); - mfile->addMenu(msession, Core::Constants::G_FILE_OPEN); - dd->m_sessionMenu = msession->menu(); - connect(mfile->menu(), &QMenu::aboutToShow, - dd, &ProjectExplorerPluginPrivate::updateSessionMenu); - - // session manager action - dd->m_sessionManagerAction = new QAction(Tr::tr("&Manage..."), this); - dd->m_sessionMenu->addAction(dd->m_sessionManagerAction); - dd->m_sessionMenu->addSeparator(); - cmd = ActionManager::registerAction(dd->m_sessionManagerAction, - "ProjectExplorer.ManageSessions"); - cmd->setDefaultKeySequence(QKeySequence()); - // unload action dd->m_unloadAction = new ParameterAction(Tr::tr("Close Project"), Tr::tr("Close Pro&ject \"%1\""), ParameterAction::AlwaysEnabled, this); @@ -1345,7 +1294,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er // without a project loaded. connect(generatorContainer->menu(), &QMenu::aboutToShow, [menu = generatorContainer->menu()] { menu->clear(); - if (Project * const project = SessionManager::startupProject()) { + if (Project * const project = ProjectManager::startupProject()) { for (const auto &generator : project->allGenerators()) { menu->addAction(generator.second, [project, id = generator.first] { project->runGenerator(id); @@ -1620,7 +1569,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er Command * const expandCmd = ActionManager::registerAction( dd->m_projectTreeExpandAllAction, Constants::PROJECTTREE_EXPAND_ALL, projectTreeContext); - for (Core::ActionContainer * const ac : {mfileContextMenu, msubProjectContextMenu, + for (ActionContainer * const ac : {mfileContextMenu, msubProjectContextMenu, mfolderContextMenu, mprojectContextMenu, msessionContextMenu}) { ac->addSeparator(treeGroup); ac->addAction(expandNodeCmd, treeGroup); @@ -1657,12 +1606,8 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er connect(ICore::instance(), &ICore::saveSettingsRequested, dd, &ProjectExplorerPluginPrivate::savePersistentSettings); - connect(EditorManager::instance(), &EditorManager::autoSaved, this, [] { - if (!dd->m_shuttingDown && !SessionManager::loadingSession()) - SessionManager::save(); - }); connect(qApp, &QApplication::applicationStateChanged, this, [](Qt::ApplicationState state) { - if (!dd->m_shuttingDown && state == Qt::ApplicationActive) + if (!PluginManager::isShuttingDown() && state == Qt::ApplicationActive) dd->updateWelcomePage(); }); @@ -1697,10 +1642,6 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er .toBool(); dd->m_projectExplorerSettings.useJom = s->value(Constants::USE_JOM_SETTINGS_KEY, defaultSettings.useJom).toBool(); - dd->m_projectExplorerSettings.autorestoreLastSession - = s->value(Constants::AUTO_RESTORE_SESSION_SETTINGS_KEY, - defaultSettings.autorestoreLastSession) - .toBool(); dd->m_projectExplorerSettings.addLibraryPathsToRunEnv = s->value(Constants::ADD_LIBRARY_PATHS_TO_RUN_ENV_SETTINGS_KEY, defaultSettings.addLibraryPathsToRunEnv) @@ -1723,7 +1664,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er if (tmp < 0 || tmp > int(StopBeforeBuild::SameApp)) tmp = int(defaultSettings.stopBeforeBuild); dd->m_projectExplorerSettings.stopBeforeBuild = StopBeforeBuild(tmp); - dd->m_projectExplorerSettings.terminalMode = static_cast<ProjectExplorer::TerminalMode>( + dd->m_projectExplorerSettings.terminalMode = static_cast<TerminalMode>( s->value(Constants::TERMINAL_MODE_SETTINGS_KEY, int(defaultSettings.terminalMode)).toInt()); dd->m_projectExplorerSettings.closeSourceFilesWithProject = s->value(Constants::CLOSE_FILES_WITH_PROJECT_SETTINGS_KEY, @@ -1757,27 +1698,25 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er connect(buildManager, &BuildManager::buildQueueFinished, dd, &ProjectExplorerPluginPrivate::buildQueueFinished, Qt::QueuedConnection); - connect(dd->m_sessionManagerAction, &QAction::triggered, - dd, &ProjectExplorerPluginPrivate::showSessionManager); connect(dd->m_newAction, &QAction::triggered, dd, &ProjectExplorerPlugin::openNewProjectDialog); connect(dd->m_loadAction, &QAction::triggered, dd, &ProjectExplorerPluginPrivate::loadAction); connect(dd->m_buildProjectOnlyAction, &QAction::triggered, dd, [] { - BuildManager::buildProjectWithoutDependencies(SessionManager::startupProject()); + BuildManager::buildProjectWithoutDependencies(ProjectManager::startupProject()); }); connect(dd->m_buildAction, &QAction::triggered, dd, [] { - BuildManager::buildProjectWithDependencies(SessionManager::startupProject()); + BuildManager::buildProjectWithDependencies(ProjectManager::startupProject()); }); connect(dd->m_buildProjectForAllConfigsAction, &QAction::triggered, dd, [] { - BuildManager::buildProjectWithDependencies(SessionManager::startupProject(), + BuildManager::buildProjectWithDependencies(ProjectManager::startupProject(), ConfigSelection::All); }); connect(dd->m_buildActionContextMenu, &QAction::triggered, dd, [] { BuildManager::buildProjectWithoutDependencies(ProjectTree::currentProject()); }); connect(dd->m_buildForRunConfigAction, &QAction::triggered, dd, [] { - const Project * const project = SessionManager::startupProject(); + const Project * const project = ProjectManager::startupProject(); QTC_ASSERT(project, return); const Target * const target = project->activeTarget(); QTC_ASSERT(target, return); @@ -1792,20 +1731,20 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er BuildManager::buildProjectWithDependencies(ProjectTree::currentProject()); }); connect(dd->m_buildSessionAction, &QAction::triggered, dd, [] { - BuildManager::buildProjects(SessionManager::projectOrder(), ConfigSelection::Active); + BuildManager::buildProjects(ProjectManager::projectOrder(), ConfigSelection::Active); }); connect(dd->m_buildSessionForAllConfigsAction, &QAction::triggered, dd, [] { - BuildManager::buildProjects(SessionManager::projectOrder(), ConfigSelection::All); + BuildManager::buildProjects(ProjectManager::projectOrder(), ConfigSelection::All); }); connect(dd->m_rebuildProjectOnlyAction, &QAction::triggered, dd, [] { - BuildManager::rebuildProjectWithoutDependencies(SessionManager::startupProject()); + BuildManager::rebuildProjectWithoutDependencies(ProjectManager::startupProject()); }); connect(dd->m_rebuildAction, &QAction::triggered, dd, [] { - BuildManager::rebuildProjectWithDependencies(SessionManager::startupProject(), + BuildManager::rebuildProjectWithDependencies(ProjectManager::startupProject(), ConfigSelection::Active); }); connect(dd->m_rebuildProjectForAllConfigsAction, &QAction::triggered, dd, [] { - BuildManager::rebuildProjectWithDependencies(SessionManager::startupProject(), + BuildManager::rebuildProjectWithDependencies(ProjectManager::startupProject(), ConfigSelection::All); }); connect(dd->m_rebuildActionContextMenu, &QAction::triggered, dd, [] { @@ -1816,32 +1755,32 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er ConfigSelection::Active); }); connect(dd->m_rebuildSessionAction, &QAction::triggered, dd, [] { - BuildManager::rebuildProjects(SessionManager::projectOrder(), ConfigSelection::Active); + BuildManager::rebuildProjects(ProjectManager::projectOrder(), ConfigSelection::Active); }); connect(dd->m_rebuildSessionForAllConfigsAction, &QAction::triggered, dd, [] { - BuildManager::rebuildProjects(SessionManager::projectOrder(), ConfigSelection::All); + BuildManager::rebuildProjects(ProjectManager::projectOrder(), ConfigSelection::All); }); connect(dd->m_deployProjectOnlyAction, &QAction::triggered, dd, [] { - BuildManager::deployProjects({SessionManager::startupProject()}); + BuildManager::deployProjects({ProjectManager::startupProject()}); }); connect(dd->m_deployAction, &QAction::triggered, dd, [] { - BuildManager::deployProjects(SessionManager::projectOrder(SessionManager::startupProject())); + BuildManager::deployProjects(ProjectManager::projectOrder(ProjectManager::startupProject())); }); connect(dd->m_deployActionContextMenu, &QAction::triggered, dd, [] { BuildManager::deployProjects({ProjectTree::currentProject()}); }); connect(dd->m_deploySessionAction, &QAction::triggered, dd, [] { - BuildManager::deployProjects(SessionManager::projectOrder()); + BuildManager::deployProjects(ProjectManager::projectOrder()); }); connect(dd->m_cleanProjectOnlyAction, &QAction::triggered, dd, [] { - BuildManager::cleanProjectWithoutDependencies(SessionManager::startupProject()); + BuildManager::cleanProjectWithoutDependencies(ProjectManager::startupProject()); }); connect(dd->m_cleanAction, &QAction::triggered, dd, [] { - BuildManager::cleanProjectWithDependencies(SessionManager::startupProject(), + BuildManager::cleanProjectWithDependencies(ProjectManager::startupProject(), ConfigSelection::Active); }); connect(dd->m_cleanProjectForAllConfigsAction, &QAction::triggered, dd, [] { - BuildManager::cleanProjectWithDependencies(SessionManager::startupProject(), + BuildManager::cleanProjectWithDependencies(ProjectManager::startupProject(), ConfigSelection::All); }); connect(dd->m_cleanActionContextMenu, &QAction::triggered, dd, [] { @@ -1852,10 +1791,10 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er ConfigSelection::Active); }); connect(dd->m_cleanSessionAction, &QAction::triggered, dd, [] { - BuildManager::cleanProjects(SessionManager::projectOrder(), ConfigSelection::Active); + BuildManager::cleanProjects(ProjectManager::projectOrder(), ConfigSelection::Active); }); connect(dd->m_cleanSessionForAllConfigsAction, &QAction::triggered, dd, [] { - BuildManager::cleanProjects(SessionManager::projectOrder(), ConfigSelection::All); + BuildManager::cleanProjects(ProjectManager::projectOrder(), ConfigSelection::All); }); connect(dd->m_runAction, &QAction::triggered, dd, [] { runStartupProject(Constants::NORMAL_RUN_MODE); }); @@ -1920,7 +1859,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er connect(dd->m_setStartupProjectAction, &QAction::triggered, dd, &ProjectExplorerPluginPrivate::handleSetStartupProject); connect(dd->m_closeProjectFilesActionFileMenu, &QAction::triggered, - dd, [] { dd->closeAllFilesInProject(SessionManager::projects().first()); }); + dd, [] { dd->closeAllFilesInProject(ProjectManager::projects().first()); }); connect(dd->m_closeProjectFilesActionContextMenu, &QAction::triggered, dd, [] { dd->closeAllFilesInProject(ProjectTree::currentProject()); }); connect(dd->m_projectTreeCollapseAllAction, &QAction::triggered, @@ -1935,6 +1874,18 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er dd->updateContextMenuActions(ProjectTree::currentNode()); }); + connect(ModeManager::instance(), + &ModeManager::currentModeChanged, + dd, + &ProjectExplorerPluginPrivate::currentModeChanged); + connect(&dd->m_welcomePage, + &ProjectWelcomePage::requestProject, + m_instance, + &ProjectExplorerPlugin::openProjectWelcomePage); + connect(SessionManager::instance(), + &SessionManager::startupSessionRestored, + m_instance, + &ProjectExplorerPlugin::finishedInitialization); dd->updateWelcomePage(); MacroExpander *expander = Utils::globalMacroExpander(); @@ -1965,7 +1916,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er Project::addVariablesToMacroExpander("ActiveProject:", "Active project", expander, - &SessionManager::startupProject); + &ProjectManager::startupProject); EnvironmentProvider::addProvider( {"ActiveProject:BuildConfig:Env", Tr::tr("Active build environment of the active project."), [] { if (const BuildConfiguration * const bc = activeBuildConfiguration()) @@ -1981,15 +1932,6 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er return Environment::systemEnvironment(); }}); - const auto fileHandler = [] { - return SessionManager::sessionNameToFileName(SessionManager::activeSession()); - }; - expander->registerFileVariables("Session", Tr::tr("File where current session is saved."), - fileHandler); - expander->registerVariable("Session:Name", Tr::tr("Name of current session."), [] { - return SessionManager::activeSession(); - }); - DeviceManager::instance()->addDevice(IDevice::Ptr(new DesktopDevice)); if (auto sanitizerTester = SanitizerParser::testCreator()) @@ -2032,7 +1974,7 @@ void ProjectExplorerPluginPrivate::unloadProjectContextMenu() void ProjectExplorerPluginPrivate::unloadOtherProjectsContextMenu() { if (Project *currentProject = ProjectTree::currentProject()) { - const QList<Project *> projects = SessionManager::projects(); + const QList<Project *> projects = ProjectManager::projects(); QTC_ASSERT(!projects.isEmpty(), return); for (Project *p : projects) { @@ -2045,7 +1987,7 @@ void ProjectExplorerPluginPrivate::unloadOtherProjectsContextMenu() void ProjectExplorerPluginPrivate::handleUnloadProject() { - QList<Project *> projects = SessionManager::projects(); + QList<Project *> projects = ProjectManager::projects(); QTC_ASSERT(!projects.isEmpty(), return); ProjectExplorerPlugin::unloadProject(projects.first()); @@ -2072,7 +2014,7 @@ void ProjectExplorerPlugin::unloadProject(Project *project) dd->addToRecentProjects(project->projectFilePath(), project->displayName()); - SessionManager::removeProject(project); + ProjectManager::removeProject(project); dd->updateActions(); } @@ -2081,7 +2023,7 @@ void ProjectExplorerPluginPrivate::closeAllProjects() if (!EditorManager::closeAllDocuments()) return; // Action has been cancelled - SessionManager::closeAllProjects(); + ProjectManager::closeAllProjects(); updateActions(); ModeManager::activateMode(Core::Constants::MODE_WELCOME); @@ -2137,13 +2079,13 @@ void ProjectExplorerPlugin::extensionsInitialized() Tr::tr("Sanitizer", "Category for sanitizer issues listed under 'Issues'")); TaskHub::addCategory(Constants::TASK_CATEGORY_TASKLIST_ID, Tr::tr("My Tasks")); - SshSettings::loadSettings(Core::ICore::settings()); + SshSettings::loadSettings(ICore::settings()); const auto searchPathRetriever = [] { - FilePaths searchPaths = {Core::ICore::libexecPath()}; + FilePaths searchPaths = {ICore::libexecPath()}; if (HostOsInfo::isWindowsHost()) { - const QString gitBinary = Core::ICore::settings()->value("Git/BinaryPath", "git") + const QString gitBinary = ICore::settings()->value("Git/BinaryPath", "git") .toString(); - const QStringList rawGitSearchPaths = Core::ICore::settings()->value("Git/Path") + const QStringList rawGitSearchPaths = ICore::settings()->value("Git/Path") .toString().split(':', Qt::SkipEmptyParts); const FilePaths gitSearchPaths = Utils::transform(rawGitSearchPaths, [](const QString &rawPath) { return FilePath::fromString(rawPath); }); @@ -2177,11 +2119,12 @@ void ProjectExplorerPlugin::extensionsInitialized() void ProjectExplorerPlugin::restoreKits() { - dd->determineSessionToRestoreAtStartup(); ExtraAbi::load(); // Load this before Toolchains! ToolChainManager::restoreToolChains(); KitManager::restoreKits(); - QTimer::singleShot(0, dd, &ProjectExplorerPluginPrivate::restoreSession); // delay a bit... + // restoring startup session is supposed to be done as a result of ICore::coreOpened, + // and that is supposed to happen after restoring kits: + QTC_CHECK(!SessionManager::isStartupSessionRestored()); } void ProjectExplorerPluginPrivate::updateRunWithoutDeployMenu() @@ -2189,15 +2132,13 @@ void ProjectExplorerPluginPrivate::updateRunWithoutDeployMenu() m_runWithoutDeployAction->setVisible(m_projectExplorerSettings.deployBeforeRun); } -ExtensionSystem::IPlugin::ShutdownFlag ProjectExplorerPlugin::aboutToShutdown() +IPlugin::ShutdownFlag ProjectExplorerPlugin::aboutToShutdown() { disconnect(ModeManager::instance(), &ModeManager::currentModeChanged, dd, &ProjectExplorerPluginPrivate::currentModeChanged); ProjectTree::aboutToShutDown(); ToolChainManager::aboutToShutdown(); - SessionManager::closeAllProjects(); - - dd->m_shuttingDown = true; + ProjectManager::closeAllProjects(); // Attempt to synchronously shutdown all run controls. // If that fails, fall back to asynchronous shutdown (Debugger run controls @@ -2210,11 +2151,6 @@ ExtensionSystem::IPlugin::ShutdownFlag ProjectExplorerPlugin::aboutToShutdown() return AsynchronousShutdown; } -void ProjectExplorerPlugin::showSessionManager() -{ - dd->showSessionManager(); -} - void ProjectExplorerPlugin::openNewProjectDialog() { if (!ICore::isNewItemDialogRunning()) { @@ -2226,25 +2162,11 @@ void ProjectExplorerPlugin::openNewProjectDialog() } } -void ProjectExplorerPluginPrivate::showSessionManager() -{ - SessionManager::save(); - SessionDialog sessionDialog(ICore::dialogParent()); - sessionDialog.setAutoLoadSession(dd->m_projectExplorerSettings.autorestoreLastSession); - sessionDialog.exec(); - dd->m_projectExplorerSettings.autorestoreLastSession = sessionDialog.autoLoadSession(); - - updateActions(); - - if (ModeManager::currentModeId() == Core::Constants::MODE_WELCOME) - updateWelcomePage(); -} - void ProjectExplorerPluginPrivate::setStartupProject(Project *project) { if (!project) return; - SessionManager::setStartupProject(project); + ProjectManager::setStartupProject(project); updateActions(); } @@ -2255,7 +2177,7 @@ bool ProjectExplorerPluginPrivate::closeAllFilesInProject(const Project *project Utils::erase(openFiles, [project](const DocumentModel::Entry *entry) { return entry->pinned || !project->isKnownFile(entry->filePath()); }); - for (const Project * const otherProject : SessionManager::projects()) { + for (const Project * const otherProject : ProjectManager::projects()) { if (otherProject == project) continue; Utils::erase(openFiles, [otherProject](const DocumentModel::Entry *entry) { @@ -2267,23 +2189,15 @@ bool ProjectExplorerPluginPrivate::closeAllFilesInProject(const Project *project void ProjectExplorerPluginPrivate::savePersistentSettings() { - if (dd->m_shuttingDown) + if (PluginManager::isShuttingDown()) return; - if (!SessionManager::loadingSession()) { - for (Project *pro : SessionManager::projects()) + if (!SessionManager::isLoadingSession()) { + for (Project *pro : ProjectManager::projects()) pro->saveSettings(); - - SessionManager::save(); } QtcSettings *s = ICore::settings(); - if (SessionManager::isDefaultVirgin()) { - s->remove(Constants::STARTUPSESSION_KEY); - } else { - s->setValue(Constants::STARTUPSESSION_KEY, SessionManager::activeSession()); - s->setValue(Constants::LASTSESSION_KEY, SessionManager::activeSession()); - } s->remove(QLatin1String("ProjectExplorer/RecentProjects/Files")); QStringList fileNames; @@ -2312,9 +2226,6 @@ void ProjectExplorerPluginPrivate::savePersistentSettings() s->setValueWithDefault(Constants::USE_JOM_SETTINGS_KEY, dd->m_projectExplorerSettings.useJom, defaultSettings.useJom); - s->setValueWithDefault(Constants::AUTO_RESTORE_SESSION_SETTINGS_KEY, - dd->m_projectExplorerSettings.autorestoreLastSession, - defaultSettings.autorestoreLastSession); s->setValueWithDefault(Constants::ADD_LIBRARY_PATHS_TO_RUN_ENV_SETTINGS_KEY, dd->m_projectExplorerSettings.addLibraryPathsToRunEnv, defaultSettings.addLibraryPathsToRunEnv); @@ -2368,7 +2279,7 @@ ProjectExplorerPlugin::OpenProjectResult ProjectExplorerPlugin::openProject(cons if (!project) return result; dd->addToRecentProjects(filePath, project->displayName()); - SessionManager::setStartupProject(project); + ProjectManager::setStartupProject(project); return result; } @@ -2420,11 +2331,11 @@ ProjectExplorerPlugin::OpenProjectResult ProjectExplorerPlugin::openProjects(con QTC_ASSERT(!fileName.isEmpty(), continue); const FilePath filePath = fileName.absoluteFilePath(); - Project *found = Utils::findOrDefault(SessionManager::projects(), + Project *found = Utils::findOrDefault(ProjectManager::projects(), Utils::equal(&Project::projectFilePath, filePath)); if (found) { alreadyOpen.append(found); - SessionManager::reportProjectLoadingProgress(); + SessionManager::sessionLoadingProgress(); continue; } @@ -2439,7 +2350,7 @@ ProjectExplorerPlugin::OpenProjectResult ProjectExplorerPlugin::openProjects(con if (restoreResult == Project::RestoreResult::Ok) { connect(pro, &Project::fileListChanged, m_instance, &ProjectExplorerPlugin::fileListChanged); - SessionManager::addProject(pro); + ProjectManager::addProject(pro); openedPro += pro; } else { if (restoreResult == Project::RestoreResult::Error) @@ -2453,7 +2364,7 @@ ProjectExplorerPlugin::OpenProjectResult ProjectExplorerPlugin::openProjects(con .arg(mt.name())); } if (filePaths.size() > 1) - SessionManager::reportProjectLoadingProgress(); + SessionManager::sessionLoadingProgress(); } dd->updateActions(); @@ -2496,33 +2407,6 @@ void ProjectExplorerPluginPrivate::currentModeChanged(Id mode, Id oldMode) updateWelcomePage(); } -void ProjectExplorerPluginPrivate::determineSessionToRestoreAtStartup() -{ - // Process command line arguments first: - const bool lastSessionArg = - ExtensionSystem::PluginManager::specForPlugin(m_instance)->arguments().contains("-lastsession"); - m_sessionToRestoreAtStartup = lastSessionArg ? SessionManager::startupSession() : QString(); - const QStringList arguments = ExtensionSystem::PluginManager::arguments(); - if (!lastSessionArg) { - QStringList sessions = SessionManager::sessions(); - // We have command line arguments, try to find a session in them - // Default to no session loading - for (const QString &arg : arguments) { - if (sessions.contains(arg)) { - // Session argument - m_sessionToRestoreAtStartup = arg; - break; - } - } - } - // Handle settings only after command line arguments: - if (m_sessionToRestoreAtStartup.isEmpty() && m_projectExplorerSettings.autorestoreLastSession) - m_sessionToRestoreAtStartup = SessionManager::startupSession(); - - if (!m_sessionToRestoreAtStartup.isEmpty()) - ModeManager::activateMode(Core::Constants::MODE_EDIT); -} - // Return a list of glob patterns for project files ("*.pro", etc), use first, main pattern only. QStringList ProjectExplorerPlugin::projectFileGlobs() { @@ -2548,70 +2432,6 @@ MiniProjectTargetSelector *ProjectExplorerPlugin::targetSelector() return dd->m_targetSelector; } -/*! - This function is connected to the ICore::coreOpened signal. If - there was no session explicitly loaded, it creates an empty new - default session and puts the list of recent projects and sessions - onto the welcome page. -*/ -void ProjectExplorerPluginPrivate::restoreSession() -{ - // We have command line arguments, try to find a session in them - QStringList arguments = ExtensionSystem::PluginManager::arguments(); - if (!dd->m_sessionToRestoreAtStartup.isEmpty() && !arguments.isEmpty()) - arguments.removeOne(dd->m_sessionToRestoreAtStartup); - - // Massage the argument list. - // Be smart about directories: If there is a session of that name, load it. - // Other than that, look for project files in it. The idea is to achieve - // 'Do what I mean' functionality when starting Creator in a directory with - // the single command line argument '.' and avoid editor warnings about not - // being able to open directories. - // In addition, convert "filename" "+45" or "filename" ":23" into - // "filename+45" and "filename:23". - if (!arguments.isEmpty()) { - const QStringList sessions = SessionManager::sessions(); - for (int a = 0; a < arguments.size(); ) { - const QString &arg = arguments.at(a); - const QFileInfo fi(arg); - if (fi.isDir()) { - const QDir dir(fi.absoluteFilePath()); - // Does the directory name match a session? - if (dd->m_sessionToRestoreAtStartup.isEmpty() - && sessions.contains(dir.dirName())) { - dd->m_sessionToRestoreAtStartup = dir.dirName(); - arguments.removeAt(a); - continue; - } - } // Done directories. - // Converts "filename" "+45" or "filename" ":23" into "filename+45" and "filename:23" - if (a && (arg.startsWith(QLatin1Char('+')) || arg.startsWith(QLatin1Char(':')))) { - arguments[a - 1].append(arguments.takeAt(a)); - continue; - } - ++a; - } // for arguments - } // !arguments.isEmpty() - // Restore latest session or what was passed on the command line - - SessionManager::loadSession(!dd->m_sessionToRestoreAtStartup.isEmpty() - ? dd->m_sessionToRestoreAtStartup : QString(), true); - - // update welcome page - connect(ModeManager::instance(), &ModeManager::currentModeChanged, - dd, &ProjectExplorerPluginPrivate::currentModeChanged); - connect(&dd->m_welcomePage, &ProjectWelcomePage::requestProject, - m_instance, &ProjectExplorerPlugin::openProjectWelcomePage); - dd->m_arguments = arguments; - // delay opening projects from the command line even more - QTimer::singleShot(0, m_instance, [] { - ICore::openFiles(Utils::transform(dd->m_arguments, &FilePath::fromUserInput), - ICore::OpenFilesFlags(ICore::CanContainLineAndColumnNumbers | ICore::SwitchMode)); - emit m_instance->finishedInitialization(); - }); - updateActions(); -} - void ProjectExplorerPluginPrivate::executeRunConfiguration(RunConfiguration *runConfiguration, Id runMode) { const Tasks runConfigIssues = runConfiguration->checkForIssues(); @@ -2675,7 +2495,7 @@ void ProjectExplorerPluginPrivate::checkForShutdown() { --m_activeRunControlCount; QTC_ASSERT(m_activeRunControlCount >= 0, m_activeRunControlCount = 0); - if (m_shuttingDown && m_activeRunControlCount == 0) + if (PluginManager::isShuttingDown() && m_activeRunControlCount == 0) emit m_instance->asynchronousShutdownFinished(); } @@ -2732,7 +2552,7 @@ RecentProjectsEntries ProjectExplorerPluginPrivate::recentProjects() const void ProjectExplorerPluginPrivate::updateActions() { - const Project *const project = SessionManager::startupProject(); + const Project *const project = ProjectManager::startupProject(); const Project *const currentProject = ProjectTree::currentProject(); // for context menu actions const QPair<bool, QString> buildActionState = buildSettingsEnabled(project); @@ -2743,10 +2563,10 @@ void ProjectExplorerPluginPrivate::updateActions() const QString projectName = project ? project->displayName() : QString(); const QString projectNameContextMenu = currentProject ? currentProject->displayName() : QString(); - m_unloadAction->setParameter(SessionManager::projects().size() == 1 ? projectName : QString()); + m_unloadAction->setParameter(ProjectManager::projects().size() == 1 ? projectName : QString()); m_unloadActionContextMenu->setParameter(projectNameContextMenu); m_unloadOthersActionContextMenu->setParameter(projectNameContextMenu); - m_closeProjectFilesActionFileMenu->setParameter(SessionManager::projects().size() == 1 + m_closeProjectFilesActionFileMenu->setParameter(ProjectManager::projects().size() == 1 ? projectName : QString()); m_closeProjectFilesActionContextMenu->setParameter(projectNameContextMenu); @@ -2791,7 +2611,7 @@ void ProjectExplorerPluginPrivate::updateActions() m_setStartupProjectAction->setParameter(projectNameContextMenu); m_setStartupProjectAction->setVisible(currentProject != project); - const bool hasDependencies = SessionManager::projectOrder(currentProject).size() > 1; + const bool hasDependencies = ProjectManager::projectOrder(currentProject).size() > 1; m_buildActionContextMenu->setVisible(hasDependencies); m_rebuildActionContextMenu->setVisible(hasDependencies); m_cleanActionContextMenu->setVisible(hasDependencies); @@ -2818,17 +2638,17 @@ void ProjectExplorerPluginPrivate::updateActions() m_cleanProjectOnlyAction->setToolTip(buildActionState.second); // Session actions - m_closeAllProjects->setEnabled(SessionManager::hasProjects()); - m_unloadAction->setEnabled(SessionManager::projects().size() <= 1); - m_unloadAction->setEnabled(SessionManager::projects().size() == 1); - m_unloadActionContextMenu->setEnabled(SessionManager::hasProjects()); - m_unloadOthersActionContextMenu->setEnabled(SessionManager::projects().size() >= 2); - m_closeProjectFilesActionFileMenu->setEnabled(SessionManager::projects().size() == 1); - m_closeProjectFilesActionContextMenu->setEnabled(SessionManager::hasProjects()); + m_closeAllProjects->setEnabled(ProjectManager::hasProjects()); + m_unloadAction->setEnabled(ProjectManager::projects().size() <= 1); + m_unloadAction->setEnabled(ProjectManager::projects().size() == 1); + m_unloadActionContextMenu->setEnabled(ProjectManager::hasProjects()); + m_unloadOthersActionContextMenu->setEnabled(ProjectManager::projects().size() >= 2); + m_closeProjectFilesActionFileMenu->setEnabled(ProjectManager::projects().size() == 1); + m_closeProjectFilesActionContextMenu->setEnabled(ProjectManager::hasProjects()); ActionContainer *aci = ActionManager::actionContainer(Constants::M_UNLOADPROJECTS); - aci->menu()->menuAction()->setEnabled(SessionManager::hasProjects()); + aci->menu()->menuAction()->setEnabled(ProjectManager::hasProjects()); m_buildSessionAction->setEnabled(buildSessionState.first); m_buildSessionForAllConfigsAction->setEnabled(buildSessionState.first); @@ -2846,7 +2666,7 @@ void ProjectExplorerPluginPrivate::updateActions() m_cancelBuildAction->setEnabled(BuildManager::isBuilding()); - const bool hasProjects = SessionManager::hasProjects(); + const bool hasProjects = ProjectManager::hasProjects(); m_projectSelectorAction->setEnabled(hasProjects); m_projectSelectorActionMenu->setEnabled(hasProjects); m_projectSelectorActionQuick->setEnabled(hasProjects); @@ -2923,10 +2743,9 @@ void ProjectExplorerPluginPrivate::extendFolderNavigationWidgetFactory() "The file \"%1\" was renamed to \"%2\", " "but the following projects could not be automatically changed: %3") .arg(before.toUserOutput(), after.toUserOutput(), projects); - QTimer::singleShot(0, Core::ICore::instance(), [errorMessage] { - QMessageBox::warning(Core::ICore::dialogParent(), - Tr::tr("Project Editing Failed"), - errorMessage); + QTimer::singleShot(0, ICore::instance(), [errorMessage] { + QMessageBox::warning(ICore::dialogParent(), + Tr::tr("Project Editing Failed"), errorMessage); }); } }); @@ -2944,8 +2763,8 @@ void ProjectExplorerPluginPrivate::extendFolderNavigationWidgetFactory() const QString errorMessage = Tr::tr("The following projects failed to automatically remove the file: %1") .arg(projects); - QTimer::singleShot(0, Core::ICore::instance(), [errorMessage] { - QMessageBox::warning(Core::ICore::dialogParent(), + QTimer::singleShot(0, ICore::instance(), [errorMessage] { + QMessageBox::warning(ICore::dialogParent(), Tr::tr("Project Editing Failed"), errorMessage); }); @@ -2967,7 +2786,7 @@ void ProjectExplorerPluginPrivate::runProjectContextMenu(RunConfiguration *rc) static bool hasBuildSettings(const Project *pro) { - return Utils::anyOf(SessionManager::projectOrder(pro), [](const Project *project) { + return Utils::anyOf(ProjectManager::projectOrder(pro), [](const Project *project) { return project && project->activeTarget() && project->activeTarget()->activeBuildConfiguration(); @@ -2979,7 +2798,7 @@ static QPair<bool, QString> subprojectEnabledState(const Project *pro) QPair<bool, QString> result; result.first = true; - const QList<Project *> &projects = SessionManager::projectOrder(pro); + const QList<Project *> &projects = ProjectManager::projectOrder(pro); for (const Project *project : projects) { if (project && project->activeTarget() && project->activeTarget()->activeBuildConfiguration() @@ -3021,7 +2840,7 @@ QPair<bool, QString> ProjectExplorerPluginPrivate::buildSettingsEnabledForSessio { QPair<bool, QString> result; result.first = true; - if (!SessionManager::hasProjects()) { + if (!ProjectManager::hasProjects()) { result.first = false; result.second = Tr::tr("No project loaded."); } else if (BuildManager::isBuilding()) { @@ -3078,7 +2897,7 @@ void ProjectExplorerPlugin::handleCommandLineArguments(const QStringList &argume static bool hasDeploySettings(Project *pro) { - return Utils::anyOf(SessionManager::projectOrder(pro), [](Project *project) { + return Utils::anyOf(ProjectManager::projectOrder(pro), [](Project *project) { return project->activeTarget() && project->activeTarget()->activeDeployConfiguration(); }); @@ -3096,7 +2915,7 @@ void ProjectExplorerPlugin::runProject(Project *pro, Id mode, const bool forceSk void ProjectExplorerPlugin::runStartupProject(Id runMode, bool forceSkipDeploy) { - runProject(SessionManager::startupProject(), runMode, forceSkipDeploy); + runProject(ProjectManager::startupProject(), runMode, forceSkipDeploy); } void ProjectExplorerPlugin::runRunConfiguration(RunConfiguration *rc, @@ -3157,7 +2976,7 @@ void ProjectExplorerPluginPrivate::projectAdded(Project *pro) void ProjectExplorerPluginPrivate::projectRemoved(Project *pro) { Q_UNUSED(pro) - m_projectsMode.setEnabled(SessionManager::hasProjects()); + m_projectsMode.setEnabled(ProjectManager::hasProjects()); } void ProjectExplorerPluginPrivate::projectDisplayNameChanged(Project *pro) @@ -3168,7 +2987,7 @@ void ProjectExplorerPluginPrivate::projectDisplayNameChanged(Project *pro) void ProjectExplorerPluginPrivate::updateDeployActions() { - Project *project = SessionManager::startupProject(); + Project *project = ProjectManager::startupProject(); bool enableDeployActions = project && !BuildManager::isBuilding(project) @@ -3187,7 +3006,7 @@ void ProjectExplorerPluginPrivate::updateDeployActions() enableDeployActionsContextMenu = false; } - bool hasProjects = SessionManager::hasProjects(); + bool hasProjects = ProjectManager::hasProjects(); m_deployAction->setEnabled(enableDeployActions); @@ -3203,7 +3022,7 @@ void ProjectExplorerPluginPrivate::updateDeployActions() && !project->activeTarget()->activeBuildConfiguration()->isEnabled(); }; - if (Utils::anyOf(SessionManager::projectOrder(nullptr), hasDisabledBuildConfiguration)) + if (Utils::anyOf(ProjectManager::projectOrder(nullptr), hasDisabledBuildConfiguration)) enableDeploySessionAction = false; } if (!hasProjects || !hasDeploySettings(nullptr) || BuildManager::isBuilding()) @@ -3215,7 +3034,7 @@ void ProjectExplorerPluginPrivate::updateDeployActions() bool ProjectExplorerPlugin::canRunStartupProject(Id runMode, QString *whyNot) { - Project *project = SessionManager::startupProject(); + Project *project = ProjectManager::startupProject(); if (!project) { if (whyNot) *whyNot = Tr::tr("No active project."); @@ -3320,7 +3139,7 @@ void ProjectExplorerPluginPrivate::updateUnloadProjectMenu() ActionContainer *aci = ActionManager::actionContainer(Constants::M_UNLOADPROJECTS); QMenu *menu = aci->menu(); menu->clear(); - for (Project *project : SessionManager::projects()) { + for (Project *project : ProjectManager::projects()) { QAction *action = menu->addAction(Tr::tr("Close Project \"%1\"").arg(project->displayName())); connect(action, &QAction::triggered, [project] { ProjectExplorerPlugin::unloadProject(project); } ); @@ -3419,7 +3238,7 @@ void ProjectExplorerPluginPrivate::updateContextMenuActions(Node *currentNode) m_removeFileAction->setVisible(true); m_duplicateFileAction->setVisible(false); m_deleteFileAction->setVisible(true); - m_runActionContextMenu->setVisible(false); + m_runActionContextMenu->setEnabled(false); m_defaultRunConfiguration.clear(); m_diffFileAction->setVisible(DiffService::instance()); @@ -3448,7 +3267,7 @@ void ProjectExplorerPluginPrivate::updateContextMenuActions(Node *currentNode) if (pn && project) { if (pn == project->rootProjectNode()) { - m_runActionContextMenu->setVisible(true); + m_runActionContextMenu->setEnabled(true); } else { QList<RunConfiguration *> runConfigs; if (Target *t = project->activeTarget()) { @@ -3459,7 +3278,7 @@ void ProjectExplorerPluginPrivate::updateContextMenuActions(Node *currentNode) } } if (runConfigs.count() == 1) { - m_runActionContextMenu->setVisible(true); + m_runActionContextMenu->setEnabled(true); m_defaultRunConfiguration = runConfigs.first(); } else if (runConfigs.count() > 1) { runMenu->menu()->menuAction()->setVisible(true); @@ -3592,9 +3411,7 @@ void ProjectExplorerPluginPrivate::updateLocationSubMenus() : Tr::tr("%1 in %2").arg(li.displayName).arg(li.path.toUserOutput()); auto *action = new QAction(displayName, nullptr); connect(action, &QAction::triggered, this, [line, path] { - Core::EditorManager::openEditorAt(Link(path, line), - {}, - Core::EditorManager::AllowExternalEditor); + EditorManager::openEditorAt(Link(path, line), {}, EditorManager::AllowExternalEditor); }); projectMenu->addAction(action); @@ -3795,6 +3612,13 @@ void ProjectExplorerPluginPrivate::showInFileSystemPane() Core::FileUtils::showInFileSystemView(currentNode->filePath()); } +static BuildConfiguration *activeBuildConfiguration(Project *project) +{ + if (!project || !project->activeTarget() || !project->activeTarget()->activeBuildConfiguration()) + return {}; + return project->activeTarget()->activeBuildConfiguration(); +} + void ProjectExplorerPluginPrivate::openTerminalHere(const EnvironmentGetter &env) { const Node *currentNode = ProjectTree::currentNode(); @@ -3804,7 +3628,29 @@ void ProjectExplorerPluginPrivate::openTerminalHere(const EnvironmentGetter &env if (!environment) return; - Core::FileUtils::openTerminal(currentNode->directory(), environment.value()); + BuildConfiguration *bc = activeBuildConfiguration(ProjectTree::projectForNode(currentNode)); + if (!bc) { + Terminal::Hooks::instance().openTerminal({{}, currentNode->directory(), environment}); + return; + } + + IDeviceConstPtr buildDevice = BuildDeviceKitAspect::device(bc->target()->kit()); + + if (!buildDevice) + return; + + FilePath workingDir = currentNode->directory(); + if (!buildDevice->filePath(workingDir.path()).exists() + && !buildDevice->ensureReachable(workingDir)) + workingDir.clear(); + + const FilePath shell = Terminal::defaultShellForDevice(buildDevice->rootPath()); + + if (!shell.isEmpty() && buildDevice->rootPath().needsDevice()) { + Terminal::Hooks::instance().openTerminal({CommandLine{shell, {}}, workingDir, environment}); + } else { + Terminal::Hooks::instance().openTerminal({std::nullopt, workingDir, environment}); + } } void ProjectExplorerPluginPrivate::openTerminalHereWithRunEnv() @@ -3825,9 +3671,21 @@ void ProjectExplorerPluginPrivate::openTerminalHereWithRunEnv() if (!device) device = DeviceKitAspect::device(target->kit()); QTC_ASSERT(device && device->canOpenTerminal(), return); - const FilePath workingDir = device->type() == Constants::DESKTOP_DEVICE_TYPE - ? currentNode->directory() : runnable.workingDirectory; - device->openTerminal(runnable.environment, workingDir); + + FilePath workingDir = device->type() == Constants::DESKTOP_DEVICE_TYPE + ? currentNode->directory() + : runnable.workingDirectory; + + if (!device->filePath(workingDir.path()).exists() && !device->ensureReachable(workingDir)) + workingDir.clear(); + + const FilePath shell = Terminal::defaultShellForDevice(device->rootPath()); + if (!shell.isEmpty() && device->rootPath().needsDevice()) { + Terminal::Hooks::instance().openTerminal( + {CommandLine{shell, {}}, workingDir, runnable.environment}); + } else { + Terminal::Hooks::instance().openTerminal({std::nullopt, workingDir, runnable.environment}); + } } void ProjectExplorerPluginPrivate::removeFile() @@ -3852,7 +3710,7 @@ void ProjectExplorerPluginPrivate::removeFile() if (!siblings.isEmpty()) { const QMessageBox::StandardButton reply = QMessageBox::question( - Core::ICore::dialogParent(), Tr::tr("Remove More Files?"), + ICore::dialogParent(), Tr::tr("Remove More Files?"), Tr::tr("Remove these files as well?\n %1") .arg(Utils::transform<QStringList>(siblings, [](const NodeAndPath &np) { return np.second.fileName(); @@ -4058,41 +3916,6 @@ void ProjectExplorerPluginPrivate::handleSetStartupProject() setStartupProject(ProjectTree::currentProject()); } -void ProjectExplorerPluginPrivate::updateSessionMenu() -{ - m_sessionMenu->clear(); - dd->m_sessionMenu->addAction(dd->m_sessionManagerAction); - dd->m_sessionMenu->addSeparator(); - auto *ag = new QActionGroup(m_sessionMenu); - connect(ag, &QActionGroup::triggered, this, &ProjectExplorerPluginPrivate::setSession); - const QString activeSession = SessionManager::activeSession(); - const bool isDefaultVirgin = SessionManager::isDefaultVirgin(); - - QStringList sessions = SessionManager::sessions(); - std::sort(std::next(sessions.begin()), sessions.end(), [](const QString &s1, const QString &s2) { - return SessionManager::lastActiveTime(s1) > SessionManager::lastActiveTime(s2); - }); - for (int i = 0; i < sessions.size(); ++i) { - const QString &session = sessions[i]; - - const QString actionText = ActionManager::withNumberAccelerator(Utils::quoteAmpersands( - session), - i + 1); - QAction *act = ag->addAction(actionText); - act->setData(session); - act->setCheckable(true); - if (session == activeSession && !isDefaultVirgin) - act->setChecked(true); - } - m_sessionMenu->addActions(ag->actions()); - m_sessionMenu->setEnabled(true); -} - -void ProjectExplorerPluginPrivate::setSession(QAction *action) -{ - SessionManager::loadSession(action->data().toString()); -} - void ProjectExplorerPlugin::setProjectExplorerSettings(const ProjectExplorerSettings &pes) { QTC_ASSERT(dd->m_projectExplorerSettings.environmentId == pes.environmentId, return); @@ -4213,7 +4036,7 @@ void ProjectExplorerPlugin::updateActions() void ProjectExplorerPlugin::activateProjectPanel(Id panelId) { - Core::ModeManager::activateMode(Constants::MODE_SESSION); + ModeManager::activateMode(Constants::MODE_SESSION); dd->m_proWindow->activateProjectPanel(panelId); } @@ -4242,9 +4065,8 @@ RecentProjectsEntries ProjectExplorerPlugin::recentProjects() return dd->recentProjects(); } -void ProjectExplorerPlugin::renameFilesForSymbol( - const QString &oldSymbolName, const QString &newSymbolName, const Utils::FilePaths &files, - bool preferLowerCaseFileNames) +void ProjectExplorerPlugin::renameFilesForSymbol(const QString &oldSymbolName, + const QString &newSymbolName, const FilePaths &files, bool preferLowerCaseFileNames) { static const auto isAllLowerCase = [](const QString &text) { return text.toLower() == text; }; @@ -4319,10 +4141,18 @@ AllProjectFilesFilter::AllProjectFilesFilter() setDefaultIncludedByDefault(false); // but not included in default setFilters({}); setIsCustomFilter(false); - setDescription(Tr::tr( - "Matches all files from all project directories. Append \"+<number>\" or " - "\":<number>\" to jump to the given line number. Append another " - "\"+<number>\" or \":<number>\" to jump to the column number as well.")); + setDescription(Tr::tr("Locates files from all project directories. Append \"+<number>\" or " + "\":<number>\" to jump to the given line number. Append another " + "\"+<number>\" or \":<number>\" to jump to the column number as well.")); + + ProjectManager *projectManager = ProjectManager::instance(); + QTC_ASSERT(projectManager, return); + connect(projectManager, &ProjectManager::projectAdded, this, [this](Project *project) { + addDirectory(project->projectDirectory()); + }); + connect(projectManager, &ProjectManager::projectRemoved, this, [this](Project *project) { + removeDirectory(project->projectDirectory()); + }); } const char kDirectoriesKey[] = "directories"; @@ -4346,102 +4176,104 @@ void AllProjectFilesFilter::restoreState(const QJsonObject &object) DirectoryFilter::restoreState(withoutDirectories); } -RunConfigurationLocatorFilter::RunConfigurationLocatorFilter() +static void setupFilter(ILocatorFilter *filter) { - connect(SessionManager::instance(), &SessionManager::startupProjectChanged, - this, &RunConfigurationLocatorFilter::targetListUpdated); - - targetListUpdated(); + QObject::connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged, + filter, [filter] { filter->setEnabled(ProjectManager::startupProject()); }); + filter->setEnabled(ProjectManager::startupProject()); } -void RunConfigurationLocatorFilter::prepareSearch(const QString &entry) +using RunAcceptor = std::function<void(RunConfiguration *)>; + +static RunConfiguration *runConfigurationForDisplayName(const QString &displayName) { - m_result.clear(); - const Target *target = SessionManager::startupTarget(); + const Target *target = ProjectManager::startupTarget(); if (!target) - return; - for (auto rc : target->runConfigurations()) { - if (rc->displayName().contains(entry, Qt::CaseInsensitive)) - m_result.append(LocatorFilterEntry(this, rc->displayName())); - } + return nullptr; + const QList<RunConfiguration *> runconfigs = target->runConfigurations(); + return Utils::findOrDefault(runconfigs, [displayName](RunConfiguration *rc) { + return rc->displayName() == displayName; + }); } -QList<Core::LocatorFilterEntry> RunConfigurationLocatorFilter::matchesFor( - QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry) +static LocatorMatcherTasks runConfigurationMatchers(const RunAcceptor &acceptor) { - Q_UNUSED(future) - Q_UNUSED(entry) - return m_result; -} + using namespace Tasking; -void RunConfigurationLocatorFilter::targetListUpdated() -{ - setEnabled(SessionManager::startupProject()); // at least one project opened + TreeStorage<LocatorStorage> storage; + + const auto onSetup = [storage, acceptor] { + const QString input = storage->input(); + const Target *target = ProjectManager::startupTarget(); + if (!target) + return; + + LocatorFilterEntries entries; + for (auto rc : target->runConfigurations()) { + if (rc->displayName().contains(input, Qt::CaseInsensitive)) { + LocatorFilterEntry entry; + entry.displayName = rc->displayName(); + entry.acceptor = [name = entry.displayName, acceptor = acceptor] { + RunConfiguration *config = runConfigurationForDisplayName(name); + if (!config) + return AcceptResult(); + acceptor(config); + return AcceptResult(); + }; + entries.append(entry); + } + } + storage->reportOutput(entries); + }; + return {{Sync(onSetup), storage}}; } -static RunConfiguration *runConfigurationForDisplayName(const QString &displayName) +static void runAcceptor(RunConfiguration *config) { - const Project *project = SessionManager::instance()->startupProject(); - if (!project) - return nullptr; - const QList<RunConfiguration *> runconfigs = project->activeTarget()->runConfigurations(); - return Utils::findOrDefault(runconfigs, [displayName](RunConfiguration *rc) { - return rc->displayName() == displayName; - }); + if (!BuildManager::isBuilding(config->project())) + ProjectExplorerPlugin::runRunConfiguration(config, Constants::NORMAL_RUN_MODE, true); } -RunRunConfigurationLocatorFilter::RunRunConfigurationLocatorFilter() +RunConfigurationStartFilter::RunConfigurationStartFilter() { setId("Run run configuration"); - setDisplayName(Tr::tr("Run run configuration")); - setDescription(Tr::tr("Run a run configuration of the current active project")); + setDisplayName(Tr::tr("Run Run Configuration")); + setDescription(Tr::tr("Runs a run configuration of the active project.")); setDefaultShortcutString("rr"); setPriority(Medium); + setupFilter(this); } -void RunRunConfigurationLocatorFilter::accept(const LocatorFilterEntry &selection, QString *newText, - int *selectionStart, int *selectionLength) const +LocatorMatcherTasks RunConfigurationStartFilter::matchers() { - Q_UNUSED(newText) - Q_UNUSED(selectionStart) - Q_UNUSED(selectionLength) + return runConfigurationMatchers(&runAcceptor); +} - RunConfiguration *toStart = runConfigurationForDisplayName(selection.displayName); - if (!toStart) - return; - if (!BuildManager::isBuilding(toStart->project())) - ProjectExplorerPlugin::runRunConfiguration(toStart, Constants::NORMAL_RUN_MODE, true); +static void switchAcceptor(RunConfiguration *config) +{ + ProjectManager::startupTarget()->setActiveRunConfiguration(config); + QTimer::singleShot(200, ICore::mainWindow(), [name = config->displayName()] { + if (auto ks = ICore::mainWindow()->findChild<QWidget *>("KitSelector.Button")) { + ToolTip::show(ks->mapToGlobal(QPoint{25, 25}), + Tr::tr("Switched run configuration to\n%1").arg(name), + ICore::dialogParent()); + } + }); } -SwitchToRunConfigurationLocatorFilter::SwitchToRunConfigurationLocatorFilter() +RunConfigurationSwitchFilter::RunConfigurationSwitchFilter() { setId("Switch run configuration"); - setDisplayName(Tr::tr("Switch run configuration")); - setDescription(Tr::tr("Switch active run configuration")); + setDisplayName(Tr::tr("Switch Run Configuration")); + setDescription(Tr::tr("Switches the active run configuration of the active project.")); setDefaultShortcutString("sr"); setPriority(Medium); + setupFilter(this); } -void SwitchToRunConfigurationLocatorFilter::accept(const LocatorFilterEntry &selection, - QString *newText, int *selectionStart, - int *selectionLength) const +LocatorMatcherTasks RunConfigurationSwitchFilter::matchers() { - Q_UNUSED(newText) - Q_UNUSED(selectionStart) - Q_UNUSED(selectionLength) - - RunConfiguration *toSwitchTo = runConfigurationForDisplayName(selection.displayName); - if (!toSwitchTo) - return; - - SessionManager::startupTarget()->setActiveRunConfiguration(toSwitchTo); - QTimer::singleShot(200, this, [displayName = selection.displayName] { - if (auto ks = ICore::mainWindow()->findChild<QWidget *>("KitSelector.Button")) { - Utils::ToolTip::show(ks->mapToGlobal(QPoint{25, 25}), - Tr::tr("Switched run configuration to\n%1").arg(displayName), - ICore::dialogParent()); - } - }); + return runConfigurationMatchers(&switchAcceptor); } } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/projectexplorer.h b/src/plugins/projectexplorer/projectexplorer.h index baa8bb83a6..0b87f53310 100644 --- a/src/plugins/projectexplorer/projectexplorer.h +++ b/src/plugins/projectexplorer/projectexplorer.h @@ -155,7 +155,6 @@ public: static QThreadPool *sharedThreadPool(); static Internal::MiniProjectTargetSelector *targetSelector(); - static void showSessionManager(); static void openNewProjectDialog(); static void openOpenProjectDialog(); @@ -261,6 +260,9 @@ private slots: void testProject_projectTree(); void testProject_multipleBuildConfigs(); + void testSourceToBinaryMapping(); + void testSourceToBinaryMapping_data(); + void testSessionSwitch(); #endif // WITH_TESTS }; diff --git a/src/plugins/projectexplorer/projectexplorer.qbs b/src/plugins/projectexplorer/projectexplorer.qbs index e65cc1bdc3..c955a48590 100644 --- a/src/plugins/projectexplorer/projectexplorer.qbs +++ b/src/plugins/projectexplorer/projectexplorer.qbs @@ -45,6 +45,7 @@ Project { "codestylesettingspropertiespage.cpp", "codestylesettingspropertiespage.h", "compileoutputwindow.cpp", "compileoutputwindow.h", "configtaskhandler.cpp", "configtaskhandler.h", + "copystep.cpp", "copystep.h", "copytaskhandler.cpp", "copytaskhandler.h", "currentprojectfilter.cpp", "currentprojectfilter.h", "currentprojectfind.cpp", "currentprojectfind.h", @@ -91,7 +92,6 @@ Project { "ldparser.cpp", "ldparser.h", "lldparser.cpp", "lldparser.h", "linuxiccparser.cpp", "linuxiccparser.h", - "localenvironmentaspect.cpp", "localenvironmentaspect.h", "makestep.cpp", "makestep.h", "miniprojecttargetselector.cpp", "miniprojecttargetselector.h", "msvcparser.cpp", "msvcparser.h", @@ -110,13 +110,12 @@ Project { "projectexplorerconstants.cpp", "projectexplorerconstants.h", "projectexplorericons.h", "projectexplorericons.cpp", - "projectexplorersettings.h", - "projectexplorersettingspage.cpp", "projectexplorersettingspage.h", + "projectexplorersettings.h", "projectexplorersettings.cpp", "projectexplorertr.h", "projectfilewizardextension.cpp", "projectfilewizardextension.h", "projectimporter.cpp", "projectimporter.h", "projectmacro.cpp", "projectmacro.h", - "projectmanager.h", + "projectmanager.cpp", "projectmanager.h", "projectmodels.cpp", "projectmodels.h", "projectnodes.cpp", "projectnodes.h", "projectpanelfactory.cpp", "projectpanelfactory.h", @@ -134,10 +133,6 @@ Project { "runsettingspropertiespage.cpp", "runsettingspropertiespage.h", "sanitizerparser.cpp", "sanitizerparser.h", "selectablefilesmodel.cpp", "selectablefilesmodel.h", - "session.cpp", "session.h", - "sessionmodel.cpp", "sessionmodel.h", - "sessionview.cpp", "sessionview.h", - "sessiondialog.cpp", "sessiondialog.h", "showineditortaskhandler.cpp", "showineditortaskhandler.h", "showoutputtaskhandler.cpp", "showoutputtaskhandler.h", "simpleprojectwizard.cpp", "simpleprojectwizard.h", @@ -226,8 +221,7 @@ Project { "idevicefactory.cpp", "idevicefactory.h", "idevicefwd.h", "idevicewidget.h", - "localprocesslist.cpp", "localprocesslist.h", - "sshdeviceprocesslist.cpp", "sshdeviceprocesslist.h", + "processlist.cpp", "processlist.h", "sshparameters.cpp", "sshparameters.h", "sshsettings.cpp", "sshsettings.h", "sshsettingspage.cpp", "sshsettingspage.h", @@ -250,9 +244,7 @@ Project { ] } - Group { - name: "Tests" - condition: qtc.testsEnabled + QtcTestFiles { files: ["outputparser_test.h", "outputparser_test.cpp"] } diff --git a/src/plugins/projectexplorer/projectexplorer.qrc b/src/plugins/projectexplorer/projectexplorer.qrc index ececb0854b..0cc88e3331 100644 --- a/src/plugins/projectexplorer/projectexplorer.qrc +++ b/src/plugins/projectexplorer/projectexplorer.qrc @@ -86,5 +86,13 @@ <file>images/settingscategory_cpp@2x.png</file> <file>images/importasproject.png</file> <file>images/importasproject@2x.png</file> + <file>testdata/multi-target-project/CMakeLists.txt</file> + <file>testdata/multi-target-project/multi-target-project-app.pro</file> + <file>testdata/multi-target-project/multi-target-project-lib.cpp</file> + <file>testdata/multi-target-project/multi-target-project-lib.pro</file> + <file>testdata/multi-target-project/multi-target-project-main.cpp</file> + <file>testdata/multi-target-project/multi-target-project-shared.h</file> + <file>testdata/multi-target-project/multi-target-project.pro</file> + <file>testdata/multi-target-project/multi-target-project.qbs</file> </qresource> </RCC> diff --git a/src/plugins/projectexplorer/projectexplorerconstants.h b/src/plugins/projectexplorer/projectexplorerconstants.h index 9c86d6d9cf..75054120a8 100644 --- a/src/plugins/projectexplorer/projectexplorerconstants.h +++ b/src/plugins/projectexplorer/projectexplorerconstants.h @@ -132,6 +132,10 @@ const char BUILDSTEPS_CLEAN[] = "ProjectExplorer.BuildSteps.Clean"; const char BUILDSTEPS_BUILD[] = "ProjectExplorer.BuildSteps.Build"; const char BUILDSTEPS_DEPLOY[] = "ProjectExplorer.BuildSteps.Deploy"; +const char COPY_FILE_STEP[] = "ProjectExplorer.CopyFileStep"; +const char COPY_DIRECTORY_STEP[] = "ProjectExplorer.CopyDirectoryStep"; +const char DEVICE_CHECK_STEP[] = "ProjectExplorer.DeviceCheckBuildStep"; + // Language // Keep these short: These constants are exposed to the MacroExplorer! @@ -202,8 +206,6 @@ const char FILEOVERLAY_UNKNOWN[]=":/projectexplorer/images/fileoverlay_unknown.p // Settings const char ADD_FILES_DIALOG_FILTER_HISTORY_KEY[] = "ProjectExplorer.AddFilesFilterKey"; const char PROJECT_ROOT_PATH_KEY[] = "ProjectExplorer.Project.RootPath"; -const char STARTUPSESSION_KEY[] = "ProjectExplorer/SessionToRestore"; -const char LASTSESSION_KEY[] = "ProjectExplorer/StartupSession"; const char SETTINGS_MENU_HIDE_BUILD[] = "Menu/HideBuild"; const char SETTINGS_MENU_HIDE_DEBUG[] = "Menu/HideDebug"; const char SETTINGS_MENU_HIDE_ANALYZE[] = "Menu/HideAnalyze"; diff --git a/src/plugins/projectexplorer/projectexplorersettingspage.cpp b/src/plugins/projectexplorer/projectexplorersettings.cpp index 30eb850cd0..14f66b885b 100644 --- a/src/plugins/projectexplorer/projectexplorersettingspage.cpp +++ b/src/plugins/projectexplorer/projectexplorersettings.cpp @@ -1,7 +1,7 @@ // 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 "projectexplorersettingspage.h" +#include "projectexplorersettings.h" #include "projectexplorer.h" #include "projectexplorerconstants.h" @@ -25,15 +25,14 @@ using namespace Core; using namespace Utils; -namespace ProjectExplorer { -namespace Internal { +namespace ProjectExplorer::Internal { enum { UseCurrentDirectory, UseProjectDirectory }; -class ProjectExplorerSettingsWidget : public QWidget +class ProjectExplorerSettingsWidget : public IOptionsPageWidget { public: - explicit ProjectExplorerSettingsWidget(QWidget *parent = nullptr); + ProjectExplorerSettingsWidget(); ProjectExplorerSettings settings() const; void setSettings(const ProjectExplorerSettings &s); @@ -44,6 +43,13 @@ public: bool useProjectsDirectory(); void setUseProjectsDirectory(bool v); + void apply() final + { + ProjectExplorerPlugin::setProjectExplorerSettings(settings()); + DocumentManager::setProjectsDirectory(projectsDirectory()); + DocumentManager::setUseProjectsDirectory(useProjectsDirectory()); + } + private: void slotDirectoryButtonGroupChanged(); @@ -68,8 +74,7 @@ private: QButtonGroup *m_directoryButtonGroup; }; -ProjectExplorerSettingsWidget::ProjectExplorerSettingsWidget(QWidget *parent) : - QWidget(parent) +ProjectExplorerSettingsWidget::ProjectExplorerSettingsWidget() { m_currentDirectoryRadioButton = new QRadioButton(Tr::tr("Current directory")); m_directoryRadioButton = new QRadioButton(Tr::tr("Directory")); @@ -117,7 +122,7 @@ ProjectExplorerSettingsWidget::ProjectExplorerSettingsWidget(QWidget *parent) : "Disable it if you experience problems with your builds."); jomLabel->setWordWrap(true); - using namespace Utils::Layouting; + using namespace Layouting; Column { Group { title(Tr::tr("Projects Directory")), @@ -165,6 +170,10 @@ ProjectExplorerSettingsWidget::ProjectExplorerSettingsWidget(QWidget *parent) : connect(m_directoryButtonGroup, &QButtonGroup::buttonClicked, this, &ProjectExplorerSettingsWidget::slotDirectoryButtonGroupChanged); + + setSettings(ProjectExplorerPlugin::projectExplorerSettings()); + setProjectsDirectory(DocumentManager::projectsDirectory()); + setUseProjectsDirectory(DocumentManager::useProjectsDirectory()); } ProjectExplorerSettings ProjectExplorerSettingsWidget::settings() const @@ -236,7 +245,8 @@ void ProjectExplorerSettingsWidget::slotDirectoryButtonGroupChanged() m_projectsDirectoryPathChooser->setEnabled(enable); } -// ------------------ ProjectExplorerSettingsPage +// ProjectExplorerSettingsPage + ProjectExplorerSettingsPage::ProjectExplorerSettingsPage() { setId(Constants::BUILD_AND_RUN_SETTINGS_PAGE_ID); @@ -244,32 +254,7 @@ ProjectExplorerSettingsPage::ProjectExplorerSettingsPage() setCategory(Constants::BUILD_AND_RUN_SETTINGS_CATEGORY); setDisplayCategory(Tr::tr("Build & Run")); setCategoryIconPath(":/projectexplorer/images/settingscategory_buildrun.png"); + setWidgetCreator([] { return new ProjectExplorerSettingsWidget; }); } -QWidget *ProjectExplorerSettingsPage::widget() -{ - if (!m_widget) { - m_widget = new ProjectExplorerSettingsWidget; - m_widget->setSettings(ProjectExplorerPlugin::projectExplorerSettings()); - m_widget->setProjectsDirectory(DocumentManager::projectsDirectory()); - m_widget->setUseProjectsDirectory(DocumentManager::useProjectsDirectory()); - } - return m_widget; -} - -void ProjectExplorerSettingsPage::apply() -{ - if (m_widget) { - ProjectExplorerPlugin::setProjectExplorerSettings(m_widget->settings()); - DocumentManager::setProjectsDirectory(m_widget->projectsDirectory()); - DocumentManager::setUseProjectsDirectory(m_widget->useProjectsDirectory()); - } -} - -void ProjectExplorerSettingsPage::finish() -{ - delete m_widget; -} - -} // namespace Internal -} // namespace ProjectExplorer +} // ProjectExplorer::Internal diff --git a/src/plugins/projectexplorer/projectexplorersettings.h b/src/plugins/projectexplorer/projectexplorersettings.h index b4b80124cd..48cc85c414 100644 --- a/src/plugins/projectexplorer/projectexplorersettings.h +++ b/src/plugins/projectexplorer/projectexplorersettings.h @@ -4,6 +4,8 @@ #pragma once #include <coreplugin/coreconstants.h> +#include <coreplugin/dialogs/ioptionspage.h> + #include <utils/hostosinfo.h> #include <QUuid> @@ -23,7 +25,6 @@ public: && p1.deployBeforeRun == p2.deployBeforeRun && p1.saveBeforeBuild == p2.saveBeforeBuild && p1.useJom == p2.useJom - && p1.autorestoreLastSession == p2.autorestoreLastSession && p1.prompToStopRunControl == p2.prompToStopRunControl && p1.automaticallyCreateRunConfigurations == p2.automaticallyCreateRunConfigurations && p1.addLibraryPathsToRunEnv == p2.addLibraryPathsToRunEnv @@ -40,7 +41,6 @@ public: bool deployBeforeRun = true; bool saveBeforeBuild = false; bool useJom = true; - bool autorestoreLastSession = false; // This option is set in the Session Manager! bool prompToStopRunControl = false; bool automaticallyCreateRunConfigurations = true; bool addLibraryPathsToRunEnv = true; @@ -74,12 +74,10 @@ public: int maxCharCount = Core::Constants::DEFAULT_MAX_CHAR_COUNT; }; -class CompileOutputSettings +class ProjectExplorerSettingsPage : public Core::IOptionsPage { public: - bool popUp = false; - bool wrapOutput = false; - int maxCharCount = Core::Constants::DEFAULT_MAX_CHAR_COUNT; + ProjectExplorerSettingsPage(); }; } // namespace Internal diff --git a/src/plugins/projectexplorer/projectexplorersettingspage.h b/src/plugins/projectexplorer/projectexplorersettingspage.h deleted file mode 100644 index 82764c3850..0000000000 --- a/src/plugins/projectexplorer/projectexplorersettingspage.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <coreplugin/dialogs/ioptionspage.h> - -#include <QPointer> - -namespace ProjectExplorer { -namespace Internal { - -class ProjectExplorerSettingsWidget; - -class ProjectExplorerSettingsPage : public Core::IOptionsPage -{ -public: - ProjectExplorerSettingsPage(); - - QWidget *widget() override; - void apply() override; - void finish() override; - -private: - QPointer<ProjectExplorerSettingsWidget> m_widget; -}; - -} // namespace Internal -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/projectfilewizardextension.cpp b/src/plugins/projectexplorer/projectfilewizardextension.cpp index 53d3eb1578..134db6ea48 100644 --- a/src/plugins/projectexplorer/projectfilewizardextension.cpp +++ b/src/plugins/projectexplorer/projectfilewizardextension.cpp @@ -7,10 +7,10 @@ #include "project.h" #include "projectexplorerconstants.h" #include "projectexplorertr.h" +#include "projectmanager.h" #include "projectnodes.h" #include "projecttree.h" #include "projectwizardpage.h" -#include "session.h" #include <coreplugin/icore.h> @@ -27,7 +27,6 @@ #include <utils/stringutils.h> #include <QDebug> -#include <QFileInfo> #include <QMessageBox> #include <QPointer> #include <QTextCursor> @@ -134,7 +133,7 @@ Node *ProjectFileWizardExtension::findWizardContextNode(Node *contextNode, Proje const FilePath &path) { if (contextNode && !ProjectTree::hasNode(contextNode)) { - if (SessionManager::projects().contains(project) && project->rootProjectNode()) { + if (ProjectManager::projects().contains(project) && project->rootProjectNode()) { contextNode = project->rootProjectNode()->findNode([path](const Node *n) { return path == n->filePath(); }); diff --git a/src/plugins/projectexplorer/projectmanager.cpp b/src/plugins/projectexplorer/projectmanager.cpp new file mode 100644 index 0000000000..e3c8e65c59 --- /dev/null +++ b/src/plugins/projectexplorer/projectmanager.cpp @@ -0,0 +1,768 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "projectmanager.h" + + +#include "buildconfiguration.h" +#include "editorconfiguration.h" +#include "project.h" +#include "projectexplorer.h" +#include "projectexplorerconstants.h" +#include "projectexplorertr.h" +#include "projectmanager.h" +#include "projectnodes.h" +#include "target.h" + +#include <coreplugin/coreconstants.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/foldernavigationwidget.h> +#include <coreplugin/icore.h> +#include <coreplugin/idocument.h> +#include <coreplugin/imode.h> +#include <coreplugin/modemanager.h> +#include <coreplugin/progressmanager/progressmanager.h> +#include <coreplugin/session.h> + +#include <texteditor/texteditor.h> + +#include <utils/algorithm.h> +#include <utils/filepath.h> +#include <utils/qtcassert.h> +#include <utils/stylehelper.h> +#include <utils/qtcassert.h> + +#include <QDebug> +#include <QMessageBox> +#include <QPushButton> + +#ifdef WITH_TESTS +#include <QTemporaryFile> +#include <QTest> +#include <vector> +#endif + +using namespace Core; +using namespace Utils; +using namespace ProjectExplorer::Internal; + +namespace ProjectExplorer { + +class ProjectManagerPrivate +{ +public: + void loadSession(); + void saveSession(); + void restoreDependencies(); + void restoreStartupProject(); + void restoreProjects(const FilePaths &fileList); + void askUserAboutFailedProjects(); + + bool recursiveDependencyCheck(const FilePath &newDep, const FilePath &checkDep) const; + FilePaths dependencies(const FilePath &proName) const; + FilePaths dependenciesOrder() const; + void dependencies(const FilePath &proName, FilePaths &result) const; + + static QString windowTitleAddition(const FilePath &filePath); + static QString sessionTitle(const FilePath &filePath); + + bool hasProjects() const { return !m_projects.isEmpty(); } + + bool m_casadeSetActive = false; + + Project *m_startupProject = nullptr; + QList<Project *> m_projects; + FilePaths m_failedProjects; + QMap<FilePath, FilePaths> m_depMap; + +private: + static QString locationInProject(const FilePath &filePath); +}; + +static ProjectManager *m_instance = nullptr; +static ProjectManagerPrivate *d = nullptr; + +static QString projectFolderId(Project *pro) +{ + return pro->projectFilePath().toString(); +} + +const int PROJECT_SORT_VALUE = 100; + +ProjectManager::ProjectManager() +{ + m_instance = this; + d = new ProjectManagerPrivate; + + connect(EditorManager::instance(), &EditorManager::editorCreated, + this, &ProjectManager::configureEditor); + connect(this, &ProjectManager::projectAdded, + EditorManager::instance(), &EditorManager::updateWindowTitles); + connect(this, &ProjectManager::projectRemoved, + EditorManager::instance(), &EditorManager::updateWindowTitles); + connect(this, &ProjectManager::projectDisplayNameChanged, + EditorManager::instance(), &EditorManager::updateWindowTitles); + + EditorManager::setWindowTitleAdditionHandler(&ProjectManagerPrivate::windowTitleAddition); + EditorManager::setSessionTitleHandler(&ProjectManagerPrivate::sessionTitle); + + connect(SessionManager::instance(), &SessionManager::aboutToLoadSession, this, [] { + d->loadSession(); + }); + connect(SessionManager::instance(), &SessionManager::aboutToSaveSession, this, [] { + d->saveSession(); + }); +} + +ProjectManager::~ProjectManager() +{ + EditorManager::setWindowTitleAdditionHandler({}); + EditorManager::setSessionTitleHandler({}); + delete d; + d = nullptr; +} + +ProjectManager *ProjectManager::instance() +{ + return m_instance; +} + +bool ProjectManagerPrivate::recursiveDependencyCheck(const FilePath &newDep, + const FilePath &checkDep) const +{ + if (newDep == checkDep) + return false; + + const FilePaths depList = m_depMap.value(checkDep); + for (const FilePath &dependency : depList) { + if (!recursiveDependencyCheck(newDep, dependency)) + return false; + } + + return true; +} + +/* + * The dependency management exposes an interface based on projects, but + * is internally purely string based. This is suboptimal. Probably it would be + * nicer to map the filenames to projects on load and only map it back to + * filenames when saving. + */ + +QList<Project *> ProjectManager::dependencies(const Project *project) +{ + const FilePath proName = project->projectFilePath(); + const FilePaths proDeps = d->m_depMap.value(proName); + + QList<Project *> projects; + for (const FilePath &dep : proDeps) { + Project *pro = Utils::findOrDefault(d->m_projects, [&dep](Project *p) { + return p->projectFilePath() == dep; + }); + if (pro) + projects += pro; + } + + return projects; +} + +bool ProjectManager::hasDependency(const Project *project, const Project *depProject) +{ + const FilePath proName = project->projectFilePath(); + const FilePath depName = depProject->projectFilePath(); + + const FilePaths proDeps = d->m_depMap.value(proName); + return proDeps.contains(depName); +} + +bool ProjectManager::canAddDependency(const Project *project, const Project *depProject) +{ + const FilePath newDep = project->projectFilePath(); + const FilePath checkDep = depProject->projectFilePath(); + + return d->recursiveDependencyCheck(newDep, checkDep); +} + +bool ProjectManager::addDependency(Project *project, Project *depProject) +{ + const FilePath proName = project->projectFilePath(); + const FilePath depName = depProject->projectFilePath(); + + // check if this dependency is valid + if (!d->recursiveDependencyCheck(proName, depName)) + return false; + + FilePaths proDeps = d->m_depMap.value(proName); + if (!proDeps.contains(depName)) { + proDeps.append(depName); + d->m_depMap[proName] = proDeps; + } + emit m_instance->dependencyChanged(project, depProject); + + return true; +} + +void ProjectManager::removeDependency(Project *project, Project *depProject) +{ + const FilePath proName = project->projectFilePath(); + const FilePath depName = depProject->projectFilePath(); + + FilePaths proDeps = d->m_depMap.value(proName); + proDeps.removeAll(depName); + if (proDeps.isEmpty()) + d->m_depMap.remove(proName); + else + d->m_depMap[proName] = proDeps; + emit m_instance->dependencyChanged(project, depProject); +} + +bool ProjectManager::isProjectConfigurationCascading() +{ + return d->m_casadeSetActive; +} + +void ProjectManager::setProjectConfigurationCascading(bool b) +{ + d->m_casadeSetActive = b; + SessionManager::markSessionFileDirty(); +} + +void ProjectManager::setStartupProject(Project *startupProject) +{ + QTC_ASSERT((!startupProject && d->m_projects.isEmpty()) + || (startupProject && d->m_projects.contains(startupProject)), return); + + if (d->m_startupProject == startupProject) + return; + + d->m_startupProject = startupProject; + if (d->m_startupProject && d->m_startupProject->needsConfiguration()) { + ModeManager::activateMode(Constants::MODE_SESSION); + ModeManager::setFocusToCurrentMode(); + } + FolderNavigationWidgetFactory::setFallbackSyncFilePath( + startupProject ? startupProject->projectFilePath().parentDir() : FilePath()); + emit m_instance->startupProjectChanged(startupProject); +} + +Project *ProjectManager::startupProject() +{ + return d->m_startupProject; +} + +Target *ProjectManager::startupTarget() +{ + return d->m_startupProject ? d->m_startupProject->activeTarget() : nullptr; +} + +BuildSystem *ProjectManager::startupBuildSystem() +{ + Target *t = startupTarget(); + return t ? t->buildSystem() : nullptr; +} + +/*! + * Returns the RunConfiguration of the currently active target + * of the startup project, if such exists, or \c nullptr otherwise. + */ + + +RunConfiguration *ProjectManager::startupRunConfiguration() +{ + Target *t = startupTarget(); + return t ? t->activeRunConfiguration() : nullptr; +} + +void ProjectManager::addProject(Project *pro) +{ + QTC_ASSERT(pro, return); + QTC_CHECK(!pro->displayName().isEmpty()); + QTC_CHECK(pro->id().isValid()); + + SessionManager::markSessionFileDirty(); + QTC_ASSERT(!d->m_projects.contains(pro), return); + + d->m_projects.append(pro); + + connect(pro, &Project::displayNameChanged, + m_instance, [pro]() { emit m_instance->projectDisplayNameChanged(pro); }); + + emit m_instance->projectAdded(pro); + const auto updateFolderNavigation = [pro] { + // destructing projects might trigger changes, so check if the project is actually there + if (QTC_GUARD(d->m_projects.contains(pro))) { + const QIcon icon = pro->rootProjectNode() ? pro->rootProjectNode()->icon() : QIcon(); + FolderNavigationWidgetFactory::insertRootDirectory({projectFolderId(pro), + PROJECT_SORT_VALUE, + pro->displayName(), + pro->projectFilePath().parentDir(), + icon}); + } + }; + updateFolderNavigation(); + configureEditors(pro); + connect(pro, &Project::fileListChanged, m_instance, [pro, updateFolderNavigation]() { + configureEditors(pro); + updateFolderNavigation(); // update icon + }); + connect(pro, &Project::displayNameChanged, m_instance, updateFolderNavigation); + + if (!startupProject()) + setStartupProject(pro); +} + +void ProjectManager::removeProject(Project *project) +{ + SessionManager::markSessionFileDirty(); + QTC_ASSERT(project, return); + removeProjects({project}); +} + +void ProjectManagerPrivate::saveSession() +{ + // save the startup project + if (d->m_startupProject) + SessionManager::setSessionValue("StartupProject", + m_startupProject->projectFilePath().toSettings()); + + FilePaths projectFiles = Utils::transform(m_projects, &Project::projectFilePath); + // Restore information on projects that failed to load: + // don't read projects to the list, which the user loaded + for (const FilePath &failed : std::as_const(m_failedProjects)) { + if (!projectFiles.contains(failed)) + projectFiles << failed; + } + + SessionManager::setSessionValue("ProjectList", + Utils::transform<QStringList>(projectFiles, + &FilePath::toString)); + SessionManager::setSessionValue("CascadeSetActive", m_casadeSetActive); + + QVariantMap depMap; + auto i = m_depMap.constBegin(); + while (i != m_depMap.constEnd()) { + QString key = i.key().toString(); + QStringList values; + const FilePaths valueList = i.value(); + for (const FilePath &value : valueList) + values << value.toString(); + depMap.insert(key, values); + ++i; + } + SessionManager::setSessionValue(QLatin1String("ProjectDependencies"), QVariant(depMap)); +} + +/*! + Closes all projects + */ +void ProjectManager::closeAllProjects() +{ + removeProjects(projects()); +} + +const QList<Project *> ProjectManager::projects() +{ + return d->m_projects; +} + +bool ProjectManager::hasProjects() +{ + return d->hasProjects(); +} + +bool ProjectManager::hasProject(Project *p) +{ + return d->m_projects.contains(p); +} + +FilePaths ProjectManagerPrivate::dependencies(const FilePath &proName) const +{ + FilePaths result; + dependencies(proName, result); + return result; +} + +void ProjectManagerPrivate::dependencies(const FilePath &proName, FilePaths &result) const +{ + const FilePaths depends = m_depMap.value(proName); + + for (const FilePath &dep : depends) + dependencies(dep, result); + + if (!result.contains(proName)) + result.append(proName); +} + +QString ProjectManagerPrivate::sessionTitle(const FilePath &filePath) +{ + const QString sessionName = SessionManager::activeSession(); + if (SessionManager::isDefaultSession(sessionName)) { + if (filePath.isEmpty()) { + // use single project's name if there is only one loaded. + const QList<Project *> projects = ProjectManager::projects(); + if (projects.size() == 1) + return projects.first()->displayName(); + } + } else { + return sessionName.isEmpty() ? Tr::tr("Untitled") : sessionName; + } + return QString(); +} + +QString ProjectManagerPrivate::locationInProject(const FilePath &filePath) +{ + const Project *project = ProjectManager::projectForFile(filePath); + if (!project) + return QString(); + + const FilePath parentDir = filePath.parentDir(); + if (parentDir == project->projectDirectory()) + return "@ " + project->displayName(); + + if (filePath.isChildOf(project->projectDirectory())) { + const FilePath dirInProject = parentDir.relativeChildPath(project->projectDirectory()); + return "(" + dirInProject.toUserOutput() + " @ " + project->displayName() + ")"; + } + + // For a file that is "outside" the project it belongs to, we display its + // dir's full path because it is easier to read than a series of "../../.". + // Example: /home/hugo/GenericProject/App.files lists /home/hugo/lib/Bar.cpp + return "(" + parentDir.toUserOutput() + " @ " + project->displayName() + ")"; +} + +QString ProjectManagerPrivate::windowTitleAddition(const FilePath &filePath) +{ + return filePath.isEmpty() ? QString() : locationInProject(filePath); +} + +FilePaths ProjectManagerPrivate::dependenciesOrder() const +{ + QList<QPair<FilePath, FilePaths>> unordered; + FilePaths ordered; + + // copy the map to a temporary list + for (const Project *pro : m_projects) { + const FilePath proName = pro->projectFilePath(); + const FilePaths depList = filtered(m_depMap.value(proName), + [this](const FilePath &proPath) { + return contains(m_projects, [proPath](const Project *p) { + return p->projectFilePath() == proPath; + }); + }); + unordered.push_back({proName, depList}); + } + + while (!unordered.isEmpty()) { + for (int i = (unordered.count() - 1); i >= 0; --i) { + if (unordered.at(i).second.isEmpty()) { + ordered << unordered.at(i).first; + unordered.removeAt(i); + } + } + + // remove the handled projects from the dependency lists + // of the remaining unordered projects + for (int i = 0; i < unordered.count(); ++i) { + for (const FilePath &pro : std::as_const(ordered)) { + FilePaths depList = unordered.at(i).second; + depList.removeAll(pro); + unordered[i].second = depList; + } + } + } + + return ordered; +} + +QList<Project *> ProjectManager::projectOrder(const Project *project) +{ + QList<Project *> result; + + FilePaths pros; + if (project) + pros = d->dependencies(project->projectFilePath()); + else + pros = d->dependenciesOrder(); + + for (const FilePath &proFile : std::as_const(pros)) { + for (Project *pro : projects()) { + if (pro->projectFilePath() == proFile) { + result << pro; + break; + } + } + } + + return result; +} + +Project *ProjectManager::projectForFile(const FilePath &fileName) +{ + if (Project * const project = Utils::findOrDefault(ProjectManager::projects(), + [&fileName](const Project *p) { return p->isKnownFile(fileName); })) { + return project; + } + return Utils::findOrDefault(ProjectManager::projects(), + [&fileName](const Project *p) { + for (const Target * const target : p->targets()) { + for (const BuildConfiguration * const bc : target->buildConfigurations()) { + if (fileName.isChildOf(bc->buildDirectory())) + return false; + } + } + return fileName.isChildOf(p->projectDirectory()); + }); +} + +Project *ProjectManager::projectWithProjectFilePath(const FilePath &filePath) +{ + return Utils::findOrDefault(ProjectManager::projects(), + [&filePath](const Project *p) { return p->projectFilePath() == filePath; }); +} + +void ProjectManager::configureEditor(IEditor *editor, const QString &fileName) +{ + if (auto textEditor = qobject_cast<TextEditor::BaseTextEditor*>(editor)) { + Project *project = projectForFile(Utils::FilePath::fromString(fileName)); + // Global settings are the default. + if (project) + project->editorConfiguration()->configureEditor(textEditor); + } +} + +void ProjectManager::configureEditors(Project *project) +{ + const QList<IDocument *> documents = DocumentModel::openedDocuments(); + for (IDocument *document : documents) { + if (project->isKnownFile(document->filePath())) { + const QList<IEditor *> editors = DocumentModel::editorsForDocument(document); + for (IEditor *editor : editors) { + if (auto textEditor = qobject_cast<TextEditor::BaseTextEditor*>(editor)) { + project->editorConfiguration()->configureEditor(textEditor); + } + } + } + } +} + +void ProjectManager::removeProjects(const QList<Project *> &remove) +{ + for (Project *pro : remove) + emit m_instance->aboutToRemoveProject(pro); + + bool changeStartupProject = false; + + // Delete projects + for (Project *pro : remove) { + pro->saveSettings(); + pro->markAsShuttingDown(); + + // Remove the project node: + d->m_projects.removeOne(pro); + + if (pro == d->m_startupProject) + changeStartupProject = true; + + FolderNavigationWidgetFactory::removeRootDirectory(projectFolderId(pro)); + disconnect(pro, nullptr, m_instance, nullptr); + emit m_instance->projectRemoved(pro); + } + + if (changeStartupProject) + setStartupProject(hasProjects() ? projects().first() : nullptr); + + qDeleteAll(remove); +} + +void ProjectManagerPrivate::restoreDependencies() +{ + QMap<QString, QVariant> depMap = SessionManager::sessionValue("ProjectDependencies").toMap(); + auto i = depMap.constBegin(); + while (i != depMap.constEnd()) { + const QString &key = i.key(); + FilePaths values; + const QStringList valueList = i.value().toStringList(); + for (const QString &value : valueList) + values << FilePath::fromString(value); + m_depMap.insert(FilePath::fromString(key), values); + ++i; + } +} + +void ProjectManagerPrivate::askUserAboutFailedProjects() +{ + FilePaths failedProjects = m_failedProjects; + if (!failedProjects.isEmpty()) { + QString fileList = FilePath::formatFilePaths(failedProjects, "<br>"); + QMessageBox box(QMessageBox::Warning, + Tr::tr("Failed to restore project files"), + Tr::tr("Could not restore the following project files:<br><b>%1</b>"). + arg(fileList)); + auto keepButton = new QPushButton(Tr::tr("Keep projects in Session"), &box); + auto removeButton = new QPushButton(Tr::tr("Remove projects from Session"), &box); + box.addButton(keepButton, QMessageBox::AcceptRole); + box.addButton(removeButton, QMessageBox::DestructiveRole); + + box.exec(); + + if (box.clickedButton() == removeButton) + m_failedProjects.clear(); + } +} + +void ProjectManagerPrivate::restoreStartupProject() +{ + const FilePath startupProject = FilePath::fromSettings( + SessionManager::sessionValue("StartupProject")); + if (!startupProject.isEmpty()) { + for (Project *pro : std::as_const(m_projects)) { + if (pro->projectFilePath() == startupProject) { + m_instance->setStartupProject(pro); + break; + } + } + } + if (!m_startupProject) { + if (!startupProject.isEmpty()) + qWarning() << "Could not find startup project" << startupProject; + if (hasProjects()) + m_instance->setStartupProject(m_projects.first()); + } +} + +/*! + Loads a session, takes a session name (not filename). +*/ +void ProjectManagerPrivate::restoreProjects(const FilePaths &fileList) +{ + // indirectly adds projects to session + // Keep projects that failed to load in the session! + m_failedProjects = fileList; + if (!fileList.isEmpty()) { + ProjectExplorerPlugin::OpenProjectResult result = ProjectExplorerPlugin::openProjects(fileList); + if (!result) + ProjectExplorerPlugin::showOpenProjectError(result); + const QList<Project *> projects = result.projects(); + for (const Project *p : projects) + m_failedProjects.removeAll(p->projectFilePath()); + } +} + +void ProjectManagerPrivate::loadSession() +{ + d->m_failedProjects.clear(); + d->m_depMap.clear(); + d->m_casadeSetActive = false; + + // not ideal that this is in ProjectManager + Id modeId = Id::fromSetting(SessionManager::value(QLatin1String("ActiveMode"))); + if (!modeId.isValid()) + modeId = Id(Core::Constants::MODE_EDIT); + + // find a list of projects to close later + const FilePaths fileList = FileUtils::toFilePathList( + SessionManager::sessionValue("ProjectList").toStringList()); + const QList<Project *> projectsToRemove + = Utils::filtered(ProjectManager::projects(), [&fileList](Project *p) { + return !fileList.contains(p->projectFilePath()); + }); + const QList<Project *> openProjects = ProjectManager::projects(); + const FilePaths projectPathsToLoad + = Utils::filtered(fileList, [&openProjects](const FilePath &path) { + return !Utils::contains(openProjects, + [&path](Project *p) { return p->projectFilePath() == path; }); + }); + + SessionManager::addSessionLoadingSteps(projectPathsToLoad.count()); + + d->restoreProjects(projectPathsToLoad); + d->restoreDependencies(); + d->restoreStartupProject(); + + // only remove old projects now that the startup project is set! + ProjectManager::removeProjects(projectsToRemove); + + // Fall back to Project mode if the startup project is unconfigured and + // use the mode saved in the session otherwise + if (d->m_startupProject && d->m_startupProject->needsConfiguration()) + modeId = Id(Constants::MODE_SESSION); + + ModeManager::activateMode(modeId); + ModeManager::setFocusToCurrentMode(); + + d->m_casadeSetActive = SessionManager::sessionValue("CascadeSetActive", false).toBool(); + + // Starts a event loop, better do that at the very end + QMetaObject::invokeMethod(m_instance, [this] { askUserAboutFailedProjects(); }); +} + +FilePaths ProjectManager::projectsForSessionName(const QString &session) +{ + const FilePath fileName = SessionManager::sessionNameToFileName(session); + PersistentSettingsReader reader; + if (fileName.exists()) { + if (!reader.load(fileName)) { + qWarning() << "Could not restore session" << fileName.toUserOutput(); + return {}; + } + } + return transform(reader.restoreValue(QLatin1String("ProjectList")).toStringList(), + &FilePath::fromUserInput); +} + +#ifdef WITH_TESTS + +void ProjectExplorerPlugin::testSessionSwitch() +{ + QVERIFY(SessionManager::createSession("session1")); + QVERIFY(SessionManager::createSession("session2")); + QTemporaryFile cppFile("main.cpp"); + QVERIFY(cppFile.open()); + cppFile.close(); + QTemporaryFile projectFile1("XXXXXX.pro"); + QTemporaryFile projectFile2("XXXXXX.pro"); + struct SessionSpec { + SessionSpec(const QString &n, QTemporaryFile &f) : name(n), projectFile(f) {} + const QString name; + QTemporaryFile &projectFile; + }; + std::vector<SessionSpec> sessionSpecs{SessionSpec("session1", projectFile1), + SessionSpec("session2", projectFile2)}; + for (const SessionSpec &sessionSpec : sessionSpecs) { + static const QByteArray proFileContents + = "TEMPLATE = app\n" + "CONFIG -= qt\n" + "SOURCES = " + cppFile.fileName().toLocal8Bit(); + QVERIFY(sessionSpec.projectFile.open()); + sessionSpec.projectFile.write(proFileContents); + sessionSpec.projectFile.close(); + QVERIFY(SessionManager::loadSession(sessionSpec.name)); + const OpenProjectResult openResult + = ProjectExplorerPlugin::openProject( + FilePath::fromString(sessionSpec.projectFile.fileName())); + if (openResult.errorMessage().contains("text/plain")) + QSKIP("This test requires the presence of QmakeProjectManager to be fully functional"); + QVERIFY(openResult); + QCOMPARE(openResult.projects().count(), 1); + QVERIFY(openResult.project()); + QCOMPARE(ProjectManager::projects().count(), 1); + } + for (int i = 0; i < 30; ++i) { + QVERIFY(SessionManager::loadSession("session1")); + QCOMPARE(SessionManager::activeSession(), "session1"); + QCOMPARE(ProjectManager::projects().count(), 1); + QVERIFY(SessionManager::loadSession("session2")); + QCOMPARE(SessionManager::activeSession(), "session2"); + QCOMPARE(ProjectManager::projects().count(), 1); + } + QVERIFY(SessionManager::loadSession("session1")); + ProjectManager::closeAllProjects(); + QVERIFY(SessionManager::loadSession("session2")); + ProjectManager::closeAllProjects(); + QVERIFY(SessionManager::deleteSession("session1")); + QVERIFY(SessionManager::deleteSession("session2")); +} + +#endif // WITH_TESTS + +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/projectmanager.h b/src/plugins/projectexplorer/projectmanager.h index 9d54eba572..f49cc96e54 100644 --- a/src/plugins/projectexplorer/projectmanager.h +++ b/src/plugins/projectexplorer/projectmanager.h @@ -6,20 +6,35 @@ #include "projectexplorer_export.h" #include <QString> +#include <QObject> + +namespace Core { class IEditor; } #include <functional> namespace Utils { class FilePath; +using FilePaths = QList<FilePath>; class MimeType; } // Utils namespace ProjectExplorer { +class BuildSystem; class Project; +class RunConfiguration; +class Target; -class PROJECTEXPLORER_EXPORT ProjectManager +class PROJECTEXPLORER_EXPORT ProjectManager : public QObject { + Q_OBJECT + +public: + ProjectManager(); + ~ProjectManager() override; + + static ProjectManager *instance(); + public: static bool canOpenProjectForMimeType(const Utils::MimeType &mt); static Project *openProject(const Utils::MimeType &mt, const Utils::FilePath &fileName); @@ -32,7 +47,59 @@ public: }); } + static void closeAllProjects(); + + static void addProject(Project *project); + static void removeProject(Project *project); + static void removeProjects(const QList<Project *> &remove); + + static void setStartupProject(Project *startupProject); + + static QList<Project *> dependencies(const Project *project); + static bool hasDependency(const Project *project, const Project *depProject); + static bool canAddDependency(const Project *project, const Project *depProject); + static bool addDependency(Project *project, Project *depProject); + static void removeDependency(Project *project, Project *depProject); + + static bool isProjectConfigurationCascading(); + static void setProjectConfigurationCascading(bool b); + + static Project *startupProject(); + static Target *startupTarget(); + static BuildSystem *startupBuildSystem(); + static RunConfiguration *startupRunConfiguration(); + + static const QList<Project *> projects(); + static bool hasProjects(); + static bool hasProject(Project *p); + + // NBS rewrite projectOrder (dependency management) + static QList<Project *> projectOrder(const Project *project = nullptr); + + static Project *projectForFile(const Utils::FilePath &fileName); + static Project *projectWithProjectFilePath(const Utils::FilePath &filePath); + + static Utils::FilePaths projectsForSessionName(const QString &session); + +signals: + void targetAdded(ProjectExplorer::Target *target); + void targetRemoved(ProjectExplorer::Target *target); + void projectAdded(ProjectExplorer::Project *project); + void aboutToRemoveProject(ProjectExplorer::Project *project); + void projectDisplayNameChanged(ProjectExplorer::Project *project); + void projectRemoved(ProjectExplorer::Project *project); + + void startupProjectChanged(ProjectExplorer::Project *project); + + void dependencyChanged(ProjectExplorer::Project *a, ProjectExplorer::Project *b); + + // for tests only + void projectFinishedParsing(ProjectExplorer::Project *project); + private: + static void configureEditor(Core::IEditor *editor, const QString &fileName); + static void configureEditors(Project *project); + static void registerProjectCreator(const QString &mimeType, const std::function<Project *(const Utils::FilePath &)> &); }; diff --git a/src/plugins/projectexplorer/projectmodels.cpp b/src/plugins/projectexplorer/projectmodels.cpp index 0a761f91e6..9805e654b7 100644 --- a/src/plugins/projectexplorer/projectmodels.cpp +++ b/src/plugins/projectexplorer/projectmodels.cpp @@ -8,8 +8,8 @@ #include "projectnodes.h" #include "projectexplorer.h" #include "projectexplorertr.h" +#include "projectmanager.h" #include "projecttree.h" -#include "session.h" #include "target.h" #include <app/app_version.h> @@ -17,6 +17,7 @@ #include <coreplugin/documentmanager.h> #include <coreplugin/icore.h> #include <coreplugin/iversioncontrol.h> +#include <coreplugin/session.h> #include <coreplugin/vcsmanager.h> #include <utils/utilsicons.h> @@ -24,8 +25,8 @@ #include <utils/dropsupport.h> #include <utils/fsengine/fileiconprovider.h> #include <utils/pathchooser.h> +#include <utils/process.h> #include <utils/qtcassert.h> -#include <utils/qtcprocess.h> #include <utils/stringutils.h> #include <utils/theme/theme.h> @@ -174,14 +175,15 @@ FlatModel::FlatModel(QObject *parent) ProjectTree *tree = ProjectTree::instance(); connect(tree, &ProjectTree::subtreeChanged, this, &FlatModel::updateSubtree); - SessionManager *sm = SessionManager::instance(); - connect(sm, &SessionManager::projectRemoved, this, &FlatModel::handleProjectRemoved); - connect(sm, &SessionManager::aboutToLoadSession, this, &FlatModel::loadExpandData); - connect(sm, &SessionManager::aboutToSaveSession, this, &FlatModel::saveExpandData); - connect(sm, &SessionManager::projectAdded, this, &FlatModel::handleProjectAdded); - connect(sm, &SessionManager::startupProjectChanged, this, [this] { emit layoutChanged(); }); + ProjectManager *sm = ProjectManager::instance(); + SessionManager *sb = SessionManager::instance(); + connect(sm, &ProjectManager::projectRemoved, this, &FlatModel::handleProjectRemoved); + connect(sb, &SessionManager::aboutToLoadSession, this, &FlatModel::loadExpandData); + connect(sb, &SessionManager::aboutToSaveSession, this, &FlatModel::saveExpandData); + connect(sm, &ProjectManager::projectAdded, this, &FlatModel::handleProjectAdded); + connect(sm, &ProjectManager::startupProjectChanged, this, [this] { emit layoutChanged(); }); - for (Project *project : SessionManager::projects()) + for (Project *project : ProjectManager::projects()) handleProjectAdded(project); } @@ -234,7 +236,7 @@ QVariant FlatModel::data(const QModelIndex &index, int role) const } case Qt::FontRole: { QFont font; - if (project == SessionManager::startupProject()) + if (project == ProjectManager::startupProject()) font.setBold(true); return font; } @@ -407,7 +409,7 @@ void FlatModel::updateSubtree(FolderNode *node) void FlatModel::rebuildModel() { - const QList<Project *> projects = SessionManager::projects(); + const QList<Project *> projects = ProjectManager::projects(); for (Project *project : projects) addOrRebuildProjectModel(project); } diff --git a/src/plugins/projectexplorer/projectnodes.cpp b/src/plugins/projectexplorer/projectnodes.cpp index 10699b7d90..e5c8a9a3c6 100644 --- a/src/plugins/projectexplorer/projectnodes.cpp +++ b/src/plugins/projectexplorer/projectnodes.cpp @@ -326,14 +326,13 @@ FilePath Node::pathOrDirectory(bool dir) const FilePath location; // Virtual Folder case // If there are files directly below or no subfolders, take the folder path - if (!folder->fileNodes().isEmpty() || folder->folderNodes().isEmpty()) { + auto Any = [](auto) { return true; }; + if (folder->findChildFileNode(Any) || !folder->findChildFolderNode(Any)) { location = m_filePath; } else { // Otherwise we figure out a commonPath from the subfolders FilePaths list; - const QList<FolderNode *> folders = folder->folderNodes(); - for (FolderNode *f : folders) - list << f->filePath(); + folder->forEachFolderNode([&](FolderNode *f) { list << f->filePath(); }); location = FileUtils::commonPath(list); } @@ -547,6 +546,22 @@ void FolderNode::forEachProjectNode(const std::function<void(const ProjectNode * } } +void FolderNode::forEachFileNode(const std::function<void (FileNode *)> &fileTask) const +{ + for (const std::unique_ptr<Node> &n : m_nodes) { + if (FileNode *fn = n->asFileNode()) + fileTask(fn); + } +} + +void FolderNode::forEachFolderNode(const std::function<void (FolderNode *)> &folderTask) const +{ + for (const std::unique_ptr<Node> &n : m_nodes) { + if (FolderNode *fn = n->asFolderNode()) + folderTask(fn); + } +} + ProjectNode *FolderNode::findProjectNode(const std::function<bool(const ProjectNode *)> &predicate) { if (ProjectNode *projectNode = asProjectNode()) { @@ -563,19 +578,29 @@ ProjectNode *FolderNode::findProjectNode(const std::function<bool(const ProjectN return nullptr; } -const QList<Node *> FolderNode::nodes() const +FolderNode *FolderNode::findChildFolderNode(const std::function<bool(FolderNode *)> &predicate) const { - return Utils::toRawPointer<QList>(m_nodes); + for (const std::unique_ptr<Node> &n : m_nodes) { + if (FolderNode *fn = n->asFolderNode()) + if (predicate(fn)) + return fn; + } + return nullptr; } -QList<FileNode *> FolderNode::fileNodes() const +FileNode *FolderNode::findChildFileNode(const std::function<bool(FileNode *)> &predicate) const { - QList<FileNode *> result; for (const std::unique_ptr<Node> &n : m_nodes) { if (FileNode *fn = n->asFileNode()) - result.append(fn); + if (predicate(fn)) + return fn; } - return result; + return nullptr; +} + +const QList<Node *> FolderNode::nodes() const +{ + return Utils::toRawPointer<QList>(m_nodes); } FileNode *FolderNode::fileNode(const Utils::FilePath &file) const @@ -587,16 +612,6 @@ FileNode *FolderNode::fileNode(const Utils::FilePath &file) const })); } -QList<FolderNode *> FolderNode::folderNodes() const -{ - QList<FolderNode *> result; - for (const std::unique_ptr<Node> &n : m_nodes) { - if (FolderNode *fn = n->asFolderNode()) - result.append(fn); - } - return result; -} - FolderNode *FolderNode::folderNode(const Utils::FilePath &directory) const { Node *node = Utils::findOrDefault(m_nodes, [directory](const std::unique_ptr<Node> &n) { @@ -669,8 +684,7 @@ void FolderNode::compress() compress(); } else { - for (FolderNode *fn : folderNodes()) - fn->compress(); + forEachFolderNode([&](FolderNode *fn) { fn->compress(); }); } } diff --git a/src/plugins/projectexplorer/projectnodes.h b/src/plugins/projectexplorer/projectnodes.h index 35927c02c8..0e0068ba7b 100644 --- a/src/plugins/projectexplorer/projectnodes.h +++ b/src/plugins/projectexplorer/projectnodes.h @@ -32,6 +32,8 @@ enum class FileType : quint16 { Resource, QML, Project, + App, + Lib, FileTypeSize }; @@ -228,11 +230,13 @@ public: const std::function<bool(const FolderNode *)> &folderFilterTask = {}) const; void forEachGenericNode(const std::function<void(Node *)> &genericTask) const; void forEachProjectNode(const std::function<void(const ProjectNode *)> &genericTask) const; - ProjectNode *findProjectNode(const std::function<bool(const ProjectNode *)> &predicate); + void forEachFileNode(const std::function<void(FileNode *)> &fileTask) const; + void forEachFolderNode(const std::function<void(FolderNode *)> &folderTask) const; + ProjectNode *findProjectNode(const std::function<bool(const ProjectNode *)> &predicate); // recursive + FolderNode *findChildFolderNode(const std::function<bool (FolderNode *)> &predicate) const; // non-recursive + FileNode *findChildFileNode(const std::function<bool (FileNode *)> &predicate) const; // non-recursive const QList<Node *> nodes() const; - QList<FileNode *> fileNodes() const; FileNode *fileNode(const Utils::FilePath &file) const; - QList<FolderNode *> folderNodes() const; FolderNode *folderNode(const Utils::FilePath &directory) const; using FolderNodeFactory = std::function<std::unique_ptr<FolderNode>(const Utils::FilePath &)>; diff --git a/src/plugins/projectexplorer/projectnodeshelper.h b/src/plugins/projectexplorer/projectnodeshelper.h index 727497c6bf..bcb5454fd8 100644 --- a/src/plugins/projectexplorer/projectnodeshelper.h +++ b/src/plugins/projectexplorer/projectnodeshelper.h @@ -11,18 +11,19 @@ #include <utils/algorithm.h> #include <utils/filepath.h> +#include <QPromise> + namespace ProjectExplorer { template<typename Result> -QList<FileNode *> scanForFiles(QFutureInterface<Result> &future, - const Utils::FilePath &directory, +QList<FileNode *> scanForFiles(QPromise<Result> &promise, const Utils::FilePath &directory, const std::function<FileNode *(const Utils::FilePath &)> factory); namespace Internal { template<typename Result> QList<FileNode *> scanForFilesRecursively( - QFutureInterface<Result> &future, + QPromise<Result> &promise, double progressStart, double progressRange, const Utils::FilePath &directory, @@ -46,7 +47,7 @@ QList<FileNode *> scanForFilesRecursively( const double progressIncrement = progressRange / static_cast<double>(entries.count()); int lastIntProgress = 0; for (const QFileInfo &entry : entries) { - if (future.isCanceled()) + if (promise.isCanceled()) return result; const Utils::FilePath entryName = Utils::FilePath::fromString(entry.absoluteFilePath()); @@ -54,7 +55,7 @@ QList<FileNode *> scanForFilesRecursively( return vc->isVcsFileOrDirectory(entryName); })) { if (entry.isDir()) - result.append(scanForFilesRecursively(future, + result.append(scanForFilesRecursively(promise, progress, progressIncrement, entryName, @@ -66,26 +67,25 @@ QList<FileNode *> scanForFilesRecursively( } progress += progressIncrement; const int intProgress = std::min(static_cast<int>(progressStart + progress), - future.progressMaximum()); + promise.future().progressMaximum()); if (lastIntProgress < intProgress) { - future.setProgressValue(intProgress); + promise.setProgressValue(intProgress); lastIntProgress = intProgress; } } - future.setProgressValue( - std::min(static_cast<int>(progressStart + progressRange), future.progressMaximum())); + promise.setProgressValue(std::min(static_cast<int>(progressStart + progressRange), + promise.future().progressMaximum())); return result; } } // namespace Internal template<typename Result> -QList<FileNode *> scanForFiles(QFutureInterface<Result> &future, - const Utils::FilePath &directory, +QList<FileNode *> scanForFiles(QPromise<Result> &promise, const Utils::FilePath &directory, const std::function<FileNode *(const Utils::FilePath &)> factory) { QSet<QString> visited; - future.setProgressRange(0, 1000000); - return Internal::scanForFilesRecursively(future, + promise.setProgressRange(0, 1000000); + return Internal::scanForFilesRecursively(promise, 0.0, 1000000.0, directory, diff --git a/src/plugins/projectexplorer/projecttree.cpp b/src/plugins/projectexplorer/projecttree.cpp index 18b03d3ae0..5f81f473fe 100644 --- a/src/plugins/projectexplorer/projecttree.cpp +++ b/src/plugins/projectexplorer/projecttree.cpp @@ -6,9 +6,9 @@ #include "project.h" #include "projectexplorerconstants.h" #include "projectexplorertr.h" +#include "projectmanager.h" #include "projectnodes.h" #include "projecttreewidget.h" -#include "session.h" #include "target.h" #include <coreplugin/actionmanager/actioncontainer.h> @@ -53,11 +53,11 @@ ProjectTree::ProjectTree(QObject *parent) : QObject(parent) connect(qApp, &QApplication::focusChanged, this, &ProjectTree::update); - connect(SessionManager::instance(), &SessionManager::projectAdded, + connect(ProjectManager::instance(), &ProjectManager::projectAdded, this, &ProjectTree::sessionAndTreeChanged); - connect(SessionManager::instance(), &SessionManager::projectRemoved, + connect(ProjectManager::instance(), &ProjectManager::projectRemoved, this, &ProjectTree::sessionAndTreeChanged); - connect(SessionManager::instance(), &SessionManager::startupProjectChanged, + connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged, this, &ProjectTree::sessionChanged); connect(this, &ProjectTree::subtreeChanged, this, &ProjectTree::treeChanged); } @@ -170,7 +170,7 @@ void ProjectTree::updateFromNode(Node *node) if (node) project = projectForNode(node); else - project = SessionManager::startupProject(); + project = ProjectManager::startupProject(); setCurrent(node, project); for (ProjectTreeWidget *widget : std::as_const(m_projectTreeWidgets)) @@ -224,7 +224,7 @@ void ProjectTree::sessionChanged() { if (m_currentProject) { Core::DocumentManager::setDefaultLocationForNewFiles(m_currentProject->projectDirectory()); - } else if (Project *project = SessionManager::startupProject()) { + } else if (Project *project = ProjectManager::startupProject()) { Core::DocumentManager::setDefaultLocationForNewFiles(project->projectDirectory()); updateFromNode(nullptr); // Make startup project current if there is no other current } else { @@ -300,7 +300,7 @@ void ProjectTree::updateFileWarning(Core::IDocument *document, const QString &te if (!infoBar->canInfoBeAdded(infoId)) return; const FilePath filePath = document->filePath(); - const QList<Project *> projects = SessionManager::projects(); + const QList<Project *> projects = ProjectManager::projects(); if (projects.isEmpty()) return; for (Project *project : projects) { @@ -394,7 +394,7 @@ void ProjectTree::applyTreeManager(FolderNode *folder, ConstructionPhase phase) bool ProjectTree::hasNode(const Node *node) { - return Utils::contains(SessionManager::projects(), [node](const Project *p) { + return Utils::contains(ProjectManager::projects(), [node](const Project *p) { if (!p) return false; if (p->containerNode() == node) @@ -409,7 +409,7 @@ bool ProjectTree::hasNode(const Node *node) void ProjectTree::forEachNode(const std::function<void(Node *)> &task) { - const QList<Project *> projects = SessionManager::projects(); + const QList<Project *> projects = ProjectManager::projects(); for (Project *project : projects) { if (ProjectNode *projectNode = project->rootProjectNode()) { task(projectNode); @@ -430,7 +430,7 @@ Project *ProjectTree::projectForNode(const Node *node) while (folder && folder->parentFolderNode()) folder = folder->parentFolderNode(); - return Utils::findOrDefault(SessionManager::projects(), [folder](const Project *pro) { + return Utils::findOrDefault(ProjectManager::projects(), [folder](const Project *pro) { return pro->containerNode() == folder; }); } @@ -438,7 +438,7 @@ Project *ProjectTree::projectForNode(const Node *node) Node *ProjectTree::nodeForFile(const FilePath &fileName) { Node *node = nullptr; - for (const Project *project : SessionManager::projects()) { + for (const Project *project : ProjectManager::projects()) { project->nodeForFilePath(fileName, [&](const Node *n) { if (!node || (!node->asFileNode() && n->asFileNode())) node = const_cast<Node *>(n); diff --git a/src/plugins/projectexplorer/projecttreewidget.cpp b/src/plugins/projectexplorer/projecttreewidget.cpp index 65f38626fe..a3d608ebd8 100644 --- a/src/plugins/projectexplorer/projecttreewidget.cpp +++ b/src/plugins/projectexplorer/projecttreewidget.cpp @@ -6,10 +6,10 @@ #include "project.h" #include "projectexplorerconstants.h" #include "projectexplorertr.h" +#include "projectmanager.h" #include "projectmodels.h" #include "projectnodes.h" #include "projecttree.h" -#include "session.h" #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/command.h> @@ -24,6 +24,7 @@ #include <utils/navigationtreeview.h> #include <utils/progressindicator.h> #include <utils/qtcassert.h> +#include <utils/stylehelper.h> #include <utils/tooltip/tooltip.h> #include <utils/utilsicons.h> @@ -343,7 +344,7 @@ Node *ProjectTreeWidget::nodeForFile(const FilePath &fileName) int bestNodeExpandCount = INT_MAX; // FIXME: Looks like this could be done with less cycles. - for (Project *project : SessionManager::projects()) { + for (Project *project : ProjectManager::projects()) { if (ProjectNode *projectNode = project->rootProjectNode()) { projectNode->forEachGenericNode([&](Node *node) { if (node->filePath() == fileName) { @@ -420,7 +421,7 @@ QList<QToolButton *> ProjectTreeWidget::createToolButtons() filter->setIcon(Icons::FILTER.icon()); filter->setToolTip(Tr::tr("Filter Tree")); filter->setPopupMode(QToolButton::InstantPopup); - filter->setProperty("noArrow", true); + filter->setProperty(StyleHelper::C_NO_ARROW, true); auto filterMenu = new QMenu(filter); filterMenu->addAction(m_filterProjectsAction); diff --git a/src/plugins/projectexplorer/projectwelcomepage.cpp b/src/plugins/projectexplorer/projectwelcomepage.cpp index 156212a504..4eef637077 100644 --- a/src/plugins/projectexplorer/projectwelcomepage.cpp +++ b/src/plugins/projectexplorer/projectwelcomepage.cpp @@ -3,10 +3,9 @@ #include "projectwelcomepage.h" -#include "session.h" -#include "sessionmodel.h" #include "projectexplorer.h" #include "projectexplorertr.h" +#include "projectmanager.h" #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/command.h> @@ -15,6 +14,8 @@ #include <coreplugin/icontext.h> #include <coreplugin/icore.h> #include <coreplugin/iwizardfactory.h> +#include <coreplugin/session.h> +#include <coreplugin/sessionmodel.h> #include <coreplugin/welcomepagehelper.h> #include <utils/algorithm.h> @@ -324,7 +325,7 @@ public: if (expanded) { painter->setPen(textColor); painter->setFont(sizedFont(12, option.widget)); - const FilePaths projects = SessionManager::projectsForSessionName(sessionName); + const FilePaths projects = ProjectManager::projectsForSessionName(sessionName); int yy = firstBase + SESSION_LINE_HEIGHT - 3; QFontMetrics fm(option.widget->font()); for (const FilePath &projectPath : projects) { @@ -378,7 +379,7 @@ public: int h = SESSION_LINE_HEIGHT; QString sessionName = idx.data(Qt::DisplayRole).toString(); if (m_expandedSessions.contains(sessionName)) { - const FilePaths projects = SessionManager::projectsForSessionName(sessionName); + const FilePaths projects = ProjectManager::projectsForSessionName(sessionName); h += projects.size() * 40 + LINK_HEIGHT - 6; } return QSize(380, h + ItemGap); @@ -579,7 +580,7 @@ public: auto manageSessionsButton = new WelcomePageButton(this); manageSessionsButton->setText(Tr::tr("Manage...")); manageSessionsButton->setWithAccentColor(true); - manageSessionsButton->setOnClicked([] { ProjectExplorerPlugin::showSessionManager(); }); + manageSessionsButton->setOnClicked([] { SessionManager::showSessionManager(); }); auto sessionsLabel = new QLabel(this); sessionsLabel->setFont(brandFont()); diff --git a/src/plugins/projectexplorer/projectwelcomepage.h b/src/plugins/projectexplorer/projectwelcomepage.h index a9c8145963..7fb2a828db 100644 --- a/src/plugins/projectexplorer/projectwelcomepage.h +++ b/src/plugins/projectexplorer/projectwelcomepage.h @@ -12,10 +12,11 @@ #include <QAbstractListModel> #include <QCoreApplication> +namespace Core { class SessionModel; } + namespace ProjectExplorer { namespace Internal { -class SessionModel; class SessionsPage; class ProjectModel : public QAbstractListModel @@ -56,7 +57,6 @@ public slots: signals: void requestProject(const Utils::FilePath &project); - void manageSessions(); private: void openSessionAt(int index); @@ -64,7 +64,7 @@ private: void createActions(); friend class SessionsPage; - SessionModel *m_sessionModel = nullptr; + Core::SessionModel *m_sessionModel = nullptr; ProjectModel *m_projectModel = nullptr; }; diff --git a/src/plugins/projectexplorer/projectwindow.cpp b/src/plugins/projectexplorer/projectwindow.cpp index 8fae44b297..a69ee4c855 100644 --- a/src/plugins/projectexplorer/projectwindow.cpp +++ b/src/plugins/projectexplorer/projectwindow.cpp @@ -12,9 +12,9 @@ #include "projectexplorerconstants.h" #include "projectexplorertr.h" #include "projectimporter.h" +#include "projectmanager.h" #include "projectpanelfactory.h" #include "projectsettingswidget.h" -#include "session.h" #include "target.h" #include "targetsettingspanel.h" @@ -352,7 +352,7 @@ public: case Qt::FontRole: { QFont font; - font.setBold(m_project == SessionManager::startupProject()); + font.setBold(m_project == ProjectManager::startupProject()); return font; } @@ -392,7 +392,7 @@ public: if (role == ItemActivatedDirectlyRole) { // Someone selected the project using the combobox or similar. - SessionManager::setStartupProject(m_project); + ProjectManager::setStartupProject(m_project); m_currentChildIndex = 0; // Use some Target page by defaults m_targetsItem->setData(column, dat, ItemActivatedFromAboveRole); // And propagate downwards. announceChange(); @@ -546,18 +546,18 @@ public: m_projectSelection->showPopup(); }); - SessionManager *sessionManager = SessionManager::instance(); - connect(sessionManager, &SessionManager::projectAdded, + ProjectManager *sessionManager = ProjectManager::instance(); + connect(sessionManager, &ProjectManager::projectAdded, this, &ProjectWindowPrivate::registerProject); - connect(sessionManager, &SessionManager::aboutToRemoveProject, + connect(sessionManager, &ProjectManager::aboutToRemoveProject, this, &ProjectWindowPrivate::deregisterProject); - connect(sessionManager, &SessionManager::startupProjectChanged, + connect(sessionManager, &ProjectManager::startupProjectChanged, this, &ProjectWindowPrivate::startupProjectChanged); m_importBuild = new QPushButton(Tr::tr("Import Existing Build...")); connect(m_importBuild, &QPushButton::clicked, this, &ProjectWindowPrivate::handleImportBuild); - connect(sessionManager, &SessionManager::startupProjectChanged, this, [this](Project *project) { + connect(sessionManager, &ProjectManager::startupProjectChanged, this, [this](Project *project) { m_importBuild->setEnabled(project && project->projectImporter()); }); @@ -572,9 +572,6 @@ public: selectorView->setObjectName("ProjectSelector"); // Needed for dock widget state saving selectorView->setWindowTitle(Tr::tr("Project Selector")); selectorView->setAutoFillBackground(true); - selectorView->setContextMenuPolicy(Qt::CustomContextMenu); - connect(selectorView, &QWidget::customContextMenuRequested, - this, &ProjectWindowPrivate::openContextMenu); auto activeLabel = new QLabel(Tr::tr("Active Project")); QFont font = activeLabel->font(); @@ -677,7 +674,7 @@ public: void projectSelected(int index) { Project *project = m_comboBoxModel.rootItem()->childAt(index)->m_projectItem->project(); - SessionManager::setStartupProject(project); + ProjectManager::setStartupProject(project); } ComboBoxItem *itemForProject(Project *project) const @@ -746,8 +743,7 @@ public: void handleManageKits() { if (ProjectItem *projectItem = m_projectsModel.rootItem()->childAt(0)) { - if (auto kitPage = KitOptionsPage::instance()) - kitPage->showKit(KitManager::kit(Id::fromSetting(projectItem->data(0, KitIdRole)))); + KitOptionsPage::showKit(KitManager::kit(Id::fromSetting(projectItem->data(0, KitIdRole)))); } ICore::showOptionsDialog(Constants::KITS_SETTINGS_PAGE_ID); } @@ -780,8 +776,8 @@ public: } } if (lastTarget && lastBc) { - SessionManager::setActiveBuildConfiguration(lastTarget, lastBc, SetActive::Cascade); - SessionManager::setActiveTarget(project, lastTarget, SetActive::Cascade); + lastTarget->setActiveBuildConfiguration(lastBc, SetActive::Cascade); + project->setActiveTarget(lastTarget, SetActive::Cascade); } } diff --git a/src/plugins/projectexplorer/projectwizardpage.cpp b/src/plugins/projectexplorer/projectwizardpage.cpp index 34dc0b9aaa..83fd4d2cba 100644 --- a/src/plugins/projectexplorer/projectwizardpage.cpp +++ b/src/plugins/projectexplorer/projectwizardpage.cpp @@ -5,8 +5,8 @@ #include "project.h" #include "projectexplorertr.h" +#include "projectmanager.h" #include "projectmodels.h" -#include "session.h" #include <coreplugin/icore.h> #include <coreplugin/iversioncontrol.h> @@ -245,12 +245,10 @@ static AddNewTree *buildAddFilesTree(FolderNode *root, const FilePaths &files, Node *contextNode, BestNodeSelector *selector) { QList<AddNewTree *> children; - const QList<FolderNode *> folderNodes = root->folderNodes(); - for (FolderNode *fn : folderNodes) { - AddNewTree *child = buildAddFilesTree(fn, files, contextNode, selector); - if (child) + root->forEachFolderNode([&](FolderNode *fn) { + if (AddNewTree *child = buildAddFilesTree(fn, files, contextNode, selector)) children.append(child); - } + }); if (root->supportsAction(AddNewFile, root) && !root->supportsAction(InheritedFromParent, root)) { FolderNode::AddNewInformation info = root->addNewInformation(files, contextNode); @@ -290,7 +288,7 @@ ProjectWizardPage::ProjectWizardPage(QWidget *parent) scrollArea->setWidgetResizable(true); scrollArea->setWidget(m_filesLabel); - using namespace Utils::Layouting; + using namespace Layouting; Column { Form { m_projectLabel, m_projectComboBox, br, @@ -463,7 +461,7 @@ void ProjectWizardPage::initializeProjectTree(Node *context, const FilePaths &pa TreeItem *root = m_model.rootItem(); root->removeChildren(); - for (Project *project : SessionManager::projects()) { + for (Project *project : ProjectManager::projects()) { if (ProjectNode *pn = project->rootProjectNode()) { if (kind == IWizardFactory::ProjectWizard) { if (AddNewTree *child = buildAddProjectTree(pn, paths.first(), context, &selector)) diff --git a/src/plugins/projectexplorer/rawprojectpart.cpp b/src/plugins/projectexplorer/rawprojectpart.cpp index 6abbedde93..31147c3803 100644 --- a/src/plugins/projectexplorer/rawprojectpart.cpp +++ b/src/plugins/projectexplorer/rawprojectpart.cpp @@ -11,13 +11,14 @@ #include "target.h" #include <ios/iosconstants.h> + #include <utils/algorithm.h> namespace ProjectExplorer { RawProjectPartFlags::RawProjectPartFlags(const ToolChain *toolChain, const QStringList &commandLineFlags, - const QString &includeFileBaseDir) + const Utils::FilePath &includeFileBaseDir) { // Keep the following cheap/non-blocking for the ui thread. Expensive // operations are encapsulated in ToolChainInfo as "runners". @@ -25,7 +26,8 @@ RawProjectPartFlags::RawProjectPartFlags(const ToolChain *toolChain, if (toolChain) { warningFlags = toolChain->warningFlags(commandLineFlags); languageExtensions = toolChain->languageExtensions(commandLineFlags); - includedFiles = toolChain->includedFiles(commandLineFlags, includeFileBaseDir); + includedFiles = Utils::transform(toolChain->includedFiles(commandLineFlags, includeFileBaseDir), + &Utils::FilePath::toFSPathString); } } diff --git a/src/plugins/projectexplorer/rawprojectpart.h b/src/plugins/projectexplorer/rawprojectpart.h index 580c83d439..ca210ed43e 100644 --- a/src/plugins/projectexplorer/rawprojectpart.h +++ b/src/plugins/projectexplorer/rawprojectpart.h @@ -37,7 +37,7 @@ class PROJECTEXPLORER_EXPORT RawProjectPartFlags public: RawProjectPartFlags() = default; RawProjectPartFlags(const ToolChain *toolChain, const QStringList &commandLineFlags, - const QString &includeFileBaseDir); + const Utils::FilePath &includeFileBaseDir); public: QStringList commandLineFlags; diff --git a/src/plugins/projectexplorer/runconfiguration.cpp b/src/plugins/projectexplorer/runconfiguration.cpp index b5f3f8e703..29b59ad898 100644 --- a/src/plugins/projectexplorer/runconfiguration.cpp +++ b/src/plugins/projectexplorer/runconfiguration.cpp @@ -12,10 +12,10 @@ #include "projectexplorer.h" #include "projectexplorerconstants.h" #include "projectexplorertr.h" +#include "projectmanager.h" #include "projectnodes.h" #include "runconfigurationaspects.h" #include "runcontrol.h" -#include "session.h" #include "target.h" #include <coreplugin/icontext.h> @@ -33,7 +33,6 @@ #include <utils/utilsicons.h> #include <utils/variablechooser.h> -#include <QDir> #include <QHash> #include <QPushButton> #include <QTimer> @@ -217,13 +216,15 @@ bool RunConfiguration::isEnabled() const QWidget *RunConfiguration::createConfigurationWidget() { - Layouting::Form builder; - for (BaseAspect *aspect : std::as_const(m_aspects)) { - if (aspect->isVisible()) - aspect->addToLayout(builder.finishRow()); + Layouting::Form form; + for (BaseAspect *aspect : std::as_const(*this)) { + if (aspect->isVisible()) { + form.addItem(aspect); + form.addItem(Layouting::br); + } } - - auto widget = builder.emerge(Layouting::WithoutMargins); + form.addItem(Layouting::noMargin); + auto widget = form.emerge(); VariableChooser::addSupportForChildWidgets(widget, &m_expander); @@ -246,7 +247,7 @@ void RunConfiguration::addAspectFactory(const AspectFactory &aspectFactory) QMap<Utils::Id, QVariantMap> RunConfiguration::settingsData() const { QMap<Utils::Id, QVariantMap> data; - for (BaseAspect *aspect : m_aspects) + for (BaseAspect *aspect : *this) aspect->toActiveMap(data[aspect->id()]); return data; } @@ -254,7 +255,7 @@ QMap<Utils::Id, QVariantMap> RunConfiguration::settingsData() const AspectContainerData RunConfiguration::aspectData() const { AspectContainerData data; - for (BaseAspect *aspect : m_aspects) + for (BaseAspect *aspect : *this) data.append(aspect->extractData()); return data; } @@ -299,6 +300,13 @@ CommandLine RunConfiguration::commandLine() const return m_commandLineGetter(); } +bool RunConfiguration::isPrintEnvironmentEnabled() const +{ + if (const auto envAspect = aspect<EnvironmentAspect>()) + return envAspect->isPrintOnRunEnabled(); + return false; +} + void RunConfiguration::setRunnableModifier(const RunnableModifier &runnableModifier) { m_runnableModifier = runnableModifier; @@ -313,7 +321,7 @@ void RunConfiguration::update() const bool isActive = target()->isActive() && target()->activeRunConfiguration() == this; - if (isActive && project() == SessionManager::startupProject()) + if (isActive && project() == ProjectManager::startupProject()) ProjectExplorerPlugin::updateRunActions(); } @@ -392,7 +400,7 @@ Runnable RunConfiguration::runnable() const Runnable r; r.command = commandLine(); if (auto workingDirectoryAspect = aspect<WorkingDirectoryAspect>()) - r.workingDirectory = workingDirectoryAspect->workingDirectory().onDevice(r.command.executable()); + r.workingDirectory = r.command.executable().withNewPath(workingDirectoryAspect->workingDirectory().path()); if (auto environmentAspect = aspect<EnvironmentAspect>()) r.environment = environmentAspect->environment(); if (m_runnableModifier) @@ -558,7 +566,7 @@ RunConfiguration *RunConfigurationFactory::create(Target *target) const // Add the universal aspects. for (const RunConfiguration::AspectFactory &factory : theAspectFactories) - rc->m_aspects.registerAspect(factory(target)); + rc->registerAspect(factory(target)); return rc; } diff --git a/src/plugins/projectexplorer/runconfiguration.h b/src/plugins/projectexplorer/runconfiguration.h index 32f61b5a30..5970414156 100644 --- a/src/plugins/projectexplorer/runconfiguration.h +++ b/src/plugins/projectexplorer/runconfiguration.h @@ -116,6 +116,7 @@ public: using CommandLineGetter = std::function<Utils::CommandLine()>; void setCommandLineGetter(const CommandLineGetter &cmdGetter); Utils::CommandLine commandLine() const; + bool isPrintEnvironmentEnabled() const; using RunnableModifier = std::function<void(Runnable &)>; void setRunnableModifier(const RunnableModifier &extraModifier); @@ -137,6 +138,7 @@ public: return nullptr; } + using ProjectConfiguration::registerAspect; using AspectFactory = std::function<Utils::BaseAspect *(Target *)>; template <class T> static void registerAspect() { diff --git a/src/plugins/projectexplorer/runconfigurationaspects.cpp b/src/plugins/projectexplorer/runconfigurationaspects.cpp index 9ba24c0467..ce28582d55 100644 --- a/src/plugins/projectexplorer/runconfigurationaspects.cpp +++ b/src/plugins/projectexplorer/runconfigurationaspects.cpp @@ -19,8 +19,8 @@ #include <utils/fancylineedit.h> #include <utils/layoutbuilder.h> #include <utils/pathchooser.h> +#include <utils/process.h> #include <utils/qtcassert.h> -#include <utils/qtcprocess.h> #include <utils/utilsicons.h> #include <QCheckBox> @@ -33,7 +33,7 @@ #include <QPushButton> using namespace Utils; -using namespace Utils::Layouting; +using namespace Layouting; namespace ProjectExplorer { @@ -62,12 +62,13 @@ TerminalAspect::TerminalAspect() /*! \reimp */ -void TerminalAspect::addToLayout(LayoutBuilder &builder) +void TerminalAspect::addToLayout(LayoutItem &parent) { QTC_CHECK(!m_checkBox); - m_checkBox = new QCheckBox(Tr::tr("Run in terminal")); + m_checkBox = createSubWidget<QCheckBox>(Tr::tr("Run in terminal")); m_checkBox->setChecked(m_useTerminal); - builder.addItems({{}, m_checkBox.data()}); + m_checkBox->setEnabled(isEnabled()); + parent.addItems({{}, m_checkBox.data()}); connect(m_checkBox.data(), &QAbstractButton::clicked, this, [this] { m_userSet = true; m_useTerminal = m_checkBox->isChecked(); @@ -123,7 +124,7 @@ void TerminalAspect::calculateUseTerminal() */ bool TerminalAspect::useTerminal() const { - return m_useTerminal; + return m_useTerminal && isEnabled(); } /*! @@ -163,7 +164,7 @@ WorkingDirectoryAspect::WorkingDirectoryAspect(const MacroExpander *expander, /*! \reimp */ -void WorkingDirectoryAspect::addToLayout(LayoutBuilder &builder) +void WorkingDirectoryAspect::addToLayout(LayoutItem &builder) { QTC_CHECK(!m_chooser); m_chooser = new PathChooser; @@ -435,7 +436,7 @@ QWidget *ArgumentsAspect::setupChooser() /*! \reimp */ -void ArgumentsAspect::addToLayout(LayoutBuilder &builder) +void ArgumentsAspect::addToLayout(LayoutItem &builder) { QTC_CHECK(!m_chooser && !m_multiLineChooser && !m_multiLineButton); @@ -501,7 +502,7 @@ ExecutableAspect::ExecutableAspect(Target *target, ExecutionDeviceSelector selec setId("ExecutableAspect"); addDataExtractor(this, &ExecutableAspect::executable, &Data::executable); - m_executable.setPlaceHolderText(Tr::tr("<unknown>")); + m_executable.setPlaceHolderText(Tr::tr("path to the executable cannot be empty")); m_executable.setLabelText(Tr::tr("Executable:")); m_executable.setDisplayStyle(StringAspect::LabelDisplay); @@ -570,19 +571,12 @@ void ExecutableAspect::setExpectedKind(const PathChooser::Kind 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. - - \sa Utils::StringAspect::setEnvironmentChange() */ -void ExecutableAspect::setEnvironmentChange(const EnvironmentChange &change) -{ - m_executable.setEnvironmentChange(change); - if (m_alternativeExecutable) - m_alternativeExecutable->setEnvironmentChange(change); -} - void ExecutableAspect::setEnvironment(const Environment &env) { - setEnvironmentChange(EnvironmentChange::fromDictionary(env.toDictionary())); + m_executable.setEnvironment(env); + if (m_alternativeExecutable) + m_alternativeExecutable->setEnvironment(env); } /*! @@ -631,7 +625,7 @@ FilePath ExecutableAspect::executable() const : m_executable.filePath(); if (const IDevice::ConstPtr dev = executionDevice(m_target, m_selector)) - exe = exe.onDevice(dev->rootPath()); + exe = dev->rootPath().withNewMappedPath(exe); return exe; } @@ -639,11 +633,11 @@ FilePath ExecutableAspect::executable() const /*! \reimp */ -void ExecutableAspect::addToLayout(LayoutBuilder &builder) +void ExecutableAspect::addToLayout(LayoutItem &builder) { - m_executable.addToLayout(builder); + builder.addItem(m_executable); if (m_alternativeExecutable) - m_alternativeExecutable->addToLayout(builder.finishRow()); + builder.addItems({br, m_alternativeExecutable}); } /*! @@ -837,7 +831,7 @@ void InterpreterAspect::toMap(QVariantMap &map) const saveToMap(map, m_currentId, QString(), settingsKey()); } -void InterpreterAspect::addToLayout(LayoutBuilder &builder) +void InterpreterAspect::addToLayout(LayoutItem &builder) { if (QTC_GUARD(m_comboBox.isNull())) m_comboBox = new QComboBox; diff --git a/src/plugins/projectexplorer/runconfigurationaspects.h b/src/plugins/projectexplorer/runconfigurationaspects.h index 9e948bc6ec..9fc7364772 100644 --- a/src/plugins/projectexplorer/runconfigurationaspects.h +++ b/src/plugins/projectexplorer/runconfigurationaspects.h @@ -29,7 +29,7 @@ class PROJECTEXPLORER_EXPORT TerminalAspect : public Utils::BaseAspect public: TerminalAspect(); - void addToLayout(Utils::Layouting::LayoutBuilder &builder) override; + void addToLayout(Layouting::LayoutItem &parent) override; bool useTerminal() const; void setUseTerminalHint(bool useTerminal); @@ -62,7 +62,7 @@ public: explicit WorkingDirectoryAspect(const Utils::MacroExpander *expander, EnvironmentAspect *envAspect); - void addToLayout(Utils::Layouting::LayoutBuilder &builder) override; + void addToLayout(Layouting::LayoutItem &parent) override; Utils::FilePath workingDirectory() const; Utils::FilePath defaultWorkingDirectory() const; @@ -91,7 +91,7 @@ class PROJECTEXPLORER_EXPORT ArgumentsAspect : public Utils::BaseAspect public: explicit ArgumentsAspect(const Utils::MacroExpander *macroExpander); - void addToLayout(Utils::Layouting::LayoutBuilder &builder) override; + void addToLayout(Layouting::LayoutItem &parent) override; QString arguments() const; QString unexpandedArguments() const; @@ -163,12 +163,11 @@ public: void setSettingsKey(const QString &key); void makeOverridable(const QString &overridingKey, const QString &useOverridableKey); - void addToLayout(Utils::Layouting::LayoutBuilder &builder) override; + void addToLayout(Layouting::LayoutItem &parent) override; void setLabelText(const QString &labelText); void setPlaceHolderText(const QString &placeHolderText); void setHistoryCompleter(const QString &historyCompleterKey); void setExpectedKind(const Utils::PathChooser::Kind expectedKind); - void setEnvironmentChange(const Utils::EnvironmentChange &change); void setEnvironment(const Utils::Environment &env); void setDisplayStyle(Utils::StringAspect::DisplayStyle style); @@ -236,7 +235,7 @@ public: void fromMap(const QVariantMap &) override; void toMap(QVariantMap &) const override; - void addToLayout(Utils::Layouting::LayoutBuilder &builder) override; + void addToLayout(Layouting::LayoutItem &parent) override; struct Data : Utils::BaseAspect::Data { Interpreter interpreter; }; diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp index 72b2e76e50..f9bafd234a 100644 --- a/src/plugins/projectexplorer/runcontrol.cpp +++ b/src/plugins/projectexplorer/runcontrol.cpp @@ -25,9 +25,9 @@ #include <utils/checkablemessagebox.h> #include <utils/fileinprojectfinder.h> #include <utils/outputformatter.h> +#include <utils/process.h> #include <utils/processinterface.h> #include <utils/qtcassert.h> -#include <utils/qtcprocess.h> #include <utils/utilsicons.h> #include <coreplugin/icontext.h> @@ -255,9 +255,9 @@ public: // A handle to the actual application process. ProcessHandle applicationProcessHandle; - RunControlState state = RunControlState::Initialized; - QList<QPointer<RunWorker>> m_workers; + RunControlState state = RunControlState::Initialized; + bool printEnvironment = false; }; class RunControlPrivate : public QObject, public RunControlPrivateData @@ -334,6 +334,7 @@ void RunControl::copyDataFromRunConfiguration(RunConfiguration *runConfig) d->buildKey = runConfig->buildKey(); d->settingsData = runConfig->settingsData(); d->aspectData = runConfig->aspectData(); + d->printEnvironment = runConfig->isPrintEnvironmentEnabled(); setTarget(runConfig->target()); @@ -817,6 +818,11 @@ Utils::Id RunControl::runMode() const return d->runMode; } +bool RunControl::isPrintEnvironmentEnabled() const +{ + return d->printEnvironment; +} + const Runnable &RunControl::runnable() const { return d->runnable; @@ -1045,35 +1051,31 @@ bool RunControl::showPromptToStopDialog(const QString &title, { // Show a question message box where user can uncheck this // question for this class. - Utils::CheckableMessageBox messageBox(Core::ICore::dialogParent()); - messageBox.setWindowTitle(title); - messageBox.setText(text); - messageBox.setStandardButtons(QDialogButtonBox::Yes|QDialogButtonBox::Cancel); + QMap<QMessageBox::StandardButton, QString> buttonTexts; if (!stopButtonText.isEmpty()) - messageBox.button(QDialogButtonBox::Yes)->setText(stopButtonText); + buttonTexts[QMessageBox::Yes] = stopButtonText; if (!cancelButtonText.isEmpty()) - messageBox.button(QDialogButtonBox::Cancel)->setText(cancelButtonText); - messageBox.setDefaultButton(QDialogButtonBox::Yes); - if (prompt) { - messageBox.setCheckBoxText(Utils::CheckableMessageBox::msgDoNotAskAgain()); - messageBox.setChecked(false); - } else { - messageBox.setCheckBoxVisible(false); - } - messageBox.exec(); - const bool close = messageBox.clickedStandardButton() == QDialogButtonBox::Yes; - if (close && prompt && messageBox.isChecked()) - *prompt = false; - return close; + buttonTexts[QMessageBox::Cancel] = cancelButtonText; + + CheckableDecider decider; + if (prompt) + decider = CheckableDecider(prompt); + + auto selected = CheckableMessageBox::question(Core::ICore::dialogParent(), + title, + text, + decider, + QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Yes); + + return selected == QMessageBox::Yes; } void RunControl::provideAskPassEntry(Environment &env) { - if (env.value("SUDO_ASKPASS").isEmpty()) { - const FilePath askpass = SshSettings::askpassFilePath(); - if (askpass.exists()) - env.set("SUDO_ASKPASS", askpass.toUserOutput()); - } + const FilePath askpass = SshSettings::askpassFilePath(); + if (askpass.exists()) + env.setFallback("SUDO_ASKPASS", askpass.toUserOutput()); } bool RunControlPrivate::isAllowedTransition(RunControlState from, RunControlState to) @@ -1175,7 +1177,7 @@ public: bool m_runAsRoot = false; - QtcProcess m_process; + Process m_process; QTextCodec *m_outputCodec = nullptr; QTextCodec::ConverterState m_outputCodecState; @@ -1212,11 +1214,11 @@ SimpleTargetRunnerPrivate::SimpleTargetRunnerPrivate(SimpleTargetRunner *parent) : q(parent) { m_process.setProcessChannelMode(defaultProcessChannelMode()); - connect(&m_process, &QtcProcess::started, this, &SimpleTargetRunnerPrivate::forwardStarted); - connect(&m_process, &QtcProcess::done, this, &SimpleTargetRunnerPrivate::handleDone); - connect(&m_process, &QtcProcess::readyReadStandardError, + connect(&m_process, &Process::started, this, &SimpleTargetRunnerPrivate::forwardStarted); + connect(&m_process, &Process::done, this, &SimpleTargetRunnerPrivate::handleDone); + connect(&m_process, &Process::readyReadStandardError, this, &SimpleTargetRunnerPrivate::handleStandardError); - connect(&m_process, &QtcProcess::readyReadStandardOutput, + connect(&m_process, &Process::readyReadStandardOutput, this, &SimpleTargetRunnerPrivate::handleStandardOutput); if (WinDebugInterface::instance()) { @@ -1371,7 +1373,7 @@ void SimpleTargetRunnerPrivate::start() Encapsulates processes running in a console or as GUI processes, captures debug output of GUI processes on Windows (outputDebugString()). - \sa Utils::QtcProcess + \sa Utils::Process */ SimpleTargetRunner::SimpleTargetRunner(RunControl *runControl) @@ -1433,11 +1435,20 @@ void SimpleTargetRunner::start() d->m_stopForced = false; d->m_stopReported = false; d->disconnect(this); - d->m_process.setTerminalMode(useTerminal ? Utils::TerminalMode::On : Utils::TerminalMode::Off); + d->m_process.setTerminalMode(useTerminal ? Utils::TerminalMode::Run : Utils::TerminalMode::Off); d->m_runAsRoot = runAsRoot; const QString msg = Tr::tr("Starting %1...").arg(d->m_command.displayName()); appendMessage(msg, NormalMessageFormat); + if (runControl()->isPrintEnvironmentEnabled()) { + appendMessage(Tr::tr("Environment:"), NormalMessageFormat); + runControl()->runnable().environment + .forEachEntry([this](const QString &key, const QString &value, bool enabled) { + if (enabled) + appendMessage(key + '=' + value, StdOutFormat); + }); + appendMessage({}, StdOutFormat); + } const bool isDesktop = !d->m_command.executable().needsDevice(); if (isDesktop && d->m_command.isEmpty()) { @@ -1653,9 +1664,9 @@ void RunWorker::reportFailure(const QString &msg) * Appends a message in the specified \a format to * the owning RunControl's \uicontrol{Application Output} pane. */ -void RunWorker::appendMessage(const QString &msg, OutputFormat format) +void RunWorker::appendMessage(const QString &msg, OutputFormat format, bool appendNewLine) { - if (msg.endsWith('\n')) + if (!appendNewLine || msg.endsWith('\n')) emit d->runControl->appendMessage(msg, format); else emit d->runControl->appendMessage(msg + '\n', format); diff --git a/src/plugins/projectexplorer/runcontrol.h b/src/plugins/projectexplorer/runcontrol.h index d779b693a8..84a5b5c166 100644 --- a/src/plugins/projectexplorer/runcontrol.h +++ b/src/plugins/projectexplorer/runcontrol.h @@ -67,7 +67,7 @@ public: QVariant recordedData(const QString &channel) const; // Part of read-only interface of RunControl for convenience. - void appendMessage(const QString &msg, Utils::OutputFormat format); + void appendMessage(const QString &msg, Utils::OutputFormat format, bool appendNewLine = true); void appendMessageChunk(const QString &msg, Utils::OutputFormat format); IDeviceConstPtr device() const; @@ -205,6 +205,7 @@ public: void setupFormatter(Utils::OutputFormatter *formatter) const; Utils::Id runMode() const; + bool isPrintEnvironmentEnabled() const; const Runnable &runnable() const; diff --git a/src/plugins/projectexplorer/runsettingspropertiespage.cpp b/src/plugins/projectexplorer/runsettingspropertiespage.cpp index bd8b30f2b7..43edee9b66 100644 --- a/src/plugins/projectexplorer/runsettingspropertiespage.cpp +++ b/src/plugins/projectexplorer/runsettingspropertiespage.cpp @@ -10,9 +10,10 @@ #include "projectconfigurationmodel.h" #include "projectexplorertr.h" #include "runconfiguration.h" -#include "session.h" #include "target.h" +#include <coreplugin/session.h> + #include <utils/algorithm.h> #include <utils/qtcassert.h> #include <utils/stringutils.h> @@ -291,11 +292,10 @@ void RunSettingsWidget::currentDeployConfigurationChanged(int index) if (m_ignoreChanges.isLocked()) return; if (index == -1) - SessionManager::setActiveDeployConfiguration(m_target, nullptr, SetActive::Cascade); + m_target->setActiveDeployConfiguration(nullptr, SetActive::Cascade); else - SessionManager::setActiveDeployConfiguration(m_target, - qobject_cast<DeployConfiguration *>(m_target->deployConfigurationModel()->projectConfigurationAt(index)), - SetActive::Cascade); + m_target->setActiveDeployConfiguration(qobject_cast<DeployConfiguration *>(m_target->deployConfigurationModel()->projectConfigurationAt(index)), + SetActive::Cascade); } void RunSettingsWidget::aboutToShowDeployMenu() @@ -309,7 +309,7 @@ void RunSettingsWidget::aboutToShowDeployMenu() if (!newDc) return; m_target->addDeployConfiguration(newDc); - SessionManager::setActiveDeployConfiguration(m_target, newDc, SetActive::Cascade); + m_target->setActiveDeployConfiguration(newDc, SetActive::Cascade); m_removeDeployToolButton->setEnabled(m_target->deployConfigurations().size() > 1); }); } diff --git a/src/plugins/projectexplorer/selectablefilesmodel.cpp b/src/plugins/projectexplorer/selectablefilesmodel.cpp index 365c319992..de5b0b79b4 100644 --- a/src/plugins/projectexplorer/selectablefilesmodel.cpp +++ b/src/plugins/projectexplorer/selectablefilesmodel.cpp @@ -9,10 +9,10 @@ #include <coreplugin/icore.h> #include <utils/algorithm.h> +#include <utils/async.h> #include <utils/fancylineedit.h> #include <utils/fsengine/fileiconprovider.h> #include <utils/pathchooser.h> -#include <utils/runextensions.h> #include <utils/stringutils.h> #include <QDialogButtonBox> @@ -51,13 +51,13 @@ void SelectableFilesFromDirModel::startParsing(const Utils::FilePath &baseDir) m_rootForFuture->fullPath = baseDir; m_rootForFuture->isDir = true; - m_watcher.setFuture(Utils::runAsync(&SelectableFilesFromDirModel::run, this)); + m_watcher.setFuture(Utils::asyncRun(&SelectableFilesFromDirModel::run, this)); } -void SelectableFilesFromDirModel::run(QFutureInterface<void> &fi) +void SelectableFilesFromDirModel::run(QPromise<void> &promise) { m_futureCount = 0; - buildTree(m_baseDir, m_rootForFuture, fi, 5); + buildTree(m_baseDir, m_rootForFuture, promise, 5); } void SelectableFilesFromDirModel::buildTreeFinished() @@ -97,7 +97,7 @@ SelectableFilesModel::FilterState SelectableFilesModel::filter(Tree *t) } void SelectableFilesFromDirModel::buildTree(const Utils::FilePath &baseDir, Tree *tree, - QFutureInterface<void> &fi, int symlinkDepth) + QPromise<void> &promise, int symlinkDepth) { if (symlinkDepth == 0) return; @@ -111,7 +111,7 @@ void SelectableFilesFromDirModel::buildTree(const Utils::FilePath &baseDir, Tree Utils::FilePath fn = Utils::FilePath::fromFileInfo(fileInfo); if (m_futureCount % 100) { emit parsingProgress(fn); - if (fi.isCanceled()) + if (promise.isCanceled()) return; } ++m_futureCount; @@ -121,7 +121,7 @@ void SelectableFilesFromDirModel::buildTree(const Utils::FilePath &baseDir, Tree t->name = fileInfo.fileName(); t->fullPath = fn; t->isDir = true; - buildTree(fn, t, fi, symlinkDepth - fileInfo.isSymLink()); + buildTree(fn, t, promise, symlinkDepth - fileInfo.isSymLink()); allChecked &= t->checked == Qt::Checked; allUnchecked &= t->checked == Qt::Unchecked; tree->childDirectories.append(t); diff --git a/src/plugins/projectexplorer/selectablefilesmodel.h b/src/plugins/projectexplorer/selectablefilesmodel.h index 6340e2d1a2..8c47fbd990 100644 --- a/src/plugins/projectexplorer/selectablefilesmodel.h +++ b/src/plugins/projectexplorer/selectablefilesmodel.h @@ -9,7 +9,6 @@ #include <QAbstractItemModel> #include <QDialog> -#include <QFutureInterface> #include <QFutureWatcher> #include <QLabel> #include <QRegularExpression> @@ -147,11 +146,9 @@ signals: void parsingProgress(const Utils::FilePath &fileName); private: - void buildTree(const Utils::FilePath &baseDir, - Tree *tree, - QFutureInterface<void> &fi, + void buildTree(const Utils::FilePath &baseDir, Tree *tree, QPromise<void> &promise, int symlinkDepth); - void run(QFutureInterface<void> &fi); + void run(QPromise<void> &promise); void buildTreeFinished(); // Used in the future thread need to all not used after calling startParsing diff --git a/src/plugins/projectexplorer/session.cpp b/src/plugins/projectexplorer/session.cpp deleted file mode 100644 index 6dcfd9a461..0000000000 --- a/src/plugins/projectexplorer/session.cpp +++ /dev/null @@ -1,1262 +0,0 @@ -// 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 "session.h" - -#include "buildconfiguration.h" -#include "deployconfiguration.h" -#include "editorconfiguration.h" -#include "kit.h" -#include "project.h" -#include "projectexplorer.h" -#include "projectexplorerconstants.h" -#include "projectexplorertr.h" -#include "projectnodes.h" -#include "target.h" - -#include <coreplugin/coreconstants.h> -#include <coreplugin/editormanager/editormanager.h> -#include <coreplugin/foldernavigationwidget.h> -#include <coreplugin/icore.h> -#include <coreplugin/idocument.h> -#include <coreplugin/imode.h> -#include <coreplugin/modemanager.h> -#include <coreplugin/progressmanager/progressmanager.h> - -#include <texteditor/texteditor.h> - -#include <utils/algorithm.h> -#include <utils/filepath.h> -#include <utils/qtcassert.h> -#include <utils/stylehelper.h> -#include <utils/qtcassert.h> - -#include <QDebug> -#include <QMessageBox> -#include <QPushButton> - -#ifdef WITH_TESTS -#include <QTemporaryFile> -#include <QTest> -#include <vector> -#endif - -using namespace Core; -using namespace Utils; -using namespace ProjectExplorer::Internal; - -namespace ProjectExplorer { - -const char DEFAULT_SESSION[] = "default"; -const char LAST_ACTIVE_TIMES_KEY[] = "LastActiveTimes"; - -/*! - \class ProjectExplorer::SessionManager - - \brief The SessionManager class manages sessions. - - TODO the interface of this class is not really great. - The implementation suffers from that all the functions from the - public interface just wrap around functions which do the actual work. - This could be improved. -*/ - -class SessionManagerPrivate -{ -public: - void restoreValues(const PersistentSettingsReader &reader); - void restoreDependencies(const PersistentSettingsReader &reader); - void restoreStartupProject(const PersistentSettingsReader &reader); - void restoreEditors(const PersistentSettingsReader &reader); - void restoreProjects(const FilePaths &fileList); - void askUserAboutFailedProjects(); - void sessionLoadingProgress(); - - bool recursiveDependencyCheck(const FilePath &newDep, const FilePath &checkDep) const; - FilePaths dependencies(const FilePath &proName) const; - FilePaths dependenciesOrder() const; - void dependencies(const FilePath &proName, FilePaths &result) const; - - static QString windowTitleAddition(const FilePath &filePath); - static QString sessionTitle(const FilePath &filePath); - - bool hasProjects() const { return !m_projects.isEmpty(); } - - QString m_sessionName = QLatin1String(DEFAULT_SESSION); - bool m_virginSession = true; - bool m_loadingSession = false; - bool m_casadeSetActive = false; - - mutable QStringList m_sessions; - mutable QHash<QString, QDateTime> m_sessionDateTimes; - QHash<QString, QDateTime> m_lastActiveTimes; - - Project *m_startupProject = nullptr; - QList<Project *> m_projects; - FilePaths m_failedProjects; - QMap<FilePath, FilePaths> m_depMap; - QMap<QString, QVariant> m_values; - QFutureInterface<void> m_future; - PersistentSettingsWriter *m_writer = nullptr; - -private: - static QString locationInProject(const FilePath &filePath); -}; - -static SessionManager *m_instance = nullptr; -static SessionManagerPrivate *d = nullptr; - -static QString projectFolderId(Project *pro) -{ - return pro->projectFilePath().toString(); -} - -const int PROJECT_SORT_VALUE = 100; - -SessionManager::SessionManager(QObject *parent) : QObject(parent) -{ - m_instance = this; - d = new SessionManagerPrivate; - - connect(ModeManager::instance(), &ModeManager::currentModeChanged, - this, &SessionManager::saveActiveMode); - - connect(ICore::instance(), &ICore::saveSettingsRequested, this, [] { - QVariantMap times; - for (auto it = d->m_lastActiveTimes.cbegin(); it != d->m_lastActiveTimes.cend(); ++it) - times.insert(it.key(), it.value()); - ICore::settings()->setValue(LAST_ACTIVE_TIMES_KEY, times); - }); - - connect(EditorManager::instance(), &EditorManager::editorCreated, - this, &SessionManager::configureEditor); - connect(this, &SessionManager::projectAdded, - EditorManager::instance(), &EditorManager::updateWindowTitles); - connect(this, &SessionManager::projectRemoved, - EditorManager::instance(), &EditorManager::updateWindowTitles); - connect(this, &SessionManager::projectDisplayNameChanged, - EditorManager::instance(), &EditorManager::updateWindowTitles); - connect(EditorManager::instance(), &EditorManager::editorOpened, - this, &SessionManager::markSessionFileDirty); - connect(EditorManager::instance(), &EditorManager::editorsClosed, - this, &SessionManager::markSessionFileDirty); - - EditorManager::setWindowTitleAdditionHandler(&SessionManagerPrivate::windowTitleAddition); - EditorManager::setSessionTitleHandler(&SessionManagerPrivate::sessionTitle); -} - -SessionManager::~SessionManager() -{ - EditorManager::setWindowTitleAdditionHandler({}); - EditorManager::setSessionTitleHandler({}); - emit m_instance->aboutToUnloadSession(d->m_sessionName); - delete d->m_writer; - delete d; - d = nullptr; -} - -SessionManager *SessionManager::instance() -{ - return m_instance; -} - -bool SessionManager::isDefaultVirgin() -{ - return isDefaultSession(d->m_sessionName) && d->m_virginSession; -} - -bool SessionManager::isDefaultSession(const QString &session) -{ - return session == QLatin1String(DEFAULT_SESSION); -} - -void SessionManager::saveActiveMode(Id mode) -{ - if (mode != Core::Constants::MODE_WELCOME) - setValue(QLatin1String("ActiveMode"), mode.toString()); -} - -bool SessionManagerPrivate::recursiveDependencyCheck(const FilePath &newDep, - const FilePath &checkDep) const -{ - if (newDep == checkDep) - return false; - - const FilePaths depList = m_depMap.value(checkDep); - for (const FilePath &dependency : depList) { - if (!recursiveDependencyCheck(newDep, dependency)) - return false; - } - - return true; -} - -/* - * The dependency management exposes an interface based on projects, but - * is internally purely string based. This is suboptimal. Probably it would be - * nicer to map the filenames to projects on load and only map it back to - * filenames when saving. - */ - -QList<Project *> SessionManager::dependencies(const Project *project) -{ - const FilePath proName = project->projectFilePath(); - const FilePaths proDeps = d->m_depMap.value(proName); - - QList<Project *> projects; - for (const FilePath &dep : proDeps) { - Project *pro = Utils::findOrDefault(d->m_projects, [&dep](Project *p) { - return p->projectFilePath() == dep; - }); - if (pro) - projects += pro; - } - - return projects; -} - -bool SessionManager::hasDependency(const Project *project, const Project *depProject) -{ - const FilePath proName = project->projectFilePath(); - const FilePath depName = depProject->projectFilePath(); - - const FilePaths proDeps = d->m_depMap.value(proName); - return proDeps.contains(depName); -} - -bool SessionManager::canAddDependency(const Project *project, const Project *depProject) -{ - const FilePath newDep = project->projectFilePath(); - const FilePath checkDep = depProject->projectFilePath(); - - return d->recursiveDependencyCheck(newDep, checkDep); -} - -bool SessionManager::addDependency(Project *project, Project *depProject) -{ - const FilePath proName = project->projectFilePath(); - const FilePath depName = depProject->projectFilePath(); - - // check if this dependency is valid - if (!d->recursiveDependencyCheck(proName, depName)) - return false; - - FilePaths proDeps = d->m_depMap.value(proName); - if (!proDeps.contains(depName)) { - proDeps.append(depName); - d->m_depMap[proName] = proDeps; - } - emit m_instance->dependencyChanged(project, depProject); - - return true; -} - -void SessionManager::removeDependency(Project *project, Project *depProject) -{ - const FilePath proName = project->projectFilePath(); - const FilePath depName = depProject->projectFilePath(); - - FilePaths proDeps = d->m_depMap.value(proName); - proDeps.removeAll(depName); - if (proDeps.isEmpty()) - d->m_depMap.remove(proName); - else - d->m_depMap[proName] = proDeps; - emit m_instance->dependencyChanged(project, depProject); -} - -bool SessionManager::isProjectConfigurationCascading() -{ - return d->m_casadeSetActive; -} - -void SessionManager::setProjectConfigurationCascading(bool b) -{ - d->m_casadeSetActive = b; - markSessionFileDirty(); -} - -void SessionManager::setActiveTarget(Project *project, Target *target, SetActive cascade) -{ - QTC_ASSERT(project, return); - - if (project->isShuttingDown()) - return; - - project->setActiveTarget(target); - - if (!target) // never cascade setting no target - return; - - if (cascade != SetActive::Cascade || !d->m_casadeSetActive) - return; - - Utils::Id kitId = target->kit()->id(); - for (Project *otherProject : SessionManager::projects()) { - if (otherProject == project) - continue; - if (Target *otherTarget = Utils::findOrDefault(otherProject->targets(), - [kitId](Target *t) { return t->kit()->id() == kitId; })) - otherProject->setActiveTarget(otherTarget); - } -} - -void SessionManager::setActiveBuildConfiguration(Target *target, BuildConfiguration *bc, SetActive cascade) -{ - QTC_ASSERT(target, return); - QTC_ASSERT(target->project(), return); - - if (target->project()->isShuttingDown() || target->isShuttingDown()) - return; - - target->setActiveBuildConfiguration(bc); - - if (!bc) - return; - if (cascade != SetActive::Cascade || !d->m_casadeSetActive) - return; - - Utils::Id kitId = target->kit()->id(); - QString name = bc->displayName(); // We match on displayname - for (Project *otherProject : SessionManager::projects()) { - if (otherProject == target->project()) - continue; - Target *otherTarget = otherProject->activeTarget(); - if (!otherTarget || otherTarget->kit()->id() != kitId) - continue; - - for (BuildConfiguration *otherBc : otherTarget->buildConfigurations()) { - if (otherBc->displayName() == name) { - otherTarget->setActiveBuildConfiguration(otherBc); - break; - } - } - } -} - -void SessionManager::setActiveDeployConfiguration(Target *target, DeployConfiguration *dc, SetActive cascade) -{ - QTC_ASSERT(target, return); - QTC_ASSERT(target->project(), return); - - if (target->project()->isShuttingDown() || target->isShuttingDown()) - return; - - target->setActiveDeployConfiguration(dc); - - if (!dc) - return; - if (cascade != SetActive::Cascade || !d->m_casadeSetActive) - return; - - Utils::Id kitId = target->kit()->id(); - QString name = dc->displayName(); // We match on displayname - for (Project *otherProject : SessionManager::projects()) { - if (otherProject == target->project()) - continue; - Target *otherTarget = otherProject->activeTarget(); - if (!otherTarget || otherTarget->kit()->id() != kitId) - continue; - - for (DeployConfiguration *otherDc : otherTarget->deployConfigurations()) { - if (otherDc->displayName() == name) { - otherTarget->setActiveDeployConfiguration(otherDc); - break; - } - } - } -} - -void SessionManager::setStartupProject(Project *startupProject) -{ - QTC_ASSERT((!startupProject && d->m_projects.isEmpty()) - || (startupProject && d->m_projects.contains(startupProject)), return); - - if (d->m_startupProject == startupProject) - return; - - d->m_startupProject = startupProject; - if (d->m_startupProject && d->m_startupProject->needsConfiguration()) { - ModeManager::activateMode(Constants::MODE_SESSION); - ModeManager::setFocusToCurrentMode(); - } - FolderNavigationWidgetFactory::setFallbackSyncFilePath( - startupProject ? startupProject->projectFilePath().parentDir() : FilePath()); - emit m_instance->startupProjectChanged(startupProject); -} - -Project *SessionManager::startupProject() -{ - return d->m_startupProject; -} - -Target *SessionManager::startupTarget() -{ - return d->m_startupProject ? d->m_startupProject->activeTarget() : nullptr; -} - -BuildSystem *SessionManager::startupBuildSystem() -{ - Target *t = startupTarget(); - return t ? t->buildSystem() : nullptr; -} - -/*! - * Returns the RunConfiguration of the currently active target - * of the startup project, if such exists, or \c nullptr otherwise. - */ - - -RunConfiguration *SessionManager::startupRunConfiguration() -{ - Target *t = startupTarget(); - return t ? t->activeRunConfiguration() : nullptr; -} - -void SessionManager::addProject(Project *pro) -{ - QTC_ASSERT(pro, return); - QTC_CHECK(!pro->displayName().isEmpty()); - QTC_CHECK(pro->id().isValid()); - - d->m_virginSession = false; - QTC_ASSERT(!d->m_projects.contains(pro), return); - - d->m_projects.append(pro); - - connect(pro, &Project::displayNameChanged, - m_instance, [pro]() { emit m_instance->projectDisplayNameChanged(pro); }); - - emit m_instance->projectAdded(pro); - const auto updateFolderNavigation = [pro] { - // destructing projects might trigger changes, so check if the project is actually there - if (QTC_GUARD(d->m_projects.contains(pro))) { - const QIcon icon = pro->rootProjectNode() ? pro->rootProjectNode()->icon() : QIcon(); - FolderNavigationWidgetFactory::insertRootDirectory({projectFolderId(pro), - PROJECT_SORT_VALUE, - pro->displayName(), - pro->projectFilePath().parentDir(), - icon}); - } - }; - updateFolderNavigation(); - configureEditors(pro); - connect(pro, &Project::fileListChanged, m_instance, [pro, updateFolderNavigation]() { - configureEditors(pro); - updateFolderNavigation(); // update icon - }); - connect(pro, &Project::displayNameChanged, m_instance, updateFolderNavigation); - - if (!startupProject()) - setStartupProject(pro); -} - -void SessionManager::removeProject(Project *project) -{ - d->m_virginSession = false; - QTC_ASSERT(project, return); - removeProjects({project}); -} - -bool SessionManager::loadingSession() -{ - return d->m_loadingSession; -} - -bool SessionManager::save() -{ - emit m_instance->aboutToSaveSession(); - - const FilePath filePath = sessionNameToFileName(d->m_sessionName); - QVariantMap data; - - // See the explanation at loadSession() for how we handle the implicit default session. - if (isDefaultVirgin()) { - if (filePath.exists()) { - PersistentSettingsReader reader; - if (!reader.load(filePath)) { - QMessageBox::warning(ICore::dialogParent(), Tr::tr("Error while saving session"), - Tr::tr("Could not save session %1").arg(filePath.toUserOutput())); - return false; - } - data = reader.restoreValues(); - } - } else { - // save the startup project - if (d->m_startupProject) - data.insert("StartupProject", d->m_startupProject->projectFilePath().toSettings()); - - const QColor c = StyleHelper::requestedBaseColor(); - if (c.isValid()) { - QString tmp = QString::fromLatin1("#%1%2%3") - .arg(c.red(), 2, 16, QLatin1Char('0')) - .arg(c.green(), 2, 16, QLatin1Char('0')) - .arg(c.blue(), 2, 16, QLatin1Char('0')); - data.insert(QLatin1String("Color"), tmp); - } - - FilePaths projectFiles = Utils::transform(projects(), &Project::projectFilePath); - // Restore information on projects that failed to load: - // don't read projects to the list, which the user loaded - for (const FilePath &failed : std::as_const(d->m_failedProjects)) { - if (!projectFiles.contains(failed)) - projectFiles << failed; - } - - data.insert("ProjectList", Utils::transform<QStringList>(projectFiles, - &FilePath::toString)); - data.insert("CascadeSetActive", d->m_casadeSetActive); - - QVariantMap depMap; - auto i = d->m_depMap.constBegin(); - while (i != d->m_depMap.constEnd()) { - QString key = i.key().toString(); - QStringList values; - const FilePaths valueList = i.value(); - for (const FilePath &value : valueList) - values << value.toString(); - depMap.insert(key, values); - ++i; - } - data.insert(QLatin1String("ProjectDependencies"), QVariant(depMap)); - data.insert(QLatin1String("EditorSettings"), EditorManager::saveState().toBase64()); - } - - const auto end = d->m_values.constEnd(); - QStringList keys; - for (auto it = d->m_values.constBegin(); it != end; ++it) { - data.insert(QLatin1String("value-") + it.key(), it.value()); - keys << it.key(); - } - data.insert(QLatin1String("valueKeys"), keys); - - if (!d->m_writer || d->m_writer->fileName() != filePath) { - delete d->m_writer; - d->m_writer = new PersistentSettingsWriter(filePath, "QtCreatorSession"); - } - const bool result = d->m_writer->save(data, ICore::dialogParent()); - if (result) { - if (!isDefaultVirgin()) - d->m_sessionDateTimes.insert(activeSession(), QDateTime::currentDateTime()); - } else { - QMessageBox::warning(ICore::dialogParent(), Tr::tr("Error while saving session"), - Tr::tr("Could not save session to file %1").arg(d->m_writer->fileName().toUserOutput())); - } - - return result; -} - -/*! - Closes all projects - */ -void SessionManager::closeAllProjects() -{ - removeProjects(projects()); -} - -const QList<Project *> SessionManager::projects() -{ - return d->m_projects; -} - -bool SessionManager::hasProjects() -{ - return d->hasProjects(); -} - -bool SessionManager::hasProject(Project *p) -{ - return d->m_projects.contains(p); -} - -FilePaths SessionManagerPrivate::dependencies(const FilePath &proName) const -{ - FilePaths result; - dependencies(proName, result); - return result; -} - -void SessionManagerPrivate::dependencies(const FilePath &proName, FilePaths &result) const -{ - const FilePaths depends = m_depMap.value(proName); - - for (const FilePath &dep : depends) - dependencies(dep, result); - - if (!result.contains(proName)) - result.append(proName); -} - -QString SessionManagerPrivate::sessionTitle(const FilePath &filePath) -{ - if (SessionManager::isDefaultSession(d->m_sessionName)) { - if (filePath.isEmpty()) { - // use single project's name if there is only one loaded. - const QList<Project *> projects = SessionManager::projects(); - if (projects.size() == 1) - return projects.first()->displayName(); - } - } else { - QString sessionName = d->m_sessionName; - if (sessionName.isEmpty()) - sessionName = Tr::tr("Untitled"); - return sessionName; - } - return QString(); -} - -QString SessionManagerPrivate::locationInProject(const FilePath &filePath) { - const Project *project = SessionManager::projectForFile(filePath); - if (!project) - return QString(); - - const FilePath parentDir = filePath.parentDir(); - if (parentDir == project->projectDirectory()) - return "@ " + project->displayName(); - - if (filePath.isChildOf(project->projectDirectory())) { - const FilePath dirInProject = parentDir.relativeChildPath(project->projectDirectory()); - return "(" + dirInProject.toUserOutput() + " @ " + project->displayName() + ")"; - } - - // For a file that is "outside" the project it belongs to, we display its - // dir's full path because it is easier to read than a series of "../../.". - // Example: /home/hugo/GenericProject/App.files lists /home/hugo/lib/Bar.cpp - return "(" + parentDir.toUserOutput() + " @ " + project->displayName() + ")"; -} - -QString SessionManagerPrivate::windowTitleAddition(const FilePath &filePath) -{ - return filePath.isEmpty() ? QString() : locationInProject(filePath); -} - -FilePaths SessionManagerPrivate::dependenciesOrder() const -{ - QList<QPair<FilePath, FilePaths>> unordered; - FilePaths ordered; - - // copy the map to a temporary list - for (const Project *pro : m_projects) { - const FilePath proName = pro->projectFilePath(); - const FilePaths depList = filtered(m_depMap.value(proName), - [this](const FilePath &proPath) { - return contains(m_projects, [proPath](const Project *p) { - return p->projectFilePath() == proPath; - }); - }); - unordered.push_back({proName, depList}); - } - - while (!unordered.isEmpty()) { - for (int i = (unordered.count() - 1); i >= 0; --i) { - if (unordered.at(i).second.isEmpty()) { - ordered << unordered.at(i).first; - unordered.removeAt(i); - } - } - - // remove the handled projects from the dependency lists - // of the remaining unordered projects - for (int i = 0; i < unordered.count(); ++i) { - for (const FilePath &pro : std::as_const(ordered)) { - FilePaths depList = unordered.at(i).second; - depList.removeAll(pro); - unordered[i].second = depList; - } - } - } - - return ordered; -} - -QList<Project *> SessionManager::projectOrder(const Project *project) -{ - QList<Project *> result; - - FilePaths pros; - if (project) - pros = d->dependencies(project->projectFilePath()); - else - pros = d->dependenciesOrder(); - - for (const FilePath &proFile : std::as_const(pros)) { - for (Project *pro : projects()) { - if (pro->projectFilePath() == proFile) { - result << pro; - break; - } - } - } - - return result; -} - -Project *SessionManager::projectForFile(const FilePath &fileName) -{ - if (Project * const project = Utils::findOrDefault(SessionManager::projects(), - [&fileName](const Project *p) { return p->isKnownFile(fileName); })) { - return project; - } - return Utils::findOrDefault(SessionManager::projects(), - [&fileName](const Project *p) { - for (const Target * const target : p->targets()) { - for (const BuildConfiguration * const bc : target->buildConfigurations()) { - if (fileName.isChildOf(bc->buildDirectory())) - return false; - } - } - return fileName.isChildOf(p->projectDirectory()); - }); -} - -Project *SessionManager::projectWithProjectFilePath(const FilePath &filePath) -{ - return Utils::findOrDefault(SessionManager::projects(), - [&filePath](const Project *p) { return p->projectFilePath() == filePath; }); -} - -void SessionManager::configureEditor(IEditor *editor, const QString &fileName) -{ - if (auto textEditor = qobject_cast<TextEditor::BaseTextEditor*>(editor)) { - Project *project = projectForFile(Utils::FilePath::fromString(fileName)); - // Global settings are the default. - if (project) - project->editorConfiguration()->configureEditor(textEditor); - } -} - -void SessionManager::configureEditors(Project *project) -{ - const QList<IDocument *> documents = DocumentModel::openedDocuments(); - for (IDocument *document : documents) { - if (project->isKnownFile(document->filePath())) { - const QList<IEditor *> editors = DocumentModel::editorsForDocument(document); - for (IEditor *editor : editors) { - if (auto textEditor = qobject_cast<TextEditor::BaseTextEditor*>(editor)) { - project->editorConfiguration()->configureEditor(textEditor); - } - } - } - } -} - -void SessionManager::removeProjects(const QList<Project *> &remove) -{ - for (Project *pro : remove) - emit m_instance->aboutToRemoveProject(pro); - - bool changeStartupProject = false; - - // Delete projects - for (Project *pro : remove) { - pro->saveSettings(); - pro->markAsShuttingDown(); - - // Remove the project node: - d->m_projects.removeOne(pro); - - if (pro == d->m_startupProject) - changeStartupProject = true; - - FolderNavigationWidgetFactory::removeRootDirectory(projectFolderId(pro)); - disconnect(pro, nullptr, m_instance, nullptr); - emit m_instance->projectRemoved(pro); - } - - if (changeStartupProject) - setStartupProject(hasProjects() ? projects().first() : nullptr); - - qDeleteAll(remove); -} - -/*! - Lets other plugins store persistent values within the session file. -*/ - -void SessionManager::setValue(const QString &name, const QVariant &value) -{ - if (d->m_values.value(name) == value) - return; - d->m_values.insert(name, value); -} - -QVariant SessionManager::value(const QString &name) -{ - auto it = d->m_values.constFind(name); - return (it == d->m_values.constEnd()) ? QVariant() : *it; -} - -QString SessionManager::activeSession() -{ - return d->m_sessionName; -} - -QStringList SessionManager::sessions() -{ - if (d->m_sessions.isEmpty()) { - // We are not initialized yet, so do that now - const FilePaths sessionFiles = - ICore::userResourcePath().dirEntries({{"*qws"}}, QDir::Time | QDir::Reversed); - const QVariantMap lastActiveTimes = ICore::settings()->value(LAST_ACTIVE_TIMES_KEY).toMap(); - for (const FilePath &file : sessionFiles) { - const QString &name = file.completeBaseName(); - d->m_sessionDateTimes.insert(name, file.lastModified()); - const auto lastActiveTime = lastActiveTimes.find(name); - d->m_lastActiveTimes.insert(name, lastActiveTime != lastActiveTimes.end() - ? lastActiveTime->toDateTime() - : file.lastModified()); - if (name != QLatin1String(DEFAULT_SESSION)) - d->m_sessions << name; - } - d->m_sessions.prepend(QLatin1String(DEFAULT_SESSION)); - } - return d->m_sessions; -} - -QDateTime SessionManager::sessionDateTime(const QString &session) -{ - return d->m_sessionDateTimes.value(session); -} - -QDateTime SessionManager::lastActiveTime(const QString &session) -{ - return d->m_lastActiveTimes.value(session); -} - -FilePath SessionManager::sessionNameToFileName(const QString &session) -{ - return ICore::userResourcePath(session + ".qws"); -} - -/*! - Creates \a session, but does not actually create the file. -*/ - -bool SessionManager::createSession(const QString &session) -{ - if (sessions().contains(session)) - return false; - Q_ASSERT(d->m_sessions.size() > 0); - d->m_sessions.insert(1, session); - d->m_lastActiveTimes.insert(session, QDateTime::currentDateTime()); - return true; -} - -bool SessionManager::renameSession(const QString &original, const QString &newName) -{ - if (!cloneSession(original, newName)) - return false; - if (original == activeSession()) - loadSession(newName); - emit instance()->sessionRenamed(original, newName); - return deleteSession(original); -} - - -/*! - \brief Shows a dialog asking the user to confirm deleting the session \p session -*/ -bool SessionManager::confirmSessionDelete(const QStringList &sessions) -{ - const QString title = sessions.size() == 1 ? Tr::tr("Delete Session") : Tr::tr("Delete Sessions"); - const QString question = sessions.size() == 1 - ? Tr::tr("Delete session %1?").arg(sessions.first()) - : Tr::tr("Delete these sessions?\n %1").arg(sessions.join("\n ")); - return QMessageBox::question(ICore::dialogParent(), - title, - question, - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes; -} - -/*! - Deletes \a session name from session list and the file from disk. -*/ -bool SessionManager::deleteSession(const QString &session) -{ - if (!d->m_sessions.contains(session)) - return false; - d->m_sessions.removeOne(session); - d->m_lastActiveTimes.remove(session); - emit instance()->sessionRemoved(session); - FilePath sessionFile = sessionNameToFileName(session); - if (sessionFile.exists()) - return sessionFile.removeFile(); - return false; -} - -void SessionManager::deleteSessions(const QStringList &sessions) -{ - for (const QString &session : sessions) - deleteSession(session); -} - -bool SessionManager::cloneSession(const QString &original, const QString &clone) -{ - if (!d->m_sessions.contains(original)) - return false; - - FilePath sessionFile = sessionNameToFileName(original); - // If the file does not exist, we can still clone - if (!sessionFile.exists() || sessionFile.copyFile(sessionNameToFileName(clone))) { - d->m_sessions.insert(1, clone); - d->m_sessionDateTimes.insert(clone, sessionNameToFileName(clone).lastModified()); - return true; - } - return false; -} - -void SessionManagerPrivate::restoreValues(const PersistentSettingsReader &reader) -{ - const QStringList keys = reader.restoreValue(QLatin1String("valueKeys")).toStringList(); - for (const QString &key : keys) { - QVariant value = reader.restoreValue(QLatin1String("value-") + key); - m_values.insert(key, value); - } -} - -void SessionManagerPrivate::restoreDependencies(const PersistentSettingsReader &reader) -{ - QMap<QString, QVariant> depMap = reader.restoreValue(QLatin1String("ProjectDependencies")).toMap(); - auto i = depMap.constBegin(); - while (i != depMap.constEnd()) { - const QString &key = i.key(); - FilePaths values; - const QStringList valueList = i.value().toStringList(); - for (const QString &value : valueList) - values << FilePath::fromString(value); - m_depMap.insert(FilePath::fromString(key), values); - ++i; - } -} - -void SessionManagerPrivate::askUserAboutFailedProjects() -{ - FilePaths failedProjects = m_failedProjects; - if (!failedProjects.isEmpty()) { - QString fileList = FilePath::formatFilePaths(failedProjects, "<br>"); - QMessageBox box(QMessageBox::Warning, - Tr::tr("Failed to restore project files"), - Tr::tr("Could not restore the following project files:<br><b>%1</b>"). - arg(fileList)); - auto keepButton = new QPushButton(Tr::tr("Keep projects in Session"), &box); - auto removeButton = new QPushButton(Tr::tr("Remove projects from Session"), &box); - box.addButton(keepButton, QMessageBox::AcceptRole); - box.addButton(removeButton, QMessageBox::DestructiveRole); - - box.exec(); - - if (box.clickedButton() == removeButton) - m_failedProjects.clear(); - } -} - -void SessionManagerPrivate::restoreStartupProject(const PersistentSettingsReader &reader) -{ - const FilePath startupProject = FilePath::fromSettings(reader.restoreValue("StartupProject")); - if (!startupProject.isEmpty()) { - for (Project *pro : std::as_const(m_projects)) { - if (pro->projectFilePath() == startupProject) { - m_instance->setStartupProject(pro); - break; - } - } - } - if (!m_startupProject) { - if (!startupProject.isEmpty()) - qWarning() << "Could not find startup project" << startupProject; - if (hasProjects()) - m_instance->setStartupProject(m_projects.first()); - } -} - -void SessionManagerPrivate::restoreEditors(const PersistentSettingsReader &reader) -{ - const QVariant editorsettings = reader.restoreValue(QLatin1String("EditorSettings")); - if (editorsettings.isValid()) { - EditorManager::restoreState(QByteArray::fromBase64(editorsettings.toByteArray())); - sessionLoadingProgress(); - } -} - -/*! - Loads a session, takes a session name (not filename). -*/ -void SessionManagerPrivate::restoreProjects(const FilePaths &fileList) -{ - // indirectly adds projects to session - // Keep projects that failed to load in the session! - m_failedProjects = fileList; - if (!fileList.isEmpty()) { - ProjectExplorerPlugin::OpenProjectResult result = ProjectExplorerPlugin::openProjects(fileList); - if (!result) - ProjectExplorerPlugin::showOpenProjectError(result); - const QList<Project *> projects = result.projects(); - for (const Project *p : projects) - m_failedProjects.removeAll(p->projectFilePath()); - } -} - -/* - * ========== Notes on storing and loading the default session ========== - * The default session comes in two flavors: implicit and explicit. The implicit one, - * also referred to as "default virgin" in the code base, is the one that is active - * at start-up, if no session has been explicitly loaded due to command-line arguments - * or the "restore last session" setting in the session manager. - * The implicit default session silently turns into the explicit default session - * by loading a project or a file or changing settings in the Dependencies panel. The explicit - * default session can also be loaded by the user via the Welcome Screen. - * This mechanism somewhat complicates the handling of session-specific settings such as - * the ones in the task pane: Users expect that changes they make there become persistent, even - * when they are in the implicit default session. However, we can't just blindly store - * the implicit default session, because then we'd overwrite the project list of the explicit - * default session. Therefore, we use the following logic: - * - Upon start-up, if no session is to be explicitly loaded, we restore the parts of the - * explicit default session that are not related to projects, editors etc; the - * "general settings" of the session, so to speak. - * - When storing the implicit default session, we overwrite only these "general settings" - * of the explicit default session and keep the others as they are. - * - When switching from the implicit to the explicit default session, we keep the - * "general settings" and load everything else from the session file. - * This guarantees that user changes are properly transferred and nothing gets lost from - * either the implicit or the explicit default session. - * - */ -bool SessionManager::loadSession(const QString &session, bool initial) -{ - const bool loadImplicitDefault = session.isEmpty(); - const bool switchFromImplicitToExplicitDefault = session == DEFAULT_SESSION - && d->m_sessionName == DEFAULT_SESSION && !initial; - - // Do nothing if we have that session already loaded, - // exception if the session is the default virgin session - // we still want to be able to load the default session - if (session == d->m_sessionName && !isDefaultVirgin()) - return true; - - if (!loadImplicitDefault && !sessions().contains(session)) - return false; - - FilePaths fileList; - // Try loading the file - FilePath fileName = sessionNameToFileName(loadImplicitDefault ? DEFAULT_SESSION : session); - PersistentSettingsReader reader; - if (fileName.exists()) { - if (!reader.load(fileName)) { - QMessageBox::warning(ICore::dialogParent(), Tr::tr("Error while restoring session"), - Tr::tr("Could not restore session %1").arg(fileName.toUserOutput())); - - return false; - } - - if (loadImplicitDefault) { - d->restoreValues(reader); - emit m_instance->sessionLoaded(DEFAULT_SESSION); - return true; - } - - fileList = FileUtils::toFilePathList(reader.restoreValue("ProjectList").toStringList()); - } else if (loadImplicitDefault) { - return true; - } - - d->m_loadingSession = true; - - // Allow everyone to set something in the session and before saving - emit m_instance->aboutToUnloadSession(d->m_sessionName); - - if (!save()) { - d->m_loadingSession = false; - return false; - } - - // Clean up - if (!EditorManager::closeAllEditors()) { - d->m_loadingSession = false; - return false; - } - - // find a list of projects to close later - const QList<Project *> projectsToRemove = Utils::filtered(projects(), [&fileList](Project *p) { - return !fileList.contains(p->projectFilePath()); - }); - const QList<Project *> openProjects = projects(); - const FilePaths projectPathsToLoad = Utils::filtered(fileList, [&openProjects](const FilePath &path) { - return !Utils::contains(openProjects, [&path](Project *p) { - return p->projectFilePath() == path; - }); - }); - d->m_failedProjects.clear(); - d->m_depMap.clear(); - if (!switchFromImplicitToExplicitDefault) - d->m_values.clear(); - d->m_casadeSetActive = false; - - d->m_sessionName = session; - delete d->m_writer; - d->m_writer = nullptr; - EditorManager::updateWindowTitles(); - - if (fileName.exists()) { - d->m_virginSession = false; - - ProgressManager::addTask(d->m_future.future(), Tr::tr("Loading Session"), - "ProjectExplorer.SessionFile.Load"); - - d->m_future.setProgressRange(0, 1); - d->m_future.setProgressValue(0); - - if (!switchFromImplicitToExplicitDefault) - d->restoreValues(reader); - emit m_instance->aboutToLoadSession(session); - - // retrieve all values before the following code could change them again - Id modeId = Id::fromSetting(value(QLatin1String("ActiveMode"))); - if (!modeId.isValid()) - modeId = Id(Core::Constants::MODE_EDIT); - - QColor c = QColor(reader.restoreValue(QLatin1String("Color")).toString()); - if (c.isValid()) - StyleHelper::setBaseColor(c); - - d->m_future.setProgressRange(0, projectPathsToLoad.count() + 1/*initialization above*/ + 1/*editors*/); - d->m_future.setProgressValue(1); - QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); - - d->restoreProjects(projectPathsToLoad); - d->sessionLoadingProgress(); - d->restoreDependencies(reader); - d->restoreStartupProject(reader); - - removeProjects(projectsToRemove); // only remove old projects now that the startup project is set! - - d->restoreEditors(reader); - - d->m_future.reportFinished(); - d->m_future = QFutureInterface<void>(); - - // Fall back to Project mode if the startup project is unconfigured and - // use the mode saved in the session otherwise - if (d->m_startupProject && d->m_startupProject->needsConfiguration()) - modeId = Id(Constants::MODE_SESSION); - - ModeManager::activateMode(modeId); - ModeManager::setFocusToCurrentMode(); - } else { - removeProjects(projects()); - ModeManager::activateMode(Id(Core::Constants::MODE_EDIT)); - ModeManager::setFocusToCurrentMode(); - } - - d->m_casadeSetActive = reader.restoreValue(QLatin1String("CascadeSetActive"), false).toBool(); - d->m_lastActiveTimes.insert(session, QDateTime::currentDateTime()); - - emit m_instance->sessionLoaded(session); - - // Starts a event loop, better do that at the very end - d->askUserAboutFailedProjects(); - d->m_loadingSession = false; - return true; -} - -/*! - Returns the last session that was opened by the user. -*/ -QString SessionManager::lastSession() -{ - return ICore::settings()->value(Constants::LASTSESSION_KEY).toString(); -} - -/*! - Returns the session that was active when Qt Creator was last closed, if any. -*/ -QString SessionManager::startupSession() -{ - return ICore::settings()->value(Constants::STARTUPSESSION_KEY).toString(); -} - -void SessionManager::reportProjectLoadingProgress() -{ - d->sessionLoadingProgress(); -} - -void SessionManager::markSessionFileDirty() -{ - d->m_virginSession = false; -} - -void SessionManagerPrivate::sessionLoadingProgress() -{ - m_future.setProgressValue(m_future.progressValue() + 1); - QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); -} - -FilePaths SessionManager::projectsForSessionName(const QString &session) -{ - const FilePath fileName = sessionNameToFileName(session); - PersistentSettingsReader reader; - if (fileName.exists()) { - if (!reader.load(fileName)) { - qWarning() << "Could not restore session" << fileName.toUserOutput(); - return {}; - } - } - return transform(reader.restoreValue(QLatin1String("ProjectList")).toStringList(), - &FilePath::fromUserInput); -} - -#ifdef WITH_TESTS - -void ProjectExplorerPlugin::testSessionSwitch() -{ - QVERIFY(SessionManager::createSession("session1")); - QVERIFY(SessionManager::createSession("session2")); - QTemporaryFile cppFile("main.cpp"); - QVERIFY(cppFile.open()); - cppFile.close(); - QTemporaryFile projectFile1("XXXXXX.pro"); - QTemporaryFile projectFile2("XXXXXX.pro"); - struct SessionSpec { - SessionSpec(const QString &n, QTemporaryFile &f) : name(n), projectFile(f) {} - const QString name; - QTemporaryFile &projectFile; - }; - std::vector<SessionSpec> sessionSpecs{SessionSpec("session1", projectFile1), - SessionSpec("session2", projectFile2)}; - for (const SessionSpec &sessionSpec : sessionSpecs) { - static const QByteArray proFileContents - = "TEMPLATE = app\n" - "CONFIG -= qt\n" - "SOURCES = " + cppFile.fileName().toLocal8Bit(); - QVERIFY(sessionSpec.projectFile.open()); - sessionSpec.projectFile.write(proFileContents); - sessionSpec.projectFile.close(); - QVERIFY(SessionManager::loadSession(sessionSpec.name)); - const OpenProjectResult openResult - = ProjectExplorerPlugin::openProject( - FilePath::fromString(sessionSpec.projectFile.fileName())); - if (openResult.errorMessage().contains("text/plain")) - QSKIP("This test requires the presence of QmakeProjectManager to be fully functional"); - QVERIFY(openResult); - QCOMPARE(openResult.projects().count(), 1); - QVERIFY(openResult.project()); - QCOMPARE(SessionManager::projects().count(), 1); - } - for (int i = 0; i < 30; ++i) { - QVERIFY(SessionManager::loadSession("session1")); - QCOMPARE(SessionManager::activeSession(), "session1"); - QCOMPARE(SessionManager::projects().count(), 1); - QVERIFY(SessionManager::loadSession("session2")); - QCOMPARE(SessionManager::activeSession(), "session2"); - QCOMPARE(SessionManager::projects().count(), 1); - } - QVERIFY(SessionManager::loadSession("session1")); - SessionManager::closeAllProjects(); - QVERIFY(SessionManager::loadSession("session2")); - SessionManager::closeAllProjects(); - QVERIFY(SessionManager::deleteSession("session1")); - QVERIFY(SessionManager::deleteSession("session2")); -} - -#endif // WITH_TESTS - -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/session.h b/src/plugins/projectexplorer/session.h deleted file mode 100644 index a29e478046..0000000000 --- a/src/plugins/projectexplorer/session.h +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "projectexplorer_export.h" - -#include <utils/id.h> -#include <utils/persistentsettings.h> - -#include <QDateTime> -#include <QString> -#include <QStringList> - -namespace Core { class IEditor; } - -namespace ProjectExplorer { - -class Project; -class Target; -class BuildConfiguration; -class BuildSystem; -class DeployConfiguration; -class RunConfiguration; - -enum class SetActive { Cascade, NoCascade }; - -class PROJECTEXPLORER_EXPORT SessionManager : public QObject -{ - Q_OBJECT - -public: - explicit SessionManager(QObject *parent = nullptr); - ~SessionManager() override; - - static SessionManager *instance(); - - // higher level session management - static QString activeSession(); - static QString lastSession(); - static QString startupSession(); - static QStringList sessions(); - static QDateTime sessionDateTime(const QString &session); - static QDateTime lastActiveTime(const QString &session); - - static bool createSession(const QString &session); - - static bool confirmSessionDelete(const QStringList &sessions); - static bool deleteSession(const QString &session); - static void deleteSessions(const QStringList &sessions); - - static bool cloneSession(const QString &original, const QString &clone); - static bool renameSession(const QString &original, const QString &newName); - - static bool loadSession(const QString &session, bool initial = false); - - static bool save(); - static void closeAllProjects(); - - static void addProject(Project *project); - static void removeProject(Project *project); - static void removeProjects(const QList<Project *> &remove); - - static void setStartupProject(Project *startupProject); - - static QList<Project *> dependencies(const Project *project); - static bool hasDependency(const Project *project, const Project *depProject); - static bool canAddDependency(const Project *project, const Project *depProject); - static bool addDependency(Project *project, Project *depProject); - static void removeDependency(Project *project, Project *depProject); - - static bool isProjectConfigurationCascading(); - static void setProjectConfigurationCascading(bool b); - - static void setActiveTarget(Project *p, Target *t, SetActive cascade); - static void setActiveBuildConfiguration(Target *t, BuildConfiguration *bc, SetActive cascade); - static void setActiveDeployConfiguration(Target *t, DeployConfiguration *dc, SetActive cascade); - - static Utils::FilePath sessionNameToFileName(const QString &session); - static Project *startupProject(); - static Target *startupTarget(); - static BuildSystem *startupBuildSystem(); - static RunConfiguration *startupRunConfiguration(); - - static const QList<Project *> projects(); - static bool hasProjects(); - static bool hasProject(Project *p); - - static bool isDefaultVirgin(); - static bool isDefaultSession(const QString &session); - - // Let other plugins store persistent values within the session file - static void setValue(const QString &name, const QVariant &value); - static QVariant value(const QString &name); - - // NBS rewrite projectOrder (dependency management) - static QList<Project *> projectOrder(const Project *project = nullptr); - - static Project *projectForFile(const Utils::FilePath &fileName); - static Project *projectWithProjectFilePath(const Utils::FilePath &filePath); - - static Utils::FilePaths projectsForSessionName(const QString &session); - - static void reportProjectLoadingProgress(); - static bool loadingSession(); - -signals: - void targetAdded(ProjectExplorer::Target *target); - void targetRemoved(ProjectExplorer::Target *target); - void projectAdded(ProjectExplorer::Project *project); - void aboutToRemoveProject(ProjectExplorer::Project *project); - void projectDisplayNameChanged(ProjectExplorer::Project *project); - void projectRemoved(ProjectExplorer::Project *project); - - void startupProjectChanged(ProjectExplorer::Project *project); - - void aboutToUnloadSession(QString sessionName); - void aboutToLoadSession(QString sessionName); - void sessionLoaded(QString sessionName); - void aboutToSaveSession(); - void dependencyChanged(ProjectExplorer::Project *a, ProjectExplorer::Project *b); - - void sessionRenamed(const QString &oldName, const QString &newName); - void sessionRemoved(const QString &name); - - // for tests only - void projectFinishedParsing(ProjectExplorer::Project *project); - -private: - static void saveActiveMode(Utils::Id mode); - static void configureEditor(Core::IEditor *editor, const QString &fileName); - static void markSessionFileDirty(); - static void configureEditors(Project *project); -}; - -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/sessiondialog.cpp b/src/plugins/projectexplorer/sessiondialog.cpp deleted file mode 100644 index 745a876230..0000000000 --- a/src/plugins/projectexplorer/sessiondialog.cpp +++ /dev/null @@ -1,223 +0,0 @@ -// 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 "sessiondialog.h" - -#include "projectexplorertr.h" -#include "session.h" -#include "sessionview.h" - -#include <utils/algorithm.h> -#include <utils/layoutbuilder.h> - -#include <QCheckBox> -#include <QDialogButtonBox> -#include <QInputDialog> -#include <QLabel> -#include <QPushButton> -#include <QValidator> - -namespace ProjectExplorer::Internal { - -class SessionValidator : public QValidator -{ -public: - SessionValidator(QObject *parent, const QStringList &sessions); - void fixup(QString & input) const override; - QValidator::State validate(QString & input, int & pos) const override; -private: - QStringList m_sessions; -}; - -SessionValidator::SessionValidator(QObject *parent, const QStringList &sessions) - : QValidator(parent), m_sessions(sessions) -{ -} - -QValidator::State SessionValidator::validate(QString &input, int &pos) const -{ - Q_UNUSED(pos) - - if (input.contains(QLatin1Char('/')) - || input.contains(QLatin1Char(':')) - || input.contains(QLatin1Char('\\')) - || input.contains(QLatin1Char('?')) - || input.contains(QLatin1Char('*'))) - return QValidator::Invalid; - - if (m_sessions.contains(input)) - return QValidator::Intermediate; - else - return QValidator::Acceptable; -} - -void SessionValidator::fixup(QString &input) const -{ - int i = 2; - QString copy; - do { - copy = input + QLatin1String(" (") + QString::number(i) + QLatin1Char(')'); - ++i; - } while (m_sessions.contains(copy)); - input = copy; -} - -SessionNameInputDialog::SessionNameInputDialog(QWidget *parent) - : QDialog(parent) -{ - m_newSessionLineEdit = new QLineEdit(this); - m_newSessionLineEdit->setValidator(new SessionValidator(this, SessionManager::sessions())); - - auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this); - m_okButton = buttons->button(QDialogButtonBox::Ok); - m_switchToButton = new QPushButton; - m_switchToButton->setDefault(true); - buttons->addButton(m_switchToButton, QDialogButtonBox::AcceptRole); - connect(m_switchToButton, &QPushButton::clicked, this, [this] { - m_usedSwitchTo = true; - }); - - using namespace Utils::Layouting; - Column { - Tr::tr("Enter the name of the session:"), - m_newSessionLineEdit, - buttons, - }.attachTo(this); - - connect(m_newSessionLineEdit, &QLineEdit::textChanged, [this](const QString &text) { - m_okButton->setEnabled(!text.isEmpty()); - m_switchToButton->setEnabled(!text.isEmpty()); - }); - m_okButton->setEnabled(false); - m_switchToButton->setEnabled(false); - - connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); - connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); -} - -void SessionNameInputDialog::setActionText(const QString &actionText, const QString &openActionText) -{ - m_okButton->setText(actionText); - m_switchToButton->setText(openActionText); -} - -void SessionNameInputDialog::setValue(const QString &value) -{ - m_newSessionLineEdit->setText(value); -} - -QString SessionNameInputDialog::value() const -{ - return m_newSessionLineEdit->text(); -} - -bool SessionNameInputDialog::isSwitchToRequested() const -{ - return m_usedSwitchTo; -} - -SessionDialog::SessionDialog(QWidget *parent) : QDialog(parent) -{ - setObjectName("ProjectExplorer.SessionDialog"); - resize(550, 400); - setWindowTitle(Tr::tr("Session Manager")); - - - auto sessionView = new SessionView(this); - sessionView->setObjectName("sessionView"); - sessionView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - sessionView->setActivationMode(Utils::DoubleClickActivation); - - auto createNewButton = new QPushButton(Tr::tr("&New")); - createNewButton->setObjectName("btCreateNew"); - - m_openButton = new QPushButton(Tr::tr("&Open")); - m_openButton->setObjectName("btOpen"); - m_renameButton = new QPushButton(Tr::tr("&Rename")); - m_cloneButton = new QPushButton(Tr::tr("C&lone")); - m_deleteButton = new QPushButton(Tr::tr("&Delete")); - - m_autoLoadCheckBox = new QCheckBox(Tr::tr("Restore last session on startup")); - - auto buttonBox = new QDialogButtonBox(this); - buttonBox->setStandardButtons(QDialogButtonBox::Close); - - m_openButton->setDefault(true); - - // FIXME: Simplify translator's work. - auto whatsASessionLabel = new QLabel( - Tr::tr("<a href=\"qthelp://org.qt-project.qtcreator/doc/creator-project-managing-sessions.html\">" - "What is a Session?</a>")); - whatsASessionLabel->setOpenExternalLinks(true); - - using namespace Utils::Layouting; - - Column { - Row { - sessionView, - Column { - createNewButton, - m_openButton, - m_renameButton, - m_cloneButton, - m_deleteButton, - st - } - }, - m_autoLoadCheckBox, - hr, - Row { whatsASessionLabel, buttonBox }, - }.attachTo(this); - - connect(createNewButton, &QAbstractButton::clicked, - sessionView, &SessionView::createNewSession); - connect(m_openButton, &QAbstractButton::clicked, - sessionView, &SessionView::switchToCurrentSession); - connect(m_renameButton, &QAbstractButton::clicked, - sessionView, &SessionView::renameCurrentSession); - connect(m_cloneButton, &QAbstractButton::clicked, - sessionView, &SessionView::cloneCurrentSession); - connect(m_deleteButton, &QAbstractButton::clicked, - sessionView, &SessionView::deleteSelectedSessions); - connect(sessionView, &SessionView::sessionActivated, - sessionView, &SessionView::switchToCurrentSession); - - connect(sessionView, &SessionView::sessionsSelected, - this, &SessionDialog::updateActions); - connect(sessionView, &SessionView::sessionSwitched, - this, &QDialog::reject); - - connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); - connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); -} - -void SessionDialog::setAutoLoadSession(bool check) -{ - m_autoLoadCheckBox->setChecked(check); -} - -bool SessionDialog::autoLoadSession() const -{ - return m_autoLoadCheckBox->checkState() == Qt::Checked; -} - -void SessionDialog::updateActions(const QStringList &sessions) -{ - if (sessions.isEmpty()) { - m_openButton->setEnabled(false); - m_renameButton->setEnabled(false); - m_cloneButton->setEnabled(false); - m_deleteButton->setEnabled(false); - return; - } - const bool defaultIsSelected = sessions.contains("default"); - const bool activeIsSelected = Utils::anyOf(sessions, [](const QString &session) { - return session == SessionManager::activeSession(); - }); - m_openButton->setEnabled(sessions.size() == 1); - m_renameButton->setEnabled(sessions.size() == 1 && !defaultIsSelected); - m_cloneButton->setEnabled(sessions.size() == 1); - m_deleteButton->setEnabled(!defaultIsSelected && !activeIsSelected); -} - -} // ProjectExplorer::Internal diff --git a/src/plugins/projectexplorer/sessiondialog.h b/src/plugins/projectexplorer/sessiondialog.h deleted file mode 100644 index 56cab430ea..0000000000 --- a/src/plugins/projectexplorer/sessiondialog.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <QString> -#include <QDialog> - -QT_BEGIN_NAMESPACE -class QCheckBox; -class QLineEdit; -class QPushButton; -QT_END_NAMESPACE - -namespace ProjectExplorer::Internal { - -class SessionDialog : public QDialog -{ - Q_OBJECT - -public: - explicit SessionDialog(QWidget *parent = nullptr); - - void setAutoLoadSession(bool); - bool autoLoadSession() const; - -private: - void updateActions(const QStringList &sessions); - - QPushButton *m_openButton; - QPushButton *m_renameButton; - QPushButton *m_cloneButton; - QPushButton *m_deleteButton; - QCheckBox *m_autoLoadCheckBox; -}; - -class SessionNameInputDialog : public QDialog -{ - Q_OBJECT - -public: - explicit SessionNameInputDialog(QWidget *parent); - - void setActionText(const QString &actionText, const QString &openActionText); - void setValue(const QString &value); - QString value() const; - bool isSwitchToRequested() const; - -private: - QLineEdit *m_newSessionLineEdit = nullptr; - QPushButton *m_switchToButton = nullptr; - QPushButton *m_okButton = nullptr; - bool m_usedSwitchTo = false; -}; - -} // ProjectExplorer::Internal diff --git a/src/plugins/projectexplorer/sessionmodel.cpp b/src/plugins/projectexplorer/sessionmodel.cpp deleted file mode 100644 index d8c9aa935e..0000000000 --- a/src/plugins/projectexplorer/sessionmodel.cpp +++ /dev/null @@ -1,266 +0,0 @@ -// 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 "sessionmodel.h" - -#include "projectexplorertr.h" -#include "session.h" -#include "sessiondialog.h" - -#include <coreplugin/actionmanager/actionmanager.h> - -#include <utils/algorithm.h> -#include <utils/fileutils.h> -#include <utils/stringutils.h> - -#include <QFileInfo> -#include <QDir> - -using namespace Core; -using namespace Utils; - -namespace ProjectExplorer { -namespace Internal { - -SessionModel::SessionModel(QObject *parent) - : QAbstractTableModel(parent) -{ - m_sortedSessions = SessionManager::sessions(); - connect(SessionManager::instance(), &SessionManager::sessionLoaded, - this, &SessionModel::resetSessions); -} - -int SessionModel::indexOfSession(const QString &session) -{ - return m_sortedSessions.indexOf(session); -} - -QString SessionModel::sessionAt(int row) const -{ - return m_sortedSessions.value(row, QString()); -} - -QVariant SessionModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - QVariant result; - if (orientation == Qt::Horizontal) { - switch (role) { - case Qt::DisplayRole: - switch (section) { - case 0: result = Tr::tr("Session"); - break; - case 1: result = Tr::tr("Last Modified"); - break; - } // switch (section) - break; - } // switch (role) { - } - return result; -} - -int SessionModel::columnCount(const QModelIndex &) const -{ - static int sectionCount = 0; - if (sectionCount == 0) { - // headers sections defining possible columns - while (!headerData(sectionCount, Qt::Horizontal, Qt::DisplayRole).isNull()) - sectionCount++; - } - - return sectionCount; -} - -int SessionModel::rowCount(const QModelIndex &) const -{ - return m_sortedSessions.count(); -} - -QStringList pathsToBaseNames(const FilePaths &paths) -{ - return Utils::transform(paths, &FilePath::completeBaseName); -} - -QStringList pathsWithTildeHomePath(const FilePaths &paths) -{ - return Utils::transform(paths, &FilePath::withTildeHomePath); -} - -QVariant SessionModel::data(const QModelIndex &index, int role) const -{ - QVariant result; - if (index.isValid()) { - QString sessionName = m_sortedSessions.at(index.row()); - - switch (role) { - case Qt::DisplayRole: - switch (index.column()) { - case 0: result = sessionName; - break; - case 1: result = SessionManager::sessionDateTime(sessionName); - break; - } // switch (section) - break; - case Qt::FontRole: { - QFont font; - if (SessionManager::isDefaultSession(sessionName)) - font.setItalic(true); - else - font.setItalic(false); - if (SessionManager::activeSession() == sessionName && !SessionManager::isDefaultVirgin()) - font.setBold(true); - else - font.setBold(false); - result = font; - } break; - case DefaultSessionRole: - result = SessionManager::isDefaultSession(sessionName); - break; - case LastSessionRole: - result = SessionManager::lastSession() == sessionName; - break; - case ActiveSessionRole: - result = SessionManager::activeSession() == sessionName; - break; - case ProjectsPathRole: - result = pathsWithTildeHomePath(SessionManager::projectsForSessionName(sessionName)); - break; - case ProjectsDisplayRole: - result = pathsToBaseNames(SessionManager::projectsForSessionName(sessionName)); - break; - case ShortcutRole: { - const Id sessionBase = SESSION_BASE_ID; - if (Command *cmd = ActionManager::command(sessionBase.withSuffix(index.row() + 1))) - result = cmd->keySequence().toString(QKeySequence::NativeText); - } break; - } // switch (role) - } - - return result; -} - -QHash<int, QByteArray> SessionModel::roleNames() const -{ - static const QHash<int, QByteArray> extraRoles{ - {Qt::DisplayRole, "sessionName"}, - {DefaultSessionRole, "defaultSession"}, - {ActiveSessionRole, "activeSession"}, - {LastSessionRole, "lastSession"}, - {ProjectsPathRole, "projectsPath"}, - {ProjectsDisplayRole, "projectsName"} - }; - QHash<int, QByteArray> roles = QAbstractTableModel::roleNames(); - Utils::addToHash(&roles, extraRoles); - return roles; -} - -void SessionModel::sort(int column, Qt::SortOrder order) -{ - beginResetModel(); - const auto cmp = [column, order](const QString &s1, const QString &s2) { - bool isLess; - if (column == 0) { - if (s1 == s2) - return false; - isLess = s1 < s2; - } - else { - const auto s1time = SessionManager::sessionDateTime(s1); - const auto s2time = SessionManager::sessionDateTime(s2); - if (s1time == s2time) - return false; - isLess = s1time < s2time; - } - if (order == Qt::DescendingOrder) - isLess = !isLess; - return isLess; - }; - Utils::sort(m_sortedSessions, cmp); - m_currentSortColumn = column; - m_currentSortOrder = order; - endResetModel(); -} - -bool SessionModel::isDefaultVirgin() const -{ - return SessionManager::isDefaultVirgin(); -} - -void SessionModel::resetSessions() -{ - beginResetModel(); - m_sortedSessions = SessionManager::sessions(); - endResetModel(); -} - -void SessionModel::newSession(QWidget *parent) -{ - SessionNameInputDialog sessionInputDialog(parent); - sessionInputDialog.setWindowTitle(Tr::tr("New Session Name")); - sessionInputDialog.setActionText(Tr::tr("&Create"), Tr::tr("Create and &Open")); - - runSessionNameInputDialog(&sessionInputDialog, [](const QString &newName) { - SessionManager::createSession(newName); - }); -} - -void SessionModel::cloneSession(QWidget *parent, const QString &session) -{ - SessionNameInputDialog sessionInputDialog(parent); - sessionInputDialog.setWindowTitle(Tr::tr("New Session Name")); - sessionInputDialog.setActionText(Tr::tr("&Clone"), Tr::tr("Clone and &Open")); - sessionInputDialog.setValue(session + " (2)"); - - runSessionNameInputDialog(&sessionInputDialog, [session](const QString &newName) { - SessionManager::cloneSession(session, newName); - }); -} - -void SessionModel::deleteSessions(const QStringList &sessions) -{ - if (!SessionManager::confirmSessionDelete(sessions)) - return; - beginResetModel(); - SessionManager::deleteSessions(sessions); - m_sortedSessions = SessionManager::sessions(); - sort(m_currentSortColumn, m_currentSortOrder); - endResetModel(); -} - -void SessionModel::renameSession(QWidget *parent, const QString &session) -{ - SessionNameInputDialog sessionInputDialog(parent); - sessionInputDialog.setWindowTitle(Tr::tr("Rename Session")); - sessionInputDialog.setActionText(Tr::tr("&Rename"), Tr::tr("Rename and &Open")); - sessionInputDialog.setValue(session); - - runSessionNameInputDialog(&sessionInputDialog, [session](const QString &newName) { - SessionManager::renameSession(session, newName); - }); -} - -void SessionModel::switchToSession(const QString &session) -{ - SessionManager::loadSession(session); - emit sessionSwitched(); -} - -void SessionModel::runSessionNameInputDialog(SessionNameInputDialog *sessionInputDialog, std::function<void(const QString &)> createSession) -{ - if (sessionInputDialog->exec() == QDialog::Accepted) { - QString newSession = sessionInputDialog->value(); - if (newSession.isEmpty() || SessionManager::sessions().contains(newSession)) - return; - beginResetModel(); - createSession(newSession); - m_sortedSessions = SessionManager::sessions(); - endResetModel(); - sort(m_currentSortColumn, m_currentSortOrder); - - if (sessionInputDialog->isSwitchToRequested()) - switchToSession(newSession); - emit sessionCreated(newSession); - } -} - -} // namespace Internal -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/sessionmodel.h b/src/plugins/projectexplorer/sessionmodel.h deleted file mode 100644 index 82f1994210..0000000000 --- a/src/plugins/projectexplorer/sessionmodel.h +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <QAbstractTableModel> - -#include <functional> - -namespace ProjectExplorer { -namespace Internal { - -const char SESSION_BASE_ID[] = "Welcome.OpenSession"; - -class SessionNameInputDialog; - -class SessionModel : public QAbstractTableModel -{ - Q_OBJECT - -public: - enum { - DefaultSessionRole = Qt::UserRole+1, - LastSessionRole, - ActiveSessionRole, - ProjectsPathRole, - ProjectsDisplayRole, - ShortcutRole - }; - - explicit SessionModel(QObject *parent = nullptr); - - int indexOfSession(const QString &session); - QString sessionAt(int row) const; - - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - int columnCount(const QModelIndex &parent = QModelIndex()) const override; - - QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - QVariant data(const QModelIndex &index, int role) const override; - QHash<int, QByteArray> roleNames() const override; - void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; - - Q_SCRIPTABLE bool isDefaultVirgin() const; - -signals: - void sessionSwitched(); - void sessionCreated(const QString &sessionName); - -public slots: - void resetSessions(); - void newSession(QWidget *parent); - void cloneSession(QWidget *parent, const QString &session); - void deleteSessions(const QStringList &sessions); - void renameSession(QWidget *parent, const QString &session); - void switchToSession(const QString &session); - -private: - void runSessionNameInputDialog(ProjectExplorer::Internal::SessionNameInputDialog *sessionInputDialog, std::function<void(const QString &)> createSession); - - QStringList m_sortedSessions; - int m_currentSortColumn = 0; - Qt::SortOrder m_currentSortOrder = Qt::AscendingOrder; -}; - -} // namespace Internal -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/sessionview.cpp b/src/plugins/projectexplorer/sessionview.cpp deleted file mode 100644 index e4b3b1a5d0..0000000000 --- a/src/plugins/projectexplorer/sessionview.cpp +++ /dev/null @@ -1,152 +0,0 @@ -// 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 "sessionview.h" - -#include "session.h" - -#include <utils/algorithm.h> - -#include <QHeaderView> -#include <QItemSelection> -#include <QStringList> -#include <QStyledItemDelegate> - -namespace ProjectExplorer { -namespace Internal { - -// custom item delegate class -class RemoveItemFocusDelegate : public QStyledItemDelegate -{ -public: - RemoveItemFocusDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) { - } - -protected: - void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; -}; - -void RemoveItemFocusDelegate::paint(QPainter* painter, const QStyleOptionViewItem & option, const QModelIndex &index) const -{ - QStyleOptionViewItem opt = option; - opt.state &= ~QStyle::State_HasFocus; - QStyledItemDelegate::paint(painter, opt, index); -} - -SessionView::SessionView(QWidget *parent) - : Utils::TreeView(parent) -{ - setItemDelegate(new RemoveItemFocusDelegate(this)); - setSelectionBehavior(QAbstractItemView::SelectRows); - setSelectionMode(QAbstractItemView::ExtendedSelection); - setWordWrap(false); - setRootIsDecorated(false); - setSortingEnabled(true); - - setModel(&m_sessionModel); - sortByColumn(0, Qt::AscendingOrder); - - // Ensure that the full session name is visible. - header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); - - QItemSelection firstRow(m_sessionModel.index(0,0), m_sessionModel.index( - 0, m_sessionModel.columnCount() - 1)); - selectionModel()->select(firstRow, QItemSelectionModel::QItemSelectionModel:: - SelectCurrent); - - connect(this, &Utils::TreeView::activated, [this](const QModelIndex &index){ - emit sessionActivated(m_sessionModel.sessionAt(index.row())); - }); - connect(selectionModel(), &QItemSelectionModel::selectionChanged, [this] { - emit sessionsSelected(selectedSessions()); - }); - - connect(&m_sessionModel, &SessionModel::sessionSwitched, - this, &SessionView::sessionSwitched); - connect(&m_sessionModel, &SessionModel::modelReset, - this, &SessionView::selectActiveSession); - connect(&m_sessionModel, &SessionModel::sessionCreated, - this, &SessionView::selectSession); - } - -void SessionView::createNewSession() -{ - m_sessionModel.newSession(this); -} - -void SessionView::deleteSelectedSessions() -{ - deleteSessions(selectedSessions()); -} - -void SessionView::deleteSessions(const QStringList &sessions) -{ - m_sessionModel.deleteSessions(sessions); -} - -void SessionView::cloneCurrentSession() -{ - m_sessionModel.cloneSession(this, currentSession()); -} - -void SessionView::renameCurrentSession() -{ - m_sessionModel.renameSession(this, currentSession()); -} - -void SessionView::switchToCurrentSession() -{ - m_sessionModel.switchToSession(currentSession()); -} - -QString SessionView::currentSession() -{ - return m_sessionModel.sessionAt(selectionModel()->currentIndex().row()); -} - -SessionModel *SessionView::sessionModel() -{ - return &m_sessionModel; -} - -void SessionView::selectActiveSession() -{ - selectSession(SessionManager::activeSession()); -} - -void SessionView::selectSession(const QString &sessionName) -{ - int row = m_sessionModel.indexOfSession(sessionName); - selectionModel()->setCurrentIndex(model()->index(row, 0), - QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); -} - -void SessionView::showEvent(QShowEvent *event) -{ - Utils::TreeView::showEvent(event); - selectActiveSession(); - setFocus(); -} - -void SessionView::keyPressEvent(QKeyEvent *event) -{ - if (event->key() != Qt::Key_Delete && event->key() != Qt::Key_Backspace) { - TreeView::keyPressEvent(event); - return; - } - const QStringList sessions = selectedSessions(); - if (!sessions.contains("default") && !Utils::anyOf(sessions, - [](const QString &session) { return session == SessionManager::activeSession(); })) { - deleteSessions(sessions); - } -} - -QStringList SessionView::selectedSessions() const -{ - return Utils::transform(selectionModel()->selectedRows(), [this](const QModelIndex &index) { - return m_sessionModel.sessionAt(index.row()); - }); -} - -} // namespace Internal -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/sessionview.h b/src/plugins/projectexplorer/sessionview.h deleted file mode 100644 index 656da080ea..0000000000 --- a/src/plugins/projectexplorer/sessionview.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "sessionmodel.h" - -#include <utils/itemviews.h> - -#include <QAbstractTableModel> - -namespace ProjectExplorer::Internal { - -class SessionView : public Utils::TreeView -{ - Q_OBJECT - -public: - explicit SessionView(QWidget *parent = nullptr); - - void createNewSession(); - void deleteSelectedSessions(); - void cloneCurrentSession(); - void renameCurrentSession(); - void switchToCurrentSession(); - - QString currentSession(); - SessionModel* sessionModel(); - void selectActiveSession(); - void selectSession(const QString &sessionName); - -signals: - void sessionActivated(const QString &session); - void sessionsSelected(const QStringList &sessions); - void sessionSwitched(); - -private: - void showEvent(QShowEvent* event) override; - void keyPressEvent(QKeyEvent *event) override; - - void deleteSessions(const QStringList &sessions); - QStringList selectedSessions() const; - - SessionModel m_sessionModel; -}; - -} // ProjectExplorer::Internal diff --git a/src/plugins/projectexplorer/target.cpp b/src/plugins/projectexplorer/target.cpp index 8f227512ee..d2e59aab06 100644 --- a/src/plugins/projectexplorer/target.cpp +++ b/src/plugins/projectexplorer/target.cpp @@ -21,8 +21,8 @@ #include "projectexplorericons.h" #include "projectexplorersettings.h" #include "projectexplorertr.h" +#include "projectmanager.h" #include "runconfiguration.h" -#include "session.h" #include <coreplugin/coreconstants.h> @@ -120,10 +120,10 @@ Target::Target(Project *project, Kit *k, _constructor_tag) : }); connect(this, &Target::parsingFinished, this, [this, project](bool success) { - if (success && this == SessionManager::startupTarget()) + if (success && this == ProjectManager::startupTarget()) updateDefaultRunConfigurations(); // For testing. - emit SessionManager::instance()->projectFinishedParsing(project); + emit ProjectManager::instance()->projectFinishedParsing(project); emit project->anyParsingFinished(this, success); }, Qt::QueuedConnection); // Must wait for run configs to change their enabled state. @@ -242,6 +242,70 @@ QString Target::activeBuildKey() const return d->m_activeRunConfiguration->buildKey(); } +void Target::setActiveBuildConfiguration(BuildConfiguration *bc, SetActive cascade) +{ + QTC_ASSERT(project(), return); + + if (project()->isShuttingDown() || isShuttingDown()) + return; + + setActiveBuildConfiguration(bc); + + if (!bc) + return; + if (cascade != SetActive::Cascade || !ProjectManager::isProjectConfigurationCascading()) + return; + + Id kitId = kit()->id(); + QString name = bc->displayName(); // We match on displayname + for (Project *otherProject : ProjectManager::projects()) { + if (otherProject == project()) + continue; + Target *otherTarget = otherProject->activeTarget(); + if (!otherTarget || otherTarget->kit()->id() != kitId) + continue; + + for (BuildConfiguration *otherBc : otherTarget->buildConfigurations()) { + if (otherBc->displayName() == name) { + otherTarget->setActiveBuildConfiguration(otherBc); + break; + } + } + } +} + +void Target::setActiveDeployConfiguration(DeployConfiguration *dc, SetActive cascade) +{ + QTC_ASSERT(project(), return); + + if (project()->isShuttingDown() || isShuttingDown()) + return; + + setActiveDeployConfiguration(dc); + + if (!dc) + return; + if (cascade != SetActive::Cascade || !ProjectManager::isProjectConfigurationCascading()) + return; + + Id kitId = kit()->id(); + QString name = dc->displayName(); // We match on displayname + for (Project *otherProject : ProjectManager::projects()) { + if (otherProject == project()) + continue; + Target *otherTarget = otherProject->activeTarget(); + if (!otherTarget || otherTarget->kit()->id() != kitId) + continue; + + for (DeployConfiguration *otherDc : otherTarget->deployConfigurations()) { + if (otherDc->displayName() == name) { + otherTarget->setActiveDeployConfiguration(otherDc); + break; + } + } + } +} + Utils::Id Target::id() const { return d->m_kit->id(); @@ -307,9 +371,9 @@ bool Target::removeBuildConfiguration(BuildConfiguration *bc) if (activeBuildConfiguration() == bc) { if (d->m_buildConfigurations.isEmpty()) - SessionManager::setActiveBuildConfiguration(this, nullptr, SetActive::Cascade); + setActiveBuildConfiguration(nullptr, SetActive::Cascade); else - SessionManager::setActiveBuildConfiguration(this, d->m_buildConfigurations.at(0), SetActive::Cascade); + setActiveBuildConfiguration(d->m_buildConfigurations.at(0), SetActive::Cascade); } emit removedBuildConfiguration(bc); @@ -377,10 +441,9 @@ bool Target::removeDeployConfiguration(DeployConfiguration *dc) if (activeDeployConfiguration() == dc) { if (d->m_deployConfigurations.isEmpty()) - SessionManager::setActiveDeployConfiguration(this, nullptr, SetActive::Cascade); + setActiveDeployConfiguration(nullptr, SetActive::Cascade); else - SessionManager::setActiveDeployConfiguration(this, d->m_deployConfigurations.at(0), - SetActive::Cascade); + setActiveDeployConfiguration(d->m_deployConfigurations.at(0), SetActive::Cascade); } ProjectExplorerPlugin::targetSelector()->removedDeployConfiguration(dc); diff --git a/src/plugins/projectexplorer/target.h b/src/plugins/projectexplorer/target.h index 78f0b5f3b5..aeca2fd9e0 100644 --- a/src/plugins/projectexplorer/target.h +++ b/src/plugins/projectexplorer/target.h @@ -26,11 +26,13 @@ class Project; class ProjectConfigurationModel; class RunConfiguration; +enum class SetActive : int { Cascade, NoCascade }; + class TargetPrivate; class PROJECTEXPLORER_EXPORT Target : public QObject { - friend class SessionManager; // for setActiveBuild and setActiveDeployConfiguration + friend class ProjectManager; // for setActiveBuild and setActiveDeployConfiguration Q_OBJECT public: @@ -109,6 +111,9 @@ public: QString activeBuildKey() const; // Build key of active run configuaration + void setActiveBuildConfiguration(BuildConfiguration *bc, SetActive cascade); + void setActiveDeployConfiguration(DeployConfiguration *dc, SetActive cascade); + signals: void targetEnabled(bool); void iconChanged(); diff --git a/src/plugins/projectexplorer/targetsettingspanel.cpp b/src/plugins/projectexplorer/targetsettingspanel.cpp index 6d8c81e634..d2fdc3b43a 100644 --- a/src/plugins/projectexplorer/targetsettingspanel.cpp +++ b/src/plugins/projectexplorer/targetsettingspanel.cpp @@ -12,9 +12,9 @@ #include "project.h" #include "projectexplorericons.h" #include "projectexplorertr.h" +#include "projectmanager.h" #include "projectwindow.h" #include "runsettingspropertiespage.h" -#include "session.h" #include "target.h" #include "targetsetuppage.h" #include "task.h" @@ -281,7 +281,7 @@ public: QFont font = parent()->data(column, role).value<QFont>(); if (TargetItem *targetItem = parent()->currentTargetItem()) { Target *t = targetItem->target(); - if (t && t->id() == m_kitId && m_project == SessionManager::startupProject()) + if (t && t->id() == m_kitId && m_project == ProjectManager::startupProject()) font.setBold(true); } return font; @@ -334,7 +334,7 @@ public: // Go to Run page, when on Run previously etc. TargetItem *previousItem = parent()->currentTargetItem(); m_currentChild = previousItem ? previousItem->m_currentChild : DefaultPage; - SessionManager::setActiveTarget(m_project, target(), SetActive::Cascade); + m_project->setActiveTarget(target(), SetActive::Cascade); parent()->setData(column, QVariant::fromValue(static_cast<TreeItem *>(this)), ItemActivatedFromBelowRole); } @@ -346,7 +346,7 @@ public: int child = indexOf(data.value<TreeItem *>()); QTC_ASSERT(child != -1, return false); m_currentChild = child; // Triggered from sub-item. - SessionManager::setActiveTarget(m_project, target(), SetActive::Cascade); + m_project->setActiveTarget(target(), SetActive::Cascade); // Propagate Build/Run selection up. parent()->setData(column, QVariant::fromValue(static_cast<TreeItem *>(this)), ItemActivatedFromBelowRole); @@ -355,7 +355,7 @@ public: if (role == ItemActivatedFromAboveRole) { // Usually programmatic activation, e.g. after opening the Project mode. - SessionManager::setActiveTarget(m_project, target(), SetActive::Cascade); + m_project->setActiveTarget(target(), SetActive::Cascade); return true; } return false; @@ -377,7 +377,7 @@ public: = menu->addAction(Tr::tr("Enable Kit for All Projects")); enableForAllAction->setEnabled(isSelectable); QObject::connect(enableForAllAction, &QAction::triggered, [kit] { - for (Project * const p : SessionManager::projects()) { + for (Project * const p : ProjectManager::projects()) { if (!p->target(kit)) p->addTargetForKit(kit); } @@ -411,7 +411,7 @@ public: QAction *disableForAllAction = menu->addAction(Tr::tr("Disable Kit for All Projects")); disableForAllAction->setEnabled(isSelectable); QObject::connect(disableForAllAction, &QAction::triggered, [kit] { - for (Project * const p : SessionManager::projects()) { + for (Project * const p : ProjectManager::projects()) { Target * const t = p->target(kit); if (!t) continue; diff --git a/src/plugins/projectexplorer/targetsetuppage.cpp b/src/plugins/projectexplorer/targetsetuppage.cpp index a17aa58c94..fd1e8a78c6 100644 --- a/src/plugins/projectexplorer/targetsetuppage.cpp +++ b/src/plugins/projectexplorer/targetsetuppage.cpp @@ -11,18 +11,17 @@ #include "project.h" #include "projectexplorerconstants.h" #include "projectexplorertr.h" -#include "session.h" #include "target.h" #include "targetsetupwidget.h" #include "task.h" #include <coreplugin/icore.h> -#include <utils/qtcassert.h> -#include <utils/qtcprocess.h> -#include <utils/wizard.h> #include <utils/algorithm.h> #include <utils/fancylineedit.h> +#include <utils/process.h> +#include <utils/qtcassert.h> +#include <utils/wizard.h> #include <QApplication> #include <QCheckBox> @@ -658,7 +657,7 @@ bool TargetSetupPage::setupProject(Project *project) if (m_importer) activeTarget = m_importer->preferredTarget(project->targets()); if (activeTarget) - SessionManager::setActiveTarget(project, activeTarget, SetActive::NoCascade); + project->setActiveTarget(activeTarget, SetActive::NoCascade); return true; } diff --git a/src/plugins/projectexplorer/targetsetupwidget.cpp b/src/plugins/projectexplorer/targetsetupwidget.cpp index cae28fc3e1..63b61401d5 100644 --- a/src/plugins/projectexplorer/targetsetupwidget.cpp +++ b/src/plugins/projectexplorer/targetsetupwidget.cpp @@ -180,10 +180,8 @@ void TargetSetupWidget::manageKit() if (!m_kit) return; - if (auto kitPage = KitOptionsPage::instance()) { - kitPage->showKit(m_kit); - Core::ICore::showOptionsDialog(Constants::KITS_SETTINGS_PAGE_ID, parentWidget()); - } + KitOptionsPage::showKit(m_kit); + Core::ICore::showOptionsDialog(Constants::KITS_SETTINGS_PAGE_ID, parentWidget()); } void TargetSetupWidget::setProjectPath(const FilePath &projectPath) diff --git a/src/plugins/projectexplorer/task.cpp b/src/plugins/projectexplorer/task.cpp index dab4c7ed17..03c2a282be 100644 --- a/src/plugins/projectexplorer/task.cpp +++ b/src/plugins/projectexplorer/task.cpp @@ -8,8 +8,8 @@ #include "projectexplorertr.h" #include <app/app_version.h> +#include <texteditor/fontsettings.h> #include <texteditor/textmark.h> - #include <utils/algorithm.h> #include <utils/utilsicons.h> #include <utils/qtcassert.h> @@ -105,11 +105,16 @@ void Task::setFile(const Utils::FilePath &file_) } } -QString Task::description() const +QString Task::description(DescriptionTags tags) const { - QString desc = summary; - if (!details.isEmpty()) - desc.append('\n').append(details.join('\n')); + QString desc; + if (tags & WithSummary) + desc = summary; + if (!details.isEmpty()) { + if (!desc.isEmpty()) + desc.append('\n'); + desc.append(details.join('\n')); + } return desc; } @@ -120,6 +125,39 @@ QIcon Task::icon() const return m_icon; } +QString Task::formattedDescription(DescriptionTags tags, const QString &extraHeading) const +{ + if (isNull()) + return {}; + + QString text = description(tags); + const int offset = (tags & WithSummary) ? 0 : summary.size() + 1; + static const QString linkTagStartPlaceholder("__QTC_LINK_TAG_START__"); + static const QString linkTagEndPlaceholder("__QTC_LINK_TAG_END__"); + static const QString linkEndPlaceholder("__QTC_LINK_END__"); + if (tags & WithLinks) { + for (auto formatRange = formats.crbegin(); formatRange != formats.crend(); ++formatRange) { + if (!formatRange->format.isAnchor()) + continue; + text.insert(formatRange->start - offset + formatRange->length, linkEndPlaceholder); + text.insert(formatRange->start - offset, QString::fromLatin1("%1%2%3").arg( + linkTagStartPlaceholder, formatRange->format.anchorHref(), linkTagEndPlaceholder)); + } + } + text = text.toHtmlEscaped(); + if (tags & WithLinks) { + text.replace(linkEndPlaceholder, "</a>"); + text.replace(linkTagStartPlaceholder, "<a href=\""); + text.replace(linkTagEndPlaceholder, "\">"); + } + const QString htmlExtraHeading = extraHeading.isEmpty() + ? QString() + : QString::fromUtf8("<b>%1</b><br/>").arg(extraHeading); + return QString::fromUtf8("<html><body>%1<code style=\"white-space:pre;font-family:%2\">" + "%3</code></body></html>") + .arg(htmlExtraHeading, TextEditor::FontSettings::defaultFixedFontFamily(), text); +} + // // functions // diff --git a/src/plugins/projectexplorer/task.h b/src/plugins/projectexplorer/task.h index f38302f90f..830cea7939 100644 --- a/src/plugins/projectexplorer/task.h +++ b/src/plugins/projectexplorer/task.h @@ -38,6 +38,9 @@ public: }; using Options = char; + enum DescriptionTag { WithSummary = 1, WithLinks = 2 }; + using DescriptionTags = QFlags<DescriptionTag>; + Task() = default; Task(TaskType type, const QString &description, const Utils::FilePath &file, int line, Utils::Id category, @@ -49,8 +52,9 @@ public: bool isNull() const; void clear(); void setFile(const Utils::FilePath &file); - QString description() const; + QString description(DescriptionTags tags = WithSummary) const; QIcon icon() const; + QString formattedDescription(DescriptionTags tags, const QString &extraHeading = {}) const; friend PROJECTEXPLORER_EXPORT bool operator==(const Task &t1, const Task &t2); friend PROJECTEXPLORER_EXPORT bool operator<(const Task &a, const Task &b); diff --git a/src/plugins/projectexplorer/taskfile.cpp b/src/plugins/projectexplorer/taskfile.cpp index 6375d12ee7..c967521d89 100644 --- a/src/plugins/projectexplorer/taskfile.cpp +++ b/src/plugins/projectexplorer/taskfile.cpp @@ -6,20 +6,22 @@ #include "projectexplorer.h" #include "projectexplorerconstants.h" #include "projectexplorertr.h" -#include "session.h" #include "taskhub.h" #include <coreplugin/documentmanager.h> #include <coreplugin/icore.h> +#include <coreplugin/messagemanager.h> +#include <coreplugin/session.h> #include <utils/algorithm.h> #include <utils/filepath.h> +#include <utils/process.h> #include <utils/qtcassert.h> -#include <utils/qtcprocess.h> #include <QAction> #include <QMessageBox> +using namespace Core; using namespace Utils; namespace ProjectExplorer { @@ -147,6 +149,10 @@ static bool parseTaskFile(QString *errorString, const FilePath &name) } description = unescape(description); + if (description.trimmed().isEmpty()) { + MessageManager::writeFlashing(Tr::tr("Ignoring invalid task (no text).")); + continue; + } TaskHub::addTask(Task(type, description, FilePath::fromUserInput(file), line, Constants::TASK_CATEGORY_TASKLIST_ID)); } diff --git a/src/plugins/projectexplorer/taskhub.cpp b/src/plugins/projectexplorer/taskhub.cpp index da839e89f1..58e797de80 100644 --- a/src/plugins/projectexplorer/taskhub.cpp +++ b/src/plugins/projectexplorer/taskhub.cpp @@ -51,13 +51,9 @@ public: : Tr::tr("Warning")); setPriority(task.type == Task::Error ? TextEditor::TextMark::NormalPriority : TextEditor::TextMark::LowPriority); - if (task.category == Constants::TASK_CATEGORY_COMPILE) { - setToolTip("<html><body><b>" + Tr::tr("Build Issue") - + "</b><br/><code style=\"white-space:pre;font-family:monospace\">" - + task.description().toHtmlEscaped() + "</code></body></html>"); - } else { - setToolTip(task.description()); - } + setToolTip(task.formattedDescription({Task::WithSummary | Task::WithLinks}, + task.category == Constants::TASK_CATEGORY_COMPILE + ? Tr::tr("Build Issue") : QString())); setIcon(task.icon()); setVisible(!task.icon().isNull()); } diff --git a/src/plugins/projectexplorer/taskmodel.cpp b/src/plugins/projectexplorer/taskmodel.cpp index b8f7d72306..11591e707d 100644 --- a/src/plugins/projectexplorer/taskmodel.cpp +++ b/src/plugins/projectexplorer/taskmodel.cpp @@ -210,58 +210,82 @@ void TaskModel::clearTasks(Utils::Id categoryId) QModelIndex TaskModel::index(int row, int column, const QModelIndex &parent) const { if (parent.isValid()) - return QModelIndex(); + return createIndex(row, column, quintptr(parent.row() + 1)); return createIndex(row, column); } QModelIndex TaskModel::parent(const QModelIndex &child) const { - Q_UNUSED(child) - return QModelIndex(); + if (child.internalId()) + return index(child.internalId() - 1, 0); + return {}; } int TaskModel::rowCount(const QModelIndex &parent) const { - return parent.isValid() ? 0 : m_tasks.count(); + if (!parent.isValid()) + return m_tasks.count(); + if (parent.column() != 0) + return 0; + return task(parent).details.isEmpty() ? 0 : 1; } int TaskModel::columnCount(const QModelIndex &parent) const { - return parent.isValid() ? 0 : 1; + return parent.isValid() ? 1 : 2; } QVariant TaskModel::data(const QModelIndex &index, int role) const { - int row = index.row(); - if (!index.isValid() || row < 0 || row >= m_tasks.count() || index.column() != 0) - return QVariant(); - - if (role == TaskModel::File) - return m_tasks.at(index.row()).file.toString(); - else if (role == TaskModel::Line) - return m_tasks.at(index.row()).line; - else if (role == TaskModel::MovedLine) - return m_tasks.at(index.row()).movedLine; - else if (role == TaskModel::Description) - return m_tasks.at(index.row()).description(); - else if (role == TaskModel::FileNotFound) - return m_fileNotFound.value(m_tasks.at(index.row()).file.toString()); - else if (role == TaskModel::Type) - return (int)m_tasks.at(index.row()).type; - else if (role == TaskModel::Category) - return m_tasks.at(index.row()).category.uniqueIdentifier(); - else if (role == TaskModel::Icon) - return m_tasks.at(index.row()).icon(); - else if (role == TaskModel::Task_t) - return QVariant::fromValue(task(index)); - return QVariant(); + if (!index.isValid() || index.row() < 0 || index.row() >= rowCount(index.parent()) + || index.column() >= columnCount(index.parent())) { + return {}; + } + + if (index.internalId()) { + const Task &task = m_tasks.at(index.internalId() - 1); + if (role != Qt::DisplayRole) + return {}; + return task.formattedDescription(Task::WithLinks); + } + + static const auto lineString = [](const Task &task) { + QString file = task.file.fileName(); + const int line = task.movedLine > 0 ? task.movedLine : task.line; + if (line > 0) + file.append(':').append(QString::number(line)); + return file; + }; + + const Task &task = m_tasks.at(index.row()); + if (index.column() == 1) { + if (role == Qt::DisplayRole) + return lineString(task); + if (role == Qt::ToolTipRole) + return task.file.toUserOutput(); + return {}; + } + + switch (role) { + case Qt::DecorationRole: + return task.icon(); + case Qt::DisplayRole: + return task.summary; + case TaskModel::Description: + return task.description(); + case TaskModel::Type: + return int(task.type); + } + return {}; } Task TaskModel::task(const QModelIndex &index) const { int row = index.row(); - if (!index.isValid() || row < 0 || row >= m_tasks.count()) + if (!index.isValid() || row < 0 || row >= m_tasks.count() || index.internalId() + || index.column() > 0) { return Task(); + } return m_tasks.at(row); } @@ -387,7 +411,8 @@ void TaskFilterModel::updateFilterProperties( bool TaskFilterModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { - Q_UNUSED(source_parent) + if (source_parent.isValid()) + return true; return filterAcceptsTask(taskModel()->tasks().at(source_row)); } diff --git a/src/plugins/projectexplorer/taskmodel.h b/src/plugins/projectexplorer/taskmodel.h index e10833f613..5fa37c02d1 100644 --- a/src/plugins/projectexplorer/taskmodel.h +++ b/src/plugins/projectexplorer/taskmodel.h @@ -44,7 +44,7 @@ public: int sizeOfLineNumber(const QFont &font); void setFileNotFound(const QModelIndex &index, bool b); - enum Roles { File = Qt::UserRole, Line, MovedLine, Description, FileNotFound, Type, Category, Icon, Task_t }; + enum Roles { Description = Qt::UserRole, Type}; int taskCount(Utils::Id categoryId); int errorTaskCount(Utils::Id categoryId); diff --git a/src/plugins/projectexplorer/taskwindow.cpp b/src/plugins/projectexplorer/taskwindow.cpp index 52e3899d65..9ae93c0bbf 100644 --- a/src/plugins/projectexplorer/taskwindow.cpp +++ b/src/plugins/projectexplorer/taskwindow.cpp @@ -6,7 +6,6 @@ #include "itaskhandler.h" #include "projectexplorericons.h" #include "projectexplorertr.h" -#include "session.h" #include "task.h" #include "taskhub.h" #include "taskmodel.h" @@ -17,32 +16,38 @@ #include <coreplugin/actionmanager/command.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/find/itemviewfind.h> -#include <coreplugin/icore.h> #include <coreplugin/icontext.h> +#include <coreplugin/icore.h> +#include <coreplugin/session.h> #include <utils/algorithm.h> #include <utils/fileinprojectfinder.h> +#include <utils/hostosinfo.h> #include <utils/itemviews.h> #include <utils/outputformatter.h> #include <utils/qtcassert.h> #include <utils/stylehelper.h> #include <utils/theme/theme.h> +#include <utils/tooltip/tooltip.h> #include <utils/utilsicons.h> +#include <QAbstractTextDocumentLayout> +#include <QApplication> #include <QDir> +#include <QLabel> +#include <QMenu> #include <QPainter> +#include <QScrollBar> #include <QStyledItemDelegate> -#include <QMenu> +#include <QTextDocument> #include <QToolButton> -#include <QScrollBar> +#include <QVBoxLayout> +using namespace Core; using namespace Utils; -namespace { -const int ELLIPSIS_GRADIENT_WIDTH = 16; const char SESSION_FILTER_CATEGORIES[] = "TaskWindow.Categories"; const char SESSION_FILTER_WARNINGS[] = "TaskWindow.IncludeWarnings"; -} namespace ProjectExplorer { @@ -84,195 +89,42 @@ bool ITaskHandler::canHandle(const Tasks &tasks) const namespace Internal { -class TaskView : public ListView +class TaskView : public TreeView { public: - TaskView(QWidget *parent = nullptr); - ~TaskView() override; + TaskView(); + void resizeColumns(); private: void resizeEvent(QResizeEvent *e) override; + void keyReleaseEvent(QKeyEvent *e) override; + bool event(QEvent *e) override; void mousePressEvent(QMouseEvent *e) override; - void mouseReleaseEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; - Link locationForPos(const QPoint &pos); + QString anchorAt(const QPoint &pos); + void showToolTip(const Task &task, const QPoint &pos); - bool m_linksActive = true; - Qt::MouseButton m_mouseButtonPressed = Qt::NoButton; + QString m_hoverAnchor; + QString m_clickAnchor; }; class TaskDelegate : public QStyledItemDelegate { - Q_OBJECT - - friend class TaskView; // for using Positions::minimumSize() - public: - TaskDelegate(QObject * parent = nullptr); - ~TaskDelegate() override; - void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; - QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; - - // TaskView uses this method if the size of the taskview changes - void emitSizeHintChanged(const QModelIndex &index); - - void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); - - QString hrefForPos(const QPointF &pos); + using QStyledItemDelegate::QStyledItemDelegate; + QTextDocument &doc() { return m_doc; } private: - void generateGradientPixmap(int width, int height, QColor color, bool selected) const; - - mutable int m_cachedHeight = 0; - mutable QFont m_cachedFont; - mutable QList<QPair<QRectF, QString>> m_hrefs; - - /* - Collapsed: - +----------------------------------------------------------------------------------------------------+ - | TASKICONAREA TEXTAREA FILEAREA LINEAREA | - +----------------------------------------------------------------------------------------------------+ - - Expanded: - +----------------------------------------------------------------------------------------------------+ - | TASKICONICON TEXTAREA FILEAREA LINEAREA | - | more text -------------------------------------------------------------------------> | - +----------------------------------------------------------------------------------------------------+ - */ - class Positions - { - public: - Positions(const QStyleOptionViewItem &options, TaskModel *model) : - m_totalWidth(options.rect.width()), - m_maxFileLength(model->sizeOfFile(options.font)), - m_maxLineLength(model->sizeOfLineNumber(options.font)), - m_realFileLength(m_maxFileLength), - m_top(options.rect.top()), - m_bottom(options.rect.bottom()) - { - int flexibleArea = lineAreaLeft() - textAreaLeft() - ITEM_SPACING; - if (m_maxFileLength > flexibleArea / 2) - m_realFileLength = flexibleArea / 2; - m_fontHeight = QFontMetrics(options.font).height(); - } - - int top() const { return m_top + ITEM_MARGIN; } - int left() const { return ITEM_MARGIN; } - int right() const { return m_totalWidth - ITEM_MARGIN; } - int bottom() const { return m_bottom; } - int firstLineHeight() const { return m_fontHeight + 1; } - static int minimumHeight() { return taskIconHeight() + 2 * ITEM_MARGIN; } - - int taskIconLeft() const { return left(); } - static int taskIconWidth() { return TASK_ICON_SIZE; } - static int taskIconHeight() { return TASK_ICON_SIZE; } - int taskIconRight() const { return taskIconLeft() + taskIconWidth(); } - QRect taskIcon() const { return QRect(taskIconLeft(), top(), taskIconWidth(), taskIconHeight()); } - - int textAreaLeft() const { return taskIconRight() + ITEM_SPACING; } - int textAreaWidth() const { return textAreaRight() - textAreaLeft(); } - int textAreaRight() const { return fileAreaLeft() - ITEM_SPACING; } - QRect textArea() const { return QRect(textAreaLeft(), top(), textAreaWidth(), firstLineHeight()); } - - int fileAreaLeft() const { return fileAreaRight() - fileAreaWidth(); } - int fileAreaWidth() const { return m_realFileLength; } - int fileAreaRight() const { return lineAreaLeft() - ITEM_SPACING; } - QRect fileArea() const { return QRect(fileAreaLeft(), top(), fileAreaWidth(), firstLineHeight()); } - - int lineAreaLeft() const { return lineAreaRight() - lineAreaWidth(); } - int lineAreaWidth() const { return m_maxLineLength; } - int lineAreaRight() const { return right(); } - QRect lineArea() const { return QRect(lineAreaLeft(), top(), lineAreaWidth(), firstLineHeight()); } - - private: - int m_totalWidth; - int m_maxFileLength; - int m_maxLineLength; - int m_realFileLength; - int m_top; - int m_bottom; - int m_fontHeight; - - static const int TASK_ICON_SIZE = 16; - static const int ITEM_MARGIN = 2; - static const int ITEM_SPACING = 2 * ITEM_MARGIN; - }; -}; - -TaskView::TaskView(QWidget *parent) - : ListView(parent) -{ - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); - setMouseTracking(true); - setAutoScroll(false); // QTCREATORBUG-25101 - - QFontMetrics fm(font()); - int vStepSize = fm.height() + 3; - if (vStepSize < TaskDelegate::Positions::minimumHeight()) - vStepSize = TaskDelegate::Positions::minimumHeight(); - - verticalScrollBar()->setSingleStep(vStepSize); -} - -TaskView::~TaskView() = default; - -void TaskView::resizeEvent(QResizeEvent *e) -{ - Q_UNUSED(e) - static_cast<TaskDelegate *>(itemDelegate())->emitSizeHintChanged(selectionModel()->currentIndex()); -} - -void TaskView::mousePressEvent(QMouseEvent *e) -{ - m_mouseButtonPressed = e->button(); - ListView::mousePressEvent(e); -} - -void TaskView::mouseReleaseEvent(QMouseEvent *e) -{ - if (m_linksActive && m_mouseButtonPressed == Qt::LeftButton) { - const Link loc = locationForPos(e->pos()); - if (!loc.targetFilePath.isEmpty()) { - Core::EditorManager::openEditorAt(loc, {}, - Core::EditorManager::SwitchSplitIfAlreadyVisible); - } - } - - // Mouse was released, activate links again - m_linksActive = true; - m_mouseButtonPressed = Qt::NoButton; - ListView::mouseReleaseEvent(e); -} - -void TaskView::mouseMoveEvent(QMouseEvent *e) -{ - // Cursor was dragged, deactivate links - if (m_mouseButtonPressed != Qt::NoButton) - m_linksActive = false; - - viewport()->setCursor(m_linksActive && !locationForPos(e->pos()).targetFilePath.isEmpty() - ? Qt::PointingHandCursor : Qt::ArrowCursor); - ListView::mouseMoveEvent(e); -} + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; -Link TaskView::locationForPos(const QPoint &pos) -{ - const auto delegate = qobject_cast<TaskDelegate *>(itemDelegate(indexAt(pos))); - if (!delegate) - return {}; - OutputFormatter formatter; - Link loc; - connect(&formatter, &OutputFormatter::openInEditorRequested, this, [&loc](const Link &link) { - loc = link; - }); + bool needsSpecialHandling(const QModelIndex &index) const; - const QString href = delegate->hrefForPos(pos); - if (!href.isEmpty()) - formatter.handleLink(href); - return loc; -} + mutable QTextDocument m_doc; +}; ///// // TaskWindow @@ -289,9 +141,8 @@ public: Internal::TaskModel *m_model; Internal::TaskFilterModel *m_filter; - Internal::TaskView *m_listview; + TaskView m_treeView; Core::IContext *m_taskWindowContext; - QMenu *m_contextMenu; QMap<const QAction *, ITaskHandler *> m_actionToHandlerMap; ITaskHandler *m_defaultHandler = nullptr; QToolButton *m_filterWarningsButton; @@ -318,45 +169,45 @@ TaskWindow::TaskWindow() : d(std::make_unique<TaskWindowPrivate>()) { d->m_model = new Internal::TaskModel(this); d->m_filter = new Internal::TaskFilterModel(d->m_model); - d->m_listview = new Internal::TaskView; + d->m_filter->setAutoAcceptChildRows(true); auto agg = new Aggregation::Aggregate; - agg->add(d->m_listview); - agg->add(new Core::ItemViewFind(d->m_listview, TaskModel::Description)); - - d->m_listview->setModel(d->m_filter); - d->m_listview->setFrameStyle(QFrame::NoFrame); - d->m_listview->setWindowTitle(displayName()); - d->m_listview->setSelectionMode(QAbstractItemView::ExtendedSelection); - auto *tld = new Internal::TaskDelegate(this); - d->m_listview->setItemDelegate(tld); - d->m_listview->setWindowIcon(Icons::WINDOW.icon()); - d->m_listview->setContextMenuPolicy(Qt::ActionsContextMenu); - d->m_listview->setAttribute(Qt::WA_MacShowFocusRect, false); - - d->m_taskWindowContext = new Core::IContext(d->m_listview); - d->m_taskWindowContext->setWidget(d->m_listview); + agg->add(&d->m_treeView); + agg->add(new Core::ItemViewFind(&d->m_treeView, TaskModel::Description)); + + d->m_treeView.setHeaderHidden(true); + d->m_treeView.setExpandsOnDoubleClick(false); + d->m_treeView.setAlternatingRowColors(true); + d->m_treeView.setTextElideMode(Qt::ElideMiddle); + d->m_treeView.setItemDelegate(new TaskDelegate(this)); + d->m_treeView.setModel(d->m_filter); + d->m_treeView.setFrameStyle(QFrame::NoFrame); + d->m_treeView.setWindowTitle(displayName()); + d->m_treeView.setSelectionMode(QAbstractItemView::ExtendedSelection); + d->m_treeView.setWindowIcon(Icons::WINDOW.icon()); + d->m_treeView.setContextMenuPolicy(Qt::ActionsContextMenu); + d->m_treeView.setAttribute(Qt::WA_MacShowFocusRect, false); + d->m_treeView.resizeColumns(); + + d->m_taskWindowContext = new Core::IContext(&d->m_treeView); + d->m_taskWindowContext->setWidget(&d->m_treeView); d->m_taskWindowContext->setContext(Core::Context(Core::Constants::C_PROBLEM_PANE)); Core::ICore::addContextObject(d->m_taskWindowContext); - connect(d->m_listview->selectionModel(), &QItemSelectionModel::currentChanged, - tld, &TaskDelegate::currentChanged); - connect(d->m_listview->selectionModel(), &QItemSelectionModel::currentChanged, - this, [this](const QModelIndex &index) { d->m_listview->scrollTo(index); }); - connect(d->m_listview, &QAbstractItemView::activated, + connect(d->m_treeView.selectionModel(), &QItemSelectionModel::currentChanged, + this, [this](const QModelIndex &index) { d->m_treeView.scrollTo(index); }); + connect(&d->m_treeView, &QAbstractItemView::activated, this, &TaskWindow::triggerDefaultHandler); - connect(d->m_listview->selectionModel(), &QItemSelectionModel::selectionChanged, + connect(d->m_treeView.selectionModel(), &QItemSelectionModel::selectionChanged, this, [this] { - const Tasks tasks = d->m_filter->tasks(d->m_listview->selectionModel()->selectedIndexes()); + const Tasks tasks = d->m_filter->tasks(d->m_treeView.selectionModel()->selectedIndexes()); for (QAction * const action : std::as_const(d->m_actions)) { ITaskHandler * const h = d->handler(action); action->setEnabled(h && h->canHandle(tasks)); } }); - d->m_contextMenu = new QMenu(d->m_listview); - - d->m_listview->setContextMenuPolicy(Qt::ActionsContextMenu); + d->m_treeView.setContextMenuPolicy(Qt::ActionsContextMenu); d->m_filterWarningsButton = createFilterButton( Utils::Icons::WARNING_TOOLBAR.icon(), @@ -365,7 +216,7 @@ TaskWindow::TaskWindow() : d(std::make_unique<TaskWindowPrivate>()) d->m_categoriesButton = new QToolButton; d->m_categoriesButton->setIcon(Utils::Icons::FILTER.icon()); d->m_categoriesButton->setToolTip(Tr::tr("Filter by categories")); - d->m_categoriesButton->setProperty("noArrow", true); + d->m_categoriesButton->setProperty(StyleHelper::C_NO_ARROW, true); d->m_categoriesButton->setPopupMode(QToolButton::InstantPopup); d->m_categoriesMenu = new QMenu(d->m_categoriesButton); @@ -411,7 +262,6 @@ TaskWindow::TaskWindow() : d(std::make_unique<TaskWindowPrivate>()) TaskWindow::~TaskWindow() { delete d->m_filterWarningsButton; - delete d->m_listview; delete d->m_filter; delete d->m_model; } @@ -435,7 +285,7 @@ void TaskWindow::delayedInitialization() connect(action, &QAction::triggered, this, [this, action] { ITaskHandler *h = d->handler(action); if (h) - h->handle(d->m_filter->tasks(d->m_listview->selectionModel()->selectedIndexes())); + h->handle(d->m_filter->tasks(d->m_treeView.selectionModel()->selectedIndexes())); }); d->m_actions << action; @@ -445,7 +295,7 @@ void TaskWindow::delayedInitialization() Core::ActionManager::registerAction(action, id, d->m_taskWindowContext->context(), true); action = cmd->action(); } - d->m_listview->addAction(action); + d->m_treeView.addAction(action); } } @@ -461,7 +311,7 @@ QString TaskWindow::displayName() const QWidget *TaskWindow::outputWidget(QWidget *) { - return d->m_listview; + return &d->m_treeView; } void TaskWindow::clearTasks(Id categoryId) @@ -565,7 +415,7 @@ void TaskWindow::showTask(const Task &task) int sourceRow = d->m_model->rowForTask(task); QModelIndex sourceIdx = d->m_model->index(sourceRow, 0); QModelIndex filterIdx = d->m_filter->mapFromSource(sourceIdx); - d->m_listview->setCurrentIndex(filterIdx); + d->m_treeView.setCurrentIndex(filterIdx); popup(Core::IOutputPane::ModeSwitch); } @@ -582,7 +432,10 @@ void TaskWindow::triggerDefaultHandler(const QModelIndex &index) if (!index.isValid() || !d->m_defaultHandler) return; - Task task(d->m_filter->task(index)); + QModelIndex taskIndex = index; + if (index.parent().isValid()) + taskIndex = index.parent(); + Task task(d->m_filter->task(taskIndex)); if (task.isNull()) return; @@ -599,7 +452,7 @@ void TaskWindow::triggerDefaultHandler(const QModelIndex &index) d->m_defaultHandler->handle(task); } else { if (!task.file.exists()) - d->m_model->setFileNotFound(index, true); + d->m_model->setFileNotFound(taskIndex, true); } } @@ -665,7 +518,7 @@ void TaskWindow::clearContents() bool TaskWindow::hasFocus() const { - return d->m_listview->window()->focusWidget() == d->m_listview; + return d->m_treeView.window()->focusWidget() == &d->m_treeView; } bool TaskWindow::canFocus() const @@ -676,9 +529,13 @@ bool TaskWindow::canFocus() const void TaskWindow::setFocus() { if (d->m_filter->rowCount()) { - d->m_listview->setFocus(); - if (d->m_listview->currentIndex() == QModelIndex()) - d->m_listview->setCurrentIndex(d->m_filter->index(0,0, QModelIndex())); + d->m_treeView.setFocus(); + if (!d->m_treeView.currentIndex().isValid()) + d->m_treeView.setCurrentIndex(d->m_filter->index(0,0, QModelIndex())); + if (d->m_treeView.selectionModel()->selection().isEmpty()) { + d->m_treeView.selectionModel()->setCurrentIndex(d->m_treeView.currentIndex(), + QItemSelectionModel::Select); + } } } @@ -696,7 +553,7 @@ void TaskWindow::goToNext() { if (!canNext()) return; - QModelIndex startIndex = d->m_listview->currentIndex(); + QModelIndex startIndex = d->m_treeView.currentIndex(); QModelIndex currentIndex = startIndex; if (startIndex.isValid()) { @@ -711,7 +568,7 @@ void TaskWindow::goToNext() } else { currentIndex = d->m_filter->index(0, 0); } - d->m_listview->setCurrentIndex(currentIndex); + d->m_treeView.setCurrentIndex(currentIndex); triggerDefaultHandler(currentIndex); } @@ -719,7 +576,7 @@ void TaskWindow::goToPrev() { if (!canPrevious()) return; - QModelIndex startIndex = d->m_listview->currentIndex(); + QModelIndex startIndex = d->m_treeView.currentIndex(); QModelIndex currentIndex = startIndex; if (startIndex.isValid()) { @@ -734,7 +591,7 @@ void TaskWindow::goToPrev() } else { currentIndex = d->m_filter->index(0, 0); } - d->m_listview->setCurrentIndex(currentIndex); + d->m_treeView.setCurrentIndex(currentIndex); triggerDefaultHandler(currentIndex); } @@ -749,268 +606,167 @@ bool TaskWindow::canNavigate() const return true; } -///// -// Delegate -///// - -TaskDelegate::TaskDelegate(QObject *parent) : - QStyledItemDelegate(parent) -{ } - -TaskDelegate::~TaskDelegate() = default; - -QSize TaskDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +void TaskDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const { - QStyleOptionViewItem opt = option; - initStyleOption(&opt, index); - - auto view = qobject_cast<const QAbstractItemView *>(opt.widget); - const bool current = view->selectionModel()->currentIndex() == index; - QSize s; - s.setWidth(option.rect.width()); - - if (!current && option.font == m_cachedFont && m_cachedHeight > 0) { - s.setHeight(m_cachedHeight); - return s; + if (!needsSpecialHandling(index)) { + QStyledItemDelegate::paint(painter, option, index); + return; } - QFontMetrics fm(option.font); - int fontHeight = fm.height(); - int fontLeading = fm.leading(); - - auto model = static_cast<TaskFilterModel *>(view->model())->taskModel(); - Positions positions(option, model); - - if (current) { - QString description = index.data(TaskModel::Description).toString(); - // Layout the description - int leading = fontLeading; - int height = 0; - description.replace(QLatin1Char('\n'), QChar::LineSeparator); - QTextLayout tl(description); - tl.setFormats(index.data(TaskModel::Task_t).value<Task>().formats); - tl.beginLayout(); - while (true) { - QTextLine line = tl.createLine(); - if (!line.isValid()) - break; - line.setLineWidth(positions.textAreaWidth()); - height += leading; - line.setPosition(QPoint(0, height)); - height += static_cast<int>(line.height()); - } - tl.endLayout(); + QStyleOptionViewItem options = option; + initStyleOption(&options, index); - s.setHeight(height + leading + fontHeight + 3); - } else { - s.setHeight(fontHeight + 3); + painter->save(); + m_doc.setHtml(options.text); + options.text = ""; + options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter); + painter->translate(options.rect.left(), options.rect.top()); + QRect clip(0, 0, options.rect.width(), options.rect.height()); + QAbstractTextDocumentLayout::PaintContext paintContext; + paintContext.palette = options.palette; + painter->setClipRect(clip); + paintContext.clip = clip; + if (qobject_cast<const QAbstractItemView *>(options.widget) + ->selectionModel()->isSelected(index)) { + QAbstractTextDocumentLayout::Selection selection; + selection.cursor = QTextCursor(&m_doc); + selection.cursor.select(QTextCursor::Document); + selection.format.setBackground(options.palette.brush(QPalette::Highlight)); + selection.format.setForeground(options.palette.brush(QPalette::HighlightedText)); + paintContext.selections << selection; } - if (s.height() < Positions::minimumHeight()) - s.setHeight(Positions::minimumHeight()); + m_doc.documentLayout()->draw(painter, paintContext); + painter->restore(); +} - if (!current) { - m_cachedHeight = s.height(); - m_cachedFont = option.font; - } +QSize TaskDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + if (!needsSpecialHandling(index)) + return QStyledItemDelegate::sizeHint(option, index); - return s; + QStyleOptionViewItem options = option; + initStyleOption(&options, index); + m_doc.setHtml(options.text); + m_doc.setTextWidth(options.rect.width()); + return QSize(m_doc.idealWidth(), m_doc.size().height()); } -void TaskDelegate::emitSizeHintChanged(const QModelIndex &index) +bool TaskDelegate::needsSpecialHandling(const QModelIndex &index) const { - emit sizeHintChanged(index); + QModelIndex sourceIndex = index; + if (const auto proxyModel = qobject_cast<const QAbstractProxyModel *>(index.model())) + sourceIndex = proxyModel->mapToSource(index); + return sourceIndex.internalId(); } -void TaskDelegate::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) +TaskView::TaskView() { - m_hrefs.clear(); - emit sizeHintChanged(current); - emit sizeHintChanged(previous); + setMouseTracking(true); + setVerticalScrollMode(ScrollPerPixel); } -QString TaskDelegate::hrefForPos(const QPointF &pos) +void TaskView::resizeColumns() { - for (const auto &link : std::as_const(m_hrefs)) { - if (link.first.contains(pos)) - return link.second; - } - return {}; + setColumnWidth(0, width() * 0.85); + setColumnWidth(1, width() * 0.15); } -void TaskDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +void TaskView::resizeEvent(QResizeEvent *e) { - QStyleOptionViewItem opt = option; - initStyleOption(&opt, index); - painter->save(); + TreeView::resizeEvent(e); + resizeColumns(); +} - QFontMetrics fm(opt.font); - QColor backgroundColor; - QColor textColor; +void TaskView::mousePressEvent(QMouseEvent *e) +{ + m_clickAnchor = anchorAt(e->pos()); + if (m_clickAnchor.isEmpty()) + TreeView::mousePressEvent(e); +} - auto view = qobject_cast<const QAbstractItemView *>(opt.widget); - const bool selected = view->selectionModel()->isSelected(index); - const bool current = view->selectionModel()->currentIndex() == index; +void TaskView::mouseMoveEvent(QMouseEvent *e) +{ + const QString anchor = anchorAt(e->pos()); + if (m_clickAnchor != anchor) + m_clickAnchor.clear(); + if (m_hoverAnchor != anchor) { + m_hoverAnchor = anchor; + if (!m_hoverAnchor.isEmpty()) + setCursor(Qt::PointingHandCursor); + else + unsetCursor(); + } +} - if (selected) { - painter->setBrush(opt.palette.highlight().color()); - backgroundColor = opt.palette.highlight().color(); - } else { - painter->setBrush(opt.palette.window().color()); - backgroundColor = opt.palette.window().color(); +void TaskView::mouseReleaseEvent(QMouseEvent *e) +{ + if (m_clickAnchor.isEmpty() || e->button() == Qt::RightButton) { + TreeView::mouseReleaseEvent(e); + return; } - painter->setPen(Qt::NoPen); - painter->drawRect(opt.rect); - // Set Text Color - if (selected) - textColor = opt.palette.highlightedText().color(); - else - textColor = opt.palette.text().color(); - - painter->setPen(textColor); - - auto model = static_cast<TaskFilterModel *>(view->model())->taskModel(); - Positions positions(opt, model); - - // Paint TaskIconArea: - QIcon icon = index.data(TaskModel::Icon).value<QIcon>(); - painter->drawPixmap(positions.left(), positions.top(), - icon.pixmap(Positions::taskIconWidth(), Positions::taskIconHeight())); - - // Paint TextArea: - if (!current) { - // in small mode we lay out differently - QString bottom = index.data(TaskModel::Description).toString().split(QLatin1Char('\n')).first(); - painter->setClipRect(positions.textArea()); - painter->drawText(positions.textAreaLeft(), positions.top() + fm.ascent(), bottom); - if (fm.horizontalAdvance(bottom) > positions.textAreaWidth()) { - // draw a gradient to mask the text - int gradientStart = positions.textAreaRight() - ELLIPSIS_GRADIENT_WIDTH + 1; - QLinearGradient lg(gradientStart, 0, gradientStart + ELLIPSIS_GRADIENT_WIDTH, 0); - lg.setColorAt(0, Qt::transparent); - lg.setColorAt(1, backgroundColor); - painter->fillRect(gradientStart, positions.top(), ELLIPSIS_GRADIENT_WIDTH, positions.firstLineHeight(), lg); - } - } else { - // Description - QString description = index.data(TaskModel::Description).toString(); - // Layout the description - int leading = fm.leading(); - int height = 0; - description.replace(QLatin1Char('\n'), QChar::LineSeparator); - QTextLayout tl(description); - QVector<QTextLayout::FormatRange> formats = index.data(TaskModel::Task_t).value<Task>().formats; - for (QTextLayout::FormatRange &format : formats) - format.format.setForeground(textColor); - tl.setFormats(formats); - tl.beginLayout(); - while (true) { - QTextLine line = tl.createLine(); - if (!line.isValid()) - break; - line.setLineWidth(positions.textAreaWidth()); - height += leading; - line.setPosition(QPoint(0, height)); - height += static_cast<int>(line.height()); - } - tl.endLayout(); - const QPoint indexPos = view->visualRect(index).topLeft(); - tl.draw(painter, QPoint(positions.textAreaLeft(), positions.top())); - m_hrefs.clear(); - for (const auto &range : tl.formats()) { - if (!range.format.isAnchor()) - continue; - const QTextLine &firstLinkLine = tl.lineForTextPosition(range.start); - const QTextLine &lastLinkLine = tl.lineForTextPosition(range.start + range.length - 1); - for (int i = firstLinkLine.lineNumber(); i <= lastLinkLine.lineNumber(); ++i) { - const QTextLine &linkLine = tl.lineAt(i); - if (!linkLine.isValid()) - break; - const QPointF linePos = linkLine.position(); - const int linkStartPos = i == firstLinkLine.lineNumber() - ? range.start : linkLine.textStart(); - const qreal startOffset = linkLine.cursorToX(linkStartPos); - const int linkEndPos = i == lastLinkLine.lineNumber() - ? range.start + range.length - : linkLine.textStart() + linkLine.textLength(); - const qreal endOffset = linkLine.cursorToX(linkEndPos); - const QPointF linkPos(indexPos.x() + positions.textAreaLeft() + linePos.x() - + startOffset, positions.top() + linePos.y()); - const QSize linkSize(endOffset - startOffset, linkLine.height()); - const QRectF linkRect(linkPos, linkSize); - m_hrefs.push_back({linkRect, range.format.anchorHref()}); - } - } + const QString anchor = anchorAt(e->pos()); + if (anchor == m_clickAnchor) { + Core::EditorManager::openEditorAt(OutputLineParser::parseLinkTarget(m_clickAnchor), {}, + Core::EditorManager::SwitchSplitIfAlreadyVisible); + } + m_clickAnchor.clear(); +} - const QColor mix = StyleHelper::mergedColors(textColor, backgroundColor, 70); - const QString directory = QDir::toNativeSeparators(index.data(TaskModel::File).toString()); - int secondBaseLine = positions.top() + fm.ascent() + height + leading; - if (index.data(TaskModel::FileNotFound).toBool() && !directory.isEmpty()) { - const QString fileNotFound = Tr::tr("File not found: %1").arg(directory); - const QColor errorColor = selected ? mix : creatorTheme()->color(Theme::TextColorError); - painter->setPen(errorColor); - painter->drawText(positions.textAreaLeft(), secondBaseLine, fileNotFound); - } else { - painter->setPen(mix); - painter->drawText(positions.textAreaLeft(), secondBaseLine, directory); +void TaskView::keyReleaseEvent(QKeyEvent *e) +{ + TreeView::keyReleaseEvent(e); + if (e->key() == Qt::Key_Space) { + const Task task = static_cast<TaskFilterModel *>(model())->task(currentIndex()); + if (!task.isNull()) { + const QPoint toolTipPos = mapToGlobal(visualRect(currentIndex()).topLeft()); + QMetaObject::invokeMethod(this, [this, task, toolTipPos] { + showToolTip(task, toolTipPos); }, Qt::QueuedConnection); } } - painter->setPen(textColor); - - // Paint FileArea - QString file = index.data(TaskModel::File).toString(); - const int pos = file.lastIndexOf(QLatin1Char('/')); - if (pos != -1) - file = file.mid(pos +1); - const int realFileWidth = fm.horizontalAdvance(file); - painter->setClipRect(positions.fileArea()); - painter->drawText(qMin(positions.fileAreaLeft(), positions.fileAreaRight() - realFileWidth), - positions.top() + fm.ascent(), file); - if (realFileWidth > positions.fileAreaWidth()) { - // draw a gradient to mask the text - int gradientStart = positions.fileAreaLeft() - 1; - QLinearGradient lg(gradientStart + ELLIPSIS_GRADIENT_WIDTH, 0, gradientStart, 0); - lg.setColorAt(0, Qt::transparent); - lg.setColorAt(1, backgroundColor); - painter->fillRect(gradientStart, positions.top(), ELLIPSIS_GRADIENT_WIDTH, positions.firstLineHeight(), lg); - } +} - // Paint LineArea - int line = index.data(TaskModel::Line).toInt(); - int movedLine = index.data(TaskModel::MovedLine).toInt(); - QString lineText; - - if (line == -1) { - // No line information at all - } else if (movedLine == -1) { - // removed the line, but we had line information, show the line in () - QFont f = painter->font(); - f.setItalic(true); - painter->setFont(f); - lineText = QLatin1Char('(') + QString::number(line) + QLatin1Char(')'); - } else if (movedLine != line) { - // The line was moved - QFont f = painter->font(); - f.setItalic(true); - painter->setFont(f); - lineText = QString::number(movedLine); - } else { - lineText = QString::number(line); +bool TaskView::event(QEvent *e) +{ + if (e->type() != QEvent::ToolTip) + return TreeView::event(e); + + const auto helpEvent = static_cast<QHelpEvent*>(e); + const Task task = static_cast<TaskFilterModel *>(model())->task(indexAt(helpEvent->pos())); + if (task.isNull()) + return TreeView::event(e); + showToolTip(task, helpEvent->globalPos()); + e->accept(); + return true; +} + +void TaskView::showToolTip(const Task &task, const QPoint &pos) +{ + if (task.details.isEmpty()) { + ToolTip::hideImmediately(); + return; } - painter->setClipRect(positions.lineArea()); - const int realLineWidth = fm.horizontalAdvance(lineText); - painter->drawText(positions.lineAreaRight() - realLineWidth, positions.top() + fm.ascent(), lineText); - painter->setClipRect(opt.rect); + const auto layout = new QVBoxLayout; + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(new QLabel(task.formattedDescription({}))); + ToolTip::show(pos, layout); +} - // Separator lines - painter->setPen(QColor::fromRgb(150,150,150)); - const QRectF borderRect = QRectF(opt.rect).adjusted(0.5, 0.5, -0.5, -0.5); - painter->drawLine(borderRect.bottomLeft(), borderRect.bottomRight()); - painter->restore(); +QString TaskView::anchorAt(const QPoint &pos) +{ + const QModelIndex index = indexAt(pos); + if (!index.isValid() || !index.internalId()) + return {}; + + const QRect itemRect = visualRect(index); + QTextDocument &doc = static_cast<TaskDelegate *>(itemDelegate())->doc(); + doc.setHtml(model()->data(index, Qt::DisplayRole).toString()); + const QAbstractTextDocumentLayout * const textLayout = doc.documentLayout(); + QTC_ASSERT(textLayout, return {}); + return textLayout->anchorAt(pos - itemRect.topLeft()); } } // namespace Internal } // namespace ProjectExplorer - -#include "taskwindow.moc" diff --git a/src/plugins/projectexplorer/testdata/multi-target-project/CMakeLists.txt b/src/plugins/projectexplorer/testdata/multi-target-project/CMakeLists.txt new file mode 100644 index 0000000000..5313539cd7 --- /dev/null +++ b/src/plugins/projectexplorer/testdata/multi-target-project/CMakeLists.txt @@ -0,0 +1,3 @@ +project(multi-target-project) +add_executable(multi-target-project-app multi-target-project-main.cpp multi-target-project-shared.h) +add_library(multi-target-project-lib STATIC multi-target-project-lib.cpp multi-target-project-shared.h) diff --git a/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-app.pro b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-app.pro new file mode 100644 index 0000000000..96870c05c1 --- /dev/null +++ b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-app.pro @@ -0,0 +1,4 @@ +TARGET = app +CONFIG -= qt +SOURCES = multi-target-project-main.cpp +HEADERS = multi-target-project-shared.h diff --git a/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-lib.cpp b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-lib.cpp new file mode 100644 index 0000000000..9b7a34861c --- /dev/null +++ b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-lib.cpp @@ -0,0 +1,6 @@ +#include "multi-target-project-shared.h" + +int increaseNumber() +{ + return getNumber() + 1; +} diff --git a/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-lib.pro b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-lib.pro new file mode 100644 index 0000000000..4257154051 --- /dev/null +++ b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-lib.pro @@ -0,0 +1,4 @@ +TEMPLATE = lib +CONFIG += static +SOURCES = multi-target-project-lib.cpp +HEADERS = multi-target-project-shared.h diff --git a/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-main.cpp b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-main.cpp new file mode 100644 index 0000000000..306400b350 --- /dev/null +++ b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-main.cpp @@ -0,0 +1,6 @@ +#include "multi-target-project-shared.h" + +int main() +{ + return getNumber(); +} diff --git a/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-shared.h b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-shared.h new file mode 100644 index 0000000000..9f839e82d0 --- /dev/null +++ b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-shared.h @@ -0,0 +1,3 @@ +#pragma once + +inline int getNumber() { return 5; } diff --git a/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project.pro b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project.pro new file mode 100644 index 0000000000..5e6289d7b2 --- /dev/null +++ b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs +app.file = multi-target-project-app.pro +lib.file = multi-target-project-lib.pro +SUBDIRS = app lib diff --git a/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project.qbs b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project.qbs new file mode 100644 index 0000000000..079ac82c95 --- /dev/null +++ b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project.qbs @@ -0,0 +1,10 @@ +Project { + CppApplication { + name: "app" + files: ["multi-target-project-main.cpp", "multi-target-project-shared.h"] + } + StaticLibrary { + Depends { name: "cpp" } + files: ["multi-target-project-lib.cpp", "multi-target-project-shared.h"] + } +} diff --git a/src/plugins/projectexplorer/toolchain.cpp b/src/plugins/projectexplorer/toolchain.cpp index 1a3d3d43f5..8b8cfce2c8 100644 --- a/src/plugins/projectexplorer/toolchain.cpp +++ b/src/plugins/projectexplorer/toolchain.cpp @@ -192,7 +192,7 @@ bool ToolChain::isValid() const return d->m_isValid.value_or(false); } -QStringList ToolChain::includedFiles(const QStringList &flags, const QString &directory) const +FilePaths ToolChain::includedFiles(const QStringList &flags, const FilePath &directory) const { Q_UNUSED(flags) Q_UNUSED(directory) @@ -466,12 +466,12 @@ Utils::LanguageVersion ToolChain::languageVersion(const Utils::Id &language, con } } -QStringList ToolChain::includedFiles(const QString &option, - const QStringList &flags, - const QString &directoryPath, - PossiblyConcatenatedFlag possiblyConcatenated) +FilePaths ToolChain::includedFiles(const QString &option, + const QStringList &flags, + const FilePath &directoryPath, + PossiblyConcatenatedFlag possiblyConcatenated) { - QStringList result; + FilePaths result; for (int i = 0; i < flags.size(); ++i) { QString includeFile; @@ -484,11 +484,8 @@ QStringList ToolChain::includedFiles(const QString &option, if (includeFile.isEmpty() && flag == option && i + 1 < flags.size()) includeFile = flags[++i]; - if (!includeFile.isEmpty()) { - if (!QFileInfo(includeFile).isAbsolute()) - includeFile = directoryPath + "/" + includeFile; - result.append(QDir::cleanPath(includeFile)); - } + if (!includeFile.isEmpty()) + result.append(directoryPath.resolvePath(includeFile)); } return result; @@ -677,7 +674,9 @@ ToolchainDetector::ToolchainDetector(const Toolchains &alreadyKnown, const IDevice::ConstPtr &device, const FilePaths &searchPaths) : alreadyKnown(alreadyKnown), device(device), searchPaths(searchPaths) -{} +{ + QTC_CHECK(device); +} BadToolchain::BadToolchain(const Utils::FilePath &filePath) : BadToolchain(filePath, filePath.symLinkTarget(), filePath.lastModified()) diff --git a/src/plugins/projectexplorer/toolchain.h b/src/plugins/projectexplorer/toolchain.h index 15bee2fdaa..35cad5333e 100644 --- a/src/plugins/projectexplorer/toolchain.h +++ b/src/plugins/projectexplorer/toolchain.h @@ -102,7 +102,7 @@ public: virtual Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const = 0; virtual Utils::WarningFlags warningFlags(const QStringList &cflags) const = 0; - virtual QStringList includedFiles(const QStringList &flags, const QString &directory) const; + virtual Utils::FilePaths includedFiles(const QStringList &flags, const Utils::FilePath &directory) const; virtual QString sysRoot() const; QString explicitCodeModelTargetTriple() const; @@ -184,10 +184,10 @@ protected: virtual bool fromMap(const QVariantMap &data); enum class PossiblyConcatenatedFlag { No, Yes }; - static QStringList includedFiles(const QString &option, - const QStringList &flags, - const QString &directoryPath, - PossiblyConcatenatedFlag possiblyConcatenated); + static Utils::FilePaths includedFiles(const QString &option, + const QStringList &flags, + const Utils::FilePath &directoryPath, + PossiblyConcatenatedFlag possiblyConcatenated); private: ToolChain(const ToolChain &) = delete; diff --git a/src/plugins/projectexplorer/toolchainconfigwidget.cpp b/src/plugins/projectexplorer/toolchainconfigwidget.cpp index ef97ad72cf..84b80b4e04 100644 --- a/src/plugins/projectexplorer/toolchainconfigwidget.cpp +++ b/src/plugins/projectexplorer/toolchainconfigwidget.cpp @@ -7,8 +7,8 @@ #include "projectexplorertr.h" #include <utils/detailswidget.h> +#include <utils/process.h> #include <utils/qtcassert.h> -#include <utils/qtcprocess.h> #include <QFormLayout> #include <QLineEdit> diff --git a/src/plugins/projectexplorer/toolchainoptionspage.cpp b/src/plugins/projectexplorer/toolchainoptionspage.cpp index b8df603850..e0717ab780 100644 --- a/src/plugins/projectexplorer/toolchainoptionspage.cpp +++ b/src/plugins/projectexplorer/toolchainoptionspage.cpp @@ -48,20 +48,8 @@ class ToolChainTreeItem : public TreeItem { public: ToolChainTreeItem(QStackedWidget *parentWidget, ToolChain *tc, bool c) : - toolChain(tc), changed(c) - { - widget = tc->createConfigurationWidget().release(); - if (widget) { - parentWidget->addWidget(widget); - if (tc->isAutoDetected()) - widget->makeReadOnly(); - QObject::connect(widget, &ToolChainConfigWidget::dirty, - [this] { - changed = true; - update(); - }); - } - } + toolChain(tc), changed(c), m_parentWidget(parentWidget) + {} QVariant data(int column, int role) const override { @@ -93,9 +81,30 @@ public: return QVariant(); } + ToolChainConfigWidget *widget() + { + if (!m_widget) { + m_widget = toolChain->createConfigurationWidget().release(); + if (m_widget) { + m_parentWidget->addWidget(m_widget); + if (toolChain->isAutoDetected()) + m_widget->makeReadOnly(); + QObject::connect(m_widget, &ToolChainConfigWidget::dirty, + [this] { + changed = true; + update(); + }); + } + } + return m_widget; + } + ToolChain *toolChain; - ToolChainConfigWidget *widget; bool changed; + +private: + ToolChainConfigWidget *m_widget = nullptr; + QStackedWidget *m_parentWidget = nullptr; }; class DetectionSettingsDialog : public QDialog @@ -423,7 +432,7 @@ void ToolChainOptionsWidget::toolChainSelectionChanged() { ToolChainTreeItem *item = currentTreeItem(); - QWidget *currentTcWidget = item ? item->widget : nullptr; + QWidget *currentTcWidget = item ? item->widget() : nullptr; if (currentTcWidget) m_widgetStack->setCurrentWidget(currentTcWidget); m_container->setVisible(currentTcWidget); @@ -447,8 +456,8 @@ void ToolChainOptionsWidget::apply() for (TreeItem *item : *parent) { auto tcItem = static_cast<ToolChainTreeItem *>(item); Q_ASSERT(tcItem->toolChain); - if (!tcItem->toolChain->isAutoDetected() && tcItem->widget && tcItem->changed) - tcItem->widget->apply(); + if (!tcItem->toolChain->isAutoDetected() && tcItem->widget() && tcItem->changed) + tcItem->widget()->apply(); tcItem->changed = false; tcItem->update(); } diff --git a/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp b/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp index 4152e9882e..35224e4d02 100644 --- a/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp +++ b/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp @@ -3,6 +3,7 @@ #include "toolchainsettingsaccessor.h" +#include "devicesupport/devicemanager.h" #include "projectexplorerconstants.h" #include "projectexplorertr.h" #include "toolchain.h" @@ -168,11 +169,10 @@ static ToolChainOperations mergeToolChainLists(const Toolchains &systemFileTcs, // ToolChainSettingsAccessor: // -------------------------------------------------------------------- -ToolChainSettingsAccessor::ToolChainSettingsAccessor() : - UpgradingSettingsAccessor("QtCreatorToolChains", - Tr::tr("Tool Chains"), - Core::Constants::IDE_DISPLAY_NAME) +ToolChainSettingsAccessor::ToolChainSettingsAccessor() { + setDocType("QtCreatorToolChains"); + setApplicationDisplayName(Core::Constants::IDE_DISPLAY_NAME); setBaseFilePath(Core::ICore::userResourcePath(TOOLCHAIN_FILENAME)); addVersionUpgrader(std::make_unique<ToolChainSettingsUpgraderV0>()); @@ -192,9 +192,11 @@ Toolchains ToolChainSettingsAccessor::restoreToolChains(QWidget *parent) const // Autodetect: Pass autodetected toolchains from user file so the information can be reused: const Toolchains autodetectedUserFileTcs = Utils::filtered(userFileTcs, &ToolChain::isAutoDetected); - // FIXME: Use real device? - const Toolchains autodetectedTcs = - autoDetectToolChains(ToolchainDetector(autodetectedUserFileTcs, {}, {})); + + // Autodect from system paths on the desktop device. + // The restriction is intentional to keep startup and automatic validation a limited effort + ToolchainDetector detector(autodetectedUserFileTcs, DeviceManager::defaultDesktopDevice(), {}); + const Toolchains autodetectedTcs = autoDetectToolChains(detector); // merge tool chains and register those that we need to keep: const ToolChainOperations ops = mergeToolChainLists(systemFileTcs, userFileTcs, autodetectedTcs); diff --git a/src/plugins/projectexplorer/treescanner.cpp b/src/plugins/projectexplorer/treescanner.cpp index abcc66c3cc..294ef4988e 100644 --- a/src/plugins/projectexplorer/treescanner.cpp +++ b/src/plugins/projectexplorer/treescanner.cpp @@ -9,9 +9,9 @@ #include <coreplugin/iversioncontrol.h> #include <coreplugin/vcsmanager.h> +#include <utils/async.h> #include <utils/qtcassert.h> #include <utils/algorithm.h> -#include <utils/runextensions.h> #include <memory> @@ -42,9 +42,9 @@ bool TreeScanner::asyncScanForFiles(const Utils::FilePath &directory) if (!m_futureWatcher.isFinished()) return false; - m_scanFuture = Utils::runAsync( - [directory, filter = m_filter, factory = m_factory] (FutureInterface &fi) { - TreeScanner::scanForFiles(fi, directory, filter, factory); + m_scanFuture = Utils::asyncRun( + [directory, filter = m_filter, factory = m_factory] (Promise &promise) { + TreeScanner::scanForFiles(promise, directory, filter, factory); }); m_futureWatcher.setFuture(m_scanFuture); @@ -139,10 +139,10 @@ static std::unique_ptr<FolderNode> createFolderNode(const Utils::FilePath &direc return fileSystemNode; } -void TreeScanner::scanForFiles(FutureInterface &fi, const Utils::FilePath& directory, +void TreeScanner::scanForFiles(Promise &promise, const Utils::FilePath& directory, const FileFilter &filter, const FileTypeFactory &factory) { - QList<FileNode *> nodes = ProjectExplorer::scanForFiles(fi, directory, + QList<FileNode *> nodes = ProjectExplorer::scanForFiles(promise, directory, [&filter, &factory](const Utils::FilePath &fn) -> FileNode * { const Utils::MimeType mimeType = Utils::mimeTypeForFile(fn); @@ -160,10 +160,10 @@ void TreeScanner::scanForFiles(FutureInterface &fi, const Utils::FilePath& direc Utils::sort(nodes, ProjectExplorer::Node::sortByPath); - fi.setProgressValue(fi.progressMaximum()); + promise.setProgressValue(promise.future().progressMaximum()); Result result{createFolderNode(directory, nodes), nodes}; - fi.reportResult(result); + promise.addResult(result); } } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/treescanner.h b/src/plugins/projectexplorer/treescanner.h index e324303c17..f8d019121b 100644 --- a/src/plugins/projectexplorer/treescanner.h +++ b/src/plugins/projectexplorer/treescanner.h @@ -31,7 +31,7 @@ public: }; using Future = QFuture<Result>; using FutureWatcher = QFutureWatcher<Result>; - using FutureInterface = QFutureInterface<Result>; + using Promise = QPromise<Result>; using FileFilter = std::function<bool(const Utils::MimeType &, const Utils::FilePath &)>; using FileTypeFactory = std::function<ProjectExplorer::FileType(const Utils::MimeType &, const Utils::FilePath &)>; @@ -69,7 +69,7 @@ signals: void finished(); private: - static void scanForFiles(FutureInterface &fi, const Utils::FilePath &directory, + static void scanForFiles(Promise &fi, const Utils::FilePath &directory, const FileFilter &filter, const FileTypeFactory &factory); private: diff --git a/src/plugins/projectexplorer/userfileaccessor.cpp b/src/plugins/projectexplorer/userfileaccessor.cpp index cf347e032f..ae900ea818 100644 --- a/src/plugins/projectexplorer/userfileaccessor.cpp +++ b/src/plugins/projectexplorer/userfileaccessor.cpp @@ -18,8 +18,8 @@ #include <utils/environment.h> #include <utils/hostosinfo.h> #include <utils/persistentsettings.h> +#include <utils/process.h> #include <utils/qtcassert.h> -#include <utils/qtcprocess.h> #include <QRegularExpression> @@ -283,19 +283,21 @@ FilePaths UserFileBackUpStrategy::readFileCandidates(const FilePath &baseFileNam // UserFileAccessor: // -------------------------------------------------------------------- -UserFileAccessor::UserFileAccessor(Project *project) : - MergingSettingsAccessor(std::make_unique<VersionedBackUpStrategy>(this), - "QtCreatorProject", project->displayName(), - Core::Constants::IDE_DISPLAY_NAME), - m_project(project) +UserFileAccessor::UserFileAccessor(Project *project) + : m_project(project) { + setStrategy(std::make_unique<VersionedBackUpStrategy>(this)); + setDocType("QtCreatorProject"); + setApplicationDisplayName(Core::Constants::IDE_DISPLAY_NAME); + // Setup: const FilePath externalUser = externalUserFile(); const FilePath projectUser = projectUserFile(); setBaseFilePath(externalUser.isEmpty() ? projectUser : externalUser); - auto secondary - = std::make_unique<SettingsAccessor>(docType, displayName, applicationDisplayName); + auto secondary = std::make_unique<SettingsAccessor>(); + secondary->setDocType(m_docType); + secondary->setApplicationDisplayName(m_applicationDisplayName); secondary->setBaseFilePath(sharedFile()); secondary->setReadOnly(); setSecondaryAccessor(std::move(secondary)); |