Hello community, here is the log from the commit of package kitemmodels for openSUSE:Factory checked in at 2015-09-02 07:46:39 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 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-07-14 17:28:10.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.kitemmodels.new/kitemmodels.changes 2015-09-02 07:46:40.000000000 +0200 @@ -1,0 +2,13 @@ +Tue Aug 4 19:20:32 UTC 2015 - hrvoje.sen...@gmail.com + +- Update to 5.13.0 + * The Qt version requirement has been bumped from 5.2 to 5.3 + * Debug output has been ported to categorized output, for less + noise by default + * Docbook documentation has been reviewed and updated + * New proxy: KExtraColumnsProxyModel, allows to add columns to + an existing model. + * For more details please see: + https://www.kde.org/announcements/kde-frameworks-5.13.0.php + +------------------------------------------------------------------- Old: ---- kitemmodels-5.12.0.tar.xz New: ---- kitemmodels-5.13.0.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ kitemmodels.spec ++++++ --- /var/tmp/diff_new_pack.WQKJtF/_old 2015-09-02 07:46:41.000000000 +0200 +++ /var/tmp/diff_new_pack.WQKJtF/_new 2015-09-02 07:46:41.000000000 +0200 @@ -17,15 +17,15 @@ %define lname libKF5ItemModels5 -%define _tar_path 5.12 +%define _tar_path 5.13 Name: kitemmodels -Version: 5.12.0 +Version: 5.13.0 Release: 0 BuildRequires: cmake >= 2.8.12 BuildRequires: extra-cmake-modules >= %{_tar_path} BuildRequires: fdupes BuildRequires: kf5-filesystem -BuildRequires: pkgconfig(Qt5Core) >= 5.2.0 +BuildRequires: cmake(Qt5Core) >= 5.3.0 Summary: Set of item models extending the Qt model-view framework License: LGPL-2.1+ Group: System/GUI/KDE @@ -50,7 +50,7 @@ Group: Development/Libraries/KDE Requires: %lname = %{version} Requires: extra-cmake-modules -Requires: pkgconfig(Qt5Core) >= 5.2.0 +Requires: cmake(Qt5Core) >= 5.3.0 %description devel KItemModels provides a set of item models extending the Qt model-view framework. ++++++ kitemmodels-5.12.0.tar.xz -> kitemmodels-5.13.0.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.12.0/CMakeLists.txt new/kitemmodels-5.13.0/CMakeLists.txt --- old/kitemmodels-5.12.0/CMakeLists.txt 2015-07-04 22:37:34.000000000 +0200 +++ new/kitemmodels-5.13.0/CMakeLists.txt 2015-08-01 14:41:18.000000000 +0200 @@ -3,7 +3,7 @@ project(KItemModels) include(FeatureSummary) -find_package(ECM 5.12.0 NO_MODULE) +find_package(ECM 5.13.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) @@ -14,7 +14,7 @@ include(KDEFrameworkCompilerSettings) include(KDECMakeSettings) -set(REQUIRED_QT_VERSION 5.2.0) +set(REQUIRED_QT_VERSION 5.3.0) find_package(Qt5Core ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE) @@ -23,7 +23,7 @@ include(ECMSetupVersion) include(ECMGenerateHeaders) -set(KF5_VERSION "5.12.0") # handled by release scripts +set(KF5_VERSION "5.13.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.12.0/README.md new/kitemmodels-5.13.0/README.md --- old/kitemmodels-5.12.0/README.md 2015-07-04 22:37:34.000000000 +0200 +++ new/kitemmodels-5.13.0/README.md 2015-08-01 14:41:18.000000000 +0200 @@ -10,6 +10,7 @@ breadcrumbs * KCheckableProxyModel - Adds a checkable capability to a source model * 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 have the same source model * KModelIndexProxyMapper - Mapping of indexes and selections through proxy diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.12.0/autotests/CMakeLists.txt new/kitemmodels-5.13.0/autotests/CMakeLists.txt --- old/kitemmodels-5.12.0/autotests/CMakeLists.txt 2015-07-04 22:37:34.000000000 +0200 +++ new/kitemmodels-5.13.0/autotests/CMakeLists.txt 2015-08-01 14:41:18.000000000 +0200 @@ -16,6 +16,7 @@ ecm_add_tests( kdescendantsproxymodel_smoketest.cpp + kextracolumnsproxymodeltest.cpp klinkitemselectionmodeltest.cpp testmodelqueuedconnections.cpp kselectionproxymodeltest.cpp diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.12.0/autotests/kextracolumnsproxymodeltest.cpp new/kitemmodels-5.13.0/autotests/kextracolumnsproxymodeltest.cpp --- old/kitemmodels-5.12.0/autotests/kextracolumnsproxymodeltest.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/kitemmodels-5.13.0/autotests/kextracolumnsproxymodeltest.cpp 2015-08-01 14:41:18.000000000 +0200 @@ -0,0 +1,339 @@ +/* + 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 <QSignalSpy> +#include <QSortFilterProxyModel> +#include <QTest> +#include <QDebug> +#include <QStandardItemModel> + +#include <QTreeView> + +#include <kextracolumnsproxymodel.h> +#include "test_model_helpers.h" +using namespace TestModelHelpers; + +Q_DECLARE_METATYPE(QModelIndex) + +class tst_KExtraColumnsProxyModel : public QObject +{ + Q_OBJECT + +private: + class NoExtraColumns : public KExtraColumnsProxyModel + { + QVariant extraColumnData(const QModelIndex &, int, int, int) const Q_DECL_OVERRIDE + { + Q_ASSERT(0); + return QVariant(); + } + }; + + class TwoExtraColumnsProxyModel : public KExtraColumnsProxyModel + { + public: + TwoExtraColumnsProxyModel() : KExtraColumnsProxyModel(), m_extraColumnData('Z') + { + appendColumn("H5"); + appendColumn("H6"); + } + QVariant extraColumnData(const QModelIndex &, int row, int extraColumn, int role) const Q_DECL_OVERRIDE + { + Q_ASSERT(role == Qt::DisplayRole); + switch (extraColumn) { + case 0: + return QString(m_extraColumnData); + case 1: + return QString::number(row); + default: + Q_ASSERT(0); + return QVariant(); + } + } + bool setExtraColumnData(const QModelIndex &parent, int row, int extraColumn, const QVariant &data, int role) Q_DECL_OVERRIDE { + if (extraColumn == 0 && role == Qt::EditRole) + { + m_extraColumnData = data.toString().at(0); + extraColumnDataChanged(QModelIndex(), 0, extraColumn, QVector<int>() << Qt::EditRole); + return true; + } + return KExtraColumnsProxyModel::setExtraColumnData(parent, row, extraColumn, data, role); + } + void changeExtraColumnData() { + m_extraColumnData = '<'; + extraColumnDataChanged(QModelIndex(), 0, 0, QVector<int>() << Qt::EditRole); + } + private: + QChar m_extraColumnData; + }; + +private Q_SLOTS: + + void initTestCase() + { + qRegisterMetaType<QModelIndex>(); + } + + void init() + { + // Prepare the source model to use later on + mod.clear(); + mod.appendRow(makeStandardItems(QStringList() << "A" << "B" << "C" << "D")); + mod.item(0, 0)->appendRow(makeStandardItems(QStringList() << "m" << "n" << "o" << "p")); + mod.item(0, 0)->appendRow(makeStandardItems(QStringList() << "q" << "r" << "s" << "t")); + mod.appendRow(makeStandardItems(QStringList() << "E" << "F" << "G" << "H")); + mod.item(1, 0)->appendRow(makeStandardItems(QStringList() << "x" << "y" << "z" << ".")); + mod.setHorizontalHeaderLabels(QStringList() << "H1" << "H2" << "H3" << "H4"); + + QCOMPARE(extractRowTexts(&mod, 0), QString("ABCD")); + QCOMPARE(extractRowTexts(&mod, 0, mod.index(0, 0)), QString("mnop")); + QCOMPARE(extractRowTexts(&mod, 1, mod.index(0, 0)), QString("qrst")); + QCOMPARE(extractRowTexts(&mod, 1), QString("EFGH")); + QCOMPARE(extractRowTexts(&mod, 0, mod.index(1, 0)), QString("xyz.")); + QCOMPARE(extractHorizontalHeaderTexts(&mod), QString("H1H2H3H4")); + + // test code to see the model + // showModel(&mod); + } + + void shouldDoNothingIfNoExtraColumns() + { + // Given a extra-columns proxy + NoExtraColumns pm; + + // When setting it to a source model + pm.setSourceModel(&mod); + + // Then the proxy should show the same as the model + QCOMPARE(pm.rowCount(), mod.rowCount()); + QCOMPARE(pm.columnCount(), mod.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), QString("ABCD")); + QCOMPARE(extractRowTexts(&pm, 0, pm.index(0, 0)), QString("mnop")); + QCOMPARE(extractRowTexts(&pm, 1, pm.index(0, 0)), QString("qrst")); + QCOMPARE(extractRowTexts(&pm, 1), QString("EFGH")); + QCOMPARE(extractRowTexts(&pm, 0, pm.index(1, 0)), QString("xyz.")); + QCOMPARE(extractHorizontalHeaderTexts(&pm), QString("H1H2H3H4")); + } + + void shouldShowExtraColumns() + { + // Given a extra-columns proxy with two extra columns + TwoExtraColumnsProxyModel pm; + + // When setting it to a source model + pm.setSourceModel(&mod); + + // Then the proxy should show the extra column + QCOMPARE(extractRowTexts(&pm, 0), QString("ABCDZ0")); + QCOMPARE(extractRowTexts(&pm, 0, pm.index(0, 0)), QString("mnopZ0")); + QCOMPARE(extractRowTexts(&pm, 1, pm.index(0, 0)), QString("qrstZ1")); + QCOMPARE(extractRowTexts(&pm, 1), QString("EFGHZ1")); + QCOMPARE(extractRowTexts(&pm, 0, pm.index(1, 0)), QString("xyz.Z0")); + QCOMPARE(extractHorizontalHeaderTexts(&pm), QString("H1H2H3H4H5H6")); + + // 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("0,4,")); + QCOMPARE(indexToText(pm.index(0, 4, secondParent).parent()), indexToText(secondParent)); + QVERIFY(indexToText(pm.index(0, 5)).startsWith("0,5,")); + QCOMPARE(indexToText(pm.index(0, 5, secondParent).parent()), indexToText(secondParent)); + + QVERIFY(!pm.canFetchMore(QModelIndex())); + } + + void shouldHandleDataChanged() + { + // Given a extra-columns proxy, with two extra columns + TwoExtraColumnsProxyModel pm; + setup(pm); + QSignalSpy dataChangedSpy(&pm, SIGNAL(dataChanged(QModelIndex,QModelIndex))); + + // When a cell in a source model changes + mod.item(0, 2)->setData("c", 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, 2)); + QCOMPARE(extractRowTexts(&pm, 0), QString("ABcDZ0")); + } + + void shouldHandleDataChangedInExtraColumn() + { + // Given a extra-columns proxy, with two extra columns + TwoExtraColumnsProxyModel pm; + setup(pm); + QSignalSpy dataChangedSpy(&pm, SIGNAL(dataChanged(QModelIndex,QModelIndex))); + + // When the proxy wants to signal a change in an extra column + pm.changeExtraColumnData(); + + // Then the change should be available and notified + QCOMPARE(extractRowTexts(&pm, 0), QString("ABCD<0")); + QCOMPARE(dataChangedSpy.count(), 1); + QCOMPARE(dataChangedSpy.at(0).at(0).value<QModelIndex>(), pm.index(0, 4)); + } + + void shouldHandleSetDataInNormalColumn() + { + // Given a extra-columns proxy, with two extra columns + TwoExtraColumnsProxyModel pm; + setup(pm); + QSignalSpy dataChangedSpy(&pm, SIGNAL(dataChanged(QModelIndex,QModelIndex))); + + // When editing a cell in the proxy + QVERIFY(pm.setData(pm.index(0, 2), "c", Qt::EditRole)); + + // Then the change should be available and notified + QCOMPARE(extractRowTexts(&pm, 0), QString("ABcDZ0")); + QCOMPARE(dataChangedSpy.count(), 1); + QCOMPARE(dataChangedSpy.at(0).at(0).value<QModelIndex>(), pm.index(0, 2)); + } + + void shouldHandleSetDataInExtraColumn() + { + // Given a extra-columns proxy, with two extra columns + TwoExtraColumnsProxyModel pm; + setup(pm); + QSignalSpy dataChangedSpy(&pm, SIGNAL(dataChanged(QModelIndex,QModelIndex))); + + // When editing a cell in the proxy + QVERIFY(pm.setData(pm.index(0, 4), "-", Qt::EditRole)); + + // Then the change should be available and notified + QCOMPARE(extractRowTexts(&pm, 0), QString("ABCD-0")); + QCOMPARE(dataChangedSpy.count(), 1); + QCOMPARE(dataChangedSpy.at(0).at(0).value<QModelIndex>(), pm.index(0, 4)); + } + + void shouldHandleRowInsertion() + { + // Given a extra-columns proxy, with two extra columns + TwoExtraColumnsProxyModel pm; + setup(pm); + + QSignalSpy rowATBISpy(&pm, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int))); + QSignalSpy rowInsertedSpy(&pm, SIGNAL(rowsInserted(QModelIndex,int,int))); + + // When a source model inserts a new (child) row + mod.item(1, 0)->appendRow(makeStandardItems(QStringList() << "1" << "2" << "3" << "4")); + + // 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(), 2); + QCOMPARE(extractRowTexts(&pm, 0), QString("ABCDZ0")); + QCOMPARE(extractRowTexts(&pm, 0, pm.index(0, 0)), QString("mnopZ0")); + QCOMPARE(extractRowTexts(&pm, 1, pm.index(0, 0)), QString("qrstZ1")); + QCOMPARE(extractRowTexts(&pm, 1), QString("EFGHZ1")); + QCOMPARE(extractRowTexts(&pm, 0, pm.index(1, 0)), QString("xyz.Z0")); + QCOMPARE(extractRowTexts(&pm, 1, pm.index(1, 0)), QString("1234Z1")); + QCOMPARE(extractHorizontalHeaderTexts(&pm), QString("H1H2H3H4H5H6")); + } + + void shouldHandleColumnInsertion() + { + // Given a extra-columns proxy, with two extra columns + TwoExtraColumnsProxyModel pm; + setup(pm); + + QCOMPARE(pm.columnCount(), 6); + QCOMPARE(mod.columnCount(), 4); + + QSignalSpy colATBISpy(&pm, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int))); + QSignalSpy colInsertedSpy(&pm, SIGNAL(columnsInserted(QModelIndex,int,int))); + + // When a source model inserts a new column + mod.setColumnCount(5); // like QStandardItem::setChild does + QCOMPARE(mod.columnCount(), 5); + // QStandardItemModel is quite dumb, it records the number of columns in each item + for (int row = 0; row < mod.rowCount(); ++row) { + mod.item(row, 0)->setColumnCount(5); + } + + // Then the proxy should notify its users and show changes + QCOMPARE(rowSpyToText(colATBISpy), QString("4,4;4,4;4,4")); // QStandardItemModel emits it for each parent + QCOMPARE(rowSpyToText(colInsertedSpy), QString("4,4;4,4;4,4")); + QCOMPARE(pm.columnCount(), 7); + QCOMPARE(extractRowTexts(&pm, 0), QString("ABCD Z0")); + QCOMPARE(extractRowTexts(&pm, 0, pm.index(0, 0)), QString("mnop Z0")); + QCOMPARE(extractRowTexts(&pm, 1, pm.index(0, 0)), QString("qrst Z1")); + QCOMPARE(extractRowTexts(&pm, 1), QString("EFGH Z1")); + QCOMPARE(extractRowTexts(&pm, 0, pm.index(1, 0)), QString("xyz. Z0")); + QCOMPARE(extractHorizontalHeaderTexts(&pm), QString("H1H2H3H45H5H6")); // '5' was inserted in there + } + + // row removal, layoutChanged, modelReset -> same thing, works via QIdentityProxyModel + // missing: test for mapSelectionToSource + + void shouldHandleLayoutChanged() + { + // Given a extra-columns proxy, with two extra columns + TwoExtraColumnsProxyModel pm; + // And a QSFPM underneath + QSortFilterProxyModel proxy; + proxy.setSourceModel(&mod); + pm.setSourceModel(&proxy); + // And a selection + QItemSelectionModel selection(&pm); + selection.select(pm.index(0, 0), QItemSelectionModel::Select | QItemSelectionModel::Rows); + + // When sorting + pm.sort(0, Qt::DescendingOrder); + QCOMPARE(extractRowTexts(&pm, 0), QString("EFGHZ0")); + QCOMPARE(extractRowTexts(&pm, 1), QString("ABCDZ1")); + } + +private: + + void setup(KExtraColumnsProxyModel &pm) + { + pm.setSourceModel(&mod); + } + + static QString indexToText(const QModelIndex &index) + { + if (!index.isValid()) { + return "invalid"; + } + return QString::number(index.row()) + "," + QString::number(index.column()) + "," + + QString::number(reinterpret_cast<qulonglong>(index.internalPointer()), 16) + + " in " + QString::number(reinterpret_cast<qulonglong>(index.model()), 16); + } + + QStandardItemModel mod; +}; + +QTEST_MAIN(tst_KExtraColumnsProxyModel) + +#include "kextracolumnsproxymodeltest.moc" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.12.0/autotests/test_model_helpers.h new/kitemmodels-5.13.0/autotests/test_model_helpers.h --- old/kitemmodels-5.12.0/autotests/test_model_helpers.h 2015-07-04 22:37:34.000000000 +0200 +++ new/kitemmodels-5.13.0/autotests/test_model_helpers.h 2015-08-01 14:41:18.000000000 +0200 @@ -58,5 +58,20 @@ return result; } +inline QString rowSpyToText(const QSignalSpy &spy) +{ + if (!spy.isValid()) { + return QString::fromLatin1("THE SIGNALSPY IS INVALID!"); + } + QString str; + for (int i = 0; i < spy.count(); ++i) { + str += spy.at(i).at(1).toString() + ',' + spy.at(i).at(2).toString(); + if (i + 1 < spy.count()) { + str += QLatin1Char(';'); + } + } + return str; +} + } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.12.0/src/CMakeLists.txt new/kitemmodels-5.13.0/src/CMakeLists.txt --- old/kitemmodels-5.12.0/src/CMakeLists.txt 2015-07-04 22:37:34.000000000 +0200 +++ new/kitemmodels-5.13.0/src/CMakeLists.txt 2015-08-01 14:41:18.000000000 +0200 @@ -3,6 +3,7 @@ kbreadcrumbselectionmodel.cpp kcheckableproxymodel.cpp kdescendantsproxymodel.cpp + kextracolumnsproxymodel.cpp klinkitemselectionmodel.cpp kmodelindexproxymapper.cpp krearrangecolumnsproxymodel.cpp @@ -27,6 +28,7 @@ HEADER_NAMES KBreadcrumbSelectionModel KCheckableProxyModel + KExtraColumnsProxyModel KLinkItemSelectionModel KRearrangeColumnsProxyModel KRecursiveFilterProxyModel diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.12.0/src/kextracolumnsproxymodel.cpp new/kitemmodels-5.13.0/src/kextracolumnsproxymodel.cpp --- old/kitemmodels-5.12.0/src/kextracolumnsproxymodel.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/kitemmodels-5.13.0/src/kextracolumnsproxymodel.cpp 2015-08-01 14:41:18.000000000 +0200 @@ -0,0 +1,315 @@ +/* + 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 "kextracolumnsproxymodel.h" +#include <QItemSelection> +#include <QDebug> + +class KExtraColumnsProxyModelPrivate +{ + Q_DECLARE_PUBLIC(KExtraColumnsProxyModel) + KExtraColumnsProxyModel *const q_ptr; + +public: + KExtraColumnsProxyModelPrivate(KExtraColumnsProxyModel *model) + : q_ptr(model) + { + } + + void _ec_sourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint); + void _ec_sourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint); + + // Configuration (doesn't change once source model is plugged in) + QVector<QString> m_extraHeaders; + + // for layoutAboutToChanged/layoutChanged + QVector<QPersistentModelIndex> layoutChangePersistentIndexes; + QVector<int> layoutChangeProxyColumns; + QModelIndexList proxyIndexes; +}; + +KExtraColumnsProxyModel::KExtraColumnsProxyModel(QObject *parent) + : QIdentityProxyModel(parent), + d_ptr(new KExtraColumnsProxyModelPrivate(this)) +{ +} + +KExtraColumnsProxyModel::~KExtraColumnsProxyModel() +{ +} + +void KExtraColumnsProxyModel::appendColumn(const QString &header) +{ + Q_D(KExtraColumnsProxyModel); + d->m_extraHeaders.append(header); +} + +bool KExtraColumnsProxyModel::setExtraColumnData(const QModelIndex &parent, int row, int extraColumn, const QVariant &data, int role) +{ + Q_UNUSED(parent); + Q_UNUSED(row); + Q_UNUSED(extraColumn); + Q_UNUSED(data); + Q_UNUSED(role); + return false; +} + +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); +} + +void KExtraColumnsProxyModel::setSourceModel(QAbstractItemModel *model) +{ + if (sourceModel()) { + disconnect(sourceModel(), SIGNAL(layoutAboutToBeChanged(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))); + } + + QIdentityProxyModel::setSourceModel(model); + + if (model) { + // 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))); + disconnect(model, SIGNAL(layoutChanged(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)), + this, SLOT(_ec_sourceLayoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); + } +} + +QModelIndex KExtraColumnsProxyModel::mapToSource(const QModelIndex &proxyIndex) const +{ + if (!proxyIndex.isValid()) { // happens in e.g. rowCount(mapToSource(parent)) + return QModelIndex(); + } + const int column = proxyIndex.column(); + if (column >= sourceModel()->columnCount()) { + qDebug() << "Returning invalid index in mapToSource"; + return QModelIndex(); + } + return QIdentityProxyModel::mapToSource(proxyIndex); +} + +QModelIndex KExtraColumnsProxyModel::sibling(int row, int column, const QModelIndex &idx) const +{ + if (idx.column() >= sourceModel()->columnCount()) { + return index(row, column, parent(idx)); + } + return mapFromSource(sourceModel()->sibling(row, column, mapToSource(idx))); +} + +QItemSelection KExtraColumnsProxyModel::mapSelectionToSource(const QItemSelection &selection) const +{ + Q_D(const KExtraColumnsProxyModel); + 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 = sourceModel()->columnCount(); + 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; +} + +int KExtraColumnsProxyModel::columnCount(const QModelIndex &parent) const +{ + Q_D(const KExtraColumnsProxyModel); + return QIdentityProxyModel::columnCount(parent) + d->m_extraHeaders.count(); +} + +QVariant KExtraColumnsProxyModel::data(const QModelIndex &index, int role) const +{ + Q_D(const KExtraColumnsProxyModel); + const int extraCol = extraColumnForProxyColumn(index.column()); + if (extraCol >= 0 && !d->m_extraHeaders.isEmpty()) { + return extraColumnData(index.parent(), index.row(), extraCol, role); + } + return sourceModel()->data(mapToSource(index), role); +} + +bool KExtraColumnsProxyModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + Q_D(const KExtraColumnsProxyModel); + const int extraCol = extraColumnForProxyColumn(index.column()); + if (extraCol >= 0 && !d->m_extraHeaders.isEmpty()) { + return setExtraColumnData(index.parent(), index.row(), extraCol, value, role); + } + return sourceModel()->setData(mapToSource(index), value, role); +} + +Qt::ItemFlags KExtraColumnsProxyModel::flags(const QModelIndex &index) const +{ + const int extraCol = extraColumnForProxyColumn(index.column()); + if (extraCol >= 0) { + // extra columns are readonly + return Qt::ItemIsSelectable | Qt::ItemIsEnabled; + } + return sourceModel()->flags(mapToSource(index)); +} + +QVariant KExtraColumnsProxyModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + Q_D(const KExtraColumnsProxyModel); + if (orientation == Qt::Horizontal) { + const int extraCol = extraColumnForProxyColumn(section); + if (extraCol >= 0) { + // Only text is supported, in headers for extra columns + if (role == Qt::DisplayRole) { + return d->m_extraHeaders.at(extraCol); + } + return QVariant(); + } + } + return QIdentityProxyModel::headerData(section, orientation, role); +} + +QModelIndex KExtraColumnsProxyModel::index(int row, int column, const QModelIndex &parent) const +{ + const int extraCol = extraColumnForProxyColumn(column); + if (extraCol >= 0) { + // We store the internal pointer of the index for column 0 in the proxy index for extra columns. + // This will be useful in the parent method. + return createIndex(row, column, QIdentityProxyModel::index(row, 0, parent).internalPointer()); + } + return QIdentityProxyModel::index(row, column, parent); +} + +QModelIndex KExtraColumnsProxyModel::parent(const QModelIndex &child) const +{ + const int extraCol = extraColumnForProxyColumn(child.column()); + if (extraCol >= 0) { + // Create an index for column 0 and use that to get the parent. + const QModelIndex proxySibling = createIndex(child.row(), 0, child.internalPointer()); + return QIdentityProxyModel::parent(proxySibling); + } + return QIdentityProxyModel::parent(child); +} + +int KExtraColumnsProxyModel::extraColumnForProxyColumn(int proxyColumn) const +{ + const int sourceColumnCount = sourceModel()->columnCount(); + if (proxyColumn >= sourceColumnCount) { + return proxyColumn - sourceColumnCount; + } + return -1; +} + +int KExtraColumnsProxyModel::proxyColumnForExtraColumn(int extraColumn) const +{ + return sourceModel()->columnCount() + extraColumn; +} + +void KExtraColumnsProxyModelPrivate::_ec_sourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint) +{ + Q_Q(KExtraColumnsProxyModel); + const QModelIndexList persistentIndexList = q->persistentIndexList(); + layoutChangePersistentIndexes.reserve(persistentIndexList.size()); + layoutChangeProxyColumns.reserve(persistentIndexList.size()); + + foreach (QPersistentModelIndex proxyPersistentIndex, persistentIndexList) { + proxyIndexes << proxyPersistentIndex; + Q_ASSERT(proxyPersistentIndex.isValid()); + const int column = proxyPersistentIndex.column(); + layoutChangeProxyColumns << column; + if (column >= q->sourceModel()->columnCount()) { + proxyPersistentIndex = proxyPersistentIndex.sibling(proxyPersistentIndex.row(), 0); + } + 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 KExtraColumnsProxyModelPrivate::_ec_sourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint) +{ + Q_Q(KExtraColumnsProxyModel); + for (int i = 0; i < proxyIndexes.size(); ++i) { + const QModelIndex proxyIdx = proxyIndexes.at(i); + QModelIndex newProxyIdx = q->mapFromSource(layoutChangePersistentIndexes.at(i)); + if (proxyIdx.column() >= q->sourceModel()->columnCount()) { + newProxyIdx = newProxyIdx.sibling(newProxyIdx.row(), layoutChangeProxyColumns.at(i)); + } + q->changePersistentIndex(proxyIdx, newProxyIdx); + } + + layoutChangePersistentIndexes.clear(); + layoutChangeProxyColumns.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); +} + +#include "moc_kextracolumnsproxymodel.cpp" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.12.0/src/kextracolumnsproxymodel.h new/kitemmodels-5.13.0/src/kextracolumnsproxymodel.h --- old/kitemmodels-5.12.0/src/kextracolumnsproxymodel.h 1970-01-01 01:00:00.000000000 +0100 +++ new/kitemmodels-5.13.0/src/kextracolumnsproxymodel.h 2015-08-01 14:41:18.000000000 +0200 @@ -0,0 +1,142 @@ +/* + 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 KEXTRACOLUMNSPROXYMODEL_H +#define KEXTRACOLUMNSPROXYMODEL_H + +#include <QIdentityProxyModel> +#include <QScopedPointer> +#include "kitemmodels_export.h" + +class KExtraColumnsProxyModelPrivate; + +/** + * This proxy appends extra columns (after all existing columns). + * + * The proxy supports source models that have a tree structure. + * It also supports editing, and propagating changes from the source model. + * Row insertion/removal, column insertion/removal in the source model are supported. + * + * Not supported: adding/removing extra columns at runtime; having a different number of columns in subtrees; + * drag-n-drop support in the extra columns; moving columns. + * + * Derive from KExtraColumnsProxyModel, call appendColumn (typically in the constructor) for each extra column, + * and reimplement extraColumnData() to allow KExtraColumnsProxyModel to retrieve the data to show in the extra columns. + * + * If you want your new column(s) to be somewhere else than at the right of the existing columns, you can + * use a KRearrangeColumnsProxyModel on top. + * + * Author: David Faure, KDAB + * @since 5.13 + */ +class KITEMMODELS_EXPORT KExtraColumnsProxyModel : public QIdentityProxyModel +{ + Q_OBJECT +public: + /** + * Base class constructor. + * Remember to call setSourceModel afterwards, and appendColumn. + */ + explicit KExtraColumnsProxyModel(QObject *parent = 0); + /** + * Destructor. + */ + ~KExtraColumnsProxyModel(); + + // API + + /** + * Appends an extra column. + * @param header an optional text for the horizontal header + */ + void appendColumn(const QString &header = QString()); + + /** + * This method is called by data() for extra columns. + * Reimplement this method to return the data for the extra columns. + * + * @param parent the parent model index (only useful in tree models) + * @param row the row number for which the model is querying for data (child of @p parent, if set) + * @param extraColumn the number of the extra column, starting at 0 (this doesn't require knowing how many columns the source model has) + * @param role the role being queried + * @return the data at @p row and @p extraColumn + */ + virtual QVariant extraColumnData(const QModelIndex &parent, int row, int extraColumn, int role = Qt::DisplayRole) const = 0; + + /** + * This method is called by setData() for extra columns. + * Reimplement this method to set the data for the extra columns, if editing is supported. + * Remember to call extraColumnDataChanged() after changing the data storage. + * The default implementation returns false. + */ + virtual bool setExtraColumnData(const QModelIndex &parent, int row, int extraColumn, const QVariant &data, int role = Qt::EditRole); + + /** + * This method can be called by your derived class when the data in an extra column has changed. + * The use case is data that changes "by itself", unrelated to setData. + */ + void extraColumnDataChanged(const QModelIndex &parent, int row, int extraColumn, const QVector<int> &roles); + + /** + * Returns the extra column number (0, 1, ...) for a given column number of the proxymodel. + * This basically means subtracting the amount of columns in the source model. + */ + int extraColumnForProxyColumn(int proxyColumn) const; + /** + * Returns the proxy column number for a given extra column number (starting at 0). + * This basically means adding the amount of columns in the source model. + */ + int proxyColumnForExtraColumn(int extraColumn) const; + + + // Implementation + /// @reimp + void setSourceModel(QAbstractItemModel *model) Q_DECL_OVERRIDE; + /// @reimp + QModelIndex mapToSource(const QModelIndex &proxyIndex) const Q_DECL_OVERRIDE; + /// @reimp + QItemSelection mapSelectionToSource(const QItemSelection &selection) const Q_DECL_OVERRIDE; + /// @reimp + int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + /// @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::EditRole) Q_DECL_OVERRIDE; + /// @reimp + QModelIndex sibling(int row, int column, const QModelIndex &idx) const Q_DECL_OVERRIDE; + /// @reimp + Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE; + /// @reimp + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + /// @reimp + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + /// @reimp + QModelIndex parent(const QModelIndex &child) const Q_DECL_OVERRIDE; + +private: + Q_DECLARE_PRIVATE(KExtraColumnsProxyModel) + Q_PRIVATE_SLOT(d_func(), void _ec_sourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &, QAbstractItemModel::LayoutChangeHint)) + Q_PRIVATE_SLOT(d_func(), void _ec_sourceLayoutChanged(const QList<QPersistentModelIndex> &, QAbstractItemModel::LayoutChangeHint)) + const QScopedPointer<KExtraColumnsProxyModelPrivate> d_ptr; + +}; + +#endif