Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F20064573
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
31 KB
Subscribers
None
View Options
diff --git a/src/widgets/mainwindow.cpp b/src/widgets/mainwindow.cpp
index 8bc0ddb..2fc1b0c 100644
--- a/src/widgets/mainwindow.cpp
+++ b/src/widgets/mainwindow.cpp
@@ -1,874 +1,874 @@
/*
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 <KActionCollection>
#include <KLocalizedString>
#include <KMessageBox>
#include <KMessageWidget>
#include <KStandardAction>
#include <KStandardGuiItem>
#include <QCloseEvent>
#include <QComboBox>
#include <QDebug>
#include <QDirIterator>
#include <QFileInfo>
#include <QInputDialog>
#include <QMenu>
#include <QMessageBox>
#include <QShortcut>
#include <qstringliteral.h>
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)
: KXmlGuiWindow(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))
{
ui->setupUi(this);
initToolBarButtons();
setupGUI(ToolBar | Keys | Save | Create, QStringLiteral("gpgpassui.rc"));
setCentralWidget(ui->centralWidget);
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);
connect(m_passwordEditor, &PasswordEditorWidget::editorClosed, this, [this]() {
m_actionEdit->setChecked(false);
});
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);
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()
{
m_actionAddEntry = new QAction(QIcon::fromTheme(QStringLiteral("entry-new")), i18nc("@action", "Add Entry"), this);
m_actionAddEntry->setToolTip(i18nc("@info:tooltip", "Add Password Entry"));
actionCollection()->addAction(QStringLiteral("add_entry"), m_actionAddEntry);
connect(m_actionAddEntry, &QAction::triggered, this, &MainWindow::addPassword);
m_actionAddFolder = new QAction(QIcon::fromTheme(QStringLiteral("folder-new-symbolic")), i18nc("@action", "Add Folder"), this);
actionCollection()->addAction(QStringLiteral("add_folder"), m_actionAddFolder);
connect(m_actionAddFolder, &QAction::triggered, this, &MainWindow::addFolder);
m_actionDelete = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete-symbolic")), i18nc("@action", "Delete"), this);
actionCollection()->addAction(QStringLiteral("gpgpass_delete"), m_actionDelete);
connect(m_actionDelete, &QAction::triggered, this, &MainWindow::onDelete);
m_actionEdit = new QAction(QIcon::fromTheme(QStringLiteral("document-properties-symbolic")), i18nc("@action", "Edit"), this);
m_actionEdit->setCheckable(true);
actionCollection()->addAction(QStringLiteral("gpgpass_edit"), m_actionEdit);
connect(m_actionEdit, &QAction::toggled, this, &MainWindow::onEdit);
m_actionUsers = new QAction(QIcon::fromTheme(QStringLiteral("x-office-address-book-symbolic")), i18nc("@action", "Users"), this);
m_actionUsers->setToolTip(i18nc("@info:tooltip", "Manage who can read password in folder"));
actionCollection()->addAction(QStringLiteral("gpgpass_users"), m_actionUsers);
connect(m_actionUsers, &QAction::triggered, this, &MainWindow::onUsers);
KStandardAction::quit(this, &MainWindow::close, actionCollection());
m_actionConfig = KStandardAction::preferences(
this,
[this] {
openConfig(ConfigureDialog::Page::None);
},
actionCollection());
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);
}
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) {
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;
m_clipboardHelper->clearClippedText();
QString file = index.data(QFileSystemModel::FilePathRole).toString();
m_actionEdit->setChecked(false);
QFileInfo fileInfo(file);
if (!file.isEmpty() && fileInfo.isFile() && !cleared) {
m_selectedFile = file;
const auto filePath = fileInfo.absoluteFilePath();
if (m_passwordViewer->filePath() != filePath) {
m_passwordViewer->setFilePath(ui->treeView->selectionModel()->currentIndex().data().toString(), filePath);
}
ui->stackedWidget->setCurrentIndex((int)StackLayer::PasswordViewer);
} else {
m_passwordViewer->clear();
m_actionEdit->setEnabled(false);
m_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;
}
const auto filePath = fileInfo.absoluteFilePath();
if (m_passwordViewer->filePath() == filePath && !m_passwordViewer->rawContent().isEmpty()) {
switchToPasswordEditor(filePath, m_passwordViewer->rawContent());
} else {
auto decryptJob = new FileDecryptJob(filePath);
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)
{
// Ensure we don't trigger the actionEdit
disconnect(m_actionEdit, &QAction::toggled, this, &MainWindow::onEdit);
m_actionEdit->setChecked(true);
connect(m_actionEdit, &QAction::toggled, this, &MainWindow::onEdit);
const QFileInfo fileInfo(filePath);
const auto name = fileInfo.baseName();
const auto absoluteFilePath = fileInfo.absoluteFilePath();
PassEntry entry(name, content);
m_passwordEditor->setPassEntry(entry);
ui->stackedWidget->setCurrentIndex((int)StackLayer::PasswordEditor);
disconnect(m_passwordEditor, &PasswordEditorWidget::save, this, nullptr);
connect(m_passwordEditor, &PasswordEditorWidget::save, this, [this, name, fileInfo, absoluteFilePath](const QString &content) {
const auto recipients = m_storeModel.recipientsForFile(fileInfo);
if (recipients.isEmpty()) {
m_errorMessage->setText(i18n("Could not read encryption key to use, .gpg-id file missing or invalid."));
m_errorMessage->animatedShow();
return;
}
auto encryptJob = new FileEncryptJob(absoluteFilePath, content.toUtf8(), recipients);
connect(encryptJob, &FileEncryptJob::finished, this, [absoluteFilePath, name, encryptJob, content, this](KJob *) {
if (encryptJob->error() != KJob::NoError) {
m_errorMessage->setText(encryptJob->errorText());
m_errorMessage->animatedShow();
qWarning() << encryptJob->errorText();
return;
}
ui->treeView->setFocus();
// Ensure we don't trigger the actionEdit
disconnect(m_actionEdit, &QAction::toggled, this, &MainWindow::onEdit);
m_actionEdit->setChecked(false);
connect(m_actionEdit, &QAction::toggled, this, &MainWindow::onEdit);
if (m_passwordViewer->filePath() == absoluteFilePath) {
m_passwordViewer->setRawContent(content);
} else {
- // slower but insure state is correct
+ // slower but ensure state is correct
m_passwordViewer->setFilePath(name, absoluteFilePath);
}
ui->stackedWidget->setCurrentIndex((int)StackLayer::PasswordViewer);
auto index = m_storeModel.indexForPath(absoluteFilePath);
ui->treeView->setCurrentIndex(index);
selectTreeItem(index);
});
encryptJob->start();
});
}
/**
* @brief MainWindow::deselect clear the selection, password and copy buffer
*/
void MainWindow::deselect()
{
m_clipboardHelper->clearClipboard();
ui->treeView->clearSelection();
m_actionEdit->setEnabled(false);
m_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);
m_actionAddEntry->setEnabled(state);
m_actionAddFolder->setEnabled(state);
m_actionUsers->setEnabled(state);
m_actionConfig->setEnabled(state);
// is a file selected?
state &= ui->treeView->currentIndex().isValid();
m_actionDelete->setEnabled(state);
m_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 {
m_actionEdit->setEnabled(false);
m_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 entry?"),
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());
m_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;
}
setUiElementsEnabled(true);
ui->treeView->setFocus();
verifyInitialized();
usersDialog->deleteLater();
});
// 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();
m_actionEdit->setEnabled(false);
m_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);
m_actionAddFolder->setEnabled(alreadyConfigured);
m_actionAddEntry->setEnabled(alreadyConfigured);
m_actionDelete->setEnabled(m_actionDelete->isEnabled() && alreadyConfigured);
m_actionEdit->setEnabled(m_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();
m_actionDelete->setEnabled(false);
m_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 Entry"));
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 Entry"));
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 Entry"));
renamePasswordAction->setToolTip(i18nc("@info:tooltip", "Rename Password Entry"));
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 Entry") : 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);
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Feb 23, 7:17 PM (5 h, 3 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
0a/16/a138e9b115b7a31d6b684d4bfdbf
Attached To
rGPGPASS GnuPG Password Manager
Event Timeline
Log In to Comment