mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-12-24 14:59:44 -05:00
Make search always visible (PR #67)
* 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
This commit is contained in:
parent
3f80134f07
commit
13983d0e51
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -16,7 +16,6 @@
|
||||
*/
|
||||
|
||||
#include "DatabaseWidget.h"
|
||||
#include "ui_SearchWidget.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QDesktopServices>
|
||||
@ -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"
|
||||
@ -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");
|
||||
@ -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);
|
||||
@ -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);
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
if (searchtext.isEmpty())
|
||||
{
|
||||
endSearch();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
showSearch();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
QList<Entry*> searchResult = EntrySearcher().search(m_searchUi->searchEdit->text(), searchGroup, sensitivity);
|
||||
|
||||
m_entryView->setEntryList(searchResult);
|
||||
if (isInSearchMode())
|
||||
search(m_lastSearchText);
|
||||
}
|
||||
|
||||
void DatabaseWidget::startSearchTimer()
|
||||
void DatabaseWidget::setSearchCurrentGroup(bool state)
|
||||
{
|
||||
if (!m_searchTimer->isActive()) {
|
||||
m_searchTimer->stop();
|
||||
}
|
||||
m_searchTimer->start(100);
|
||||
m_searchCurrentGroup = state;
|
||||
|
||||
if (isInSearchMode())
|
||||
search(m_lastSearchText);
|
||||
}
|
||||
|
||||
void DatabaseWidget::startSearch()
|
||||
QString DatabaseWidget::getCurrentSearch()
|
||||
{
|
||||
if (!m_searchTimer->isActive()) {
|
||||
m_searchTimer->stop();
|
||||
return m_lastSearchText;
|
||||
}
|
||||
|
||||
void DatabaseWidget::endSearch()
|
||||
{
|
||||
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)
|
||||
@ -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();
|
||||
@ -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;
|
||||
}
|
||||
|
@ -39,12 +39,9 @@ class KeePass1OpenWidget;
|
||||
class QFile;
|
||||
class QMenu;
|
||||
class QSplitter;
|
||||
class QLabel;
|
||||
class UnlockDatabaseWidget;
|
||||
|
||||
namespace Ui {
|
||||
class SearchWidget;
|
||||
}
|
||||
|
||||
class DatabaseWidget : public QStackedWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -64,6 +61,7 @@ public:
|
||||
bool dbHasKey() const;
|
||||
bool canDeleteCurrentGroup() const;
|
||||
bool isInSearchMode() const;
|
||||
QString getCurrentSearch();
|
||||
int addWidget(QWidget* w);
|
||||
void setCurrentIndex(int index);
|
||||
void setCurrentWidget(QWidget* widget);
|
||||
@ -101,9 +99,7 @@ Q_SIGNALS:
|
||||
void searchModeActivated();
|
||||
void splitterSizesChanged();
|
||||
void entryColumnSizesChanged();
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject* object, QEvent* event) override;
|
||||
void updateSearch(QString text);
|
||||
|
||||
public Q_SLOTS:
|
||||
void createEntry();
|
||||
@ -127,7 +123,11 @@ public Q_SLOTS:
|
||||
void switchToOpenDatabase(const QString& fileName);
|
||||
void switchToOpenDatabase(const QString& fileName, const QString& password, const QString& keyFile);
|
||||
void switchToImportKeepass1(const QString& fileName);
|
||||
void openSearch();
|
||||
// Search related slots
|
||||
void search(const QString& searchtext);
|
||||
void setSearchCaseSensitive(bool state);
|
||||
void setSearchCurrentGroup(bool state);
|
||||
void endSearch();
|
||||
|
||||
private Q_SLOTS:
|
||||
void entryActivationSignalReceived(Entry* entry, EntryModel::ModelColumn column);
|
||||
@ -144,11 +144,6 @@ private Q_SLOTS:
|
||||
void unlockDatabase(bool accepted);
|
||||
void emitCurrentModeChanged();
|
||||
void clearLastGroup(Group* group);
|
||||
void search();
|
||||
void startSearch();
|
||||
void startSearchTimer();
|
||||
void showSearch();
|
||||
void closeSearch();
|
||||
|
||||
private:
|
||||
void setClipboardTextAndMinimize(const QString& text);
|
||||
@ -156,8 +151,6 @@ private:
|
||||
void replaceDatabase(Database* db);
|
||||
|
||||
Database* m_db;
|
||||
const QScopedPointer<Ui::SearchWidget> m_searchUi;
|
||||
QWidget* const m_searchWidget;
|
||||
QWidget* m_mainWidget;
|
||||
EditEntryWidget* m_editEntryWidget;
|
||||
EditEntryWidget* m_historyEditEntryWidget;
|
||||
@ -170,13 +163,18 @@ private:
|
||||
QSplitter* m_splitter;
|
||||
GroupView* m_groupView;
|
||||
EntryView* m_entryView;
|
||||
QLabel* m_searchingLabel;
|
||||
Group* m_newGroup;
|
||||
Entry* m_newEntry;
|
||||
Group* m_newParent;
|
||||
Group* m_lastGroup;
|
||||
QTimer* m_searchTimer;
|
||||
QString m_filename;
|
||||
Uuid m_groupBeforeLock;
|
||||
|
||||
// Search state
|
||||
QString m_lastSearchText;
|
||||
bool m_searchCaseSensitive;
|
||||
bool m_searchCurrentGroup;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_DATABASEWIDGET_H
|
||||
|
@ -48,16 +48,13 @@ void DatabaseWidgetStateSync::setActive(DatabaseWidget* dbWidget)
|
||||
if (m_activeDbWidget) {
|
||||
m_blockUpdates = true;
|
||||
|
||||
if (!m_splitterSizes.isEmpty()) {
|
||||
if (!m_splitterSizes.isEmpty())
|
||||
m_activeDbWidget->setSplitterSizes(m_splitterSizes);
|
||||
}
|
||||
|
||||
if (m_activeDbWidget->isGroupSelected()) {
|
||||
restoreListView();
|
||||
}
|
||||
else {
|
||||
if (m_activeDbWidget->isInSearchMode())
|
||||
restoreSearchView();
|
||||
}
|
||||
else
|
||||
restoreListView();
|
||||
|
||||
m_blockUpdates = false;
|
||||
|
||||
|
@ -33,13 +33,15 @@
|
||||
#include "gui/DatabaseRepairWidget.h"
|
||||
#include "gui/FileDialog.h"
|
||||
#include "gui/MessageBox.h"
|
||||
#include "gui/SearchWidget.h"
|
||||
|
||||
#include "http/Service.h"
|
||||
#include "http/HttpSettings.h"
|
||||
#include "http/OptionDialog.h"
|
||||
#include "gui/SettingsWidget.h"
|
||||
|
||||
class HttpPlugin: public ISettingsPage {
|
||||
class HttpPlugin: public ISettingsPage
|
||||
{
|
||||
public:
|
||||
HttpPlugin(DatabaseTabWidget * tabWidget) {
|
||||
m_service = new Service(tabWidget);
|
||||
@ -68,7 +70,7 @@ class HttpPlugin: public ISettingsPage {
|
||||
}
|
||||
private:
|
||||
Service *m_service;
|
||||
};
|
||||
};
|
||||
|
||||
const QString MainWindow::BaseWindowTitle = "KeePassX";
|
||||
|
||||
@ -80,6 +82,12 @@ MainWindow::MainWindow()
|
||||
|
||||
m_ui->setupUi(this);
|
||||
|
||||
// Setup the search widget in the toolbar
|
||||
SearchWidget *search = new SearchWidget();
|
||||
search->connectSignals(m_actionMultiplexer);
|
||||
m_searchWidgetAction = m_ui->toolBar->addWidget(search);
|
||||
m_searchWidgetAction->setEnabled(false);
|
||||
|
||||
m_countDefaultAttributes = m_ui->menuEntryCopyAttribute->actions().size();
|
||||
|
||||
restoreGeometry(config()->get("GUI/MainWindowGeometry").toByteArray());
|
||||
@ -125,7 +133,6 @@ MainWindow::MainWindow()
|
||||
setShortcut(m_ui->actionDatabaseClose, QKeySequence::Close, Qt::CTRL + Qt::Key_W);
|
||||
m_ui->actionLockDatabases->setShortcut(Qt::CTRL + Qt::Key_L);
|
||||
setShortcut(m_ui->actionQuit, QKeySequence::Quit, Qt::CTRL + Qt::Key_Q);
|
||||
setShortcut(m_ui->actionSearch, QKeySequence::Find, Qt::CTRL + Qt::Key_F);
|
||||
m_ui->actionEntryNew->setShortcut(Qt::CTRL + Qt::Key_N);
|
||||
m_ui->actionEntryEdit->setShortcut(Qt::CTRL + Qt::Key_E);
|
||||
m_ui->actionEntryDelete->setShortcut(Qt::CTRL + Qt::Key_D);
|
||||
@ -164,8 +171,6 @@ MainWindow::MainWindow()
|
||||
|
||||
m_ui->actionAbout->setIcon(filePath()->icon("actions", "help-about"));
|
||||
|
||||
m_ui->actionSearch->setIcon(filePath()->icon("actions", "system-search"));
|
||||
|
||||
m_actionMultiplexer.connect(SIGNAL(currentModeChanged(DatabaseWidget::Mode)),
|
||||
this, SLOT(setMenuActionState(DatabaseWidget::Mode)));
|
||||
m_actionMultiplexer.connect(SIGNAL(groupChanged()),
|
||||
@ -177,6 +182,10 @@ MainWindow::MainWindow()
|
||||
m_actionMultiplexer.connect(SIGNAL(entryContextMenuRequested(QPoint)),
|
||||
this, SLOT(showEntryContextMenu(QPoint)));
|
||||
|
||||
// Notify search when the active database changes
|
||||
connect(m_ui->tabWidget, SIGNAL(activateDatabaseChanged(DatabaseWidget*)),
|
||||
search, SLOT(databaseChanged(DatabaseWidget*)));
|
||||
|
||||
connect(m_ui->tabWidget, SIGNAL(tabNameChanged()),
|
||||
SLOT(updateWindowTitle()));
|
||||
connect(m_ui->tabWidget, SIGNAL(currentChanged(int)),
|
||||
@ -253,9 +262,6 @@ MainWindow::MainWindow()
|
||||
|
||||
connect(m_ui->actionAbout, SIGNAL(triggered()), SLOT(showAboutDialog()));
|
||||
|
||||
m_actionMultiplexer.connect(m_ui->actionSearch, SIGNAL(triggered()),
|
||||
SLOT(openSearch()));
|
||||
|
||||
updateTrayIcon();
|
||||
}
|
||||
|
||||
@ -367,13 +373,13 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
|
||||
m_ui->actionGroupNew->setEnabled(groupSelected);
|
||||
m_ui->actionGroupEdit->setEnabled(groupSelected);
|
||||
m_ui->actionGroupDelete->setEnabled(groupSelected && dbWidget->canDeleteCurrentGroup());
|
||||
// TODO: get checked state from db widget
|
||||
m_ui->actionSearch->setEnabled(true);
|
||||
m_ui->actionChangeMasterKey->setEnabled(true);
|
||||
m_ui->actionChangeDatabaseSettings->setEnabled(true);
|
||||
m_ui->actionDatabaseSave->setEnabled(true);
|
||||
m_ui->actionDatabaseSaveAs->setEnabled(true);
|
||||
m_ui->actionExportCsv->setEnabled(true);
|
||||
|
||||
m_searchWidgetAction->setEnabled(true);
|
||||
break;
|
||||
}
|
||||
case DatabaseWidget::EditMode:
|
||||
@ -394,12 +400,13 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
|
||||
m_ui->actionEntryCopyNotes->setEnabled(false);
|
||||
m_ui->menuEntryCopyAttribute->setEnabled(false);
|
||||
|
||||
m_ui->actionSearch->setEnabled(false);
|
||||
m_ui->actionChangeMasterKey->setEnabled(false);
|
||||
m_ui->actionChangeDatabaseSettings->setEnabled(false);
|
||||
m_ui->actionDatabaseSave->setEnabled(false);
|
||||
m_ui->actionDatabaseSaveAs->setEnabled(false);
|
||||
m_ui->actionExportCsv->setEnabled(false);
|
||||
|
||||
m_searchWidgetAction->setEnabled(false);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -424,14 +431,14 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
|
||||
m_ui->actionEntryCopyNotes->setEnabled(false);
|
||||
m_ui->menuEntryCopyAttribute->setEnabled(false);
|
||||
|
||||
m_ui->actionSearch->setEnabled(false);
|
||||
m_ui->actionChangeMasterKey->setEnabled(false);
|
||||
m_ui->actionChangeDatabaseSettings->setEnabled(false);
|
||||
m_ui->actionDatabaseSave->setEnabled(false);
|
||||
m_ui->actionDatabaseSaveAs->setEnabled(false);
|
||||
|
||||
m_ui->actionDatabaseClose->setEnabled(false);
|
||||
m_ui->actionExportCsv->setEnabled(false);
|
||||
|
||||
m_searchWidgetAction->setEnabled(false);
|
||||
}
|
||||
|
||||
bool inDatabaseTabWidgetOrWelcomeWidget = inDatabaseTabWidget || inWelcomeWidget;
|
||||
|
@ -84,6 +84,7 @@ private:
|
||||
const QScopedPointer<Ui::MainWindow> m_ui;
|
||||
SignalMultiplexer m_actionMultiplexer;
|
||||
QAction* m_clearHistoryAction;
|
||||
QAction* m_searchWidgetAction;
|
||||
QActionGroup* m_lastDatabasesActions;
|
||||
QActionGroup* m_copyAdditionalAttributeActions;
|
||||
QStringList m_openDatabases;
|
||||
|
@ -97,7 +97,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>20</height>
|
||||
<height>26</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuFile">
|
||||
@ -106,7 +106,7 @@
|
||||
</property>
|
||||
<widget class="QMenu" name="menuRecentDatabases">
|
||||
<property name="title">
|
||||
<string>Recent databases</string>
|
||||
<string>&Recent databases</string>
|
||||
</property>
|
||||
</widget>
|
||||
<addaction name="actionDatabaseNew"/>
|
||||
@ -127,20 +127,20 @@
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuHelp">
|
||||
<property name="title">
|
||||
<string>Help</string>
|
||||
<string>He&lp</string>
|
||||
</property>
|
||||
<addaction name="actionAbout"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuEntries">
|
||||
<property name="title">
|
||||
<string>Entries</string>
|
||||
<string>E&ntries</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuEntryCopyAttribute">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Copy attribute to clipboard</string>
|
||||
<string>Copy att&ribute to clipboard</string>
|
||||
</property>
|
||||
<addaction name="actionEntryCopyTitle"/>
|
||||
<addaction name="actionEntryCopyURL"/>
|
||||
@ -156,11 +156,10 @@
|
||||
<addaction name="menuEntryCopyAttribute"/>
|
||||
<addaction name="actionEntryAutoType"/>
|
||||
<addaction name="actionEntryOpenUrl"/>
|
||||
<addaction name="actionSearch"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuGroups">
|
||||
<property name="title">
|
||||
<string>Groups</string>
|
||||
<string>&Groups</string>
|
||||
</property>
|
||||
<addaction name="actionGroupNew"/>
|
||||
<addaction name="actionGroupEdit"/>
|
||||
@ -175,7 +174,7 @@
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuView">
|
||||
<property name="title">
|
||||
<string>View</string>
|
||||
<string>&View</string>
|
||||
</property>
|
||||
</widget>
|
||||
<addaction name="menuFile"/>
|
||||
@ -206,21 +205,21 @@
|
||||
<addaction name="actionEntryCopyPassword"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionLockDatabases"/>
|
||||
<addaction name="actionSearch"/>
|
||||
<addaction name="separator"/>
|
||||
</widget>
|
||||
<action name="actionQuit">
|
||||
<property name="text">
|
||||
<string>Quit</string>
|
||||
<string>&Quit</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAbout">
|
||||
<property name="text">
|
||||
<string>About</string>
|
||||
<string>&About</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDatabaseOpen">
|
||||
<property name="text">
|
||||
<string>Open database</string>
|
||||
<string>&Open database</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDatabaseSave">
|
||||
@ -228,7 +227,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save database</string>
|
||||
<string>&Save database</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDatabaseClose">
|
||||
@ -236,12 +235,12 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Close database</string>
|
||||
<string>&Close database</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDatabaseNew">
|
||||
<property name="text">
|
||||
<string>New database</string>
|
||||
<string>&New database</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryNew">
|
||||
@ -249,7 +248,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add new entry</string>
|
||||
<string>&Add new entry</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryEdit">
|
||||
@ -257,7 +256,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>View/Edit entry</string>
|
||||
<string>&View/Edit entry</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryDelete">
|
||||
@ -265,7 +264,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete entry</string>
|
||||
<string>&Delete entry</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGroupNew">
|
||||
@ -273,7 +272,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add new group</string>
|
||||
<string>&Add new group</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGroupEdit">
|
||||
@ -281,7 +280,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Edit group</string>
|
||||
<string>&Edit group</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGroupDelete">
|
||||
@ -289,7 +288,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete group</string>
|
||||
<string>&Delete group</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDatabaseSaveAs">
|
||||
@ -297,7 +296,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save database as</string>
|
||||
<string>Sa&ve database as</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionChangeMasterKey">
|
||||
@ -305,7 +304,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Change master key</string>
|
||||
<string>Change &master key</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionChangeDatabaseSettings">
|
||||
@ -313,7 +312,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Database settings</string>
|
||||
<string>&Database settings</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Database settings</string>
|
||||
@ -321,7 +320,7 @@
|
||||
</action>
|
||||
<action name="actionImportKeePass1">
|
||||
<property name="text">
|
||||
<string>Import KeePass 1 database</string>
|
||||
<string>&Import KeePass 1 database</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryClone">
|
||||
@ -329,15 +328,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Clone entry</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSearch">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Find</string>
|
||||
<string>&Clone entry</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryCopyUsername">
|
||||
@ -345,7 +336,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Copy username</string>
|
||||
<string>Copy &username</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Copy username to clipboard</string>
|
||||
@ -356,7 +347,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Copy password</string>
|
||||
<string>Cop&y password</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Copy password to clipboard</string>
|
||||
@ -364,7 +355,7 @@
|
||||
</action>
|
||||
<action name="actionSettings">
|
||||
<property name="text">
|
||||
<string>Settings</string>
|
||||
<string>&Settings</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryAutoType">
|
||||
@ -372,7 +363,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Perform Auto-Type</string>
|
||||
<string>&Perform Auto-Type</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryOpenUrl">
|
||||
@ -380,7 +371,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Open URL</string>
|
||||
<string>&Open URL</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionLockDatabases">
|
||||
@ -388,7 +379,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Lock databases</string>
|
||||
<string>&Lock databases</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryCopyTitle">
|
||||
@ -396,7 +387,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Title</string>
|
||||
<string>&Title</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryCopyURL">
|
||||
@ -404,7 +395,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>URL</string>
|
||||
<string>&URL</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryCopyNotes">
|
||||
@ -412,7 +403,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Notes</string>
|
||||
<string>&Notes</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExportCsv">
|
||||
@ -420,12 +411,12 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Export to CSV file</string>
|
||||
<string>&Export to CSV file</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRepairDatabase">
|
||||
<property name="text">
|
||||
<string>Repair database</string>
|
||||
<string>Re&pair database</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
|
124
src/gui/SearchWidget.cpp
Normal file
124
src/gui/SearchWidget.cpp
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jonathan White <support@dmapps.us>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SearchWidget.h"
|
||||
#include "ui_SearchWidget.h"
|
||||
|
||||
#include <QKeyEvent>
|
||||
#include <QMenu>
|
||||
#include <QShortcut>
|
||||
|
||||
#include "core/FilePath.h"
|
||||
|
||||
bool SearchEventFilter::eventFilter(QObject *obj, QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::KeyPress) {
|
||||
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(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())
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
m_searchTimer = new QTimer(this);
|
||||
m_searchTimer->setSingleShot(true);
|
||||
|
||||
connect(m_ui->searchEdit, SIGNAL(textChanged(QString)), SLOT(startSearchTimer()));
|
||||
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()));
|
||||
|
||||
new QShortcut(Qt::CTRL + Qt::Key_F, m_ui->searchEdit, SLOT(setFocus()), nullptr, Qt::ApplicationShortcut);
|
||||
|
||||
m_ui->searchEdit->installEventFilter(&m_searchEventFilter);
|
||||
|
||||
QMenu *searchMenu = new QMenu();
|
||||
m_actionCaseSensitive = searchMenu->addAction(tr("Case Sensitive"), this, SLOT(updateCaseSensitive()));
|
||||
m_actionCaseSensitive->setCheckable(true);
|
||||
|
||||
m_actionGroupSearch = searchMenu->addAction(tr("Search Current Group"), this, SLOT(updateGroupSearch()));
|
||||
m_actionGroupSearch->setCheckable(true);
|
||||
|
||||
m_ui->searchIcon->setIcon(filePath()->icon("actions", "system-search"));
|
||||
m_ui->searchIcon->setMenu(searchMenu);
|
||||
m_ui->searchIcon->setPopupMode(QToolButton::MenuButtonPopup);
|
||||
}
|
||||
|
||||
SearchWidget::~SearchWidget()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void SearchWidget::connectSignals(SignalMultiplexer& mx)
|
||||
{
|
||||
mx.connect(this, SIGNAL(search(QString)), SLOT(search(QString)));
|
||||
mx.connect(this, SIGNAL(setCaseSensitive(bool)), SLOT(setSearchCaseSensitive(bool)));
|
||||
mx.connect(this, SIGNAL(setGroupSearch(bool)), SLOT(setSearchCurrentGroup(bool)));
|
||||
mx.connect(SIGNAL(groupChanged()), m_ui->searchEdit, SLOT(clear()));
|
||||
}
|
||||
|
||||
void SearchWidget::databaseChanged(DatabaseWidget *dbWidget)
|
||||
{
|
||||
if (dbWidget != nullptr) {
|
||||
// Set current search text from this database
|
||||
m_ui->searchEdit->setText(dbWidget->getCurrentSearch());
|
||||
|
||||
// Enforce search policy
|
||||
emit setCaseSensitive(m_actionCaseSensitive->isChecked());
|
||||
emit setGroupSearch(m_actionGroupSearch->isChecked());
|
||||
} else {
|
||||
m_ui->searchEdit->clear();
|
||||
}
|
||||
}
|
||||
|
||||
void SearchWidget::startSearchTimer()
|
||||
{
|
||||
if (!m_searchTimer->isActive()) {
|
||||
m_searchTimer->stop();
|
||||
}
|
||||
m_searchTimer->start(100);
|
||||
}
|
||||
|
||||
void SearchWidget::startSearch()
|
||||
{
|
||||
if (!m_searchTimer->isActive()) {
|
||||
m_searchTimer->stop();
|
||||
}
|
||||
|
||||
search(m_ui->searchEdit->text());
|
||||
}
|
||||
|
||||
void SearchWidget::updateCaseSensitive()
|
||||
{
|
||||
emit setCaseSensitive(m_actionCaseSensitive->isChecked());
|
||||
}
|
||||
|
||||
void SearchWidget::updateGroupSearch()
|
||||
{
|
||||
emit setGroupSearch(m_actionGroupSearch->isChecked());
|
||||
}
|
77
src/gui/SearchWidget.h
Normal file
77
src/gui/SearchWidget.h
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jonathan White <support@dmapps.us>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSX_SEARCHWIDGET_H
|
||||
#define KEEPASSX_SEARCHWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QTimer>
|
||||
|
||||
#include "gui/DatabaseWidget.h"
|
||||
#include "core/SignalMultiplexer.h"
|
||||
|
||||
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
|
||||
|
||||
public:
|
||||
explicit SearchWidget(QWidget *parent = 0);
|
||||
~SearchWidget();
|
||||
|
||||
void connectSignals(SignalMultiplexer& mx);
|
||||
|
||||
signals:
|
||||
void search(const QString &text);
|
||||
void setCaseSensitive(bool state);
|
||||
void setGroupSearch(bool state);
|
||||
|
||||
public slots:
|
||||
void databaseChanged(DatabaseWidget* dbWidget);
|
||||
|
||||
private slots:
|
||||
void startSearchTimer();
|
||||
void startSearch();
|
||||
void updateCaseSensitive();
|
||||
void updateGroupSearch();
|
||||
|
||||
private:
|
||||
const QScopedPointer<Ui::SearchWidget> m_ui;
|
||||
QTimer* m_searchTimer;
|
||||
SearchEventFilter m_searchEventFilter;
|
||||
|
||||
QAction *m_actionCaseSensitive;
|
||||
QAction *m_actionGroupSearch;
|
||||
|
||||
Q_DISABLE_COPY(SearchWidget)
|
||||
};
|
||||
|
||||
#endif // SEARCHWIDGET_H
|
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>630</width>
|
||||
<height>87</height>
|
||||
<height>34</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
@ -23,16 +23,16 @@
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="1">
|
||||
<widget class="LineEdit" name="searchEdit"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QToolButton" name="closeSearchButton">
|
||||
<widget class="QToolButton" name="searchIcon">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::ClickFocus</enum>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonIconOnly</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@ -47,79 +47,14 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QWidget" name="optionsWidget" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="caseSensitiveCheckBox">
|
||||
<property name="text">
|
||||
<string>Case sensitive</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="searchCurrentRadioButton">
|
||||
<property name="text">
|
||||
<string>Current group</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="searchRootRadioButton">
|
||||
<property name="text">
|
||||
<string>Root group</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>255</width>
|
||||
<height>1</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="searchEdit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>LineEdit</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>gui/LineEdit.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>closeSearchButton</tabstop>
|
||||
<tabstop>searchIcon</tabstop>
|
||||
<tabstop>searchEdit</tabstop>
|
||||
<tabstop>caseSensitiveCheckBox</tabstop>
|
||||
<tabstop>searchCurrentRadioButton</tabstop>
|
||||
<tabstop>searchRootRadioButton</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
@ -136,7 +136,10 @@ void GroupView::syncExpandedState(const QModelIndex& parent, int start, int end)
|
||||
|
||||
void GroupView::setCurrentGroup(Group* group)
|
||||
{
|
||||
setCurrentIndex(m_model->index(group));
|
||||
if (group == nullptr)
|
||||
setCurrentIndex(QModelIndex());
|
||||
else
|
||||
setCurrentIndex(m_model->index(group));
|
||||
}
|
||||
|
||||
void GroupView::modelReset()
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <QTest>
|
||||
#include <QToolBar>
|
||||
#include <QToolButton>
|
||||
#include <QTimer>
|
||||
|
||||
#include "config-keepassx-tests.h"
|
||||
#include "core/Config.h"
|
||||
@ -59,20 +60,25 @@ void TestGui::initTestCase()
|
||||
m_mainWindow->activateWindow();
|
||||
Tools::wait(50);
|
||||
|
||||
// Load the NewDatabase.kdbx file into temporary storage
|
||||
QByteArray tmpData;
|
||||
QFile sourceDbFile(QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.kdbx"));
|
||||
QVERIFY(sourceDbFile.open(QIODevice::ReadOnly));
|
||||
QVERIFY(Tools::readAllFromDevice(&sourceDbFile, tmpData));
|
||||
sourceDbFile.close();
|
||||
|
||||
QVERIFY(m_orgDbFile.open());
|
||||
m_orgDbFileName = QFileInfo(m_orgDbFile.fileName()).fileName();
|
||||
QCOMPARE(m_orgDbFile.write(tmpData), static_cast<qint64>((tmpData.size())));
|
||||
m_orgDbFile.close();
|
||||
// Write the temp storage to a temp database file for use in our tests
|
||||
QVERIFY(m_dbFile.open());
|
||||
QCOMPARE(m_dbFile.write(tmpData), static_cast<qint64>((tmpData.size())));
|
||||
m_dbFile.close();
|
||||
|
||||
m_dbFileName = QFileInfo(m_dbFile).fileName();
|
||||
}
|
||||
|
||||
void TestGui::testOpenDatabase()
|
||||
// Every test starts with opening the temp database
|
||||
void TestGui::init()
|
||||
{
|
||||
fileDialog()->setNextFileName(m_orgDbFile.fileName());
|
||||
fileDialog()->setNextFileName(m_dbFile.fileName());
|
||||
triggerAction("actionDatabaseOpen");
|
||||
|
||||
QWidget* databaseOpenWidget = m_mainWindow->findChild<QWidget*>("databaseOpenWidget");
|
||||
@ -81,60 +87,91 @@ void TestGui::testOpenDatabase()
|
||||
|
||||
QTest::keyClicks(editPassword, "a");
|
||||
QTest::keyClick(editPassword, Qt::Key_Enter);
|
||||
}
|
||||
Tools::wait(100);
|
||||
|
||||
void TestGui::testTabs()
|
||||
{
|
||||
QCOMPARE(m_tabWidget->count(), 1);
|
||||
QCOMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), m_orgDbFileName);
|
||||
QVERIFY(m_tabWidget->currentDatabaseWidget());
|
||||
|
||||
m_dbWidget = m_tabWidget->currentDatabaseWidget();
|
||||
m_db = m_dbWidget->database();
|
||||
}
|
||||
|
||||
// Every test ends with closing the temp database without saving
|
||||
void TestGui::cleanup()
|
||||
{
|
||||
// DO NOT save the database
|
||||
MessageBox::setNextAnswer(QMessageBox::No);
|
||||
triggerAction("actionDatabaseClose");
|
||||
Tools::wait(100);
|
||||
|
||||
m_db = nullptr;
|
||||
m_dbWidget = nullptr;
|
||||
}
|
||||
|
||||
void TestGui::testTabs()
|
||||
{
|
||||
QCOMPARE(m_tabWidget->count(), 1);
|
||||
QCOMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), m_dbFileName);
|
||||
}
|
||||
|
||||
void TestGui::testEditEntry()
|
||||
{
|
||||
EntryView* entryView = m_dbWidget->findChild<EntryView*>("entryView");
|
||||
QModelIndex item = entryView->model()->index(0, 1);
|
||||
QRect itemRect = entryView->visualRect(item);
|
||||
QTest::mouseClick(entryView->viewport(), Qt::LeftButton, Qt::NoModifier, itemRect.center());
|
||||
QToolBar* toolBar = m_mainWindow->findChild<QToolBar*>("toolBar");
|
||||
|
||||
// Select the first entry in the database
|
||||
EntryView* entryView = m_dbWidget->findChild<EntryView*>("entryView");
|
||||
QModelIndex entryItem = entryView->model()->index(0, 1);
|
||||
clickIndex(entryItem, entryView, Qt::LeftButton);
|
||||
|
||||
// Confirm the edit action button is enabled
|
||||
QAction* entryEditAction = m_mainWindow->findChild<QAction*>("actionEntryEdit");
|
||||
QVERIFY(entryEditAction->isEnabled());
|
||||
QToolBar* toolBar = m_mainWindow->findChild<QToolBar*>("toolBar");
|
||||
QWidget* entryEditWidget = toolBar->widgetForAction(entryEditAction);
|
||||
QVERIFY(entryEditWidget->isVisible());
|
||||
QVERIFY(entryEditWidget->isEnabled());
|
||||
QTest::mouseClick(entryEditWidget, Qt::LeftButton);
|
||||
|
||||
// Edit the first entry ("Sample Entry")
|
||||
QTest::mouseClick(entryEditWidget, Qt::LeftButton);
|
||||
QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode);
|
||||
EditEntryWidget* editEntryWidget = m_dbWidget->findChild<EditEntryWidget*>("editEntryWidget");
|
||||
QVERIFY(m_dbWidget->currentWidget() == editEntryWidget);
|
||||
QLineEdit* titleEdit = editEntryWidget->findChild<QLineEdit*>("titleEdit");
|
||||
QTest::keyClicks(titleEdit, "_test");
|
||||
|
||||
// Save the edit
|
||||
QDialogButtonBox* editEntryWidgetButtonBox = editEntryWidget->findChild<QDialogButtonBox*>("buttonBox");
|
||||
QVERIFY(editEntryWidgetButtonBox);
|
||||
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton);
|
||||
// make sure the database isn't marked as modified
|
||||
// wait for modified timer
|
||||
QTRY_COMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), m_orgDbFileName);
|
||||
|
||||
// Confirm edit was made
|
||||
QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::ViewMode);
|
||||
Entry* entry = entryView->entryFromIndex(entryItem);
|
||||
QCOMPARE(entry->title(), QString("Sample Entry_test"));
|
||||
QCOMPARE(entry->historyItems().size(), 1);
|
||||
|
||||
// Confirm modified indicator is showing
|
||||
QTRY_COMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("%1*").arg(m_dbFileName));
|
||||
}
|
||||
|
||||
void TestGui::testAddEntry()
|
||||
{
|
||||
QToolBar* toolBar = m_mainWindow->findChild<QToolBar*>("toolBar");
|
||||
EntryView* entryView = m_dbWidget->findChild<EntryView*>("entryView");
|
||||
|
||||
// Find the new entry action
|
||||
QAction* entryNewAction = m_mainWindow->findChild<QAction*>("actionEntryNew");
|
||||
QVERIFY(entryNewAction->isEnabled());
|
||||
QToolBar* toolBar = m_mainWindow->findChild<QToolBar*>("toolBar");
|
||||
|
||||
// Find the button associated with the new entry action
|
||||
QWidget* entryNewWidget = toolBar->widgetForAction(entryNewAction);
|
||||
QVERIFY(entryNewWidget->isVisible());
|
||||
QVERIFY(entryNewWidget->isEnabled());
|
||||
|
||||
// Click the new entry button and check that we enter edit mode
|
||||
QTest::mouseClick(entryNewWidget, Qt::LeftButton);
|
||||
|
||||
QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode);
|
||||
|
||||
// Add entry "test" and confirm added
|
||||
EditEntryWidget* editEntryWidget = m_dbWidget->findChild<EditEntryWidget*>("editEntryWidget");
|
||||
QLineEdit* titleEdit = editEntryWidget->findChild<QLineEdit*>("titleEdit");
|
||||
QTest::keyClicks(titleEdit, "test");
|
||||
|
||||
QDialogButtonBox* editEntryWidgetButtonBox = editEntryWidget->findChild<QDialogButtonBox*>("buttonBox");
|
||||
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton);
|
||||
|
||||
@ -144,98 +181,106 @@ void TestGui::testAddEntry()
|
||||
|
||||
QCOMPARE(entry->title(), QString("test"));
|
||||
QCOMPARE(entry->historyItems().size(), 0);
|
||||
// wait for modified timer
|
||||
QTRY_COMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("%1*").arg(m_orgDbFileName));
|
||||
|
||||
QAction* entryEditAction = m_mainWindow->findChild<QAction*>("actionEntryEdit");
|
||||
QVERIFY(entryEditAction->isEnabled());
|
||||
QWidget* entryEditWidget = toolBar->widgetForAction(entryEditAction);
|
||||
QVERIFY(entryEditWidget->isVisible());
|
||||
QVERIFY(entryEditWidget->isEnabled());
|
||||
QTest::mouseClick(entryEditWidget, Qt::LeftButton);
|
||||
|
||||
QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode);
|
||||
QTest::keyClicks(titleEdit, "something");
|
||||
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton);
|
||||
|
||||
QCOMPARE(entry->title(), QString("testsomething"));
|
||||
QCOMPARE(entry->historyItems().size(), 1);
|
||||
|
||||
|
||||
// Add entry "something 2"
|
||||
QTest::mouseClick(entryNewWidget, Qt::LeftButton);
|
||||
QTest::keyClicks(titleEdit, "something 2");
|
||||
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton);
|
||||
|
||||
|
||||
// Add entry "something 3"
|
||||
QTest::mouseClick(entryNewWidget, Qt::LeftButton);
|
||||
QTest::keyClicks(titleEdit, "something 3");
|
||||
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton);
|
||||
|
||||
|
||||
// Confirm that 4 entries now exist
|
||||
QTRY_COMPARE(entryView->model()->rowCount(), 4);
|
||||
}
|
||||
|
||||
void TestGui::testSearch()
|
||||
{
|
||||
QAction* searchAction = m_mainWindow->findChild<QAction*>("actionSearch");
|
||||
QVERIFY(searchAction->isEnabled());
|
||||
QToolBar* toolBar = m_mainWindow->findChild<QToolBar*>("toolBar");
|
||||
QWidget* searchActionWidget = toolBar->widgetForAction(searchAction);
|
||||
EntryView* entryView = m_dbWidget->findChild<EntryView*>("entryView");
|
||||
QLineEdit* searchEdit = m_dbWidget->findChild<QLineEdit*>("searchEdit");
|
||||
QToolButton* clearSearch = m_dbWidget->findChild<QToolButton*>("clearButton");
|
||||
// Add canned entries for consistent testing
|
||||
testAddEntry();
|
||||
|
||||
QVERIFY(!searchEdit->isVisible());
|
||||
QToolBar* toolBar = m_mainWindow->findChild<QToolBar*>("toolBar");
|
||||
|
||||
QWidget* searchActionWidget = toolBar->findChild<QWidget*>("SearchWidget");
|
||||
QVERIFY(searchActionWidget->isEnabled());
|
||||
QLineEdit* searchEdit = searchActionWidget->findChild<QLineEdit*>("searchEdit");
|
||||
|
||||
EntryView* entryView = m_dbWidget->findChild<EntryView*>("entryView");
|
||||
QVERIFY(entryView->isVisible());
|
||||
|
||||
// Enter search
|
||||
QTest::mouseClick(searchActionWidget, Qt::LeftButton);
|
||||
QTest::mouseClick(searchEdit, Qt::LeftButton);
|
||||
QTRY_VERIFY(searchEdit->hasFocus());
|
||||
// Search for "ZZZ"
|
||||
QTest::keyClicks(searchEdit, "ZZZ");
|
||||
QTRY_COMPARE(entryView->model()->rowCount(), 0);
|
||||
// Escape
|
||||
QTest::keyClick(m_mainWindow, Qt::Key_Escape);
|
||||
QTRY_VERIFY(!searchEdit->hasFocus());
|
||||
// Enter search again
|
||||
QTest::mouseClick(searchActionWidget, Qt::LeftButton);
|
||||
QTRY_VERIFY(searchEdit->hasFocus());
|
||||
// Input and clear
|
||||
QTest::keyClicks(searchEdit, "ZZZ");
|
||||
QTRY_COMPARE(searchEdit->text(), QString("ZZZ"));
|
||||
QTest::mouseClick(clearSearch, Qt::LeftButton);
|
||||
QTRY_COMPARE(searchEdit->text(), QString(""));
|
||||
// Triggering search should select the existing text
|
||||
QTest::keyClicks(searchEdit, "ZZZ");
|
||||
QTest::mouseClick(searchActionWidget, Qt::LeftButton);
|
||||
QTRY_VERIFY(m_dbWidget->isInSearchMode());
|
||||
QTRY_COMPARE(entryView->model()->rowCount(), 0);
|
||||
// Escape clears searchedit and retains focus
|
||||
QTest::keyClick(searchEdit, Qt::Key_Escape);
|
||||
QTRY_VERIFY(searchEdit->text().isEmpty());
|
||||
QTRY_VERIFY(searchEdit->hasFocus());
|
||||
QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::ViewMode);
|
||||
// Search for "some"
|
||||
QTest::keyClicks(searchEdit, "some");
|
||||
QTRY_COMPARE(entryView->model()->rowCount(), 4);
|
||||
QTRY_VERIFY(m_dbWidget->isInSearchMode());
|
||||
QTRY_COMPARE(entryView->model()->rowCount(), 3);
|
||||
// Press Down to focus on the entry view
|
||||
QVERIFY(!entryView->hasFocus());
|
||||
QTest::keyClick(searchEdit, Qt::Key_Down);
|
||||
QVERIFY(entryView->hasFocus());
|
||||
QTest::keyClicks(searchEdit, "thing");
|
||||
QTRY_COMPARE(entryView->model()->rowCount(), 2);
|
||||
//QVERIFY(!entryView->hasFocus());
|
||||
//QTest::keyClick(searchEdit, Qt::Key_Down);
|
||||
//QVERIFY(entryView->hasFocus());
|
||||
|
||||
clickIndex(entryView->model()->index(0, 1), entryView, Qt::LeftButton);
|
||||
// Try to edit the first entry from the search view
|
||||
QModelIndex item = entryView->model()->index(0, 1);
|
||||
Entry* entry = entryView->entryFromIndex(item);
|
||||
QVERIFY(m_dbWidget->isInSearchMode());
|
||||
clickIndex(item, entryView, Qt::LeftButton);
|
||||
QAction* entryEditAction = m_mainWindow->findChild<QAction*>("actionEntryEdit");
|
||||
QVERIFY(entryEditAction->isEnabled());
|
||||
QWidget* entryEditWidget = toolBar->widgetForAction(entryEditAction);
|
||||
QVERIFY(entryEditWidget->isVisible());
|
||||
QVERIFY(entryEditWidget->isEnabled());
|
||||
QTest::mouseClick(entryEditWidget, Qt::LeftButton);
|
||||
|
||||
QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode);
|
||||
|
||||
// Perform the edit and save it
|
||||
EditEntryWidget* editEntryWidget = m_dbWidget->findChild<EditEntryWidget*>("editEntryWidget");
|
||||
QLineEdit* titleEdit = editEntryWidget->findChild<QLineEdit*>("titleEdit");
|
||||
QString origTitle = titleEdit->text();
|
||||
QTest::keyClicks(titleEdit, "_edited");
|
||||
QDialogButtonBox* editEntryWidgetButtonBox = editEntryWidget->findChild<QDialogButtonBox*>("buttonBox");
|
||||
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton);
|
||||
|
||||
QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::ViewMode);
|
||||
// Confirm the edit was made and we are back in view mode
|
||||
QTRY_VERIFY(m_dbWidget->isInSearchMode());
|
||||
QCOMPARE(entry->title(), origTitle.append("_edited"));
|
||||
|
||||
clickIndex(entryView->model()->index(1, 0), entryView, Qt::LeftButton);
|
||||
// Cancel search, should return to normal view
|
||||
QTest::mouseClick(searchEdit, Qt::LeftButton);
|
||||
QTest::keyClick(searchEdit, Qt::Key_Escape);
|
||||
QTRY_COMPARE(m_dbWidget->currentMode(), DatabaseWidget::ViewMode);
|
||||
//QCOMPARE(entryView->model()->rowCount(), 4);
|
||||
|
||||
// TODO: add tests to confirm case sensitive and group search
|
||||
}
|
||||
|
||||
void TestGui::testDeleteEntry()
|
||||
{
|
||||
// Add canned entries for consistent testing
|
||||
testAddEntry();
|
||||
|
||||
GroupView* groupView = m_dbWidget->findChild<GroupView*>("groupView");
|
||||
EntryView* entryView = m_dbWidget->findChild<EntryView*>("entryView");
|
||||
QToolBar* toolBar = m_mainWindow->findChild<QToolBar*>("toolBar");
|
||||
QAction* entryDeleteAction = m_mainWindow->findChild<QAction*>("actionEntryDelete");
|
||||
|
||||
QWidget* entryDeleteWidget = toolBar->widgetForAction(entryDeleteAction);
|
||||
|
||||
QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::ViewMode);
|
||||
clickIndex(entryView->model()->index(1, 0), entryView, Qt::LeftButton);
|
||||
QVERIFY(entryDeleteWidget->isVisible());
|
||||
QVERIFY(entryDeleteWidget->isEnabled());
|
||||
QVERIFY(!m_db->metadata()->recycleBin());
|
||||
@ -260,21 +305,7 @@ void TestGui::testSearch()
|
||||
QCOMPARE(entryView->model()->rowCount(), 1);
|
||||
QCOMPARE(m_db->metadata()->recycleBin()->entries().size(), 3);
|
||||
|
||||
QWidget* closeSearchButton = m_dbWidget->findChild<QToolButton*>("closeSearchButton");
|
||||
QTest::mouseClick(closeSearchButton, Qt::LeftButton);
|
||||
|
||||
QCOMPARE(entryView->model()->rowCount(), 1);
|
||||
}
|
||||
|
||||
void TestGui::testDeleteEntry()
|
||||
{
|
||||
GroupView* groupView = m_dbWidget->findChild<GroupView*>("groupView");
|
||||
EntryView* entryView = m_dbWidget->findChild<EntryView*>("entryView");
|
||||
QToolBar* toolBar = m_mainWindow->findChild<QToolBar*>("toolBar");
|
||||
QAction* entryDeleteAction = m_mainWindow->findChild<QAction*>("actionEntryDelete");
|
||||
QWidget* entryDeleteWidget = toolBar->widgetForAction(entryDeleteAction);
|
||||
QCOMPARE(groupView->currentGroup(), m_db->rootGroup());
|
||||
|
||||
QModelIndex rootGroupIndex = groupView->model()->index(0, 0);
|
||||
clickIndex(groupView->model()->index(groupView->model()->rowCount(rootGroupIndex) - 1, 0, rootGroupIndex),
|
||||
groupView, Qt::LeftButton);
|
||||
@ -363,28 +394,29 @@ void TestGui::testDragAndDropGroup()
|
||||
|
||||
dragAndDropGroup(groupModel->index(0, 0, rootIndex),
|
||||
rootIndex,
|
||||
-1, true, "NewDatabase", 5);
|
||||
-1, true, "NewDatabase", 4);
|
||||
}
|
||||
|
||||
void TestGui::testSaveAs()
|
||||
{
|
||||
QFileInfo fileInfo(m_orgDbFile.fileName());
|
||||
QFileInfo fileInfo(m_dbFile.fileName());
|
||||
QDateTime lastModified = fileInfo.lastModified();
|
||||
|
||||
m_db->metadata()->setName("SaveAs");
|
||||
|
||||
QTemporaryFile* tmpFile = new QTemporaryFile();
|
||||
// open temporary file so it creates a filename
|
||||
QVERIFY(tmpFile->open());
|
||||
m_tmpFileName = tmpFile->fileName();
|
||||
delete tmpFile;
|
||||
fileDialog()->setNextFileName(m_tmpFileName);
|
||||
QTemporaryFile tmpFile;
|
||||
QVERIFY(tmpFile.open());
|
||||
QString tmpFileName = tmpFile.fileName();
|
||||
tmpFile.remove();
|
||||
|
||||
fileDialog()->setNextFileName(tmpFileName);
|
||||
|
||||
triggerAction("actionDatabaseSaveAs");
|
||||
|
||||
QCOMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("SaveAs"));
|
||||
|
||||
checkDatabase();
|
||||
checkDatabase(tmpFileName);
|
||||
|
||||
fileInfo.refresh();
|
||||
QCOMPARE(fileInfo.lastModified(), lastModified);
|
||||
@ -433,40 +465,48 @@ void TestGui::testKeePass1Import()
|
||||
|
||||
QCOMPARE(m_tabWidget->count(), 2);
|
||||
QCOMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("basic [New database]*"));
|
||||
|
||||
// Close the KeePass1 Database
|
||||
MessageBox::setNextAnswer(QMessageBox::No);
|
||||
triggerAction("actionDatabaseClose");
|
||||
Tools::wait(100);
|
||||
|
||||
}
|
||||
|
||||
void TestGui::testDatabaseLocking()
|
||||
{
|
||||
MessageBox::setNextAnswer(QMessageBox::Cancel);
|
||||
QString origDbName = m_tabWidget->tabText(0);
|
||||
|
||||
MessageBox::setNextAnswer(QMessageBox::Cancel);
|
||||
triggerAction("actionLockDatabases");
|
||||
|
||||
QCOMPARE(m_tabWidget->tabText(0).remove('&'), QString("Save [locked]"));
|
||||
QCOMPARE(m_tabWidget->tabText(1).remove('&'), QString("basic [New database]*"));
|
||||
QCOMPARE(m_tabWidget->tabText(0).remove('&'), origDbName + " [locked]");
|
||||
|
||||
QWidget* dbWidget = m_tabWidget->currentDatabaseWidget();
|
||||
QWidget* unlockDatabaseWidget = dbWidget->findChild<QWidget*>("unlockDatabaseWidget");
|
||||
QWidget* editPassword = unlockDatabaseWidget->findChild<QLineEdit*>("editPassword");
|
||||
QVERIFY(editPassword);
|
||||
|
||||
QTest::keyClicks(editPassword, "masterpw");
|
||||
QTest::keyClicks(editPassword, "a");
|
||||
QTest::keyClick(editPassword, Qt::Key_Enter);
|
||||
|
||||
QCOMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()).remove('&'), QString("basic [New database]*"));
|
||||
QCOMPARE(m_tabWidget->tabText(0).remove('&'), origDbName);
|
||||
}
|
||||
|
||||
void TestGui::cleanupTestCase()
|
||||
{
|
||||
delete m_mainWindow;
|
||||
QFile::remove(m_tmpFileName);
|
||||
}
|
||||
|
||||
void TestGui::checkDatabase()
|
||||
void TestGui::checkDatabase(QString dbFileName)
|
||||
{
|
||||
if (dbFileName.isEmpty())
|
||||
dbFileName = m_dbFile.fileName();
|
||||
|
||||
CompositeKey key;
|
||||
key.addKey(PasswordKey("a"));
|
||||
KeePass2Reader reader;
|
||||
QScopedPointer<Database> dbSaved(reader.readDatabase(m_tmpFileName, key));
|
||||
QScopedPointer<Database> dbSaved(reader.readDatabase(dbFileName, key));
|
||||
QVERIFY(dbSaved);
|
||||
QVERIFY(!reader.hasError());
|
||||
QCOMPARE(dbSaved->metadata()->name(), m_db->metadata()->name());
|
||||
|
@ -34,7 +34,10 @@ class TestGui : public QObject
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void testOpenDatabase();
|
||||
void init();
|
||||
void cleanup();
|
||||
void cleanupTestCase();
|
||||
|
||||
void testTabs();
|
||||
void testEditEntry();
|
||||
void testAddEntry();
|
||||
@ -48,10 +51,9 @@ private Q_SLOTS:
|
||||
void testDatabaseSettings();
|
||||
void testKeePass1Import();
|
||||
void testDatabaseLocking();
|
||||
void cleanupTestCase();
|
||||
|
||||
private:
|
||||
void checkDatabase();
|
||||
void checkDatabase(QString dbFileName = "");
|
||||
void triggerAction(const QString& name);
|
||||
void dragAndDropGroup(const QModelIndex& sourceIndex, const QModelIndex& targetIndex, int row,
|
||||
bool expectedResult, const QString& expectedParentName, int expectedPos);
|
||||
@ -61,9 +63,8 @@ private:
|
||||
MainWindow* m_mainWindow;
|
||||
DatabaseTabWidget* m_tabWidget;
|
||||
DatabaseWidget* m_dbWidget;
|
||||
QTemporaryFile m_orgDbFile;
|
||||
QString m_orgDbFileName;
|
||||
QString m_tmpFileName;
|
||||
QTemporaryFile m_dbFile;
|
||||
QString m_dbFileName;
|
||||
Database* m_db;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user