Skip to content

Commit

Permalink
Make search always visible (PR keepassxreboot#67)
Browse files Browse the repository at this point in the history
* Moved search bar to toolbar and consolidated search options into dropdown list
* Updated GUI tests to be atomic and rewrote search tests
* Searches are saved between databases
* Search is cleared when all databases are closed
* Implemented global search shortcut (CTRL+F) and a notification bar when in search mode
  • Loading branch information
droidmonkey authored Nov 3, 2016
1 parent 3f80134 commit 13983d0
Show file tree
Hide file tree
Showing 15 changed files with 507 additions and 412 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ macro(add_gcc_compiler_flags FLAGS)
add_gcc_compiler_cflags("${FLAGS}")
endmacro(add_gcc_compiler_flags)

add_definitions(-DQT_NO_KEYWORDS -DQT_NO_EXCEPTIONS -DQT_STRICT_ITERATORS -DQT_NO_CAST_TO_ASCII)
add_definitions(-DQT_NO_EXCEPTIONS -DQT_STRICT_ITERATORS -DQT_NO_CAST_TO_ASCII)

add_gcc_compiler_flags("-fno-common -fstack-protector --param=ssp-buffer-size=4")
add_gcc_compiler_flags("-Wall -Wextra -Wundef -Wpointer-arith -Wno-long-long")
Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ set(keepassx_SOURCES
gui/PasswordGeneratorWidget.cpp
gui/PasswordComboBox.cpp
gui/SettingsWidget.cpp
gui/SearchWidget.cpp
gui/SortFilterHideProxyModel.cpp
gui/UnlockDatabaseWidget.cpp
gui/WelcomeWidget.cpp
Expand Down
3 changes: 3 additions & 0 deletions src/autotype/AutoType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ void AutoType::performGlobalAutoType(const QList<Database*>& dbList)
QList<Entry*> entryList;
QHash<Entry*, QString> sequenceHash;

// TODO: Check if there are any active databases here, if not do nothing
// TODO: Check if all databases are locked, if so ask to unlock them

for (Database* db : dbList) {
const QList<Entry*> dbEntries = db->rootGroup()->entriesRecursive();
for (Entry* entry : dbEntries) {
Expand Down
211 changes: 64 additions & 147 deletions src/gui/DatabaseWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
*/

#include "DatabaseWidget.h"
#include "ui_SearchWidget.h"

#include <QAction>
#include <QDesktopServices>
Expand All @@ -25,8 +24,10 @@
#include <QLineEdit>
#include <QKeyEvent>
#include <QSplitter>
#include <QTimer>
#include <QLabel>
#include <QProcess>
#include <QHeaderView>
#include <QApplication>

#include "autotype/AutoType.h"
#include "core/Config.h"
Expand All @@ -50,24 +51,16 @@
DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
: QStackedWidget(parent)
, m_db(db)
, m_searchUi(new Ui::SearchWidget())
, m_searchWidget(new QWidget())
, m_newGroup(nullptr)
, m_newEntry(nullptr)
, m_newParent(nullptr)
{
m_searchUi->setupUi(m_searchWidget);

m_searchTimer = new QTimer(this);
m_searchTimer->setSingleShot(true);

m_mainWidget = new QWidget(this);
QLayout* layout = new QHBoxLayout(m_mainWidget);
m_splitter = new QSplitter(m_mainWidget);
m_splitter->setChildrenCollapsible(false);

QWidget* rightHandSideWidget = new QWidget(m_splitter);
m_searchWidget->setParent(rightHandSideWidget);

m_groupView = new GroupView(db, m_splitter);
m_groupView->setObjectName("groupView");
Expand All @@ -82,25 +75,24 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
connect(m_entryView, SIGNAL(customContextMenuRequested(QPoint)),
SLOT(emitEntryContextMenuRequested(QPoint)));

QAction* closeAction = new QAction(m_searchWidget);
QIcon closeIcon = filePath()->icon("actions", "dialog-close");
closeAction->setIcon(closeIcon);
m_searchUi->closeSearchButton->setDefaultAction(closeAction);
m_searchUi->closeSearchButton->setShortcut(Qt::Key_Escape);
m_searchWidget->hide();
m_searchUi->caseSensitiveCheckBox->setVisible(false);
m_searchUi->searchEdit->installEventFilter(this);
// Add a notification for when we are searching
m_searchingLabel = new QLabel();
m_searchingLabel->setText(tr("Searching..."));
m_searchingLabel->setAlignment(Qt::AlignCenter);
m_searchingLabel->setStyleSheet("background-color: rgb(255, 253, 160);"
"border: 2px solid rgb(190, 190, 190);"
"border-radius: 5px;");

QVBoxLayout* vLayout = new QVBoxLayout(rightHandSideWidget);
vLayout->setMargin(0);
vLayout->addWidget(m_searchWidget);
vLayout->addWidget(m_searchingLabel);
vLayout->addWidget(m_entryView);

m_searchingLabel->setVisible(false);

rightHandSideWidget->setLayout(vLayout);

setTabOrder(m_searchUi->searchRootRadioButton, m_entryView);
setTabOrder(m_entryView, m_groupView);
setTabOrder(m_groupView, m_searchWidget);

m_splitter->addWidget(m_groupView);
m_splitter->addWidget(rightHandSideWidget);
Expand Down Expand Up @@ -158,13 +150,9 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
connect(m_keepass1OpenWidget, SIGNAL(editFinished(bool)), SLOT(openDatabase(bool)));
connect(m_unlockDatabaseWidget, SIGNAL(editFinished(bool)), SLOT(unlockDatabase(bool)));
connect(this, SIGNAL(currentChanged(int)), this, SLOT(emitCurrentModeChanged()));
connect(m_searchUi->searchEdit, SIGNAL(textChanged(QString)), this, SLOT(startSearchTimer()));
connect(m_searchUi->caseSensitiveCheckBox, SIGNAL(toggled(bool)), this, SLOT(startSearch()));
connect(m_searchUi->searchCurrentRadioButton, SIGNAL(toggled(bool)), this, SLOT(startSearch()));
connect(m_searchUi->searchRootRadioButton, SIGNAL(toggled(bool)), this, SLOT(startSearch()));
connect(m_searchUi->searchEdit, SIGNAL(returnPressed()), m_entryView, SLOT(setFocus()));
connect(m_searchTimer, SIGNAL(timeout()), this, SLOT(search()));
connect(closeAction, SIGNAL(triggered()), this, SLOT(closeSearch()));

m_searchCaseSensitive = false;
m_searchCurrentGroup = false;

setCurrentWidget(m_mainWidget);
}
Expand Down Expand Up @@ -764,118 +752,82 @@ void DatabaseWidget::switchToImportKeepass1(const QString& fileName)
setCurrentWidget(m_keepass1OpenWidget);
}

void DatabaseWidget::openSearch()
void DatabaseWidget::search(const QString& searchtext)
{
if (isInSearchMode()) {
m_searchUi->searchEdit->selectAll();

if (!m_searchUi->searchEdit->hasFocus()) {
m_searchUi->searchEdit->setFocus();
// make sure the search action is checked again
emitCurrentModeChanged();
}
}
else {
showSearch();
if (searchtext.isEmpty())
{
endSearch();
return;
}
}

void DatabaseWidget::closeSearch()
{
Q_ASSERT(m_lastGroup);

Q_EMIT listModeAboutToActivate();

m_groupView->setCurrentGroup(m_lastGroup);
m_searchTimer->stop();

Q_EMIT listModeActivated();
}

void DatabaseWidget::showSearch()
{
Q_EMIT searchModeAboutToActivate();

m_searchUi->searchEdit->blockSignals(true);
m_searchUi->searchEdit->clear();
m_searchUi->searchEdit->blockSignals(false);
if (!isInSearchMode())
{
m_lastGroup = m_groupView->currentGroup();
Q_ASSERT(m_lastGroup);
}

m_searchUi->searchCurrentRadioButton->blockSignals(true);
m_searchUi->searchRootRadioButton->blockSignals(true);
m_searchUi->searchRootRadioButton->setChecked(true);
m_searchUi->searchCurrentRadioButton->blockSignals(false);
m_searchUi->searchRootRadioButton->blockSignals(false);
Group* searchGroup = m_searchCurrentGroup ? m_lastGroup : m_db->rootGroup();
Qt::CaseSensitivity sensitivity = m_searchCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive;

m_lastGroup = m_groupView->currentGroup();
QList<Entry*> searchResult = EntrySearcher().search(searchtext, searchGroup, sensitivity);

Q_ASSERT(m_lastGroup);
m_entryView->setEntryList(searchResult);
m_lastSearchText = searchtext;

if (m_lastGroup == m_db->rootGroup()) {
m_searchUi->optionsWidget->hide();
m_searchUi->searchCurrentRadioButton->hide();
m_searchUi->searchRootRadioButton->hide();
// Display a label detailing our search results
if (searchResult.size() > 0) {
m_searchingLabel->setText(tr("Search Results (%1)").arg(searchResult.size()));
}
else {
m_searchUi->optionsWidget->show();
m_searchUi->searchCurrentRadioButton->show();
m_searchUi->searchRootRadioButton->show();
m_searchUi->searchCurrentRadioButton->setText(tr("Current group")
.append(" (")
.append(m_lastGroup->name())
.append(")"));
m_searchingLabel->setText(tr("No Results"));
}
m_groupView->setCurrentIndex(QModelIndex());

m_searchWidget->show();
search();
m_searchUi->searchEdit->setFocus();
m_searchingLabel->setVisible(true);

Q_EMIT searchModeActivated();
}

void DatabaseWidget::search()
void DatabaseWidget::setSearchCaseSensitive(bool state)
{
Q_ASSERT(m_lastGroup);
m_searchCaseSensitive = state;

Group* searchGroup;
if (m_searchUi->searchCurrentRadioButton->isChecked()) {
searchGroup = m_lastGroup;
}
else if (m_searchUi->searchRootRadioButton->isChecked()) {
searchGroup = m_db->rootGroup();
}
else {
Q_ASSERT(false);
return;
}

Qt::CaseSensitivity sensitivity;
if (m_searchUi->caseSensitiveCheckBox->isChecked()) {
sensitivity = Qt::CaseSensitive;
}
else {
sensitivity = Qt::CaseInsensitive;
}
if (isInSearchMode())
search(m_lastSearchText);
}

QList<Entry*> searchResult = EntrySearcher().search(m_searchUi->searchEdit->text(), searchGroup, sensitivity);
void DatabaseWidget::setSearchCurrentGroup(bool state)
{
m_searchCurrentGroup = state;

m_entryView->setEntryList(searchResult);
if (isInSearchMode())
search(m_lastSearchText);
}

void DatabaseWidget::startSearchTimer()
QString DatabaseWidget::getCurrentSearch()
{
if (!m_searchTimer->isActive()) {
m_searchTimer->stop();
}
m_searchTimer->start(100);
return m_lastSearchText;
}

void DatabaseWidget::startSearch()
void DatabaseWidget::endSearch()
{
if (!m_searchTimer->isActive()) {
m_searchTimer->stop();
if (isInSearchMode())
{
Q_ASSERT(m_lastGroup);

Q_EMIT listModeAboutToActivate();

m_groupView->setCurrentGroup(m_lastGroup);
m_entryView->setGroup(m_lastGroup);

Q_EMIT listModeActivated();
}
search();

m_searchingLabel->setVisible(false);
m_searchingLabel->setText(tr("Searching..."));

m_lastSearchText.clear();
}

void DatabaseWidget::emitGroupContextMenuRequested(const QPoint& pos)
Expand Down Expand Up @@ -908,16 +860,12 @@ void DatabaseWidget::clearLastGroup(Group* group)
{
if (group) {
m_lastGroup = nullptr;
m_searchWidget->hide();
}
}

void DatabaseWidget::lock()
{
Q_ASSERT(currentMode() != DatabaseWidget::LockedMode);
if (isInSearchMode()) {
closeSearch();
}

if (m_groupView->currentGroup()) {
m_groupBeforeLock = m_groupView->currentGroup()->uuid();
Expand Down Expand Up @@ -1008,34 +956,3 @@ bool DatabaseWidget::currentEntryHasNotes()
}
return !currentEntry->notes().isEmpty();
}

bool DatabaseWidget::eventFilter(QObject* object, QEvent* event)
{
if (object == m_searchUi->searchEdit) {
if (event->type() == QEvent::KeyPress) {
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);

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.
Entry* currentEntry = m_entryView->currentEntry();
if (currentEntry && !m_searchUi->searchEdit->hasSelectedText()) {
setClipboardTextAndMinimize(currentEntry->password());
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.
if (!m_searchUi->searchEdit->hasSelectedText()
&& m_searchUi->searchEdit->cursorPosition() == m_searchUi->searchEdit->text().size()) {
m_entryView->setFocus();
return true;
}
}
}
}

return false;
}
Loading

0 comments on commit 13983d0

Please sign in to comment.