Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34215452
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/mainwindow.cpp b/src/mainwindow.cpp
index 227e3df..0a9d5fd 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -1,1019 +1,1023 @@
#include "mainwindow.h"
#ifdef QT_DEBUG
#include "debughelper.h"
#endif
#ifndef VERSION
#include <qtpass_version.h>
#define VERSION QTPASS_VERSION_STRING
#endif
#include "configdialog.h"
#include "filecontent.h"
#include "passworddialog.h"
#include "qpushbuttonfactory.h"
#include "qtpass.h"
#include "qtpasssettings.h"
#include "trayicon.h"
#include "ui_mainwindow.h"
#include "usersdialog.h"
#include "util.h"
#include <QCloseEvent>
#include <QDialog>
#include <QFileInfo>
#include <QInputDialog>
#include <QLabel>
#include <QMenu>
#include <QMessageBox>
#include <QShortcut>
#include <QTimer>
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(const QString &searchText, QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow), keygen(nullptr),
tray(nullptr) {
#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);
m_qtPass = new QtPass(this);
// register shortcut ctrl/cmd + Q to close the main window
new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q), this, SLOT(close()));
model.setNameFilters(QStringList() << QStringLiteral("*.gpg"));
model.setNameFilterDisables(false);
/*
* I added this to solve Windows bug but now on GNU/Linux the main folder,
* if hidden, disappear
*
* model.setFilter(QDir::NoDot);
*/
QString passStore = QtPassSettings::getPassStore(Util::findPasswordStore());
QModelIndex rootDir = model.setRootPath(passStore);
model.fetchMore(rootDir);
proxyModel.setModelAndStore(&model, passStore);
selectionModel.reset(new QItemSelectionModel(&proxyModel));
ui->treeView->setModel(&proxyModel);
ui->treeView->setRootIndex(proxyModel.mapFromSource(rootDir));
ui->treeView->setColumnHidden(1, true);
ui->treeView->setColumnHidden(2, true);
ui->treeView->setColumnHidden(3, true);
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);
if (QtPassSettings::isNoLineWrapping()) {
ui->textBrowser->setLineWrapMode(QTextBrowser::NoWrap);
}
ui->textBrowser->setOpenExternalLinks(true);
ui->textBrowser->setContextMenuPolicy(Qt::DefaultContextMenu);
updateProfileBox();
clearPanelTimer.setInterval(1000 *
QtPassSettings::getAutoclearPanelSeconds());
clearPanelTimer.setSingleShot(true);
connect(&clearPanelTimer, SIGNAL(timeout()), this, SLOT(clearPanel()));
searchTimer.setInterval(350);
searchTimer.setSingleShot(true);
connect(&searchTimer, &QTimer::timeout, this, &MainWindow::onTimeoutSearch);
initToolBarButtons();
initStatusBar();
ui->lineEdit->setClearButtonEnabled(true);
setUiElementsEnabled(true);
QTimer::singleShot(10, this, SLOT(focusInput()));
ui->lineEdit->setText(searchText);
if (!m_qtPass->init())
// no working config so this should just quit
QApplication::quit();
}
MainWindow::~MainWindow() { delete m_qtPass; }
/**
* @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::triggered, 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, &MainWindow::onConfig);
connect(ui->treeView, &QTreeView::clicked, this, &MainWindow::selectTreeItem);
connect(ui->treeView, &QTreeView::doubleClicked, this, &MainWindow::editTreeItem);
connect(ui->profileBox, &QComboBox::currentTextChanged, this, &MainWindow::selectProfile);
connect(ui->lineEdit, &QLineEdit::textChanged, this, &MainWindow::filterList);
connect(ui->lineEdit, &QLineEdit::returnPressed, this, &MainWindow::selectFromSearch);
ui->actionAddPassword->setIcon(
QIcon::fromTheme(QStringLiteral("document-new"), QIcon(QStringLiteral(":/icons/document-new.svg"))));
ui->actionAddFolder->setIcon(
QIcon::fromTheme(QStringLiteral("folder-new"), QIcon(QStringLiteral(":/icons/folder-new.svg"))));
ui->actionEdit->setIcon(QIcon::fromTheme(
QStringLiteral("document-properties"), QIcon(QStringLiteral(":/icons/document-properties.svg"))));
ui->actionDelete->setIcon(
QIcon::fromTheme(QStringLiteral("edit-delete"), QIcon(QStringLiteral(":/icons/edit-delete.svg"))));
ui->actionUsers->setIcon(QIcon::fromTheme(
QStringLiteral("x-office-address-book"), QIcon(QStringLiteral(":/icons/x-office-address-book.svg"))));
ui->actionConfig->setIcon(QIcon::fromTheme(
QStringLiteral("applications-system"), QIcon(QStringLiteral(":/icons/applications-system.svg"))));
}
/**
* @brief MainWindow::initStatusBar init statusBar with default message and logo
*/
void MainWindow::initStatusBar() {
ui->statusBar->showMessage(tr("Welcome to QtPass %1").arg(QString::fromLocal8Bit(VERSION)), 2000);
QPixmap logo = QPixmap::fromImage(QImage(QStringLiteral(":/artwork/icon.svg")))
.scaledToHeight(statusBar()->height());
QLabel *logoApp = new QLabel(statusBar());
logoApp->setPixmap(logo);
statusBar()->addPermanentWidget(logoApp);
}
const QModelIndex MainWindow::getCurrentTreeViewIndex() {
return ui->treeView->currentIndex();
}
void MainWindow::cleanKeygenDialog() {
this->keygen->realDone(QDialog::Accepted);
this->keygen = nullptr;
}
void MainWindow::flashText(const QString &text, const bool isError,
const bool isHtml) {
if (isError)
ui->textBrowser->setTextColor(Qt::red);
if (isHtml) {
QString _text = text;
if (!ui->textBrowser->toPlainText().isEmpty())
_text = ui->textBrowser->toHtml() + _text;
ui->textBrowser->setHtml(_text);
} else {
ui->textBrowser->setText(text);
ui->textBrowser->setTextColor(Qt::black);
}
}
/**
* @brief MainWindow::config pops up the configuration screen and handles all
* inter-window communication
*/
void MainWindow::config() {
QScopedPointer<ConfigDialog> d(new ConfigDialog(this));
d->setModal(true);
if (m_qtPass->isFreshStart())
d->wizard(); // does shit
if (d->exec()) {
if (d->result() == QDialog::Accepted) {
// Update the textBrowser line wrap mode
if (QtPassSettings::isNoLineWrapping()) {
ui->textBrowser->setLineWrapMode(QTextBrowser::NoWrap);
} else {
ui->textBrowser->setLineWrapMode(QTextBrowser::WidgetWidth);
}
this->show();
updateProfileBox();
ui->treeView->setRootIndex(proxyModel.mapFromSource(
model.setRootPath(QtPassSettings::getPassStore())));
clearPanelTimer.setInterval(1000 *
QtPassSettings::getAutoclearPanelSeconds());
m_qtPass->setClipboardTimer();
if (QtPassSettings::isUseTrayIcon() && tray == nullptr)
initTrayIcon();
else if (!QtPassSettings::isUseTrayIcon() && tray != nullptr) {
destroyTrayIcon();
}
}
m_qtPass->setFreshStart(false);
}
}
/**
* @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_qtPass->clearClippedText();
QString file = index.data(QFileSystemModel::FilePathRole).toString();
ui->passwordName->setText(index.data().toString());
- if (!file.isEmpty() && !cleared) {
+ if (!file.isEmpty() && QFileInfo(file).isFile() && !cleared) {
QtPassSettings::getPass()->Show(file);
} else {
clearPanel(false);
ui->actionEdit->setEnabled(false);
ui->actionDelete->setEnabled(true);
}
}
/**
* @brief MainWindow::on_treeView_doubleClicked when doubleclicked on
* TreeViewItem, open the edit Window
* @param index
*/
void MainWindow::editTreeItem(const QModelIndex &index) {
QFileInfo fileOrFolder{index.data(QFileSystemModel::Roles::FilePathRole).toString()};
if (fileOrFolder.isFile()) {
editPassword(fileOrFolder.absoluteFilePath());
}
}
/**
* @brief MainWindow::deselect clear the selection, password and copy buffer
*/
void MainWindow::deselect() {
m_qtPass->clearClipboard();
ui->treeView->clearSelection();
ui->actionEdit->setEnabled(false);
ui->actionDelete->setEnabled(false);
ui->passwordName->setText(QString{});
clearPanel(false);
}
void MainWindow::executeWrapperStarted() {
clearTemplateWidgets();
ui->textBrowser->clear();
setUiElementsEnabled(false);
clearPanelTimer.stop();
}
void MainWindow::passShowHandler(const QString &p_output) {
QStringList templ = QtPassSettings::isUseTemplate()
? QtPassSettings::getPassTemplate().split(QStringLiteral("\n"))
: QStringList();
bool allFields =
QtPassSettings::isUseTemplate() && QtPassSettings::isTemplateAllFields();
FileContent fileContent = FileContent::parse(p_output, templ, allFields);
QString output = p_output;
QString password = fileContent.getPassword();
// set clipped text
m_qtPass->setClippedText(password, p_output);
// first clear the current view:
clearTemplateWidgets();
// show what is needed:
if (QtPassSettings::isHideContent()) {
output = QStringLiteral("***") + tr("Content hidden") + QStringLiteral("***");
} else if (!QtPassSettings::isDisplayAsIs()) {
if (!password.isEmpty()) {
// set the password, it is hidden if needed in addToGridLayout
addToGridLayout(0, tr("Password"), password);
}
NamedValues namedValues = fileContent.getNamedValues();
for (int j = 0; j < namedValues.length(); ++j) {
NamedValue nv = namedValues.at(j);
addToGridLayout(j + 1, nv.name, nv.value);
}
if (ui->gridLayout->count() == 0)
ui->verticalLayoutPassword->setSpacing(0);
else
ui->verticalLayoutPassword->setSpacing(6);
output = fileContent.getRemainingDataForDisplay();
}
if (QtPassSettings::isUseAutoclearPanel()) {
clearPanelTimer.start();
}
Q_EMIT passShowHandlerFinished(output);
setUiElementsEnabled(true);
}
/**
* @brief MainWindow::clearPanel hide the information from shoulder surfers
*/
void MainWindow::clearPanel(bool notify) {
while (ui->gridLayout->count() > 0) {
QLayoutItem *item = ui->gridLayout->takeAt(0);
delete item->widget();
delete item;
}
if (notify) {
QString output = QStringLiteral("***") + tr("Password and Content hidden") + QStringLiteral("***");
ui->textBrowser->setHtml(output);
} else {
ui->textBrowser->setHtml(QString{});
}
}
/**
* @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);
}
void MainWindow::restoreWindow() {
QByteArray geometry = QtPassSettings::getGeometry(saveGeometry());
restoreGeometry(geometry);
QByteArray savestate = QtPassSettings::getSavestate(saveState());
restoreState(savestate);
QPoint position = QtPassSettings::getPos(pos());
move(position);
QSize newSize = QtPassSettings::getSize(size());
resize(newSize);
if (QtPassSettings::isUseTrayIcon() && tray == nullptr) {
initTrayIcon();
if (QtPassSettings::isStartMinimized()) {
// since we are still in constructor, can't directly hide
QTimer::singleShot(10, this, SLOT(hide()));
}
} else if (!QtPassSettings::isUseTrayIcon() && tray != nullptr) {
destroyTrayIcon();
}
}
/**
* @brief MainWindow::on_configButton_clicked run Mainwindow::config
*/
void MainWindow::onConfig() { config(); }
/**
* @brief Executes when the string in the search box changes, collapses the
* TreeView
* @param arg1
*/
void MainWindow::filterList(const QString &arg1) {
ui->statusBar->showMessage(tr("Looking for: %1").arg(arg1), 1000);
ui->treeView->expandAll();
clearPanel(false);
ui->passwordName->setText(QString{});
ui->actionEdit->setEnabled(false);
ui->actionDelete->setEnabled(false);
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);
proxyModel.setFilterRegularExpression(regExp);
ui->treeView->setRootIndex(proxyModel.mapFromSource(
model.setRootPath(QtPassSettings::getPassStore())));
if (proxyModel.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() {
#ifdef QT_DEBUG
dbg() << "on_lineEdit_returnPressed" << proxyModel.rowCount();
#endif
if (proxyModel.rowCount() > 0) {
selectFirstFile();
selectTreeItem(ui->treeView->currentIndex());
}
}
/**
* @brief MainWindow::selectFirstFile select the first possible file in the
* tree
*/
void MainWindow::selectFirstFile() {
QModelIndex index = proxyModel.mapFromSource(
model.setRootPath(QtPassSettings::getPassStore()));
index = firstFile(index);
ui->treeView->setCurrentIndex(index);
}
/**
* @brief MainWindow::firstFile return location of first possible file
* @param parentIndex
* @return QModelIndex
*/
QModelIndex MainWindow::firstFile(QModelIndex parentIndex) {
QModelIndex index = parentIndex;
int numRows = proxyModel.rowCount(parentIndex);
for (int row = 0; row < numRows; ++row) {
index = proxyModel.index(row, 0, parentIndex);
if (model.fileInfo(proxyModel.mapToSource(index)).isFile())
return index;
if (proxyModel.hasChildren(index))
return firstFile(index);
}
return index;
}
/**
* @brief MainWindow::setPassword open passworddialog
* @param file which pgp file
* @param isNew insert (not update)
*/
void MainWindow::setPassword(QString file, bool isNew) {
PasswordDialog d(file, isNew, this);
if (!d.exec()) {
ui->treeView->setFocus();
}
}
/**
* @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());
QString file =
QInputDialog::getText(this, tr("New file"),
tr("New password file: \n(Will be placed in %1 )")
.arg(dir),
QLineEdit::Normal, QString{}, &ok);
if (!ok || file.isEmpty())
return;
file = QDir(dir).absoluteFilePath(file);
setPassword(file);
}
/**
* @brief MainWindow::onDelete remove password, if you are
* sure.
*/
void MainWindow::onDelete() {
QModelIndex currentIndex = ui->treeView->currentIndex();
if (!currentIndex.isValid()) {
// This fixes https://github.com/IJHack/QtPass/issues/556
// Otherwise the entire password directory would be deleted if
// nothing is selected in the tree view.
return;
}
QFileInfo fileOrFolder = currentIndex.data(QFileSystemModel::FilePathRole).toString();
QString file = QString{};
bool isDir = fileOrFolder.isDir();
file = fileOrFolder.absoluteFilePath();
QString dirMessage = tr(" and the whole content?");
if (isDir) {
QDirIterator it(model.rootPath() + QDir::separator() + file,
QDirIterator::Subdirectories);
bool okDir = true;
while (it.hasNext() && okDir) {
it.next();
if (auto fi = it.fileInfo(); fi.isFile()) {
if (fi.suffix() != QStringLiteral("gpg")) {
okDir = false;
dirMessage = tr(" and the whole content? <br><strong>Attention: "
"there are unexpected files in the given folder, "
"check them before continue.</strong>");
}
}
}
}
if (QMessageBox::question(
this, isDir ? tr("Delete folder?") : tr("Delete password?"),
tr("Are you sure you want to delete %1%2?")
.arg(QDir::separator() + file, isDir ? dirMessage : QStringLiteral("?")),
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
return;
QtPassSettings::getPass()->Remove(file, isDir);
}
/**
* @brief MainWindow::onEdit try and edit (selected) password.
*/
void MainWindow::onEdit() {
QString file = ui->treeView->currentIndex().data(QFileSystemModel::FilePathRole).toString();
editPassword(file);
}
/**
* @brief MainWindow::userDialog see MainWindow::onUsers()
* @param dir folder to edit users for.
*/
void MainWindow::userDialog(QString dir) {
QFileInfo fi(dir);
if (!fi.isDir()) {
dir = fi.absolutePath();
}
UsersDialog d(dir, this);
if (!d.exec()) {
ui->treeView->setFocus();
}
}
/**
* @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();
- QFileInfo fi(dir);
- if (!fi.isDir()) {
+ if (dir.isEmpty()) {
+ dir = QtPassSettings::getPassStore();
+ } else {
+ QFileInfo fi(dir);
+ if (!fi.isDir()) {
dir = fi.absolutePath();
+ }
}
userDialog(dir);
}
/**
* @brief MainWindow::messageAvailable we have some text/message/search to do.
* @param message
*/
void MainWindow::messageAvailable(QString message) {
if (message.isEmpty()) {
focusInput();
} else {
ui->treeView->expandAll();
ui->lineEdit->setText(message);
selectFromSearch();
}
show();
raise();
}
/**
* @brief MainWindow::generateKeyPair internal gpg keypair generator . .
* @param batch
* @param keygenWindow
*/
void MainWindow::generateKeyPair(GenKeyData batch, KeygenDialog *keygenWindow) {
keygen = keygenWindow;
Q_EMIT generateGPGKeyPair(batch);
}
/**
* @brief MainWindow::updateProfileBox update the list of profiles, optionally
* select a more appropriate one to view too
*/
void MainWindow::updateProfileBox() {
QHash<QString, QString> profiles = QtPassSettings::getProfiles();
if (profiles.isEmpty()) {
ui->profileWidget->hide();
} else {
ui->profileWidget->show();
ui->profileBox->setEnabled(profiles.size() > 1);
ui->profileBox->clear();
QHashIterator<QString, QString> i(profiles);
while (i.hasNext()) {
i.next();
if (!i.key().isEmpty())
ui->profileBox->addItem(i.key());
}
ui->profileBox->model()->sort(0);
}
int index = ui->profileBox->findText(QtPassSettings::getProfile());
if (index != -1) // -1 for not found
ui->profileBox->setCurrentIndex(index);
}
/**
* @brief MainWindow::on_profileBox_currentIndexChanged make sure we show the
* correct "profile"
* @param name
*/
void MainWindow::selectProfile(QString name) {
if (m_qtPass->isFreshStart() || name == QtPassSettings::getProfile())
return;
ui->lineEdit->clear();
QtPassSettings::setProfile(name);
QtPassSettings::setPassStore(QtPassSettings::getProfiles().value(name));
ui->statusBar->showMessage(tr("Profile changed to %1").arg(name), 2000);
ui->treeView->selectionModel()->clear();
ui->treeView->setRootIndex(proxyModel.mapFromSource(
model.setRootPath(QtPassSettings::getPassStore())));
ui->actionEdit->setEnabled(false);
ui->actionDelete->setEnabled(false);
}
/**
* @brief MainWindow::initTrayIcon show a nice tray icon on systems that
* support
* it
*/
void MainWindow::initTrayIcon() {
this->tray = new TrayIcon(this);
// Setup tray icon
if (tray == nullptr) {
#ifdef QT_DEBUG
dbg() << "Allocating tray icon failed.";
#endif
}
if (!tray->getIsAllocated()) {
destroyTrayIcon();
}
}
/**
* @brief MainWindow::destroyTrayIcon remove that pesky tray icon
*/
void MainWindow::destroyTrayIcon() {
delete this->tray;
tray = nullptr;
}
/**
* @brief MainWindow::closeEvent hide or quit
* @param event
*/
void MainWindow::closeEvent(QCloseEvent *event) {
if (QtPassSettings::isHideOnClose()) {
this->hide();
event->ignore();
} else {
m_qtPass->clearClipboard();
QtPassSettings::setGeometry(saveGeometry());
QtPassSettings::setSavestate(saveState());
QtPassSettings::setPos(pos());
QtPassSettings::setSize(size());
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 (proxyModel.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 =
model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
QMenu contextMenu;
if (!selected || fileOrFolder.isDir()) {
QAction *addFolder = contextMenu.addAction(tr("Add folder"));
QAction *addPassword = contextMenu.addAction(tr("Add password"));
QAction *users = contextMenu.addAction(tr("Users"));
connect(addFolder, &QAction::triggered, this, &MainWindow::addFolder);
connect(addPassword, &QAction::triggered, this, &MainWindow::addPassword);
connect(users, &QAction::triggered, this, &MainWindow::onUsers);
} else if (fileOrFolder.isFile()) {
QAction *edit = contextMenu.addAction(tr("Edit"));
connect(edit, &QAction::triggered, this, &MainWindow::onEdit);
}
if (selected) {
contextMenu.addSeparator();
if (fileOrFolder.isDir()) {
QAction *renameFolder = contextMenu.addAction(tr("Rename folder"));
connect(renameFolder, &QAction::triggered, this,
&MainWindow::renameFolder);
} else if (fileOrFolder.isFile()) {
QAction *renamePassword = contextMenu.addAction(tr("Rename password"));
connect(renamePassword, &QAction::triggered, this,
&MainWindow::renamePassword);
}
QAction *deleteItem = contextMenu.addAction(tr("Delete"));
connect(deleteItem, &QAction::triggered, this, &MainWindow::onDelete);
}
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());
QString newdir =
QInputDialog::getText(this, tr("New file"),
tr("New Folder: \n(Will be placed in %1 )")
.arg(dir),
QLineEdit::Normal, QString{}, &ok);
if (!ok || newdir.isEmpty())
return;
// dbg()<< newdir;
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()));
QString srcDirName = QDir(srcDir).dirName();
QString newName =
QInputDialog::getText(this, tr("Rename file"), tr("Rename Folder To: "),
QLineEdit::Normal, srcDirName, &ok);
if (!ok || newName.isEmpty())
return;
QString destDir = srcDir;
destDir.replace(srcDir.lastIndexOf(srcDirName), srcDirName.length(), newName);
QtPassSettings::getPass()->Move(srcDir, destDir);
}
/**
* @brief MainWindow::editPassword read password and open edit window via
* MainWindow::onEdit()
*/
void MainWindow::editPassword(const QString &file) {
if (!file.isEmpty()) {
setPassword(file, false);
}
}
/**
* @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, tr("Rename file"), tr("Rename File To: "),
QLineEdit::Normal, fileName, &ok);
if (!ok || newName.isEmpty())
return;
QString newFile = QDir(filePath).filePath(newName);
QtPassSettings::getPass()->Move(file, newFile);
}
/**
* @brief MainWindow::clearTemplateWidgets empty the template widget fields in
* the UI
*/
void MainWindow::clearTemplateWidgets() {
while (ui->gridLayout->count() > 0) {
QLayoutItem *item = ui->gridLayout->takeAt(0);
delete item->widget();
delete item;
}
ui->verticalLayoutPassword->setSpacing(0);
}
/**
* @brief MainWindow::addToGridLayout add a field to the template grid
* @param position
* @param field
* @param value
*/
void MainWindow::addToGridLayout(int position, const QString &field,
const QString &value) {
QString trimmedField = field.trimmed();
QString trimmedValue = value.trimmed();
// Combine the Copy button and the line edit in one widget
QFrame *frame = new QFrame();
QLayout *ly = new QHBoxLayout();
ly->setContentsMargins(5, 2, 2, 2);
ly->setSpacing(0);
frame->setLayout(ly);
auto fieldLabel = createPushButton(QIcon::fromTheme(QStringLiteral("edit-copy")), m_qtPass, [this, trimmedValue] {
m_qtPass->copyTextToClipboard(trimmedValue);
});
frame->layout()->addWidget(fieldLabel.release());
auto qrButton = createPushButton(QIcon::fromTheme(QStringLiteral("view-barcode-qr")), m_qtPass, [this, trimmedValue]() {
m_qtPass->showTextAsQRCode(trimmedValue);
});
frame->layout()->addWidget(qrButton.release());
if (trimmedField == tr("Password")) {
auto *line = new QLineEdit();
line->setObjectName(trimmedField);
line->setText(trimmedValue);
line->setReadOnly(true);
line->setContentsMargins(0, 0, 0, 0);
line->setEchoMode(QLineEdit::Password);
auto icon = QIcon::fromTheme(QStringLiteral("password-show-on"));
icon.addFile(QStringLiteral("password-show-off"),QSize(),QIcon::Normal, QIcon::Off);
auto showButton = createPushButton(icon, line, [line]() {
if (line->echoMode() == QLineEdit::Password) {
line->setEchoMode(QLineEdit::Normal);
} else {
line->setEchoMode(QLineEdit::Password);
}
});
showButton->setCheckable(true);
showButton->setContentsMargins(0, 0, 0, 0);
frame->layout()->addWidget(showButton.release());
frame->layout()->addWidget(line);
} else {
auto *line = new QTextBrowser();
line->setOpenExternalLinks(true);
line->setOpenLinks(true);
line->setMaximumHeight(26);
line->setMinimumHeight(26);
line->setSizePolicy(
QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
line->setObjectName(trimmedField);
trimmedValue.replace(Util::protocolRegex(), QStringLiteral(R"(<a href="\1">\1</a>)"));
line->setText(trimmedValue);
line->setReadOnly(true);
line->setContentsMargins(0, 0, 0, 0);
frame->layout()->addWidget(line);
}
// set into the layout
ui->gridLayout->addWidget(new QLabel(trimmedField), position, 0);
ui->gridLayout->addWidget(frame, position, 1);
}
/**
* @brief Displays message in status bar
*
* @param msg text to be displayed
* @param timeout time for which msg shall be visible
*/
void MainWindow::showStatusMessage(QString msg, int timeout) {
ui->statusBar->showMessage(msg, timeout);
}
/**
* @brief MainWindow::startReencryptPath disable ui elements and treeview
*/
void MainWindow::startReencryptPath() {
setUiElementsEnabled(false);
ui->treeView->setDisabled(true);
}
/**
* @brief MainWindow::endReencryptPath re-enable ui elements
*/
void MainWindow::endReencryptPath() { setUiElementsEnabled(true); }
/**
* @brief MainWindow::critical critical message popup wrapper.
* @param title
* @param msg
*/
void MainWindow::critical(QString title, QString msg) {
QMessageBox::critical(this, title, msg);
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Dec 19, 2:32 PM (1 d, 15 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
a6/44/4f49edfae4151564747ff95d7577
Attached To
rGPGPASS GnuPG Password Manager
Event Timeline
Log In to Comment