diff --git a/share/icons/application/16x16/actions/system-help.png b/share/icons/application/16x16/actions/system-help.png new file mode 100644 index 0000000000..75ebaf7f5f Binary files /dev/null and b/share/icons/application/16x16/actions/system-help.png differ diff --git a/share/icons/application/22x22/actions/system-help.png b/share/icons/application/22x22/actions/system-help.png new file mode 100644 index 0000000000..86b64075fb Binary files /dev/null and b/share/icons/application/22x22/actions/system-help.png differ diff --git a/share/icons/application/32x32/actions/system-help.png b/share/icons/application/32x32/actions/system-help.png new file mode 100644 index 0000000000..8a9eb1a827 Binary files /dev/null and b/share/icons/application/32x32/actions/system-help.png differ diff --git a/src/core/EntrySearcher.cpp b/src/core/EntrySearcher.cpp index 6614ab4637..f582d62e7c 100644 --- a/src/core/EntrySearcher.cpp +++ b/src/core/EntrySearcher.cpp @@ -96,6 +96,7 @@ bool EntrySearcher::searchEntryImpl(const QString& searchString, Entry* entry) found = !attachments.filter(term->regex).empty(); break; default: + // Terms without a specific field try to match title, username, url, and notes found = term->regex.match(entry->resolvePlaceholder(entry->title())).hasMatch() || term->regex.match(entry->resolvePlaceholder(entry->username())).hasMatch() || term->regex.match(entry->resolvePlaceholder(entry->url())).hasMatch() || @@ -139,7 +140,7 @@ QList > EntrySearcher::parseSearchTerm term->regex = Tools::convertToRegex(term->word, !mods.contains("*"), mods.contains("+"), m_caseSensitive); // Exclude modifier - term->exclude = mods.contains("-"); + term->exclude = mods.contains("-") || mods.contains("!"); // Determine the field to search QString field = result.captured(2); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index ac26dd8794..90ef007e73 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -129,6 +129,7 @@ MainWindow::MainWindow() // Setup the search widget in the toolbar SearchWidget* search = new SearchWidget(); search->connectSignals(m_actionMultiplexer); + connect(this, SIGNAL(windowMoved()), search, SLOT(windowMoved())); m_searchWidgetAction = m_ui->toolBar->addWidget(search); m_searchWidgetAction->setEnabled(false); @@ -775,6 +776,12 @@ void MainWindow::changeEvent(QEvent* event) } } +void MainWindow::moveEvent(QMoveEvent* event) +{ + emit windowMoved(); + QMainWindow::moveEvent(event); +} + void MainWindow::saveWindowInformation() { if (isVisible()) { diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index 174e47564f..43ab2956ae 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -55,6 +55,9 @@ class MainWindow : public QMainWindow PasswordGeneratorScreen = 3 }; +signals: + void windowMoved(); + public slots: void openDatabase(const QString& fileName, const QString& pw = QString(), const QString& keyFile = QString()); void appExit(); @@ -78,6 +81,7 @@ public slots: protected: void closeEvent(QCloseEvent* event) override; void changeEvent(QEvent* event) override; + void moveEvent(QMoveEvent* event) override; private slots: void setMenuActionState(DatabaseWidget::Mode mode = DatabaseWidget::None); diff --git a/src/gui/SearchHelpWidget.ui b/src/gui/SearchHelpWidget.ui new file mode 100644 index 0000000000..701531c444 --- /dev/null +++ b/src/gui/SearchHelpWidget.ui @@ -0,0 +1,392 @@ + + + SearchHelpWidget + + + + 0 + 0 + 365 + 253 + + + + Search Help + + + #SearchHelpWidget { border-width: 1px; border-style: ridge } + + + + + + + 0 + 0 + + + + + 75 + true + + + + Search terms can be in the form: [modifiers][field:]["]term["] + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Every search term must match (ie, they are logically AND'd) + + + + + + + + + + 50 + false + + + + Modifiers + + + + + + + 10 + 0 + + + + + 75 + true + + + + - + + + false + + + Qt::AlignCenter + + + + + + + exclude term from results + + + + + + + match term exactly + + + + + + + + 10 + 0 + + + + + 75 + true + + + + * + + + Qt::AlignCenter + + + + + + + use regex in term + + + + + + + + 10 + 0 + + + + + 75 + true + + + + + + + + Qt::AlignCenter + + + + + + + + + + + 50 + false + + + + Fields + + + false + + + + 12 + + + + + username + + + + + + + password + + + + + + + title + + + + + + + url + + + + + + + notes + + + + + + + attribute + + + + + + + attachment + + + + + + + + + + + + + + Term Wildcards + + + + + + + 10 + 0 + + + + + 75 + true + + + + * + + + false + + + Qt::AlignCenter + + + + + + + + 10 + 0 + + + + + 75 + true + + + + ? + + + Qt::AlignCenter + + + + + + + + 10 + 0 + + + + + 75 + true + + + + | + + + Qt::AlignCenter + + + + + + + match anything + + + + + + + match one + + + + + + + logical OR + + + + + + + + + + Examples + + + + + + + 0 + 0 + + + + user:name1 url:google + + + + + + + + 0 + 0 + + + + user:"name1|name2" + + + + + + + + 0 + 0 + + + + +user:name1 *notes:"secret \d" + + + + + + + + + + + + + diff --git a/src/gui/SearchWidget.cpp b/src/gui/SearchWidget.cpp index eed3c6db35..f272affec9 100644 --- a/src/gui/SearchWidget.cpp +++ b/src/gui/SearchWidget.cpp @@ -18,6 +18,7 @@ #include "SearchWidget.h" #include "ui_SearchWidget.h" +#include "ui_SearchHelpWidget.h" #include #include @@ -30,14 +31,21 @@ SearchWidget::SearchWidget(QWidget* parent) : QWidget(parent) , m_ui(new Ui::SearchWidget()) + , m_helpUi(new Ui::SearchHelpWidget()) + , m_helpWidget(new QWidget(parent)) { m_ui->setupUi(this); + m_helpUi->setupUi(m_helpWidget); + m_helpWidget->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool); + m_helpWidget->hide(); + m_searchTimer = new QTimer(this); m_searchTimer->setSingleShot(true); connect(m_ui->searchEdit, SIGNAL(textChanged(QString)), SLOT(startSearchTimer())); connect(m_ui->clearIcon, SIGNAL(triggered(bool)), m_ui->searchEdit, SLOT(clear())); + connect(m_ui->helpIcon, SIGNAL(triggered()), SLOT(toggleHelp())); connect(m_searchTimer, SIGNAL(timeout()), this, SLOT(startSearch())); connect(this, SIGNAL(escapePressed()), m_ui->searchEdit, SLOT(clear())); @@ -60,6 +68,9 @@ SearchWidget::SearchWidget(QWidget* parent) m_ui->searchIcon->setMenu(searchMenu); m_ui->searchEdit->addAction(m_ui->searchIcon, QLineEdit::LeadingPosition); + m_ui->helpIcon->setIcon(filePath()->icon("actions", "system-help")); + m_ui->searchEdit->addAction(m_ui->helpIcon, QLineEdit::TrailingPosition); + m_ui->clearIcon->setIcon(filePath()->icon("actions", "edit-clear-locationbar-rtl")); m_ui->clearIcon->setVisible(false); m_ui->searchEdit->addAction(m_ui->clearIcon, QLineEdit::TrailingPosition); @@ -81,13 +92,6 @@ bool SearchWidget::eventFilter(QObject* obj, QEvent* 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 (m_ui->searchEdit->cursorPosition() == m_ui->searchEdit->text().length()) { // If down is pressed at EOL, move the focus to the entry view @@ -99,9 +103,24 @@ bool SearchWidget::eventFilter(QObject* obj, QEvent* event) return true; } } + } else if (event->type() == QEvent::FocusOut) { + m_helpWidget->hide(); } - return QObject::eventFilter(obj, event); + return QWidget::eventFilter(obj, event); +} + +void SearchWidget::resizeEvent(QResizeEvent* event) +{ + windowMoved(); + QWidget::resizeEvent(event); +} + +void SearchWidget::windowMoved() +{ + if (m_helpWidget->isVisible()) { + moveHelpPopup(); + } } void SearchWidget::connectSignals(SignalMultiplexer& mx) @@ -178,3 +197,19 @@ void SearchWidget::searchFocus() m_ui->searchEdit->setFocus(); m_ui->searchEdit->selectAll(); } + +void SearchWidget::toggleHelp() +{ + if (m_helpWidget->isVisible()) { + m_helpWidget->hide(); + } else { + moveHelpPopup(); + m_helpWidget->show(); + } +} + +void SearchWidget::moveHelpPopup() +{ + auto pos = mapToGlobal(m_ui->searchEdit->geometry().bottomLeft()); + m_helpWidget->move(pos); +} diff --git a/src/gui/SearchWidget.h b/src/gui/SearchWidget.h index 39e17bcf49..e0ef81424e 100644 --- a/src/gui/SearchWidget.h +++ b/src/gui/SearchWidget.h @@ -28,6 +28,7 @@ namespace Ui { class SearchWidget; + class SearchHelpWidget; } class SearchWidget : public QWidget @@ -45,7 +46,9 @@ class SearchWidget : public QWidget void setLimitGroup(bool state); protected: + // Filter key presses in the search field bool eventFilter(QObject* obj, QEvent* event) override; + void resizeEvent(QResizeEvent* event) override; signals: void search(const QString& text); @@ -58,6 +61,7 @@ class SearchWidget : public QWidget public slots: void databaseChanged(DatabaseWidget* dbWidget = nullptr); + void windowMoved(); private slots: void startSearchTimer(); @@ -65,9 +69,14 @@ private slots: void updateCaseSensitive(); void updateLimitGroup(); void searchFocus(); + void toggleHelp(); private: + void moveHelpPopup(); + const QScopedPointer m_ui; + const QScopedPointer m_helpUi; + QWidget* m_helpWidget; QTimer* m_searchTimer; QAction* m_actionCaseSensitive; QAction* m_actionLimitGroup; diff --git a/src/gui/SearchWidget.ui b/src/gui/SearchWidget.ui index 5af2a0f32b..fdd8a806a0 100644 --- a/src/gui/SearchWidget.ui +++ b/src/gui/SearchWidget.ui @@ -81,6 +81,11 @@ Clear + + + Search Help + + searchEdit