Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F18825016
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
50 KB
Subscribers
None
View Options
diff --git a/src/models/storemodel.cpp b/src/models/storemodel.cpp
index a3b25f9..e7bcf4b 100644
--- a/src/models/storemodel.cpp
+++ b/src/models/storemodel.cpp
@@ -1,422 +1,421 @@
/*
SPDX-FileCopyrightText: 2014-2023 Anne Jan Brouwer <brouwer@annejan.com>
SPDX-FileCopyrightText: 2018 Claudio Maradonna <penguyman@stronzi.org>
SPDX-FileCopyrightText: 2019 Maciej S. Szmigiero <mail@maciej.szmigiero.name>
SPDX-FileCopyrightText: 2023 g10 Code GmbH
SPDX-FileContributor: Sune Stolborg Vuorela <sune@vuorela.dk>
SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "storemodel.h"
#include "addfileinfoproxy.h"
#include "job/filereencryptjob.h"
#include "rootfolderconfig.h"
#include "rootfoldersmanager.h"
#include "util.h"
#include <KLocalizedString>
#include <KSelectionProxyModel>
#include <QDir>
#include <QFileSystemModel>
#include <QItemSelectionModel>
#include <QMessageBox>
#include <QMimeData>
#include <QRegularExpression>
#include <qstringliteral.h>
static const QString mimeType = QStringLiteral("application/vnd+gnupgpass.dragAndDropInfoPasswordStore");
/// \brief holds values to share beetween drag and drop on the passwordstorage view.
struct DragAndDropInfoPasswordStore {
bool isDir = false;
bool isFile = false;
QString path;
};
QDataStream &operator<<(QDataStream &out, const DragAndDropInfoPasswordStore &dragAndDropInfoPasswordStore)
{
out << dragAndDropInfoPasswordStore.isDir << dragAndDropInfoPasswordStore.isFile << dragAndDropInfoPasswordStore.path;
return out;
}
QDataStream &operator>>(QDataStream &in, DragAndDropInfoPasswordStore &dragAndDropInfoPasswordStore)
{
in >> dragAndDropInfoPasswordStore.isDir >> dragAndDropInfoPasswordStore.isFile >> dragAndDropInfoPasswordStore.path;
return in;
}
StoreModel::StoreModel(QObject *parent)
: QSortFilterProxyModel(parent)
, m_fileSystemModel(new QFileSystemModel(this))
, m_addRoleModel(new AddFileInfoProxy(this))
, m_itemSelectionModel(new QItemSelectionModel(m_addRoleModel, this))
, m_selectionProxyModel(new KSelectionProxyModel(m_itemSelectionModel, this))
{
m_fileSystemModel->setNameFilters({QStringLiteral("*.gpg")});
m_fileSystemModel->setNameFilterDisables(false);
m_addRoleModel->setSourceModel(m_fileSystemModel);
m_selectionProxyModel->setFilterBehavior(KSelectionProxyModel::SubTrees);
m_selectionProxyModel->setSourceModel(m_addRoleModel);
setObjectName(QStringLiteral("StoreModel"));
setRecursiveFilteringEnabled(true);
setSourceModel(m_selectionProxyModel);
#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
setAutoAcceptChildRows(true);
#endif
connect(m_fileSystemModel, &QFileSystemModel::directoryLoaded, this, &StoreModel::directoryLoaded);
}
int StoreModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return 1;
}
RootFoldersManager *StoreModel::rootFoldersManager() const
{
return m_rootFoldersManager;
}
void StoreModel::setRootFoldersManager(RootFoldersManager *rootFoldersManager)
{
if (m_rootFoldersManager) {
disconnect(m_rootFoldersManager, &RootFoldersManager::rootFoldersChanged, this, &StoreModel::updateRootFolders);
}
m_rootFoldersManager = rootFoldersManager;
if (m_rootFoldersManager) {
updateRootFolders();
connect(m_rootFoldersManager, &RootFoldersManager::rootFoldersChanged, this, &StoreModel::updateRootFolders);
}
}
void StoreModel::updateRootFolders()
{
m_itemSelectionModel->clear();
for (const auto &rootFolder : m_rootFoldersManager->rootFolders()) {
QDir dir(rootFolder->path());
if (!dir.exists()) {
dir.mkpath(QStringLiteral("."));
}
QModelIndex rootDirIndex = m_fileSystemModel->setRootPath(rootFolder->path());
m_fileSystemModel->fetchMore(rootDirIndex);
m_itemSelectionModel->select(m_addRoleModel->mapFromSource(rootDirIndex), QItemSelectionModel::Select);
}
- m_selectionProxyModel->setFilterBehavior(m_rootFoldersManager->rootFolders().count() > 1 ? KSelectionProxyModel::SubTrees
- : KSelectionProxyModel::SubTreesWithoutRoots);
+ Q_EMIT rootFoldersSizeChanged();
}
QVariant StoreModel::data(const QModelIndex &index, int role) const
{
Q_ASSERT(checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid));
const auto initialValue = QSortFilterProxyModel::data(index, role);
if (index.column() == 0 && !index.parent().isValid() && m_rootFoldersManager) {
if (role == Qt::DisplayRole && m_rootFoldersManager->rootFolders().count() > 1) {
const auto uuid = initialValue.toString();
if (!uuid.isEmpty()) {
return m_rootFoldersManager->rootFolderName(uuid);
}
} else if (role == Qt::DecorationRole) {
return QIcon::fromTheme(QStringLiteral("folder-root-symbolic"));
}
}
if (role == Qt::ToolTipRole) {
const auto fileInfo = index.data(AddFileInfoProxy::FileInfoRole).value<QFileInfo>();
const bool isDir = fileInfo.isDir();
if (isDir) {
const auto recipients = recipientsForFile(fileInfo);
QString tooltip = i18nc("@info:tooltip", "<p>This directory is encrypted for the following users:</p><ul>");
for (const auto &recipient : recipients) {
tooltip += QStringLiteral("<li>") + QString::fromUtf8(recipient) + QStringLiteral("</li>");
}
tooltip += QStringLiteral("</ul>");
return tooltip;
}
} else if (role == Qt::DisplayRole) {
QString name = initialValue.toString();
name.replace(Util::endsWithGpg(), QString{});
return name;
}
return initialValue;
}
Qt::DropActions StoreModel::supportedDropActions() const
{
return Qt::CopyAction | Qt::MoveAction;
}
Qt::DropActions StoreModel::supportedDragActions() const
{
return Qt::CopyAction | Qt::MoveAction;
}
Qt::ItemFlags StoreModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags defaultFlags = QSortFilterProxyModel::flags(index);
if (index.isValid()) {
return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
}
return Qt::ItemIsDropEnabled | defaultFlags;
}
QStringList StoreModel::mimeTypes() const
{
QStringList types;
types << mimeType;
return types;
}
QMimeData *StoreModel::mimeData(const QModelIndexList &indexes) const
{
DragAndDropInfoPasswordStore info;
QByteArray encodedData;
// only use the first, otherwise we should enable multiselection
QModelIndex index = indexes.at(0);
if (index.isValid()) {
auto fileInfo = index.data(AddFileInfoProxy::FileInfoRole).value<QFileInfo>();
info.isDir = fileInfo.isDir();
info.isFile = fileInfo.isFile();
info.path = fileInfo.absoluteFilePath();
QDataStream stream(&encodedData, QIODevice::WriteOnly);
stream << info;
}
auto *mimeData = new QMimeData();
mimeData->setData(mimeType, encodedData);
return mimeData;
}
bool StoreModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const
{
#ifdef QT_DEBUG
qDebug() << action << row;
#else
Q_UNUSED(action)
Q_UNUSED(row)
#endif
if (!sourceModel()) {
return false;
}
const QModelIndex idx = index(parent.row(), parent.column(), parent.parent());
QByteArray encodedData = data->data(mimeType);
QDataStream stream(&encodedData, QIODevice::ReadOnly);
DragAndDropInfoPasswordStore info;
stream >> info;
if (!data->hasFormat(mimeType))
return false;
if (column > 0) {
return false;
}
auto fileInfo = idx.data(AddFileInfoProxy::FileInfoRole).value<QFileInfo>();
const auto rootDir = fileInfo.absoluteFilePath().isEmpty();
// you can drop a folder on a folder
if ((fileInfo.isDir() || rootDir) && info.isDir) {
return true;
}
// you can drop a file on a folder
if ((fileInfo.isDir() || rootDir) && info.isFile) {
return true;
}
// you can drop a file on a file
if (fileInfo.isFile() && info.isFile) {
return true;
}
return false;
}
bool StoreModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
if (!canDropMimeData(data, action, row, column, parent))
return false;
if (action == Qt::IgnoreAction) {
return true;
}
QByteArray encodedData = data->data(mimeType);
QDataStream stream(&encodedData, QIODevice::ReadOnly);
DragAndDropInfoPasswordStore info;
stream >> info;
QModelIndex destIndex = this->index(parent.row(), parent.column(), parent.parent());
QFileInfo destFileinfo = destIndex.data(AddFileInfoProxy::FileInfoRole).value<QFileInfo>();
if (destFileinfo.absoluteFilePath().isEmpty()) {
auto rootFolder = m_rootFoldersManager->rootFolders().constFirst();
destFileinfo = QFileInfo(rootFolder->path());
}
QFileInfo srcFileInfo = QFileInfo(info.path);
QString cleanedSrc = QDir::cleanPath(srcFileInfo.absoluteFilePath());
QString cleanedDest = QDir::cleanPath(destFileinfo.absoluteFilePath());
if (info.isDir) {
// dropped dir onto dir
if (destFileinfo.isDir()) {
QDir destDir = QDir(cleanedDest).filePath(srcFileInfo.fileName());
QString cleanedDestDir = QDir::cleanPath(destDir.absolutePath());
if (action == Qt::MoveAction) {
move(cleanedSrc, cleanedDestDir);
} else if (action == Qt::CopyAction) {
copy(cleanedSrc, cleanedDestDir);
}
}
} else if (info.isFile) {
// dropped file onto a directory
if (destFileinfo.isDir()) {
if (action == Qt::MoveAction) {
move(cleanedSrc, cleanedDest);
} else if (action == Qt::CopyAction) {
copy(cleanedSrc, cleanedDest);
}
} else if (destFileinfo.isFile()) {
// dropped file onto a file
int answer = QMessageBox::question(nullptr,
i18n("Force overwrite?"),
i18nc("Overwrite DestinationFile with SourceFile", "Overwrite %1 with %2?", cleanedDest, cleanedSrc),
QMessageBox::Yes | QMessageBox::No);
bool force = answer == QMessageBox::Yes;
if (action == Qt::MoveAction) {
move(cleanedSrc, cleanedDest, force);
} else if (action == Qt::CopyAction) {
copy(cleanedSrc, cleanedDest, force);
}
}
}
return true;
}
bool StoreModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
/* matches logic in QFileSystemModelSorter::compareNodes() */
#ifndef Q_OS_MAC
if (source_left.column() == 0 || source_left.column() == 1) {
bool leftD = source_left.data(AddFileInfoProxy::FileInfoRole).value<QFileInfo>().isDir();
bool rightD = source_right.data(AddFileInfoProxy::FileInfoRole).value<QFileInfo>().isDir();
if (leftD ^ rightD)
return leftD;
}
#endif
return QSortFilterProxyModel::lessThan(source_left, source_right);
}
QList<QByteArray> StoreModel::recipientsForFile(const QFileInfo &fileInfo) const
{
QDir gpgIdPath(fileInfo.isDir() ? fileInfo.absoluteFilePath() : fileInfo.absoluteDir());
bool found = false;
QString rootDir;
while (gpgIdPath.exists()) {
bool isInRootFolder = false;
const auto rootFolders = m_rootFoldersManager->rootFolders();
for (const auto &rootFolder : rootFolders) {
auto rootFolderPath = rootFolder->path();
rootFolderPath.chop(1); // remove / at end
if (gpgIdPath.absolutePath().startsWith(rootFolderPath)) {
isInRootFolder = true;
rootDir = rootFolder->path();
}
}
if (!isInRootFolder) {
break;
}
if (QFile(gpgIdPath.absoluteFilePath(QStringLiteral(".gpg-id"))).exists()) {
found = true;
break;
}
if (!gpgIdPath.cdUp())
break;
}
QFile gpgId(found ? gpgIdPath.absoluteFilePath(QStringLiteral(".gpg-id")) : rootDir + QStringLiteral(".gpg-id"));
if (!gpgId.open(QIODevice::ReadOnly | QIODevice::Text)) {
return {};
}
QList<QByteArray> recipients;
while (!gpgId.atEnd()) {
const auto recipient(gpgId.readLine().trimmed());
if (!recipient.isEmpty()) {
recipients += recipient;
}
}
std::sort(recipients.begin(), recipients.end());
return recipients;
}
void StoreModel::move(const QString &source, const QString &destination, const bool force)
{
QFileInfo srcFileInfo(source);
QFileInfo destFileInfo(destination);
QString destFile;
QString srcFileBaseName = srcFileInfo.fileName();
if (srcFileInfo.isFile()) {
if (destFileInfo.isFile()) {
if (!force) {
return;
}
} else if (destFileInfo.isDir()) {
destFile = QDir(destination).filePath(srcFileBaseName);
} else {
destFile = destination;
}
if (destFile.endsWith(QStringLiteral(".gpg"), Qt::CaseInsensitive))
destFile.chop(4); // make sure suffix is lowercase
destFile.append(QStringLiteral(".gpg"));
} else if (srcFileInfo.isDir()) {
if (destFileInfo.isDir()) {
destFile = QDir(destination).filePath(srcFileBaseName);
} else if (destFileInfo.isFile()) {
return;
} else {
destFile = destination;
}
} else {
return;
}
QDir qDir;
if (force) {
qDir.remove(destFile);
}
qDir.rename(source, destFile);
}
void StoreModel::copy(const QString &source, const QString &destination, const bool force)
{
QDir qDir;
if (force) {
qDir.remove(destination);
}
if (!QFile::copy(source, destination)) {
Q_EMIT errorOccurred(QStringLiteral("Failed to copy file"));
}
const QFileInfo destinationInfo(destination);
const QFileInfo sourceInfo(source);
if (destinationInfo.isDir()) {
const auto recipients = recipientsForFile(destinationInfo);
auto fileReencryptJob = new FileReencryptJob(destinationInfo.absoluteFilePath() + u'/' + sourceInfo.fileName(), recipients);
fileReencryptJob->start();
} else if (destinationInfo.isFile()) {
const auto recipients = recipientsForFile(destinationInfo);
auto fileReencryptJob = new FileReencryptJob(destinationInfo.absoluteFilePath(), recipients);
fileReencryptJob->start();
}
}
diff --git a/src/models/storemodel.h b/src/models/storemodel.h
index 695d30a..ffbeb19 100644
--- a/src/models/storemodel.h
+++ b/src/models/storemodel.h
@@ -1,73 +1,74 @@
/*
SPDX-FileCopyrightText: 2014-2023 Anne Jan Brouwer <brouwer@annejan.com>
SPDX-FileCopyrightText: 2018 Claudio Maradonna <penguyman@stronzi.org>
SPDX-FileCopyrightText: 2019 Maciej S. Szmigiero <mail@maciej.szmigiero.name>
SPDX-FileCopyrightText: 2023 g10 Code GmbH
SPDX-FileContributor: Sune Stolborg Vuorela <sune@vuorela.dk>
SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef STOREMODEL_H_
#define STOREMODEL_H_
#include <QSortFilterProxyModel>
class QFileInfo;
class QFileSystemModel;
class QItemSelectionModel;
class KSelectionProxyModel;
class AddFileInfoProxy;
class Pass;
class RootFoldersManager;
class RootFolderConfig;
/// \brief The QSortFilterProxyModel for handling filesystem searches.
///
/// This model support drag and drop.
class StoreModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
explicit StoreModel(QObject *parent = nullptr);
RootFoldersManager *rootFoldersManager() const;
void setRootFoldersManager(RootFoldersManager *rootFoldersManager);
/// Overriden to remove .gpg at the end of the file name.
QVariant data(const QModelIndex &index, int role) const override;
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
int columnCount(const QModelIndex &parent) const override;
Qt::DropActions supportedDropActions() const override;
Qt::DropActions supportedDragActions() const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QStringList mimeTypes() const override;
QMimeData *mimeData(const QModelIndexList &indexes) const override;
bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const override;
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
/// \return The recipients for a file
/// This list is created from the .gpg-id file in the same directory or parent
/// directory and is sorted.
QList<QByteArray> recipientsForFile(const QFileInfo &fileInfo) const;
void move(const QString &source, const QString &destination, const bool force = false);
void copy(const QString &source, const QString &destination, const bool force = false);
Q_SIGNALS:
void errorOccurred(const QString &errorText);
void directoryLoaded(const QString &path);
+ void rootFoldersSizeChanged();
private:
void updateRootFolders();
private:
QFileSystemModel *const m_fileSystemModel;
AddFileInfoProxy *const m_addRoleModel;
QItemSelectionModel *const m_itemSelectionModel;
KSelectionProxyModel *const m_selectionProxyModel;
RootFoldersManager *m_rootFoldersManager = nullptr;
};
#endif // STOREMODEL_H_
diff --git a/src/widgets/mainwindow.cpp b/src/widgets/mainwindow.cpp
index 5226e2d..9de1e0f 100644
--- a/src/widgets/mainwindow.cpp
+++ b/src/widgets/mainwindow.cpp
@@ -1,811 +1,824 @@
/*
SPDX-FileCopyrightText: 2014-2023 Anne Jan Brouwer <brouwer@annejan.com>
SPDX-FileCopyrightText: 2016-2017 tezeb <tezeb+github@outoftheblue.pl>
SPDX-FileCopyrightText: 2018 Lukas Vogel <lukedirtwalker@gmail.com>
SPDX-FileCopyrightText: 2018 Claudio Maradonna <penguyman@stronzi.org>
SPDX-FileCopyrightText: 2019 Maciej S. Szmigiero <mail@maciej.szmigiero.name>
SPDX-FileCopyrightText: 2023 g10 Code GmbH
SPDX-FileContributor: Sune Stolborg Vuorela <sune@vuorela.dk>
SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "mainwindow.h"
#include <gpgpass_version.h>
#include "clipboardhelper.h"
#include "conf/configuredialog.h"
#include "job/directoryreencryptjob.h"
#include "job/filedecryptjob.h"
#include "job/fileencryptjob.h"
#include "models/addfileinfoproxy.h"
#include "passentry.h"
#include "rootfoldersmanager.h"
#include "setupwidget.h"
#include "ui_mainwindow.h"
#include "usersdialog.h"
#include "util.h"
#include "widgets/passwordeditorwidget.h"
#include "widgets/passwordviewerwidget.h"
#include <KLocalizedString>
#include <KMessageBox>
#include <KMessageWidget>
#include <KStandardGuiItem>
#include <QCloseEvent>
#include <QComboBox>
#include <QDebug>
#include <QDirIterator>
#include <QFileInfo>
#include <QInputDialog>
#include <QMenu>
#include <QMessageBox>
#include <QShortcut>
enum class StackLayer {
WelcomePage = 0,
PasswordViewer = 1,
ConfigurationPage = 2,
PasswordEditor = 3,
};
static QString directoryName(const QString &dirOrFile)
{
QFileInfo fi{dirOrFile};
if (fi.isDir()) {
return fi.absoluteFilePath();
} else {
return fi.absolutePath();
}
}
/**
* @brief MainWindow::MainWindow handles all of the main functionality and also
* the main window.
* @param searchText for searching from cli
* @param parent pointer
*/
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, m_clipboardHelper(new ClipboardHelper(this))
, m_passwordViewer(new PasswordViewerWidget(m_clipboardHelper, this))
, m_passwordEditor(new PasswordEditorWidget(m_clipboardHelper, this))
, m_rootFoldersManager(new RootFoldersManager(this))
{
#ifdef __APPLE__
// extra treatment for mac os
// see http://doc.qt.io/qt-5/qkeysequence.html#qt_set_sequence_auto_mnemonic
qt_set_sequence_auto_mnemonic(true);
#endif
ui->setupUi(this);
ui->stackedWidget->addWidget(m_passwordViewer);
auto setupWidget = new SetupWidget(this);
ui->stackedWidget->addWidget(setupWidget);
ui->stackedWidget->addWidget(m_passwordEditor);
connect(setupWidget, &SetupWidget::setupComplete, this, &MainWindow::slotSetupFinished);
ui->actionEdit->setCheckable(true);
ui->separator->setFixedHeight(1);
ui->separator->setFrameStyle(QFrame::HLine);
ui->lineEditWrapper->setContentsMargins(style()->pixelMetric(QStyle::PM_LayoutLeftMargin),
style()->pixelMetric(QStyle::PM_LayoutTopMargin),
style()->pixelMetric(QStyle::PM_LayoutRightMargin),
style()->pixelMetric(QStyle::PM_LayoutBottomMargin));
m_errorMessage = new KMessageWidget();
m_errorMessage->setMessageType(KMessageWidget::Error);
#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
m_errorMessage->setPosition(KMessageWidget::Position::Header);
#endif
m_errorMessage->hide();
ui->messagesArea->addWidget(m_errorMessage);
m_storeModel.setRootFoldersManager(m_rootFoldersManager);
connect(&m_storeModel, &StoreModel::errorOccurred, this, [this](auto str) {
m_errorMessage->setText(str);
m_errorMessage->animatedShow();
setUiElementsEnabled(true);
});
+ connect(&m_storeModel, &StoreModel::rootFoldersSizeChanged, this, &MainWindow::updateRootIndex);
+
connect(m_passwordViewer, &PasswordViewerWidget::loaded, this, [this] {
setUiElementsEnabled(true);
});
}
void MainWindow::setVisible(bool visible)
{
// This originated in the ctor, but we want this to happen after the first start wizard has been run
// so for now, moved to show() on first call
if (visible && firstShow) {
firstShow = false;
// register shortcut ctrl/cmd + Q to close the main window
new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q), this, SLOT(close()));
ui->treeView->setModel(&m_storeModel);
ui->treeView->setHeaderHidden(true);
ui->treeView->setIndentation(15);
ui->treeView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu);
ui->treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
ui->treeView->sortByColumn(0, Qt::AscendingOrder);
connect(ui->treeView, &QWidget::customContextMenuRequested, this, &MainWindow::showContextMenu);
connect(ui->treeView, &DeselectableTreeView::emptyClicked, this, &MainWindow::deselect);
searchTimer.setInterval(350);
searchTimer.setSingleShot(true);
connect(&searchTimer, &QTimer::timeout, this, &MainWindow::onTimeoutSearch);
initToolBarButtons();
ui->lineEdit->setClearButtonEnabled(true);
setUiElementsEnabled(true);
QTimer::singleShot(10, this, SLOT(focusInput()));
verifyInitialized();
+
+ updateRootIndex();
}
QMainWindow::setVisible(visible);
}
MainWindow::~MainWindow() = default;
+void MainWindow::updateRootIndex()
+{
+ if (m_rootFoldersManager->rootFolders().count() == 1) {
+ ui->treeView->setRootIndex(m_storeModel.index(0, 0));
+ } else {
+ ui->treeView->setRootIndex({});
+ }
+}
+
/**
* @brief MainWindow::focusInput selects any text (if applicable) in the search
* box and sets focus to it. Allows for easy searching, called at application
* start and when receiving empty message in MainWindow::messageAvailable when
* compiled with SINGLE_APP=1 (default).
*/
void MainWindow::focusInput()
{
ui->lineEdit->selectAll();
ui->lineEdit->setFocus();
}
/**
* @brief MainWindow::changeEvent sets focus to the search box
* @param event
*/
void MainWindow::changeEvent(QEvent *event)
{
QWidget::changeEvent(event);
if (event->type() == QEvent::ActivationChange) {
if (isActiveWindow()) {
focusInput();
}
}
}
/**
* @brief MainWindow::initToolBarButtons init main ToolBar and connect actions
*/
void MainWindow::initToolBarButtons()
{
connect(ui->actionAddPassword, &QAction::triggered, this, &MainWindow::addPassword);
connect(ui->actionAddFolder, &QAction::triggered, this, &MainWindow::addFolder);
connect(ui->actionEdit, &QAction::toggled, this, &MainWindow::onEdit);
connect(ui->actionDelete, &QAction::triggered, this, &MainWindow::onDelete);
connect(ui->actionUsers, &QAction::triggered, this, &MainWindow::onUsers);
connect(ui->actionConfig, &QAction::triggered, this, [this] {
openConfig(ConfigureDialog::Page::None);
});
connect(ui->treeView, &QTreeView::clicked, this, &MainWindow::selectTreeItem);
connect(ui->treeView, &QTreeView::doubleClicked, this, &MainWindow::editTreeItem);
connect(ui->lineEdit, &QLineEdit::textChanged, this, &MainWindow::filterList);
connect(ui->lineEdit, &QLineEdit::returnPressed, this, &MainWindow::selectFromSearch);
ui->actionAddPassword->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
ui->actionAddFolder->setIcon(QIcon::fromTheme(QStringLiteral("folder-new")));
ui->actionEdit->setIcon(QIcon::fromTheme(QStringLiteral("document-properties")));
ui->actionDelete->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
ui->actionUsers->setIcon(QIcon::fromTheme(QStringLiteral("x-office-address-book")));
ui->actionConfig->setIcon(QIcon::fromTheme(QStringLiteral("configure-symbolic")));
}
const QModelIndex MainWindow::getCurrentTreeViewIndex()
{
return ui->treeView->currentIndex();
}
void MainWindow::openConfig(ConfigureDialog::Page page)
{
QScopedPointer<ConfigureDialog> dialog(new ConfigureDialog(m_rootFoldersManager, this));
dialog->setModal(true);
dialog->openPage(page);
if (dialog->exec()) {
if (dialog->result() == QDialog::Accepted) {
this->show();
m_passwordViewer->setPanelTimer();
m_clipboardHelper->setClipboardTimer();
}
}
}
/**
* @brief MainWindow::on_treeView_clicked read the selected password file
* @param index
*/
void MainWindow::selectTreeItem(const QModelIndex &index)
{
bool cleared = ui->treeView->currentIndex().flags() == Qt::NoItemFlags;
// TODO(bezet): "Could not decrypt";
m_clipboardHelper->clearClippedText();
QString file = index.data(QFileSystemModel::FilePathRole).toString();
ui->actionEdit->setChecked(false);
if (!file.isEmpty() && QFileInfo(file).isFile() && !cleared) {
m_selectedFile = file;
m_passwordViewer->setFileName(ui->treeView->selectionModel()->currentIndex().data().toString(), file);
ui->stackedWidget->setCurrentIndex((int)StackLayer::PasswordViewer);
} else {
m_passwordViewer->clear();
ui->actionEdit->setEnabled(false);
ui->actionDelete->setEnabled(index.parent().isValid() || m_rootFoldersManager->rootFolders().count() == 1);
ui->stackedWidget->setCurrentIndex((int)StackLayer::WelcomePage);
}
}
/**
* @brief MainWindow::on_treeView_doubleClicked when doubleclicked on
* TreeViewItem, open the edit Window
* @param index
*/
void MainWindow::editTreeItem(const QModelIndex &index)
{
QFileInfo fileInfo{index.data(QFileSystemModel::Roles::FilePathRole).toString()};
if (!fileInfo.isFile()) {
return;
}
auto decryptJob = new FileDecryptJob(fileInfo.absoluteFilePath());
connect(decryptJob, &FileDecryptJob::finished, this, [this, decryptJob](KJob *) {
if (decryptJob->error() != KJob::NoError) {
m_errorMessage->setText(decryptJob->errorText());
m_errorMessage->animatedShow();
return;
}
switchToPasswordEditor(decryptJob->filePath(), decryptJob->content());
});
decryptJob->start();
}
void MainWindow::switchToPasswordEditor(const QString &filePath, const QString &content)
{
PassEntry entry;
const auto name = QFileInfo(filePath).baseName();
if (!content.isEmpty()) {
entry = PassEntry(name, content);
}
m_passwordEditor->setPassEntry(entry);
ui->actionEdit->setChecked(true);
ui->stackedWidget->setCurrentIndex((int)StackLayer::PasswordEditor);
connect(m_passwordEditor, &PasswordEditorWidget::save, this, [this, filePath](const QString &content) {
const QFileInfo fileInfo(filePath);
const auto recipients = m_storeModel.recipientsForFile(fileInfo);
auto encryptJob = new FileEncryptJob(fileInfo.absoluteFilePath(), content.toUtf8(), recipients);
connect(encryptJob, &FileDecryptJob::finished, this, [encryptJob, this](KJob *) {
if (encryptJob->error() != KJob::NoError) {
m_errorMessage->setText(encryptJob->errorText());
m_errorMessage->animatedShow();
return;
}
ui->treeView->setFocus();
ui->actionEdit->setChecked(false);
selectTreeItem(getCurrentTreeViewIndex());
});
encryptJob->start();
});
connect(m_passwordEditor, &PasswordEditorWidget::editorClosed, this, [this]() {
selectTreeItem(getCurrentTreeViewIndex());
ui->actionEdit->setChecked(false);
});
}
/**
* @brief MainWindow::deselect clear the selection, password and copy buffer
*/
void MainWindow::deselect()
{
m_clipboardHelper->clearClipboard();
ui->treeView->clearSelection();
ui->actionEdit->setEnabled(false);
ui->actionDelete->setEnabled(false);
m_passwordViewer->clear();
ui->stackedWidget->setCurrentIndex((int)StackLayer::WelcomePage);
}
/**
* @brief MainWindow::setUiElementsEnabled enable or disable the relevant UI
* elements
* @param state
*/
void MainWindow::setUiElementsEnabled(bool state)
{
ui->treeView->setEnabled(state);
ui->lineEdit->setEnabled(state);
ui->lineEdit->installEventFilter(this);
ui->actionAddPassword->setEnabled(state);
ui->actionAddFolder->setEnabled(state);
ui->actionUsers->setEnabled(state);
ui->actionConfig->setEnabled(state);
// is a file selected?
state &= ui->treeView->currentIndex().isValid();
ui->actionDelete->setEnabled(state);
ui->actionEdit->setEnabled(state);
}
/**
* @brief Executes when the string in the search box changes, collapses the
* TreeView
* @param arg1
*/
void MainWindow::filterList(const QString &arg1)
{
ui->treeView->expandAll();
searchTimer.start();
}
/**
* @brief MainWindow::onTimeoutSearch Fired when search is finished or too much
* time from two keypresses is elapsed
*/
void MainWindow::onTimeoutSearch()
{
QString query = ui->lineEdit->text();
if (query.isEmpty()) {
ui->treeView->collapseAll();
deselect();
}
query.replace(QStringLiteral(" "), QStringLiteral(".*"));
QRegularExpression regExp(query, QRegularExpression::CaseInsensitiveOption);
m_storeModel.setFilterRegularExpression(regExp);
if (m_storeModel.rowCount() > 0 && !query.isEmpty()) {
selectFirstFile();
} else {
ui->actionEdit->setEnabled(false);
ui->actionDelete->setEnabled(false);
}
}
/**
* @brief MainWindow::on_lineEdit_returnPressed get searching
*
* Select the first possible file in the tree
*/
void MainWindow::selectFromSearch()
{
if (m_storeModel.rowCount() > 0) {
selectFirstFile();
selectTreeItem(ui->treeView->currentIndex());
}
}
/**
* @brief MainWindow::selectFirstFile select the first possible file in the
* tree
*/
void MainWindow::selectFirstFile()
{
auto model = ui->treeView->model();
auto index = firstFile(model->index(0, 0));
ui->treeView->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
}
/**
* @brief MainWindow::firstFile return location of first possible file
* @param parentIndex
* @return QModelIndex
*/
QModelIndex MainWindow::firstFile(QModelIndex parentIndex)
{
auto model = parentIndex.model();
int numRows = model->rowCount(parentIndex);
for (int row = 0; row < numRows; ++row) {
auto index = model->index(row, 0, parentIndex);
if (index.data(AddFileInfoProxy::FileInfoRole).value<QFileInfo>().isFile())
return index;
if (model->hasChildren(index))
return firstFile(index);
}
return parentIndex;
}
QString MainWindow::fallbackStore()
{
const auto rootFolders = m_rootFoldersManager->rootFolders();
if (rootFolders.isEmpty()) {
QMessageBox::critical(this, i18nc("@title:dialog", "No password store found"), i18nc("@info", "Please add a password store first."));
return {};
}
return rootFolders[0]->path();
}
/**
* @brief MainWindow::addPassword add a new password by showing a
* number of dialogs.
*/
void MainWindow::addPassword()
{
bool ok;
QString dir = directoryName(ui->treeView->currentIndex().data(QFileSystemModel::Roles::FilePathRole).toString());
if (dir.isEmpty()) {
dir = fallbackStore();
if (dir.isEmpty()) {
return;
}
}
QString file = QInputDialog::getText(this, i18n("New file"), i18n("New password file: \n(Will be placed in %1 )", dir), QLineEdit::Normal, QString{}, &ok);
if (!ok || file.isEmpty())
return;
file = QDir(dir).absoluteFilePath(file + QStringLiteral(".gpg"));
switchToPasswordEditor(file);
}
/**
* @brief MainWindow::onDelete remove password, if you are
* sure.
*/
void MainWindow::onDelete()
{
QModelIndex currentIndex = ui->treeView->currentIndex();
if (!currentIndex.isValid()) {
// If not valid, we might end up passing empty string
// to delete, and taht might delete unexpected things on disk
return;
}
QFileInfo fileOrFolder{currentIndex.data(QFileSystemModel::FilePathRole).toString()};
bool isDir = fileOrFolder.isDir();
QString file = fileOrFolder.absoluteFilePath();
QString message;
if (isDir) {
message = i18nc("deleting a folder; placeholder is folder name", "Are you sure you want to remove %1 and the whole content?", file);
QDirIterator it(file, QDirIterator::Subdirectories);
while (it.hasNext()) {
it.next();
if (auto fi = it.fileInfo(); fi.isFile()) {
if (fi.suffix() != QStringLiteral("gpg")) {
message += QStringLiteral("<br><strong>")
+ i18nc("extra warning during certain folder deletions",
"Attention: "
"there are unexpected files in the given folder, "
"check them before continue")
+ QStringLiteral("</strong>");
break;
}
}
}
} else {
message = i18nc("deleting a file; placeholder is file name", "Are you sure you want to remove %1?", file);
}
if (KMessageBox::warningTwoActions(this,
message,
isDir ? i18n("Remove folder?") : i18n("Remove password?"),
KStandardGuiItem::remove(),
KStandardGuiItem::cancel())
!= KMessageBox::PrimaryAction)
return;
if (!isDir) {
QFile(file).remove();
} else {
QDir dir(file);
dir.removeRecursively();
}
}
/**
* @brief MainWindow::onEdit try and edit (selected) password.
*/
void MainWindow::onEdit(bool edit)
{
if (edit) {
editTreeItem(ui->treeView->currentIndex());
} else {
selectTreeItem(getCurrentTreeViewIndex());
ui->actionEdit->setChecked(false);
}
}
/**
* @brief MainWindow::userDialog see MainWindow::onUsers()
* @param dir folder to edit users for.
*/
void MainWindow::userDialog(QString dir)
{
if (dir.isEmpty()) {
dir = fallbackStore();
if (dir.isEmpty()) {
return;
}
}
QFileInfo fi(dir);
if (!fi.isDir()) {
dir = fi.absolutePath();
}
const auto recipients = m_storeModel.recipientsForFile(QFileInfo(dir));
auto usersDialog = new UsersDialog(recipients, this);
usersDialog->setAttribute(Qt::WA_DeleteOnClose);
connect(usersDialog, &UsersDialog::save, this, [dir, this, usersDialog](const QList<QByteArray> &recipients) {
auto reencryptJob = new DirectoryReencryptJob(m_storeModel, recipients, dir);
connect(reencryptJob, &DirectoryReencryptJob::result, this, [this, usersDialog](KJob *job) {
if (job->error() != KJob::NoError) {
usersDialog->showError(job->errorText());
return;
}
usersDialog->close();
setUiElementsEnabled(true);
ui->treeView->setFocus();
verifyInitialized();
});
// statusBar()->showMessage(i18n("Re-encrypting folders"), 3000);
setUiElementsEnabled(false);
ui->treeView->setDisabled(true);
reencryptJob->start();
});
usersDialog->show();
}
/**
* @brief MainWindow::onUsers edit users for the current
* folder,
* gets lists and opens UserDialog.
*/
void MainWindow::onUsers()
{
QString dir = ui->treeView->currentIndex().data(QFileSystemModel::Roles::FilePathRole).toString();
if (dir.isEmpty()) {
dir = fallbackStore();
if (dir.isEmpty()) {
return;
}
} else {
QFileInfo fi(dir);
if (!fi.isDir()) {
dir = fi.absolutePath();
}
dir = Util::normalizeFolderPath(dir);
}
userDialog(dir);
}
void MainWindow::slotSetupFinished(const QString &location, const QByteArray &keyId)
{
const QString gpgIdFile = location + QStringLiteral(".gpg-id");
QFile gpgId(gpgIdFile);
if (!gpgId.open(QIODevice::WriteOnly | QIODevice::Text)) {
KMessageBox::error(this, i18n("Unable to write user configuration at \"%1\". Error: %2", gpgIdFile, gpgId.errorString()));
return;
}
gpgId.write(keyId);
gpgId.close();
m_rootFoldersManager->addRootFolder(i18nc("Default store name", "Local Store"), location);
m_rootFoldersManager->save();
ui->lineEdit->clear();
m_passwordViewer->clear();
ui->treeView->selectionModel()->clear();
ui->actionEdit->setEnabled(false);
ui->actionDelete->setEnabled(false);
ui->stackedWidget->setCurrentIndex((int)StackLayer::WelcomePage);
verifyInitialized();
}
void MainWindow::verifyInitialized()
{
auto alreadyConfigured = !m_rootFoldersManager->rootFolders().isEmpty();
ui->sidebar->setVisible(alreadyConfigured);
ui->stackedWidget->setCurrentIndex(alreadyConfigured ? (int)StackLayer::WelcomePage : (int)StackLayer::ConfigurationPage);
ui->actionAddFolder->setEnabled(alreadyConfigured);
ui->actionAddPassword->setEnabled(alreadyConfigured);
ui->actionDelete->setEnabled(ui->actionDelete->isEnabled() && alreadyConfigured);
ui->actionEdit->setEnabled(ui->actionEdit->isEnabled() && alreadyConfigured);
}
/**
* @brief MainWindow::closeEvent hide or quit
* @param event
*/
void MainWindow::closeEvent(QCloseEvent *event)
{
m_clipboardHelper->clearClipboard();
event->accept();
}
/**
* @brief MainWindow::eventFilter filter out some events and focus the
* treeview
* @param obj
* @param event
* @return
*/
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == ui->lineEdit && event->type() == QEvent::KeyPress) {
auto *key = dynamic_cast<QKeyEvent *>(event);
if (key != nullptr && key->key() == Qt::Key_Down) {
ui->treeView->setFocus();
}
}
return QObject::eventFilter(obj, event);
}
/**
* @brief MainWindow::keyPressEvent did anyone press return, enter or escape?
* @param event
*/
void MainWindow::keyPressEvent(QKeyEvent *event)
{
switch (event->key()) {
case Qt::Key_Delete:
onDelete();
break;
case Qt::Key_Return:
case Qt::Key_Enter:
if (m_storeModel.rowCount() > 0)
selectTreeItem(ui->treeView->currentIndex());
break;
case Qt::Key_Escape:
ui->lineEdit->clear();
break;
default:
break;
}
}
/**
* @brief MainWindow::showContextMenu show us the (file or folder) context
* menu
* @param pos
*/
void MainWindow::showContextMenu(const QPoint &pos)
{
QModelIndex index = ui->treeView->indexAt(pos);
bool selected = true;
if (!index.isValid()) {
ui->treeView->clearSelection();
ui->actionDelete->setEnabled(false);
ui->actionEdit->setEnabled(false);
selected = false;
}
ui->treeView->setCurrentIndex(index);
QPoint globalPos = ui->treeView->viewport()->mapToGlobal(pos);
QFileInfo fileOrFolder = ui->treeView->currentIndex().data(AddFileInfoProxy::FileInfoRole).value<QFileInfo>();
QMenu contextMenu;
if (!selected || fileOrFolder.isDir()) {
QAction *addFolderAction = contextMenu.addAction(i18nc("@action:inmenu", "Add Folder"));
addFolderAction->setIcon(QIcon::fromTheme(QStringLiteral("folder-add-symbolic")));
connect(addFolderAction, &QAction::triggered, this, &MainWindow::addFolder);
QAction *addPasswordAction = contextMenu.addAction(i18nc("@action:inmenu", "Add Password"));
addPasswordAction->setIcon(QIcon::fromTheme(QStringLiteral("lock-symbolic")));
connect(addPasswordAction, &QAction::triggered, this, &MainWindow::addPassword);
QAction *usersAction = contextMenu.addAction(i18nc("@action:inmenu", "Configure Users"));
usersAction->setIcon(QIcon::fromTheme(QStringLiteral("system-users-symbolic")));
connect(usersAction, &QAction::triggered, this, &MainWindow::onUsers);
} else if (fileOrFolder.isFile()) {
QAction *edit = contextMenu.addAction(i18nc("@action:inmenu", "Edit Password"));
edit->setIcon(QIcon::fromTheme(QStringLiteral("document-edit")));
connect(edit, &QAction::triggered, this, [this] {
onEdit(true);
});
}
if (selected && (index.parent().isValid() || m_rootFoldersManager->rootFolders().count() == 1)) {
contextMenu.addSeparator();
if (fileOrFolder.isDir()) {
QAction *renameFolderAction = contextMenu.addAction(i18nc("@action:inmenu", "Rename Folder"));
connect(renameFolderAction, &QAction::triggered, this, &MainWindow::renameFolder);
renameFolderAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename-symbolic")));
} else if (fileOrFolder.isFile()) {
QAction *renamePasswordAction = contextMenu.addAction(i18nc("@action:inmenu", "Rename Password"));
renamePasswordAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename-symbolic")));
connect(renamePasswordAction, &QAction::triggered, this, &MainWindow::renamePassword);
}
QAction *deleteItem =
contextMenu.addAction(fileOrFolder.isFile() ? i18nc("@action:inmenu", "Delete Password") : i18nc("@action:inmenu", "Delete Folder"));
deleteItem->setIcon(QIcon::fromTheme(QStringLiteral("delete-symbolic")));
connect(deleteItem, &QAction::triggered, this, &MainWindow::onDelete);
}
if (!index.parent().isValid() && m_rootFoldersManager->rootFolders().count() != 1) {
contextMenu.addSeparator();
const auto configureAction = contextMenu.addAction(i18nc("@action:inmenu", "Configure Password Stores"));
configureAction->setIcon(QIcon::fromTheme(QStringLiteral("configure-symbolic")));
connect(configureAction, &QAction::triggered, this, [this] {
openConfig(ConfigureDialog::Page::PasswordsStore);
});
}
contextMenu.exec(globalPos);
}
/**
* @brief MainWindow::addFolder add a new folder to store passwords in
*/
void MainWindow::addFolder()
{
bool ok;
QString dir = directoryName(ui->treeView->currentIndex().data(QFileSystemModel::FilePathRole).toString());
if (dir.isEmpty()) {
dir = fallbackStore();
if (dir.isEmpty()) {
return;
}
}
QString newdir = QInputDialog::getText(this, i18n("New file"), i18n("New Folder: \n(Will be placed in %1 )", dir), QLineEdit::Normal, QString{}, &ok);
if (!ok || newdir.isEmpty())
return;
QDir(dir).mkdir(newdir);
}
/**
* @brief MainWindow::renameFolder rename an existing folder
*/
void MainWindow::renameFolder()
{
bool ok;
QString srcDir = QDir::cleanPath(directoryName(ui->treeView->currentIndex().data(QFileSystemModel::FilePathRole).toString()));
if (srcDir.isEmpty()) {
return;
}
QString srcDirName = QDir(srcDir).dirName();
QString newName = QInputDialog::getText(this, i18n("Rename file"), i18n("Rename Folder To: "), QLineEdit::Normal, srcDirName, &ok);
if (!ok || newName.isEmpty())
return;
QString destDir = srcDir;
destDir.replace(srcDir.lastIndexOf(srcDirName), srcDirName.length(), newName);
m_storeModel.move(srcDir, destDir);
}
/**
* @brief MainWindow::renamePassword rename an existing password
*/
void MainWindow::renamePassword()
{
bool ok;
QString file = ui->treeView->currentIndex().data(QFileSystemModel::FilePathRole).toString();
QString filePath = QFileInfo(file).path();
QString fileName = QFileInfo(file).fileName();
if (fileName.endsWith(QStringLiteral(".gpg"), Qt::CaseInsensitive))
fileName.chop(4);
QString newName = QInputDialog::getText(this, i18n("Rename file"), i18n("Rename File To: "), QLineEdit::Normal, fileName, &ok);
if (!ok || newName.isEmpty())
return;
QString newFile = QDir(filePath).filePath(newName);
m_storeModel.move(file, newFile);
}
diff --git a/src/widgets/mainwindow.h b/src/widgets/mainwindow.h
index 81b825a..ce0562b 100644
--- a/src/widgets/mainwindow.h
+++ b/src/widgets/mainwindow.h
@@ -1,127 +1,128 @@
/*
SPDX-FileCopyrightText: 2014-2023 Anne Jan Brouwer <brouwer@annejan.com>
SPDX-FileCopyrightText: 2016-2017 tezeb <tezeb+github@outoftheblue.pl>
SPDX-FileCopyrightText: 2018 Lukas Vogel <lukedirtwalker@gmail.com>
SPDX-FileCopyrightText: 2018 Claudio Maradonna <penguyman@stronzi.org>
SPDX-FileCopyrightText: 2019 Maciej S. Szmigiero <mail@maciej.szmigiero.name>
SPDX-FileCopyrightText: 2023 g10 Code GmbH
SPDX-FileContributor: Sune Stolborg Vuorela <sune@vuorela.dk>
SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef MAINWINDOW_H_
#define MAINWINDOW_H_
#include "conf/configuredialog.h"
#include "models/storemodel.h"
#include <KSelectionProxyModel>
#include <QFileSystemModel>
#include <QItemSelectionModel>
#include <QMainWindow>
#include <QProcess>
#include <QTimer>
#ifdef __APPLE__
// http://doc.qt.io/qt-5/qkeysequence.html#qt_set_sequence_auto_mnemonic
void qt_set_sequence_auto_mnemonic(bool b);
#endif
namespace Ui
{
class MainWindow;
}
class QComboBox;
class ClipboardHelper;
class KMessageWidget;
class AddFileInfoProxy;
class KJob;
class PasswordViewerWidget;
class PasswordEditorWidget;
class RootFoldersManager;
/*!
\class MainWindow
\brief The MainWindow class does way too much, not only is it a switchboard,
configuration handler and more, it's also the process-manager.
This class could really do with an overhaul.
*/
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
void restoreWindow();
void userDialog(QString = {});
/// Open the configuration dialog
void openConfig(ConfigureDialog::Page page = ConfigureDialog::Page::None);
void setUiElementsEnabled(bool state);
const QModelIndex getCurrentTreeViewIndex();
void setVisible(bool visible) override;
protected:
void closeEvent(QCloseEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
void changeEvent(QEvent *event) override;
bool eventFilter(QObject *obj, QEvent *event) override;
public Q_SLOTS:
void deselect();
void selectTreeItem(const QModelIndex &index);
private Q_SLOTS:
void addPassword();
void addFolder();
void onEdit(bool edit);
void onDelete();
void onUsers();
void editTreeItem(const QModelIndex &index);
void switchToPasswordEditor(const QString &filePath, const QString &content = {});
void filterList(const QString &arg1);
void selectFromSearch();
void showContextMenu(const QPoint &pos);
void renameFolder();
void renamePassword();
void focusInput();
void onTimeoutSearch();
void verifyInitialized();
void slotSetupFinished(const QString &location, const QByteArray &keyId);
+ void updateRootIndex();
private:
QString fallbackStore();
QScopedPointer<Ui::MainWindow> ui;
ClipboardHelper *const m_clipboardHelper;
PasswordViewerWidget *const m_passwordViewer;
PasswordEditorWidget *const m_passwordEditor;
RootFoldersManager *const m_rootFoldersManager;
StoreModel m_storeModel;
QTimer searchTimer;
KMessageWidget *m_notInitialized;
KMessageWidget *m_errorMessage;
QString m_selectedFile;
bool firstShow = true;
void initToolBarButtons();
void updateText();
void selectFirstFile();
QModelIndex firstFile(QModelIndex parentIndex);
void setPassword(QString, bool isNew = true);
void initTrayIcon();
void destroyTrayIcon();
void reencryptPath(QString dir);
};
#endif // MAINWINDOW_H_
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Dec 23, 1:47 PM (1 d, 51 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
cd/d4/77fe0b8bd877ef4f87d9e6cb31c0
Attached To
rGPGPASS GnuPG Password Manager
Event Timeline
Log In to Comment