Merge pull request #1385 from keepassxreboot/refactor/saving

Correct saving files to DropBox/Drive/OneDrive
This commit is contained in:
Janek Bevendorff 2018-01-28 21:19:15 +01:00 committed by GitHub
commit bed921c593
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 406 additions and 279 deletions

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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",

View File

@ -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">

View File

@ -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();