Session Manager: Do not lose settings made in the default session

The "implicit" default session, that is. See the verbose comment inside
this patch for details.

Fixes: QTCREATORBUG-19388
Change-Id: I270ca25ce3d4c32db51c53de2af055bdfe8c54af
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
Christian Kandeler
2019-12-12 11:27:22 +01:00
parent bfc73a356c
commit 1291755288
3 changed files with 110 additions and 58 deletions

View File

@@ -2306,8 +2306,9 @@ void ProjectExplorerPluginPrivate::restoreSession()
} // for arguments } // for arguments
} // !arguments.isEmpty() } // !arguments.isEmpty()
// Restore latest session or what was passed on the command line // Restore latest session or what was passed on the command line
if (!dd->m_sessionToRestoreAtStartup.isEmpty())
SessionManager::loadSession(dd->m_sessionToRestoreAtStartup); SessionManager::loadSession(!dd->m_sessionToRestoreAtStartup.isEmpty()
? dd->m_sessionToRestoreAtStartup : QString(), true);
// update welcome page // update welcome page
connect(ModeManager::instance(), &ModeManager::currentModeChanged, connect(ModeManager::instance(), &ModeManager::currentModeChanged,

View File

@@ -456,24 +456,30 @@ bool SessionManager::loadingSession()
bool SessionManager::save() bool SessionManager::save()
{ {
// do not save new virgin default sessions
if (isDefaultVirgin())
return true;
emit m_instance->aboutToSaveSession(); emit m_instance->aboutToSaveSession();
if (!d->m_writer || d->m_writer->fileName() != sessionNameToFileName(d->m_sessionName)) { const FilePath filePath = sessionNameToFileName(d->m_sessionName);
delete d->m_writer; QVariantMap data;
d->m_writer = new PersistentSettingsWriter(sessionNameToFileName(d->m_sessionName),
QLatin1String("QtCreatorSession")); // 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("Error while saving session"),
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(QLatin1String("StartupProject"),
d->m_startupProject->projectFilePath().toString());
} }
QVariantMap data; const QColor c = StyleHelper::requestedBaseColor();
// save the startup project
if (d->m_startupProject)
data.insert(QLatin1String("StartupProject"), d->m_startupProject->projectFilePath().toString());
QColor c = StyleHelper::requestedBaseColor();
if (c.isValid()) { if (c.isValid()) {
QString tmp = QString::fromLatin1("#%1%2%3") QString tmp = QString::fromLatin1("#%1%2%3")
.arg(c.red(), 2, 16, QLatin1Char('0')) .arg(c.red(), 2, 16, QLatin1Char('0'))
@@ -482,10 +488,11 @@ bool SessionManager::save()
data.insert(QLatin1String("Color"), tmp); data.insert(QLatin1String("Color"), tmp);
} }
QStringList projectFiles QStringList projectFiles = Utils::transform(projects(), [](Project *p) {
= Utils::transform(projects(), [](Project *p) { return p->projectFilePath().toString(); }); return p->projectFilePath().toString();
// Restore infromation on projects that failed to load: });
// don't readd projects to the list, which the user loaded // Restore information on projects that failed to load:
// don't read projects to the list, which the user loaded
foreach (const QString &failed, d->m_failedProjects) { foreach (const QString &failed, d->m_failedProjects) {
if (!projectFiles.contains(failed)) if (!projectFiles.contains(failed))
projectFiles << failed; projectFiles << failed;
@@ -494,7 +501,7 @@ bool SessionManager::save()
data.insert(QLatin1String("ProjectList"), projectFiles); data.insert(QLatin1String("ProjectList"), projectFiles);
data.insert(QLatin1String("CascadeSetActive"), d->m_casadeSetActive); data.insert(QLatin1String("CascadeSetActive"), d->m_casadeSetActive);
QMap<QString, QVariant> depMap; QVariantMap depMap;
auto i = d->m_depMap.constBegin(); auto i = d->m_depMap.constBegin();
while (i != d->m_depMap.constEnd()) { while (i != d->m_depMap.constEnd()) {
QString key = i.key(); QString key = i.key();
@@ -506,18 +513,23 @@ bool SessionManager::save()
} }
data.insert(QLatin1String("ProjectDependencies"), QVariant(depMap)); data.insert(QLatin1String("ProjectDependencies"), QVariant(depMap));
data.insert(QLatin1String("EditorSettings"), EditorManager::saveState().toBase64()); data.insert(QLatin1String("EditorSettings"), EditorManager::saveState().toBase64());
}
auto end = d->m_values.constEnd(); const auto end = d->m_values.constEnd();
QStringList keys; QStringList keys;
for (auto it = d->m_values.constBegin(); it != end; ++it) { for (auto it = d->m_values.constBegin(); it != end; ++it) {
data.insert(QLatin1String("value-") + it.key(), it.value()); data.insert(QLatin1String("value-") + it.key(), it.value());
keys << it.key(); keys << it.key();
} }
data.insert(QLatin1String("valueKeys"), keys); data.insert(QLatin1String("valueKeys"), keys);
bool result = d->m_writer->save(data, ICore::mainWindow()); 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::mainWindow());
if (result) { if (result) {
if (!isDefaultVirgin())
d->m_sessionDateTimes.insert(activeSession(), QDateTime::currentDateTime()); d->m_sessionDateTimes.insert(activeSession(), QDateTime::currentDateTime());
} else { } else {
QMessageBox::warning(ICore::dialogParent(), tr("Error while saving session"), QMessageBox::warning(ICore::dialogParent(), tr("Error while saving session"),
@@ -962,21 +974,49 @@ void SessionManagerPrivate::restoreProjects(const QStringList &fileList)
} }
} }
bool SessionManager::loadSession(const QString &session) /*
* ========== 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"
&& d->m_sessionName == "default" && !initial;
// Do nothing if we have that session already loaded, // Do nothing if we have that session already loaded,
// exception if the session is the default virgin session // exception if the session is the default virgin session
// we still want to be able to load the default session // we still want to be able to load the default session
if (session == d->m_sessionName && !isDefaultVirgin()) if (session == d->m_sessionName && !isDefaultVirgin())
return true; return true;
if (!sessions().contains(session)) if (!loadImplicitDefault && !sessions().contains(session))
return false; return false;
QStringList fileList; QStringList fileList;
// Try loading the file // Try loading the file
FilePath fileName = sessionNameToFileName(session); FilePath fileName = sessionNameToFileName(loadImplicitDefault ? "default" : session);
PersistentSettingsReader reader; PersistentSettingsReader reader;
if (fileName.exists()) { if (fileName.exists()) {
if (!reader.load(fileName)) { if (!reader.load(fileName)) {
@@ -985,7 +1025,16 @@ bool SessionManager::loadSession(const QString &session)
return false; return false;
} }
if (loadImplicitDefault) {
d->restoreValues(reader);
emit m_instance->sessionLoaded("default");
return true;
}
fileList = reader.restoreValue(QLatin1String("ProjectList")).toStringList(); fileList = reader.restoreValue(QLatin1String("ProjectList")).toStringList();
} else if (loadImplicitDefault) {
return true;
} }
d->m_loadingSession = true; d->m_loadingSession = true;
@@ -1016,6 +1065,7 @@ bool SessionManager::loadSession(const QString &session)
}); });
d->m_failedProjects.clear(); d->m_failedProjects.clear();
d->m_depMap.clear(); d->m_depMap.clear();
if (!switchFromImplicitToExplicitDefault)
d->m_values.clear(); d->m_values.clear();
d->m_casadeSetActive = false; d->m_casadeSetActive = false;
@@ -1033,6 +1083,7 @@ bool SessionManager::loadSession(const QString &session)
d->m_future.setProgressRange(0, 1); d->m_future.setProgressRange(0, 1);
d->m_future.setProgressValue(0); d->m_future.setProgressValue(0);
if (!switchFromImplicitToExplicitDefault)
d->restoreValues(reader); d->restoreValues(reader);
emit m_instance->aboutToLoadSession(session); emit m_instance->aboutToLoadSession(session);

View File

@@ -73,7 +73,7 @@ public:
static bool cloneSession(const QString &original, const QString &clone); static bool cloneSession(const QString &original, const QString &clone);
static bool renameSession(const QString &original, const QString &newName); static bool renameSession(const QString &original, const QString &newName);
static bool loadSession(const QString &session); static bool loadSession(const QString &session, bool initial = false);
static bool save(); static bool save();
static void closeAllProjects(); static void closeAllProjects();