Hello community, here is the log from the commit of package kitemmodels for openSUSE:Factory checked in at 2015-09-24 07:12:26 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/kitemmodels (Old) and /work/SRC/openSUSE:Factory/.kitemmodels.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "kitemmodels" Changes: -------- --- /work/SRC/openSUSE:Factory/kitemmodels/kitemmodels.changes 2015-09-02 07:46:40.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.kitemmodels.new/kitemmodels.changes 2015-09-24 07:12:27.000000000 +0200 @@ -1,0 +2,13 @@ +Tue Sep 8 17:13:15 UTC 2015 - hrvoje.sen...@gmail.com + +- Update to 5.14.0 + * New proxymodel: KConcatenateRowsProxyModel + * KConcatenateRowsProxyModelPrivate: fix handling of + layoutChanged. + * More checking on the selection after sorting. + * KExtraColumnsProxyModel: fix bug in sibling() which broke + e.g. selections + * For more details please see: + https://www.kde.org/announcements/kde-frameworks-5.14.0.php + +------------------------------------------------------------------- Old: ---- kitemmodels-5.13.0.tar.xz New: ---- kitemmodels-5.14.0.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ kitemmodels.spec ++++++ --- /var/tmp/diff_new_pack.hhXLO8/_old 2015-09-24 07:12:28.000000000 +0200 +++ /var/tmp/diff_new_pack.hhXLO8/_new 2015-09-24 07:12:28.000000000 +0200 @@ -17,9 +17,9 @@ %define lname libKF5ItemModels5 -%define _tar_path 5.13 +%define _tar_path 5.14 Name: kitemmodels -Version: 5.13.0 +Version: 5.14.0 Release: 0 BuildRequires: cmake >= 2.8.12 BuildRequires: extra-cmake-modules >= %{_tar_path} ++++++ kitemmodels-5.13.0.tar.xz -> kitemmodels-5.14.0.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.13.0/CMakeLists.txt new/kitemmodels-5.14.0/CMakeLists.txt --- old/kitemmodels-5.13.0/CMakeLists.txt 2015-08-01 14:41:18.000000000 +0200 +++ new/kitemmodels-5.14.0/CMakeLists.txt 2015-09-04 22:14:38.000000000 +0200 @@ -3,7 +3,7 @@ project(KItemModels) include(FeatureSummary) -find_package(ECM 5.13.0 NO_MODULE) +find_package(ECM 5.14.0 NO_MODULE) set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://projects.kde.org/projects/kdesupport/extra-cmake-modules") feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES) @@ -23,7 +23,7 @@ include(ECMSetupVersion) include(ECMGenerateHeaders) -set(KF5_VERSION "5.13.0") # handled by release scripts +set(KF5_VERSION "5.14.0") # handled by release scripts ecm_setup_version(${KF5_VERSION} VARIABLE_PREFIX KITEMMODELS VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kitemmodels_version.h" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.13.0/README.md new/kitemmodels-5.14.0/README.md --- old/kitemmodels-5.13.0/README.md 2015-08-01 14:41:18.000000000 +0200 +++ new/kitemmodels-5.14.0/README.md 2015-09-04 22:14:38.000000000 +0200 @@ -9,6 +9,7 @@ * KBreadcrumbSelectionModel - Selects the parents of selected items to create breadcrumbs * KCheckableProxyModel - Adds a checkable capability to a source model +* KConcatenateRowsProxyModel - Concatenates rows from multiple source models * KDescendantsProxyModel - Proxy Model for restructuring a Tree into a list * KExtraColumnsProxyModel - Adds columns after existing columns * KLinkItemSelectionModel - Share a selection in multiple views which do not diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.13.0/autotests/CMakeLists.txt new/kitemmodels-5.14.0/autotests/CMakeLists.txt --- old/kitemmodels-5.13.0/autotests/CMakeLists.txt 2015-08-01 14:41:18.000000000 +0200 +++ new/kitemmodels-5.14.0/autotests/CMakeLists.txt 2015-09-04 22:14:38.000000000 +0200 @@ -15,6 +15,7 @@ include(ECMAddTests) ecm_add_tests( + kconcatenaterowsproxymodeltest.cpp kdescendantsproxymodel_smoketest.cpp kextracolumnsproxymodeltest.cpp klinkitemselectionmodeltest.cpp diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.13.0/autotests/kconcatenaterowsproxymodeltest.cpp new/kitemmodels-5.14.0/autotests/kconcatenaterowsproxymodeltest.cpp --- old/kitemmodels-5.13.0/autotests/kconcatenaterowsproxymodeltest.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/kitemmodels-5.14.0/autotests/kconcatenaterowsproxymodeltest.cpp 2015-09-04 22:14:38.000000000 +0200 @@ -0,0 +1,425 @@ +#include <QSignalSpy> +#include <QSortFilterProxyModel> +#include <QTest> +#include <QStandardItemModel> +#include <QIdentityProxyModel> +#include <QItemSelectionModel> + +#include <kconcatenaterowsproxymodel.h> +#include "test_model_helpers.h" +using namespace TestModelHelpers; + +Q_DECLARE_METATYPE(QModelIndex) + +class tst_KConcatenateRowsProxyModel : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + + void initTestCase() + { + } + + void init() + { + // Prepare some source models to use later on + mod.clear(); + mod.insertRow(0, makeStandardItems(QStringList() << "A" << "B" << "C")); + mod.setHorizontalHeaderLabels(QStringList() << "H1" << "H2" << "H3"); + mod.setVerticalHeaderLabels(QStringList() << "One"); + + mod2.clear(); + mod2.insertRow(0, makeStandardItems(QStringList() << "D" << "E" << "F")); + mod2.setHorizontalHeaderLabels(QStringList() << "H1" << "H2" << "H3"); + mod2.setVerticalHeaderLabels(QStringList() << "Two"); + } + + void shouldAggregateTwoModelsCorrectly() + { + // Given a combining proxy + KConcatenateRowsProxyModel pm; + + // When adding two source models + pm.addSourceModel(&mod); + pm.addSourceModel(&mod2); + + // Then the proxy should show 2 rows + QCOMPARE(pm.rowCount(), 2); + QCOMPARE(extractRowTexts(&pm, 0), QString("ABC")); + QCOMPARE(extractRowTexts(&pm, 1), QString("DEF")); + + // ... and correct headers + QCOMPARE(pm.headerData(0, Qt::Horizontal).toString(), QString("H1")); + QCOMPARE(pm.headerData(1, Qt::Horizontal).toString(), QString("H2")); + QCOMPARE(pm.headerData(2, Qt::Horizontal).toString(), QString("H3")); + QCOMPARE(pm.headerData(0, Qt::Vertical).toString(), QString("One")); + QCOMPARE(pm.headerData(1, Qt::Vertical).toString(), QString("Two")); + + QVERIFY(!pm.canFetchMore(QModelIndex())); + } + + void shouldAggregateThenRemoveTwoEmptyModelsCorrectly() + { + // Given a combining proxy + KConcatenateRowsProxyModel pm; + + // When adding two empty models + QSignalSpy rowATBISpy(&pm, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int))); + QSignalSpy rowInsertedSpy(&pm, SIGNAL(rowsInserted(QModelIndex,int,int))); + QSignalSpy rowATBRSpy(&pm, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int))); + QSignalSpy rowRemovedSpy(&pm, SIGNAL(rowsRemoved(QModelIndex,int,int))); + QIdentityProxyModel i1, i2; + pm.addSourceModel(&i1); + pm.addSourceModel(&i2); + + // Then the proxy should still be empty (and no signals emitted) + QCOMPARE(pm.rowCount(), 0); + QCOMPARE(pm.columnCount(), 0); + QCOMPARE(rowATBISpy.count(), 0); + QCOMPARE(rowInsertedSpy.count(), 0); + + // When removing the empty models + pm.removeSourceModel(&i1); + pm.removeSourceModel(&i2); + + // Then the proxy should still be empty (and no signals emitted) + QCOMPARE(pm.rowCount(), 0); + QCOMPARE(pm.columnCount(), 0); + QCOMPARE(rowATBRSpy.count(), 0); + QCOMPARE(rowRemovedSpy.count(), 0); + } + + void shouldAggregateTwoEmptyModelsWhichThenGetFilled() + { + // Given a combining proxy + KConcatenateRowsProxyModel pm; + + // When adding two empty models + QIdentityProxyModel i1, i2; + pm.addSourceModel(&i1); + pm.addSourceModel(&i2); + + QCOMPARE(pm.rowCount(), 0); + QCOMPARE(pm.columnCount(), 0); + + i1.setSourceModel(&mod); + i2.setSourceModel(&mod2); + + // Then the proxy should show 2 rows + QCOMPARE(pm.rowCount(), 2); + QCOMPARE(extractRowTexts(&pm, 0), QString("ABC")); + QCOMPARE(extractRowTexts(&pm, 1), QString("DEF")); + + // ... and correct headers + QCOMPARE(pm.headerData(0, Qt::Horizontal).toString(), QString("H1")); + QCOMPARE(pm.headerData(1, Qt::Horizontal).toString(), QString("H2")); + QCOMPARE(pm.headerData(2, Qt::Horizontal).toString(), QString("H3")); + QCOMPARE(pm.headerData(0, Qt::Vertical).toString(), QString("One")); + QCOMPARE(pm.headerData(1, Qt::Vertical).toString(), QString("Two")); + + QVERIFY(!pm.canFetchMore(QModelIndex())); + } + + void shouldHandleDataChanged() + { + // Given two models combined + KConcatenateRowsProxyModel pm; + pm.addSourceModel(&mod); + pm.addSourceModel(&mod2); + QSignalSpy dataChangedSpy(&pm, SIGNAL(dataChanged(QModelIndex,QModelIndex))); + + // When a cell in a source model changes + mod.item(0, 0)->setData("a", Qt::EditRole); + + // Then the change should be notified to the proxy + QCOMPARE(dataChangedSpy.count(), 1); + QCOMPARE(dataChangedSpy.at(0).at(0).value<QModelIndex>(), pm.index(0, 0)); + QCOMPARE(extractRowTexts(&pm, 0), QString("aBC")); + + // Same test with the other model + mod2.item(0, 2)->setData("f", Qt::EditRole); + + QCOMPARE(dataChangedSpy.count(), 2); + QCOMPARE(dataChangedSpy.at(1).at(0).value<QModelIndex>(), pm.index(1, 2)); + QCOMPARE(extractRowTexts(&pm, 1), QString("DEf")); + } + + void shouldHandleSetData() + { + // Given two models combined + KConcatenateRowsProxyModel pm; + pm.addSourceModel(&mod); + pm.addSourceModel(&mod2); + QSignalSpy dataChangedSpy(&pm, SIGNAL(dataChanged(QModelIndex,QModelIndex))); + + // When changing a cell using setData + pm.setData(pm.index(0, 0), "a", Qt::EditRole); + + // Then the change should be notified to the proxy + QCOMPARE(dataChangedSpy.count(), 1); + QCOMPARE(dataChangedSpy.at(0).at(0).value<QModelIndex>(), pm.index(0, 0)); + QCOMPARE(extractRowTexts(&pm, 0), QString("aBC")); + + // Same test with the other model + pm.setData(pm.index(1, 2), "f", Qt::EditRole); + + QCOMPARE(dataChangedSpy.count(), 2); + QCOMPARE(dataChangedSpy.at(1).at(0).value<QModelIndex>(), pm.index(1, 2)); + QCOMPARE(extractRowTexts(&pm, 1), QString("DEf")); + } + + void shouldHandleRowInsertionAndRemoval() + { + // Given two models combined + KConcatenateRowsProxyModel pm; + pm.addSourceModel(&mod); + pm.addSourceModel(&mod2); + QSignalSpy rowATBISpy(&pm, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int))); + QSignalSpy rowInsertedSpy(&pm, SIGNAL(rowsInserted(QModelIndex,int,int))); + + // When a source model inserts a new row + QList<QStandardItem *> row; + row.append(new QStandardItem("1")); + row.append(new QStandardItem("2")); + row.append(new QStandardItem("3")); + mod2.insertRow(0, row); + + // Then the proxy should notify its users and show changes + QCOMPARE(rowSpyToText(rowATBISpy), QString("1,1")); + QCOMPARE(rowSpyToText(rowInsertedSpy), QString("1,1")); + QCOMPARE(pm.rowCount(), 3); + QCOMPARE(extractRowTexts(&pm, 0), QString("ABC")); + QCOMPARE(extractRowTexts(&pm, 1), QString("123")); + QCOMPARE(extractRowTexts(&pm, 2), QString("DEF")); + + // When removing that row + QSignalSpy rowATBRSpy(&pm, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int))); + QSignalSpy rowRemovedSpy(&pm, SIGNAL(rowsRemoved(QModelIndex,int,int))); + mod2.removeRow(0); + + // Then the proxy should notify its users and show changes + QCOMPARE(rowATBRSpy.count(), 1); + QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 1); + QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 1); + QCOMPARE(rowRemovedSpy.count(), 1); + QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 1); + QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 1); + QCOMPARE(pm.rowCount(), 2); + QCOMPARE(extractRowTexts(&pm, 0), QString("ABC")); + QCOMPARE(extractRowTexts(&pm, 1), QString("DEF")); + + // When removing the last row from mod2 + rowATBRSpy.clear(); + rowRemovedSpy.clear(); + mod2.removeRow(0); + + // Then the proxy should notify its users and show changes + QCOMPARE(rowATBRSpy.count(), 1); + QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 1); + QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 1); + QCOMPARE(rowRemovedSpy.count(), 1); + QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 1); + QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 1); + QCOMPARE(pm.rowCount(), 1); + QCOMPARE(extractRowTexts(&pm, 0), QString("ABC")); + } + + void shouldAggregateAnotherModelThenRemoveModels() + { + // Given two models combined, and a third model + KConcatenateRowsProxyModel pm; + pm.addSourceModel(&mod); + pm.addSourceModel(&mod2); + + QStandardItemModel mod3; + mod3.appendRow(makeStandardItems(QStringList() << "1" << "2" << "3")); + mod3.appendRow(makeStandardItems(QStringList() << "4" << "5" << "6")); + + QSignalSpy rowATBISpy(&pm, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int))); + QSignalSpy rowInsertedSpy(&pm, SIGNAL(rowsInserted(QModelIndex,int,int))); + + // When adding the new source model + pm.addSourceModel(&mod3); + + // Then the proxy should notify its users about the two rows inserted + QCOMPARE(rowSpyToText(rowATBISpy), QString("2,3")); + QCOMPARE(rowSpyToText(rowInsertedSpy), QString("2,3")); + QCOMPARE(pm.rowCount(), 4); + QCOMPARE(extractRowTexts(&pm, 0), QString("ABC")); + QCOMPARE(extractRowTexts(&pm, 1), QString("DEF")); + QCOMPARE(extractRowTexts(&pm, 2), QString("123")); + QCOMPARE(extractRowTexts(&pm, 3), QString("456")); + + // When removing that source model again + QSignalSpy rowATBRSpy(&pm, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int))); + QSignalSpy rowRemovedSpy(&pm, SIGNAL(rowsRemoved(QModelIndex,int,int))); + pm.removeSourceModel(&mod3); + + // Then the proxy should notify its users about the row removed + QCOMPARE(rowATBRSpy.count(), 1); + QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 2); + QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 3); + QCOMPARE(rowRemovedSpy.count(), 1); + QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 2); + QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 3); + QCOMPARE(pm.rowCount(), 2); + QCOMPARE(extractRowTexts(&pm, 0), QString("ABC")); + QCOMPARE(extractRowTexts(&pm, 1), QString("DEF")); + + // When removing model 2 + rowATBRSpy.clear(); + rowRemovedSpy.clear(); + pm.removeSourceModel(&mod2); + QCOMPARE(rowATBRSpy.count(), 1); + QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 1); + QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 1); + QCOMPARE(rowRemovedSpy.count(), 1); + QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 1); + QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 1); + QCOMPARE(pm.rowCount(), 1); + QCOMPARE(extractRowTexts(&pm, 0), QString("ABC")); + + // When removing model 1 + rowATBRSpy.clear(); + rowRemovedSpy.clear(); + pm.removeSourceModel(&mod); + QCOMPARE(rowATBRSpy.count(), 1); + QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 0); + QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 0); + QCOMPARE(rowRemovedSpy.count(), 1); + QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 0); + QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 0); + QCOMPARE(pm.rowCount(), 0); + } + + void shouldHandleColumnInsertionAndRemoval() + { + // Given two models combined + KConcatenateRowsProxyModel pm; + pm.addSourceModel(&mod); + pm.addSourceModel(&mod2); + QSignalSpy colATBISpy(&pm, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int))); + QSignalSpy colInsertedSpy(&pm, SIGNAL(columnsInserted(QModelIndex,int,int))); + + // When the first source model inserts a new column + QCOMPARE(mod.columnCount(), 3); + mod.setColumnCount(4); + + // Then the proxy should notify its users and show changes + QCOMPARE(rowSpyToText(colATBISpy), QString("3,3")); + QCOMPARE(rowSpyToText(colInsertedSpy), QString("3,3")); + QCOMPARE(pm.rowCount(), 2); + QCOMPARE(pm.columnCount(), 4); + QCOMPARE(extractRowTexts(&pm, 0), QString("ABC ")); + QCOMPARE(extractRowTexts(&pm, 1), QString("DEF ")); + } + + void shouldPropagateLayoutChanged() + { + // Given two source models, the second one being a QSFPM + KConcatenateRowsProxyModel pm; + pm.addSourceModel(&mod); + + QStandardItemModel mod3; + QList<QStandardItem *> row; + row.append(new QStandardItem("1")); + row.append(new QStandardItem("2")); + row.append(new QStandardItem("3")); + mod3.insertRow(0, row); + row.clear(); + row.append(new QStandardItem("4")); + row.append(new QStandardItem("5")); + row.append(new QStandardItem("6")); + mod3.appendRow(row); + + QSortFilterProxyModel qsfpm; + qsfpm.setSourceModel(&mod3); + pm.addSourceModel(&qsfpm); + + QCOMPARE(extractRowTexts(&pm, 0), QString("ABC")); + QCOMPARE(extractRowTexts(&pm, 1), QString("123")); + QCOMPARE(extractRowTexts(&pm, 2), QString("456")); + + // And a selection (row 1) + QItemSelectionModel selection(&pm); + selection.select(pm.index(1, 0), QItemSelectionModel::Select | QItemSelectionModel::Rows); + const QModelIndexList lst = selection.selectedIndexes(); + QCOMPARE(lst.count(), 3); + for (int col = 0; col < lst.count(); ++col) { + QCOMPARE(lst.at(col).row(), 1); + QCOMPARE(lst.at(col).column(), col); + } + + QSignalSpy layoutATBCSpy(&pm, SIGNAL(layoutAboutToBeChanged())); + QSignalSpy layoutChangedSpy(&pm, SIGNAL(layoutChanged())); + + // When changing the sorting in the QSFPM + qsfpm.sort(0, Qt::DescendingOrder); + + // Then the proxy should emit the layoutChanged signals, and show re-sorted data + QCOMPARE(extractRowTexts(&pm, 0), QString("ABC")); + QCOMPARE(extractRowTexts(&pm, 1), QString("456")); + QCOMPARE(extractRowTexts(&pm, 2), QString("123")); + QCOMPARE(layoutATBCSpy.count(), 1); + QCOMPARE(layoutChangedSpy.count(), 1); + + // And the selection should be updated accordingly (it became row 2) + const QModelIndexList lstAfter = selection.selectedIndexes(); + QCOMPARE(lstAfter.count(), 3); + for (int col = 0; col < lstAfter.count(); ++col) { + QCOMPARE(lstAfter.at(col).row(), 2); + QCOMPARE(lstAfter.at(col).column(), col); + } + } + + void shouldReactToModelReset() + { + // Given two source models, the second one being a QSFPM + KConcatenateRowsProxyModel pm; + pm.addSourceModel(&mod); + + QStandardItemModel mod3; + QList<QStandardItem *> row; + row.append(new QStandardItem("1")); + row.append(new QStandardItem("2")); + row.append(new QStandardItem("3")); + mod3.insertRow(0, row); + row.clear(); + row.append(new QStandardItem("4")); + row.append(new QStandardItem("5")); + row.append(new QStandardItem("6")); + mod3.appendRow(row); + + QSortFilterProxyModel qsfpm; + qsfpm.setSourceModel(&mod3); + pm.addSourceModel(&qsfpm); + + QCOMPARE(extractRowTexts(&pm, 0), QString("ABC")); + QCOMPARE(extractRowTexts(&pm, 1), QString("123")); + QCOMPARE(extractRowTexts(&pm, 2), QString("456")); + QSignalSpy rowATBRSpy(&pm, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int))); + QSignalSpy rowRemovedSpy(&pm, SIGNAL(rowsRemoved(QModelIndex,int,int))); + QSignalSpy rowATBISpy(&pm, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int))); + QSignalSpy rowInsertedSpy(&pm, SIGNAL(rowsInserted(QModelIndex,int,int))); + + // When changing the source model of the QSFPM + qsfpm.setSourceModel(&mod2); + + // Then the proxy should emit the row removed/inserted signals, and show the new data + QCOMPARE(extractRowTexts(&pm, 0), QString("ABC")); + QCOMPARE(extractRowTexts(&pm, 1), QString("DEF")); + QCOMPARE(rowSpyToText(rowATBRSpy), QString("1,2")); + QCOMPARE(rowSpyToText(rowRemovedSpy), QString("1,2")); + QCOMPARE(rowSpyToText(rowATBISpy), QString("1,1")); + QCOMPARE(rowSpyToText(rowInsertedSpy), QString("1,1")); + } + +private: + QStandardItemModel mod; + QStandardItemModel mod2; +}; + +QTEST_MAIN(tst_KConcatenateRowsProxyModel) + +#include "kconcatenaterowsproxymodeltest.moc" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.13.0/autotests/kextracolumnsproxymodeltest.cpp new/kitemmodels-5.14.0/autotests/kextracolumnsproxymodeltest.cpp --- old/kitemmodels-5.13.0/autotests/kextracolumnsproxymodeltest.cpp 2015-08-01 14:41:18.000000000 +0200 +++ new/kitemmodels-5.14.0/autotests/kextracolumnsproxymodeltest.cpp 2015-09-04 22:14:38.000000000 +0200 @@ -76,7 +76,8 @@ } return KExtraColumnsProxyModel::setExtraColumnData(parent, row, extraColumn, data, role); } - void changeExtraColumnData() { + void changeExtraColumnData() + { m_extraColumnData = '<'; extraColumnDataChanged(QModelIndex(), 0, 0, QVector<int>() << Qt::EditRole); } @@ -169,6 +170,9 @@ QVERIFY(indexToText(pm.index(0, 5)).startsWith("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())); } @@ -303,15 +307,33 @@ // And a QSFPM underneath QSortFilterProxyModel proxy; proxy.setSourceModel(&mod); + QCOMPARE(proxy.columnCount(), 4); pm.setSourceModel(&proxy); + QCOMPARE(pm.columnCount(), 6); + QCOMPARE(extractRowTexts(&pm, 0), QString("ABCDZ0")); // And a selection QItemSelectionModel selection(&pm); selection.select(pm.index(0, 0), QItemSelectionModel::Select | QItemSelectionModel::Rows); + const QModelIndexList lst = selection.selectedIndexes(); + QCOMPARE(lst.count(), 6); + for (int col = 0; col < lst.count(); ++col) { + QCOMPARE(lst.at(col).row(), 0); + QCOMPARE(lst.at(col).column(), col); + } // When sorting pm.sort(0, Qt::DescendingOrder); + + // Then the proxy should be sorted QCOMPARE(extractRowTexts(&pm, 0), QString("EFGHZ0")); QCOMPARE(extractRowTexts(&pm, 1), QString("ABCDZ1")); + // And the selection should be updated accordingly + const QModelIndexList lstAfter = selection.selectedIndexes(); + QCOMPARE(lstAfter.count(), 6); + for (int col = 0; col < lstAfter.count(); ++col) { + QCOMPARE(lstAfter.at(col).row(), 1); + QCOMPARE(lstAfter.at(col).column(), col); + } } private: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.13.0/src/CMakeLists.txt new/kitemmodels-5.14.0/src/CMakeLists.txt --- old/kitemmodels-5.13.0/src/CMakeLists.txt 2015-08-01 14:41:18.000000000 +0200 +++ new/kitemmodels-5.14.0/src/CMakeLists.txt 2015-09-04 22:14:38.000000000 +0200 @@ -2,6 +2,7 @@ set(kitemmodels_SRCS kbreadcrumbselectionmodel.cpp kcheckableproxymodel.cpp + kconcatenaterowsproxymodel.cpp kdescendantsproxymodel.cpp kextracolumnsproxymodel.cpp klinkitemselectionmodel.cpp @@ -27,6 +28,7 @@ ecm_generate_headers(KItemModels_HEADERS HEADER_NAMES KBreadcrumbSelectionModel + KConcatenateRowsProxyModel KCheckableProxyModel KExtraColumnsProxyModel KLinkItemSelectionModel diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.13.0/src/kconcatenaterowsproxymodel.cpp new/kitemmodels-5.14.0/src/kconcatenaterowsproxymodel.cpp --- old/kitemmodels-5.13.0/src/kconcatenaterowsproxymodel.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/kitemmodels-5.14.0/src/kconcatenaterowsproxymodel.cpp 2015-09-04 22:14:38.000000000 +0200 @@ -0,0 +1,413 @@ +/* + Copyright (c) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, i...@kdab.com + Authors: David Faure <david.fa...@kdab.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 "kconcatenaterowsproxymodel.h" + +class KConcatenateRowsProxyModelPrivate +{ +public: + KConcatenateRowsProxyModelPrivate(KConcatenateRowsProxyModel* model) + : q(model), + m_rowCount(0) + {} + + int computeRowsPrior(const QAbstractItemModel *sourceModel) const; + QAbstractItemModel *sourceModelForRow(int row, int *sourceRow) const; + + void slotRowsAboutToBeInserted(const QModelIndex &, int start, int end); + void slotRowsInserted(const QModelIndex &, int start, int end); + void slotRowsAboutToBeRemoved(const QModelIndex &, int start, int end); + void slotRowsRemoved(const QModelIndex &, int start, int end); + void slotColumnsAboutToBeInserted(const QModelIndex &parent, int start, int end); + void slotColumnsInserted(const QModelIndex &parent, int, int); + void slotColumnsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void slotColumnsRemoved(const QModelIndex &parent, int, int); + void slotDataChanged(const QModelIndex &from, const QModelIndex &to, const QVector<int> &roles); + void slotSourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint); + void slotSourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint); + void slotModelAboutToBeReset(); + void slotModelReset(); + + KConcatenateRowsProxyModel *q; + QList<QAbstractItemModel *> m_models; + int m_rowCount; // have to maintain it here since we can't compute during model destruction + + // for layoutAboutToBeChanged/layoutChanged + QVector<QPersistentModelIndex> layoutChangePersistentIndexes; + QModelIndexList proxyIndexes; +}; + +KConcatenateRowsProxyModel::KConcatenateRowsProxyModel(QObject *parent) + : QAbstractItemModel(parent), + d(new KConcatenateRowsProxyModelPrivate(this)) +{ +} + +KConcatenateRowsProxyModel::~KConcatenateRowsProxyModel() +{ +} + +QModelIndex KConcatenateRowsProxyModel::mapFromSource(const QModelIndex &sourceIndex) const +{ + const QAbstractItemModel *sourceModel = sourceIndex.model(); + int rowsPrior = d->computeRowsPrior(sourceModel); + return createIndex(rowsPrior + sourceIndex.row(), sourceIndex.column()); +} + +QModelIndex KConcatenateRowsProxyModel::mapToSource(const QModelIndex &proxyIndex) const +{ + if (!proxyIndex.isValid()) { + return QModelIndex(); + } + const int row = proxyIndex.row(); + int sourceRow; + QAbstractItemModel *sourceModel = d->sourceModelForRow(row, &sourceRow); + if (!sourceModel) { + return QModelIndex(); + } + return sourceModel->index(sourceRow, proxyIndex.column()); +} + +QVariant KConcatenateRowsProxyModel::data(const QModelIndex &index, int role) const +{ + const QModelIndex sourceIndex = mapToSource(index); + if (!sourceIndex.isValid()) { + return QVariant(); + } + return sourceIndex.model()->data(sourceIndex, role); +} + +bool KConcatenateRowsProxyModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + const QModelIndex sourceIndex = mapToSource(index); + if (!sourceIndex.isValid()) { + return false; + } + QAbstractItemModel *sourceModel = const_cast<QAbstractItemModel *>(sourceIndex.model()); + return sourceModel->setData(sourceIndex, value, role); +} + +QMap<int, QVariant> KConcatenateRowsProxyModel::itemData(const QModelIndex &proxyIndex) const +{ + const QModelIndex sourceIndex = mapToSource(proxyIndex); + return sourceIndex.model()->itemData(sourceIndex); +} + +Qt::ItemFlags KConcatenateRowsProxyModel::flags(const QModelIndex &index) const +{ + const QModelIndex sourceIndex = mapToSource(index); + return sourceIndex.model()->flags(sourceIndex); +} + +QVariant KConcatenateRowsProxyModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (d->m_models.isEmpty()) { + return QVariant(); + } + if (orientation == Qt::Horizontal) { + return d->m_models.at(0)->headerData(section, orientation, role); + } else { + int sourceRow; + QAbstractItemModel *sourceModel = d->sourceModelForRow(section, &sourceRow); + if (!sourceModel) { + return QVariant(); + } + return sourceModel->headerData(sourceRow, orientation, role); + } +} + +int KConcatenateRowsProxyModel::columnCount(const QModelIndex &parent) const +{ + if (d->m_models.isEmpty()) { + return 0; + } + if (parent.isValid()) { + return 0; // flat model; + } + return d->m_models.at(0)->columnCount(QModelIndex()); +} + +QModelIndex KConcatenateRowsProxyModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_ASSERT(row >= 0); + Q_ASSERT(column >= 0); + int sourceRow; + QAbstractItemModel *sourceModel = d->sourceModelForRow(row, &sourceRow); + if (!sourceModel) { + return QModelIndex(); + } + return mapFromSource(sourceModel->index(sourceRow, column, parent)); +} + +QModelIndex KConcatenateRowsProxyModel::parent(const QModelIndex &) const +{ + return QModelIndex(); // we are flat, no hierarchy +} + +int KConcatenateRowsProxyModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) { + return 0; // flat model + } + + return d->m_rowCount; +} + +void KConcatenateRowsProxyModel::addSourceModel(QAbstractItemModel *sourceModel) +{ + Q_ASSERT(sourceModel); + Q_ASSERT(!d->m_models.contains(sourceModel)); + connect(sourceModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), this, SLOT(slotDataChanged(QModelIndex,QModelIndex,QVector<int>))); + connect(sourceModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(slotRowsInserted(QModelIndex,int,int))); + connect(sourceModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(slotRowsRemoved(QModelIndex,int,int))); + connect(sourceModel, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(slotRowsAboutToBeInserted(QModelIndex,int,int))); + connect(sourceModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int))); + + connect(sourceModel, SIGNAL(columnsInserted(QModelIndex,int,int)), this, SLOT(slotColumnsInserted(QModelIndex,int,int))); + connect(sourceModel, SIGNAL(columnsRemoved(QModelIndex,int,int)), this, SLOT(slotColumnsRemoved(QModelIndex,int,int))); + connect(sourceModel, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(slotColumnsAboutToBeInserted(QModelIndex,int,int))); + connect(sourceModel, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(slotColumnsAboutToBeRemoved(QModelIndex,int,int))); + + connect(sourceModel, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint)), + this, SLOT(slotSourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint))); + connect(sourceModel, SIGNAL(layoutChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint)), + this, SLOT(slotSourceLayoutChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint))); + connect(sourceModel, SIGNAL(modelAboutToBeReset()), this, SLOT(slotModelAboutToBeReset())); + connect(sourceModel, SIGNAL(modelReset()), this, SLOT(slotModelReset())); + + const int newRows = sourceModel->rowCount(); + if (newRows > 0) { + beginInsertRows(QModelIndex(), d->m_rowCount, d->m_rowCount + newRows - 1); + } + d->m_rowCount += newRows; + d->m_models.append(sourceModel); + if (newRows > 0) { + endInsertRows(); + } +} + +void KConcatenateRowsProxyModel::removeSourceModel(QAbstractItemModel *sourceModel) +{ + Q_ASSERT(d->m_models.contains(sourceModel)); + disconnect(sourceModel, 0, this, 0); + + const int rowsRemoved = sourceModel->rowCount(); + const int rowsPrior = d->computeRowsPrior(sourceModel); // location of removed section + + if (rowsRemoved > 0) { + beginRemoveRows(QModelIndex(), rowsPrior, rowsPrior + rowsRemoved - 1); + } + d->m_models.removeOne(sourceModel); + d->m_rowCount -= rowsRemoved; + if (rowsRemoved > 0) { + endRemoveRows(); + } +} + +void KConcatenateRowsProxyModelPrivate::slotRowsAboutToBeInserted(const QModelIndex &, int start, int end) +{ + const QAbstractItemModel *model = qobject_cast<QAbstractItemModel *>(q->sender()); + const int rowsPrior = computeRowsPrior(model); + q->beginInsertRows(QModelIndex(), rowsPrior + start, rowsPrior + end); +} + +void KConcatenateRowsProxyModelPrivate::slotRowsInserted(const QModelIndex &, int start, int end) +{ + m_rowCount += end - start + 1; + q->endInsertRows(); +} + +void KConcatenateRowsProxyModelPrivate::slotRowsAboutToBeRemoved(const QModelIndex &, int start, int end) +{ + const QAbstractItemModel *model = qobject_cast<QAbstractItemModel *>(q->sender()); + const int rowsPrior = computeRowsPrior(model); + q->beginRemoveRows(QModelIndex(), rowsPrior + start, rowsPrior + end); +} + +void KConcatenateRowsProxyModelPrivate::slotRowsRemoved(const QModelIndex &, int start, int end) +{ + m_rowCount -= end - start + 1; + q->endRemoveRows(); +} + +void KConcatenateRowsProxyModelPrivate::slotColumnsAboutToBeInserted(const QModelIndex &parent, int start, int end) +{ + if (parent.isValid()) { // we are flat + return; + } + const QAbstractItemModel *model = qobject_cast<QAbstractItemModel *>(q->sender()); + if (m_models.at(0) == model) { + q->beginInsertColumns(QModelIndex(), start, end); + } +} + +void KConcatenateRowsProxyModelPrivate::slotColumnsInserted(const QModelIndex &parent, int, int) +{ + if (parent.isValid()) { // we are flat + return; + } + const QAbstractItemModel *model = qobject_cast<QAbstractItemModel *>(q->sender()); + if (m_models.at(0) == model) { + q->endInsertColumns(); + } +} + +void KConcatenateRowsProxyModelPrivate::slotColumnsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +{ + if (parent.isValid()) { // we are flat + return; + } + const QAbstractItemModel *model = qobject_cast<QAbstractItemModel *>(q->sender()); + if (m_models.at(0) == model) { + q->beginRemoveColumns(QModelIndex(), start, end); + } +} + +void KConcatenateRowsProxyModelPrivate::slotColumnsRemoved(const QModelIndex &parent, int, int) +{ + if (parent.isValid()) { // we are flat + return; + } + const QAbstractItemModel *model = qobject_cast<QAbstractItemModel *>(q->sender()); + if (m_models.at(0) == model) { + q->endRemoveColumns(); + } +} + +void KConcatenateRowsProxyModelPrivate::slotDataChanged(const QModelIndex &from, const QModelIndex &to, const QVector<int> &roles) +{ + if (!from.isValid()) { // QSFPM bug, it emits dataChanged(invalid, invalid) if a cell in a hidden column changes + return; + } + const QModelIndex myFrom = q->mapFromSource(from); + const QModelIndex myTo = q->mapFromSource(to); + emit q->dataChanged(myFrom, myTo, roles); +} + +void KConcatenateRowsProxyModelPrivate::slotSourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint) +{ + const QModelIndexList persistentIndexList = q->persistentIndexList(); + layoutChangePersistentIndexes.reserve(persistentIndexList.size()); + + foreach (QPersistentModelIndex proxyPersistentIndex, persistentIndexList) { + proxyIndexes << proxyPersistentIndex; + Q_ASSERT(proxyPersistentIndex.isValid()); + const QPersistentModelIndex srcPersistentIndex = q->mapToSource(proxyPersistentIndex); + Q_ASSERT(srcPersistentIndex.isValid()); + layoutChangePersistentIndexes << srcPersistentIndex; + } + + QList<QPersistentModelIndex> parents; + parents.reserve(sourceParents.size()); + foreach (const QPersistentModelIndex &parent, sourceParents) { + if (!parent.isValid()) { + parents << QPersistentModelIndex(); + continue; + } + const QModelIndex mappedParent = q->mapFromSource(parent); + Q_ASSERT(mappedParent.isValid()); + parents << mappedParent; + } + + emit q->layoutAboutToBeChanged(parents, hint); +} + +void KConcatenateRowsProxyModelPrivate::slotSourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint) +{ + for (int i = 0; i < proxyIndexes.size(); ++i) { + const QModelIndex proxyIdx = proxyIndexes.at(i); + QModelIndex newProxyIdx = q->mapFromSource(layoutChangePersistentIndexes.at(i)); + q->changePersistentIndex(proxyIdx, newProxyIdx); + } + + layoutChangePersistentIndexes.clear(); + proxyIndexes.clear(); + + QList<QPersistentModelIndex> parents; + parents.reserve(sourceParents.size()); + foreach (const QPersistentModelIndex &parent, sourceParents) { + if (!parent.isValid()) { + parents << QPersistentModelIndex(); + continue; + } + const QModelIndex mappedParent = q->mapFromSource(parent); + Q_ASSERT(mappedParent.isValid()); + parents << mappedParent; + } + emit q->layoutChanged(parents, hint); +} + +void KConcatenateRowsProxyModelPrivate::slotModelAboutToBeReset() +{ + const QAbstractItemModel *sourceModel = qobject_cast<const QAbstractItemModel *>(q->sender()); + Q_ASSERT(m_models.contains(const_cast<QAbstractItemModel *>(sourceModel))); + const int oldRows = sourceModel->rowCount(); + if (oldRows > 0) { + slotRowsAboutToBeRemoved(QModelIndex(), 0, oldRows - 1); + slotRowsRemoved(QModelIndex(), 0, oldRows - 1); + } + if (m_models.at(0) == sourceModel) { + q->beginResetModel(); + } +} + +void KConcatenateRowsProxyModelPrivate::slotModelReset() +{ + const QAbstractItemModel *sourceModel = qobject_cast<const QAbstractItemModel *>(q->sender()); + Q_ASSERT(m_models.contains(const_cast<QAbstractItemModel *>(sourceModel))); + if (m_models.at(0) == sourceModel) { + q->endResetModel(); + } + const int newRows = sourceModel->rowCount(); + if (newRows > 0) { + slotRowsAboutToBeInserted(QModelIndex(), 0, newRows - 1); + slotRowsInserted(QModelIndex(), 0, newRows - 1); + } +} + +int KConcatenateRowsProxyModelPrivate::computeRowsPrior(const QAbstractItemModel *sourceModel) const +{ + int rowsPrior = 0; + foreach (const QAbstractItemModel *model, m_models) { + if (model == sourceModel) { + break; + } + rowsPrior += model->rowCount(); + } + return rowsPrior; +} + +QAbstractItemModel *KConcatenateRowsProxyModelPrivate::sourceModelForRow(int row, int *sourceRow) const +{ + int rowCount = 0; + QAbstractItemModel *selection = NULL; + foreach (QAbstractItemModel *model, m_models) { + const int subRowCount = model->rowCount(); + if (rowCount + subRowCount > row) { + selection = model; + break; + } + rowCount += subRowCount; + } + *sourceRow = row - rowCount; + return selection; +} + +#include "moc_kconcatenaterowsproxymodel.cpp" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.13.0/src/kconcatenaterowsproxymodel.h new/kitemmodels-5.14.0/src/kconcatenaterowsproxymodel.h --- old/kitemmodels-5.13.0/src/kconcatenaterowsproxymodel.h 1970-01-01 01:00:00.000000000 +0100 +++ new/kitemmodels-5.14.0/src/kconcatenaterowsproxymodel.h 2015-09-04 22:14:38.000000000 +0200 @@ -0,0 +1,151 @@ +/* + Copyright (c) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, i...@kdab.com + Authors: David Faure <david.fa...@kdab.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. +*/ + +#ifndef KCONCATENATEROWSPROXYMODEL_H +#define KCONCATENATEROWSPROXYMODEL_H + +#include <QAbstractItemModel> +#include <QScopedPointer> +#include "kitemmodels_export.h" + +class KConcatenateRowsProxyModelPrivate; + +/** + * This proxy takes multiple source models and concatenates their rows. + * + * In other words, the proxy will have all rows of the first source model, + * followed by all rows of the second source model, etc. + * + * Only flat models (lists and tables) are supported, no trees. + * + * All models are assumed to have the same number of columns. + * More precisely, the number of columns of the first source model is used, + * so all source models should have at least as many columns as the first source model, + * and additional columns in other source models will simply be ignored. + * + * Source models can be added and removed at runtime, including the first source + * model (but it should keep the same number of columns). + * + * Dynamic insertion and removal of rows and columns in any source model is supported. + * dataChanged, layoutChanged and reset coming from the source models are supported. + * + * At the moment this model doesn't support editing, drag-n-drop. + * It could be added though, nothing prevents it. + * + * This proxy does not inherit from QAbstractProxyModel because it uses multiple source + * models, rather than a single one. + * + * Author: David Faure, KDAB + * @since 5.14 + */ +class KITEMMODELS_EXPORT KConcatenateRowsProxyModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + /** + * Creates a KConcatenateRowsProxyModel. + * @param parent optional parent + */ + KConcatenateRowsProxyModel(QObject *parent = 0); + /** + * Destructor. + */ + virtual ~KConcatenateRowsProxyModel(); + + /** + * Adds a source model @p sourceModel, after all existing source models. + * @param sourceModel the source model + * + * The ownership of @p sourceModel is not affected by this. + * The same source model cannot be added more than once. + */ + void addSourceModel(QAbstractItemModel *sourceModel); + + /** + * Removes the source model @p sourceModel. + * @param sourceModel a source model previously added to this proxy + * + * The ownership of @sourceModel is not affected by this. + */ + void removeSourceModel(QAbstractItemModel *sourceModel); + + /** + * Returns the proxy index for a given source index + * @param sourceIndex an index coming from any of the source models + * @return a proxy index + * Calling this method with an index not from a source model is undefined behavior. + */ + QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; + + /** + * Returns the source index for a given proxy index. + * @param proxyIndex an index for this proxy model + * @return a source index + */ + QModelIndex mapToSource(const QModelIndex &proxyIndex) const; + + /// @reimp + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + /// @reimp + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole) Q_DECL_OVERRIDE; + /// @reimp + QMap<int, QVariant> itemData(const QModelIndex &proxyIndex) const Q_DECL_OVERRIDE; + /// @reimp + Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE; + /// @reimp + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + /// @reimp + QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE; + /// @reimp + int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + + /** + * The horizontal header data for the first source model is returned here. + * @reimp + */ + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + /** + * The column count for the first source model is returned here. + * @reimp + */ + int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + +private: + Q_PRIVATE_SLOT(d, void slotRowsAboutToBeInserted(const QModelIndex &, int start, int end)) + Q_PRIVATE_SLOT(d, void slotRowsInserted(const QModelIndex &, int start, int end)) + Q_PRIVATE_SLOT(d, void slotRowsAboutToBeRemoved(const QModelIndex &, int start, int end)) + Q_PRIVATE_SLOT(d, void slotRowsRemoved(const QModelIndex &, int start, int end)) + Q_PRIVATE_SLOT(d, void slotColumnsAboutToBeInserted(const QModelIndex &parent, int start, int end)) + Q_PRIVATE_SLOT(d, void slotColumnsInserted(const QModelIndex &parent, int, int)) + Q_PRIVATE_SLOT(d, void slotColumnsAboutToBeRemoved(const QModelIndex &parent, int start, int end)) + Q_PRIVATE_SLOT(d, void slotColumnsRemoved(const QModelIndex &parent, int, int)) + Q_PRIVATE_SLOT(d, void slotDataChanged(const QModelIndex &from, const QModelIndex &to, const QVector<int> &roles)) + Q_PRIVATE_SLOT(d, void slotSourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint)) + Q_PRIVATE_SLOT(d, void slotSourceLayoutChanged(const QList<QPersistentModelIndex> &, QAbstractItemModel::LayoutChangeHint)) + Q_PRIVATE_SLOT(d, void slotModelAboutToBeReset()) + Q_PRIVATE_SLOT(d, void slotModelReset()) + +private: + friend class KConcatenateRowsProxyModelPrivate; + const QScopedPointer<KConcatenateRowsProxyModelPrivate> d; +}; + +#endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.13.0/src/kextracolumnsproxymodel.cpp new/kitemmodels-5.14.0/src/kextracolumnsproxymodel.cpp --- old/kitemmodels-5.13.0/src/kextracolumnsproxymodel.cpp 2015-08-01 14:41:18.000000000 +0200 +++ new/kitemmodels-5.14.0/src/kextracolumnsproxymodel.cpp 2015-09-04 22:14:38.000000000 +0200 @@ -39,7 +39,7 @@ // Configuration (doesn't change once source model is plugged in) QVector<QString> m_extraHeaders; - // for layoutAboutToChanged/layoutChanged + // for layoutAboutToBeChanged/layoutChanged QVector<QPersistentModelIndex> layoutChangePersistentIndexes; QVector<int> layoutChangeProxyColumns; QModelIndexList proxyIndexes; @@ -71,7 +71,7 @@ return false; } -void KExtraColumnsProxyModel::extraColumnDataChanged(const QModelIndex &parent, int row, int extraColumn, const QVector<int> & roles) +void KExtraColumnsProxyModel::extraColumnDataChanged(const QModelIndex &parent, int row, int extraColumn, const QVector<int> &roles) { const QModelIndex idx = index(row, proxyColumnForExtraColumn(extraColumn), parent); emit dataChanged(idx, idx, roles); @@ -81,9 +81,9 @@ { if (sourceModel()) { disconnect(sourceModel(), SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - this, SLOT(_ec_sourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); + this, SLOT(_ec_sourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); disconnect(sourceModel(), SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - this, SLOT(_ec_sourceLayoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); + this, SLOT(_ec_sourceLayoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); } QIdentityProxyModel::setSourceModel(model); @@ -92,9 +92,9 @@ // The handling of persistent model indexes assumes mapToSource can be called for any index // This breaks for the extra column, so we'll have to do it ourselves disconnect(model, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - this, SLOT(_q_sourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); + this, SLOT(_q_sourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); disconnect(model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - this, SLOT(_q_sourceLayoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); + this, SLOT(_q_sourceLayoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); connect(model, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), this, SLOT(_ec_sourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); connect(model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), @@ -117,15 +117,14 @@ QModelIndex KExtraColumnsProxyModel::sibling(int row, int column, const QModelIndex &idx) const { - if (idx.column() >= sourceModel()->columnCount()) { - return index(row, column, parent(idx)); + if (row == idx.row() && column == idx.column()) { + return idx; } - return mapFromSource(sourceModel()->sibling(row, column, mapToSource(idx))); + return index(row, column, parent(idx)); } QItemSelection KExtraColumnsProxyModel::mapSelectionToSource(const QItemSelection &selection) const { - Q_D(const KExtraColumnsProxyModel); QItemSelection sourceSelection; if (!sourceModel()) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.13.0/src/krearrangecolumnsproxymodel.cpp new/kitemmodels-5.14.0/src/krearrangecolumnsproxymodel.cpp --- old/kitemmodels-5.13.0/src/krearrangecolumnsproxymodel.cpp 2015-08-01 14:41:18.000000000 +0200 +++ new/kitemmodels-5.14.0/src/krearrangecolumnsproxymodel.cpp 2015-09-04 22:14:38.000000000 +0200 @@ -28,7 +28,7 @@ KRearrangeColumnsProxyModel::KRearrangeColumnsProxyModel(QObject *parent) : QIdentityProxyModel(parent), - d_ptr(new KRearrangeColumnsProxyModelPrivate) + d_ptr(new KRearrangeColumnsProxyModelPrivate) { }