mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-02-08 18:58:29 -05:00
Add direct write save option
* Closes #6335 * Modify application settings presentation to allow for alternative saving strategies * Transition Database::save calls to using flags to control saving behavior. Reduces boolean flags on function call. * Made direct write save option a local setting to prevent unintentional carry over between platforms.
This commit is contained in:
parent
484bc5dd01
commit
f2aa32c7b0
@ -380,10 +380,6 @@
|
|||||||
<source>Automatically launch KeePassXC at system startup</source>
|
<source>Automatically launch KeePassXC at system startup</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Safely save database files (disable if experiencing problems with Dropbox, etc.)</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>User Interface</source>
|
<source>User Interface</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
@ -432,6 +428,18 @@
|
|||||||
<source>Hide expired entries from Auto-Type</source>
|
<source>Hide expired entries from Auto-Type</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use alternative saving method (may solve problems with Dropbox, Google Drive, GVFS, etc.)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Temporary file moved into place</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Directly write to database file (dangerous)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ApplicationSettingsWidgetSecurity</name>
|
<name>ApplicationSettingsWidgetSecurity</name>
|
||||||
|
@ -121,7 +121,7 @@ int Add::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<Q
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
if (!database->save(&errorMessage, true, false)) {
|
if (!database->save(Database::Atomic, false, &errorMessage)) {
|
||||||
err << QObject::tr("Writing the database failed %1.").arg(errorMessage) << endl;
|
err << QObject::tr("Writing the database failed %1.").arg(errorMessage) << endl;
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ int AddGroup::executeWithDatabase(QSharedPointer<Database> database, QSharedPoin
|
|||||||
newGroup->setParent(parentGroup);
|
newGroup->setParent(parentGroup);
|
||||||
|
|
||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
if (!database->save(&errorMessage, true, false)) {
|
if (!database->save(Database::Atomic, false, &errorMessage)) {
|
||||||
err << QObject::tr("Writing the database failed %1.").arg(errorMessage) << endl;
|
err << QObject::tr("Writing the database failed %1.").arg(errorMessage) << endl;
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
@ -165,7 +165,7 @@ int Create::execute(const QStringList& arguments)
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
if (!db->saveAs(databaseFilename, &errorMessage, true, false)) {
|
if (!db->saveAs(databaseFilename, Database::Atomic, false, &errorMessage)) {
|
||||||
err << QObject::tr("Failed to save the database: %1.").arg(errorMessage) << endl;
|
err << QObject::tr("Failed to save the database: %1.").arg(errorMessage) << endl;
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ int Edit::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
|||||||
entry->endUpdate();
|
entry->endUpdate();
|
||||||
|
|
||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
if (!database->save(&errorMessage, true, false)) {
|
if (!database->save(Database::Atomic, false, &errorMessage)) {
|
||||||
err << QObject::tr("Writing the database failed: %1").arg(errorMessage) << endl;
|
err << QObject::tr("Writing the database failed: %1").arg(errorMessage) << endl;
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ int Import::execute(const QStringList& arguments)
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!db->saveAs(dbPath, &errorMessage, true, false)) {
|
if (!db->saveAs(dbPath, Database::Atomic, false, &errorMessage)) {
|
||||||
err << QObject::tr("Failed to save the database: %1.").arg(errorMessage) << endl;
|
err << QObject::tr("Failed to save the database: %1.").arg(errorMessage) << endl;
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ int Merge::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer
|
|||||||
|
|
||||||
if (!changeList.isEmpty() && !parser->isSet(Merge::DryRunOption)) {
|
if (!changeList.isEmpty() && !parser->isSet(Merge::DryRunOption)) {
|
||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
if (!database->save(&errorMessage, true, false)) {
|
if (!database->save(Database::Atomic, false, &errorMessage)) {
|
||||||
err << QObject::tr("Unable to save database to file : %1").arg(errorMessage) << endl;
|
err << QObject::tr("Unable to save database to file : %1").arg(errorMessage) << endl;
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ int Move::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
|||||||
entry->endUpdate();
|
entry->endUpdate();
|
||||||
|
|
||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
if (!database->save(&errorMessage, true, false)) {
|
if (!database->save(Database::Atomic, false, &errorMessage)) {
|
||||||
err << QObject::tr("Writing the database failed %1.").arg(errorMessage) << endl;
|
err << QObject::tr("Writing the database failed %1.").arg(errorMessage) << endl;
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ int Remove::executeWithDatabase(QSharedPointer<Database> database, QSharedPointe
|
|||||||
};
|
};
|
||||||
|
|
||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
if (!database->save(&errorMessage, true, false)) {
|
if (!database->save(Database::Atomic, false, &errorMessage)) {
|
||||||
err << QObject::tr("Unable to save database to file: %1").arg(errorMessage) << endl;
|
err << QObject::tr("Unable to save database to file: %1").arg(errorMessage) << endl;
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ int RemoveGroup::executeWithDatabase(QSharedPointer<Database> database, QSharedP
|
|||||||
};
|
};
|
||||||
|
|
||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
if (!database->save(&errorMessage, true, false)) {
|
if (!database->save(Database::Atomic, false, &errorMessage)) {
|
||||||
err << QObject::tr("Unable to save database to file: %1").arg(errorMessage) << endl;
|
err << QObject::tr("Unable to save database to file: %1").arg(errorMessage) << endl;
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,7 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
|
|||||||
{Config::AutoSaveNonDataChanges,{QS("AutoSaveNonDataChanges"), Roaming, true}},
|
{Config::AutoSaveNonDataChanges,{QS("AutoSaveNonDataChanges"), Roaming, true}},
|
||||||
{Config::BackupBeforeSave,{QS("BackupBeforeSave"), Roaming, false}},
|
{Config::BackupBeforeSave,{QS("BackupBeforeSave"), Roaming, false}},
|
||||||
{Config::UseAtomicSaves,{QS("UseAtomicSaves"), Roaming, true}},
|
{Config::UseAtomicSaves,{QS("UseAtomicSaves"), Roaming, true}},
|
||||||
|
{Config::UseDirectWriteSaves,{QS("UseDirectWriteSaves"), Local, false}},
|
||||||
{Config::SearchLimitGroup,{QS("SearchLimitGroup"), Roaming, false}},
|
{Config::SearchLimitGroup,{QS("SearchLimitGroup"), Roaming, false}},
|
||||||
{Config::MinimizeOnOpenUrl,{QS("MinimizeOnOpenUrl"), Roaming, false}},
|
{Config::MinimizeOnOpenUrl,{QS("MinimizeOnOpenUrl"), Roaming, false}},
|
||||||
{Config::HideWindowOnCopy,{QS("HideWindowOnCopy"), Roaming, false}},
|
{Config::HideWindowOnCopy,{QS("HideWindowOnCopy"), Roaming, false}},
|
||||||
|
@ -44,6 +44,7 @@ public:
|
|||||||
AutoSaveNonDataChanges,
|
AutoSaveNonDataChanges,
|
||||||
BackupBeforeSave,
|
BackupBeforeSave,
|
||||||
UseAtomicSaves,
|
UseAtomicSaves,
|
||||||
|
UseDirectWriteSaves,
|
||||||
SearchLimitGroup,
|
SearchLimitGroup,
|
||||||
MinimizeOnOpenUrl,
|
MinimizeOnOpenUrl,
|
||||||
HideWindowOnCopy,
|
HideWindowOnCopy,
|
||||||
|
@ -181,7 +181,7 @@ bool Database::isSaving()
|
|||||||
* @param backup Backup the existing database file, if exists
|
* @param backup Backup the existing database file, if exists
|
||||||
* @return true on success
|
* @return true on success
|
||||||
*/
|
*/
|
||||||
bool Database::save(QString* error, bool atomic, bool backup)
|
bool Database::save(SaveAction action, bool backup, QString* error)
|
||||||
{
|
{
|
||||||
Q_ASSERT(!m_data.filePath.isEmpty());
|
Q_ASSERT(!m_data.filePath.isEmpty());
|
||||||
if (m_data.filePath.isEmpty()) {
|
if (m_data.filePath.isEmpty()) {
|
||||||
@ -191,7 +191,7 @@ bool Database::save(QString* error, bool atomic, bool backup)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return saveAs(m_data.filePath, error, atomic, backup);
|
return saveAs(m_data.filePath, action, backup, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -212,7 +212,7 @@ bool Database::save(QString* error, bool atomic, bool backup)
|
|||||||
* @param backup Backup the existing database file, if exists
|
* @param backup Backup the existing database file, if exists
|
||||||
* @return true on success
|
* @return true on success
|
||||||
*/
|
*/
|
||||||
bool Database::saveAs(const QString& filePath, QString* error, bool atomic, bool backup)
|
bool Database::saveAs(const QString& filePath, SaveAction action, bool backup, QString* error)
|
||||||
{
|
{
|
||||||
// Disallow overlapping save operations
|
// Disallow overlapping save operations
|
||||||
if (isSaving()) {
|
if (isSaving()) {
|
||||||
@ -260,7 +260,7 @@ bool Database::saveAs(const QString& filePath, QString* error, bool atomic, bool
|
|||||||
QFileInfo fileInfo(filePath);
|
QFileInfo fileInfo(filePath);
|
||||||
auto realFilePath = fileInfo.exists() ? fileInfo.canonicalFilePath() : fileInfo.absoluteFilePath();
|
auto realFilePath = fileInfo.exists() ? fileInfo.canonicalFilePath() : fileInfo.absoluteFilePath();
|
||||||
bool isNewFile = !QFile::exists(realFilePath);
|
bool isNewFile = !QFile::exists(realFilePath);
|
||||||
bool ok = AsyncTask::runAndWaitForFuture([&] { return performSave(realFilePath, error, atomic, backup); });
|
bool ok = AsyncTask::runAndWaitForFuture([&] { return performSave(realFilePath, action, backup, error); });
|
||||||
if (ok) {
|
if (ok) {
|
||||||
markAsClean();
|
markAsClean();
|
||||||
setFilePath(filePath);
|
setFilePath(filePath);
|
||||||
@ -276,14 +276,19 @@ bool Database::saveAs(const QString& filePath, QString* error, bool atomic, bool
|
|||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Database::performSave(const QString& filePath, QString* error, bool atomic, bool backup)
|
bool Database::performSave(const QString& filePath, SaveAction action, bool backup, QString* error)
|
||||||
{
|
{
|
||||||
|
if (backup) {
|
||||||
|
backupDatabase(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||||
QFileInfo info(filePath);
|
QFileInfo info(filePath);
|
||||||
auto createTime = info.exists() ? info.birthTime() : QDateTime::currentDateTime();
|
auto createTime = info.exists() ? info.birthTime() : QDateTime::currentDateTime();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (atomic) {
|
switch (action) {
|
||||||
|
case Atomic: {
|
||||||
QSaveFile saveFile(filePath);
|
QSaveFile saveFile(filePath);
|
||||||
if (saveFile.open(QIODevice::WriteOnly)) {
|
if (saveFile.open(QIODevice::WriteOnly)) {
|
||||||
// write the database to the file
|
// write the database to the file
|
||||||
@ -291,12 +296,8 @@ bool Database::performSave(const QString& filePath, QString* error, bool atomic,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (backup) {
|
|
||||||
backupDatabase(filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||||
// Retain orginal creation time
|
// Retain original creation time
|
||||||
saveFile.setFileTime(createTime, QFile::FileBirthTime);
|
saveFile.setFileTime(createTime, QFile::FileBirthTime);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -309,20 +310,17 @@ bool Database::performSave(const QString& filePath, QString* error, bool atomic,
|
|||||||
if (error) {
|
if (error) {
|
||||||
*error = saveFile.errorString();
|
*error = saveFile.errorString();
|
||||||
}
|
}
|
||||||
} else {
|
break;
|
||||||
|
}
|
||||||
|
case TempFile: {
|
||||||
QTemporaryFile tempFile;
|
QTemporaryFile tempFile;
|
||||||
if (tempFile.open()) {
|
if (tempFile.open()) {
|
||||||
// write the database to the file
|
// write the database to the file
|
||||||
if (!writeDatabase(&tempFile, error)) {
|
if (!writeDatabase(&tempFile, error)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
tempFile.close(); // flush to disk
|
tempFile.close(); // flush to disk
|
||||||
|
|
||||||
if (backup) {
|
|
||||||
backupDatabase(filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete the original db and move the temp file in place
|
// Delete the original db and move the temp file in place
|
||||||
auto perms = QFile::permissions(filePath);
|
auto perms = QFile::permissions(filePath);
|
||||||
QFile::remove(filePath);
|
QFile::remove(filePath);
|
||||||
@ -353,6 +351,23 @@ bool Database::performSave(const QString& filePath, QString* error, bool atomic,
|
|||||||
if (error) {
|
if (error) {
|
||||||
*error = tempFile.errorString();
|
*error = tempFile.errorString();
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DirectWrite: {
|
||||||
|
// Open the original database file for direct-write
|
||||||
|
QFile dbFile(filePath);
|
||||||
|
if (dbFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||||
|
if (!writeDatabase(&dbFile, error)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
dbFile.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (error) {
|
||||||
|
*error = dbFile.errorString();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Saving failed
|
// Saving failed
|
||||||
|
@ -63,6 +63,13 @@ public:
|
|||||||
};
|
};
|
||||||
static const quint32 CompressionAlgorithmMax = CompressionGZip;
|
static const quint32 CompressionAlgorithmMax = CompressionGZip;
|
||||||
|
|
||||||
|
enum SaveAction
|
||||||
|
{
|
||||||
|
Atomic, // Saves are transactional and atomic
|
||||||
|
TempFile, // Write to a temporary location then move into place, may be non-atomic
|
||||||
|
DirectWrite, // Directly write to the destination file (dangerous)
|
||||||
|
};
|
||||||
|
|
||||||
Database();
|
Database();
|
||||||
explicit Database(const QString& filePath);
|
explicit Database(const QString& filePath);
|
||||||
~Database() override;
|
~Database() override;
|
||||||
@ -72,8 +79,8 @@ public:
|
|||||||
QSharedPointer<const CompositeKey> key,
|
QSharedPointer<const CompositeKey> key,
|
||||||
QString* error = nullptr,
|
QString* error = nullptr,
|
||||||
bool readOnly = false);
|
bool readOnly = false);
|
||||||
bool save(QString* error = nullptr, bool atomic = true, bool backup = false);
|
bool save(SaveAction action = Atomic, bool backup = false, QString* error = nullptr);
|
||||||
bool saveAs(const QString& filePath, QString* error = nullptr, bool atomic = true, bool backup = false);
|
bool saveAs(const QString& filePath, SaveAction action = Atomic, bool backup = false, QString* error = nullptr);
|
||||||
bool extract(QByteArray&, QString* error = nullptr);
|
bool extract(QByteArray&, QString* error = nullptr);
|
||||||
bool import(const QString& xmlExportPath, QString* error = nullptr);
|
bool import(const QString& xmlExportPath, QString* error = nullptr);
|
||||||
|
|
||||||
@ -198,7 +205,7 @@ private:
|
|||||||
bool writeDatabase(QIODevice* device, QString* error = nullptr);
|
bool writeDatabase(QIODevice* device, QString* error = nullptr);
|
||||||
bool backupDatabase(const QString& filePath);
|
bool backupDatabase(const QString& filePath);
|
||||||
bool restoreDatabase(const QString& filePath);
|
bool restoreDatabase(const QString& filePath);
|
||||||
bool performSave(const QString& filePath, QString* error, bool atomic, bool backup);
|
bool performSave(const QString& filePath, SaveAction flags, bool backup, QString* error);
|
||||||
void startModifiedTimer();
|
void startModifiedTimer();
|
||||||
void stopModifiedTimer();
|
void stopModifiedTimer();
|
||||||
|
|
||||||
|
@ -109,6 +109,8 @@ ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent)
|
|||||||
connect(m_generalUi->systrayShowCheckBox, SIGNAL(toggled(bool)), SLOT(systrayToggled(bool)));
|
connect(m_generalUi->systrayShowCheckBox, SIGNAL(toggled(bool)), SLOT(systrayToggled(bool)));
|
||||||
connect(m_generalUi->rememberLastDatabasesCheckBox, SIGNAL(toggled(bool)), SLOT(rememberDatabasesToggled(bool)));
|
connect(m_generalUi->rememberLastDatabasesCheckBox, SIGNAL(toggled(bool)), SLOT(rememberDatabasesToggled(bool)));
|
||||||
connect(m_generalUi->resetSettingsButton, SIGNAL(clicked()), SLOT(resetSettings()));
|
connect(m_generalUi->resetSettingsButton, SIGNAL(clicked()), SLOT(resetSettings()));
|
||||||
|
connect(m_generalUi->useAlternativeSaveCheckBox, SIGNAL(toggled(bool)),
|
||||||
|
m_generalUi->alternativeSaveComboBox, SLOT(setEnabled(bool)));
|
||||||
|
|
||||||
connect(m_secUi->clearClipboardCheckBox, SIGNAL(toggled(bool)),
|
connect(m_secUi->clearClipboardCheckBox, SIGNAL(toggled(bool)),
|
||||||
m_secUi->clearClipboardSpinBox, SLOT(setEnabled(bool)));
|
m_secUi->clearClipboardSpinBox, SLOT(setEnabled(bool)));
|
||||||
@ -186,7 +188,8 @@ void ApplicationSettingsWidget::loadSettings()
|
|||||||
m_generalUi->autoSaveOnExitCheckBox->setChecked(config()->get(Config::AutoSaveOnExit).toBool());
|
m_generalUi->autoSaveOnExitCheckBox->setChecked(config()->get(Config::AutoSaveOnExit).toBool());
|
||||||
m_generalUi->autoSaveNonDataChangesCheckBox->setChecked(config()->get(Config::AutoSaveNonDataChanges).toBool());
|
m_generalUi->autoSaveNonDataChangesCheckBox->setChecked(config()->get(Config::AutoSaveNonDataChanges).toBool());
|
||||||
m_generalUi->backupBeforeSaveCheckBox->setChecked(config()->get(Config::BackupBeforeSave).toBool());
|
m_generalUi->backupBeforeSaveCheckBox->setChecked(config()->get(Config::BackupBeforeSave).toBool());
|
||||||
m_generalUi->useAtomicSavesCheckBox->setChecked(config()->get(Config::UseAtomicSaves).toBool());
|
m_generalUi->useAlternativeSaveCheckBox->setChecked(!config()->get(Config::UseAtomicSaves).toBool());
|
||||||
|
m_generalUi->alternativeSaveComboBox->setCurrentIndex(config()->get(Config::UseDirectWriteSaves).toBool() ? 1 : 0);
|
||||||
m_generalUi->autoReloadOnChangeCheckBox->setChecked(config()->get(Config::AutoReloadOnChange).toBool());
|
m_generalUi->autoReloadOnChangeCheckBox->setChecked(config()->get(Config::AutoReloadOnChange).toBool());
|
||||||
m_generalUi->minimizeAfterUnlockCheckBox->setChecked(config()->get(Config::MinimizeAfterUnlock).toBool());
|
m_generalUi->minimizeAfterUnlockCheckBox->setChecked(config()->get(Config::MinimizeAfterUnlock).toBool());
|
||||||
m_generalUi->minimizeOnOpenUrlCheckBox->setChecked(config()->get(Config::MinimizeOnOpenUrl).toBool());
|
m_generalUi->minimizeOnOpenUrlCheckBox->setChecked(config()->get(Config::MinimizeOnOpenUrl).toBool());
|
||||||
@ -323,7 +326,8 @@ void ApplicationSettingsWidget::saveSettings()
|
|||||||
config()->set(Config::AutoSaveOnExit, m_generalUi->autoSaveOnExitCheckBox->isChecked());
|
config()->set(Config::AutoSaveOnExit, m_generalUi->autoSaveOnExitCheckBox->isChecked());
|
||||||
config()->set(Config::AutoSaveNonDataChanges, m_generalUi->autoSaveNonDataChangesCheckBox->isChecked());
|
config()->set(Config::AutoSaveNonDataChanges, m_generalUi->autoSaveNonDataChangesCheckBox->isChecked());
|
||||||
config()->set(Config::BackupBeforeSave, m_generalUi->backupBeforeSaveCheckBox->isChecked());
|
config()->set(Config::BackupBeforeSave, m_generalUi->backupBeforeSaveCheckBox->isChecked());
|
||||||
config()->set(Config::UseAtomicSaves, m_generalUi->useAtomicSavesCheckBox->isChecked());
|
config()->set(Config::UseAtomicSaves, !m_generalUi->useAlternativeSaveCheckBox->isChecked());
|
||||||
|
config()->set(Config::UseDirectWriteSaves, m_generalUi->alternativeSaveComboBox->currentIndex() == 1);
|
||||||
config()->set(Config::AutoReloadOnChange, m_generalUi->autoReloadOnChangeCheckBox->isChecked());
|
config()->set(Config::AutoReloadOnChange, m_generalUi->autoReloadOnChangeCheckBox->isChecked());
|
||||||
config()->set(Config::MinimizeAfterUnlock, m_generalUi->minimizeAfterUnlockCheckBox->isChecked());
|
config()->set(Config::MinimizeAfterUnlock, m_generalUi->minimizeAfterUnlockCheckBox->isChecked());
|
||||||
config()->set(Config::MinimizeOnOpenUrl, m_generalUi->minimizeOnOpenUrlCheckBox->isChecked());
|
config()->set(Config::MinimizeOnOpenUrl, m_generalUi->minimizeOnOpenUrlCheckBox->isChecked());
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>605</width>
|
<width>605</width>
|
||||||
<height>1279</height>
|
<height>968</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_3" stretch="0">
|
<layout class="QVBoxLayout" name="verticalLayout_3" stretch="0">
|
||||||
@ -59,7 +59,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>581</width>
|
<width>581</width>
|
||||||
<height>1235</height>
|
<height>924</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_8">
|
<layout class="QVBoxLayout" name="verticalLayout_8">
|
||||||
@ -264,13 +264,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="backupBeforeSaveCheckBox">
|
|
||||||
<property name="text">
|
|
||||||
<string>Backup database file before saving</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="autoReloadOnChangeCheckBox">
|
<widget class="QCheckBox" name="autoReloadOnChangeCheckBox">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -279,15 +272,72 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="useAtomicSavesCheckBox">
|
<widget class="QCheckBox" name="backupBeforeSaveCheckBox">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Safely save database files (disable if experiencing problems with Dropbox, etc.)</string>
|
<string>Backup database file before saving</string>
|
||||||
</property>
|
|
||||||
<property name="checked">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="useAlternativeSaveCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Use alternative saving method (may solve problems with Dropbox, Google Drive, GVFS, etc.)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_8">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>30</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="alternativeSaveComboBox">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Temporary file moved into place</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Directly write to database file (dangerous)</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_7">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -1046,8 +1096,7 @@
|
|||||||
<tabstop>autoSaveOnExitCheckBox</tabstop>
|
<tabstop>autoSaveOnExitCheckBox</tabstop>
|
||||||
<tabstop>autoSaveNonDataChangesCheckBox</tabstop>
|
<tabstop>autoSaveNonDataChangesCheckBox</tabstop>
|
||||||
<tabstop>backupBeforeSaveCheckBox</tabstop>
|
<tabstop>backupBeforeSaveCheckBox</tabstop>
|
||||||
<tabstop>autoReloadOnChangeCheckBox</tabstop>
|
<tabstop>useAlternativeSaveCheckBox</tabstop>
|
||||||
<tabstop>useAtomicSavesCheckBox</tabstop>
|
|
||||||
<tabstop>useGroupIconOnEntryCreationCheckBox</tabstop>
|
<tabstop>useGroupIconOnEntryCreationCheckBox</tabstop>
|
||||||
<tabstop>minimizeOnOpenUrlCheckBox</tabstop>
|
<tabstop>minimizeOnOpenUrlCheckBox</tabstop>
|
||||||
<tabstop>hideWindowOnCopyCheckBox</tabstop>
|
<tabstop>hideWindowOnCopyCheckBox</tabstop>
|
||||||
|
@ -1867,16 +1867,20 @@ bool DatabaseWidget::performSave(QString& errorMessage, const QString& fileName)
|
|||||||
m_groupView->setDisabled(true);
|
m_groupView->setDisabled(true);
|
||||||
QApplication::processEvents();
|
QApplication::processEvents();
|
||||||
|
|
||||||
|
Database::SaveAction saveAction = Database::Atomic;
|
||||||
|
if (!config()->get(Config::UseAtomicSaves).toBool()) {
|
||||||
|
if (config()->get(Config::UseDirectWriteSaves).toBool()) {
|
||||||
|
saveAction = Database::DirectWrite;
|
||||||
|
} else {
|
||||||
|
saveAction = Database::TempFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool ok;
|
bool ok;
|
||||||
if (fileName.isEmpty()) {
|
if (fileName.isEmpty()) {
|
||||||
ok = m_db->save(&errorMessage,
|
ok = m_db->save(saveAction, config()->get(Config::BackupBeforeSave).toBool(), &errorMessage);
|
||||||
config()->get(Config::UseAtomicSaves).toBool(),
|
|
||||||
config()->get(Config::BackupBeforeSave).toBool());
|
|
||||||
} else {
|
} else {
|
||||||
ok = m_db->saveAs(fileName,
|
ok = m_db->saveAs(fileName, saveAction, config()->get(Config::BackupBeforeSave).toBool(), &errorMessage);
|
||||||
&errorMessage,
|
|
||||||
config()->get(Config::UseAtomicSaves).toBool(),
|
|
||||||
config()->get(Config::BackupBeforeSave).toBool());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return control
|
// Return control
|
||||||
|
@ -75,17 +75,22 @@ void TestDatabase::testSave()
|
|||||||
// Test safe saves
|
// Test safe saves
|
||||||
db->metadata()->setName("test");
|
db->metadata()->setName("test");
|
||||||
QVERIFY(db->isModified());
|
QVERIFY(db->isModified());
|
||||||
QVERIFY2(db->save(&error), error.toLatin1());
|
QVERIFY2(db->save(Database::Atomic, false, &error), error.toLatin1());
|
||||||
QVERIFY(!db->isModified());
|
QVERIFY(!db->isModified());
|
||||||
|
|
||||||
// Test unsafe saves
|
// Test temp-file saves
|
||||||
db->metadata()->setName("test2");
|
db->metadata()->setName("test2");
|
||||||
QVERIFY2(db->save(&error, false, false), error.toLatin1());
|
QVERIFY2(db->save(Database::TempFile, false, &error), error.toLatin1());
|
||||||
|
QVERIFY(!db->isModified());
|
||||||
|
|
||||||
|
// Test direct-write saves
|
||||||
|
db->metadata()->setName("test3");
|
||||||
|
QVERIFY2(db->save(Database::DirectWrite, false, &error), error.toLatin1());
|
||||||
QVERIFY(!db->isModified());
|
QVERIFY(!db->isModified());
|
||||||
|
|
||||||
// Test save backups
|
// Test save backups
|
||||||
db->metadata()->setName("test3");
|
db->metadata()->setName("test4");
|
||||||
QVERIFY2(db->save(&error, true, true), error.toLatin1());
|
QVERIFY2(db->save(Database::Atomic, true, &error), error.toLatin1());
|
||||||
QVERIFY(!db->isModified());
|
QVERIFY(!db->isModified());
|
||||||
|
|
||||||
// Confirm backup exists and then delete it
|
// Confirm backup exists and then delete it
|
||||||
@ -118,7 +123,7 @@ void TestDatabase::testSignals()
|
|||||||
QTRY_COMPARE(spyModified.count(), 1);
|
QTRY_COMPARE(spyModified.count(), 1);
|
||||||
|
|
||||||
QSignalSpy spySaved(db.data(), SIGNAL(databaseSaved()));
|
QSignalSpy spySaved(db.data(), SIGNAL(databaseSaved()));
|
||||||
QVERIFY(db->save(&error));
|
QVERIFY(db->save(Database::Atomic, false, &error));
|
||||||
QCOMPARE(spySaved.count(), 1);
|
QCOMPARE(spySaved.count(), 1);
|
||||||
|
|
||||||
// Short delay to allow file system settling to reduce test failures
|
// Short delay to allow file system settling to reduce test failures
|
||||||
|
Loading…
x
Reference in New Issue
Block a user