IDocument: Simplify permission changes

Take care of handling file permission changes centrally.

TextDocument had its own, caching implementation of tracking the backing
file's read-only state. Move that into IDocument directly.

IDocument::reload with a permission-only change is not a very
interesting case, but every subclass needed to add handling of it.
Instead, remove TypePermission from the file-change types, and handle it
separately via the now unified checkPermissions() implementation.
IDocument::reloadBehavior already was never called with TypePermission.

Change-Id: I321d47ba6193bc878efa9bb50ba7a739fa492745
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Eike Ziller
2021-01-13 16:27:02 +01:00
parent 71b5a9e19a
commit 484d40258a
15 changed files with 65 additions and 120 deletions

View File

@@ -322,22 +322,13 @@ public:
: m_widget->isModified(); : m_widget->isModified();
} }
bool isFileReadOnly() const override {
const FilePath fn = filePath();
if (fn.isEmpty())
return false;
return !fn.toFileInfo().isWritable();
}
bool isSaveAsAllowed() const override { return true; } bool isSaveAsAllowed() const override { return true; }
bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override
{ {
Q_UNUSED(type)
if (flag == FlagIgnore) if (flag == FlagIgnore)
return true; return true;
if (type == TypePermissions) {
emit changed();
} else {
emit aboutToReload(); emit aboutToReload();
int cPos = m_widget->cursorPosition(); int cPos = m_widget->cursorPosition();
m_widget->clear(); m_widget->clear();
@@ -346,8 +337,6 @@ public:
emit reloadFinished(success); emit reloadFinished(success);
return success; return success;
} }
return true;
}
private: private:
BinEditorWidget *m_widget; BinEditorWidget *m_widget;

View File

