summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Zaslavsky <diazona@ellipsix.net>2016-08-15 13:07:16 (GMT)
committerRolf Eike Beer <kde@opensource.sf-tec.de>2016-08-20 07:07:06 (GMT)
commit0779334e4cb13bed796b8f3c22617179972d6105 (patch)
tree4d941da0d5a40e26de1e6e016f1b6d3196c09464
parent6d329a53390510b19695dd1f3e69349678078d98 (diff)
Encapsulate QModelIndex operations in search result model
This adds two new methods for handling QModelIndex instances as well as comments explaining some non-obvious design choices REVIEW:128701
-rw-r--r--model/kgpgsearchresultmodel.cpp171
-rw-r--r--model/kgpgsearchresultmodel.h26
2 files changed, 145 insertions, 52 deletions
diff --git a/model/kgpgsearchresultmodel.cpp b/model/kgpgsearchresultmodel.cpp
index 066d95b..5b1d939 100644
--- a/model/kgpgsearchresultmodel.cpp
+++ b/model/kgpgsearchresultmodel.cpp
@@ -183,6 +183,53 @@ KGpgSearchResultModel::~KGpgSearchResultModel()
delete d;
}
+/*
+ * In this implementation, the top-level node is identified by
+ * an invalid `QModelIndex`. Sublevel nodes correspond to valid
+ * `QModelIndex` instances. First-level nodes have a null `internalPointer`,
+ * and the `SearchResult` that holds a key is stored as the `internalPointer`
+ * of each second-level subnode under that key's first-level subnode.
+ *
+ * This design works better than storing pointers to the `SearchResult`s
+ * in the first-level nodes because the second-level nodes need some way
+ * to be linked to their parent nodes. Storing a pointer to the parent
+ * `QModelIndex` in the second-level `QModelIndex` is tricky because of
+ * the short lifetime of `QModelIndex` instances. However, it's
+ * straightforward to get from a `SearchResult` to the corresponding
+ * first-level `QModelIndex`, so effectively the `SearchResult` instances
+ * do double duty as "proxy pointers" to first-level `QModelIndex`s,
+ * which aren't going to disappear from memory at any moment.
+ */
+
+KGpgSearchResultModel::NodeLevel
+KGpgSearchResultModel::nodeLevel(const QModelIndex &index)
+{
+ if (!index.isValid())
+ return ROOT_LEVEL;
+ else if (index.internalPointer() == Q_NULLPTR)
+ return KEY_LEVEL;
+ else
+ return ATTRIBUTE_LEVEL;
+}
+
+SearchResult *
+KGpgSearchResultModel::resultForIndex(const QModelIndex &index) const
+{
+ switch (nodeLevel(index)) {
+ case KEY_LEVEL:
+ return d->m_items.at(index.row());
+ case ATTRIBUTE_LEVEL:
+ {
+ SearchResult *tmp = static_cast<SearchResult *>(index.internalPointer());
+ Q_ASSERT(tmp != Q_NULLPTR);
+ return tmp;
+ }
+ default:
+ return Q_NULLPTR;
+ }
+}
+
+
QVariant
KGpgSearchResultModel::data(const QModelIndex &index, int role) const
{
@@ -195,17 +242,19 @@ KGpgSearchResultModel::data(const QModelIndex &index, int role) const
if (index.row() < 0)
return QVariant();
- SearchResult *tmp = static_cast<SearchResult *>(index.internalPointer());
+ SearchResult *tmp = resultForIndex(index);
int row;
- if (tmp == Q_NULLPTR) {
+ switch (nodeLevel(index)) {
+ case KEY_LEVEL:
// this is a "top" item, show the first uid
if (index.row() >= d->m_items.count())
return QVariant();
- tmp = d->m_items.at(index.row());
row = 0;
- } else {
+ break;
+ case ATTRIBUTE_LEVEL:
+ {
row = index.row() + 1;
int summaryRow = tmp->getUidCount();
int uatRow;
@@ -228,6 +277,13 @@ KGpgSearchResultModel::data(const QModelIndex &index, int role) const
return QVariant();
}
Q_ASSERT(row < tmp->getUidCount());
+ break;
+ }
+ case ROOT_LEVEL:
+ // The root index, level 0, should have been caught by the conditional
+ // if (!index.isValid()) {...} at the top of this method
+ Q_ASSERT(false);
+ break;
}
switch (index.column()) {
@@ -243,47 +299,45 @@ KGpgSearchResultModel::data(const QModelIndex &index, int role) const
int
KGpgSearchResultModel::columnCount(const QModelIndex &parent) const
{
- if (parent.isValid()) {
+ switch (nodeLevel(parent)) {
+ case KEY_LEVEL:
if (parent.column() != 0)
return 0;
-
- SearchResult *tmp = static_cast<SearchResult *>(parent.internalPointer());
-
- if (tmp == Q_NULLPTR)
- return 2;
else
- return 0;
- } else {
+ return 2;
+ case ATTRIBUTE_LEVEL:
+ return 0;
+ case ROOT_LEVEL:
return 2;
+ default:
+ Q_ASSERT(false);
}
}
QModelIndex
KGpgSearchResultModel::index(int row, int column, const QModelIndex &parent) const
{
- // there are three hierarchy levels:
- // root: this is simply QModelIndex()
- // key items: parent is invalid, internalPointer is Q_NULLPTR
- // uid entries: parent is key item, internalPointer is set to SearchResult*
-
- if (parent.isValid()) {
- if (parent.internalPointer() != Q_NULLPTR) {
+ switch (nodeLevel(parent)) {
+ case ATTRIBUTE_LEVEL:
+ return QModelIndex();
+ case KEY_LEVEL:
+ {
+ if (parent.row() >= d->m_items.count())
return QModelIndex();
- } else {
- if (parent.row() >= d->m_items.count())
- return QModelIndex();
- SearchResult *tmp = d->m_items.at(parent.row());
- int maxRow = tmp->getUidCount();
- if (tmp->m_uatCount != 0)
- maxRow++;
- if ((row >= maxRow) || (column > 1))
- return QModelIndex();
- return createIndex(row, column, tmp);
- }
- } else {
+ SearchResult *tmp = resultForIndex(parent);
+ int maxRow = tmp->getUidCount();
+ if (tmp->m_uatCount != 0)
+ maxRow++;
+ if ((row >= maxRow) || (column > 1))
+ return QModelIndex();
+ return createIndex(row, column, tmp);
+ }
+ case ROOT_LEVEL:
if ((row >= d->m_items.count()) || (column > 1) || (row < 0) || (column < 0))
return QModelIndex();
return createIndex(row, column);
+ default:
+ Q_ASSERT(false);
}
}
@@ -293,31 +347,41 @@ KGpgSearchResultModel::parent(const QModelIndex &index) const
if (!index.isValid())
return QModelIndex();
- SearchResult *tmp = static_cast<SearchResult *>(index.internalPointer());
-
- if (tmp == Q_NULLPTR)
+ switch (nodeLevel(index)) {
+ case ROOT_LEVEL:
+ case KEY_LEVEL:
return QModelIndex();
-
- return createIndex(d->m_items.indexOf(tmp), 0);
+ case ATTRIBUTE_LEVEL:
+ {
+ SearchResult *tmp = resultForIndex(index);
+ return createIndex(d->m_items.indexOf(tmp), 0);
+ }
+ default:
+ Q_ASSERT(false);
+ }
}
int
KGpgSearchResultModel::rowCount(const QModelIndex &parent) const
{
- if (!parent.isValid()) {
+ switch (nodeLevel(parent)) {
+ case ROOT_LEVEL:
return d->m_items.count();
- } else if (parent.column() == 0) {
- if (parent.internalPointer() != Q_NULLPTR)
+ case KEY_LEVEL:
+ if (parent.column() == 0) {
+ SearchResult *item = resultForIndex(parent);
+ int cnt = item->getUidCount();
+ if (item->m_uatCount != 0)
+ cnt++;
+
+ return cnt;
+ } else {
return 0;
-
- SearchResult *item = d->m_items.at(parent.row());
- int cnt = item->getUidCount();
- if (item->m_uatCount != 0)
- cnt++;
-
- return cnt;
- } else {
+ }
+ case ATTRIBUTE_LEVEL:
return 0;
+ default:
+ Q_ASSERT(false);
}
}
@@ -345,11 +409,14 @@ KGpgSearchResultModel::idForIndex(const QModelIndex &index) const
{
Q_ASSERT(index.isValid());
- SearchResult *tmp = static_cast<SearchResult *>(index.internalPointer());
- if (tmp == Q_NULLPTR)
- tmp = d->m_items.at(index.row());
-
- return tmp->m_fingerprint;
+ switch (nodeLevel(index)) {
+ case KEY_LEVEL:
+ case ATTRIBUTE_LEVEL:
+ return resultForIndex(index)->m_fingerprint;
+ default:
+ // root level should have been caught at the beginning
+ Q_ASSERT(false);
+ }
}
void
diff --git a/model/kgpgsearchresultmodel.h b/model/kgpgsearchresultmodel.h
index c2d8a3c..3c34985 100644
--- a/model/kgpgsearchresultmodel.h
+++ b/model/kgpgsearchresultmodel.h
@@ -24,6 +24,7 @@
#include <kgpgcompiler.h>
+class SearchResult;
class KGpgSearchResultModelPrivate;
/**
@@ -57,6 +58,31 @@ public slots:
void slotAddKey(const QStringList &lines);
private:
+ typedef enum {ROOT_LEVEL, KEY_LEVEL, ATTRIBUTE_LEVEL} NodeLevel;
+ /**
+ * @brief Returns the level corresponding to a `QModelIndex` associated
+ * with this `KGpgSearchResultModel`.
+ *
+ * There are three levels of nodes. The top level, level 0, is the
+ * root node. Each first-level subnode corresponds to a key, and each
+ * second-level subnode corresponds to an attribute of that key: a UID,
+ * or the number of UATs, or the summary description of the key.
+ *
+ * @param index a `QModelIndex` representing the position of a node in the model
+ * @return the level of the node
+ */
+ static NodeLevel nodeLevel(const QModelIndex &index);
+
+ /**
+ * @brief Find the `SearchResult` associated with an index.
+ *
+ * This returns the `SearchResult` instance corresponding to an
+ * index regardless of whether the index represents a key (first-level)
+ * or an attribute (second-level). It also works for the root index,
+ * for which it returns `Q_NULLPTR`.
+ */
+ SearchResult *resultForIndex(const QModelIndex &index) const;
+
KGpgSearchResultModelPrivate * const d;
};