diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index f880b9e68..263bc43fa 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -385,6 +385,11 @@ void DatabaseWidget::deleteEntries() } } +void DatabaseWidget::setFocus() +{ + m_entryView->setFocus(); +} + void DatabaseWidget::copyTitle() { Entry* currentEntry = m_entryView->currentEntry(); diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 2fd373549..f55fa2027 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -116,6 +116,7 @@ public Q_SLOTS: void createEntry(); void cloneEntry(); void deleteEntries(); + void setFocus(); void copyTitle(); void copyUsername(); void copyPassword(); diff --git a/src/gui/SearchWidget.cpp b/src/gui/SearchWidget.cpp index 3d9325a0b..5edf17cdd 100644 --- a/src/gui/SearchWidget.cpp +++ b/src/gui/SearchWidget.cpp @@ -24,20 +24,6 @@ #include "core/FilePath.h" -bool SearchEventFilter::eventFilter(QObject *obj, QEvent *event) -{ - if (event->type() == QEvent::KeyPress) { - QKeyEvent *keyEvent = static_cast(event); - if (keyEvent->key() == Qt::Key_Escape) { - emit escapePressed(); - return true; - } - } - - return QObject::eventFilter(obj, event); -} - - SearchWidget::SearchWidget(QWidget *parent) : QWidget(parent) , m_ui(new Ui::SearchWidget()) @@ -51,11 +37,11 @@ SearchWidget::SearchWidget(QWidget *parent) connect(m_ui->searchEdit, SIGNAL(returnPressed()), SLOT(startSearch())); connect(m_ui->searchIcon, SIGNAL(triggered(QAction*)), m_ui->searchEdit, SLOT(setFocus())); connect(m_searchTimer, SIGNAL(timeout()), this, SLOT(startSearch())); - connect(&m_searchEventFilter, SIGNAL(escapePressed()), m_ui->searchEdit, SLOT(clear())); + connect(this, SIGNAL(escapePressed()), m_ui->searchEdit, SLOT(clear())); new QShortcut(Qt::CTRL + Qt::Key_F, m_ui->searchEdit, SLOT(setFocus()), nullptr, Qt::ApplicationShortcut); - m_ui->searchEdit->installEventFilter(&m_searchEventFilter); + m_ui->searchEdit->installEventFilter(this); QMenu *searchMenu = new QMenu(); m_actionCaseSensitive = searchMenu->addAction(tr("Case Sensitive"), this, SLOT(updateCaseSensitive())); @@ -72,10 +58,44 @@ SearchWidget::~SearchWidget() } +bool SearchWidget::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Escape) { + emit escapePressed(); + return true; + } + else if (keyEvent->matches(QKeySequence::Copy)) { + // If Control+C is pressed in the search edit when no + // text is selected, copy the password of the current + // entry. + if (!m_ui->searchEdit->hasSelectedText()) { + emit copyPressed(); + return true; + } + } + else if (keyEvent->matches(QKeySequence::MoveToNextLine)) { + // If Down is pressed at EOL in the search edit, move + // the focus to the entry view. + QLineEdit* searchEdit = m_ui->searchEdit; + if (!searchEdit->hasSelectedText() && + searchEdit->cursorPosition() == searchEdit->text().length()) { + emit downPressed(); + return true; + } + } + } + + return QObject::eventFilter(obj, event); +} + void SearchWidget::connectSignals(SignalMultiplexer& mx) { mx.connect(this, SIGNAL(search(QString)), SLOT(search(QString))); mx.connect(this, SIGNAL(caseSensitiveChanged(bool)), SLOT(setSearchCaseSensitive(bool))); + mx.connect(this, SIGNAL(copyPressed()), SLOT(copyPassword())); + mx.connect(this, SIGNAL(downPressed()), SLOT(setFocus())); } void SearchWidget::databaseChanged(DatabaseWidget *dbWidget) diff --git a/src/gui/SearchWidget.h b/src/gui/SearchWidget.h index 844cfb0dc..01f9e5365 100644 --- a/src/gui/SearchWidget.h +++ b/src/gui/SearchWidget.h @@ -28,17 +28,6 @@ namespace Ui { class SearchWidget; } -class SearchEventFilter : public QObject -{ - Q_OBJECT -signals: - void escapePressed(); - -protected: - virtual bool eventFilter(QObject *obj, QEvent *event) override; -}; - - class SearchWidget : public QWidget { Q_OBJECT @@ -50,9 +39,15 @@ public: void connectSignals(SignalMultiplexer& mx); void setCaseSensitive(bool state); +protected: + bool eventFilter(QObject *obj, QEvent *event); + signals: void search(const QString &text); void caseSensitiveChanged(bool state); + void escapePressed(); + void copyPressed(); + void downPressed(); public slots: void databaseChanged(DatabaseWidget* dbWidget); @@ -65,8 +60,6 @@ private slots: private: const QScopedPointer m_ui; QTimer* m_searchTimer; - SearchEventFilter m_searchEventFilter; - QAction *m_actionCaseSensitive; Q_DISABLE_COPY(SearchWidget) diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 41059276d..91271c189 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include "config-keepassx-tests.h" #include "core/Config.h" @@ -287,6 +289,10 @@ void TestGui::testAddEntry() // Add entry "something 2" QTest::mouseClick(entryNewWidget, Qt::LeftButton); QTest::keyClicks(titleEdit, "something 2"); + QLineEdit* passwordEdit = editEntryWidget->findChild("passwordEdit"); + QLineEdit* passwordRepeatEdit = editEntryWidget->findChild("passwordRepeatEdit"); + QTest::keyClicks(passwordEdit, "something 2"); + QTest::keyClicks(passwordRepeatEdit, "something 2"); QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); // Add entry "something 3" @@ -358,17 +364,17 @@ void TestGui::testEntryEntropy() QTest::keyClicks(editNewPassword, "correcthorsebatterystaple"); QCOMPARE(entropyLabel->text(), QString("Entropy: 47.98 bit")); QCOMPARE(strengthLabel->text(), QString("Password Quality: Weak")); - + editNewPassword->setText(""); QTest::keyClicks(editNewPassword, "YQC3kbXbjC652dTDH"); QCOMPARE(entropyLabel->text(), QString("Entropy: 96.07 bit")); QCOMPARE(strengthLabel->text(), QString("Password Quality: Good")); - + editNewPassword->setText(""); QTest::keyClicks(editNewPassword, "Bs5ZFfthWzR8DGFEjaCM6bGqhmCT4km"); QCOMPARE(entropyLabel->text(), QString("Entropy: 174.59 bit")); QCOMPARE(strengthLabel->text(), QString("Password Quality: Excellent")); - + // We are done } @@ -406,6 +412,19 @@ void TestGui::testSearch() // Search for "someTHING" QTest::keyClicks(searchTextEdit, "THING"); QTRY_COMPARE(entryView->model()->rowCount(), 2); + // Press Down to focus on the entry view if at EOL + QTest::keyClick(searchTextEdit, Qt::Key_Right, Qt::ControlModifier); + QTRY_VERIFY(searchTextEdit->hasFocus()); + QTest::keyClick(searchTextEdit, Qt::Key_Down); + QTRY_VERIFY(entryView->hasFocus()); + // Test clipboard + QClipboard *clipboard = QApplication::clipboard(); + QTest::keyClick(entryView, Qt::Key_C, Qt::ControlModifier); + QModelIndex searchedItem = entryView->model()->index(0, 1); + Entry* searchedEntry = entryView->entryFromIndex(searchedItem); + QTRY_COMPARE(searchedEntry->password(), clipboard->text()); + // Restore focus + QTest::mouseClick(searchTextEdit, Qt::LeftButton); // Test case sensitive search searchWidget->setCaseSensitive(true);