aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Faure <[email protected]>2016-07-03 13:44:23 +0200
committerDavid Faure <[email protected]>2016-07-05 09:02:12 +0200
commit381acbc294dcecb0d4beb63721080d231e4bca38 (patch)
treedf79c5f79388e077db48a5b47599998ef04ec3e5
parenta761b38d99974dacecd2a9d65c989929fb2b59e8 (diff)
Port StatisticsProxyModel to KExtraColumnsProxyModel.
* this fixes sibling() - the unittest confirmed Dan's assessment that sibling() got broken for StatisticsProxyModel with Qt5. * this reduces the number of dataChanged signals emitted by the proxy: instead of dataChanged((row,0) (row,0)) + dataChanged((row,1) (row,3)) the proxy now emits dataChanged((row,0) (row,3)) directly * I also removed the code emitting dataChanged for parent indexes, the reasoning was "so that cumulative totals can be updated", but there are no cumulative totals anywhere in this proxy (other than the tooltip). * the awful model-index-struct-casting hack is no longer necessary, the code is much simpler, there are now unittests (both here and for the base class KExtraColumnsProxyModel in kitemmodels)... BUG: 355229
-rw-r--r--autotests/libs/CMakeLists.txt1
-rw-r--r--autotests/libs/statisticsproxymodeltest.cpp241
-rw-r--r--autotests/libs/test_model_helpers.h79
-rw-r--r--src/core/models/statisticsproxymodel.cpp351
-rw-r--r--src/core/models/statisticsproxymodel.h27
5 files changed, 402 insertions, 297 deletions
diff --git a/autotests/libs/CMakeLists.txt b/autotests/libs/CMakeLists.txt
index ff1c947..27aa258 100644
--- a/autotests/libs/CMakeLists.txt
+++ b/autotests/libs/CMakeLists.txt
@@ -97,6 +97,7 @@ add_akonadi_test(newmailnotifierattributetest.cpp)
add_akonadi_test(pop3resourceattributetest.cpp)
add_akonadi_test_widgets(actionstatemanagertest.cpp)
add_akonadi_test(tagmodeltest.cpp)
+add_akonadi_test(statisticsproxymodeltest.cpp)
add_akonadi_test(sharedvaluepooltest.cpp)
add_akonadi_test(jobtest.cpp)
diff --git a/autotests/libs/statisticsproxymodeltest.cpp b/autotests/libs/statisticsproxymodeltest.cpp
new file mode 100644
index 0000000..4830812
--- /dev/null
+++ b/autotests/libs/statisticsproxymodeltest.cpp
@@ -0,0 +1,241 @@
+/*
+ Copyright (c) 2016 David Faure <[email protected]>
+
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This library 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 Library General Public
+ License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+*/
+
+#include <collectionstatistics.h>
+#include <qtest.h>
+
+#include <statisticsproxymodel.h>
+#include <QStandardItemModel>
+#include <AkonadiCore/Collection>
+#include <EntityTreeModel>
+#include "test_model_helpers.h"
+using namespace TestModelHelpers;
+
+using Akonadi::StatisticsProxyModel;
+
+class StatisticsProxyModelTest : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void initTestCase();
+
+ void init();
+ void shouldDoNothingIfNoExtraColumns();
+ void shouldShowExtraColumns();
+ void shouldShowToolTip();
+ void shouldHandleDataChanged();
+ void shouldHandleDataChangedInExtraColumn();
+
+private:
+ QStandardItemModel m_model;
+};
+
+// Helper functions
+
+static QString indexToText(const QModelIndex &index)
+{
+ if (!index.isValid()) {
+ return QStringLiteral("invalid");
+ }
+ return QString::number(index.row()) + QLatin1Char(',') + QString::number(index.column()) + QLatin1Char(',')
+ + QString::number(reinterpret_cast<qulonglong>(index.internalPointer()), 16)
+ + QLatin1String(" in ") + QString::number(reinterpret_cast<qulonglong>(index.model()), 16);
+}
+
+static QString indexRowCol(const QModelIndex &index)
+{
+ if (!index.isValid()) {
+ return QStringLiteral("invalid");
+ }
+ return QString::number(index.row()) + QLatin1Char(',') + QString::number(index.column());
+}
+
+static Akonadi::CollectionStatistics makeStats(qint64 unread, qint64 count, qint64 size)
+{
+ Akonadi::CollectionStatistics stats;
+ stats.setUnreadCount(unread);
+ stats.setCount(count);
+ stats.setSize(size);
+ return stats;
+}
+
+void StatisticsProxyModelTest::initTestCase()
+{
+}
+
+void StatisticsProxyModelTest::init()
+{
+ // Prepare the source model to use later on
+ m_model.clear();
+ m_model.appendRow(makeStandardItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C") << QStringLiteral("D")));
+ m_model.item(0, 0)->appendRow(makeStandardItems(QStringList() << QStringLiteral("m") << QStringLiteral("n") << QStringLiteral("o") << QStringLiteral("p")));
+ m_model.item(0, 0)->appendRow(makeStandardItems(QStringList() << QStringLiteral("q") << QStringLiteral("r") << QStringLiteral("s") << QStringLiteral("t")));
+ m_model.appendRow(makeStandardItems(QStringList() << QStringLiteral("E") << QStringLiteral("F") << QStringLiteral("G") << QStringLiteral("H")));
+ m_model.item(1, 0)->appendRow(makeStandardItems(QStringList() << QStringLiteral("x") << QStringLiteral("y") << QStringLiteral("z") << QStringLiteral(".")));
+ m_model.setHorizontalHeaderLabels(QStringList() << QStringLiteral("H1") << QStringLiteral("H2") << QStringLiteral("H3") << QStringLiteral("H4"));
+
+ // Set Collection c1 for row A
+ Akonadi::Collection c1(1);
+ c1.setName(QStringLiteral("c1"));
+ c1.setStatistics(makeStats(2, 6, 9)); // unread, count, size in bytes
+ m_model.item(0, 0)->setData(QVariant::fromValue(c1), Akonadi::EntityTreeModel::CollectionRole);
+
+ // Set Collection c2 for first child (row m)
+ Akonadi::Collection c2(2);
+ c2.setName(QStringLiteral("c2"));
+ c2.setStatistics(makeStats(1, 3, 4)); // unread, count, size in bytes
+ m_model.item(0, 0)->child(0)->setData(QVariant::fromValue(c2), Akonadi::EntityTreeModel::CollectionRole);
+
+ // Set Collection c2 for first child (row m)
+ Akonadi::Collection c3(3);
+ c3.setName(QStringLiteral("c3"));
+ c3.setStatistics(makeStats(0, 1, 1)); // unread, count, size in bytes
+ m_model.item(0, 0)->child(1)->setData(QVariant::fromValue(c3), Akonadi::EntityTreeModel::CollectionRole);
+
+ QCOMPARE(extractRowTexts(&m_model, 0), QStringLiteral("ABCD"));
+ QCOMPARE(extractRowTexts(&m_model, 0, m_model.index(0, 0)), QStringLiteral("mnop"));
+ QCOMPARE(extractRowTexts(&m_model, 1, m_model.index(0, 0)), QStringLiteral("qrst"));
+ QCOMPARE(extractRowTexts(&m_model, 1), QStringLiteral("EFGH"));
+ QCOMPARE(extractRowTexts(&m_model, 0, m_model.index(1, 0)), QStringLiteral("xyz."));
+ QCOMPARE(extractHorizontalHeaderTexts(&m_model), QStringLiteral("H1H2H3H4"));
+}
+
+void StatisticsProxyModelTest::shouldDoNothingIfNoExtraColumns()
+{
+ // Given a statistics proxy with no extra columns
+ StatisticsProxyModel pm;
+ pm.setExtraColumnsEnabled(false);
+
+ // When setting it to a source model
+ pm.setSourceModel(&m_model);
+
+ // Then the proxy should show the same as the model
+ QCOMPARE(pm.rowCount(), m_model.rowCount());
+ QCOMPARE(pm.columnCount(), m_model.columnCount());
+
+ QCOMPARE(pm.rowCount(pm.index(0, 0)), 2);
+ QCOMPARE(pm.index(0, 0).parent(), QModelIndex());
+
+ // (verify that the mapFromSource(mapToSource(x)) == x roundtrip works)
+ for (int row = 0; row < pm.rowCount(); ++row) {
+ for (int col = 0; col < pm.columnCount(); ++col) {
+ QCOMPARE(pm.mapFromSource(pm.mapToSource(pm.index(row, col))), pm.index(row, col));
+ }
+ }
+
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABCD"));
+ QCOMPARE(extractRowTexts(&pm, 0, pm.index(0, 0)), QStringLiteral("mnop"));
+ QCOMPARE(extractRowTexts(&pm, 1, pm.index(0, 0)), QStringLiteral("qrst"));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("EFGH"));
+ QCOMPARE(extractRowTexts(&pm, 0, pm.index(1, 0)), QStringLiteral("xyz."));
+ QCOMPARE(extractHorizontalHeaderTexts(&pm), QStringLiteral("H1H2H3H4"));
+}
+
+void StatisticsProxyModelTest::shouldShowExtraColumns()
+{
+ // Given a extra-columns proxy with three extra columns
+ StatisticsProxyModel pm;
+
+ // When setting it to a source model
+ pm.setSourceModel(&m_model);
+
+ // Then the proxy should show the extra column
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABCD269 B"));
+ QCOMPARE(extractRowTexts(&pm, 0, pm.index(0, 0)), QStringLiteral("mnop134 B"));
+ QCOMPARE(extractRowTexts(&pm, 1, pm.index(0, 0)), QStringLiteral("qrst 11 B"));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("EFGH "));
+ QCOMPARE(extractRowTexts(&pm, 0, pm.index(1, 0)), QStringLiteral("xyz. "));
+ QCOMPARE(extractHorizontalHeaderTexts(&pm), QStringLiteral("H1H2H3H4UnreadTotalSize"));
+
+ // Verify tree structure of proxy
+ const QModelIndex secondParent = pm.index(1, 0);
+ QVERIFY(!secondParent.parent().isValid());
+ QCOMPARE(indexToText(pm.index(0, 0, secondParent).parent()), indexToText(secondParent));
+ QCOMPARE(indexToText(pm.index(0, 3, secondParent).parent()), indexToText(secondParent));
+ QVERIFY(indexToText(pm.index(0, 4)).startsWith(QStringLiteral("0,4,")));
+ QCOMPARE(indexToText(pm.index(0, 4, secondParent).parent()), indexToText(secondParent));
+ QVERIFY(indexToText(pm.index(0, 5)).startsWith(QStringLiteral("0,5,")));
+ QCOMPARE(indexToText(pm.index(0, 5, secondParent).parent()), indexToText(secondParent));
+
+ QCOMPARE(pm.index(0, 0).sibling(0, 4).column(), 4);
+ QCOMPARE(pm.index(0, 4).sibling(0, 1).column(), 1);
+
+ QVERIFY(!pm.canFetchMore(QModelIndex()));
+}
+
+void StatisticsProxyModelTest::shouldShowToolTip()
+{
+ // Given a extra-columns proxy with three extra columns
+ StatisticsProxyModel pm;
+ pm.setSourceModel(&m_model);
+
+ // When enabling tooltips and getting the tooltip for the first folder
+ pm.setToolTipEnabled(true);
+ QString toolTip = pm.index(0, 0).data(Qt::ToolTipRole).toString();
+
+ // Then the tooltip should contain the expected information
+ toolTip.remove(QLatin1String("<strong>"));
+ toolTip.remove(QLatin1String("</strong>"));
+ QVERIFY2(toolTip.contains(QLatin1String("Total Messages: 6")), qPrintable(toolTip));
+ QVERIFY2(toolTip.contains(QLatin1String("Unread Messages: 2")), qPrintable(toolTip));
+ QVERIFY2(toolTip.contains(QLatin1String("Storage Size: 9 B")), qPrintable(toolTip));
+ QVERIFY2(toolTip.contains(QLatin1String("Subfolder Storage Size: 5 B")), qPrintable(toolTip));
+}
+
+void StatisticsProxyModelTest::shouldHandleDataChanged()
+{
+ // Given a extra-columns proxy with three extra columns
+ StatisticsProxyModel pm;
+ pm.setSourceModel(&m_model);
+ QSignalSpy dataChangedSpy(&pm, SIGNAL(dataChanged(QModelIndex,QModelIndex)));
+
+ // When ETM says the collection changed
+ m_model.item(0, 0)->setData(QStringLiteral("a"), Qt::EditRole);
+
+ // Then the change should be notified to the proxy -- including the extra columns
+ QCOMPARE(dataChangedSpy.count(), 1);
+ QCOMPARE(indexRowCol(dataChangedSpy.at(0).at(0).toModelIndex()), QStringLiteral("0,0"));
+ QCOMPARE(indexRowCol(dataChangedSpy.at(0).at(1).toModelIndex()), QStringLiteral("0,6"));
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("aBCD269 B"));
+}
+
+void StatisticsProxyModelTest::shouldHandleDataChangedInExtraColumn()
+{
+ // Given a extra-columns proxy with three extra columns
+ StatisticsProxyModel pm;
+ pm.setSourceModel(&m_model);
+ QSignalSpy dataChangedSpy(&pm, SIGNAL(dataChanged(QModelIndex,QModelIndex)));
+
+ // When the proxy wants to signal a change in an extra column
+ Akonadi::Collection c1(1);
+ c1.setName(QStringLiteral("c1"));
+ c1.setStatistics(makeStats(3, 5, 8)); // changed: unread, count, size in bytes
+ m_model.item(0, 0)->setData(QVariant::fromValue(c1), Akonadi::EntityTreeModel::CollectionRole);
+
+ // Then the change should be available and notified
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABCD358 B"));
+ QCOMPARE(dataChangedSpy.count(), 1);
+ QCOMPARE(indexRowCol(dataChangedSpy.at(0).at(0).toModelIndex()), QStringLiteral("0,0"));
+ QCOMPARE(indexRowCol(dataChangedSpy.at(0).at(1).toModelIndex()), QStringLiteral("0,6"));
+}
+
+#include "statisticsproxymodeltest.moc"
+
+QTEST_MAIN(StatisticsProxyModelTest)
diff --git a/autotests/libs/test_model_helpers.h b/autotests/libs/test_model_helpers.h
new file mode 100644
index 0000000..6b25d91
--- /dev/null
+++ b/autotests/libs/test_model_helpers.h
@@ -0,0 +1,79 @@
+/*
+ Copyright (c) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, [email protected]
+ Authors: David Faure <[email protected]>
+
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This library 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 Library General Public
+ License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+*/
+
+#include <QString>
+#include <QStandardItem>
+#include <QSignalSpy>
+
+namespace TestModelHelpers
+{
+
+// Prepares one row for a QStandardItemModel
+inline QList<QStandardItem *> makeStandardItems(const QStringList &texts)
+{
+ QList<QStandardItem *> items;
+ foreach (const QString &txt, texts) {
+ items << new QStandardItem(txt);
+ }
+ return items;
+}
+
+// Extracts a full row from a model as a string
+// Works best if every cell contains only one character
+inline QString extractRowTexts(QAbstractItemModel *model, int row, const QModelIndex &parent = QModelIndex())
+{
+ QString result;
+ const int colCount = model->columnCount();
+ for (int col = 0; col < colCount; ++col) {
+ const QString txt = model->index(row, col, parent).data().toString();
+ result += txt.isEmpty() ? QStringLiteral(" ") : txt;
+ }
+ return result;
+}
+
+// Extracts all headers
+inline QString extractHorizontalHeaderTexts(QAbstractItemModel *model)
+{
+ QString result;
+ const int colCount = model->columnCount();
+ for (int col = 0; col < colCount; ++col) {
+ const QString txt = model->headerData(col, Qt::Horizontal).toString();
+ result += txt.isEmpty() ? QStringLiteral(" ") : txt;
+ }
+ return result;
+}
+
+inline QString rowSpyToText(const QSignalSpy &spy)
+{
+ if (!spy.isValid()) {
+ return QStringLiteral("THE SIGNALSPY IS INVALID!");
+ }
+ QString str;
+ for (int i = 0; i < spy.count(); ++i) {
+ str += spy.at(i).at(1).toString() + QLatin1Char(',') + spy.at(i).at(2).toString();
+ if (i + 1 < spy.count()) {
+ str += QLatin1Char(';');
+ }
+ }
+ return str;
+}
+
+}
+
diff --git a/src/core/models/statisticsproxymodel.cpp b/src/core/models/statisticsproxymodel.cpp
index 435e490..212ef5c 100644
--- a/src/core/models/statisticsproxymodel.cpp
+++ b/src/core/models/statisticsproxymodel.cpp
@@ -1,5 +1,6 @@
/*
Copyright (c) 2009 Kevin Ottens <[email protected]>
+ 2016 David Faure <[email protected]>
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
@@ -19,6 +20,7 @@
#include "statisticsproxymodel.h"
#include "akonadicore_debug.h"
+#include "kitemmodels_version.h"
#include "entitytreemodel.h"
#include "collectionutils.h"
@@ -45,7 +47,7 @@ class Q_DECL_HIDDEN StatisticsProxyModel::Private
{
public:
Private(StatisticsProxyModel *parent)
- : mParent(parent), mToolTipEnabled(false), mExtraColumnsEnabled(true)
+ : q(parent), mToolTipEnabled(false), mExtraColumnsEnabled(false)
{
}
@@ -69,11 +71,9 @@ public:
int sourceColumnCount() const
{
- return mParent->sourceModel()->columnCount();
+ return q->sourceModel()->columnCount();
}
- QModelIndex sourceIndexAtFirstColumn(const QModelIndex &proxyIndex) const;
-
QString toolTipForCollection(const QModelIndex &index, const Collection &collection)
{
QString bckColor = QApplication::palette().color(QPalette::ToolTipBase).name();
@@ -119,7 +119,7 @@ public:
}
}
- KFormat formatter;
+ KFormat formatter;
qint64 currentFolderSize(collection.statistics().size());
tipInfo += QStringLiteral(
" <strong>%1</strong>: %2<br>\n"
@@ -182,113 +182,47 @@ public:
return tip;
}
- void proxyDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
-
- void sourceLayoutAboutToBeChanged();
- void sourceLayoutChanged();
-
- QVector<QModelIndex> m_proxyIndexes;
- QVector<QPersistentModelIndex> m_persistentSourceFirstColumn;
+ void _k_sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles);
- StatisticsProxyModel *mParent;
+ StatisticsProxyModel *q;
bool mToolTipEnabled;
bool mExtraColumnsEnabled;
};
-void StatisticsProxyModel::Private::proxyDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
+void StatisticsProxyModel::Private::_k_sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
{
- if (mExtraColumnsEnabled) {
- // Ugly hack.
- // The proper solution is a KExtraColumnsProxyModel, but this will do for now.
- QModelIndex parent = topLeft.parent();
- int parentColumnCount = mParent->columnCount(parent);
- QModelIndex extraTopLeft = mParent->index(topLeft.row(), parentColumnCount - 1 - 3, parent);
- QModelIndex extraBottomRight = mParent->index(bottomRight.row(), parentColumnCount - 1, parent);
- mParent->disconnect(mParent, SIGNAL(dataChanged(QModelIndex,QModelIndex)), mParent, SLOT(proxyDataChanged(QModelIndex,QModelIndex)));
- emit mParent->dataChanged(extraTopLeft, extraBottomRight);
-
- // We get this signal when the statistics of a row changes.
- // However, we need to emit data changed for the statistics of all ancestor rows too
- // so that recursive totals can be updated.
- while (parent.isValid()) {
- const QModelIndex left = mParent->index(parent.row(), parentColumnCount - 1 - 3, parent.parent());
- const QModelIndex right = mParent->index(parent.row(), parentColumnCount - 1, parent.parent());
- emit mParent->dataChanged(left, right);
- parent = parent.parent();
- parentColumnCount = mParent->columnCount(parent);
- }
- mParent->connect(mParent, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
- SLOT(proxyDataChanged(QModelIndex,QModelIndex)));
+ QModelIndex proxyTopLeft(q->mapFromSource(topLeft));
+ QModelIndex proxyBottomRight(q->mapFromSource(bottomRight));
+ // Emit data changed for the whole row (bug #222292)
+ if (mExtraColumnsEnabled && topLeft.column() == 0) { // in theory we could filter on roles, but ETM doesn't set any yet
+ const int lastColumn = q->columnCount() - 1;
+ proxyBottomRight = proxyBottomRight.sibling(proxyBottomRight.row(), lastColumn);
}
+ emit q->dataChanged(proxyTopLeft, proxyBottomRight, roles);
}
-void StatisticsProxyModel::Private::sourceLayoutAboutToBeChanged()
+void StatisticsProxyModel::setSourceModel(QAbstractItemModel *model)
{
- // QIdentityProxyModel took care of the first columnCount() columns
- // We have to take care of the extra columns (by storing persistent indexes in column 0,
- // waiting for the source to update them, and then looking at where they ended up)
- QModelIndexList persistent = mParent->persistentIndexList();
- const int columnCount = mParent->sourceModel()->columnCount();
- foreach (const QModelIndex &proxyPersistentIndex, persistent) {
- if (proxyPersistentIndex.column() >= columnCount) {
- m_proxyIndexes << proxyPersistentIndex;
- m_persistentSourceFirstColumn << QPersistentModelIndex(sourceIndexAtFirstColumn(proxyPersistentIndex));
- }
+ if (sourceModel()) {
+ disconnect(sourceModel(), SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)),
+ this, SLOT(_k_sourceDataChanged(QModelIndex,QModelIndex,QVector<int>)));
}
-}
-
-void StatisticsProxyModel::Private::sourceLayoutChanged()
-{
- QModelIndexList oldList;
- QModelIndexList newList;
-
- for (int i = 0; i < m_proxyIndexes.size(); ++i) {
- const QModelIndex oldProxyIndex = m_proxyIndexes.at(i);
- const QModelIndex proxyIndexFirstCol = mParent->mapFromSource(m_persistentSourceFirstColumn.at(i));
- const QModelIndex newProxyIndex = proxyIndexFirstCol.sibling(proxyIndexFirstCol.row(), oldProxyIndex.column());
- if (newProxyIndex != oldProxyIndex) {
- oldList.append(oldProxyIndex);
- newList.append(newProxyIndex);
- }
- }
- mParent->changePersistentIndexList(oldList, newList);
- m_persistentSourceFirstColumn.clear();
- m_proxyIndexes.clear();
-}
-
-void StatisticsProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
-{
- // Order is important here. sourceLayoutChanged must be called *before* any downstreams react
- // to the layoutChanged so that it can have the QPersistentModelIndexes uptodate in time.
- disconnect(this, SIGNAL(layoutChanged()), this, SLOT(sourceLayoutChanged()));
- connect(this, SIGNAL(layoutChanged()), SLOT(sourceLayoutChanged()));
- QIdentityProxyModel::setSourceModel(sourceModel);
- // This one should come *after* any downstream handlers of layoutAboutToBeChanged.
- // The connectNotify stuff below ensures that it remains the last one.
- disconnect(this, SIGNAL(layoutAboutToBeChanged()), this, SLOT(sourceLayoutAboutToBeChanged()));
- connect(this, SIGNAL(layoutAboutToBeChanged()), SLOT(sourceLayoutAboutToBeChanged()));
-}
-
-void StatisticsProxyModel::connectNotify(const QMetaMethod &signal)
-{
- static bool ignore = false;
- if (ignore || signal == QMetaMethod::fromSignal(&StatisticsProxyModel::layoutAboutToBeChanged)) {
- return QIdentityProxyModel::connectNotify(signal);
+ KExtraColumnsProxyModel::setSourceModel(model);
+ if (model) {
+ // Disconnect the default handling of dataChanged in QIdentityProxyModel, so we can extend it to the whole row
+ disconnect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)),
+ this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex,QVector<int>)));
+ connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)),
+ this, SLOT(_k_sourceDataChanged(QModelIndex,QModelIndex,QVector<int>)));
}
- ignore = true;
- disconnect(this, SIGNAL(layoutAboutToBeChanged()), this, SLOT(sourceLayoutAboutToBeChanged()));
- connect(this, SIGNAL(layoutAboutToBeChanged()), SLOT(sourceLayoutAboutToBeChanged()));
- ignore = false;
- QIdentityProxyModel::connectNotify(signal);
}
StatisticsProxyModel::StatisticsProxyModel(QObject *parent)
- : QIdentityProxyModel(parent),
+ : KExtraColumnsProxyModel(parent),
d(new Private(this))
{
- connect(this, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
- SLOT(proxyDataChanged(QModelIndex,QModelIndex)));
+ setExtraColumnsEnabled(true);
}
StatisticsProxyModel::~StatisticsProxyModel()
@@ -308,7 +242,20 @@ bool StatisticsProxyModel::isToolTipEnabled() const
void StatisticsProxyModel::setExtraColumnsEnabled(bool enable)
{
+ if (d->mExtraColumnsEnabled == enable)
+ return;
d->mExtraColumnsEnabled = enable;
+ if (enable) {
+ KExtraColumnsProxyModel::appendColumn(i18nc("number of unread entities in the collection", "Unread"));
+ KExtraColumnsProxyModel::appendColumn(i18nc("number of entities in the collection", "Total"));
+ KExtraColumnsProxyModel::appendColumn(i18nc("collection size", "Size"));
+ } else {
+#if KITEMMODELS_VERSION >= QT_VERSION_CHECK(5, 24, 0)
+ KExtraColumnsProxyModel::removeExtraColumn(2);
+ KExtraColumnsProxyModel::removeExtraColumn(1);
+ KExtraColumnsProxyModel::removeExtraColumn(0);
+#endif
+ }
}
bool StatisticsProxyModel::isExtraColumnsEnabled() const
@@ -316,163 +263,71 @@ bool StatisticsProxyModel::isExtraColumnsEnabled() const
return d->mExtraColumnsEnabled;
}
-QModelIndex StatisticsProxyModel::index(int row, int column, const QModelIndex &parent) const
+QVariant StatisticsProxyModel::extraColumnData(const QModelIndex &parent, int row, int extraColumn, int role) const
{
- if (!hasIndex(row, column, parent)) {
- return QModelIndex();
- }
-
- int sourceColumn = column;
- if (column >= d->sourceColumnCount()) {
- sourceColumn = 0;
- }
-
- QModelIndex i = QIdentityProxyModel::index(row, sourceColumn, parent);
- return createIndex(i.row(), column, i.internalPointer());
-}
-
-struct SourceModelIndex {
- SourceModelIndex(int _r, int _c, void *_p, QAbstractItemModel *_m)
- : r(_r), c(_c), p(_p), m(_m) {}
-
- operator QModelIndex()
- {
- return reinterpret_cast<QModelIndex &>(*this);
- }
-
- int r, c;
- void *p;
- const QAbstractItemModel *m;
-};
-
-QModelIndex StatisticsProxyModel::Private::sourceIndexAtFirstColumn(const QModelIndex &proxyIndex) const
-{
- // We rely on the fact that the internal pointer is the same for column 0 and for the extra columns
- return SourceModelIndex(proxyIndex.row(), 0, proxyIndex.internalPointer(), mParent->sourceModel());
-}
-
-QModelIndex StatisticsProxyModel::parent(const QModelIndex &child) const
-{
- if (!sourceModel()) {
- return QModelIndex();
- }
-
- Q_ASSERT(child.isValid() ? child.model() == this : true);
- if (child.column() >= d->sourceColumnCount()) {
- // We need to get hold of the source index at column 0. But we can't do that
- // via the proxy index at column 0, because sibling() or index() needs the
- // parent index, and that's *exactly* what we're trying to determine here.
- // So the only way is to create a source index ourselves.
- const QModelIndex sourceIndex = d->sourceIndexAtFirstColumn(child);
- const QModelIndex sourceParent = sourceIndex.parent();
- //qCDebug(AKONADICORE_LOG) << "parent of" << child.data() << "is" << sourceParent.data();
- return mapFromSource(sourceParent);
- } else {
- return QIdentityProxyModel::parent(child);
- }
-}
-
-QVariant StatisticsProxyModel::data(const QModelIndex &index, int role) const
-{
- if (!sourceModel()) {
- return QVariant();
- }
-
- const int sourceColumnCount = d->sourceColumnCount();
-
- if (role == Qt::DisplayRole && index.column() >= sourceColumnCount) {
- const QModelIndex sourceIndex = d->sourceIndexAtFirstColumn(index);
- Collection collection = sourceModel()->data(sourceIndex, EntityTreeModel::CollectionRole).value<Collection>();
-
+ switch (role) {
+ case Qt::DisplayRole: {
+ const QModelIndex firstColumn = index(row, 0, parent);
+ const Collection collection = data(firstColumn, EntityTreeModel::CollectionRole).value<Collection>();
if (collection.isValid() && collection.statistics().count() >= 0) {
- if (index.column() == sourceColumnCount + 2) {
+ const CollectionStatistics stats = collection.statistics();
+ if (extraColumn == 2) {
KFormat formatter;
- return formatter.formatByteSize(collection.statistics().size());
- } else if (index.column() == sourceColumnCount + 1) {
- return collection.statistics().count();
- } else if (index.column() == sourceColumnCount) {
- if (collection.statistics().unreadCount() > 0) {
- return collection.statistics().unreadCount();
+ return formatter.formatByteSize(stats.size());
+ } else if (extraColumn == 1) {
+ return stats.count();
+ } else if (extraColumn == 0) {
+ if (stats.unreadCount() > 0) {
+ return stats.unreadCount();
} else {
return QString();
}
} else {
qCWarning(AKONADICORE_LOG) << "We shouldn't get there for a column which is not total, unread or size.";
- return QVariant();
}
}
-
- } else if (role == Qt::TextAlignmentRole && index.column() >= sourceColumnCount) {
+ }
+ break;
+ case Qt::TextAlignmentRole: {
return Qt::AlignRight;
-
- } else if (role == Qt::ToolTipRole && d->mToolTipEnabled) {
- const QModelIndex sourceIndex = d->sourceIndexAtFirstColumn(index);
- Collection collection
- = sourceModel()->data(sourceIndex,
- EntityTreeModel::CollectionRole).value<Collection>();
-
- if (collection.isValid()) {
- const QModelIndex sourceIndex = d->sourceIndexAtFirstColumn(index);
- return d->toolTipForCollection(sourceIndex, collection);
- }
-
- } else if (role == Qt::DecorationRole && index.column() == 0) {
- const QModelIndex sourceIndex = mapToSource(index);
- return sourceModel()->data(sourceIndex, Qt::DecorationRole);
}
-
- if (index.column() >= sourceColumnCount) {
- return QVariant();
+ default:
+ break;
}
-
- return QAbstractProxyModel::data(index, role);
+ return QVariant();
}
-QVariant StatisticsProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
+QVariant StatisticsProxyModel::data(const QModelIndex &index, int role) const
{
- if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
- if (section == d->sourceColumnCount() + 2) {
- return i18nc("collection size", "Size");
- } else if (section == d->sourceColumnCount() + 1) {
- return i18nc("number of entities in the collection", "Total");
- } else if (section == d->sourceColumnCount()) {
- return i18nc("number of unread entities in the collection", "Unread");
- }
- }
+ if (role == Qt::ToolTipRole && d->mToolTipEnabled) {
+ const QModelIndex firstColumn = index.sibling(index.row(), 0);
+ const Collection collection = data(firstColumn, EntityTreeModel::CollectionRole).value<Collection>();
- if (orientation == Qt::Horizontal && section >= d->sourceColumnCount()) {
- return QVariant();
+ if (collection.isValid()) {
+ return d->toolTipForCollection(firstColumn, collection);
+ }
}
- return QIdentityProxyModel::headerData(section, orientation, role);
+ return KExtraColumnsProxyModel::data(index, role);
}
Qt::ItemFlags StatisticsProxyModel::flags(const QModelIndex &index_) const
{
if (index_.column() >= d->sourceColumnCount()) {
- return QIdentityProxyModel::flags(index(index_.row(), 0, index_.parent()))
+ return KExtraColumnsProxyModel::flags(index(index_.row(), 0, index_.parent()))
& (Qt::ItemIsSelectable | Qt::ItemIsDragEnabled // Allowed flags
| Qt::ItemIsDropEnabled | Qt::ItemIsEnabled);
}
- return QIdentityProxyModel::flags(index_);
-}
-
-int StatisticsProxyModel::columnCount(const QModelIndex & /*parent*/) const
-{
- if (sourceModel() == 0) {
- return 0;
- } else {
- return d->sourceColumnCount()
- + (d->mExtraColumnsEnabled ? 3 : 0);
- }
+ return KExtraColumnsProxyModel::flags(index_);
}
+// Not sure this is still necessary....
QModelIndexList StatisticsProxyModel::match(const QModelIndex &start, int role, const QVariant &value,
int hits, Qt::MatchFlags flags) const
{
if (role < Qt::UserRole) {
- return QIdentityProxyModel::match(start, role, value, hits, flags);
+ return KExtraColumnsProxyModel::match(start, role, value, hits, flags);
}
QModelIndexList list;
@@ -487,67 +342,5 @@ QModelIndexList StatisticsProxyModel::match(const QModelIndex &start, int role,
return list;
}
-QModelIndex StatisticsProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
-{
- if (!sourceIndex.isValid()) {
- return QModelIndex();
- }
- Q_ASSERT(sourceIndex.model() == sourceModel());
- Q_ASSERT(sourceIndex.column() < d->sourceColumnCount());
- return QIdentityProxyModel::mapFromSource(sourceIndex);
-}
-
-QModelIndex StatisticsProxyModel::mapToSource(const QModelIndex &index) const
-{
- if (!index.isValid()) {
- return QModelIndex();
- }
- Q_ASSERT(index.model() == this);
- if (index.column() >= d->sourceColumnCount()) {
- return QModelIndex();
- }
- return QIdentityProxyModel::mapToSource(index);
-}
-
-QModelIndex StatisticsProxyModel::buddy(const QModelIndex &index) const
-{
- Q_UNUSED(index);
- return QModelIndex();
-}
-
-QItemSelection StatisticsProxyModel::mapSelectionToSource(const QItemSelection &selection) const
-{
- QItemSelection sourceSelection;
-
- if (!sourceModel()) {
- return sourceSelection;
- }
-
- // mapToSource will give invalid index for our additional columns, so truncate the selection
- // to the columns known by the source model
- const int sourceColumnCount = d->sourceColumnCount();
- QItemSelection::const_iterator it = selection.constBegin();
- const QItemSelection::const_iterator end = selection.constEnd();
- for (; it != end; ++it) {
- Q_ASSERT(it->model() == this);
- QModelIndex topLeft = it->topLeft();
- Q_ASSERT(topLeft.isValid());
- Q_ASSERT(topLeft.model() == this);
- topLeft = topLeft.sibling(topLeft.row(), 0);
- QModelIndex bottomRight = it->bottomRight();
- Q_ASSERT(bottomRight.isValid());
- Q_ASSERT(bottomRight.model() == this);
- if (bottomRight.column() >= sourceColumnCount) {
- bottomRight = bottomRight.sibling(bottomRight.row(), sourceColumnCount - 1);
- }
- // This can lead to duplicate source indexes, so use merge().
- const QItemSelectionRange range(mapToSource(topLeft), mapToSource(bottomRight));
- QItemSelection newSelection; newSelection << range;
- sourceSelection.merge(newSelection, QItemSelectionModel::Select);
- }
-
- return sourceSelection;
-}
-
#include "moc_statisticsproxymodel.cpp"
diff --git a/src/core/models/statisticsproxymodel.h b/src/core/models/statisticsproxymodel.h
index e189d9c..e9ee502 100644
--- a/src/core/models/statisticsproxymodel.h
+++ b/src/core/models/statisticsproxymodel.h
@@ -1,5 +1,6 @@
/*
Copyright (c) 2009 Kevin Ottens <[email protected]>
+ 2016 David Faure <[email protected]>s
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
@@ -22,7 +23,7 @@
#include "akonadicore_export.h"
-#include <QIdentityProxyModel>
+#include <KExtraColumnsProxyModel>
namespace Akonadi
{
@@ -45,10 +46,10 @@ namespace Akonadi
*
* @endcode
*
- * @author Kevin Ottens <[email protected]>
+ * @author Kevin Ottens <[email protected]>, now maintained by David Faure <[email protected]>
* @since 4.4
*/
-class AKONADICORE_EXPORT StatisticsProxyModel : public QIdentityProxyModel
+class AKONADICORE_EXPORT StatisticsProxyModel : public KExtraColumnsProxyModel
{
Q_OBJECT
@@ -67,6 +68,7 @@ public:
/**
* @param enable Display tooltips
+ * By default, tooltips are disabled.
*/
void setToolTipEnabled(bool enable);
@@ -77,6 +79,7 @@ public:
/**
* @param enable Display extra statistics columns
+ * By default, the extra columns are enabled.
*/
void setExtraColumnsEnabled(bool enable);
@@ -85,33 +88,21 @@ public:
*/
bool isExtraColumnsEnabled() const;
- QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
- QModelIndex parent(const QModelIndex &child) const Q_DECL_OVERRIDE;
+ QVariant extraColumnData(const QModelIndex &parent, int row, int extraColumn, int role) const Q_DECL_OVERRIDE;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
- QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
- int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
virtual QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits = 1,
Qt::MatchFlags flags = Qt::MatchFlags(Qt::MatchStartsWith | Qt::MatchWrap)) const Q_DECL_OVERRIDE;
- void setSourceModel(QAbstractItemModel *sourceModel) Q_DECL_OVERRIDE;
- void connectNotify(const QMetaMethod &signal) Q_DECL_OVERRIDE;
-
- QModelIndex mapFromSource(const QModelIndex &sourceIndex) const Q_DECL_OVERRIDE;
- QModelIndex mapToSource(const QModelIndex &sourceIndex) const Q_DECL_OVERRIDE;
- QModelIndex buddy(const QModelIndex &index) const Q_DECL_OVERRIDE;
-
- QItemSelection mapSelectionToSource(const QItemSelection &selection) const Q_DECL_OVERRIDE;
+ void setSourceModel(QAbstractItemModel *model) Q_DECL_OVERRIDE;
private:
//@cond PRIVATE
class Private;
Private *const d;
- Q_PRIVATE_SLOT(d, void proxyDataChanged(QModelIndex, QModelIndex))
- Q_PRIVATE_SLOT(d, void sourceLayoutAboutToBeChanged())
- Q_PRIVATE_SLOT(d, void sourceLayoutChanged())
+ Q_PRIVATE_SLOT(d, void _k_sourceDataChanged(QModelIndex, QModelIndex, QVector<int>))
//@endcond
};