@@ -51,8 +51,9 @@
#include <utils/globalfilechangeblocker.h> #include <utils/globalfilechangeblocker.h>
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
#include <utils/mimetypes/mimedatabase.h> #include <utils/mimetypes/mimedatabase.h>
#include <utils/qtcassert.h> #include <utils/optional.h>
#include <utils/pathchooser.h> #include <utils/pathchooser.h>
#include <utils/qtcassert.h>
#include <utils/reloadpromptutils.h> #include <utils/reloadpromptutils.h>
#include <QStringList> #include <QStringList>
@@ -1141,7 +1142,7 @@ void DocumentManager::checkForReload()
QStringList filesToDiff; QStringList filesToDiff;
foreach (IDocument *document, changedIDocuments) { foreach (IDocument *document, changedIDocuments) {
IDocument::ChangeTrigger trigger = IDocument::TriggerInternal; IDocument::ChangeTrigger trigger = IDocument::TriggerInternal;
IDocument::ChangeType type = IDocument::TypePermissions; optional<IDocument::ChangeType> type;
bool changed = false; bool changed = false;
// find out the type & behavior from the two possible files // find out the type & behavior from the two possible files
// behavior is internal if all changes are expected (and none removed) // behavior is internal if all changes are expected (and none removed)
@@ -1174,7 +1175,7 @@ void DocumentManager::checkForReload()
IDocument::ChangeType fileChange = changeTypes.value(fileKey); IDocument::ChangeType fileChange = changeTypes.value(fileKey);
if (fileChange == IDocument::TypeRemoved) if (fileChange == IDocument::TypeRemoved)
type = IDocument::TypeRemoved; type = IDocument::TypeRemoved;
else if (fileChange == IDocument::TypeContents && type == IDocument::TypePermissions) else if (fileChange == IDocument::TypeContents && !type)
type = IDocument::TypeContents; type = IDocument::TypeContents;
} }
@@ -1196,35 +1197,36 @@ void DocumentManager::checkForReload()
QString errorString; QString errorString;
// we've got some modification // we've got some modification
// check if it's contents or permissions: // check if it's contents or permissions:
if (type == IDocument::TypePermissions) { if (!type) {
// Only permission change // Only permission change
success = document->reload(&errorString, IDocument::FlagReload, IDocument::TypePermissions); document->checkPermissions();
success = true;
// now we know it's a content change or file was removed // now we know it's a content change or file was removed
} else if (defaultBehavior == IDocument::ReloadUnmodified } else if (defaultBehavior == IDocument::ReloadUnmodified && type == IDocument::TypeContents
&& type == IDocument::TypeContents && !document->isModified()) { && !document->isModified()) {
// content change, but unmodified (and settings say to reload in this case) // content change, but unmodified (and settings say to reload in this case)
success = document->reload(&errorString, IDocument::FlagReload, type); success = document->reload(&errorString, IDocument::FlagReload, *type);
// file was removed or it's a content change and the default behavior for // file was removed or it's a content change and the default behavior for
// unmodified files didn't kick in // unmodified files didn't kick in
} else if (defaultBehavior == IDocument::ReloadUnmodified } else if (defaultBehavior == IDocument::ReloadUnmodified && type == IDocument::TypeRemoved
&& type == IDocument::TypeRemoved && !document->isModified()) { && !document->isModified()) {
// file removed, but unmodified files should be reloaded // file removed, but unmodified files should be reloaded
// so we close the file // so we close the file
documentsToClose << document; documentsToClose << document;
} else if (defaultBehavior == IDocument::IgnoreAll) { } else if (defaultBehavior == IDocument::IgnoreAll) {
// content change or removed, but settings say ignore // content change or removed, but settings say ignore
success = document->reload(&errorString, IDocument::FlagIgnore, type); success = document->reload(&errorString, IDocument::FlagIgnore, *type);
// either the default behavior is to always ask, // either the default behavior is to always ask,
// or the ReloadUnmodified default behavior didn't kick in, // or the ReloadUnmodified default behavior didn't kick in,
// so do whatever the IDocument wants us to do // so do whatever the IDocument wants us to do
} else { } else {
// check if IDocument wants us to ask // check if IDocument wants us to ask
if (document->reloadBehavior(trigger, type) == IDocument::BehaviorSilent) { if (document->reloadBehavior(trigger, *type) == IDocument::BehaviorSilent) {
// content change or removed, IDocument wants silent handling // content change or removed, IDocument wants silent handling
if (type == IDocument::TypeRemoved) if (type == IDocument::TypeRemoved)
documentsToClose << document; documentsToClose << document;
else else
success = document->reload(&errorString, IDocument::FlagReload, type); success = document->reload(&errorString, IDocument::FlagReload, *type);
// IDocument wants us to ask // IDocument wants us to ask
} else if (type == IDocument::TypeContents) { } else if (type == IDocument::TypeContents) {
// content change, IDocument wants to ask user // content change, IDocument wants to ask user

View File

@@ -27,6 +27,7 @@
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/infobar.h> #include <utils/infobar.h>
#include <utils/optional.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QFile> #include <QFile>
@@ -129,8 +130,6 @@
\value TypeContents \value TypeContents
The contents of the file changed. The contents of the file changed.
\value TypePermissions
The file permissions changed.
\value TypeRemoved \value TypeRemoved
The file was removed. The file was removed.
@@ -228,6 +227,7 @@ public:
QString autoSaveName; QString autoSaveName;
Utils::InfoBar *infoBar = nullptr; Utils::InfoBar *infoBar = nullptr;
Id id; Id id;
optional<bool> fileIsReadOnly;
bool temporary = false; bool temporary = false;
bool hasWriteWarning = false; bool hasWriteWarning = false;
bool restored = false; bool restored = false;
@@ -403,8 +403,6 @@ const Utils::FilePath &IDocument::filePath() const
*/ */
IDocument::ReloadBehavior IDocument::reloadBehavior(ChangeTrigger trigger, ChangeType type) const IDocument::ReloadBehavior IDocument::reloadBehavior(ChangeTrigger trigger, ChangeType type) const
{ {
if (type == TypePermissions)
return BehaviorSilent;
if (type == TypeContents && trigger == TriggerInternal && !isModified()) if (type == TypeContents && trigger == TriggerInternal && !isModified())
return BehaviorSilent; return BehaviorSilent;
return BehaviorAsk; return BehaviorAsk;
@@ -443,10 +441,18 @@ bool IDocument::reload(QString *errorString, ReloadFlag flag, ChangeType type)
} }
/*! /*!
\internal Updates the cached information about the read-only status of the backing file.
*/ */
void IDocument::checkPermissions() void IDocument::checkPermissions()
{ {
bool previousReadOnly = d->fileIsReadOnly.value_or(false);
if (!filePath().isEmpty()) {
d->fileIsReadOnly = !filePath().toFileInfo().isWritable();
} else {
d->fileIsReadOnly = false;
}
if (previousReadOnly != *(d->fileIsReadOnly))
emit changed();
} }
/*! /*!
@@ -524,7 +530,9 @@ bool IDocument::isFileReadOnly() const
{ {
if (filePath().isEmpty()) if (filePath().isEmpty())
return false; return false;
return !filePath().toFileInfo().isWritable(); if (!d->fileIsReadOnly)
const_cast<IDocument *>(this)->checkPermissions();
return d->fileIsReadOnly.value_or(false);
} }
/*! /*!

View File

@@ -68,7 +68,6 @@ public:
enum ChangeType { enum ChangeType {
TypeContents, TypeContents,
TypePermissions,
TypeRemoved TypeRemoved
}; };
@@ -104,7 +103,7 @@ public:
void setUniqueDisplayName(const QString &name); void setUniqueDisplayName(const QString &name);
QString uniqueDisplayName() const; QString uniqueDisplayName() const;
virtual bool isFileReadOnly() const; bool isFileReadOnly() const;
bool isTemporary() const; bool isTemporary() const;
void setTemporary(bool temporary); void setTemporary(bool temporary);
@@ -123,7 +122,7 @@ public:
virtual ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const; virtual ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const;
virtual bool reload(QString *errorString, ReloadFlag flag, ChangeType type); virtual bool reload(QString *errorString, ReloadFlag flag, ChangeType type);
virtual void checkPermissions(); void checkPermissions();
bool autoSave(QString *errorString, const QString &filePath); bool autoSave(QString *errorString, const QString &filePath);
void setRestoredFrom(const QString &name); void setRestoredFrom(const QString &name);

View File

@@ -225,9 +225,6 @@ bool FormWindowFile::reload(QString *errorString, ReloadFlag flag, ChangeType ty
if (!wasModified) if (!wasModified)
updateIsModified(); updateIsModified();
return true; return true;
}
if (type == TypePermissions) {
emit changed();
} else { } else {
emit aboutToReload(); emit aboutToReload();
const bool success const bool success
@@ -235,7 +232,6 @@ bool FormWindowFile::reload(QString *errorString, ReloadFlag flag, ChangeType ty
emit reloadFinished(success); emit reloadFinished(success);
return success; return success;
} }
return true;
} }
void FormWindowFile::setFallbackSaveAsFileName(const QString &fn) void FormWindowFile::setFallbackSaveAsFileName(const QString &fn)

View File

@@ -642,9 +642,7 @@ bool GenericProjectFile::reload(QString *errorString, IDocument::ReloadFlag flag
{ {
Q_UNUSED(errorString) Q_UNUSED(errorString)
Q_UNUSED(flag) Q_UNUSED(flag)
if (type == TypePermissions) Q_UNUSED(type)
return true;
if (Target *t = m_project->activeTarget()) if (Target *t = m_project->activeTarget())
static_cast<GenericBuildSystem *>(t->buildSystem())->refresh(m_options); static_cast<GenericBuildSystem *>(t->buildSystem())->refresh(m_options);

View File

@@ -148,7 +148,7 @@ Core::IDocument::OpenResult ImageViewerFile::openImpl(QString *errorString, cons
Core::IDocument::ReloadBehavior ImageViewerFile::reloadBehavior(ChangeTrigger state, ChangeType type) const Core::IDocument::ReloadBehavior ImageViewerFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
{ {
if (type == TypeRemoved || type == TypePermissions) if (type == TypeRemoved)
return BehaviorSilent; return BehaviorSilent;
if (type == TypeContents && state == TriggerInternal && !isModified()) if (type == TypeContents && state == TriggerInternal && !isModified())
return BehaviorSilent; return BehaviorSilent;
@@ -159,12 +159,9 @@ bool ImageViewerFile::reload(QString *errorString,
Core::IDocument::ReloadFlag flag, Core::IDocument::ReloadFlag flag,
Core::IDocument::ChangeType type) Core::IDocument::ChangeType type)
{ {
Q_UNUSED(type)
if (flag == FlagIgnore) if (flag == FlagIgnore)
return true; return true;
if (type == TypePermissions) {
emit changed();
return true;
}
emit aboutToReload(); emit aboutToReload();
bool success = (openImpl(errorString, filePath().toString()) == OpenResult::Success); bool success = (openImpl(errorString, filePath().toString()) == OpenResult::Success);
emit reloadFinished(success); emit reloadFinished(success);

View File

@@ -121,12 +121,9 @@ bool ModelDocument::isSaveAsAllowed() const
bool ModelDocument::reload(QString *errorString, Core::IDocument::ReloadFlag flag, bool ModelDocument::reload(QString *errorString, Core::IDocument::ReloadFlag flag,
Core::IDocument::ChangeType type) Core::IDocument::ChangeType type)
{ {
Q_UNUSED(type)
if (flag == FlagIgnore) if (flag == FlagIgnore)
return true; return true;
if (type == TypePermissions) {
emit changed();
return true;
}
try { try {
d->documentController->loadProject(filePath().toString()); d->documentController->loadProject(filePath().toString());
} catch (const qmt::FileNotFoundException &ex) { } catch (const qmt::FileNotFoundException &ex) {

View File

@@ -121,8 +121,7 @@ public:
{ {
Q_UNUSED(errorString) Q_UNUSED(errorString)
Q_UNUSED(flag) Q_UNUSED(flag)
if (type == TypePermissions) Q_UNUSED(type)
return true;
m_priFile->scheduleUpdate(); m_priFile->scheduleUpdate();
return true; return true;
} }

View File

@@ -268,19 +268,15 @@ bool ResourceEditorDocument::isSaveAsAllowed() const
bool ResourceEditorDocument::reload(QString *errorString, ReloadFlag flag, ChangeType type) bool ResourceEditorDocument::reload(QString *errorString, ReloadFlag flag, ChangeType type)
{ {
Q_UNUSED(type)
if (flag == FlagIgnore) if (flag == FlagIgnore)
return true; return true;
if (type == TypePermissions) {
emit changed();
} else {
emit aboutToReload(); emit aboutToReload();
QString fn = filePath().toString(); QString fn = filePath().toString();
const bool success = (open(errorString, fn, fn) == OpenResult::Success); const bool success = (open(errorString, fn, fn) == OpenResult::Success);
emit reloadFinished(success); emit reloadFinished(success);
return success; return success;
} }
return true;
}
void ResourceEditorDocument::dirtyChanged(bool dirty) void ResourceEditorDocument::dirtyChanged(bool dirty)
{ {

View File

@@ -70,8 +70,7 @@ public:
bool reload(QString *, ReloadFlag, ChangeType type) final bool reload(QString *, ReloadFlag, ChangeType type) final
{ {
if (type == TypePermissions) Q_UNUSED(type)
return true;
FolderNode *parent = m_node->parentFolderNode(); FolderNode *parent = m_node->parentFolderNode();
QTC_ASSERT(parent, return false); QTC_ASSERT(parent, return false);
parent->replaceSubtree(m_node, std::make_unique<ResourceTopLevelNode>( parent->replaceSubtree(m_node, std::make_unique<ResourceTopLevelNode>(

View File

@@ -136,11 +136,9 @@ bool ScxmlEditorDocument::isModified() const
bool ScxmlEditorDocument::reload(QString *errorString, ReloadFlag flag, ChangeType type) bool ScxmlEditorDocument::reload(QString *errorString, ReloadFlag flag, ChangeType type)
{ {
if (flag == FlagIgnore) { Q_UNUSED(type)
if (flag == FlagIgnore)
return true; return true;
} if (type == TypePermissions) {
emit changed();
} else {
emit aboutToReload(); emit aboutToReload();
emit reloadRequested(errorString, filePath().toString()); emit reloadRequested(errorString, filePath().toString());
const bool success = errorString->isEmpty(); const bool success = errorString->isEmpty();
@@ -148,9 +146,6 @@ bool ScxmlEditorDocument::reload(QString *errorString, ReloadFlag flag, ChangeTy
return success; return success;
} }
return true;
}
QString ScxmlEditorDocument::designWidgetContents() const QString ScxmlEditorDocument::designWidgetContents() const
{ {
return m_designWidget->contents(); return m_designWidget->contents();

View File

@@ -52,8 +52,6 @@ bool TaskFile::reload(QString *errorString, ReloadFlag flag, ChangeType type)
{ {
Q_UNUSED(flag) Q_UNUSED(flag)
if (type == TypePermissions)
return true;
if (type == TypeRemoved) { if (type == TypeRemoved) {
deleteLater(); deleteLater();
return true; return true;

View File

@@ -106,7 +106,6 @@ public:
QScopedPointer<Indenter> m_indenter; QScopedPointer<Indenter> m_indenter;
QScopedPointer<Formatter> m_formatter; QScopedPointer<Formatter> m_formatter;
bool m_fileIsReadOnly = false;
int m_autoSaveRevision = -1; int m_autoSaveRevision = -1;
TextMarks m_marksCache; // Marks not owned TextMarks m_marksCache; // Marks not owned
@@ -702,30 +701,11 @@ void TextDocument::setFilePath(const Utils::FilePath &newName)
IDocument::setFilePath(Utils::FilePath::fromUserInput(newName.toFileInfo().absoluteFilePath())); IDocument::setFilePath(Utils::FilePath::fromUserInput(newName.toFileInfo().absoluteFilePath()));
} }
bool TextDocument::isFileReadOnly() const
{
if (filePath().isEmpty()) //have no corresponding file, so editing is ok
return false;
return d->m_fileIsReadOnly;
}
bool TextDocument::isModified() const bool TextDocument::isModified() const
{ {
return d->m_document.isModified(); return d->m_document.isModified();
} }
void TextDocument::checkPermissions()
{
bool previousReadOnly = d->m_fileIsReadOnly;
if (!filePath().isEmpty()) {
d->m_fileIsReadOnly = !filePath().toFileInfo().isWritable();
} else {
d->m_fileIsReadOnly = false;
}
if (previousReadOnly != d->m_fileIsReadOnly)
emit changed();
}
Core::IDocument::OpenResult TextDocument::open(QString *errorString, const QString &fileName, Core::IDocument::OpenResult TextDocument::open(QString *errorString, const QString &fileName,
const QString &realFileName) const QString &realFileName)
{ {
@@ -747,7 +727,6 @@ Core::IDocument::OpenResult TextDocument::openImpl(QString *errorString, const Q
if (!fileName.isEmpty()) { if (!fileName.isEmpty()) {
const QFileInfo fi(fileName); const QFileInfo fi(fileName);
d->m_fileIsReadOnly = !fi.isWritable();
readResult = read(realFileName, &content, errorString); readResult = read(realFileName, &content, errorString);
const int chunks = content.size(); const int chunks = content.size();
@@ -864,13 +843,8 @@ bool TextDocument::reload(QString *errorString, ReloadFlag flag, ChangeType type
modificationChanged(true); modificationChanged(true);
return true; return true;
} }
if (type == TypePermissions) {
checkPermissions();
return true;
} else {
return reload(errorString); return reload(errorString);
} }
}
void TextDocument::setSyntaxHighlighter(SyntaxHighlighter *highlighter) void TextDocument::setSyntaxHighlighter(SyntaxHighlighter *highlighter)
{ {

View File

@@ -115,10 +115,8 @@ public:
QByteArray contents() const override; QByteArray contents() const override;
bool setContents(const QByteArray &contents) override; bool setContents(const QByteArray &contents) override;
bool shouldAutoSave() const override; bool shouldAutoSave() const override;
bool isFileReadOnly() const override;
bool isModified() const override; bool isModified() const override;
bool isSaveAsAllowed() const override; bool isSaveAsAllowed() const override;
void checkPermissions() override;
bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override; bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override;
void setFilePath(const Utils::FilePath &newName) override; void setFilePath(const Utils::FilePath &newName) override;