Enhance last used directory settings

* Use hash based storage for last used directories instead of a key/value for each type
* Explicitly declare certain operations as sensitive and follow the "Remember Last Database" setting for those.
* Introduce database backup directory location (close #6619)
This commit is contained in:
osx user 2021-07-06 00:00:38 +03:00 committed by Jonathan White
parent 5e68cd2fa2
commit 746276edea
13 changed files with 92 additions and 107 deletions

View File

@ -28,7 +28,7 @@
#include <QStandardPaths>
#include <QTemporaryFile>
#define CONFIG_VERSION 1
#define CONFIG_VERSION 2
#define QS QStringLiteral
enum ConfigType
@ -86,7 +86,6 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
{Config::LastActiveDatabase, {QS("LastActiveDatabase"), Local, {}}},
{Config::LastOpenedDatabases, {QS("LastOpenedDatabases"), Local, {}}},
{Config::LastDir, {QS("LastDir"), Local, QDir::homePath()}},
{Config::LastAttachmentDir, {QS("LastAttachmentDir"), Local, {}}},
// GUI
{Config::GUI_Language, {QS("GUI/Language"), Roaming, QS("system")}},
@ -182,9 +181,6 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
{Config::KeeShare_Own, {QS("KeeShare/Own"), Roaming, {}}},
{Config::KeeShare_Foreign, {QS("KeeShare/Foreign"), Roaming, {}}},
{Config::KeeShare_Active, {QS("KeeShare/Active"), Roaming, {}}},
{Config::KeeShare_LastDir, {QS("KeeShare/LastDir"), Local, QDir::homePath()}},
{Config::KeeShare_LastKeyDir, {QS("KeeShare/LastKeyDir"), Local, QDir::homePath()}},
{Config::KeeShare_LastShareDir, {QS("KeeShare/LastShareDir"), Local, QDir::homePath()}},
// PasswordGenerator
{Config::PasswordGenerator_LowerCase, {QS("PasswordGenerator/LowerCase"), Roaming, true}},
@ -353,7 +349,13 @@ static const QHash<QString, Config::ConfigKey> deprecationMap = {
{QS("generator/Type"), Config::PasswordGenerator_Type},
{QS("QtErrorMessageShown"), Config::Messages_Qt55CompatibilityWarning},
{QS("GUI/HidePasswords"), Config::Deleted},
{QS("GUI/DarkTrayIcon"), Config::Deleted}};
{QS("GUI/DarkTrayIcon"), Config::Deleted},
// 2.7.0
{QS("LastAttachmentDir"), Config::Deleted},
{QS("KeeShare/LastDir"), Config::Deleted},
{QS("KeeShare/LastKeyDir"), Config::Deleted},
{QS("KeeShare/LastShareDir"), Config::Deleted}};
/**
* Migrate settings from previous versions.

View File

@ -68,7 +68,6 @@ public:
LastActiveDatabase,
LastOpenedDatabases,
LastDir,
LastAttachmentDir,
GUI_Language,
GUI_HideToolbar,
@ -158,9 +157,6 @@ public:
KeeShare_Own,
KeeShare_Foreign,
KeeShare_Active,
KeeShare_LastDir,
KeeShare_LastKeyDir,
KeeShare_LastShareDir,
PasswordGenerator_LowerCase,
PasswordGenerator_UpperCase,

View File

@ -397,15 +397,15 @@ void ApplicationSettingsWidget::saveSettings()
// Security: clear storage if related settings are disabled
if (!config()->get(Config::RememberLastDatabases).toBool()) {
config()->remove(Config::LastDir);
config()->remove(Config::LastDatabases);
config()->remove(Config::LastActiveDatabase);
config()->remove(Config::LastAttachmentDir);
}
if (!config()->get(Config::RememberLastKeyFiles).toBool()) {
config()->remove(Config::LastDir);
config()->remove(Config::LastKeyFiles);
config()->remove(Config::LastChallengeResponse);
config()->remove(Config::LastDir);
}
for (const ExtraPage& page : asConst(m_extraPages)) {
@ -438,7 +438,6 @@ void ApplicationSettingsWidget::resetSettings()
// Clear recently used data
config()->remove(Config::LastDatabases);
config()->remove(Config::LastActiveDatabase);
config()->remove(Config::LastAttachmentDir);
config()->remove(Config::LastKeyFiles);
config()->remove(Config::LastDir);

View File

@ -356,9 +356,6 @@ void DatabaseOpenWidget::reject()
void DatabaseOpenWidget::browseKeyFile()
{
QString filters = QString("%1 (*);;%2 (*.keyx; *.key)").arg(tr("All files"), tr("Key files"));
if (!config()->get(Config::RememberLastKeyFiles).toBool()) {
fileDialog()->setNextForgetDialog();
}
QString filename = fileDialog()->getOpenFileName(this, tr("Select key file"), QString(), filters);
if (QFileInfo(filename).canonicalFilePath() == QFileInfo(m_filename).canonicalFilePath()) {

View File

@ -123,9 +123,10 @@ DatabaseWidget* DatabaseTabWidget::newDatabase()
void DatabaseTabWidget::openDatabase()
{
QString filter = QString("%1 (*.kdbx);;%2 (*)").arg(tr("KeePass 2 Database"), tr("All files"));
QString fileName = fileDialog()->getOpenFileName(this, tr("Open database"), "", filter);
auto filter = QString("%1 (*.kdbx);;%2 (*)").arg(tr("KeePass 2 Database"), tr("All files"));
auto fileName = fileDialog()->getOpenFileName(this, tr("Open database"), FileDialog::getLastDir("db"), filter);
if (!fileName.isEmpty()) {
FileDialog::saveLastDir("db", fileName, true);
addDatabaseTab(fileName);
}
}
@ -211,13 +212,14 @@ void DatabaseTabWidget::addDatabaseTab(DatabaseWidget* dbWidget, bool inBackgrou
void DatabaseTabWidget::importCsv()
{
QString filter = QString("%1 (*.csv);;%2 (*)").arg(tr("CSV file"), tr("All files"));
QString fileName = fileDialog()->getOpenFileName(this, tr("Select CSV file"), "", filter);
auto filter = QString("%1 (*.csv);;%2 (*)").arg(tr("CSV file"), tr("All files"));
auto fileName = fileDialog()->getOpenFileName(this, tr("Select CSV file"), FileDialog::getLastDir("csv"), filter);
if (fileName.isEmpty()) {
return;
}
FileDialog::saveLastDir("csv", fileName, true);
auto db = execNewDatabaseWizard();
if (!db) {
return;
@ -232,9 +234,11 @@ void DatabaseTabWidget::mergeDatabase()
{
auto dbWidget = currentDatabaseWidget();
if (dbWidget && !dbWidget->isLocked()) {
QString filter = QString("%1 (*.kdbx);;%2 (*)").arg(tr("KeePass 2 Database"), tr("All files"));
const QString fileName = fileDialog()->getOpenFileName(this, tr("Merge database"), QString(), filter);
auto filter = QString("%1 (*.kdbx);;%2 (*)").arg(tr("KeePass 2 Database"), tr("All files"));
auto fileName =
fileDialog()->getOpenFileName(this, tr("Merge database"), FileDialog::getLastDir("merge"), filter);
if (!fileName.isEmpty()) {
FileDialog::saveLastDir("merge", fileName, true);
mergeDatabase(fileName);
}
}
@ -247,13 +251,15 @@ void DatabaseTabWidget::mergeDatabase(const QString& filePath)
void DatabaseTabWidget::importKeePass1Database()
{
QString filter = QString("%1 (*.kdb);;%2 (*)").arg(tr("KeePass 1 database"), tr("All files"));
QString fileName = fileDialog()->getOpenFileName(this, tr("Open KeePass 1 database"), QString(), filter);
auto filter = QString("%1 (*.kdb);;%2 (*)").arg(tr("KeePass 1 database"), tr("All files"));
auto fileName =
fileDialog()->getOpenFileName(this, tr("Open KeePass 1 database"), FileDialog::getLastDir("kp1"), filter);
if (fileName.isEmpty()) {
return;
}
FileDialog::saveLastDir("kp1", fileName, true);
auto db = QSharedPointer<Database>::create();
auto* dbWidget = new DatabaseWidget(db, this);
addDatabaseTab(dbWidget);
@ -262,16 +268,19 @@ void DatabaseTabWidget::importKeePass1Database()
void DatabaseTabWidget::importOpVaultDatabase()
{
auto defaultDir = FileDialog::getLastDir("opvault");
#ifdef Q_OS_MACOS
QString fileName = fileDialog()->getOpenFileName(this, tr("Open OPVault"), {}, "OPVault (*.opvault)");
QString fileName = fileDialog()->getOpenFileName(this, tr("Open OPVault"), defaultDir, "OPVault (*.opvault)");
#else
QString fileName = fileDialog()->getExistingDirectory(this, tr("Open OPVault"));
QString fileName = fileDialog()->getExistingDirectory(this, tr("Open OPVault"), defaultDir);
#endif
if (fileName.isEmpty()) {
return;
}
FileDialog::saveLastDir("opvault", fileName);
auto db = QSharedPointer<Database>::create();
auto* dbWidget = new DatabaseWidget(db, this);
addDatabaseTab(dbWidget);
@ -413,12 +422,14 @@ void DatabaseTabWidget::exportToCsv()
return;
}
const QString fileName = fileDialog()->getSaveFileName(
this, tr("Export database to CSV file"), QString(), tr("CSV file").append(" (*.csv)"), nullptr, nullptr);
auto fileName = fileDialog()->getSaveFileName(
this, tr("Export database to CSV file"), FileDialog::getLastDir("csv"), tr("CSV file").append(" (*.csv)"));
if (fileName.isEmpty()) {
return;
}
FileDialog::saveLastDir("csv", fileName, true);
CsvExporter csvExporter;
if (!csvExporter.exportDatabase(fileName, db)) {
emit messageGlobal(tr("Writing the CSV file failed.").append("\n").append(csvExporter.errorString()),
@ -439,11 +450,13 @@ void DatabaseTabWidget::exportToHtml()
}
const QString fileName = fileDialog()->getSaveFileName(
this, tr("Export database to HTML file"), QString(), tr("HTML file").append(" (*.html)"), nullptr, nullptr);
this, tr("Export database to HTML file"), FileDialog::getLastDir("html"), tr("HTML file").append(" (*.html)"));
if (fileName.isEmpty()) {
return;
}
FileDialog::saveLastDir("html", fileName, true);
HtmlExporter htmlExporter;
if (!htmlExporter.exportDatabase(fileName, db)) {
emit messageGlobal(tr("Writing the HTML file failed.").append("\n").append(htmlExporter.errorString()),

View File

@ -1880,9 +1880,10 @@ bool DatabaseWidget::saveBackup()
oldFilePath = QDir::toNativeSeparators(config()->get(Config::LastDir).toString() + "/"
+ tr("Passwords").append(".kdbx"));
}
const QString newFilePath = fileDialog()->getSaveFileName(this,
tr("Save database backup"),
oldFilePath,
FileDialog::getLastDir("backup"),
tr("KeePass 2 Database").append(" (*.kdbx)"),
nullptr,
nullptr);
@ -1906,6 +1907,7 @@ bool DatabaseWidget::saveBackup()
// Source database is marked as clean when copy is saved, even if source has unsaved changes
m_db->markAsModified();
}
FileDialog::saveLastDir("backup", newFilePath, true);
return true;
}

View File

@ -19,12 +19,11 @@
#include "EditWidgetIcons.h"
#include "ui_EditWidgetIcons.h"
#include <QFileDialog>
#include "core/Config.h"
#include "core/Database.h"
#include "core/Metadata.h"
#include "core/Tools.h"
#include "gui/FileDialog.h"
#include "gui/IconModels.h"
#ifdef WITH_XC_NETWORKING
#include "gui/IconDownloader.h"
@ -232,9 +231,9 @@ void EditWidgetIcons::addCustomIconFromFile()
return;
}
QString filter = QString("%1 (%2);;%3 (*)").arg(tr("Images"), Tools::imageReaderFilter(), tr("All files"));
auto filenames = QFileDialog::getOpenFileNames(this, tr("Select Image(s)"), "", filter);
auto filter = QString("%1 (%2);;%3 (*)").arg(tr("Images"), Tools::imageReaderFilter(), tr("All files"));
auto filenames =
fileDialog()->getOpenFileNames(this, tr("Select Image(s)"), FileDialog::getLastDir("icons"), filter);
if (!filenames.empty()) {
QStringList errornames;
int numexisting = 0;
@ -250,6 +249,9 @@ void EditWidgetIcons::addCustomIconFromFile()
}
}
// Save last used directory using first image path
FileDialog::saveLastDir("icons", filenames[0]);
int numloaded = filenames.size() - errornames.size() - numexisting;
QString msg;

View File

@ -21,6 +21,10 @@
FileDialog* FileDialog::m_instance(nullptr);
FileDialog::FileDialog()
{
}
QString FileDialog::getOpenFileName(QWidget* parent,
const QString& caption,
const QString& dir,
@ -43,7 +47,6 @@ QString FileDialog::getOpenFileName(QWidget* parent,
parent->activateWindow();
}
#endif
saveLastDir(result);
return result;
}
}
@ -73,9 +76,6 @@ QStringList FileDialog::getOpenFileNames(QWidget* parent,
parent->activateWindow();
}
#endif
if (!results.isEmpty()) {
saveLastDir(results[0]);
}
return results;
}
}
@ -102,7 +102,6 @@ QString FileDialog::getSaveFileName(QWidget* parent,
parent->activateWindow();
}
#endif
saveLastDir(result);
return result;
}
}
@ -127,7 +126,6 @@ QString FileDialog::getExistingDirectory(QWidget* parent,
parent->activateWindow();
}
#endif
saveLastDir(result);
return result;
}
}
@ -137,32 +135,32 @@ void FileDialog::setNextFileName(const QString& fileName)
m_nextFileName = fileName;
}
void FileDialog::setNextFileNames(const QStringList& fileNames)
void FileDialog::setNextDirectory(const QString& path)
{
m_nextFileNames = fileNames;
m_nextDirName = path;
}
void FileDialog::setNextDirName(const QString& dirName)
void FileDialog::saveLastDir(const QString& role, const QString& path, bool sensitive)
{
m_nextDirName = dirName;
}
void FileDialog::setNextForgetDialog()
{
m_forgetLastDir = true;
}
FileDialog::FileDialog()
{
}
void FileDialog::saveLastDir(const QString& dir)
{
if (!dir.isEmpty() && !m_forgetLastDir) {
config()->set(Config::LastDir, QFileInfo(dir).absolutePath());
auto lastDirs = config()->get(Config::LastDir).toHash();
if (sensitive && !config()->get(Config::RememberLastDatabases).toBool()) {
// Ensure this role is forgotten
lastDirs.remove(role);
} else {
auto pathInfo = QFileInfo(path);
if (!pathInfo.exists()) {
lastDirs.remove(role);
} else {
lastDirs.insert(role, pathInfo.absolutePath());
}
}
config()->set(Config::LastDir, lastDirs);
}
m_forgetLastDir = false;
QString FileDialog::getLastDir(const QString& role, const QString& defaultDir)
{
auto lastDirs = config()->get(Config::LastDir).toHash();
return lastDirs.value(role, defaultDir).toString();
}
FileDialog* FileDialog::instance()

View File

@ -49,14 +49,14 @@ public:
const QString& dir = QString(),
const QFileDialog::Options options = QFileDialog::ShowDirsOnly);
void setNextForgetDialog();
/**
* Sets the result of the next get* method call.
* Use only for testing.
* Bypasses the selection process and returns the provided filename/directory immediately (for testing)
*/
void setNextFileName(const QString& fileName);
void setNextFileNames(const QStringList& fileNames);
void setNextDirName(const QString& dirName);
void setNextDirectory(const QString& path);
static void saveLastDir(const QString& role, const QString& path, bool sensitive = false);
static QString getLastDir(const QString& role, const QString& defaultDir = QDir::homePath());
static FileDialog* instance();
@ -65,9 +65,6 @@ private:
QString m_nextFileName;
QStringList m_nextFileNames;
QString m_nextDirName;
bool m_forgetLastDir = false;
void saveLastDir(const QString&);
static FileDialog* m_instance;

