From 8d623f37ca88204166e2de9981b998f91cfe0f33 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Wed, 18 Aug 2010 16:22:48 +0200 Subject: [PATCH] Add EntryModel. --- src/CMakeLists.txt | 1 + src/core/Entry.cpp | 20 +++++-- src/core/Entry.h | 4 ++ src/core/Group.cpp | 17 +++++- src/core/Group.h | 13 ++++- src/gui/EntryModel.cpp | 120 +++++++++++++++++++++++++++++++++++++++ src/gui/EntryModel.h | 51 +++++++++++++++++ src/gui/GroupModel.h | 2 +- tests/CMakeLists.txt | 3 + tests/TestEntryModel.cpp | 99 ++++++++++++++++++++++++++++++++ tests/TestGroup.cpp | 20 +++++++ 11 files changed, 341 insertions(+), 9 deletions(-) create mode 100644 src/gui/EntryModel.cpp create mode 100644 src/gui/EntryModel.h create mode 100644 tests/TestEntryModel.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6b73a9e1a..c288530e7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -26,6 +26,7 @@ set(keepassx_SOURCES core/Parser.cpp core/TimeInfo.cpp core/Uuid.cpp + gui/EntryModel.cpp gui/GroupModel.cpp ) diff --git a/src/core/Entry.cpp b/src/core/Entry.cpp index 986855180..69bd96fe1 100644 --- a/src/core/Entry.cpp +++ b/src/core/Entry.cpp @@ -26,6 +26,11 @@ Entry::Entry() m_db = 0; } +Entry::~Entry() +{ + // TODO notify group +} + Uuid Entry::uuid() const { return m_uuid; @@ -182,6 +187,11 @@ void Entry::addAutoTypeAssociation(const AutoTypeAssociation& assoc) void Entry::addAttribute(const QString& key, const QString& value) { m_attributes.insert(key, value); + + // TODO add all visible oclumns + if (key == "Title") { + Q_EMIT dataChanged(this); + } } void Entry::addAttachment(const QString& key, const QByteArray& value) @@ -191,27 +201,27 @@ void Entry::addAttachment(const QString& key, const QByteArray& value) void Entry::setTitle(const QString& title) { - m_attributes.insert("Title", title); + addAttribute("Title", title); } void Entry::setUrl(const QString& url) { - m_attributes.insert("URL", url); + addAttribute("URL", url); } void Entry::setUsername(const QString& username) { - m_attributes.insert("UserName", username); + addAttribute("UserName", username); } void Entry::setPassword(const QString& password) { - m_attributes.insert("Password", password); + addAttribute("Password", password); } void Entry::setNotes(const QString& notes) { - m_attributes.insert("Notes", notes); + addAttribute("Notes", notes); } void Entry::setGroup(Group* group) diff --git a/src/core/Entry.h b/src/core/Entry.h index 8aa7cccfe..b68cc01dc 100644 --- a/src/core/Entry.h +++ b/src/core/Entry.h @@ -41,6 +41,7 @@ class Entry : public QObject public: Entry(); + ~Entry(); Uuid uuid() const; QImage icon() const; QColor foregroundColor() const; @@ -80,6 +81,9 @@ public: void setGroup(Group* group); +Q_SIGNALS: + void dataChanged(const Entry* entry); + private: Uuid m_uuid; int m_iconNumber; diff --git a/src/core/Group.cpp b/src/core/Group.cpp index eff885bbf..85fe8ae2f 100644 --- a/src/core/Group.cpp +++ b/src/core/Group.cpp @@ -28,6 +28,11 @@ Group::Group() m_db = 0; } +Group::~Group() +{ + // TODO notify parent +} + Uuid Group::uuid() const { return m_uuid; @@ -229,14 +234,24 @@ QList Group::entries() const void Group::addEntry(Entry *entry) { - Q_ASSERT(entry != 0); + Q_ASSERT(entry); + + Q_EMIT entryAboutToAdd(entry); m_entries << entry; + connect(entry, SIGNAL(dataChanged(const Entry*)), SIGNAL(entryDataChanged(const Entry*))); + + Q_EMIT entryAdded(); } void Group::removeEntry(Entry* entry) { + Q_EMIT entryAboutToRemove(entry); + + entry->disconnect(this); m_entries.removeAll(entry); + + Q_EMIT entryRemoved(); } void Group::recSetDatabase(Database* db) diff --git a/src/core/Group.h b/src/core/Group.h index fa0dd192b..246000a22 100644 --- a/src/core/Group.h +++ b/src/core/Group.h @@ -32,6 +32,7 @@ class Group : public QObject public: Group(); + ~Group(); Uuid uuid() const; QString name() const; QString notes() const; @@ -65,10 +66,18 @@ public: Q_SIGNALS: void dataChanged(const Group* group); - void aboutToRemove(const Group* group); - void removed(); + void aboutToAdd(const Group* group, int index); void added(); + void aboutToRemove(const Group* group); + void removed(); + + void entryAboutToAdd(const Entry* entry); + void entryAdded(); + void entryAboutToRemove(const Entry* entry); + void entryRemoved(); + + void entryDataChanged(const Entry* entry); private: void recSetDatabase(Database* db); diff --git a/src/gui/EntryModel.cpp b/src/gui/EntryModel.cpp new file mode 100644 index 000000000..a77cb0db9 --- /dev/null +++ b/src/gui/EntryModel.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2010 Felix Geyer + * + * 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 . + */ + +#include "EntryModel.h" + +#include "core/Entry.h" +#include "core/Group.h" + +EntryModel::EntryModel(QObject* parent) + : QAbstractTableModel(parent) + , m_group(0) +{ +} + +void EntryModel::setGroup(const Group* group) +{ + beginResetModel(); + + if (m_group) { + disconnect(m_group, 0, this, 0); + } + m_group = group; + connect(group, SIGNAL(entryAboutToAdd(const Entry*)), SLOT(entryAboutToAdd(const Entry*))); + connect(group, SIGNAL(entryAdded()), SLOT(entryAdded())); + connect(group, SIGNAL(entryAboutToRemove(const Entry*)), SLOT(entryAboutToRemove(const Entry*))); + connect(group, SIGNAL(entryRemoved()), SLOT(entryRemoved())); + connect(group, SIGNAL(entryDataChanged(const Entry*)), SLOT(entryDataChanged(const Entry*))); + + endResetModel(); +} + +int EntryModel::rowCount(const QModelIndex& parent) const +{ + if (!m_group || parent.isValid()) { + return 0; + } + else { + return m_group->entries().size(); + } +} + +int EntryModel::columnCount(const QModelIndex& parent) const +{ + Q_UNUSED(parent); + + return 1; +} + +QVariant EntryModel::data(const QModelIndex& index, int role) const +{ + if (!index.isValid()) { + return QVariant(); + } + + const Entry* entry = m_group->entries().at(index.row()); + + // TODO implement other columns + if (role == Qt::DisplayRole) { + return entry->title(); + } + else if (role == Qt::DecorationRole) { + return entry->icon(); + } + else { + return QVariant(); + } +} +QVariant EntryModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + Q_UNUSED(section); + + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + return tr("Title"); + } + + return QVariant(); +} + +void EntryModel::entryAboutToAdd(const Entry* entry) +{ + Q_UNUSED(entry); + + beginInsertRows(QModelIndex(), m_group->entries().size(), m_group->entries().size()); +} + +void EntryModel::entryAdded() +{ + endInsertRows(); +} + +void EntryModel::entryAboutToRemove(const Entry* entry) +{ + beginRemoveRows(QModelIndex(), m_group->entries().indexOf(entry), m_group->entries().indexOf(entry)); +} + +void EntryModel::entryRemoved() +{ + endRemoveRows(); +} + +void EntryModel::entryDataChanged(const Entry* entry) +{ + int row = m_group->entries().indexOf(entry); + qDebug("%d", index(row, 0).row()); + Q_EMIT dataChanged(index(row, 0), index(row, columnCount()-1)); +} diff --git a/src/gui/EntryModel.h b/src/gui/EntryModel.h new file mode 100644 index 000000000..6dd753252 --- /dev/null +++ b/src/gui/EntryModel.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010 Felix Geyer + * + * 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 . + */ + +#ifndef KEEPASSX_ENTRYMODEL_H +#define KEEPASSX_ENTRYMODEL_H + +#include + +class Entry; +class Group; + +class EntryModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + explicit EntryModel(QObject* parent = 0); + int rowCount(const QModelIndex& parent = QModelIndex()) const; + int columnCount(const QModelIndex& parent = QModelIndex()) const; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + +public Q_SLOTS: + void setGroup(const Group* group); + +private Q_SLOTS: + void entryAboutToAdd(const Entry* entry); + void entryAdded(); + void entryAboutToRemove(const Entry* entry); + void entryRemoved(); + void entryDataChanged(const Entry* entry); + +private: + const Group* m_group; +}; + +#endif // KEEPASSX_ENTRYMODEL_H diff --git a/src/gui/GroupModel.h b/src/gui/GroupModel.h index 923cf7785..7689ef6b2 100644 --- a/src/gui/GroupModel.h +++ b/src/gui/GroupModel.h @@ -35,7 +35,7 @@ public: QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const; QModelIndex parent(const QModelIndex& index) const; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; - QVariant headerData(int section, Qt::Orientation orientation, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; private: QModelIndex createIndex(int row, int column, const Group* group) const; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 63fabf3c7..b4184cd97 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -71,3 +71,6 @@ target_link_libraries( testparser keepassx_core ${QT_QTCORE_LIBRARY} ${QT_QTGUI_ add_unit_test( testgroupmodel TestGroupModel.cpp modeltest.cpp ) target_link_libraries( testgroupmodel keepassx_core ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} ${QT_QTTEST_LIBRARY} ) + +add_unit_test( testentrymodel TestEntryModel.cpp modeltest.cpp ) +target_link_libraries( testentrymodel keepassx_core ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} ${QT_QTTEST_LIBRARY} ) diff --git a/tests/TestEntryModel.cpp b/tests/TestEntryModel.cpp new file mode 100644 index 000000000..7e357c4b2 --- /dev/null +++ b/tests/TestEntryModel.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2010 Felix Geyer + * + * 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 . + */ + +#include +#include + +#include "modeltest.h" +#include "core/Entry.h" +#include "core/Group.h" +#include "gui/EntryModel.h" + +class TestEntryModel : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void test(); +}; + +void TestEntryModel::test() +{ + qRegisterMetaType("QModelIndex"); + + Group* group1 = new Group(); + Group* group2 = new Group(); + + Entry* entry1 = new Entry(); + entry1->setGroup(group1); + entry1->setTitle("testTitle1"); + + Entry* entry2 = new Entry(); + entry2->setGroup(group1); + entry2->setTitle("testTitle2"); + + EntryModel* model = new EntryModel(this); + + new ModelTest(model, this); + + model->setGroup(group1); + + QCOMPARE(model->rowCount(), 2); + + QSignalSpy spyDataChanged(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&))); + entry1->setTitle("changed"); + QCOMPARE(spyDataChanged.count(), 1); + + QModelIndex index1 = model->index(0, 0); + QModelIndex index2 = model->index(1, 0); + + QCOMPARE(model->data(index1).toString(), entry1->title()); + QCOMPARE(model->data(index2).toString(), entry2->title()); + + QSignalSpy spyAboutToAdd(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex&,int,int))); + QSignalSpy spyAdded(model, SIGNAL(rowsInserted(const QModelIndex&,int,int))); + QSignalSpy spyAboutToRemove(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&,int,int))); + QSignalSpy spyRemoved(model, SIGNAL(rowsRemoved(const QModelIndex&,int,int))); + + Entry* entry3 = new Entry(); + entry3->setGroup(group1); + + QCOMPARE(spyAboutToAdd.count(), 1); + QCOMPARE(spyAdded.count(), 1); + QCOMPARE(spyAboutToRemove.count(), 0); + QCOMPARE(spyRemoved.count(), 0); + + entry2->setGroup(group2); + + QCOMPARE(spyAboutToAdd.count(), 1); + QCOMPARE(spyAdded.count(), 1); + QCOMPARE(spyAboutToRemove.count(), 1); + QCOMPARE(spyRemoved.count(), 1); + + QSignalSpy spyReset(model, SIGNAL(modelReset())); + model->setGroup(group2); + QCOMPARE(spyReset.count(), 1); + + delete group1; + delete group2; +} + +QTEST_MAIN(TestEntryModel); + +Q_DECLARE_METATYPE(QModelIndex); + +#include "TestEntryModel.moc" diff --git a/tests/TestGroup.cpp b/tests/TestGroup.cpp index 57538ed59..1e06cf416 100644 --- a/tests/TestGroup.cpp +++ b/tests/TestGroup.cpp @@ -27,6 +27,7 @@ class TestGroup : public QObject private Q_SLOTS: void testParenting(); void testSignals(); + void testEntries(); }; void TestGroup::testParenting() @@ -100,6 +101,25 @@ void TestGroup::testSignals() QCOMPARE(spyAdded.count(), 1); QCOMPARE(spyAboutToRemove.count(), 1); QCOMPARE(spyRemoved.count(), 1); + + delete db; +} + +void TestGroup::testEntries() +{ + Group* group = new Group(); + + Entry* entry1 = new Entry(); + entry1->setGroup(group); + + Entry* entry2 = new Entry(); + entry2->setGroup(group); + + QCOMPARE(group->entries().size(), 2); + QVERIFY(group->entries().at(0) == entry1); + QVERIFY(group->entries().at(1) == entry2); + + delete group; } QTEST_MAIN(TestGroup);