diff --git a/src/core/Database.cpp b/src/core/Database.cpp index 66af624ecb..a59bf03e9a 100644 --- a/src/core/Database.cpp +++ b/src/core/Database.cpp @@ -58,7 +58,7 @@ Database::Database() connect(&m_modifiedTimer, SIGNAL(timeout()), SIGNAL(databaseModified())); connect(this, SIGNAL(databaseOpened()), SLOT(updateCommonUsernames())); connect(this, SIGNAL(databaseSaved()), SLOT(updateCommonUsernames())); - connect(m_fileWatcher, SIGNAL(fileChanged()), SIGNAL(databaseFileChanged())); + connect(m_fileWatcher, &FileWatcher::fileChanged, this, &Database::databaseFileChanged); m_modified = false; m_emitModified = true; diff --git a/src/core/FileWatcher.cpp b/src/core/FileWatcher.cpp index eab27c214f..2d37734aac 100644 --- a/src/core/FileWatcher.cpp +++ b/src/core/FileWatcher.cpp @@ -1,6 +1,5 @@ /* - * Copyright (C) 2011 Felix Geyer - * Copyright (C) 2017 KeePassXC Team + * Copyright (C) 2020 KeePassXC Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,26 +18,19 @@ #include "FileWatcher.h" #include "core/AsyncTask.h" -#include "core/Clock.h" #include -#include #ifdef Q_OS_LINUX #include #endif -namespace -{ - const int FileChangeDelay = 200; -} // namespace - FileWatcher::FileWatcher(QObject* parent) : QObject(parent) { connect(&m_fileWatcher, SIGNAL(fileChanged(QString)), SLOT(checkFileChanged())); connect(&m_fileChecksumTimer, SIGNAL(timeout()), SLOT(checkFileChanged())); - connect(&m_fileChangeDelayTimer, SIGNAL(timeout()), SIGNAL(fileChanged())); + connect(&m_fileChangeDelayTimer, &QTimer::timeout, this, [this] { emit fileChanged(m_filePath); }); m_fileChangeDelayTimer.setSingleShot(true); m_fileIgnoreDelayTimer.setSingleShot(true); } @@ -151,189 +143,3 @@ QByteArray FileWatcher::calculateChecksum() // prevents unnecessary merge requests on intermittent network shares return m_fileChecksum; } - -BulkFileWatcher::BulkFileWatcher(QObject* parent) - : QObject(parent) -{ - connect(&m_fileWatcher, SIGNAL(fileChanged(QString)), SLOT(handleFileChanged(QString))); - connect(&m_fileWatcher, SIGNAL(directoryChanged(QString)), SLOT(handleDirectoryChanged(QString))); - connect(&m_watchedFilesIgnoreTimer, SIGNAL(timeout()), this, SLOT(observeFileChanges())); - connect(&m_pendingSignalsTimer, SIGNAL(timeout()), this, SLOT(emitSignals())); - m_watchedFilesIgnoreTimer.setSingleShot(true); - m_pendingSignalsTimer.setSingleShot(true); -} - -void BulkFileWatcher::clear() -{ - for (const QString& path : m_fileWatcher.files() + m_fileWatcher.directories()) { - const QFileInfo info(path); - m_fileWatcher.removePath(info.absoluteFilePath()); - m_fileWatcher.removePath(info.absolutePath()); - } - m_watchedPaths.clear(); - m_watchedFilesInDirectory.clear(); - m_watchedFilesIgnored.clear(); -} - -void BulkFileWatcher::removePath(const QString& path) -{ - const QFileInfo info(path); - const QString filePath = info.absoluteFilePath(); - const QString directoryPath = info.absolutePath(); - m_watchedFilesInDirectory[directoryPath].remove(filePath); - m_fileWatcher.removePath(filePath); - m_watchedPaths.remove(filePath); - if (m_watchedFilesInDirectory[directoryPath].isEmpty()) { - m_fileWatcher.removePath(directoryPath); - m_watchedPaths.remove(directoryPath); - m_watchedFilesInDirectory.remove(directoryPath); - } -} - -void BulkFileWatcher::addPath(const QString& path) -{ - const QFileInfo info(path); - const QString filePath = info.absoluteFilePath(); - const QString directoryPath = info.absolutePath(); - if (!m_watchedPaths.value(filePath)) { - const bool fileSuccess = m_fileWatcher.addPath(filePath); - m_watchedPaths[filePath] = fileSuccess; - } - if (!m_watchedPaths.value(directoryPath)) { - const bool directorySuccess = m_fileWatcher.addPath(directoryPath); - m_watchedPaths[directoryPath] = directorySuccess; - } - m_watchedFilesInDirectory[directoryPath][filePath] = info.exists() ? info.lastModified().toMSecsSinceEpoch() : 0; -} - -void BulkFileWatcher::handleFileChanged(const QString& path) -{ - const QFileInfo info(path); - const QString filePath = info.absoluteFilePath(); - const QString directoryPath = info.absolutePath(); - const QMap& watchedFiles = m_watchedFilesInDirectory[directoryPath]; - const qint64 lastModificationTime = info.lastModified().toMSecsSinceEpoch(); - const bool created = watchedFiles[filePath] == 0 && info.exists(); - const bool deleted = watchedFiles[filePath] != 0 && !info.exists(); - const bool changed = !created && !deleted && lastModificationTime != watchedFiles[filePath]; - - addPath(path); - - if (m_watchedFilesIgnored[info.canonicalFilePath()] > Clock::currentDateTimeUtc()) { - // changes are blocked - return; - } - if (created) { - qDebug("File created %s", qPrintable(path)); - scheduleSignal(Created, filePath); - } - if (changed) { - qDebug("File changed %s", qPrintable(path)); - scheduleSignal(Updated, filePath); - } - if (deleted) { - qDebug("File removed %s", qPrintable(path)); - scheduleSignal(Removed, filePath); - } -} - -void BulkFileWatcher::handleDirectoryChanged(const QString& path) -{ - qDebug("Directory changed %s", qPrintable(path)); - const QFileInfo directoryInfo(path); - const QString directoryPath = directoryInfo.absoluteFilePath(); - QMap& watchedFiles = m_watchedFilesInDirectory[directoryPath]; - for (const QString& filename : watchedFiles.keys()) { - const QFileInfo fileInfo(filename); - const QString filePath = fileInfo.absoluteFilePath(); - const qint64 previousModificationTime = watchedFiles[filePath]; - const qint64 lastModificationTime = fileInfo.lastModified().toMSecsSinceEpoch(); - if (!fileInfo.exists() && previousModificationTime != 0) { - qDebug("Remove watch file %s", qPrintable(fileInfo.absoluteFilePath())); - m_fileWatcher.removePath(filePath); - m_watchedPaths.remove(filePath); - watchedFiles.remove(filePath); - scheduleSignal(Removed, filePath); - } - if (previousModificationTime == 0 && fileInfo.exists()) { - qDebug("Add watch file %s", qPrintable(fileInfo.absoluteFilePath())); - if (!m_watchedPaths.value(filePath)) { - const bool success = m_fileWatcher.addPath(filePath); - m_watchedPaths[filePath] = success; - watchedFiles[filePath] = lastModificationTime; - } - scheduleSignal(Created, filePath); - } - if (fileInfo.exists() && previousModificationTime != lastModificationTime) { - // this case is handled using - qDebug("Refresh watch file %s", qPrintable(fileInfo.absoluteFilePath())); - m_fileWatcher.removePath(fileInfo.absolutePath()); - m_fileWatcher.addPath(fileInfo.absolutePath()); - scheduleSignal(Updated, filePath); - } - m_watchedFilesInDirectory[directoryPath][filePath] = fileInfo.exists() ? lastModificationTime : 0; - } -} - -void BulkFileWatcher::emitSignals() -{ - QMap> queued; - m_pendingSignals.swap(queued); - for (const auto& path : queued.keys()) { - const auto& signal = queued[path]; - if (signal.last() == Removed) { - qDebug("Emit %s removed", qPrintable(path)); - emit fileRemoved(path); - continue; - } - if (signal.first() == Created) { - qDebug("Emit %s created", qPrintable(path)); - emit fileCreated(path); - continue; - } - qDebug("Emit %s changed", qPrintable(path)); - emit fileChanged(path); - } -} - -void BulkFileWatcher::scheduleSignal(Signal signal, const QString& path) -{ - // we need to collect signals since the file watcher API may send multiple signals for a "single" change - // therefore we wait until the event loop finished before starting to import any changes - const QString filePath = QFileInfo(path).absoluteFilePath(); - m_pendingSignals[filePath] << signal; - - if (!m_pendingSignalsTimer.isActive()) { - m_pendingSignalsTimer.start(); - } -} - -void BulkFileWatcher::ignoreFileChanges(const QString& path) -{ - const QFileInfo info(path); - m_watchedFilesIgnored[info.canonicalFilePath()] = Clock::currentDateTimeUtc().addMSecs(FileChangeDelay); -} - -void BulkFileWatcher::observeFileChanges(bool delayed) -{ - int timeout = 0; - if (delayed) { - timeout = FileChangeDelay; - } else { - const QDateTime current = Clock::currentDateTimeUtc(); - for (const QString& key : m_watchedFilesIgnored.keys()) { - if (m_watchedFilesIgnored[key] < current) { - // We assume that there was no concurrent change of the database - // during our block - so no need to reimport - qDebug("Remove block from %s", qPrintable(key)); - m_watchedFilesIgnored.remove(key); - continue; - } - qDebug("Keep block from %s", qPrintable(key)); - timeout = qMin(timeout, static_cast(current.msecsTo(m_watchedFilesIgnored[key]))); - } - } - if (timeout > 0 && !m_watchedFilesIgnoreTimer.isActive()) { - m_watchedFilesIgnoreTimer.start(timeout); - } -} diff --git a/src/core/FileWatcher.h b/src/core/FileWatcher.h index 7a90853606..27159d17a8 100644 --- a/src/core/FileWatcher.h +++ b/src/core/FileWatcher.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 KeePassXC Team + * Copyright (C) 2020 KeePassXC Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,9 +19,7 @@ #define KEEPASSXC_FILEWATCHER_H #include -#include #include -#include class FileWatcher : public QObject { @@ -37,7 +35,7 @@ class FileWatcher : public QObject bool hasSameFileChecksum(); signals: - void fileChanged(); + void fileChanged(const QString& path); public slots: void pause(); @@ -60,53 +58,4 @@ private slots: bool m_ignoreFileChange = false; }; -class BulkFileWatcher : public QObject -{ - Q_OBJECT - - enum Signal - { - Created, - Updated, - Removed - }; - -public: - explicit BulkFileWatcher(QObject* parent = nullptr); - - void clear(); - - void removePath(const QString& path); - void addPath(const QString& path); - - void ignoreFileChanges(const QString& path); - -signals: - void fileCreated(QString); - void fileChanged(QString); - void fileRemoved(QString); - -public slots: - void observeFileChanges(bool delayed = false); - -private slots: - void handleFileChanged(const QString& path); - void handleDirectoryChanged(const QString& path); - void emitSignals(); - -private: - void scheduleSignal(Signal event, const QString& path); - -private: - QMap m_watchedPaths; - QMap m_watchedFilesIgnored; - QFileSystemWatcher m_fileWatcher; - QMap> m_watchedFilesInDirectory; - // needed for Import/Export-References to prevent update after self-write - QTimer m_watchedFilesIgnoreTimer; - // needed to tolerate multiple signals for same event - QTimer m_pendingSignalsTimer; - QMap> m_pendingSignals; -}; - #endif // KEEPASSXC_FILEWATCHER_H diff --git a/src/gui/FileDialog.cpp b/src/gui/FileDialog.cpp index 9de07db7c9..df713c44be 100644 --- a/src/gui/FileDialog.cpp +++ b/src/gui/FileDialog.cpp @@ -21,6 +21,20 @@ #include +namespace +{ + QString modFilter(const QString& filter) + { +#ifdef Q_OS_MACOS + // Fix macOS bug that causes the file dialog to freeze when a dot is included in the filters + // See https://github.com/keepassxreboot/keepassxc/issues/3895#issuecomment-586724167 + auto mod = filter; + return mod.replace("*.", "*"); +#endif + return filter; + } +} // namespace + FileDialog* FileDialog::m_instance(nullptr); QString FileDialog::getOpenFileName(QWidget* parent, @@ -37,7 +51,7 @@ QString FileDialog::getOpenFileName(QWidget* parent, } else { const auto& workingDir = dir.isEmpty() ? config()->get(Config::LastDir).toString() : dir; const auto result = QDir::toNativeSeparators( - QFileDialog::getOpenFileName(parent, caption, workingDir, filter, selectedFilter, options)); + QFileDialog::getOpenFileName(parent, caption, workingDir, modFilter(filter), selectedFilter, options)); #ifdef Q_OS_MACOS // on Mac OS X the focus is lost after closing the native dialog @@ -63,7 +77,8 @@ QStringList FileDialog::getOpenFileNames(QWidget* parent, return results; } else { const auto& workingDir = dir.isEmpty() ? config()->get(Config::LastDir).toString() : dir; - auto results = QFileDialog::getOpenFileNames(parent, caption, workingDir, filter, selectedFilter, options); + auto results = + QFileDialog::getOpenFileNames(parent, caption, workingDir, modFilter(filter), selectedFilter, options); for (auto& path : results) { path = QDir::toNativeSeparators(path); @@ -82,33 +97,6 @@ QStringList FileDialog::getOpenFileNames(QWidget* parent, } } -QString FileDialog::getFileName(QWidget* parent, - const QString& caption, - const QString& dir, - const QString& filter, - QString* selectedFilter, - const QFileDialog::Options options) -{ - if (!m_nextFileName.isEmpty()) { - const QString result = m_nextFileName; - m_nextFileName.clear(); - return result; - } else { - const auto& workingDir = dir.isEmpty() ? config()->get(Config::LastDir).toString() : dir; - const auto result = QDir::toNativeSeparators( - QFileDialog::getSaveFileName(parent, caption, workingDir, filter, selectedFilter, options)); - -#ifdef Q_OS_MACOS - // on Mac OS X the focus is lost after closing the native dialog - if (parent) { - parent->activateWindow(); - } -#endif - saveLastDir(result); - return result; - } -} - QString FileDialog::getSaveFileName(QWidget* parent, const QString& caption, const QString& dir, @@ -123,7 +111,7 @@ QString FileDialog::getSaveFileName(QWidget* parent, } else { const auto& workingDir = dir.isEmpty() ? config()->get(Config::LastDir).toString() : dir; const auto result = QDir::toNativeSeparators( - QFileDialog::getSaveFileName(parent, caption, workingDir, filter, selectedFilter, options)); + QFileDialog::getSaveFileName(parent, caption, workingDir, modFilter(filter), selectedFilter, options)); #ifdef Q_OS_MACOS // on Mac OS X the focus is lost after closing the native dialog diff --git a/src/gui/FileDialog.h b/src/gui/FileDialog.h index 7d03d80460..4221a620a0 100644 --- a/src/gui/FileDialog.h +++ b/src/gui/FileDialog.h @@ -37,13 +37,6 @@ class FileDialog QString* selectedFilter = nullptr, const QFileDialog::Options options = {}); - QString getFileName(QWidget* parent = nullptr, - const QString& caption = QString(), - const QString& dir = QString(), - const QString& filter = QString(), - QString* selectedFilter = nullptr, - const QFileDialog::Options options = {}); - QString getSaveFileName(QWidget* parent = nullptr, const QString& caption = QString(), const QString& dir = QString(), diff --git a/src/keeshare/KeeShare.cpp b/src/keeshare/KeeShare.cpp index 567558bdc2..beff3d9502 100644 --- a/src/keeshare/KeeShare.cpp +++ b/src/keeshare/KeeShare.cpp @@ -48,7 +48,7 @@ KeeShare* KeeShare::instance() KeeShare::KeeShare(QObject* parent) : QObject(parent) { - connect(config(), SIGNAL(changed(Config::ConfigKey)), SLOT(handleSettingsChanged(Config::ConfigKey))); + connect(config(), &Config::changed, this, &KeeShare::handleSettingsChanged); } void KeeShare::init(QObject* parent) @@ -117,7 +117,7 @@ void KeeShare::setReferenceTo(Group* group, const KeeShareSettings::Reference& r return; } const auto serialized = KeeShareSettings::Reference::serialize(reference); - const auto encoded = serialized.toUtf8().toBase64(); + customData->set(KeeShare_Reference, serialized.toUtf8().toBase64()); } bool KeeShare::isEnabled(const Group* group) diff --git a/src/keeshare/ShareObserver.cpp b/src/keeshare/ShareObserver.cpp index c4c37b9161..6dc7a748d3 100644 --- a/src/keeshare/ShareObserver.cpp +++ b/src/keeshare/ShareObserver.cpp @@ -32,12 +32,14 @@ namespace const QFileInfo info(database->filePath()); return info.absoluteDir().absoluteFilePath(path); } + + constexpr int FileWatchPeriod = 30; + constexpr int FileWatchSize = 5; } // End Namespace ShareObserver::ShareObserver(QSharedPointer db, QObject* parent) : QObject(parent) , m_db(std::move(db)) - , m_fileWatcher(new BulkFileWatcher(this)) { connect(KeeShare::instance(), SIGNAL(activeChanged()), SLOT(handleDatabaseChanged())); @@ -48,10 +50,6 @@ ShareObserver::ShareObserver(QSharedPointer db, QObject* parent) connect(m_db.data(), SIGNAL(databaseModified()), SLOT(handleDatabaseChanged())); connect(m_db.data(), SIGNAL(databaseSaved()), SLOT(handleDatabaseSaved())); - connect(m_fileWatcher, SIGNAL(fileCreated(QString)), SLOT(handleFileCreated(QString))); - connect(m_fileWatcher, SIGNAL(fileChanged(QString)), SLOT(handleFileUpdated(QString))); - connect(m_fileWatcher, SIGNAL(fileRemoved(QString)), SLOT(handleFileDeleted(QString))); - handleDatabaseChanged(); } @@ -61,39 +59,33 @@ ShareObserver::~ShareObserver() void ShareObserver::deinitialize() { - m_fileWatcher->clear(); m_groupToReference.clear(); - m_referenceToGroup.clear(); + m_shareToGroup.clear(); + m_fileWatchers.clear(); } void ShareObserver::reinitialize() { - struct Update - { - Group* group; - KeeShareSettings::Reference oldReference; - KeeShareSettings::Reference newReference; - }; - - QList updated; - const QList groups = m_db->rootGroup()->groupsRecursive(true); - for (Group* group : groups) { - const Update couple{group, m_groupToReference.value(group), KeeShare::referenceOf(group)}; - if (couple.oldReference == couple.newReference) { + QList> shares; + for (Group* group : m_db->rootGroup()->groupsRecursive(true)) { + auto oldReference = m_groupToReference.value(group); + auto newReference = KeeShare::referenceOf(group); + if (oldReference == newReference) { continue; } - m_groupToReference.remove(couple.group); - m_referenceToGroup.remove(couple.oldReference); - const auto oldResolvedPath = resolvePath(couple.oldReference.path, m_db); + const auto oldResolvedPath = resolvePath(oldReference.path, m_db); + m_groupToReference.remove(group); m_shareToGroup.remove(oldResolvedPath); - if (couple.newReference.isValid()) { - m_groupToReference[couple.group] = couple.newReference; - m_referenceToGroup[couple.newReference] = couple.group; - const auto newResolvedPath = resolvePath(couple.newReference.path, m_db); - m_shareToGroup[newResolvedPath] = couple.group; + m_fileWatchers.remove(oldResolvedPath); + + if (newReference.isValid()) { + m_groupToReference[group] = newReference; + const auto newResolvedPath = resolvePath(newReference.path, m_db); + m_shareToGroup[newResolvedPath] = group; } - updated << couple; + + shares.append({group, newReference}); } QStringList success; @@ -101,25 +93,27 @@ void ShareObserver::reinitialize() QStringList error; QMap imported; QMap exported; - for (const auto& update : asConst(updated)) { - if (!update.oldReference.path.isEmpty()) { - const auto oldResolvedPath = resolvePath(update.oldReference.path, m_db); - m_fileWatcher->removePath(oldResolvedPath); - } - if (!update.newReference.path.isEmpty() && update.newReference.type != KeeShareSettings::Inactive) { - const auto newResolvedPath = resolvePath(update.newReference.path, m_db); - m_fileWatcher->addPath(newResolvedPath); + for (const auto& share : shares) { + auto group = share.first; + auto& reference = share.second; + + if (!reference.path.isEmpty() && reference.type != KeeShareSettings::Inactive) { + const auto newResolvedPath = resolvePath(reference.path, m_db); + auto fileWatcher = QSharedPointer::create(this); + connect(fileWatcher.data(), &FileWatcher::fileChanged, this, &ShareObserver::handleFileUpdated); + fileWatcher->start(newResolvedPath, FileWatchPeriod, FileWatchSize); + m_fileWatchers.insert(newResolvedPath, fileWatcher); } - if (update.newReference.isExporting()) { - exported[update.newReference.path] << update.group->name(); + if (reference.isExporting()) { + exported[reference.path] << group->name(); // export is only on save } - if (update.newReference.isImporting()) { - imported[update.newReference.path] << update.group->name(); + if (reference.isImporting()) { + imported[reference.path] << group->name(); // import has to occur immediately - const auto result = this->importShare(update.newReference.path); + const auto result = this->importShare(reference.path); if (!result.isValid()) { // tolerable result - blocked import or missing source continue; @@ -136,11 +130,13 @@ void ShareObserver::reinitialize() } } } + for (auto it = imported.cbegin(); it != imported.cend(); ++it) { if (it.value().count() > 1) { warning << tr("Multiple import source path to %1 in %2").arg(it.key(), it.value().join(", ")); } } + for (auto it = exported.cbegin(); it != exported.cend(); ++it) { if (it.value().count() > 1) { error << tr("Conflicting export target path %1 in %2").arg(it.key(), it.value().join(", ")); @@ -184,21 +180,9 @@ void ShareObserver::handleDatabaseChanged() } } -void ShareObserver::handleFileCreated(const QString& path) -{ - // there is currently no difference in handling an added share or updating from one - this->handleFileUpdated(path); -} - -void ShareObserver::handleFileDeleted(const QString& path) -{ - Q_UNUSED(path); - // There is nothing we can or should do for now, ignore deletion -} - void ShareObserver::handleFileUpdated(const QString& path) { - const Result result = this->importShare(path); + const Result result = importShare(path); if (!result.isValid()) { return; } @@ -287,9 +271,16 @@ QList ShareObserver::exportShares() for (auto it = references.cbegin(); it != references.cend(); ++it) { const auto& reference = it.value().first(); const QString resolvedPath = resolvePath(reference.config.path, m_db); - m_fileWatcher->ignoreFileChanges(resolvedPath); + auto watcher = m_fileWatchers.value(resolvedPath); + if (watcher) { + watcher->stop(); + } + results << ShareExport::intoContainer(resolvedPath, reference.config, reference.group); - m_fileWatcher->observeFileChanges(true); + + if (watcher) { + watcher->start(resolvedPath, FileWatchPeriod, FileWatchSize); + } } return results; } diff --git a/src/keeshare/ShareObserver.h b/src/keeshare/ShareObserver.h index df81fb3954..b98d589816 100644 --- a/src/keeshare/ShareObserver.h +++ b/src/keeshare/ShareObserver.h @@ -20,12 +20,13 @@ #include #include +#include #include #include "gui/MessageWidget.h" #include "keeshare/KeeShareSettings.h" -class BulkFileWatcher; +class FileWatcher; class Group; class Database; @@ -67,9 +68,7 @@ class ShareObserver : public QObject private slots: void handleDatabaseChanged(); void handleDatabaseSaved(); - void handleFileCreated(const QString& path); void handleFileUpdated(const QString& path); - void handleFileDeleted(const QString& path); private: Result importShare(const QString& path); @@ -81,11 +80,9 @@ private slots: private: QSharedPointer m_db; - QMap> m_referenceToGroup; QMap, KeeShareSettings::Reference> m_groupToReference; QMap> m_shareToGroup; - - BulkFileWatcher* m_fileWatcher; + QMap> m_fileWatchers; }; #endif // KEEPASSXC_SHAREOBSERVER_H diff --git a/src/keeshare/group/EditGroupWidgetKeeShare.cpp b/src/keeshare/group/EditGroupWidgetKeeShare.cpp index 16fbce07fe..a3b71220f8 100644 --- a/src/keeshare/group/EditGroupWidgetKeeShare.cpp +++ b/src/keeshare/group/EditGroupWidgetKeeShare.cpp @@ -49,7 +49,7 @@ EditGroupWidgetKeeShare::EditGroupWidgetKeeShare(QWidget* parent) connect(m_ui->typeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(selectType())); connect(m_ui->clearButton, SIGNAL(clicked(bool)), SLOT(clearInputs())); - connect(KeeShare::instance(), SIGNAL(activeChanged()), SLOT(showSharingState())); + connect(KeeShare::instance(), SIGNAL(activeChanged()), SLOT(updateSharingState())); const auto types = QList() << KeeShareSettings::Inactive << KeeShareSettings::ImportFrom << KeeShareSettings::ExportTo @@ -94,9 +94,16 @@ void EditGroupWidgetKeeShare::setGroup(Group* temporaryGroup, QSharedPointertypeComboBox->currentData().toInt() > KeeShareSettings::Inactive; + m_ui->pathEdit->setEnabled(isEnabled); + m_ui->pathSelectionButton->setEnabled(isEnabled); + m_ui->passwordEdit->setEnabled(isEnabled); + + if (!m_temporaryGroup || !isEnabled) { + m_ui->messageWidget->hideMessage(); return; } @@ -107,6 +114,8 @@ void EditGroupWidgetKeeShare::showSharingState() #if defined(WITH_XC_KEESHARE_SECURE) supportedExtensions << KeeShare::signedContainerFileType(); #endif + + // Custom message for active KeeShare reference const auto reference = KeeShare::referenceOf(m_temporaryGroup); if (!reference.path.isEmpty()) { bool supported = false; @@ -157,26 +166,23 @@ void EditGroupWidgetKeeShare::showSharingState() MessageWidget::Warning); return; } - - m_ui->messageWidget->hide(); } + + // Standard message for state of KeeShare service const auto active = KeeShare::active(); if (!active.in && !active.out) { m_ui->messageWidget->showMessage( tr("KeeShare is currently disabled. You can enable import/export in the application settings.", "KeeShare is a proper noun"), MessageWidget::Information); - return; - } - if (active.in && !active.out) { + } else if (active.in && !active.out) { m_ui->messageWidget->showMessage(tr("Database export is currently disabled by application settings."), MessageWidget::Information); - return; - } - if (!active.in && active.out) { + } else if (!active.in && active.out) { m_ui->messageWidget->showMessage(tr("Database import is currently disabled by application settings."), MessageWidget::Information); - return; + } else { + m_ui->messageWidget->hideMessage(); } } @@ -191,9 +197,9 @@ void EditGroupWidgetKeeShare::update() m_ui->typeComboBox->setCurrentIndex(reference.type); m_ui->passwordEdit->setText(reference.password); m_ui->pathEdit->setText(reference.path); - - showSharingState(); } + + updateSharingState(); } void EditGroupWidgetKeeShare::clearInputs() @@ -204,6 +210,7 @@ void EditGroupWidgetKeeShare::clearInputs() m_ui->passwordEdit->clear(); m_ui->pathEdit->clear(); m_ui->typeComboBox->setCurrentIndex(KeeShareSettings::Inactive); + updateSharingState(); } void EditGroupWidgetKeeShare::selectPath() @@ -255,17 +262,14 @@ void EditGroupWidgetKeeShare::launchPathSelectionDialog() } switch (reference.type) { case KeeShareSettings::ImportFrom: - filename = fileDialog()->getFileName( - this, tr("Select import source"), defaultDirPath, filters, nullptr, QFileDialog::DontConfirmOverwrite); + filename = fileDialog()->getOpenFileName(this, tr("Select import source"), defaultDirPath, filters); break; case KeeShareSettings::ExportTo: - filename = fileDialog()->getFileName( - this, tr("Select export target"), defaultDirPath, filters, nullptr, QFileDialog::Option(0)); + filename = fileDialog()->getSaveFileName(this, tr("Select export target"), defaultDirPath, filters); break; case KeeShareSettings::SynchronizeWith: case KeeShareSettings::Inactive: - filename = fileDialog()->getFileName( - this, tr("Select import/export file"), defaultDirPath, filters, nullptr, QFileDialog::Option(0)); + filename = fileDialog()->getSaveFileName(this, tr("Select import/export file"), defaultDirPath, filters); break; } @@ -286,6 +290,8 @@ void EditGroupWidgetKeeShare::launchPathSelectionDialog() m_ui->pathEdit->setText(filename); selectPath(); config()->set(Config::KeeShare_LastShareDir, QFileInfo(filename).absolutePath()); + + updateSharingState(); } void EditGroupWidgetKeeShare::selectPassword() @@ -306,4 +312,6 @@ void EditGroupWidgetKeeShare::selectType() auto reference = KeeShare::referenceOf(m_temporaryGroup); reference.type = static_cast(m_ui->typeComboBox->currentData().toInt()); KeeShare::setReferenceTo(m_temporaryGroup, reference); + + updateSharingState(); } diff --git a/src/keeshare/group/EditGroupWidgetKeeShare.h b/src/keeshare/group/EditGroupWidgetKeeShare.h index 54eef2bb09..ae4ae193c0 100644 --- a/src/keeshare/group/EditGroupWidgetKeeShare.h +++ b/src/keeshare/group/EditGroupWidgetKeeShare.h @@ -40,7 +40,7 @@ class EditGroupWidgetKeeShare : public QWidget void setGroup(Group* temporaryGroup, QSharedPointer database); private slots: - void showSharingState(); + void updateSharingState(); private slots: void update();