From 72e19c4886066cd71cb075af0efde8f701fcfb24 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Thu, 20 Oct 2016 14:45:15 +0200 Subject: [PATCH] Fix marking files modified externally as modified It replaces 59c90e00c1e8b18d120f5b5c15b331b1ee427ace and d0c537ca759aa7facfdd0efedac5f5385d16ddb9 Task-number: QTCREATORBUG-17048 Change-Id: Ief4b1b72f2e5e7b1711be05d4ea8c03bbbf48fdf Reviewed-by: Tobias Hunger --- src/libs/utils/guard.cpp | 113 ++++++++++++++++++++++++ src/libs/utils/guard.h | 53 +++++++++++ src/libs/utils/utils-lib.pri | 6 +- src/libs/utils/utils.qbs | 2 + src/plugins/designer/formwindowfile.cpp | 17 +++- src/plugins/designer/formwindowfile.h | 2 + src/plugins/texteditor/textdocument.cpp | 38 ++++++-- src/plugins/texteditor/textdocument.h | 1 + 8 files changed, 220 insertions(+), 12 deletions(-) create mode 100644 src/libs/utils/guard.cpp create mode 100644 src/libs/utils/guard.h diff --git a/src/libs/utils/guard.cpp b/src/libs/utils/guard.cpp new file mode 100644 index 00000000000..925bdb226f9 --- /dev/null +++ b/src/libs/utils/guard.cpp @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "guard.h" +#include "qtcassert.h" + +/*! \class Utils::Guard + + \brief The Guard class implements a recursive guard with locking mechanism. + + It may be used as an alternative to QSignalBlocker. + QSignalBlocker blocks all signals of the object + which is usually not desirable. It may also block signals + which are needed internally by the object itself. + The Guard and GuardLocker classes don't block signals at all. + + When calling a object's method which may in turn emit a signal + which you are connected to, and you want to ignore + this notification, you should keep the Guard object + as your class member and declare the GuardLocker object + just before calling the mentioned method, like: + + \code + class MyClass : public QObject + { + \dots + private: + Guard updateGuard; // member of your class + }; + + \dots + + void MyClass::updateOtherObject() + { + GuardLocker updatelocker(updateGuard); + otherObject->update(); // this may trigger a signal + } + \endcode + + Inside a slot which is connected to the other's object signal + you may check if the guard is locked and ignore the further + operations in this case: + + \code + void MyClass::otherObjectUpdated() + { + if (updateGuard.isLocked) + return; + + // we didn't trigger the update + // so do update now + \dots + } + \endcode + + The GuardLock unlocks the Guard in it's destructor. + + The Guard object is recursive, you may declare many GuardLocker + objects for the same Guard instance and the Guard will be locked + as long as at least one GuardLocker object created for the Guard + is in scope. +*/ + +namespace Utils { + +Guard::Guard() +{ +} + +Guard::~Guard() +{ + QTC_CHECK(m_lockCount == 0); +} + +bool Guard::isLocked() const +{ + return m_lockCount; +} + +GuardLocker::GuardLocker(Guard &guard) + : m_guard(guard) +{ + ++m_guard.m_lockCount; +} + +GuardLocker::~GuardLocker() +{ + --m_guard.m_lockCount; +} + +} // namespace Utils diff --git a/src/libs/utils/guard.h b/src/libs/utils/guard.h new file mode 100644 index 00000000000..41bfd822887 --- /dev/null +++ b/src/libs/utils/guard.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "utils_global.h" + +namespace Utils { + +class QTCREATOR_UTILS_EXPORT Guard +{ +public: + Guard(); + ~Guard(); + bool isLocked() const; +private: + int m_lockCount = 0; + friend class GuardLocker; +}; + +class QTCREATOR_UTILS_EXPORT GuardLocker +{ +public: + GuardLocker(Guard &guard); + ~GuardLocker(); + +private: + Guard &m_guard; +}; + +} // namespace Utils diff --git a/src/libs/utils/utils-lib.pri b/src/libs/utils/utils-lib.pri index fc7b552ff67..8442d7c351c 100644 --- a/src/libs/utils/utils-lib.pri +++ b/src/libs/utils/utils-lib.pri @@ -100,7 +100,8 @@ SOURCES += $$PWD/environment.cpp \ $$PWD/icon.cpp \ $$PWD/port.cpp \ $$PWD/runextensions.cpp \ - $$PWD/utilsicons.cpp + $$PWD/utilsicons.cpp \ + $$PWD/guard.cpp win32:SOURCES += $$PWD/consoleprocess_win.cpp else:SOURCES += $$PWD/consoleprocess_unix.cpp @@ -217,7 +218,8 @@ HEADERS += \ $$PWD/smallstringvector.h \ $$PWD/smallstringlayout.h \ $$PWD/sizedarray.h \ - $$PWD/smallstringio.h + $$PWD/smallstringio.h \ + $$PWD/guard.h FORMS += $$PWD/filewizardpage.ui \ $$PWD/projectintropage.ui \ diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs index 54a2f71b840..3bf52fc8c6b 100644 --- a/src/libs/utils/utils.qbs +++ b/src/libs/utils/utils.qbs @@ -114,6 +114,8 @@ Project { "flowlayout.cpp", "flowlayout.h", "functiontraits.h", + "guard.cpp", + "guard.h", "historycompleter.cpp", "historycompleter.h", "hostosinfo.h", diff --git a/src/plugins/designer/formwindowfile.cpp b/src/plugins/designer/formwindowfile.cpp index 44ec146b776..e5b6a313bef 100644 --- a/src/plugins/designer/formwindowfile.cpp +++ b/src/plugins/designer/formwindowfile.cpp @@ -183,6 +183,9 @@ void FormWindowFile::setFilePath(const FileName &newName) void FormWindowFile::updateIsModified() { + if (m_modificationChangedGuard.isLocked()) + return; + bool value = m_formWindow && m_formWindow->isDirty(); if (value) emit contentsChanged(); @@ -209,8 +212,20 @@ bool FormWindowFile::isSaveAsAllowed() const bool FormWindowFile::reload(QString *errorString, ReloadFlag flag, ChangeType type) { - if (flag == FlagIgnore) + if (flag == FlagIgnore) { + if (!m_formWindow || type != TypeContents) + return true; + const bool wasModified = m_formWindow->isDirty(); + { + Utils::GuardLocker locker(m_modificationChangedGuard); + // hack to ensure we clean the clear state in form window + m_formWindow->setDirty(false); + m_formWindow->setDirty(true); + } + if (!wasModified) + updateIsModified(); return true; + } if (type == TypePermissions) { emit changed(); } else { diff --git a/src/plugins/designer/formwindowfile.h b/src/plugins/designer/formwindowfile.h index 7ee62bb9518..a78d576ad9a 100644 --- a/src/plugins/designer/formwindowfile.h +++ b/src/plugins/designer/formwindowfile.h @@ -26,6 +26,7 @@ #pragma once #include +#include #include @@ -84,6 +85,7 @@ private: QPointer m_formWindow; bool m_isModified = false; ResourceHandler *m_resourceHandler = nullptr; + Utils::Guard m_modificationChangedGuard; }; } // namespace Internal diff --git a/src/plugins/texteditor/textdocument.cpp b/src/plugins/texteditor/textdocument.cpp index e3e73546443..dc7bd87296f 100644 --- a/src/plugins/texteditor/textdocument.cpp +++ b/src/plugins/texteditor/textdocument.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -106,6 +107,7 @@ public: int m_autoSaveRevision; TextMarks m_marksCache; // Marks not owned + Utils::Guard m_modificationChangedGuard; }; QTextCursor TextDocumentPrivate::indentOrUnindent(const QTextCursor &textCursor, bool doIndent, @@ -236,14 +238,8 @@ void TextDocumentPrivate::updateRevisions() TextDocument::TextDocument(Id id) : d(new TextDocumentPrivate) { - QObject::connect(&d->m_document, &QTextDocument::modificationChanged, [this](bool modified) { - // we only want to update the block revisions when going back to the saved version, - // e.g. with undo - if (!modified) - d->updateRevisions(); - emit changed(); - }); - + connect(&d->m_document, &QTextDocument::modificationChanged, + this, &TextDocument::modificationChanged); connect(&d->m_document, &QTextDocument::contentsChanged, this, &Core::IDocument::contentsChanged); connect(&d->m_document, &QTextDocument::contentsChange, @@ -723,8 +719,21 @@ bool TextDocument::setPlainText(const QString &text) bool TextDocument::reload(QString *errorString, ReloadFlag flag, ChangeType type) { - if (flag == FlagIgnore) + if (flag == FlagIgnore) { + if (type != TypeContents) + return true; + + const bool wasModified = document()->isModified(); + { + Utils::GuardLocker locker(d->m_modificationChangedGuard); + // hack to ensure we clean the clear state in QTextDocument + document()->setModified(false); + document()->setModified(true); + } + if (!wasModified) + modificationChanged(true); return true; + } if (type == TypePermissions) { checkPermissions(); return true; @@ -808,6 +817,17 @@ void TextDocument::ensureFinalNewLine(QTextCursor& cursor) } } +void TextDocument::modificationChanged(bool modified) +{ + if (d->m_modificationChangedGuard.isLocked()) + return; + // we only want to update the block revisions when going back to the saved version, + // e.g. with undo + if (!modified) + d->updateRevisions(); + emit changed(); +} + TextMarks TextDocument::marks() const { return d->m_marksCache; diff --git a/src/plugins/texteditor/textdocument.h b/src/plugins/texteditor/textdocument.h index 6aa2e0bd7c6..30576043b40 100644 --- a/src/plugins/texteditor/textdocument.h +++ b/src/plugins/texteditor/textdocument.h @@ -151,6 +151,7 @@ private: bool reload); void cleanWhitespace(QTextCursor &cursor, bool cleanIndentation, bool inEntireDocument); void ensureFinalNewLine(QTextCursor &cursor); + void modificationChanged(bool modified); TextDocumentPrivate *d; };