forked from qt-creator/qt-creator
SettingsAccessor: Split out reading of shared settings and the merging
Change-Id: Iccf9b88ba7f778ccdc17e088a60680a8d5cf840b Reviewed-by: Daniel Teske <daniel.teske@digia.com>
This commit is contained in:
@@ -492,7 +492,7 @@ namespace {
|
|||||||
|
|
||||||
// It's assumed that the shared map has the same structure as the user map.
|
// It's assumed that the shared map has the same structure as the user map.
|
||||||
template <class Operation_T>
|
template <class Operation_T>
|
||||||
void synchronizeSettings(QVariantMap *userMap,
|
void synchronizeSettings(QVariantMap &userMap,
|
||||||
const QVariantMap &sharedMap,
|
const QVariantMap &sharedMap,
|
||||||
Operation_T *op)
|
Operation_T *op)
|
||||||
{
|
{
|
||||||
@@ -501,32 +501,33 @@ void synchronizeSettings(QVariantMap *userMap,
|
|||||||
for (; it != eit; ++it) {
|
for (; it != eit; ++it) {
|
||||||
const QString &key = it.key();
|
const QString &key = it.key();
|
||||||
const QVariant &sharedValue = it.value();
|
const QVariant &sharedValue = it.value();
|
||||||
const QVariant &userValue = userMap->value(key);
|
const QVariant &userValue = userMap.value(key);
|
||||||
if (sharedValue.type() == QVariant::Map) {
|
if (sharedValue.type() == QVariant::Map) {
|
||||||
if (userValue.type() != QVariant::Map) {
|
if (userValue.type() != QVariant::Map) {
|
||||||
// This should happen only if the user manually changed the file in such a way.
|
// This should happen only if the user manually changed the file in such a way.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
QVariantMap nestedUserMap = userValue.toMap();
|
QVariantMap nestedUserMap = userValue.toMap();
|
||||||
synchronizeSettings(&nestedUserMap,
|
synchronizeSettings(nestedUserMap,
|
||||||
sharedValue.toMap(),
|
sharedValue.toMap(),
|
||||||
op);
|
op);
|
||||||
userMap->insert(key, nestedUserMap);
|
userMap.insert(key, nestedUserMap);
|
||||||
} else if (userMap->contains(key) && userValue != sharedValue) {
|
} else if (userMap.contains(key) && userValue != sharedValue) {
|
||||||
op->apply(userMap, key, sharedValue);
|
op->apply(userMap, key, sharedValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct MergeSharedSetting
|
class MergeSharedSetting
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
MergeSharedSetting(const QSet<QString> &sticky) : m_userSticky(sticky) {}
|
MergeSharedSetting(const QSet<QString> &sticky) : m_userSticky(sticky) {}
|
||||||
|
|
||||||
void apply(QVariantMap *userMap, const QString &key, const QVariant &sharedValue)
|
void apply(QVariantMap &userMap, const QString &key, const QVariant &sharedValue)
|
||||||
{
|
{
|
||||||
if (!m_userSticky.contains(key))
|
if (!m_userSticky.contains(key))
|
||||||
userMap->insert(key, sharedValue);
|
userMap.insert(key, sharedValue);
|
||||||
}
|
}
|
||||||
QSet<QString> m_userSticky;
|
QSet<QString> m_userSticky;
|
||||||
};
|
};
|
||||||
@@ -536,30 +537,32 @@ struct MergeSharedSetting
|
|||||||
// corresponding ones in the .user file. Whenever we identify a corresponding setting which
|
// corresponding ones in the .user file. Whenever we identify a corresponding setting which
|
||||||
// has a different value and which is not marked as sticky, we merge the .shared value into
|
// has a different value and which is not marked as sticky, we merge the .shared value into
|
||||||
// the .user value.
|
// the .user value.
|
||||||
void mergeSharedSettings(QVariantMap *userMap, const QVariantMap &sharedMap)
|
QVariantMap mergeSharedSettings(const QVariantMap &userMap, const QVariantMap &sharedMap)
|
||||||
{
|
{
|
||||||
|
QVariantMap result = userMap;
|
||||||
if (sharedMap.isEmpty())
|
if (sharedMap.isEmpty())
|
||||||
return;
|
return result;
|
||||||
|
|
||||||
QSet<QString> stickyKeys;
|
QSet<QString> stickyKeys;
|
||||||
const QVariant stickyList = userMap->take(QLatin1String(USER_STICKY_KEYS_KEY)).toList();
|
const QVariant stickyList = result.take(QLatin1String(USER_STICKY_KEYS_KEY)).toList();
|
||||||
if (stickyList.isValid()) {
|
if (stickyList.isValid()) {
|
||||||
if (stickyList.type() != QVariant::List) {
|
if (stickyList.type() != QVariant::List) {
|
||||||
// File is messed up... The user probably changed something.
|
// File is messed up... The user probably changed something.
|
||||||
return;
|
return result;
|
||||||
}
|
}
|
||||||
foreach (const QVariant &v, stickyList.toList())
|
foreach (const QVariant &v, stickyList.toList())
|
||||||
stickyKeys.insert(v.toString());
|
stickyKeys.insert(v.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
MergeSharedSetting op(stickyKeys);
|
MergeSharedSetting op(stickyKeys);
|
||||||
synchronizeSettings(userMap, sharedMap, &op);
|
synchronizeSettings(result, sharedMap, &op);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TrackUserStickySetting
|
||||||
struct TrackUserStickySetting
|
|
||||||
{
|
{
|
||||||
void apply(QVariantMap *, const QString &key, const QVariant &)
|
public:
|
||||||
|
void apply(QVariantMap &, const QString &key, const QVariant &)
|
||||||
{
|
{
|
||||||
m_userSticky.insert(key);
|
m_userSticky.insert(key);
|
||||||
}
|
}
|
||||||
@@ -574,7 +577,7 @@ struct TrackUserStickySetting
|
|||||||
// Although this approach is more flexible than permanent/forever sticky settings, it has
|
// Although this approach is more flexible than permanent/forever sticky settings, it has
|
||||||
// the side-effect that if a particular value unintentionally becomes the same in both
|
// the side-effect that if a particular value unintentionally becomes the same in both
|
||||||
// the .user and .shared files, this setting will "unstick".
|
// the .user and .shared files, this setting will "unstick".
|
||||||
void trackUserStickySettings(QVariantMap *userMap, const QVariantMap &sharedMap)
|
void trackUserStickySettings(QVariantMap &userMap, const QVariantMap &sharedMap)
|
||||||
{
|
{
|
||||||
if (sharedMap.isEmpty())
|
if (sharedMap.isEmpty())
|
||||||
return;
|
return;
|
||||||
@@ -582,7 +585,7 @@ void trackUserStickySettings(QVariantMap *userMap, const QVariantMap &sharedMap)
|
|||||||
TrackUserStickySetting op;
|
TrackUserStickySetting op;
|
||||||
synchronizeSettings(userMap, sharedMap, &op);
|
synchronizeSettings(userMap, sharedMap, &op);
|
||||||
|
|
||||||
userMap->insert(QLatin1String(USER_STICKY_KEYS_KEY), QVariant(op.m_userSticky.toList()));
|
userMap.insert(QLatin1String(USER_STICKY_KEYS_KEY), QVariant(op.m_userSticky.toList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // Anonymous
|
} // Anonymous
|
||||||
@@ -594,77 +597,12 @@ QVariantMap SettingsAccessor::restoreSettings() const
|
|||||||
return QVariantMap();
|
return QVariantMap();
|
||||||
|
|
||||||
SettingsData userSettings = readUserSettings();
|
SettingsData userSettings = readUserSettings();
|
||||||
|
SettingsData sharedSettings = readSharedSettings();
|
||||||
// Time to consider shared settings...
|
userSettings = mergeSettings(userSettings, sharedSettings);
|
||||||
SettingsData sharedSettings;
|
|
||||||
QString fn = project()->property(m_sharedFileAcessor.id()).toString();
|
|
||||||
if (fn.isEmpty())
|
|
||||||
fn = project()->document()->fileName() + m_sharedFileAcessor.suffix();
|
|
||||||
sharedSettings.m_fileName = Utils::FileName::fromString(fn);
|
|
||||||
if (!sharedSettings.fileName().isEmpty() && m_sharedFileAcessor.readFile(&sharedSettings)) {
|
|
||||||
bool useSharedSettings = true;
|
|
||||||
if (sharedSettings.m_version != userSettings.m_version) {
|
|
||||||
int baseFileVersion;
|
|
||||||
if (sharedSettings.m_version > m_lastVersion + 1) {
|
|
||||||
// The shared file version is newer than Creator... If we have valid user
|
|
||||||
// settings we prompt the user whether we could try an *unsupported* update.
|
|
||||||
// This makes sense since the merging operation will only replace shared settings
|
|
||||||
// that perfectly match corresponding user ones. If we don't have valid user
|
|
||||||
// settings to compare against, there's nothing we can do.
|
|
||||||
if (!userSettings.isValid())
|
|
||||||
return QVariantMap();
|
|
||||||
|
|
||||||
QMessageBox msgBox(
|
|
||||||
QMessageBox::Question,
|
|
||||||
QApplication::translate("ProjectExplorer::SettingsAccessor",
|
|
||||||
"Unsupported Shared Settings File"),
|
|
||||||
QApplication::translate("ProjectExplorer::SettingsAccessor",
|
|
||||||
"The version of your .shared file is not "
|
|
||||||
"supported by this Qt Creator version. "
|
|
||||||
"Only settings that are still compatible "
|
|
||||||
"will be taken into account.\n\n"
|
|
||||||
"Do you want to try loading it?"),
|
|
||||||
QMessageBox::Yes | QMessageBox::No,
|
|
||||||
Core::ICore::mainWindow());
|
|
||||||
msgBox.setDefaultButton(QMessageBox::No);
|
|
||||||
msgBox.setEscapeButton(QMessageBox::No);
|
|
||||||
if (msgBox.exec() == QMessageBox::No)
|
|
||||||
useSharedSettings = false;
|
|
||||||
else
|
|
||||||
baseFileVersion = m_lastVersion + 1;
|
|
||||||
} else {
|
|
||||||
baseFileVersion = qMax(userSettings.m_version, sharedSettings.m_version);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (useSharedSettings) {
|
|
||||||
// We now update the user and shared settings so they are compatible.
|
|
||||||
for (int i = sharedSettings.m_version; i < baseFileVersion; ++i)
|
|
||||||
sharedSettings.m_map = m_handlers.value(i)->update(m_project, sharedSettings.m_map);
|
|
||||||
|
|
||||||
if (userSettings.isValid()) {
|
|
||||||
for (int i = userSettings.m_version; i < baseFileVersion; ++i)
|
|
||||||
userSettings.m_map = m_handlers.value(i)->update(m_project, userSettings.m_map);
|
|
||||||
userSettings.m_version = baseFileVersion;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (useSharedSettings) {
|
|
||||||
m_project->setProperty(SHARED_SETTINGS, sharedSettings.m_map);
|
|
||||||
if (userSettings.isValid())
|
|
||||||
mergeSharedSettings(&userSettings.m_map, sharedSettings.m_map);
|
|
||||||
if (!userSettings.isValid())
|
|
||||||
userSettings = sharedSettings;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!userSettings.isValid())
|
if (!userSettings.isValid())
|
||||||
return QVariantMap();
|
return QVariantMap();
|
||||||
|
|
||||||
// Update from the base version to Creator's version.
|
|
||||||
for (int i = userSettings.m_version; i <= m_lastVersion; ++i)
|
|
||||||
userSettings.m_map = m_handlers.value(i)->update(m_project, userSettings.m_map);
|
|
||||||
|
|
||||||
return userSettings.m_map;
|
return userSettings.m_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -674,13 +612,10 @@ bool SettingsAccessor::saveSettings(const QVariantMap &map) const
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
SettingsData settings(map);
|
SettingsData settings(map);
|
||||||
QString fn = project()->property(m_userFileAcessor.id()).toString();
|
settings.m_fileName = Utils::FileName::fromString(defaultFileName(m_userFileAcessor.suffix()));
|
||||||
if (fn.isEmpty())
|
|
||||||
fn = project()->document()->fileName() + m_userFileAcessor.suffix();
|
|
||||||
settings.m_fileName = Utils::FileName::fromString(fn);
|
|
||||||
const QVariant &shared = m_project->property(SHARED_SETTINGS);
|
const QVariant &shared = m_project->property(SHARED_SETTINGS);
|
||||||
if (shared.isValid())
|
if (shared.isValid())
|
||||||
trackUserStickySettings(&settings.m_map, shared.toMap());
|
trackUserStickySettings(settings.m_map, shared.toMap());
|
||||||
|
|
||||||
return m_userFileAcessor.writeFile(&settings);
|
return m_userFileAcessor.writeFile(&settings);
|
||||||
}
|
}
|
||||||
@@ -751,6 +686,12 @@ int SettingsAccessor::currentVersion() const
|
|||||||
return m_lastVersion + 1;
|
return m_lastVersion + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsAccessor::incrementVersion(SettingsAccessor::SettingsData &data) const
|
||||||
|
{
|
||||||
|
data.m_map = m_handlers.value(data.version())->update(m_project, data.m_map);
|
||||||
|
++data.m_version;
|
||||||
|
}
|
||||||
|
|
||||||
SettingsAccessor::SettingsData SettingsAccessor::readUserSettings() const
|
SettingsAccessor::SettingsData SettingsAccessor::readUserSettings() const
|
||||||
{
|
{
|
||||||
SettingsData result;
|
SettingsData result;
|
||||||
@@ -815,6 +756,42 @@ SettingsAccessor::SettingsData SettingsAccessor::readUserSettings() const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SettingsAccessor::SettingsData SettingsAccessor::readSharedSettings() const
|
||||||
|
{
|
||||||
|
SettingsData sharedSettings;
|
||||||
|
QString fn = project()->document()->fileName() + m_sharedFileAcessor.suffix();
|
||||||
|
sharedSettings.m_fileName = Utils::FileName::fromString(fn);
|
||||||
|
|
||||||
|
if (!m_sharedFileAcessor.readFile(&sharedSettings))
|
||||||
|
return sharedSettings;
|
||||||
|
|
||||||
|
if (sharedSettings.m_version > currentVersion()) {
|
||||||
|
// The shared file version is newer than Creator... If we have valid user
|
||||||
|
// settings we prompt the user whether we could try an *unsupported* update.
|
||||||
|
// This makes sense since the merging operation will only replace shared settings
|
||||||
|
// that perfectly match corresponding user ones. If we don't have valid user
|
||||||
|
// settings to compare against, there's nothing we can do.
|
||||||
|
|
||||||
|
QMessageBox msgBox(
|
||||||
|
QMessageBox::Question,
|
||||||
|
QApplication::translate("ProjectExplorer::SettingsAccessor",
|
||||||
|
"Unsupported Shared Settings File"),
|
||||||
|
QApplication::translate("ProjectExplorer::SettingsAccessor",
|
||||||
|
"The version of your .shared file is not "
|
||||||
|
"supported by Qt Creator. "
|
||||||
|
"Do you want to try loading it anyway?"),
|
||||||
|
QMessageBox::Yes | QMessageBox::No,
|
||||||
|
Core::ICore::mainWindow());
|
||||||
|
msgBox.setDefaultButton(QMessageBox::No);
|
||||||
|
msgBox.setEscapeButton(QMessageBox::No);
|
||||||
|
if (msgBox.exec() == QMessageBox::No)
|
||||||
|
sharedSettings.clear();
|
||||||
|
else
|
||||||
|
sharedSettings.m_version = currentVersion();
|
||||||
|
}
|
||||||
|
return sharedSettings;
|
||||||
|
}
|
||||||
|
|
||||||
SettingsAccessor::SettingsData SettingsAccessor::findBestSettings(const QStringList &candidates) const
|
SettingsAccessor::SettingsData SettingsAccessor::findBestSettings(const QStringList &candidates) const
|
||||||
{
|
{
|
||||||
SettingsData newestNonMatching;
|
SettingsData newestNonMatching;
|
||||||
@@ -856,6 +833,34 @@ SettingsAccessor::SettingsData SettingsAccessor::findBestSettings(const QStringL
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SettingsAccessor::SettingsData SettingsAccessor::mergeSettings(const SettingsAccessor::SettingsData &user,
|
||||||
|
const SettingsAccessor::SettingsData &shared) const
|
||||||
|
{
|
||||||
|
SettingsData newUser = user;
|
||||||
|
SettingsData newShared = shared;
|
||||||
|
if (shared.isValid() && user.isValid()) {
|
||||||
|
while (newUser.version() < newShared.version())
|
||||||
|
incrementVersion(newUser);
|
||||||
|
|
||||||
|
while (newShared.version() < newUser.version())
|
||||||
|
incrementVersion(newShared);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_project->setProperty(SHARED_SETTINGS, newShared.m_map);
|
||||||
|
|
||||||
|
SettingsData result = newUser;
|
||||||
|
result.m_map = mergeSharedSettings(newUser.m_map, newShared.m_map);
|
||||||
|
|
||||||
|
if (!result.isValid())
|
||||||
|
return result;
|
||||||
|
|
||||||
|
// Update from the base version to Creator's version.
|
||||||
|
for (int i = result.version(); i < currentVersion(); ++i)
|
||||||
|
incrementVersion(result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// SettingsData
|
// SettingsData
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
@@ -56,9 +56,9 @@ public:
|
|||||||
bool saveSettings(const QVariantMap &map) const;
|
bool saveSettings(const QVariantMap &map) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// Takes ownership of the handler!
|
// Takes ownership of the handler!
|
||||||
void addVersionHandler(Internal::UserFileVersionHandler *handler);
|
void addVersionHandler(Internal::UserFileVersionHandler *handler);
|
||||||
|
|
||||||
QStringList findSettingsFiles(const QString &suffix) const;
|
QStringList findSettingsFiles(const QString &suffix) const;
|
||||||
QByteArray creatorId() const;
|
QByteArray creatorId() const;
|
||||||
QString defaultFileName(const QString &suffix) const;
|
QString defaultFileName(const QString &suffix) const;
|
||||||
@@ -84,8 +84,12 @@ private:
|
|||||||
Utils::FileName m_fileName;
|
Utils::FileName m_fileName;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void incrementVersion(SettingsData &data) const;
|
||||||
|
|
||||||
SettingsData readUserSettings() const;
|
SettingsData readUserSettings() const;
|
||||||
|
SettingsData readSharedSettings() const;
|
||||||
SettingsData findBestSettings(const QStringList &candidates) const;
|
SettingsData findBestSettings(const QStringList &candidates) const;
|
||||||
|
SettingsData mergeSettings(const SettingsData &user, const SettingsData &shared) const;
|
||||||
|
|
||||||
// The entity which actually reads/writes to the settings file.
|
// The entity which actually reads/writes to the settings file.
|
||||||
class FileAccessor
|
class FileAccessor
|
||||||
|
Reference in New Issue
Block a user