mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-01-11 23:39:50 -05:00
Fix various issues with KeeShare
* Fix #3790, shares now use the standard FileWatcher class to detect remote file changes using checksums and file system triggers.
* Fix #3895, macOS file selection no longer hangs the app.
* Restore saving of KeeShare settings accidentally removed by 596d2cf
This commit is contained in:
parent
ce7b34e96b
commit
dcff507e02
@ -58,7 +58,7 @@ Database::Database()
|
|||||||
connect(&m_modifiedTimer, SIGNAL(timeout()), SIGNAL(databaseModified()));
|
connect(&m_modifiedTimer, SIGNAL(timeout()), SIGNAL(databaseModified()));
|
||||||
connect(this, SIGNAL(databaseOpened()), SLOT(updateCommonUsernames()));
|
connect(this, SIGNAL(databaseOpened()), SLOT(updateCommonUsernames()));
|
||||||
connect(this, SIGNAL(databaseSaved()), 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_modified = false;
|
||||||
m_emitModified = true;
|
m_emitModified = true;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2011 Felix Geyer <debfx@fobos.de>
|
* Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
|
||||||
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -19,26 +18,19 @@
|
|||||||
#include "FileWatcher.h"
|
#include "FileWatcher.h"
|
||||||
|
|
||||||
#include "core/AsyncTask.h"
|
#include "core/AsyncTask.h"
|
||||||
#include "core/Clock.h"
|
|
||||||
|
|
||||||
#include <QCryptographicHash>
|
#include <QCryptographicHash>
|
||||||
#include <QFileInfo>
|
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
#include <sys/vfs.h>
|
#include <sys/vfs.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
const int FileChangeDelay = 200;
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
FileWatcher::FileWatcher(QObject* parent)
|
FileWatcher::FileWatcher(QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
{
|
{
|
||||||
connect(&m_fileWatcher, SIGNAL(fileChanged(QString)), SLOT(checkFileChanged()));
|
connect(&m_fileWatcher, SIGNAL(fileChanged(QString)), SLOT(checkFileChanged()));
|
||||||
connect(&m_fileChecksumTimer, SIGNAL(timeout()), 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_fileChangeDelayTimer.setSingleShot(true);
|
||||||
m_fileIgnoreDelayTimer.setSingleShot(true);
|
m_fileIgnoreDelayTimer.setSingleShot(true);
|
||||||
}
|
}
|
||||||
@ -151,189 +143,3 @@ QByteArray FileWatcher::calculateChecksum()
|
|||||||
// prevents unnecessary merge requests on intermittent network shares
|
// prevents unnecessary merge requests on intermittent network shares
|
||||||
return m_fileChecksum;
|
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<QString, qint64>& 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<QString, qint64>& 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<QString, QList<Signal>> 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<int>(current.msecsTo(m_watchedFilesIgnored[key])));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (timeout > 0 && !m_watchedFilesIgnoreTimer.isActive()) {
|
|
||||||
m_watchedFilesIgnoreTimer.start(timeout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
* Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -19,9 +19,7 @@
|
|||||||
#define KEEPASSXC_FILEWATCHER_H
|
#define KEEPASSXC_FILEWATCHER_H
|
||||||
|
|
||||||
#include <QFileSystemWatcher>
|
#include <QFileSystemWatcher>
|
||||||
#include <QSet>
|
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QVariant>
|
|
||||||
|
|
||||||
class FileWatcher : public QObject
|
class FileWatcher : public QObject
|
||||||
{
|
{
|
||||||
@ -37,7 +35,7 @@ public:
|
|||||||
bool hasSameFileChecksum();
|
bool hasSameFileChecksum();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void fileChanged();
|
void fileChanged(const QString& path);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void pause();
|
void pause();
|
||||||
@ -60,53 +58,4 @@ private:
|
|||||||
bool m_ignoreFileChange = false;
|
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<QString, bool> m_watchedPaths;
|
|
||||||
QMap<QString, QDateTime> m_watchedFilesIgnored;
|
|
||||||
QFileSystemWatcher m_fileWatcher;
|
|
||||||
QMap<QString, QMap<QString, qint64>> 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<QString, QList<Signal>> m_pendingSignals;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // KEEPASSXC_FILEWATCHER_H
|
#endif // KEEPASSXC_FILEWATCHER_H
|
||||||
|
@ -21,6 +21,20 @@
|
|||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
|
||||||
|
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);
|
FileDialog* FileDialog::m_instance(nullptr);
|
||||||
|
|
||||||
QString FileDialog::getOpenFileName(QWidget* parent,
|
QString FileDialog::getOpenFileName(QWidget* parent,
|
||||||
@ -37,7 +51,7 @@ QString FileDialog::getOpenFileName(QWidget* parent,
|
|||||||
} else {
|
} else {
|
||||||
const auto& workingDir = dir.isEmpty() ? config()->get(Config::LastDir).toString() : dir;
|
const auto& workingDir = dir.isEmpty() ? config()->get(Config::LastDir).toString() : dir;
|
||||||
const auto result = QDir::toNativeSeparators(
|
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
|
#ifdef Q_OS_MACOS
|
||||||
// on Mac OS X the focus is lost after closing the native dialog
|
// on Mac OS X the focus is lost after closing the native dialog
|
||||||
@ -63,7 +77,8 @@ QStringList FileDialog::getOpenFileNames(QWidget* parent,
|
|||||||
return results;
|
return results;
|
||||||
} else {
|
} else {
|
||||||
const auto& workingDir = dir.isEmpty() ? config()->get(Config::LastDir).toString() : dir;
|
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) {
|
for (auto& path : results) {
|
||||||
path = QDir::toNativeSeparators(path);
|
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,
|
QString FileDialog::getSaveFileName(QWidget* parent,
|
||||||
const QString& caption,
|
const QString& caption,
|
||||||
const QString& dir,
|
const QString& dir,
|
||||||
@ -123,7 +111,7 @@ QString FileDialog::getSaveFileName(QWidget* parent,
|
|||||||
} else {
|
} else {
|
||||||
const auto& workingDir = dir.isEmpty() ? config()->get(Config::LastDir).toString() : dir;
|
const auto& workingDir = dir.isEmpty() ? config()->get(Config::LastDir).toString() : dir;
|
||||||
const auto result = QDir::toNativeSeparators(
|
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
|
#ifdef Q_OS_MACOS
|
||||||
// on Mac OS X the focus is lost after closing the native dialog
|
// on Mac OS X the focus is lost after closing the native dialog
|
||||||
|
@ -37,13 +37,6 @@ public:
|
|||||||
QString* selectedFilter = nullptr,
|
QString* selectedFilter = nullptr,
|
||||||
const QFileDialog::Options options = {});
|
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,
|
QString getSaveFileName(QWidget* parent = nullptr,
|
||||||
const QString& caption = QString(),
|
const QString& caption = QString(),
|
||||||
const QString& dir = QString(),
|
const QString& dir = QString(),
|
||||||
|
@ -48,7 +48,7 @@ KeeShare* KeeShare::instance()
|
|||||||
KeeShare::KeeShare(QObject* parent)
|
KeeShare::KeeShare(QObject* parent)
|
||||||
: 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)
|
void KeeShare::init(QObject* parent)
|
||||||
@ -117,7 +117,7 @@ void KeeShare::setReferenceTo(Group* group, const KeeShareSettings::Reference& r
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto serialized = KeeShareSettings::Reference::serialize(reference);
|
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)
|
bool KeeShare::isEnabled(const Group* group)
|
||||||
|
@ -32,12 +32,14 @@ namespace
|
|||||||
const QFileInfo info(database->filePath());
|
const QFileInfo info(database->filePath());
|
||||||
return info.absoluteDir().absoluteFilePath(path);
|
return info.absoluteDir().absoluteFilePath(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr int FileWatchPeriod = 30;
|
||||||
|
constexpr int FileWatchSize = 5;
|
||||||
} // End Namespace
|
} // End Namespace
|
||||||
|
|
||||||
ShareObserver::ShareObserver(QSharedPointer<Database> db, QObject* parent)
|
ShareObserver::ShareObserver(QSharedPointer<Database> db, QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_db(std::move(db))
|
, m_db(std::move(db))
|
||||||
, m_fileWatcher(new BulkFileWatcher(this))
|
|
||||||
{
|
{
|
||||||
connect(KeeShare::instance(), SIGNAL(activeChanged()), SLOT(handleDatabaseChanged()));
|
connect(KeeShare::instance(), SIGNAL(activeChanged()), SLOT(handleDatabaseChanged()));
|
||||||
|
|
||||||
@ -48,10 +50,6 @@ ShareObserver::ShareObserver(QSharedPointer<Database> db, QObject* parent)
|
|||||||
connect(m_db.data(), SIGNAL(databaseModified()), SLOT(handleDatabaseChanged()));
|
connect(m_db.data(), SIGNAL(databaseModified()), SLOT(handleDatabaseChanged()));
|
||||||
connect(m_db.data(), SIGNAL(databaseSaved()), SLOT(handleDatabaseSaved()));
|
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();
|
handleDatabaseChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,39 +59,33 @@ ShareObserver::~ShareObserver()
|
|||||||
|
|
||||||
void ShareObserver::deinitialize()
|
void ShareObserver::deinitialize()
|
||||||
{
|
{
|
||||||
m_fileWatcher->clear();
|
|
||||||
m_groupToReference.clear();
|
m_groupToReference.clear();
|
||||||
m_referenceToGroup.clear();
|
m_shareToGroup.clear();
|
||||||
|
m_fileWatchers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShareObserver::reinitialize()
|
void ShareObserver::reinitialize()
|
||||||
{
|
{
|
||||||
struct Update
|
QList<QPair<Group*, KeeShareSettings::Reference>> shares;
|
||||||
{
|
for (Group* group : m_db->rootGroup()->groupsRecursive(true)) {
|
||||||
Group* group;
|
auto oldReference = m_groupToReference.value(group);
|
||||||
KeeShareSettings::Reference oldReference;
|
auto newReference = KeeShare::referenceOf(group);
|
||||||
KeeShareSettings::Reference newReference;
|
if (oldReference == newReference) {
|
||||||
};
|
|
||||||
|
|
||||||
QList<Update> updated;
|
|
||||||
const QList<Group*> 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) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_groupToReference.remove(couple.group);
|
const auto oldResolvedPath = resolvePath(oldReference.path, m_db);
|
||||||
m_referenceToGroup.remove(couple.oldReference);
|
m_groupToReference.remove(group);
|
||||||
const auto oldResolvedPath = resolvePath(couple.oldReference.path, m_db);
|
|
||||||
m_shareToGroup.remove(oldResolvedPath);
|
m_shareToGroup.remove(oldResolvedPath);
|
||||||
if (couple.newReference.isValid()) {
|
m_fileWatchers.remove(oldResolvedPath);
|
||||||
m_groupToReference[couple.group] = couple.newReference;
|
|
||||||
m_referenceToGroup[couple.newReference] = couple.group;
|
if (newReference.isValid()) {
|
||||||
const auto newResolvedPath = resolvePath(couple.newReference.path, m_db);
|
m_groupToReference[group] = newReference;
|
||||||
m_shareToGroup[newResolvedPath] = couple.group;
|
const auto newResolvedPath = resolvePath(newReference.path, m_db);
|
||||||
|
m_shareToGroup[newResolvedPath] = group;
|
||||||
}
|
}
|
||||||
updated << couple;
|
|
||||||
|
shares.append({group, newReference});
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList success;
|
QStringList success;
|
||||||
@ -101,25 +93,27 @@ void ShareObserver::reinitialize()
|
|||||||
QStringList error;
|
QStringList error;
|
||||||
QMap<QString, QStringList> imported;
|
QMap<QString, QStringList> imported;
|
||||||
QMap<QString, QStringList> exported;
|
QMap<QString, QStringList> 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) {
|
for (const auto& share : shares) {
|
||||||
const auto newResolvedPath = resolvePath(update.newReference.path, m_db);
|
auto group = share.first;
|
||||||
m_fileWatcher->addPath(newResolvedPath);
|
auto& reference = share.second;
|
||||||
|
|
||||||
|
if (!reference.path.isEmpty() && reference.type != KeeShareSettings::Inactive) {
|
||||||
|
const auto newResolvedPath = resolvePath(reference.path, m_db);
|
||||||
|
auto fileWatcher = QSharedPointer<FileWatcher>::create(this);
|
||||||
|
connect(fileWatcher.data(), &FileWatcher::fileChanged, this, &ShareObserver::handleFileUpdated);
|
||||||
|
fileWatcher->start(newResolvedPath, FileWatchPeriod, FileWatchSize);
|
||||||
|
m_fileWatchers.insert(newResolvedPath, fileWatcher);
|
||||||
}
|
}
|
||||||
if (update.newReference.isExporting()) {
|
if (reference.isExporting()) {
|
||||||
exported[update.newReference.path] << update.group->name();
|
exported[reference.path] << group->name();
|
||||||
// export is only on save
|
// export is only on save
|
||||||
}
|
}
|
||||||
|
|
||||||
if (update.newReference.isImporting()) {
|
if (reference.isImporting()) {
|
||||||
imported[update.newReference.path] << update.group->name();
|
imported[reference.path] << group->name();
|
||||||
// import has to occur immediately
|
// import has to occur immediately
|
||||||
const auto result = this->importShare(update.newReference.path);
|
const auto result = this->importShare(reference.path);
|
||||||
if (!result.isValid()) {
|
if (!result.isValid()) {
|
||||||
// tolerable result - blocked import or missing source
|
// tolerable result - blocked import or missing source
|
||||||
continue;
|
continue;
|
||||||
@ -136,11 +130,13 @@ void ShareObserver::reinitialize()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto it = imported.cbegin(); it != imported.cend(); ++it) {
|
for (auto it = imported.cbegin(); it != imported.cend(); ++it) {
|
||||||
if (it.value().count() > 1) {
|
if (it.value().count() > 1) {
|
||||||
warning << tr("Multiple import source path to %1 in %2").arg(it.key(), it.value().join(", "));
|
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) {
|
for (auto it = exported.cbegin(); it != exported.cend(); ++it) {
|
||||||
if (it.value().count() > 1) {
|
if (it.value().count() > 1) {
|
||||||
error << tr("Conflicting export target path %1 in %2").arg(it.key(), it.value().join(", "));
|
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)
|
void ShareObserver::handleFileUpdated(const QString& path)
|
||||||
{
|
{
|
||||||
const Result result = this->importShare(path);
|
const Result result = importShare(path);
|
||||||
if (!result.isValid()) {
|
if (!result.isValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -287,9 +271,16 @@ QList<ShareObserver::Result> ShareObserver::exportShares()
|
|||||||
for (auto it = references.cbegin(); it != references.cend(); ++it) {
|
for (auto it = references.cbegin(); it != references.cend(); ++it) {
|
||||||
const auto& reference = it.value().first();
|
const auto& reference = it.value().first();
|
||||||
const QString resolvedPath = resolvePath(reference.config.path, m_db);
|
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);
|
results << ShareExport::intoContainer(resolvedPath, reference.config, reference.group);
|
||||||
m_fileWatcher->observeFileChanges(true);
|
|
||||||
|
if (watcher) {
|
||||||
|
watcher->start(resolvedPath, FileWatchPeriod, FileWatchSize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,13 @@
|
|||||||
|
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QSharedPointer>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
#include "gui/MessageWidget.h"
|
#include "gui/MessageWidget.h"
|
||||||
#include "keeshare/KeeShareSettings.h"
|
#include "keeshare/KeeShareSettings.h"
|
||||||
|
|
||||||
class BulkFileWatcher;
|
class FileWatcher;
|
||||||
class Group;
|
class Group;
|
||||||
class Database;
|
class Database;
|
||||||
|
|
||||||
@ -67,9 +68,7 @@ signals:
|
|||||||
private slots:
|
private slots:
|
||||||
void handleDatabaseChanged();
|
void handleDatabaseChanged();
|
||||||
void handleDatabaseSaved();
|
void handleDatabaseSaved();
|
||||||
void handleFileCreated(const QString& path);
|
|
||||||
void handleFileUpdated(const QString& path);
|
void handleFileUpdated(const QString& path);
|
||||||
void handleFileDeleted(const QString& path);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Result importShare(const QString& path);
|
Result importShare(const QString& path);
|
||||||
@ -81,11 +80,9 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
QSharedPointer<Database> m_db;
|
QSharedPointer<Database> m_db;
|
||||||
QMap<KeeShareSettings::Reference, QPointer<Group>> m_referenceToGroup;
|
|
||||||
QMap<QPointer<Group>, KeeShareSettings::Reference> m_groupToReference;
|
QMap<QPointer<Group>, KeeShareSettings::Reference> m_groupToReference;
|
||||||
QMap<QString, QPointer<Group>> m_shareToGroup;
|
QMap<QString, QPointer<Group>> m_shareToGroup;
|
||||||
|
QMap<QString, QSharedPointer<FileWatcher>> m_fileWatchers;
|
||||||
BulkFileWatcher* m_fileWatcher;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSXC_SHAREOBSERVER_H
|
#endif // KEEPASSXC_SHAREOBSERVER_H
|
||||||
|
@ -49,7 +49,7 @@ EditGroupWidgetKeeShare::EditGroupWidgetKeeShare(QWidget* parent)
|
|||||||
connect(m_ui->typeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(selectType()));
|
connect(m_ui->typeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(selectType()));
|
||||||
connect(m_ui->clearButton, SIGNAL(clicked(bool)), SLOT(clearInputs()));
|
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::Type>()
|
const auto types = QList<KeeShareSettings::Type>()
|
||||||
<< KeeShareSettings::Inactive << KeeShareSettings::ImportFrom << KeeShareSettings::ExportTo
|
<< KeeShareSettings::Inactive << KeeShareSettings::ImportFrom << KeeShareSettings::ExportTo
|
||||||
@ -94,9 +94,16 @@ void EditGroupWidgetKeeShare::setGroup(Group* temporaryGroup, QSharedPointer<Dat
|
|||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditGroupWidgetKeeShare::showSharingState()
|
void EditGroupWidgetKeeShare::updateSharingState()
|
||||||
{
|
{
|
||||||
if (!m_temporaryGroup) {
|
// Only enable controls if we are active
|
||||||
|
bool isEnabled = m_ui->typeComboBox->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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,6 +114,8 @@ void EditGroupWidgetKeeShare::showSharingState()
|
|||||||
#if defined(WITH_XC_KEESHARE_SECURE)
|
#if defined(WITH_XC_KEESHARE_SECURE)
|
||||||
supportedExtensions << KeeShare::signedContainerFileType();
|
supportedExtensions << KeeShare::signedContainerFileType();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Custom message for active KeeShare reference
|
||||||
const auto reference = KeeShare::referenceOf(m_temporaryGroup);
|
const auto reference = KeeShare::referenceOf(m_temporaryGroup);
|
||||||
if (!reference.path.isEmpty()) {
|
if (!reference.path.isEmpty()) {
|
||||||
bool supported = false;
|
bool supported = false;
|
||||||
@ -157,26 +166,23 @@ void EditGroupWidgetKeeShare::showSharingState()
|
|||||||
MessageWidget::Warning);
|
MessageWidget::Warning);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_ui->messageWidget->hide();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Standard message for state of KeeShare service
|
||||||
const auto active = KeeShare::active();
|
const auto active = KeeShare::active();
|
||||||
if (!active.in && !active.out) {
|
if (!active.in && !active.out) {
|
||||||
m_ui->messageWidget->showMessage(
|
m_ui->messageWidget->showMessage(
|
||||||
tr("KeeShare is currently disabled. You can enable import/export in the application settings.",
|
tr("KeeShare is currently disabled. You can enable import/export in the application settings.",
|
||||||
"KeeShare is a proper noun"),
|
"KeeShare is a proper noun"),
|
||||||
MessageWidget::Information);
|
MessageWidget::Information);
|
||||||
return;
|
} else if (active.in && !active.out) {
|
||||||
}
|
|
||||||
if (active.in && !active.out) {
|
|
||||||
m_ui->messageWidget->showMessage(tr("Database export is currently disabled by application settings."),
|
m_ui->messageWidget->showMessage(tr("Database export is currently disabled by application settings."),
|
||||||
MessageWidget::Information);
|
MessageWidget::Information);
|
||||||
return;
|
} else if (!active.in && active.out) {
|
||||||
}
|
|
||||||
if (!active.in && active.out) {
|
|
||||||
m_ui->messageWidget->showMessage(tr("Database import is currently disabled by application settings."),
|
m_ui->messageWidget->showMessage(tr("Database import is currently disabled by application settings."),
|
||||||
MessageWidget::Information);
|
MessageWidget::Information);
|
||||||
return;
|
} else {
|
||||||
|
m_ui->messageWidget->hideMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,9 +197,9 @@ void EditGroupWidgetKeeShare::update()
|
|||||||
m_ui->typeComboBox->setCurrentIndex(reference.type);
|
m_ui->typeComboBox->setCurrentIndex(reference.type);
|
||||||
m_ui->passwordEdit->setText(reference.password);
|
m_ui->passwordEdit->setText(reference.password);
|
||||||
m_ui->pathEdit->setText(reference.path);
|
m_ui->pathEdit->setText(reference.path);
|
||||||
|
|
||||||
showSharingState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateSharingState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditGroupWidgetKeeShare::clearInputs()
|
void EditGroupWidgetKeeShare::clearInputs()
|
||||||
@ -204,6 +210,7 @@ void EditGroupWidgetKeeShare::clearInputs()
|
|||||||
m_ui->passwordEdit->clear();
|
m_ui->passwordEdit->clear();
|
||||||
m_ui->pathEdit->clear();
|
m_ui->pathEdit->clear();
|
||||||
m_ui->typeComboBox->setCurrentIndex(KeeShareSettings::Inactive);
|
m_ui->typeComboBox->setCurrentIndex(KeeShareSettings::Inactive);
|
||||||
|
updateSharingState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditGroupWidgetKeeShare::selectPath()
|
void EditGroupWidgetKeeShare::selectPath()
|
||||||
@ -255,17 +262,14 @@ void EditGroupWidgetKeeShare::launchPathSelectionDialog()
|
|||||||
}
|
}
|
||||||
switch (reference.type) {
|
switch (reference.type) {
|
||||||
case KeeShareSettings::ImportFrom:
|
case KeeShareSettings::ImportFrom:
|
||||||
filename = fileDialog()->getFileName(
|
filename = fileDialog()->getOpenFileName(this, tr("Select import source"), defaultDirPath, filters);
|
||||||
this, tr("Select import source"), defaultDirPath, filters, nullptr, QFileDialog::DontConfirmOverwrite);
|
|
||||||
break;
|
break;
|
||||||
case KeeShareSettings::ExportTo:
|
case KeeShareSettings::ExportTo:
|
||||||
filename = fileDialog()->getFileName(
|
filename = fileDialog()->getSaveFileName(this, tr("Select export target"), defaultDirPath, filters);
|
||||||
this, tr("Select export target"), defaultDirPath, filters, nullptr, QFileDialog::Option(0));
|
|
||||||
break;
|
break;
|
||||||
case KeeShareSettings::SynchronizeWith:
|
case KeeShareSettings::SynchronizeWith:
|
||||||
case KeeShareSettings::Inactive:
|
case KeeShareSettings::Inactive:
|
||||||
filename = fileDialog()->getFileName(
|
filename = fileDialog()->getSaveFileName(this, tr("Select import/export file"), defaultDirPath, filters);
|
||||||
this, tr("Select import/export file"), defaultDirPath, filters, nullptr, QFileDialog::Option(0));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,6 +290,8 @@ void EditGroupWidgetKeeShare::launchPathSelectionDialog()
|
|||||||
m_ui->pathEdit->setText(filename);
|
m_ui->pathEdit->setText(filename);
|
||||||
selectPath();
|
selectPath();
|
||||||
config()->set(Config::KeeShare_LastShareDir, QFileInfo(filename).absolutePath());
|
config()->set(Config::KeeShare_LastShareDir, QFileInfo(filename).absolutePath());
|
||||||
|
|
||||||
|
updateSharingState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditGroupWidgetKeeShare::selectPassword()
|
void EditGroupWidgetKeeShare::selectPassword()
|
||||||
@ -306,4 +312,6 @@ void EditGroupWidgetKeeShare::selectType()
|
|||||||
auto reference = KeeShare::referenceOf(m_temporaryGroup);
|
auto reference = KeeShare::referenceOf(m_temporaryGroup);
|
||||||
reference.type = static_cast<KeeShareSettings::Type>(m_ui->typeComboBox->currentData().toInt());
|
reference.type = static_cast<KeeShareSettings::Type>(m_ui->typeComboBox->currentData().toInt());
|
||||||
KeeShare::setReferenceTo(m_temporaryGroup, reference);
|
KeeShare::setReferenceTo(m_temporaryGroup, reference);
|
||||||
|
|
||||||
|
updateSharingState();
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ public:
|
|||||||
void setGroup(Group* temporaryGroup, QSharedPointer<Database> database);
|
void setGroup(Group* temporaryGroup, QSharedPointer<Database> database);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void showSharingState();
|
void updateSharingState();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void update();
|
void update();
|
||||||
|
Loading…
Reference in New Issue
Block a user