Prevent reloading database while editing an entry or group

* Fix #3933 and  fix #3857. Interaction with entries and groups is disabled while the database is being reloaded or saved to prevent changes from occurring. Prevent the database from being reloading if an entry or group is currently being edited.

* Fix #3941 - Only notify components when the database file actually changes (determined by checksum). This prevents spurious merge requests when the file is merely touched by another service (e.g., DropBox).

* Fix code format of ElidedLabel.cpp
This commit is contained in:
Jonathan White 2019-12-12 22:40:17 -05:00
parent f9cb2bd5df
commit 8e76c30dd1
5 changed files with 46 additions and 24 deletions

View File

@ -35,11 +35,10 @@ namespace
FileWatcher::FileWatcher(QObject* parent)
: QObject(parent)
, m_ignoreFileChange(false)
{
connect(&m_fileWatcher, SIGNAL(fileChanged(QString)), SLOT(onWatchedFileChanged()));
connect(&m_fileWatcher, SIGNAL(fileChanged(QString)), SLOT(checkFileChanged()));
connect(&m_fileChecksumTimer, SIGNAL(timeout()), SLOT(checkFileChanged()));
connect(&m_fileChangeDelayTimer, SIGNAL(timeout()), SIGNAL(fileChanged()));
connect(&m_fileChecksumTimer, SIGNAL(timeout()), SLOT(checkFileChecksum()));
m_fileChangeDelayTimer.setSingleShot(true);
m_fileIgnoreDelayTimer.setSingleShot(true);
}
@ -101,17 +100,6 @@ void FileWatcher::resume()
}
}
void FileWatcher::onWatchedFileChanged()
{
// Don't notify if we are ignoring events or already started a notification chain
if (shouldIgnoreChanges()) {
return;
}
m_fileChecksum = calculateChecksum();
m_fileChangeDelayTimer.start(0);
}
bool FileWatcher::shouldIgnoreChanges()
{
return m_filePath.isEmpty() || m_ignoreFileChange || m_fileIgnoreDelayTimer.isActive()
@ -123,15 +111,23 @@ bool FileWatcher::hasSameFileChecksum()
return calculateChecksum() == m_fileChecksum;
}
void FileWatcher::checkFileChecksum()
void FileWatcher::checkFileChanged()
{
if (shouldIgnoreChanges()) {
return;
}
if (!hasSameFileChecksum()) {
onWatchedFileChanged();
// Prevent reentrance
m_ignoreFileChange = true;
// Only trigger the change notice if there is a checksum mismatch
auto checksum = calculateChecksum();
if (checksum != m_fileChecksum) {
m_fileChecksum = checksum;
m_fileChangeDelayTimer.start(0);
}
m_ignoreFileChange = false;
}
QByteArray FileWatcher::calculateChecksum()

View File

@ -43,8 +43,7 @@ public slots:
void resume();
private slots:
void onWatchedFileChanged();
void checkFileChecksum();
void checkFileChanged();
private:
QByteArray calculateChecksum();
@ -56,8 +55,8 @@ private:
QTimer m_fileChangeDelayTimer;
QTimer m_fileIgnoreDelayTimer;
QTimer m_fileChecksumTimer;
int m_fileChecksumSizeBytes;
bool m_ignoreFileChange;
int m_fileChecksumSizeBytes = -1;
bool m_ignoreFileChange = false;
};
class BulkFileWatcher : public QObject

View File

@ -275,6 +275,11 @@ bool DatabaseWidget::isEntryEditActive() const
return currentWidget() == m_editEntryWidget;
}
bool DatabaseWidget::isGroupEditActive() const
{
return currentWidget() == m_editGroupWidget;
}
bool DatabaseWidget::isEditWidgetModified() const
{
if (currentWidget() == m_editEntryWidget) {
@ -387,6 +392,8 @@ void DatabaseWidget::createEntry()
void DatabaseWidget::replaceDatabase(QSharedPointer<Database> db)
{
Q_ASSERT(!isEntryEditActive() && !isGroupEditActive());
// Save off new parent UUID which will be valid when creating a new entry
QUuid newParentUuid;
if (m_newParent) {
@ -1421,7 +1428,8 @@ bool DatabaseWidget::lock()
void DatabaseWidget::reloadDatabaseFile()
{
if (!m_db || isLocked()) {
// Ignore reload if we are locked or currently editing an entry or group
if (!m_db || isLocked() || isEntryEditActive() || isGroupEditActive()) {
return;
}
@ -1441,6 +1449,11 @@ void DatabaseWidget::reloadDatabaseFile()
}
}
// Lock out interactions
m_entryView->setDisabled(true);
m_groupView->setDisabled(true);
QApplication::processEvents();
QString error;
auto db = QSharedPointer<Database>::create(m_db->filePath());
if (db->open(database()->key(), &error)) {
@ -1480,6 +1493,10 @@ void DatabaseWidget::reloadDatabaseFile()
// Mark db as modified since existing data may differ from file or file was deleted
m_db->markAsModified();
}
// Return control
m_entryView->setDisabled(false);
m_groupView->setDisabled(false);
}
int DatabaseWidget::numberOfSelectedEntries() const
@ -1620,11 +1637,20 @@ bool DatabaseWidget::save()
m_blockAutoSave = true;
++m_saveAttempts;
// TODO: Make this async, but lock out the database widget to prevent re-entrance
// TODO: Make this async
// Lock out interactions
m_entryView->setDisabled(true);
m_groupView->setDisabled(true);
QApplication::processEvents();
bool useAtomicSaves = config()->get("UseAtomicSaves", true).toBool();
QString errorMessage;
bool ok = m_db->save(&errorMessage, useAtomicSaves, config()->get("BackupBeforeSave").toBool());
// Return control
m_entryView->setDisabled(false);
m_groupView->setDisabled(false);
if (ok) {
m_saveAttempts = 0;
m_blockAutoSave = false;

View File

@ -81,6 +81,7 @@ public:
bool isLocked() const;
bool isSearchActive() const;
bool isEntryEditActive() const;
bool isGroupEditActive() const;
QString getCurrentSearch();
void refreshSearch();

View File

@ -105,7 +105,7 @@ void ElidedLabel::updateElidedText()
const QFontMetrics metrix(font());
displayText = metrix.elidedText(m_rawText, m_elideMode, width() - 2);
}
bool hasUrl = !m_url.isEmpty();
setText(hasUrl ? htmlLinkTemplate.arg(m_url.toHtmlEscaped(), displayText) : displayText);
setOpenExternalLinks(!hasUrl);