aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/projectexplorer/session.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/projectexplorer/session.cpp')
-rw-r--r--src/plugins/projectexplorer/session.cpp1262
1 files changed, 0 insertions, 1262 deletions
diff --git a/src/plugins/projectexplorer/session.cpp b/src/plugins/projectexplorer/session.cpp
deleted file mode 100644
index 6dcfd9a4619..00000000000
--- 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