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;
group->addEntry(this);
QObject::setParent(group);
if (m_updateTimeinfo) {
@ -488,3 +489,11 @@ const Database* Entry::database() const
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 setUpdateTimeinfo(bool value);
bool match(const QString &searchTerm, Qt::CaseSensitivity caseSensitivity);
Q_SIGNALS:
/**

View File

@ -487,3 +487,45 @@ void Group::recCreateDelObjects()
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();
const QList<Entry*>& entries() 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:
void dataChanged(Group* group);
@ -136,6 +138,8 @@ private:
friend void Database::setRootGroup(Group* group);
friend Entry::~Entry();
friend void Entry::setGroup(Group* group);
bool includeInSearch(bool resolveInherit);
};
#endif // KEEPASSX_GROUP_H

View File

@ -20,6 +20,7 @@
#include <QtGui/QHBoxLayout>
#include <QtGui/QLabel>
#include <QtGui/QSplitter>
#include <QtGui/QLineEdit>
#include <QtGui/QMessageBox>
#include "core/Metadata.h"
@ -53,12 +54,22 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
policy.setHorizontalStretch(30);
m_groupView->setSizePolicy(policy);
policy = m_entryView->sizePolicy();
QWidget* widget = new QWidget();
policy = widget->sizePolicy();
policy.setHorizontalStretch(70);
m_entryView->setSizePolicy(policy);
widget->setSizePolicy(policy);
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);
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_databaseSettingsWidget, SIGNAL(editFinished(bool)), SLOT(updateSettings(bool)));
connect(this, SIGNAL(currentChanged(int)), this, SLOT(emitCurrentModeChanged()));
connect(m_searchEdit, SIGNAL(returnPressed()), this, SLOT(search()));
setCurrentIndex(0);
}
@ -311,6 +323,15 @@ void DatabaseWidget::switchToDatabaseSettings()
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()
{
return m_db->hasKey();

View File

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

View File

@ -31,45 +31,67 @@ EntryModel::EntryModel(QObject* parent)
Entry* EntryModel::entryFromIndex(const QModelIndex& index) const
{
Q_ASSERT(index.isValid() && index.row() < m_group->entries().size());
return m_group->entries().at(index.row());
Q_ASSERT(index.isValid() && index.row() < m_entries.size());
return m_entries.at(index.row());
}
QModelIndex EntryModel::indexFromEntry(Entry* entry) const
{
int row = m_group->entries().indexOf(entry);
int row = m_entries.indexOf(entry);
Q_ASSERT(row != -1);
return index(row, 0);
}
void EntryModel::setGroup(Group* group)
{
if (group == m_group) {
if (!group || group == m_group) {
return;
}
beginResetModel();
if (m_group) {
disconnect(m_group, 0, this, 0);
}
severConnections();
m_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*)));
m_entries = group->entries();
makeConnections(group);
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
{
if (!m_group || parent.isValid()) {
if (parent.isValid()) {
return 0;
}
else {
return m_group->entries().size();
return m_entries.size();
}
}
@ -77,7 +99,7 @@ int EntryModel::columnCount(const QModelIndex& parent) const
{
Q_UNUSED(parent);
return 3;
return 4;
}
QVariant EntryModel::data(const QModelIndex& index, int role) const
@ -91,15 +113,26 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const
if (role == Qt::DisplayRole) {
switch (index.column()) {
case 0:
return entry->title();
if (entry->group()) {
return entry->group()->name();
}
case 1:
return entry->username();
return entry->title();
case 2:
return entry->username();
case 3:
return entry->url();
}
}
else if ((role == Qt::DecorationRole) && (index.column() == 0)) {
return entry->iconPixmap();
else if (role == Qt::DecorationRole) {
switch (index.column()) {
case 0:
if (entry->group()) {
return entry->group()->iconPixmap();
}
case 1:
return entry->iconPixmap();
}
}
return QVariant();
@ -109,10 +142,12 @@ QVariant EntryModel::headerData(int section, Qt::Orientation orientation, int ro
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch (section) {
case 0:
return tr("Title");
return tr("Group");
case 1:
return tr("Username");
return tr("Title");
case 2:
return tr("Username");
case 3:
return tr("URL");
}
}
@ -156,7 +191,8 @@ QMimeData* EntryModel::mimeData(const QModelIndexList& indexes) const
if (!indexes[i].isValid()) {
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);
@ -167,26 +203,61 @@ void EntryModel::entryAboutToAdd(Entry* 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()
{
if (m_group) {
m_entries = m_group->entries();
}
endInsertRows();
}
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()
{
if (m_group) {
m_entries = m_group->entries();
}
endRemoveRows();
}
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));
}
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;
QMimeData* mimeData(const QModelIndexList& indexes) const;
void setEntries(QList<Entry*> entries);
Q_SIGNALS:
void switchedToSearch();
void switchedToView();
public Q_SLOTS:
void setGroup(Group* group);
@ -53,6 +59,11 @@ private Q_SLOTS:
private:
Group* m_group;
QList<Entry*> m_entries;
QList<const Group*> m_allGroups;
void severConnections();
void makeConnections(const Group* group);
};
#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(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);
}
@ -50,6 +52,12 @@ void EntryView::setGroup(Group* group)
Q_EMIT entrySelectionChanged();
}
void EntryView::search(QList<Entry*> entries)
{
m_model->setEntries(entries);
Q_EMIT entrySelectionChanged();
}
void EntryView::emitEntryActivated(const QModelIndex& index)
{
Q_EMIT entryActivated(entryFromIndex(index));
@ -86,3 +94,13 @@ Entry* EntryView::entryFromIndex(const QModelIndex& index)
return 0;
}
}
void EntryView::switchToSearch()
{
showColumn(0);
}
void EntryView::switchToView()
{
hideColumn(0);
}

View File

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

View File

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

View File

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

View File

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