View File

@ -26,7 +26,6 @@
#include <QColorDialog>
#include <QDesktopServices>
#include <QFileDialog>
#include <QSortFilterProxyModel>
#include <QStringListModel>
@ -50,6 +49,7 @@
#include "gui/Clipboard.h"
#include "gui/EditWidgetIcons.h"
#include "gui/EditWidgetProperties.h"
#include "gui/FileDialog.h"
#include "gui/Font.h"
#include "gui/Icons.h"
#include "gui/MessageBox.h"
@ -677,8 +677,9 @@ void EditEntryWidget::toKeeAgentSettings(KeeAgentSettings& settings) const
void EditEntryWidget::browsePrivateKey()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Select private key"), "");
auto fileName = fileDialog()->getOpenFileName(this, tr("Select private key"), FileDialog::getLastDir("sshagent"));
if (!fileName.isEmpty()) {
FileDialog::saveLastDir("sshagent", fileName);
m_sshAgentUi->externalFileEdit->setText(fileName);
m_sshAgentUi->externalFileRadioButton->setChecked(true);
updateSSHAgentKeyInfo();

View File

@ -133,12 +133,7 @@ void EntryAttachmentsWidget::insertAttachments()
return;
}
QString defaultDirPath = config()->get(Config::LastAttachmentDir).toString();
const bool dirExists = !defaultDirPath.isEmpty() && QDir(defaultDirPath).exists();
if (!dirExists) {
defaultDirPath = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation).first();
}
QString defaultDirPath = FileDialog::getLastDir("attachments");
const auto filenames = fileDialog()->getOpenFileNames(this, tr("Select files"), defaultDirPath);
if (filenames.isEmpty()) {
return;
@ -147,7 +142,8 @@ void EntryAttachmentsWidget::insertAttachments()
if (confirmedFileNames.isEmpty()) {
return;
}
config()->set(Config::LastAttachmentDir, QFileInfo(filenames.first()).absolutePath());
// Save path to first filename
FileDialog::saveLastDir("attachments", filenames[0]);
QString errorMessage;
if (!insertAttachments(confirmedFileNames, errorMessage)) {
errorOccurred(errorMessage);
@ -195,12 +191,7 @@ void EntryAttachmentsWidget::saveSelectedAttachments()
return;
}
QString defaultDirPath = config()->get(Config::LastAttachmentDir).toString();
const bool dirExists = !defaultDirPath.isEmpty() && QDir(defaultDirPath).exists();
if (!dirExists) {
defaultDirPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
}
QString defaultDirPath = FileDialog::getLastDir("attachments");
const QString saveDirPath = fileDialog()->getExistingDirectory(this, tr("Save attachments"), defaultDirPath);
if (saveDirPath.isEmpty()) {
return;
@ -213,7 +204,7 @@ void EntryAttachmentsWidget::saveSelectedAttachments()
return;
}
}
config()->set(Config::LastAttachmentDir, QFileInfo(saveDir.absolutePath()).absolutePath());
FileDialog::saveLastDir("attachments", saveDirPath);
QStringList errors;
for (const QModelIndex& index : indexes) {

View File

@ -140,15 +140,10 @@ void SettingsWidgetKeeShare::generateCertificate()
void SettingsWidgetKeeShare::importCertificate()
{
QString defaultDirPath = config()->get(Config::KeeShare_LastKeyDir).toString();
const bool dirExists = !defaultDirPath.isEmpty() && QDir(defaultDirPath).exists();
if (!dirExists) {
defaultDirPath = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation).first();
}
auto defaultDirPath = FileDialog::getLastDir("keeshare_key");
const auto filetype = tr("key.share", "Filetype for KeeShare key");
const auto filters = QString("%1 (*." + filetype + ");;%2 (*)").arg(tr("KeeShare key file"), tr("All files"));
QString filename = fileDialog()->getOpenFileName(
this, tr("Select path"), defaultDirPath, filters, nullptr, QFileDialog::Options(0));
QString filename = fileDialog()->getOpenFileName(this, tr("Select path"), defaultDirPath, filters);
if (filename.isEmpty()) {
return;
}
@ -157,7 +152,7 @@ void SettingsWidgetKeeShare::importCertificate()
QTextStream stream(&file);
m_own = KeeShareSettings::Own::deserialize(stream.readAll());
file.close();
config()->set(Config::KeeShare_LastKeyDir, QFileInfo(filename).absolutePath());
FileDialog::saveLastDir("keeshare_key", filename);
updateOwnCertificate();
}
@ -176,11 +171,7 @@ void SettingsWidgetKeeShare::exportCertificate()
return;
}
}
QString defaultDirPath = config()->get(Config::KeeShare_LastKeyDir).toString();
const bool dirExists = !defaultDirPath.isEmpty() && QDir(defaultDirPath).exists();
if (!dirExists) {
defaultDirPath = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation).first();
}
auto defaultDirPath = FileDialog::getLastDir("keeshare_key");
const auto filetype = tr("key.share", "Filetype for KeeShare key");
const auto filters = QString("%1 (*." + filetype + ");;%2 (*)").arg(tr("KeeShare key file"), tr("All files"));
QString filename = QString("%1.%2").arg(m_own.certificate.signer).arg(filetype);
@ -194,7 +185,7 @@ void SettingsWidgetKeeShare::exportCertificate()
stream << KeeShareSettings::Own::serialize(m_own);
stream.flush();
file.close();
config()->set(Config::KeeShare_LastKeyDir, QFileInfo(filename).absolutePath());
FileDialog::saveLastDir("keeshare_key", filename);
}
void SettingsWidgetKeeShare::trustSelectedCertificates()

View File

@ -224,11 +224,6 @@ void EditGroupWidgetKeeShare::launchPathSelectionDialog()
if (!m_temporaryGroup) {
return;
}
QString defaultDirPath = config()->get(Config::KeeShare_LastShareDir).toString();
const bool dirExists = !defaultDirPath.isEmpty() && QDir(defaultDirPath).exists();
if (!dirExists) {
defaultDirPath = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation).first();
}
auto reference = KeeShare::referenceOf(m_temporaryGroup);
QString defaultFiletype = "";
auto supportedExtensions = QStringList();
@ -252,6 +247,7 @@ void EditGroupWidgetKeeShare::launchPathSelectionDialog()
#endif
const auto filters = knownFilters.join(";;");
auto defaultDirPath = FileDialog::getLastDir("keeshare");
auto filename = reference.path;
if (filename.isEmpty()) {
filename = m_temporaryGroup->name();
@ -285,7 +281,7 @@ void EditGroupWidgetKeeShare::launchPathSelectionDialog()
m_ui->pathEdit->setText(filename);
selectPath();
config()->set(Config::KeeShare_LastShareDir, QFileInfo(filename).absolutePath());
FileDialog::saveLastDir("keeshare", filename);
updateSharingState();
}