[SCM] KDE Development Platform Libraries module packaging branch, kubuntu_vivid, updated. debian/4.14.2-3-5-ge3a24c3
Harald Sitter
apachelogger-guest at moszumanska.debian.org
Thu Jan 29 12:50:48 UTC 2015
Gitweb-URL: http://git.debian.org/?p=pkg-kde/kde-sc/kde4libs.git;a=commitdiff;h=e3a24c3
The following commit has been merged in the kubuntu_vivid branch:
commit e3a24c3ef2d4ca78a8d21f074ce86cafda4875ef
Author: Harald Sitter <sitter at kde.org>
Date: Thu Jan 29 13:50:42 2015 +0100
Add upstream_KRecursiveFilterProxyModel-Fixed-the-model.patch
from upstream fixing incorrect filtering in a number of cases LP: #1415291
---
debian/changelog | 8 +
debian/patches/series | 1 +
...RecursiveFilterProxyModel-Fixed-the-model.patch | 370 +++++++++++++++++++++
3 files changed, 379 insertions(+)
diff --git a/debian/changelog b/debian/changelog
index bd89de4..ccbd304 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+kde4libs (4:4.14.2-4~ubuntu4) vivid; urgency=medium
+
+ * Add upstream_KRecursiveFilterProxyModel-Fixed-the-model.patch
+ from upstream fixing incorrect filtering in a number of cases
+ LP: #1415291
+
+ -- Harald Sitter <sitter at kde.org> Thu, 29 Jan 2015 11:21:56 +0100
+
kde4libs (4:4.14.2-4~ubuntu3) vivid; urgency=medium
* Readd kdelibs5-plugins depends on katepart
diff --git a/debian/patches/series b/debian/patches/series
index 99cfd52..229e67a 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -33,3 +33,4 @@ kubuntu_langpack_desktop_files.diff
kubuntu_patched_l10n.diff
kubuntu_raise_after_drkonqi.patch
kubuntu_revert_findpythonlibrary.diff
+upstream_KRecursiveFilterProxyModel-Fixed-the-model.patch
diff --git a/debian/patches/upstream_KRecursiveFilterProxyModel-Fixed-the-model.patch b/debian/patches/upstream_KRecursiveFilterProxyModel-Fixed-the-model.patch
new file mode 100644
index 0000000..199df67
--- /dev/null
+++ b/debian/patches/upstream_KRecursiveFilterProxyModel-Fixed-the-model.patch
@@ -0,0 +1,370 @@
+From 0df92439241a76c6a67efa9485bd95c3c25d63a0 Mon Sep 17 00:00:00 2001
+From: Christian Mollekopf <chrigi_1 at fastmail.fm>
+Date: Thu, 22 Jan 2015 15:04:16 +0100
+Subject: [PATCH] KRecursiveFilterProxyModel: Fixed the model
+
+The model was not working properly and didn't include all items under
+some circumstances.
+This patch fixes the following scenarios in particular:
+
+* The change in sourceDataChanged is required to fix the shortcut condition.
+The idea is that if the parent is already part of the model (it must be if acceptRow returns true),
+we can directly invoke dataChanged on the parent, resulting in the changed index
+getting reevaluated. However, because the recursive filterAcceptsRow version was used
+the shortcut was also used when only the current index matches the filter and
+the parent index is in fact not yet in the model. In this case we failed to call
+dataChanged on the right index and thus the complete branch was never added to the model.
+
+* The change in refreshAscendantMapping is required to include indexes that were
+included by descendants. The intended way how this was supposed to work is that we
+traverse the tree upwards and find the last index that is not yet part of the model.
+We would then call dataChanged on that index causing it and its descendants to get reevaluated.
+However, acceptRow does not reflect wether an index is already in the model or not.
+Consider the following model:
+
+- A
+ - B
+ - C
+ - D
+
+If C is included in the model by default but D not, and A & B only get included due to C, we have the following model:
+
+- A
+ - B
+ - C
+
+If we then call refreshAscendantsMapping on D it will not consider B as already being part of the model.
+This results in the toplevel index A being considered lastAscendant, and a call to dataChanged on A results in
+a reevaluation of A only, which is already in the model. Thus D never gets added to the model.
+
+Unfortunately there is no way to probe QSortFilterProxyModel for indexes that are
+already part of the model. Even the const mapFromSource internally creates a mapping when called,
+and thus instead of revealing indexes that are not yet part of the model, it silently
+creates a mapping (without issuing the relevant signals!).
+
+As the only possible workaround we have to issues dataChanged for all ancestors
+which is ignored for indexes that are not yet mapped, and results in a rowsInserted
+signal for the correct indexes. It also results in superfluous dataChanged signals,
+since we don't know when to stop, but at least we have a properly behaving model
+this way.
+
+REVIEW: 120119
+BUG: 338950
+---
+ kdeui/itemviews/krecursivefilterproxymodel.cpp | 28 +---
+ kdeui/tests/CMakeLists.txt | 1 +
+ kdeui/tests/krecursivefilterproxymodeltest.cpp | 220 +++++++++++++++++++++++++
+ 3 files changed, 230 insertions(+), 19 deletions(-)
+ create mode 100644 kdeui/tests/krecursivefilterproxymodeltest.cpp
+
+diff --git a/kdeui/itemviews/krecursivefilterproxymodel.cpp b/kdeui/itemviews/krecursivefilterproxymodel.cpp
+index 6d65631..efa286a 100644
+--- a/kdeui/itemviews/krecursivefilterproxymodel.cpp
++++ b/kdeui/itemviews/krecursivefilterproxymodel.cpp
+@@ -108,12 +108,9 @@ public:
+ void sourceRowsRemoved(const QModelIndex &source_parent, int start, int end);
+
+ /**
+- Given that @p index does not match the filter, clear mappings in the QSortFilterProxyModel up to and excluding the
+- first ascendant that does match, and remake the mappings.
+-
+- If @p refreshAll is true, this method also refreshes intermediate mappings. This is significant when removing rows.
++ Given that @p index does not match the filter, clear mappings in the QSortFilterProxyModel up to roo, and remake the mappings.
+ */
+- void refreshAscendantMapping(const QModelIndex &index, bool refreshAll = false);
++ void refreshAscendantMapping(const QModelIndex &index);
+
+ bool ignoreRemove;
+ bool completeInsert;
+@@ -126,7 +123,7 @@ void KRecursiveFilterProxyModelPrivate::sourceDataChanged(const QModelIndex &sou
+
+ QModelIndex source_parent = source_top_left.parent();
+
+- if (!source_parent.isValid() || q->filterAcceptsRow(source_parent.row(), source_parent.parent()))
++ if (!source_parent.isValid() || q->acceptRow(source_parent.row(), source_parent.parent()))
+ {
+ invokeDataChanged(source_top_left, source_bottom_right);
+ return;
+@@ -146,27 +143,20 @@ void KRecursiveFilterProxyModelPrivate::sourceDataChanged(const QModelIndex &sou
+ refreshAscendantMapping(source_parent);
+ }
+
+-void KRecursiveFilterProxyModelPrivate::refreshAscendantMapping(const QModelIndex &index, bool refreshAll)
++void KRecursiveFilterProxyModelPrivate::refreshAscendantMapping(const QModelIndex &index)
+ {
+ Q_Q(KRecursiveFilterProxyModel);
+-
+ Q_ASSERT(index.isValid());
+- QModelIndex lastAscendant = index;
+- QModelIndex sourceAscendant = index.parent();
++
++ QModelIndex sourceAscendant = index;
+ // We got a matching descendant, so find the right place to insert the row.
+ // We need to tell the QSortFilterProxyModel that the first child between an existing row in the model
+ // has changed data so that it will get a mapping.
+- while(sourceAscendant.isValid() && !q->acceptRow(sourceAscendant.row(), sourceAscendant.parent()))
++ while(sourceAscendant.isValid())
+ {
+- if (refreshAll)
+- invokeDataChanged(sourceAscendant, sourceAscendant);
+-
+- lastAscendant = sourceAscendant;
++ invokeDataChanged(sourceAscendant, sourceAscendant);
+ sourceAscendant = sourceAscendant.parent();
+ }
+-
+- // Inform the model that its data changed so that it creates new mappings and finds the rows which now match the filter.
+- invokeDataChanged(lastAscendant, lastAscendant);
+ }
+
+ void KRecursiveFilterProxyModelPrivate::sourceRowsAboutToBeInserted(const QModelIndex &source_parent, int start, int end)
+@@ -261,7 +251,7 @@ void KRecursiveFilterProxyModelPrivate::sourceRowsRemoved(const QModelIndex &sou
+ // This is needed because QSFPM only invalidates the mapping for the
+ // index range given to dataChanged, not its children.
+ if (source_parent.isValid())
+- refreshAscendantMapping(source_parent, true);
++ refreshAscendantMapping(source_parent);
+ }
+
+ KRecursiveFilterProxyModel::KRecursiveFilterProxyModel(QObject* parent)
+diff --git a/kdeui/tests/CMakeLists.txt b/kdeui/tests/CMakeLists.txt
+index 6ad1b21..8a2e519 100644
+--- a/kdeui/tests/CMakeLists.txt
++++ b/kdeui/tests/CMakeLists.txt
+@@ -82,6 +82,7 @@ KDEUI_PROXYMODEL_TESTS(
+ kdescendantsproxymodeltest
+ kselectionproxymodeltest
+ testmodelqueuedconnections
++ krecursivefilterproxymodeltest
+ )
+
+ KDEUI_EXECUTABLE_TESTS(
+diff --git a/kdeui/tests/krecursivefilterproxymodeltest.cpp b/kdeui/tests/krecursivefilterproxymodeltest.cpp
+new file mode 100644
+index 0000000..3bcb729
+--- /dev/null
++++ b/kdeui/tests/krecursivefilterproxymodeltest.cpp
+@@ -0,0 +1,220 @@
++/*
++ Copyright (c) 2014 Christian Mollekopf <mollekopf at kolabsys.com>
++
++ 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 <qtest_kde.h>
++
++#include <krecursivefilterproxymodel.h>
++#include <QStandardItemModel>
++
++class ModelSignalSpy : public QObject {
++ Q_OBJECT
++public:
++ explicit ModelSignalSpy(QAbstractItemModel &model) {
++ connect(&model, SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(onRowsInserted(QModelIndex,int,int)));
++ connect(&model, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(onRowsRemoved(QModelIndex,int,int)));
++ connect(&model, SIGNAL(rowsMoved(QModelIndex, int, int, QModelIndex, int)), this, SLOT(onRowsMoved(QModelIndex,int,int, QModelIndex, int)));
++ connect(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(onDataChanged(QModelIndex,QModelIndex)));
++ connect(&model, SIGNAL(layoutChanged()), this, SLOT(onLayoutChanged()));
++ connect(&model, SIGNAL(modelReset()), this, SLOT(onModelReset()));
++ }
++
++ QStringList mSignals;
++ QModelIndex parent;
++ int start;
++ int end;
++
++public Q_SLOTS:
++ void onRowsInserted(QModelIndex p, int s, int e) {
++ mSignals << QLatin1String("rowsInserted");
++ parent = p;
++ start = s;
++ end = e;
++ }
++ void onRowsRemoved(QModelIndex p, int s, int e) {
++ mSignals << QLatin1String("rowsRemoved");
++ parent = p;
++ start = s;
++ end = e;
++ }
++ void onRowsMoved(QModelIndex,int,int,QModelIndex,int) {
++ mSignals << QLatin1String("rowsMoved");
++ }
++ void onDataChanged(QModelIndex,QModelIndex) {
++ mSignals << QLatin1String("dataChanged");
++ }
++ void onLayoutChanged() {
++ mSignals << QLatin1String("layoutChanged");
++ }
++ void onModelReset() {
++ mSignals << QLatin1String("modelReset");
++ }
++};
++
++class TestModel : public KRecursiveFilterProxyModel
++{
++ Q_OBJECT
++public:
++ virtual bool acceptRow(int sourceRow, const QModelIndex &sourceParent) const
++ {
++ // qDebug() << sourceModel()->index(sourceRow, 0, sourceParent).data().toString() << sourceModel()->index(sourceRow, 0, sourceParent).data(Qt::UserRole+1).toBool();
++ return sourceModel()->index(sourceRow, 0, sourceParent).data(Qt::UserRole+1).toBool();
++ }
++};
++
++static QModelIndex getIndex(const char *string, const QAbstractItemModel &model)
++{
++ QModelIndexList list = model.match(model.index(0, 0), Qt::DisplayRole, QString::fromLatin1(string), 1, Qt::MatchRecursive);
++ if (list.isEmpty()) {
++ return QModelIndex();
++ }
++ return list.first();
++}
++
++class KRecursiveFilterProxyModelTest : public QObject
++{
++ Q_OBJECT
++private:
++
++private slots:
++ // Test that we properly react to a data-changed signal in a descendant and include all required rows
++ void testDataChange()
++ {
++ QStandardItemModel model;
++ TestModel proxy;
++ proxy.setSourceModel(&model);
++
++ QStandardItem *index1 = new QStandardItem("1");
++ index1->setData(false);
++ model.appendRow(index1);
++
++ QVERIFY(!getIndex("1", proxy).isValid());
++
++ QStandardItem *index1_1_1 = new QStandardItem("1.1.1");
++ index1_1_1->setData(false);
++ QStandardItem *index1_1 = new QStandardItem("1.1");
++ index1_1->setData(false);
++ index1_1->appendRow(index1_1_1);
++ index1->appendRow(index1_1);
++
++ ModelSignalSpy spy(proxy);
++ index1_1_1->setData(true);
++
++ QVERIFY(getIndex("1", proxy).isValid());
++ QVERIFY(getIndex("1.1", proxy).isValid());
++ QVERIFY(getIndex("1.1.1", proxy).isValid());
++
++ QCOMPARE(spy.mSignals, QStringList() << QLatin1String("rowsInserted"));
++ }
++
++ void testInsert()
++ {
++ QStandardItemModel model;
++ TestModel proxy;
++ proxy.setSourceModel(&model);
++
++ QStandardItem *index1 = new QStandardItem("index1");
++ index1->setData(false);
++ model.appendRow(index1);
++
++ QStandardItem *index1_1 = new QStandardItem("index1_1");
++ index1_1->setData(false);
++ index1->appendRow(index1_1);
++
++ QStandardItem *index1_1_1 = new QStandardItem("index1_1_1");
++ index1_1_1->setData(false);
++ index1_1->appendRow(index1_1_1);
++
++ QVERIFY(!getIndex("index1", proxy).isValid());
++ QVERIFY(!getIndex("index1_1", proxy).isValid());
++ QVERIFY(!getIndex("index1_1_1", proxy).isValid());
++
++ ModelSignalSpy spy(proxy);
++ {
++ QStandardItem *index1_1_1_1 = new QStandardItem("index1_1_1_1");
++ index1_1_1_1->setData(true);
++ index1_1_1->appendRow(index1_1_1_1);
++ }
++
++ QVERIFY(getIndex("index1", proxy).isValid());
++ QVERIFY(getIndex("index1_1", proxy).isValid());
++ QVERIFY(getIndex("index1_1_1", proxy).isValid());
++ QVERIFY(getIndex("index1_1_1_1", proxy).isValid());
++ QCOMPARE(spy.mSignals, QStringList() << QLatin1String("rowsInserted"));
++ QCOMPARE(spy.parent, QModelIndex());
++ }
++
++
++ // We want to get index1_1_1_1 into the model which is a descendant of index1_1.
++ // index1_1 is already in the model from the neighbor2 branch. We must ensure dataChange is called on index1_1,
++ // so index1_1_1_1 is included in the model.
++ void testNeighborPath()
++ {
++ QStandardItemModel model;
++ TestModel proxy;
++ proxy.setSourceModel(&model);
++
++ QStandardItem *index1 = new QStandardItem("index1");
++ index1->setData(false);
++ model.appendRow(index1);
++
++ QStandardItem *index1_1 = new QStandardItem("index1_1");
++ index1_1->setData(false);
++ index1->appendRow(index1_1);
++
++ QStandardItem *index1_1_1 = new QStandardItem("index1_1_1");
++ index1_1_1->setData(false);
++ index1_1->appendRow(index1_1_1);
++
++ {
++ QStandardItem *nb1 = new QStandardItem("neighbor");
++ nb1->setData(false);
++ index1_1->appendRow(nb1);
++
++ QStandardItem *nb2 = new QStandardItem("neighbor2");
++ nb2->setData(true);
++ nb1->appendRow(nb2);
++ }
++
++ //These tests affect the test. It seems without them the mapping is not created in qsortfilterproxymodel, resulting in the item
++ //simply getting added later on. With these the model didn't react to the added index1_1_1_1 as it should.
++ QVERIFY(!getIndex("index1_1_1", proxy).isValid());
++ QVERIFY(getIndex("index1_1", proxy).isValid());
++ QVERIFY(getIndex("neighbor", proxy).isValid());
++ QVERIFY(getIndex("neighbor2", proxy).isValid());
++
++ ModelSignalSpy spy(proxy);
++
++ {
++ QStandardItem *index1_1_1_1 = new QStandardItem("index1_1_1_1");
++ index1_1_1_1->setData(true);
++ index1_1_1->appendRow(index1_1_1_1);
++ }
++
++ QVERIFY(getIndex("index1_1_1", proxy).isValid());
++ QVERIFY(getIndex("index1_1_1_1", proxy).isValid());
++ //The dataChanged signals are not intentional and caused by refreshAscendantMapping. Unfortunately we can't avoid them.
++ QCOMPARE(spy.mSignals, QStringList() << QLatin1String("rowsInserted") << QLatin1String("dataChanged") << QLatin1String("dataChanged"));
++ }
++
++};
++
++QTEST_KDEMAIN(KRecursiveFilterProxyModelTest, NoGUI)
++
++#include "krecursivefilterproxymodeltest.moc"
+--
+2.1.0
+
--
KDE Development Platform Libraries module packaging
More information about the pkg-kde-commits
mailing list