Add search.

Refs #24
This commit is contained in:
Florian Geyer 2012-05-12 13:22:41 +02:00
parent f836629dda
commit 6b6c109903
13 changed files with 224 additions and 35 deletions

View File

@ -465,8 +465,9 @@ void Entry::setGroup(Group* group)
} }
} }
group->addEntry(this);
m_group = group; m_group = group;
group->addEntry(this);
QObject::setParent(group); QObject::setParent(group);
if (m_updateTimeinfo) { if (m_updateTimeinfo) {
@ -488,3 +489,11 @@ const Database* Entry::database() const
return 0; return 0;
} }
} }
bool Entry::match(const QString& searchTerm, Qt::CaseSensitivity caseSensitivity)
{
return title().contains(searchTerm, caseSensitivity) ||
username().contains(searchTerm, caseSensitivity) ||
url().contains(searchTerm, caseSensitivity) ||
notes().contains(searchTerm, caseSensitivity);
}

View File

@ -126,6 +126,7 @@ public:
void setGroup(Group* group); void setGroup(Group* group);
void setUpdateTimeinfo(bool value); void setUpdateTimeinfo(bool value);
bool match(const QString &searchTerm, Qt::CaseSensitivity caseSensitivity);
Q_SIGNALS: Q_SIGNALS:
/** /**

View File

@ -487,3 +487,45 @@ void Group::recCreateDelObjects()
m_db->addDeletedObject(m_uuid); m_db->addDeletedObject(m_uuid);
} }
} }
QList<Entry*> Group::search(const QString& searchTerm, Qt::CaseSensitivity caseSensitivity,
bool resolveInherit)
{
QList<Entry*> searchResult;
if (includeInSearch(resolveInherit)) {
Q_FOREACH (Entry* entry, m_entries) {
if (entry->match(searchTerm, caseSensitivity)) {
searchResult.append(entry);
}
}
Q_FOREACH (Group* group, m_children) {
searchResult.append(group->search(searchTerm, caseSensitivity, false));
}
}
return searchResult;
}
bool Group::includeInSearch(bool resolveInherit)
{
switch (m_searchingEnabled) {
case Inherit:
if (!m_parent) {
return true;
}
else {
if (resolveInherit) {
return m_parent->includeInSearch(true);
}
else {
return true;
}
}
case Enable:
return true;
case Disable:
return false;
default:
Q_ASSERT(false);
return false;
}
}

View File

@ -76,8 +76,10 @@ public:
QList<Entry*> entries(); QList<Entry*> entries();
const QList<Entry*>& entries() const; const QList<Entry*>& entries() const;
QList<Entry*> entriesRecursive(bool includeHistoryItems = false) const; QList<Entry*> entriesRecursive(bool includeHistoryItems = false) const;
QList<const Group *> groupsRecursive(bool includeSelf) const; QList<const Group*> groupsRecursive(bool includeSelf) const;
QList<Entry*> search(const QString& searchTerm, Qt::CaseSensitivity caseSensitivity,
bool resolveInherit = true);
Q_SIGNALS: Q_SIGNALS:
void dataChanged(Group* group); void dataChanged(Group* group);
@ -136,6 +138,8 @@ private:
friend void Database::setRootGroup(Group* group); friend void Database::setRootGroup(Group* group);
friend Entry::~Entry(); friend Entry::~Entry();
friend void Entry::setGroup(Group* group); friend void Entry::setGroup(Group* group);
bool includeInSearch(bool resolveInherit);
}; };
#endif // KEEPASSX_GROUP_H #endif // KEEPASSX_GROUP_H

View File

@ -20,6 +20,7 @@
#include <QtGui/QHBoxLayout> #include <QtGui/QHBoxLayout>
#include <QtGui/QLabel> #include <QtGui/QLabel>
#include <QtGui/QSplitter> #include <QtGui/QSplitter>
#include <QtGui/QLineEdit>
#include <QtGui/QMessageBox> #include <QtGui/QMessageBox>
#include "core/Metadata.h" #include "core/Metadata.h"
@ -53,12 +54,22 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
policy.setHorizontalStretch(30); policy.setHorizontalStretch(30);
m_groupView->setSizePolicy(policy); m_groupView->setSizePolicy(policy);
policy = m_entryView->sizePolicy(); QWidget* widget = new QWidget();
policy = widget->sizePolicy();
policy.setHorizontalStretch(70); policy.setHorizontalStretch(70);
m_entryView->setSizePolicy(policy); widget->setSizePolicy(policy);
splitter->addWidget(m_groupView); splitter->addWidget(m_groupView);
splitter->addWidget(m_entryView);
QVBoxLayout* vLayout = new QVBoxLayout();
QHBoxLayout* hLayout = new QHBoxLayout();
hLayout->addWidget(new QLabel("Find:"));
m_searchEdit = new QLineEdit();
hLayout->addWidget(m_searchEdit);
vLayout->addLayout(hLayout);
vLayout->addWidget(m_entryView);
widget->setLayout(vLayout);
splitter->addWidget(widget);
layout->addWidget(splitter); layout->addWidget(splitter);
m_mainWidget->setLayout(layout); m_mainWidget->setLayout(layout);
@ -87,6 +98,7 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
connect(m_changeMasterKeyWidget, SIGNAL(editFinished(bool)), SLOT(updateMasterKey(bool))); connect(m_changeMasterKeyWidget, SIGNAL(editFinished(bool)), SLOT(updateMasterKey(bool)));
connect(m_databaseSettingsWidget, SIGNAL(editFinished(bool)), SLOT(updateSettings(bool))); connect(m_databaseSettingsWidget, SIGNAL(editFinished(bool)), SLOT(updateSettings(bool)));
connect(this, SIGNAL(currentChanged(int)), this, SLOT(emitCurrentModeChanged())); connect(this, SIGNAL(currentChanged(int)), this, SLOT(emitCurrentModeChanged()));
connect(m_searchEdit, SIGNAL(returnPressed()), this, SLOT(search()));
setCurrentIndex(0); setCurrentIndex(0);
} }
@ -311,6 +323,15 @@ void DatabaseWidget::switchToDatabaseSettings()
setCurrentIndex(4); setCurrentIndex(4);
} }
void DatabaseWidget::search()
{
Group* searchGroup = m_db->rootGroup();
QList<Entry*> searchResult = searchGroup->search(m_searchEdit->text(), Qt::CaseInsensitive);
m_groupView->setCurrentIndex(QModelIndex());
m_entryView->search(searchResult);
}
bool DatabaseWidget::dbHasKey() bool DatabaseWidget::dbHasKey()
{ {
return m_db->hasKey(); return m_db->hasKey();

View File

@ -20,6 +20,8 @@
#include <QtGui/QStackedWidget> #include <QtGui/QStackedWidget>
class QLineEdit;
class ChangeMasterKeyWidget; class ChangeMasterKeyWidget;
class DatabaseSettingsWidget; class DatabaseSettingsWidget;
class Database; class Database;
@ -64,6 +66,7 @@ public Q_SLOTS:
void switchToGroupEdit(); void switchToGroupEdit();
void switchToMasterKeyChange(); void switchToMasterKeyChange();
void switchToDatabaseSettings(); void switchToDatabaseSettings();
void search();
private Q_SLOTS: private Q_SLOTS:
void switchToView(bool accepted); void switchToView(bool accepted);
@ -86,6 +89,7 @@ private:
Group* m_newGroup; Group* m_newGroup;
Entry* m_newEntry; Entry* m_newEntry;
Group* m_newParent; Group* m_newParent;
QLineEdit* m_searchEdit;
}; };
#endif // KEEPASSX_DATABASEWIDGET_H #endif // KEEPASSX_DATABASEWIDGET_H

View File

@ -31,45 +31,67 @@ EntryModel::EntryModel(QObject* parent)
Entry* EntryModel::entryFromIndex(const QModelIndex& index) const Entry* EntryModel::entryFromIndex(const QModelIndex& index) const
{ {
Q_ASSERT(index.isValid() && index.row() < m_group->entries().size()); Q_ASSERT(index.isValid() && index.row() < m_entries.size());
return m_group->entries().at(index.row()); return m_entries.at(index.row());
} }
QModelIndex EntryModel::indexFromEntry(Entry* entry) const QModelIndex EntryModel::indexFromEntry(Entry* entry) const
{ {
int row = m_group->entries().indexOf(entry); int row = m_entries.indexOf(entry);
Q_ASSERT(row != -1); Q_ASSERT(row != -1);
return index(row, 0); return index(row, 0);
} }
void EntryModel::setGroup(Group* group) void EntryModel::setGroup(Group* group)
{ {
if (group == m_group) { if (!group || group == m_group) {
return; return;
} }
beginResetModel(); beginResetModel();
if (m_group) { severConnections();
disconnect(m_group, 0, this, 0);
}
m_group = group; m_group = group;
connect(group, SIGNAL(entryAboutToAdd(Entry*)), SLOT(entryAboutToAdd(Entry*))); m_entries = group->entries();
connect(group, SIGNAL(entryAdded()), SLOT(entryAdded()));
connect(group, SIGNAL(entryAboutToRemove(Entry*)), SLOT(entryAboutToRemove(Entry*))); makeConnections(group);
connect(group, SIGNAL(entryRemoved()), SLOT(entryRemoved()));
connect(group, SIGNAL(entryDataChanged(Entry*)), SLOT(entryDataChanged(Entry*)));
endResetModel(); endResetModel();
Q_EMIT switchedToView();
}
void EntryModel::setEntries(QList<Entry*> entries)
{
beginResetModel();
severConnections();
m_group = 0;
m_allGroups.clear();
m_entries = entries;
if (entries.count() > 0) {
m_allGroups = entries.at(0)->group()->database()->rootGroup()->groupsRecursive(true);
}
QListIterator<const Group*> iGroups(m_allGroups);
while (iGroups.hasNext()) {
const Group* group = iGroups.next();
makeConnections(group);
}
endResetModel();
Q_EMIT switchedToSearch();
} }
int EntryModel::rowCount(const QModelIndex& parent) const int EntryModel::rowCount(const QModelIndex& parent) const
{ {
if (!m_group || parent.isValid()) { if (parent.isValid()) {
return 0; return 0;
} }
else { else {
return m_group->entries().size(); return m_entries.size();
} }
} }
@ -77,7 +99,7 @@ int EntryModel::columnCount(const QModelIndex& parent) const
{ {
Q_UNUSED(parent); Q_UNUSED(parent);
return 3; return 4;
} }
QVariant EntryModel::data(const QModelIndex& index, int role) const QVariant EntryModel::data(const QModelIndex& index, int role) const
@ -91,16 +113,27 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole) {
switch (index.column()) { switch (index.column()) {
case 0: case 0:
return entry->title(); if (entry->group()) {
return entry->group()->name();
}
case 1: case 1:
return entry->username(); return entry->title();
case 2: case 2:
return entry->username();
case 3:
return entry->url(); return entry->url();
} }
} }
else if ((role == Qt::DecorationRole) && (index.column() == 0)) { else if (role == Qt::DecorationRole) {
switch (index.column()) {
case 0:
if (entry->group()) {
return entry->group()->iconPixmap();
}
case 1:
return entry->iconPixmap(); return entry->iconPixmap();
} }
}
return QVariant(); return QVariant();
} }
@ -109,10 +142,12 @@ QVariant EntryModel::headerData(int section, Qt::Orientation orientation, int ro
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch (section) { switch (section) {
case 0: case 0:
return tr("Title"); return tr("Group");
case 1: case 1:
return tr("Username"); return tr("Title");
case 2: case 2:
return tr("Username");
case 3:
return tr("URL"); return tr("URL");
} }
} }
@ -156,7 +191,8 @@ QMimeData* EntryModel::mimeData(const QModelIndexList& indexes) const
if (!indexes[i].isValid()) { if (!indexes[i].isValid()) {
continue; continue;
} }
stream << m_group->database()->uuid() << entryFromIndex(indexes[i])->uuid(); Entry* entry = entryFromIndex(indexes[i]);
stream << entry->group()->database()->uuid() << entry->uuid();
} }
data->setData(mimeTypes().first(), encoded); data->setData(mimeTypes().first(), encoded);
@ -167,26 +203,61 @@ void EntryModel::entryAboutToAdd(Entry* entry)
{ {
Q_UNUSED(entry); Q_UNUSED(entry);
beginInsertRows(QModelIndex(), m_group->entries().size(), m_group->entries().size()); beginInsertRows(QModelIndex(), m_entries.size(), m_entries.size());
if (!m_group) {
m_entries.append(entry);
}
} }
void EntryModel::entryAdded() void EntryModel::entryAdded()
{ {
if (m_group) {
m_entries = m_group->entries();
}
endInsertRows(); endInsertRows();
} }
void EntryModel::entryAboutToRemove(Entry* entry) void EntryModel::entryAboutToRemove(Entry* entry)
{ {
beginRemoveRows(QModelIndex(), m_group->entries().indexOf(entry), m_group->entries().indexOf(entry)); beginRemoveRows(QModelIndex(), m_entries.indexOf(entry), m_entries.indexOf(entry));
if (!m_group) {
m_entries.removeAll(entry);
}
} }
void EntryModel::entryRemoved() void EntryModel::entryRemoved()
{ {
if (m_group) {
m_entries = m_group->entries();
}
endRemoveRows(); endRemoveRows();
} }
void EntryModel::entryDataChanged(Entry* entry) void EntryModel::entryDataChanged(Entry* entry)
{ {
int row = m_group->entries().indexOf(entry); int row = m_entries.indexOf(entry);
Q_EMIT dataChanged(index(row, 0), index(row, columnCount()-1)); Q_EMIT dataChanged(index(row, 0), index(row, columnCount()-1));
} }
void EntryModel::severConnections()
{
if (m_group) {
disconnect(m_group, 0, this, 0);
}
QListIterator<const Group*> i(m_allGroups);
while (i.hasNext()) {
const Group* group = i.next();
disconnect(group, 0, this, 0);
}
}
void EntryModel::makeConnections(const Group *group)
{
connect(group, SIGNAL(entryAboutToAdd(Entry*)), SLOT(entryAboutToAdd(Entry*)));
connect(group, SIGNAL(entryAdded()), SLOT(entryAdded()));
connect(group, SIGNAL(entryAboutToRemove(Entry*)), SLOT(entryAboutToRemove(Entry*)));
connect(group, SIGNAL(entryRemoved()), SLOT(entryRemoved()));
connect(group, SIGNAL(entryDataChanged(Entry*)), SLOT(entryDataChanged(Entry*)));
}

View File

@ -41,6 +41,12 @@ public:
QStringList mimeTypes() const; QStringList mimeTypes() const;
QMimeData* mimeData(const QModelIndexList& indexes) const; QMimeData* mimeData(const QModelIndexList& indexes) const;
void setEntries(QList<Entry*> entries);
Q_SIGNALS:
void switchedToSearch();
void switchedToView();
public Q_SLOTS: public Q_SLOTS:
void setGroup(Group* group); void setGroup(Group* group);
@ -53,6 +59,11 @@ private Q_SLOTS:
private: private:
Group* m_group; Group* m_group;
QList<Entry*> m_entries;
QList<const Group*> m_allGroups;
void severConnections();
void makeConnections(const Group* group);
}; };
#endif // KEEPASSX_ENTRYMODEL_H #endif // KEEPASSX_ENTRYMODEL_H

View File

@ -40,6 +40,8 @@ EntryView::EntryView(QWidget* parent)
connect(this, SIGNAL(activated(const QModelIndex&)), SLOT(emitEntryActivated(const QModelIndex&))); connect(this, SIGNAL(activated(const QModelIndex&)), SLOT(emitEntryActivated(const QModelIndex&)));
connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SIGNAL(entrySelectionChanged())); connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SIGNAL(entrySelectionChanged()));
connect(m_model, SIGNAL(switchedToSearch()), this, SLOT(switchToSearch()));
connect(m_model, SIGNAL(switchedToView()), this, SLOT(switchToView()));
sortByColumn(0, Qt::AscendingOrder); sortByColumn(0, Qt::AscendingOrder);
} }
@ -50,6 +52,12 @@ void EntryView::setGroup(Group* group)
Q_EMIT entrySelectionChanged(); Q_EMIT entrySelectionChanged();
} }
void EntryView::search(QList<Entry*> entries)
{
m_model->setEntries(entries);
Q_EMIT entrySelectionChanged();
}
void EntryView::emitEntryActivated(const QModelIndex& index) void EntryView::emitEntryActivated(const QModelIndex& index)
{ {
Q_EMIT entryActivated(entryFromIndex(index)); Q_EMIT entryActivated(entryFromIndex(index));
@ -86,3 +94,13 @@ Entry* EntryView::entryFromIndex(const QModelIndex& index)
return 0; return 0;
} }
} }
void EntryView::switchToSearch()
{
showColumn(0);
}
void EntryView::switchToView()
{
hideColumn(0);
}

View File

@ -36,12 +36,15 @@ public:
bool isSingleEntrySelected(); bool isSingleEntrySelected();
void setCurrentEntry(Entry* entry); void setCurrentEntry(Entry* entry);
Entry* entryFromIndex(const QModelIndex& index); Entry* entryFromIndex(const QModelIndex& index);
void search(QList<Entry *> entries);
public Q_SLOTS: public Q_SLOTS:
void setGroup(Group* group); void setGroup(Group* group);
private Q_SLOTS: private Q_SLOTS:
void emitEntryActivated(const QModelIndex& index); void emitEntryActivated(const QModelIndex& index);
void switchToSearch();
void switchToView();
Q_SIGNALS: Q_SIGNALS:
void entryActivated(Entry* entry); void entryActivated(Entry* entry);

View File

@ -63,7 +63,12 @@ void GroupView::dragMoveEvent(QDragMoveEvent* event)
Group* GroupView::currentGroup() Group* GroupView::currentGroup()
{ {
if (currentIndex() == QModelIndex()) {
return 0;
}
else {
return m_model->groupFromIndex(currentIndex()); return m_model->groupFromIndex(currentIndex());
}
} }
void GroupView::expandedChanged(const QModelIndex& index) void GroupView::expandedChanged(const QModelIndex& index)

View File

@ -62,8 +62,8 @@ void TestEntryModel::test()
entry1->setTitle("changed"); entry1->setTitle("changed");
QCOMPARE(spyDataChanged.count(), 1); QCOMPARE(spyDataChanged.count(), 1);
QModelIndex index1 = model->index(0, 0); QModelIndex index1 = model->index(0, 1);
QModelIndex index2 = model->index(1, 0); QModelIndex index2 = model->index(1, 1);
QCOMPARE(model->data(index1).toString(), entry1->title()); QCOMPARE(model->data(index1).toString(), entry1->title());
QCOMPARE(model->data(index2).toString(), entry2->title()); QCOMPARE(model->data(index2).toString(), entry2->title());

View File

@ -74,7 +74,7 @@ void TestGui::testEditEntry()
DatabaseTabWidget* tabWidget = m_mainWindow->findChild<DatabaseTabWidget*>("tabWidget"); DatabaseTabWidget* tabWidget = m_mainWindow->findChild<DatabaseTabWidget*>("tabWidget");
DatabaseWidget* dbWidget = tabWidget->currentDatabaseWidget(); DatabaseWidget* dbWidget = tabWidget->currentDatabaseWidget();
EntryView* entryView = dbWidget->findChild<EntryView*>("entryView"); EntryView* entryView = dbWidget->findChild<EntryView*>("entryView");
QModelIndex item = entryView->model()->index(0, 0); QModelIndex item = entryView->model()->index(0, 1);
QRect itemRect = entryView->visualRect(item); QRect itemRect = entryView->visualRect(item);
QTest::mouseClick(entryView->viewport(), Qt::LeftButton, Qt::NoModifier, itemRect.center()); QTest::mouseClick(entryView->viewport(), Qt::LeftButton, Qt::NoModifier, itemRect.center());
QTest::qWait(20); QTest::qWait(20);
@ -126,7 +126,7 @@ void TestGui::testAddEntry()
QTest::qWait(20); QTest::qWait(20);
QCOMPARE(dbWidget->currentMode(), DatabaseWidget::ViewMode); QCOMPARE(dbWidget->currentMode(), DatabaseWidget::ViewMode);
QModelIndex item = entryView->model()->index(1, 0); QModelIndex item = entryView->model()->index(1, 1);
Entry* entry = entryView->entryFromIndex(item); Entry* entry = entryView->entryFromIndex(item);
QCOMPARE(entry->title(), QString("test")); QCOMPARE(entry->title(), QString("test"));