mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-12-27 00:09:53 -05:00
Merge pull request #1385 from keepassxreboot/refactor/saving
Correct saving files to DropBox/Drive/OneDrive
This commit is contained in:
commit
bed921c593
@ -111,9 +111,11 @@ void Config::init(const QString& fileName)
|
|||||||
m_defaults.insert("RememberLastDatabases", true);
|
m_defaults.insert("RememberLastDatabases", true);
|
||||||
m_defaults.insert("RememberLastKeyFiles", true);
|
m_defaults.insert("RememberLastKeyFiles", true);
|
||||||
m_defaults.insert("OpenPreviousDatabasesOnStartup", true);
|
m_defaults.insert("OpenPreviousDatabasesOnStartup", true);
|
||||||
m_defaults.insert("AutoSaveAfterEveryChange", false);
|
m_defaults.insert("AutoSaveAfterEveryChange", true);
|
||||||
m_defaults.insert("AutoReloadOnChange", true);
|
m_defaults.insert("AutoReloadOnChange", true);
|
||||||
m_defaults.insert("AutoSaveOnExit", false);
|
m_defaults.insert("AutoSaveOnExit", false);
|
||||||
|
m_defaults.insert("BackupBeforeSave", false);
|
||||||
|
m_defaults.insert("UseAtomicSaves", true);
|
||||||
m_defaults.insert("SearchLimitGroup", false);
|
m_defaults.insert("SearchLimitGroup", false);
|
||||||
m_defaults.insert("MinimizeOnCopy", false);
|
m_defaults.insert("MinimizeOnCopy", false);
|
||||||
m_defaults.insert("UseGroupIconOnEntryCreation", false);
|
m_defaults.insert("UseGroupIconOnEntryCreation", false);
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QSaveFile>
|
#include <QSaveFile>
|
||||||
|
#include <QTemporaryFile>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QXmlStreamReader>
|
#include <QXmlStreamReader>
|
||||||
@ -470,30 +471,101 @@ Database* Database::unlockFromStdin(QString databaseFilename, QString keyFilenam
|
|||||||
return Database::openDatabaseFile(databaseFilename, compositeKey);
|
return Database::openDatabaseFile(databaseFilename, compositeKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Database::saveToFile(QString filePath)
|
/**
|
||||||
|
* Save the database to a file.
|
||||||
|
*
|
||||||
|
* This function uses QTemporaryFile instead of QSaveFile due to a bug
|
||||||
|
* in Qt (https://bugreports.qt.io/browse/QTBUG-57299) that may prevent
|
||||||
|
* the QSaveFile from renaming itself when using Dropbox, Drive, or OneDrive.
|
||||||
|
*
|
||||||
|
* The risk in using QTemporaryFile is that the rename function is not atomic
|
||||||
|
* and may result in loss of data if there is a crash or power loss at the
|
||||||
|
* wrong moment.
|
||||||
|
*
|
||||||
|
* @param filePath Absolute path of the file to save
|
||||||
|
* @param atomic Use atomic file transactions
|
||||||
|
* @param backup Backup the existing database file, if exists
|
||||||
|
* @return error string, if any
|
||||||
|
*/
|
||||||
|
QString Database::saveToFile(QString filePath, bool atomic, bool backup)
|
||||||
|
{
|
||||||
|
QString error;
|
||||||
|
if (atomic) {
|
||||||
|
QSaveFile saveFile(filePath);
|
||||||
|
if (saveFile.open(QIODevice::WriteOnly)) {
|
||||||
|
// write the database to the file
|
||||||
|
error = writeDatabase(&saveFile);
|
||||||
|
if (!error.isEmpty()) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backup) {
|
||||||
|
backupDatabase(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (saveFile.commit()) {
|
||||||
|
// successfully saved database file
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error = saveFile.errorString();
|
||||||
|
} else {
|
||||||
|
QTemporaryFile tempFile;
|
||||||
|
if (tempFile.open()) {
|
||||||
|
// write the database to the file
|
||||||
|
error = writeDatabase(&tempFile);
|
||||||
|
if (!error.isEmpty()) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
tempFile.close(); // flush to disk
|
||||||
|
|
||||||
|
if (backup) {
|
||||||
|
backupDatabase(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the original db and move the temp file in place
|
||||||
|
QFile::remove(filePath);
|
||||||
|
if (tempFile.rename(filePath)) {
|
||||||
|
// successfully saved database file
|
||||||
|
tempFile.setAutoRemove(false);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error = tempFile.errorString();
|
||||||
|
}
|
||||||
|
// Saving failed
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Database::writeDatabase(QIODevice* device)
|
||||||
{
|
{
|
||||||
KeePass2Writer writer;
|
KeePass2Writer writer;
|
||||||
QSaveFile saveFile(filePath);
|
setEmitModified(false);
|
||||||
if (saveFile.open(QIODevice::WriteOnly)) {
|
writer.writeDatabase(device, this);
|
||||||
|
setEmitModified(true);
|
||||||
|
|
||||||
// write the database to the file
|
if (writer.hasError()) {
|
||||||
setEmitModified(false);
|
// the writer failed
|
||||||
writer.writeDatabase(&saveFile, this);
|
return writer.errorString();
|
||||||
setEmitModified(true);
|
|
||||||
|
|
||||||
if (writer.hasError()) {
|
|
||||||
return writer.errorString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (saveFile.commit()) {
|
|
||||||
// successfully saved database file
|
|
||||||
return QString();
|
|
||||||
} else {
|
|
||||||
return saveFile.errorString();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return saveFile.errorString();
|
|
||||||
}
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the old backup and replace it with a new one
|
||||||
|
* backups are named <filename>.old.kdbx
|
||||||
|
*
|
||||||
|
* @param filePath Path to the file to backup
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
bool Database::backupDatabase(QString filePath)
|
||||||
|
{
|
||||||
|
QString backupFilePath = filePath;
|
||||||
|
auto re = QRegularExpression("(?:\\.kdbx)?$", QRegularExpression::CaseInsensitiveOption);
|
||||||
|
backupFilePath.replace(re, ".old.kdbx");
|
||||||
|
QFile::remove(backupFilePath);
|
||||||
|
return QFile::copy(filePath, backupFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<Kdf> Database::kdf() const
|
QSharedPointer<Kdf> Database::kdf() const
|
||||||
|
@ -32,6 +32,7 @@ enum class EntryReferenceType;
|
|||||||
class Group;
|
class Group;
|
||||||
class Metadata;
|
class Metadata;
|
||||||
class QTimer;
|
class QTimer;
|
||||||
|
class QIODevice;
|
||||||
|
|
||||||
struct DeletedObject
|
struct DeletedObject
|
||||||
{
|
{
|
||||||
@ -111,7 +112,7 @@ public:
|
|||||||
void emptyRecycleBin();
|
void emptyRecycleBin();
|
||||||
void setEmitModified(bool value);
|
void setEmitModified(bool value);
|
||||||
void merge(const Database* other);
|
void merge(const Database* other);
|
||||||
QString saveToFile(QString filePath);
|
QString saveToFile(QString filePath, bool atomic = true, bool backup = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a unique id that is only valid as long as the Database exists.
|
* Returns a unique id that is only valid as long as the Database exists.
|
||||||
@ -144,6 +145,8 @@ private:
|
|||||||
Group* findGroupRecursive(const Uuid& uuid, Group* group);
|
Group* findGroupRecursive(const Uuid& uuid, Group* group);
|
||||||
|
|
||||||
void createRecycleBin();
|
void createRecycleBin();
|
||||||
|
QString writeDatabase(QIODevice* device);
|
||||||
|
bool backupDatabase(QString filePath);
|
||||||
|
|
||||||
Metadata* const m_metadata;
|
Metadata* const m_metadata;
|
||||||
Group* m_rootGroup;
|
Group* m_rootGroup;
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include "core/Database.h"
|
#include "core/Database.h"
|
||||||
#include "core/Group.h"
|
#include "core/Group.h"
|
||||||
#include "core/Metadata.h"
|
#include "core/Metadata.h"
|
||||||
|
#include "core/AsyncTask.h"
|
||||||
#include "format/CsvExporter.h"
|
#include "format/CsvExporter.h"
|
||||||
#include "gui/Clipboard.h"
|
#include "gui/Clipboard.h"
|
||||||
#include "gui/DatabaseWidget.h"
|
#include "gui/DatabaseWidget.h"
|
||||||
@ -43,6 +44,7 @@ DatabaseManagerStruct::DatabaseManagerStruct()
|
|||||||
: dbWidget(nullptr)
|
: dbWidget(nullptr)
|
||||||
, modified(false)
|
, modified(false)
|
||||||
, readOnly(false)
|
, readOnly(false)
|
||||||
|
, saveAttempts(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,12 +324,15 @@ bool DatabaseTabWidget::saveDatabase(Database* db, QString filePath)
|
|||||||
}
|
}
|
||||||
|
|
||||||
dbStruct.dbWidget->blockAutoReload(true);
|
dbStruct.dbWidget->blockAutoReload(true);
|
||||||
QString errorMessage = db->saveToFile(filePath);
|
// TODO: Make this async, but lock out the database widget to prevent re-entrance
|
||||||
|
bool useAtomicSaves = config()->get("UseAtomicSaves", true).toBool();
|
||||||
|
QString errorMessage = db->saveToFile(filePath, useAtomicSaves, config()->get("BackupBeforeSave").toBool());
|
||||||
dbStruct.dbWidget->blockAutoReload(false);
|
dbStruct.dbWidget->blockAutoReload(false);
|
||||||
|
|
||||||
if (errorMessage.isEmpty()) {
|
if (errorMessage.isEmpty()) {
|
||||||
// successfully saved database file
|
// successfully saved database file
|
||||||
dbStruct.modified = false;
|
dbStruct.modified = false;
|
||||||
|
dbStruct.saveAttempts = 0;
|
||||||
dbStruct.fileInfo = QFileInfo(filePath);
|
dbStruct.fileInfo = QFileInfo(filePath);
|
||||||
dbStruct.dbWidget->databaseSaved();
|
dbStruct.dbWidget->databaseSaved();
|
||||||
updateTabName(db);
|
updateTabName(db);
|
||||||
@ -336,6 +341,22 @@ bool DatabaseTabWidget::saveDatabase(Database* db, QString filePath)
|
|||||||
} else {
|
} else {
|
||||||
dbStruct.modified = true;
|
dbStruct.modified = true;
|
||||||
updateTabName(db);
|
updateTabName(db);
|
||||||
|
|
||||||
|
if (++dbStruct.saveAttempts > 2 && useAtomicSaves) {
|
||||||
|
// Saving failed 3 times, issue a warning and attempt to resolve
|
||||||
|
auto choice = MessageBox::question(this, tr("Disable safe saves?"),
|
||||||
|
tr("KeePassXC has failed to save the database multiple times. "
|
||||||
|
"This is likely caused by file sync services holding a lock on "
|
||||||
|
"the save file.\nDisable safe saves and try again?"),
|
||||||
|
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||||
|
if (choice == QMessageBox::Yes) {
|
||||||
|
config()->set("UseAtomicSaves", false);
|
||||||
|
return saveDatabase(db, filePath);
|
||||||
|
}
|
||||||
|
// Reset save attempts without changing anything
|
||||||
|
dbStruct.saveAttempts = 0;
|
||||||
|
}
|
||||||
|
|
||||||
emit messageTab(tr("Writing the database failed.").append("\n").append(errorMessage),
|
emit messageTab(tr("Writing the database failed.").append("\n").append(errorMessage),
|
||||||
MessageWidget::Error);
|
MessageWidget::Error);
|
||||||
return false;
|
return false;
|
||||||
|
@ -40,6 +40,7 @@ struct DatabaseManagerStruct
|
|||||||
QFileInfo fileInfo;
|
QFileInfo fileInfo;
|
||||||
bool modified;
|
bool modified;
|
||||||
bool readOnly;
|
bool readOnly;
|
||||||
|
int saveAttempts;
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_TYPEINFO(DatabaseManagerStruct, Q_MOVABLE_TYPE);
|
Q_DECLARE_TYPEINFO(DatabaseManagerStruct, Q_MOVABLE_TYPE);
|
||||||
|
@ -117,6 +117,8 @@ void SettingsWidget::loadSettings()
|
|||||||
config()->get("OpenPreviousDatabasesOnStartup").toBool());
|
config()->get("OpenPreviousDatabasesOnStartup").toBool());
|
||||||
m_generalUi->autoSaveAfterEveryChangeCheckBox->setChecked(config()->get("AutoSaveAfterEveryChange").toBool());
|
m_generalUi->autoSaveAfterEveryChangeCheckBox->setChecked(config()->get("AutoSaveAfterEveryChange").toBool());
|
||||||
m_generalUi->autoSaveOnExitCheckBox->setChecked(config()->get("AutoSaveOnExit").toBool());
|
m_generalUi->autoSaveOnExitCheckBox->setChecked(config()->get("AutoSaveOnExit").toBool());
|
||||||
|
m_generalUi->backupBeforeSaveCheckBox->setChecked(config()->get("BackupBeforeSave").toBool());
|
||||||
|
m_generalUi->useAtomicSavesCheckBox->setChecked(config()->get("UseAtomicSaves").toBool());
|
||||||
m_generalUi->autoReloadOnChangeCheckBox->setChecked(config()->get("AutoReloadOnChange").toBool());
|
m_generalUi->autoReloadOnChangeCheckBox->setChecked(config()->get("AutoReloadOnChange").toBool());
|
||||||
m_generalUi->minimizeOnCopyCheckBox->setChecked(config()->get("MinimizeOnCopy").toBool());
|
m_generalUi->minimizeOnCopyCheckBox->setChecked(config()->get("MinimizeOnCopy").toBool());
|
||||||
m_generalUi->useGroupIconOnEntryCreationCheckBox->setChecked(config()->get("UseGroupIconOnEntryCreation").toBool());
|
m_generalUi->useGroupIconOnEntryCreationCheckBox->setChecked(config()->get("UseGroupIconOnEntryCreation").toBool());
|
||||||
@ -193,6 +195,8 @@ void SettingsWidget::saveSettings()
|
|||||||
config()->set("AutoSaveAfterEveryChange",
|
config()->set("AutoSaveAfterEveryChange",
|
||||||
m_generalUi->autoSaveAfterEveryChangeCheckBox->isChecked());
|
m_generalUi->autoSaveAfterEveryChangeCheckBox->isChecked());
|
||||||
config()->set("AutoSaveOnExit", m_generalUi->autoSaveOnExitCheckBox->isChecked());
|
config()->set("AutoSaveOnExit", m_generalUi->autoSaveOnExitCheckBox->isChecked());
|
||||||
|
config()->set("BackupBeforeSave", m_generalUi->backupBeforeSaveCheckBox->isChecked());
|
||||||
|
config()->set("UseAtomicSaves", m_generalUi->useAtomicSavesCheckBox->isChecked());
|
||||||
config()->set("AutoReloadOnChange", m_generalUi->autoReloadOnChangeCheckBox->isChecked());
|
config()->set("AutoReloadOnChange", m_generalUi->autoReloadOnChangeCheckBox->isChecked());
|
||||||
config()->set("MinimizeOnCopy", m_generalUi->minimizeOnCopyCheckBox->isChecked());
|
config()->set("MinimizeOnCopy", m_generalUi->minimizeOnCopyCheckBox->isChecked());
|
||||||
config()->set("UseGroupIconOnEntryCreation",
|
config()->set("UseGroupIconOnEntryCreation",
|
||||||
|
@ -34,124 +34,134 @@
|
|||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="singleInstanceCheckBox">
|
<widget class="QGroupBox" name="startupGroup">
|
||||||
<property name="text">
|
<property name="title">
|
||||||
<string>Start only a single instance of KeePassXC</string>
|
<string>Startup</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="checked">
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="rememberLastDatabasesCheckBox">
|
|
||||||
<property name="text">
|
|
||||||
<string>Remember last databases</string>
|
|
||||||
</property>
|
|
||||||
<property name="checked">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="rememberLastKeyFilesCheckBox">
|
|
||||||
<property name="text">
|
|
||||||
<string>Remember last key files and security dongles</string>
|
|
||||||
</property>
|
|
||||||
<property name="checked">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="openPreviousDatabasesOnStartupCheckBox">
|
|
||||||
<property name="text">
|
|
||||||
<string>Load previous databases on startup</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="autoSaveOnExitCheckBox">
|
|
||||||
<property name="text">
|
|
||||||
<string>Automatically save on exit</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="autoSaveAfterEveryChangeCheckBox">
|
|
||||||
<property name="text">
|
|
||||||
<string>Automatically save after every change</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="autoReloadOnChangeCheckBox">
|
|
||||||
<property name="text">
|
|
||||||
<string>Automatically reload the database when modified externally</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="minimizeOnCopyCheckBox">
|
|
||||||
<property name="text">
|
|
||||||
<string>Minimize when copying to clipboard</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="systrayMinimizeOnStartup">
|
|
||||||
<property name="text">
|
|
||||||
<string>Minimize window at application startup</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="useGroupIconOnEntryCreationCheckBox">
|
|
||||||
<property name="text">
|
|
||||||
<string>Use group icon on entry creation</string>
|
|
||||||
</property>
|
|
||||||
<property name="checked">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="ignoreGroupExpansionCheckBox">
|
|
||||||
<property name="text">
|
|
||||||
<string>Don't mark database as modified for non-data changes (e.g., expanding groups)</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QWidget" name="systraySettings" native="true">
|
|
||||||
<layout class="QVBoxLayout" name="systrayLayout">
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer">
|
<widget class="QCheckBox" name="singleInstanceCheckBox">
|
||||||
<property name="orientation">
|
<property name="text">
|
||||||
<enum>Qt::Vertical</enum>
|
<string>Start only a single instance of KeePassXC</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeType">
|
<property name="checked">
|
||||||
<enum>QSizePolicy::Fixed</enum>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" stdset="0">
|
</widget>
|
||||||
<size>
|
</item>
|
||||||
<width>20</width>
|
<item>
|
||||||
<height>30</height>
|
<widget class="QCheckBox" name="rememberLastDatabasesCheckBox">
|
||||||
</size>
|
<property name="text">
|
||||||
|
<string>Remember last databases</string>
|
||||||
</property>
|
</property>
|
||||||
</spacer>
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="rememberLastKeyFilesCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Remember last key files and security dongles</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="openPreviousDatabasesOnStartupCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Load previous databases on startup</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="systrayMinimizeOnStartup">
|
||||||
|
<property name="text">
|
||||||
|
<string>Minimize window at application startup</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="saveGroup">
|
||||||
|
<property name="title">
|
||||||
|
<string>File Management</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="useAtomicSavesCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Safely save database files (may be incompatible with Dropbox, etc)</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="backupBeforeSaveCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Backup database file before saving</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="autoSaveAfterEveryChangeCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Automatically save after every change</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="autoSaveOnExitCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Automatically save on exit</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="ignoreGroupExpansionCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Don't mark database as modified for non-data changes (e.g., expanding groups)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="autoReloadOnChangeCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Automatically reload the database when modified externally</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="entryGroup">
|
||||||
|
<property name="title">
|
||||||
|
<string>Entry Management</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="useGroupIconOnEntryCreationCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Use group icon on entry creation</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="minimizeOnCopyCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Minimize when copying to clipboard</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="detailsHideCheckBox">
|
<widget class="QCheckBox" name="detailsHideCheckBox">
|
||||||
@ -160,6 +170,15 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="generalGroup">
|
||||||
|
<property name="title">
|
||||||
|
<string>General</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="systrayShowCheckBox">
|
<widget class="QCheckBox" name="systrayShowCheckBox">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -168,114 +187,162 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
<widget class="QWidget" name="systraySettings" native="true">
|
||||||
|
<layout class="QVBoxLayout" name="systrayLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="sizeConstraint">
|
||||||
|
<enum>QLayout::SetMaximumSize</enum>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="systrayMinimizeToTrayCheckBox">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Hide window to system tray when minimized</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="sizeConstraint">
|
||||||
|
<enum>QLayout::SetMaximumSize</enum>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="systrayMinimizeOnCloseCheckBox">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Hide window to system tray instead of app exit</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="sizeConstraint">
|
||||||
|
<enum>QLayout::SetMaximumSize</enum>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="systrayDarkIconCheckBox">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Dark system tray icon</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="languageLabelLayout_2">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>0</number>
|
<number>15</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeConstraint">
|
<item alignment="Qt::AlignRight">
|
||||||
<enum>QLayout::SetMaximumSize</enum>
|
<widget class="QLabel" name="languageLabel_2">
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<spacer name="horizontalSpacer">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeType">
|
|
||||||
<enum>QSizePolicy::Fixed</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="systrayMinimizeToTrayCheckBox">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Hide window to system tray when minimized</string>
|
<string>Language</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
|
||||||
<property name="spacing">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="sizeConstraint">
|
|
||||||
<enum>QLayout::SetMaximumSize</enum>
|
|
||||||
</property>
|
|
||||||
<item>
|
<item>
|
||||||
<spacer name="horizontalSpacer_2">
|
<widget class="QComboBox" name="languageComboBox">
|
||||||
<property name="orientation">
|
<property name="sizePolicy">
|
||||||
<enum>Qt::Horizontal</enum>
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
</property>
|
<horstretch>0</horstretch>
|
||||||
<property name="sizeType">
|
<verstretch>0</verstretch>
|
||||||
<enum>QSizePolicy::Fixed</enum>
|
</sizepolicy>
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="systrayMinimizeOnCloseCheckBox">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Hide window to system tray instead of app exit</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
|
||||||
<property name="spacing">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="sizeConstraint">
|
|
||||||
<enum>QLayout::SetMaximumSize</enum>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<spacer name="horizontalSpacer_3">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeType">
|
|
||||||
<enum>QSizePolicy::Fixed</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="systrayDarkIconCheckBox">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Dark system tray icon</string>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -284,52 +351,6 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<spacer name="trayIconSpacer_2">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeType">
|
|
||||||
<enum>QSizePolicy::Fixed</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>30</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="languageLabelLayout_2">
|
|
||||||
<property name="spacing">
|
|
||||||
<number>15</number>
|
|
||||||
</property>
|
|
||||||
<item alignment="Qt::AlignRight">
|
|
||||||
<widget class="QLabel" name="languageLabel_2">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Language</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QComboBox" name="languageComboBox">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer_2">
|
<spacer name="verticalSpacer_2">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
@ -67,6 +67,9 @@ void TestGui::initTestCase()
|
|||||||
{
|
{
|
||||||
QVERIFY(Crypto::init());
|
QVERIFY(Crypto::init());
|
||||||
Config::createTempFileInstance();
|
Config::createTempFileInstance();
|
||||||
|
// Disable autosave so we can test the modified file indicator
|
||||||
|
Config::instance()->set("AutoSaveAfterEveryChange", false);
|
||||||
|
|
||||||
m_mainWindow = new MainWindow();
|
m_mainWindow = new MainWindow();
|
||||||
m_tabWidget = m_mainWindow->findChild<DatabaseTabWidget*>("tabWidget");
|
m_tabWidget = m_mainWindow->findChild<DatabaseTabWidget*>("tabWidget");
|
||||||
m_mainWindow->show();
|
m_mainWindow->show();
|
||||||
@ -141,7 +144,6 @@ void TestGui::testCreateDatabase()
|
|||||||
DatabaseWidget* dbWidget = m_tabWidget->currentDatabaseWidget();
|
DatabaseWidget* dbWidget = m_tabWidget->currentDatabaseWidget();
|
||||||
|
|
||||||
QWidget* databaseNewWidget = dbWidget->findChild<QWidget*>("changeMasterKeyWidget");
|
QWidget* databaseNewWidget = dbWidget->findChild<QWidget*>("changeMasterKeyWidget");
|
||||||
QList<QWidget*> databaseNewWidgets = dbWidget->findChildren<QWidget*>("changeMasterKeyWidget");
|
|
||||||
PasswordEdit* editPassword = databaseNewWidget->findChild<PasswordEdit*>("enterPasswordEdit");
|
PasswordEdit* editPassword = databaseNewWidget->findChild<PasswordEdit*>("enterPasswordEdit");
|
||||||
QVERIFY(editPassword->isVisible());
|
QVERIFY(editPassword->isVisible());
|
||||||
|
|
||||||
@ -154,6 +156,7 @@ void TestGui::testCreateDatabase()
|
|||||||
QTest::keyClicks(editPasswordRepeat, "test");
|
QTest::keyClicks(editPasswordRepeat, "test");
|
||||||
QTest::keyClick(editPasswordRepeat, Qt::Key_Enter);
|
QTest::keyClick(editPasswordRepeat, Qt::Key_Enter);
|
||||||
|
|
||||||
|
// Auto-save after every change is enabled by default, ensure the db saves right away
|
||||||
QTRY_VERIFY(m_tabWidget->tabText(m_tabWidget->currentIndex()).contains("*"));
|
QTRY_VERIFY(m_tabWidget->tabText(m_tabWidget->currentIndex()).contains("*"));
|
||||||
|
|
||||||
m_db = m_tabWidget->currentDatabaseWidget()->database();
|
m_db = m_tabWidget->currentDatabaseWidget()->database();
|
||||||
|
Loading…
Reference in New Issue
Block a user